* Update to packages/alpine.spec to account for the new location of man
[alpine.git] / alpine / adrbkcmd.c
blob4ab80f0f9cd07cd9b7bf866db6ea2f1d7afeb7a2
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: adrbkcmd.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 adrbkcmds.c
21 Commands called from the addrbook screens.
22 ====*/
25 #include "headers.h"
26 #include "adrbkcmd.h"
27 #include "addrbook.h"
28 #include "takeaddr.h"
29 #include "status.h"
30 #include "keymenu.h"
31 #include "mailview.h"
32 #include "mailcmd.h"
33 #include "mailindx.h"
34 #include "radio.h"
35 #include "folder.h"
36 #include "reply.h"
37 #include "help.h"
38 #include "titlebar.h"
39 #include "signal.h"
40 #include "roleconf.h"
41 #include "send.h"
42 #include "../pith/adrbklib.h"
43 #include "../pith/addrbook.h"
44 #include "../pith/abdlc.h"
45 #include "../pith/ablookup.h"
46 #include "../pith/bldaddr.h"
47 #include "../pith/ldap.h"
48 #include "../pith/state.h"
49 #include "../pith/filter.h"
50 #include "../pith/conf.h"
51 #include "../pith/msgno.h"
52 #include "../pith/addrstring.h"
53 #include "../pith/remote.h"
54 #include "../pith/url.h"
55 #include "../pith/util.h"
56 #include "../pith/detoken.h"
57 #include "../pith/stream.h"
58 #include "../pith/send.h"
59 #include "../pith/list.h"
60 #include "../pith/busy.h"
61 #include "../pith/icache.h"
62 #include "../pith/osdep/color.h"
65 /* internal prototypes */
66 int url_hilite_abook(long, char *, LT_INS_S **, void *);
67 int process_abook_view_cmd(int, MSGNO_S *, SCROLL_S *);
68 int expand_addrs_for_pico(struct headerentry *, char ***);
69 char *view_message_for_pico(char **);
70 int verify_nick(char *, char **, char **, BUILDER_ARG *, int *);
71 int verify_addr(char *, char **, char **, BUILDER_ARG *, int *);
72 int pico_sendexit_for_adrbk(struct headerentry *, void(*)(void), int, char **);
73 char *pico_cancelexit_for_adrbk(char *, void (*)(void));
74 char *pico_cancel_for_adrbk_take(void (*)(void));
75 char *pico_cancel_for_adrbk_edit(void (*)(void));
76 int ab_modify_abook_list(int, int, int, char *, char *, char *);
77 int convert_abook_to_remote(struct pine *, PerAddrBook *, char *, size_t, int);
78 int any_rule_files_to_warn_about(struct pine *);
79 int verify_folder_name(char *,char **,char **,BUILDER_ARG *, int *);
80 int verify_server_name(char *,char **,char **,BUILDER_ARG *, int *);
81 int verify_abook_nick(char *, char **,char **,BUILDER_ARG *, int *);
82 int do_the_shuffle(int *, int, int, char **);
83 void ab_compose_internal(BuildTo, int);
84 int ab_export(struct pine *, long, int, int);
85 VCARD_INFO_S *prepare_abe_for_vcard(struct pine *, AdrBk_Entry *, int);
86 void write_single_tab_entry(gf_io_t, VCARD_INFO_S *);
87 int percent_done_copying(void);
88 int cmp_action_list(const qsort_t *, const qsort_t *);
89 void set_act_list_member(ACTION_LIST_S *, a_c_arg_t, PerAddrBook *, PerAddrBook *, char *);
90 void convert_pinerc_to_remote(struct pine *, char *);
92 #ifdef ENABLE_LDAP
93 typedef struct _saved_query {
94 char *qq,
95 *cn,
96 *sn,
97 *gn,
98 *mail,
99 *org,
100 *unit,
101 *country,
102 *state,
103 *locality,
104 *custom;
105 } SAVED_QUERY_S;
107 int process_ldap_cmd(int, MSGNO_S *, SCROLL_S *);
108 int pico_simpleexit(struct headerentry *, void (*)(void), int, char **);
109 char *pico_simplecancel(void (*)(void));
110 void save_query_parameters(SAVED_QUERY_S *);
111 SAVED_QUERY_S *copy_query_parameters(SAVED_QUERY_S *);
112 void free_query_parameters(SAVED_QUERY_S **);
113 int restore_query_parameters(struct headerentry *, char ***);
115 static char *expander_address;
116 #endif /* ENABLE_LDAP */
118 static char *fakedomain = "@";
121 #define VIEW_ABOOK_NONE 0
122 #define VIEW_ABOOK_EDITED 1
123 #define VIEW_ABOOK_WARPED 2
126 * View an addrbook entry.
127 * Call scrolltool to do the work.
129 void
130 view_abook_entry(struct pine *ps, long int cur_line)
132 AdrBk_Entry *abe;
133 STORE_S *in_store, *out_store;
134 char *string, *errstr;
135 SCROLL_S sargs;
136 HANDLE_S *handles = NULL;
137 URL_HILITE_S uh;
138 gf_io_t pc, gc;
139 int cmd, abook_indent;
140 long offset = 0L;
141 char b[500];
143 dprint((5, "- view_abook_entry -\n"));
145 if(is_addr(cur_line)){
146 abe = ae(cur_line);
147 if(!abe){
148 q_status_message(SM_ORDER, 0, 3, _("Error reading entry"));
149 return;
152 else{
153 q_status_message(SM_ORDER, 0, 3, _("Nothing to view"));
154 return;
157 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
158 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
159 return;
162 abook_indent = utf8_width(_("Nickname")) + 2;
164 /* TRANSLATORS: Nickname is a shorthand name for something */
165 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
166 so_puts(in_store, b);
167 if(abe->nickname)
168 so_puts(in_store, abe->nickname);
170 so_puts(in_store, "\015\012");
172 /* TRANSLATORS: Full name is the name that goes with an email address.
173 For example, in
174 Fred Flintstone <fred@bedrock.org>
175 Fred Flintstone is the Full Name. */
176 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
177 so_puts(in_store, b);
178 if(abe->fullname)
179 so_puts(in_store, abe->fullname);
181 so_puts(in_store, "\015\012");
183 /* TRANSLATORS: Fcc is an abbreviation for File carbon copy. It is like
184 a cc which is a copy of a message that goes to somebody other than the
185 main recipient, only this is a copy of a message which is put into a
186 file on the user's computer. */
187 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
188 so_puts(in_store, b);
189 if(abe->fcc)
190 so_puts(in_store, abe->fcc);
192 so_puts(in_store, "\015\012");
194 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, AB_COMMENT_STR);
195 so_puts(in_store, b);
196 if(abe->extra)
197 so_puts(in_store, abe->extra);
199 so_puts(in_store, "\015\012");
201 /* TRANSLATORS: Addresses refers to email Addresses */
202 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
203 so_puts(in_store, b);
204 if(abe->tag == Single){
205 char *tmp = abe->addr.addr ? abe->addr.addr : "";
206 #ifdef ENABLE_LDAP
207 if(!strncmp(tmp, QRUN_LDAP, LEN_QRL))
208 string = LDAP_DISP;
209 else
210 #endif
211 string = tmp;
213 so_puts(in_store, string);
215 else{
216 char **ll;
218 for(ll = abe->addr.list; ll && *ll; ll++){
219 if(ll != abe->addr.list){
220 so_puts(in_store, "\015\012");
221 so_puts(in_store, repeat_char(abook_indent+2, SPACE));
224 #ifdef ENABLE_LDAP
225 if(!strncmp(*ll, QRUN_LDAP, LEN_QRL))
226 string = LDAP_DISP;
227 else
228 #endif
229 string = *ll;
231 so_puts(in_store, string);
233 if(*(ll+1)) /* not the last one */
234 so_puts(in_store, ",");
238 so_puts(in_store, "\015\012");
241 if(!(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
242 so_give(&in_store);
243 q_status_message(SM_ORDER | SM_DING, 3, 3,
244 _("Error allocating space."));
245 return;
248 so_seek(in_store, 0L, 0);
250 init_handles(&handles);
251 gf_filter_init();
253 if(F_ON(F_VIEW_SEL_URL, ps_global)
254 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
255 || F_ON(F_SCAN_ADDR, ps_global))
256 gf_link_filter(gf_line_test,
257 gf_line_test_opt(url_hilite_abook,
258 gf_url_hilite_opt(&uh,&handles,0)));
260 gf_link_filter(gf_wrap, gf_wrap_filter_opt(ps->ttyo->screen_cols - 4,
261 ps->ttyo->screen_cols,
262 NULL, abook_indent+2,
263 GFW_HANDLES));
264 gf_link_filter(gf_nvtnl_local, NULL);
266 gf_set_so_readc(&gc, in_store);
267 gf_set_so_writec(&pc, out_store);
269 if((errstr = gf_pipe(gc, pc)) != NULL){
270 so_give(&in_store);
271 so_give(&out_store);
272 free_handles(&handles);
273 q_status_message1(SM_ORDER | SM_DING, 3, 3,
274 /* TRANSLATORS: %s is the error message */
275 _("Can't format entry: %s"), errstr);
276 return;
279 gf_clear_so_writec(out_store);
280 gf_clear_so_readc(in_store);
282 memset(&sargs, 0, sizeof(SCROLL_S));
283 sargs.text.text = so_text(out_store);
284 sargs.text.src = CharStar;
285 sargs.text.desc = _("expanded entry");
286 sargs.text.handles = handles;
288 if(offset){ /* resize? preserve paging! */
289 sargs.start.on = Offset;
290 sargs.start.loc.offset = offset;
291 offset = 0L;
294 /* TRANSLATORS: a screen title. We are viewing an address book */
295 sargs.bar.title = _("ADDRESS BOOK (View)");
296 sargs.bar.style = TextPercent;
297 sargs.proc.tool = process_abook_view_cmd;
298 sargs.proc.data.i = VIEW_ABOOK_NONE;
299 sargs.resize_exit = 1;
300 sargs.help.text = h_abook_view;
301 /* TRANSLATORS: help screen title */
302 sargs.help.title = _("HELP FOR ADDRESS BOOK VIEW");
303 sargs.keys.menu = &abook_view_keymenu;
304 setbitmap(sargs.keys.bitmap);
306 if(handles)
307 sargs.keys.menu->how_many = 2;
308 else{
309 sargs.keys.menu->how_many = 1;
310 clrbitn(OTHER_KEY, sargs.keys.bitmap);
313 if((cmd = scrolltool(&sargs)) == MC_RESIZE)
314 offset = sargs.start.loc.offset;
316 so_give(&out_store);
317 free_handles(&handles);
319 while(cmd == MC_RESIZE);
321 so_give(&in_store);
323 if(sargs.proc.data.i != VIEW_ABOOK_NONE){
324 long old_l_p_p, old_top_ent, old_cur_row;
326 if(sargs.proc.data.i == VIEW_ABOOK_WARPED){
328 * Warped means we got plopped down somewhere in the display
329 * list so that we don't know where we are relative to where
330 * we were before we warped. The current line number will
331 * be zero, since that is what the warp would have set.
333 as.top_ent = first_line(0L - as.l_p_page/2L);
334 as.cur_row = 0L - as.top_ent;
336 else if(sargs.proc.data.i == VIEW_ABOOK_EDITED){
337 old_l_p_p = as.l_p_page;
338 old_top_ent = as.top_ent;
339 old_cur_row = as.cur_row;
342 /* Window size may have changed while in pico. */
343 ab_resize();
345 /* fix up what ab_resize messed up */
346 if(sargs.proc.data.i != VIEW_ABOOK_WARPED && old_l_p_p == as.l_p_page){
347 as.top_ent = old_top_ent;
348 as.cur_row = old_cur_row;
349 as.old_cur_row = old_cur_row;
352 cur_line = as.top_ent+as.cur_row;
355 ps->mangled_screen = 1;
360 url_hilite_abook(long int linenum, char *line, LT_INS_S **ins, void *local)
362 register char *lp;
364 if((lp = strchr(line, ':')) &&
365 !strncmp(line, AB_COMMENT_STR, strlen(AB_COMMENT_STR)))
366 (void) url_hilite(linenum, lp + 1, ins, local);
368 return(0);
373 process_abook_view_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
375 int rv = 1, i;
376 PerAddrBook *pab;
377 AddrScrn_Disp *dl;
378 static ESCKEY_S text_or_vcard[] = {
379 /* TRANSLATORS: Text refers to plain old text, probably the text of
380 an email message */
381 {'t', 't', "T", N_("Text")},
382 /* TRANSLATORS: A VCard is kind of like an electronic business card. It is not
383 something specific to alpine, it is universal. */
384 {'v', 'v', "V", N_("VCard")},
385 {-1, 0, NULL, NULL}};
387 ps_global->next_screen = SCREEN_FUN_NULL;
389 switch(cmd){
390 case MC_EDIT :
392 * MC_EDIT works a little differently from the other cmds here.
393 * The others return 0 to scrolltool so that we are still in
394 * the view screen. This one is different because we may have
395 * changed what we're viewing. We handle that by returning 1
396 * to scrolltool and setting the sparms opt union's gint
397 * to the value below.
399 * (Late breaking news. Now we're going to return 1 from all these
400 * commands and then in the caller we're going to bounce back out
401 * to the index view instead of the view of the individual entry.
402 * So there is some dead code around for now.)
404 * Then, in the view_abook_entry function we check the value
405 * of this field on scrolltool's return and if it is one of
406 * the two special values below view_abook_entry resets the
407 * current line if necessary, flushes the display cache (in
408 * ab_resize) and loops back and starts over, effectively
409 * putting us back in the view screen but with updated
410 * contents. A side effect is that the screen above that (the
411 * abook index) will also have been flushed and corrected by
412 * the ab_resize.
414 pab = &as.adrbks[cur_addr_book()];
415 if(pab && pab->access == ReadOnly){
416 /* TRANSLATORS: Address book can be viewed but not changed */
417 q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
418 rv = 0;
419 break;
422 if(adrbk_check_all_validity_now()){
423 if(resync_screen(pab, AddrBookScreen, 0)){
424 q_status_message(SM_ORDER | SM_DING, 3, 4,
425 /* TRANSLATORS: The address book was changed by some other
426 process. The user is being told that their change (their
427 Update) has been canceled and they should try again. */
428 _("Address book changed. Update cancelled. Try again."));
429 ps_global->mangled_screen = 1;
430 break;
434 if(pab && pab->access == ReadOnly){
435 q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
436 rv = 0;
437 break;
441 * Arguments all come from globals, not from the arguments to
442 * process_abook_view_cmd. It would be a little cleaner if the
443 * information was contained in att, I suppose.
445 if(is_addr(as.cur_row+as.top_ent)){
446 AdrBk_Entry *abe, *abe_copy;
447 a_c_arg_t entry;
448 int warped = 0;
450 dl = dlist(as.top_ent+as.cur_row);
451 entry = dl->elnum;
452 abe = adrbk_get_ae(pab->address_book, entry);
453 abe_copy = copy_ae(abe);
454 dprint((9,"Calling edit_entry to edit from view\n"));
455 /* TRANSLATORS: update as in update an address book entry */
456 edit_entry(pab->address_book, abe_copy, entry,
457 abe->tag, 0, &warped, _("update"));
459 * The ABOOK_EDITED case doesn't mean that we necessarily
460 * changed something, just that we might have but we know
461 * we didn't change the sort order (causing the warp).
463 sparms->proc.data.i = warped
464 ? VIEW_ABOOK_WARPED : VIEW_ABOOK_EDITED;
466 free_ae(&abe_copy);
467 rv = 1; /* causes scrolltool to return */
469 else{
470 q_status_message(SM_ORDER, 0, 4,
471 "Something wrong, entry not updateable");
472 rv = 0;
473 break;
476 break;
478 case MC_SAVE :
479 pab = &as.adrbks[cur_addr_book()];
481 * Arguments all come from globals, not from the arguments to
482 * process_abook_view_cmd. It would be a little cleaner if the
483 * information was contained in att, I suppose.
485 (void)ab_save(ps_global, pab ? pab->address_book : NULL,
486 as.top_ent+as.cur_row, -FOOTER_ROWS(ps_global), 0);
487 rv = 1;
488 break;
490 case MC_COMPOSE :
491 (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 0);
492 rv = 1;
493 break;
495 case MC_ROLE :
496 (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 1);
497 rv = 1;
498 break;
500 case MC_FORWARD :
501 rv = 1;
502 /* TRANSLATORS: A question with two choices for the answer. Forward
503 as text means to include the text of the message being forwarded
504 in the new message. Forward as a Vcard attachment means to
505 attach it to the message in a special format so it is recognizable
506 as a Vcard. */
507 i = radio_buttons(_("Forward as text or forward as Vcard attachment ? "),
508 -FOOTER_ROWS(ps_global), text_or_vcard, 't', 'x',
509 h_ab_text_or_vcard, RB_NORM);
510 switch(i){
511 case 'x':
512 q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
513 rv = 0;
514 break;
516 case 't':
517 forward_text(ps_global, sparms->text.text, sparms->text.src);
518 break;
520 case 'v':
521 (void)ab_forward(ps_global, as.top_ent+as.cur_row, 0);
522 break;
524 default:
525 q_status_message(SM_ORDER, 3, 3,
526 "can't happen in process_abook_view_cmd");
527 break;
530 break;
532 default:
533 alpine_panic("Unexpected command in process_abook_view_cmd");
534 break;
537 return(rv);
542 * Give expanded view of this address entry.
543 * Call scrolltool to do the work.
545 * Args: headents -- The headerentry array from pico.
546 * s -- Unused here.
548 * Returns -- Always 0.
551 expand_addrs_for_pico(struct headerentry *headents, char ***s)
553 BuildTo bldto;
554 STORE_S *store;
555 char *error = NULL, *addr = NULL, *fullname = NULL, *address = NULL;
556 SAVE_STATE_S state;
557 ADDRESS *adrlist = NULL, *a;
558 int j, address_index = -1, fullname_index = -1, no_a_fld = 0;
559 void (*redraw)(void) = ps_global->redrawer;
561 char *tmp, *tmp2, *tmp3;
562 SCROLL_S sargs;
563 AdrBk_Entry abe;
564 char fakeaddrpmt[500];
565 int abook_indent;
567 dprint((5, "- expand_addrs_for_pico -\n"));
569 abook_indent = utf8_width(_("Nickname")) + 2;
570 utf8_snprintf(fakeaddrpmt, sizeof(fakeaddrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Address"));
572 if(s)
573 *s = NULL;
575 ps_global->redrawer = NULL;
576 fix_windsize(ps_global);
578 ab_nesting_level++;
579 save_state(&state);
581 for(j=0;
582 headents[j].name != NULL && (address_index < 0 || fullname_index < 0);
583 j++){
584 if(!strncmp(headents[j].name, "Address", 7) || !strncmp(headents[j].name, _("Address"), strlen(_("Address"))))
585 address_index = j;
586 else if(!strncmp(headents[j].name, "Fullname", 8) || !strncmp(headents[j].name, _("Fullname"), strlen(_("Fullname"))))
587 fullname_index = j;
590 if(address_index >= 0)
591 address = *headents[address_index].realaddr;
592 else{
593 address_index = 1000; /* a big number */
594 no_a_fld++;
597 if(fullname_index >= 0)
598 fullname = adrbk_formatname(*headents[fullname_index].realaddr,
599 NULL, NULL);
601 memset(&abe, 0, sizeof(abe));
602 if(fullname)
603 abe.fullname = cpystr(fullname);
605 if(address){
606 char *tmp_a_string;
608 tmp_a_string = cpystr(address);
609 rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
610 fs_give((void **)&tmp_a_string);
611 if(adrlist && adrlist->next){
612 int cnt = 0;
614 abe.tag = List;
615 for(a = adrlist; a; a = a->next)
616 cnt++;
618 abe.addr.list = (char **)fs_get((cnt+1) * sizeof(char *));
619 cnt = 0;
620 for(a = adrlist; a; a = a->next){
621 if(a->host && a->host[0] == '@')
622 abe.addr.list[cnt++] = cpystr(a->mailbox);
623 else if(a->host && a->host[0] && a->mailbox && a->mailbox[0])
624 abe.addr.list[cnt++] =
625 cpystr(simple_addr_string(a, tmp_20k_buf,
626 SIZEOF_20KBUF));
629 abe.addr.list[cnt] = NULL;
631 else{
632 abe.tag = Single;
633 abe.addr.addr = address;
636 if(adrlist)
637 mail_free_address(&adrlist);
639 bldto.type = Abe;
640 bldto.arg.abe = &abe;
641 our_build_address(bldto, &addr, &error, NULL, NULL);
642 if(error){
643 q_status_message1(SM_ORDER, 3, 4, "%s", error);
644 fs_give((void **)&error);
647 if(addr){
648 tmp_a_string = cpystr(addr);
649 rfc822_parse_adrlist(&adrlist, tmp_a_string, ps_global->maildomain);
650 fs_give((void **)&tmp_a_string);
653 #ifdef ENABLE_LDAP
654 else if(no_a_fld && expander_address){
655 WP_ERR_S wp_err;
657 memset(&wp_err, 0, sizeof(wp_err));
658 adrlist = wp_lookups(expander_address, &wp_err, 0);
659 if(fullname && *fullname){
660 if(adrlist){
661 if(adrlist->personal)
662 fs_give((void **)&adrlist->personal);
664 adrlist->personal = cpystr(fullname);
668 if(wp_err.error){
669 q_status_message1(SM_ORDER, 3, 4, "%s", wp_err.error);
670 fs_give((void **)&wp_err.error);
673 #endif
675 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
676 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
677 restore_state(&state);
678 return(0);
681 for(j = 0; j < address_index && headents[j].name != NULL; j++){
682 if((tmp = fold(*headents[j].realaddr,
683 ps_global->ttyo->screen_cols,
684 ps_global->ttyo->screen_cols,
685 headents[j].prompt,
686 repeat_char(headents[j].prwid, SPACE), FLD_NONE)) != NULL){
687 so_puts(store, tmp);
688 fs_give((void **)&tmp);
693 * There is an assumption that Addresses is the last field.
695 tmp3 = cpystr(repeat_char(headents[0].prwid + 2, SPACE));
696 if(adrlist){
697 for(a = adrlist; a; a = a->next){
698 ADDRESS *next_addr;
699 char *bufp;
700 size_t len;
702 next_addr = a->next;
703 a->next = NULL;
704 len = est_size(a);
705 bufp = (char *) fs_get(len * sizeof(char));
706 (void) addr_string(a, bufp, len);
707 a->next = next_addr;
710 * Another assumption, all the prwids are the same.
712 if((tmp = fold(bufp,
713 ps_global->ttyo->screen_cols,
714 ps_global->ttyo->screen_cols,
715 (a == adrlist) ? (no_a_fld
716 ? fakeaddrpmt
717 : headents[address_index].prompt)
718 : tmp3+2,
719 tmp3, FLD_NONE)) != NULL){
720 so_puts(store, tmp);
721 fs_give((void **) &tmp);
724 fs_give((void **) &bufp);
727 else{
728 tmp2 = NULL;
729 if((tmp = fold(tmp2=cpystr("<none>"),
730 ps_global->ttyo->screen_cols,
731 ps_global->ttyo->screen_cols,
732 no_a_fld ? fakeaddrpmt
733 : headents[address_index].prompt,
734 tmp3, FLD_NONE)) != NULL){
735 so_puts(store, tmp);
736 fs_give((void **) &tmp);
739 if(tmp2)
740 fs_give((void **)&tmp2);
743 if(tmp3)
744 fs_give((void **)&tmp3);
746 memset(&sargs, 0, sizeof(SCROLL_S));
747 sargs.text.text = so_text(store);
748 sargs.text.src = CharStar;
749 sargs.text.desc = _("expanded entry");
750 /* TRANSLATORS: screen title for viewing an address book entry in Rich
751 view mode. Rich just means it is expanded to include everything. */
752 sargs.bar.title = _("ADDRESS BOOK (Rich View)");
753 sargs.bar.style = TextPercent;
754 sargs.keys.menu = &abook_text_km;
755 setbitmap(sargs.keys.bitmap);
757 scrolltool(&sargs);
758 so_give(&store);
760 restore_state(&state);
761 ab_nesting_level--;
763 if(addr)
764 fs_give((void **)&addr);
766 if(adrlist)
767 mail_free_address(&adrlist);
769 if(fullname)
770 fs_give((void **)&fullname);
772 if(abe.fullname)
773 fs_give((void **)&abe.fullname);
775 if(abe.tag == List && abe.addr.list)
776 free_list_array(&(abe.addr.list));
778 ps_global->redrawer = redraw;
780 return(0);
785 * Callback from TakeAddr editing screen to see message that was being
786 * viewed. Call scrolltool to do the work.
788 char *
789 view_message_for_pico(char **error)
791 STORE_S *store;
792 gf_io_t pc;
793 void (*redraw)(void) = ps_global->redrawer;
794 SourceType src = CharStar;
795 SCROLL_S sargs;
797 dprint((5, "- view_message_for_pico -\n"));
799 ps_global->redrawer = NULL;
800 fix_windsize(ps_global);
802 #ifdef DOS
803 src = TmpFileStar;
804 #endif
806 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
807 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
808 return(NULL);
811 gf_set_so_writec(&pc, store);
813 format_message(msgno_for_pico_callback, env_for_pico_callback,
814 body_for_pico_callback, NULL, FM_NEW_MESS | FM_DISPLAY, pc);
816 gf_clear_so_writec(store);
818 memset(&sargs, 0, sizeof(SCROLL_S));
819 sargs.text.text = so_text(store);
820 sargs.text.src = src;
821 sargs.text.desc = _("expanded entry");
822 /* TRANSLATORS: this is a screen title */
823 sargs.bar.title = _("MESSAGE TEXT");
824 sargs.bar.style = TextPercent;
825 sargs.keys.menu = &abook_text_km;
826 setbitmap(sargs.keys.bitmap);
828 scrolltool(&sargs);
830 so_give(&store);
832 ps_global->redrawer = redraw;
834 return(NULL);
839 prompt::name::help::prwid::maxlen::realaddr::
840 builder::affected_entry::next_affected::selector::key_label::fileedit::nickcmpl
841 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
842 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
844 static struct headerentry headents_for_edit[]={
845 {"Nickname : ", N_("Nickname"), h_composer_abook_nick, 12, 0, NULL,
846 /* TRANSLATORS: To AddrBk is a command that takes the user to
847 the address book screen to select an entry from there. */
848 verify_nick, NULL, NULL, addr_book_nick_for_edit, N_("To AddrBk"), NULL, abook_nickname_complete,
849 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
850 {"Fullname : ", N_("Fullname"), h_composer_abook_full, 12, 0, NULL,
851 NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
852 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
853 /* TRANSLATORS: a File Copy is a copy of a sent message saved in a regular
854 file on the computer's disk */
855 {"Fcc : ", N_("FileCopy"), h_composer_abook_fcc, 12, 0, NULL,
856 /* TRANSLATORS: To Folders */
857 NULL, NULL, NULL, folders_for_fcc, N_("To Fldrs"), NULL, NULL,
858 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
859 {"Comment : ", N_("Comment"), h_composer_abook_comment, 12, 0, NULL,
860 NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
861 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
862 {"Addresses : ", N_("Addresses"), h_composer_abook_addrs, 12, 0, NULL,
863 verify_addr, NULL, NULL, addr_book_change_list, N_("To AddrBk"), NULL, abook_nickname_complete,
864 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
865 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
866 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
868 #define NNN_NICK 0
869 #define NNN_FULL 1
870 #define NNN_FCC 2
871 #define NNN_COMMENT 3
872 #define NNN_ADDR 4
873 #define NNN_END 5
875 static char *nick_saved_for_pico_check;
876 static AdrBk *abook_saved_for_pico_check;
879 * Args: abook -- Address book handle
880 * abe -- AdrBk_Entry of old entry to work on. If NULL, this will
881 * be a new entry. This has to be a pointer to a copy of
882 * an abe that won't go away until we finish this function.
883 * In other words, don't pass in a pointer to an abe in
884 * the cache, copy it first. The tag on this abe is only
885 * used to decide whether to read abe->addr.list or
886 * abe->addr.addr, not to determine what the final result
887 * will be. That's determined solely by how many addresses
888 * there are after the user edits.
889 * entry -- The entry number of the old entry that we will be changing.
890 * old_tag -- If we're changing an old entry, then this is the tag of
891 * that old entry.
892 * readonly -- Call pico with readonly flag
893 * warped -- We warped to a new part of the addrbook
894 * (We also overload warped in a couple places and use it's
895 * being set as an indicator of whether we are Taking or
896 * not. It will be NULL if we are Taking.)
898 void
899 edit_entry(AdrBk *abook, AdrBk_Entry *abe, a_c_arg_t entry, Tag old_tag, int readonly, int *warped, char *cmd)
901 AdrBk_Entry local_abe;
902 struct headerentry *he;
903 PICO pbf;
904 STORE_S *msgso;
905 adrbk_cntr_t old_entry_num, new_entry_num = NO_NEXT;
906 int rc = 0, resort_happened = 0, list_changed = 0, which_addrbook;
907 int editor_result, i = 0, add, n_end, ldap = 0;
908 char *nick, *full, *fcc, *comment, *fname, *pp;
909 char **orig_addrarray = NULL;
910 char **new_addrarray = NULL;
911 char *comma_sep_addr = NULL;
912 char **p, **q;
913 Tag new_tag;
914 char titlebar[40];
915 char nickpmt[100], fullpmt[100], fccpmt[100], cmtpmt[100], addrpmt[100];
916 int abook_indent;
917 long length;
918 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
920 dprint((2, "- edit_entry -\n"));
922 old_entry_num = (adrbk_cntr_t) entry;
923 save_state(&state);
924 abook_saved_for_pico_check = abook;
926 add = (abe == NULL); /* doing add or change? */
927 if(add){
928 local_abe.nickname = "";
929 local_abe.fullname = "";
930 local_abe.fcc = "";
931 local_abe.extra = "";
932 local_abe.addr.addr = "";
933 local_abe.tag = NotSet;
934 abe = &local_abe;
935 old_entry_num = NO_NEXT;
938 new_tag = abe->tag;
940 #ifdef ENABLE_LDAP
941 expander_address = NULL;
942 if(abe->tag == Single &&
943 abe->addr.addr &&
944 !strncmp(abe->addr.addr,QRUN_LDAP,LEN_QRL)){
945 ldap = 1;
946 expander_address = cpystr(abe->addr.addr);
947 removing_double_quotes(expander_address);
949 else if(abe->tag == List &&
950 abe->addr.list &&
951 abe->addr.list[0] &&
952 !abe->addr.list[1] &&
953 !strncmp(abe->addr.list[0],QRUN_LDAP,LEN_QRL)){
954 ldap = 2;
955 expander_address = cpystr(abe->addr.list[0]);
956 removing_double_quotes(expander_address);
958 #endif
960 standard_picobuf_setup(&pbf);
961 pbf.exittest = pico_sendexit_for_adrbk;
962 pbf.canceltest = warped ? pico_cancel_for_adrbk_edit
963 : pico_cancel_for_adrbk_take;
964 pbf.expander = expand_addrs_for_pico;
965 pbf.ctrlr_label = _("RichView");
966 /* xgettext: c-format */
967 if(readonly)
968 /* TRANSLATORS: screen titles */
969 snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (View)"));
970 else
971 snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (%c%s)"),
972 islower((unsigned char)(*cmd))
973 ? toupper((unsigned char)*cmd)
974 : *cmd, (cmd+1));
976 pbf.pine_anchor = set_titlebar(titlebar,
977 ps_global->mail_stream,
978 ps_global->context_current,
979 ps_global->cur_folder,ps_global->msgmap,
980 0, FolderName, 0, 0, NULL);
981 pbf.pine_flags |= P_NOBODY;
982 if(readonly)
983 pbf.pine_flags |= P_VIEW;
985 /* An informational message */
986 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
987 pbf.msgtext = (void *)so_text(msgso);
989 * It's nice if we can make it so these lines make sense even if
990 * they don't all make it on the screen, because the user can't
991 * scroll down to see them. So just make each line a whole sentence
992 * that doesn't need the others below it to make sense.
994 if(add){
995 so_puts(msgso,
997 * TRANSLATORS: The following lines go together to form a screen of
998 * explanation about how to edit an address book entry.
1000 _("\n Fill in the fields. It is ok to leave fields blank."));
1001 so_puts(msgso,
1002 _("\n To form a list, just enter multiple comma-separated addresses."));
1004 else{
1005 so_puts(msgso,
1006 /* TRANSLATORS: Same here, but a different version of the screen. */
1007 _("\n Change any of the fields. It is ok to leave fields blank."));
1008 if(ldap)
1009 so_puts(msgso,
1010 _("\n Since this entry does a directory lookup you may not edit the address field."));
1011 else
1012 so_puts(msgso,
1013 _("\n Additional comma-separated addresses may be entered in the address field."));
1016 so_puts(msgso,
1017 _("\n Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."));
1018 so_puts(msgso,
1019 _("\n If you want to use quotation marks inside the Fullname field, it is best"));
1020 so_puts(msgso,
1021 _("\n to use single quotation marks; for example: George 'Husky' Washington."));
1024 he = (struct headerentry *) fs_get((NNN_END+1) * sizeof(struct headerentry));
1025 memset((void *)he, 0, (NNN_END+1) * sizeof(struct headerentry));
1026 pbf.headents = he;
1028 abook_indent = utf8_width(_("Nickname")) + 2;
1030 /* make a copy of each field */
1031 nick = cpystr(abe->nickname ? abe->nickname : "");
1032 removing_leading_and_trailing_white_space(nick);
1033 nick_saved_for_pico_check = cpystr(nick);
1034 he[NNN_NICK] = headents_for_edit[NNN_NICK];
1035 he[NNN_NICK].realaddr = &nick;
1036 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
1037 he[NNN_NICK].prompt = nickpmt;
1038 he[NNN_NICK].prwid = abook_indent+2;
1039 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
1040 he[NNN_NICK].nickcmpl = NULL;
1042 full = cpystr(abe->fullname ? abe->fullname : "");
1043 removing_leading_and_trailing_white_space(full);
1044 he[NNN_FULL] = headents_for_edit[NNN_FULL];
1045 he[NNN_FULL].realaddr = &full;
1046 utf8_snprintf(fullpmt, sizeof(fullpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
1047 he[NNN_FULL].prompt = fullpmt;
1048 he[NNN_FULL].prwid = abook_indent+2;
1050 fcc = cpystr(abe->fcc ? abe->fcc : "");
1051 removing_leading_and_trailing_white_space(fcc);
1052 he[NNN_FCC] = headents_for_edit[NNN_FCC];
1053 he[NNN_FCC].realaddr = &fcc;
1054 utf8_snprintf(fccpmt, sizeof(fccpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
1055 he[NNN_FCC].prompt = fccpmt;
1056 he[NNN_FCC].prwid = abook_indent+2;
1058 comment = cpystr(abe->extra ? abe->extra : "");
1059 removing_leading_and_trailing_white_space(comment);
1060 he[NNN_COMMENT] = headents_for_edit[NNN_COMMENT];
1061 he[NNN_COMMENT].realaddr = &comment;
1062 utf8_snprintf(cmtpmt, sizeof(cmtpmt), "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
1063 he[NNN_COMMENT].prompt = cmtpmt;
1064 he[NNN_COMMENT].prwid = abook_indent+2;
1066 n_end = NNN_END;
1067 if(ldap)
1068 n_end--;
1070 if(!ldap){
1071 he[NNN_ADDR] = headents_for_edit[NNN_ADDR];
1072 he[NNN_ADDR].realaddr = &comma_sep_addr;
1073 utf8_snprintf(addrpmt, sizeof(addrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
1074 he[NNN_ADDR].prompt = addrpmt;
1075 he[NNN_ADDR].prwid = abook_indent+2;
1076 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
1077 he[NNN_NICK].nickcmpl = NULL;
1079 if(abe->tag == Single){
1080 if(abe->addr.addr){
1081 orig_addrarray = (char **) fs_get(2 * sizeof(char *));
1082 orig_addrarray[0] = cpystr(abe->addr.addr);
1083 orig_addrarray[1] = NULL;
1086 else if(abe->tag == List){
1087 if(listmem_count_from_abe(abe) > 0){
1088 orig_addrarray = (char **) fs_get(
1089 (size_t)(listmem_count_from_abe(abe) + 1)
1090 * sizeof(char *));
1091 for(q = orig_addrarray, p = abe->addr.list; p && *p; p++, q++)
1092 *q = cpystr(*p);
1094 *q = NULL;
1098 /* figure out how large a string we need to allocate */
1099 length = 0L;
1100 for(p = orig_addrarray; p && *p; p++)
1101 length += (strlen(*p) + 2);
1103 if(length)
1104 length -= 2L;
1106 pp = comma_sep_addr = (char *) fs_get((size_t)(length+1L) * sizeof(char));
1107 *pp = '\0';
1108 for(p = orig_addrarray; p && *p; p++){
1109 sstrncpy(&pp, *p, length-(pp-comma_sep_addr));
1110 if(*(p+1))
1111 sstrncpy(&pp, ", ", length-(pp-comma_sep_addr));
1114 comma_sep_addr[length] = '\0';
1116 if(verify_addr(comma_sep_addr, NULL, NULL, NULL, NULL) < 0)
1117 he[NNN_ADDR].start_here = 1;
1120 he[n_end] = headents_for_edit[NNN_END];
1121 for(i = 0; i < n_end; i++){
1122 /* no callbacks in some cases */
1123 if(readonly || ((i == NNN_FULL || i == NNN_COMMENT) && !env_for_pico_callback)){
1124 he[i].selector = NULL;
1125 he[i].key_label = NULL;
1128 /* no builders for readonly */
1129 if(readonly)
1130 he[i].builder = NULL;
1133 /* pass to pico and let user change them */
1134 editor_result = pico(&pbf);
1135 ps_global->mangled_screen = 1;
1136 standard_picobuf_teardown(&pbf);
1138 if(editor_result & COMP_GOTHUP)
1139 hup_signal();
1140 else{
1141 fix_windsize(ps_global);
1142 init_signals();
1145 if(editor_result & COMP_CANCEL){
1146 if(!readonly)
1147 /* TRANSLATOR: Something like
1148 Address book save cancelled */
1149 q_status_message1(SM_INFO, 0, 2, _("Address book %s cancelled"), cmd);
1151 else if(editor_result & COMP_EXIT){
1152 if(pico_usingcolor())
1153 clear_index_cache(ps_global->mail_stream, 0);
1154 removing_leading_and_trailing_white_space(nick);
1155 removing_leading_and_trailing_white_space(full);
1156 removing_leading_and_trailing_white_space(fcc);
1157 removing_leading_and_trailing_white_space(comment);
1158 removing_leading_and_trailing_white_space(comma_sep_addr);
1160 /* not needed if pico is returning UTF-8 */
1161 convert_possibly_encoded_str_to_utf8(&nick);
1162 convert_possibly_encoded_str_to_utf8(&full);
1163 convert_possibly_encoded_str_to_utf8(&fcc);
1164 convert_possibly_encoded_str_to_utf8(&comment);
1165 convert_possibly_encoded_str_to_utf8(&comma_sep_addr);
1167 /* don't allow adding null entry */
1168 if(add && !*nick && !*full && !*fcc && !*comment && !*comma_sep_addr)
1169 goto outtahere;
1172 * comma_sep_addr is now the string which has been edited
1174 if(comma_sep_addr)
1175 new_addrarray = parse_addrlist(comma_sep_addr);
1177 if(!ldap && (!new_addrarray || !new_addrarray[0]))
1178 q_status_message(SM_ORDER, 3, 5, _("Warning: entry has no addresses"));
1180 if(!new_addrarray || !new_addrarray[0] || !new_addrarray[1])
1181 new_tag = Single; /* one or zero addresses means its a Single */
1182 else
1183 new_tag = List; /* more than one addresses means its a List */
1185 if(new_tag == List && old_tag == List){
1187 * If Taking, make sure we write it even if user didn't edit
1188 * it any further.
1190 if(!warped)
1191 list_changed++;
1192 else if(he[NNN_ADDR].dirty)
1193 for(q = orig_addrarray, p = new_addrarray; p && *p && q && *q; p++, q++)
1194 if(strcmp(*p, *q) != 0){
1195 list_changed++;
1196 break;
1199 if(!list_changed && he[NNN_ADDR].dirty
1200 && ((!(p && *p) && (q && *q)) || ((p && *p) && !(q && *q))))
1201 list_changed++;
1203 if(list_changed){
1205 * need to delete old list members and add new members below
1207 rc = adrbk_listdel_all(abook, (a_c_arg_t) old_entry_num);
1209 else{
1210 /* don't need new_addrarray */
1211 free_list_array(&new_addrarray);
1214 if(comma_sep_addr)
1215 fs_give((void **) &comma_sep_addr);
1217 else if((new_tag == List && old_tag == Single)
1218 || (new_tag == Single && old_tag == List)){
1219 /* delete old entry */
1220 rc = adrbk_delete(abook, (a_c_arg_t) old_entry_num, 0, 0, 0, 0);
1221 old_entry_num = NO_NEXT;
1222 if(comma_sep_addr && new_tag == List)
1223 fs_give((void **) &comma_sep_addr);
1227 * This will be an edit in the cases where the tag didn't change
1228 * and an add in the cases where it did.
1230 if(rc == 0)
1231 rc = adrbk_add(abook,
1232 (a_c_arg_t)old_entry_num,
1233 nick,
1234 he[NNN_FULL].dirty ? full : abe->fullname,
1235 new_tag == Single ? (ldap == 1 ? abe->addr.addr :
1236 ldap == 2 ? abe->addr.list[0] :
1237 comma_sep_addr)
1238 : NULL,
1239 fcc,
1240 he[NNN_COMMENT].dirty ? comment : abe->extra,
1241 new_tag,
1242 &new_entry_num,
1243 &resort_happened,
1246 (new_tag != List || !new_addrarray));
1249 if(rc == 0 && new_tag == List && new_addrarray)
1250 rc = adrbk_nlistadd(abook, (a_c_arg_t) new_entry_num, &new_entry_num,
1251 &resort_happened, new_addrarray, 1, 0, 1);
1253 restore_state(&state);
1255 if(rc == -2 || rc == -3){
1256 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1257 _("Error updating address book: %s"),
1258 rc == -2 ? error_description(errno) : "Alpine bug");
1260 else if(rc == 0
1261 && strucmp(nick, nick_saved_for_pico_check) != 0
1262 && (editor_result & COMP_EXIT)){
1263 int added_to;
1265 for(added_to = 0; added_to < as.n_addrbk; added_to++)
1266 if(abook_saved_for_pico_check == as.adrbks[added_to].address_book)
1267 break;
1269 if(added_to >= as.n_addrbk)
1270 added_to = -1;
1272 fname = addr_lookup(nick, &which_addrbook, added_to);
1273 if(fname){
1274 q_status_message4(SM_ORDER, 5, 9,
1275 /* TRANSLATORS: The first %s is the nickname the user is
1276 trying to use that exists in another address book.
1277 The second %s is the name of the other address book.
1278 The third %s is " as " because it will say something
1279 like also exists in <name> as <description>. */
1280 _("Warning! Nickname %s also exists in \"%s\"%s%s"),
1281 nick, as.adrbks[which_addrbook].abnick,
1282 (fname && *fname) ? _(" as ") : "",
1283 (fname && *fname) ? fname : "");
1284 fs_give((void **)&fname);
1288 if(resort_happened || list_changed){
1289 DL_CACHE_S dlc_restart;
1291 dlc_restart.adrbk_num = as.cur;
1292 dlc_restart.dlcelnum = new_entry_num;
1293 switch(new_tag){
1294 case Single:
1295 dlc_restart.type = DlcSimple;
1296 break;
1298 case List:
1299 dlc_restart.type = DlcListHead;
1300 break;
1302 default:
1303 break;
1306 warp_to_dlc(&dlc_restart, 0L);
1307 if(warped)
1308 *warped = 1;
1311 outtahere:
1312 if(he)
1313 free_headents(&he);
1315 if(msgso)
1316 so_give(&msgso);
1318 if(nick)
1319 fs_give((void **)&nick);
1320 if(full)
1321 fs_give((void **)&full);
1322 if(fcc)
1323 fs_give((void **)&fcc);
1324 if(comment)
1325 fs_give((void **)&comment);
1327 if(comma_sep_addr)
1328 fs_give((void **)&comma_sep_addr);
1329 if(nick_saved_for_pico_check)
1330 fs_give((void **)&nick_saved_for_pico_check);
1332 free_list_array(&orig_addrarray);
1333 free_list_array(&new_addrarray);
1334 #ifdef ENABLE_LDAP
1335 if(expander_address)
1336 fs_give((void **) &expander_address);
1337 #endif
1341 /*ARGSUSED*/
1343 verify_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
1345 char *tmp;
1347 dprint((7, "- verify_nick - (%s)\n", given ? given : "nul"));
1349 tmp = cpystr(given);
1350 removing_leading_and_trailing_white_space(tmp);
1352 if(nickname_check(tmp, error)){
1353 fs_give((void **)&tmp);
1354 if(mangled){
1355 if(ps_global->mangled_screen)
1356 *mangled |= BUILDER_SCREEN_MANGLED;
1357 else if(ps_global->mangled_footer)
1358 *mangled |= BUILDER_FOOTER_MANGLED;
1361 return -2;
1364 if(ps_global->remote_abook_validity > 0 &&
1365 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
1366 *mangled |= BUILDER_SCREEN_MANGLED;
1368 ab_nesting_level++;
1369 if(strucmp(tmp, nick_saved_for_pico_check) != 0
1370 && adrbk_lookup_by_nick(abook_saved_for_pico_check,
1371 tmp, (adrbk_cntr_t *)NULL)){
1372 if(error){
1373 char buf[MAX_NICKNAME + 80];
1375 /* TRANSLATORS: The %s is the nickname of an entry that is already
1376 in the address book */
1377 snprintf(buf, sizeof(buf), _("\"%s\" already in address book."), tmp);
1378 buf[sizeof(buf)-1] = '\0';
1379 *error = cpystr(buf);
1382 ab_nesting_level--;
1383 fs_give((void **)&tmp);
1384 if(mangled){
1385 if(ps_global->mangled_screen)
1386 *mangled |= BUILDER_SCREEN_MANGLED;
1387 else if(ps_global->mangled_footer)
1388 *mangled |= BUILDER_FOOTER_MANGLED;
1391 return -2;
1394 ab_nesting_level--;
1395 if(expanded)
1396 *expanded = tmp;
1397 else
1398 fs_give((void **)&tmp);
1400 /* This is so pico will erase any old message */
1401 if(error)
1402 *error = cpystr("");
1404 if(mangled){
1405 if(ps_global->mangled_screen)
1406 *mangled |= BUILDER_SCREEN_MANGLED;
1407 else if(ps_global->mangled_footer)
1408 *mangled |= BUILDER_FOOTER_MANGLED;
1411 return 0;
1416 * Args: to -- the passed in line to parse
1417 * full_to -- Address of a pointer to return the full address in.
1418 * This will be allocated here and freed by the caller.
1419 * However, this function is just going to copy "to".
1420 * (special case for the route-addr-hack in build_address_int)
1421 * We're just looking for the error messages.
1422 * error -- Address of a pointer to return an error message in.
1423 * This will be allocated here and freed by the caller.
1424 * fcc -- This should be passed in NULL.
1425 * This builder doesn't support affected_entry's.
1427 * Result: 0 is returned if address was OK,
1428 * -2 if address wasn't OK.
1430 * Side effect: Can flush addrbook entry cache entries so they need to be
1431 * re-fetched afterwords.
1434 verify_addr(char *to, char **full_to, char **error, BUILDER_ARG *fcc, int *mangled)
1436 register char *p;
1437 int ret_val;
1438 BuildTo bldto;
1439 jmp_buf save_jmp_buf;
1440 int *save_nesting_level;
1442 dprint((7, "- verify_addr - (%s)\n", to ? to : "nul"));
1444 /* check to see if to string is empty to avoid work */
1445 for(p = to; p && *p && isspace((unsigned char)(*p)); p++)
1446 ;/* do nothing */
1448 if(!p || !*p){
1449 if(full_to)
1450 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
1452 return 0;
1455 if(full_to != NULL)
1456 *full_to = (char *)NULL;
1458 if(error != NULL)
1459 *error = (char *)NULL;
1462 * If we end up jumping back here because somebody else changed one of
1463 * our addrbooks out from underneath us, we may well leak some memory.
1464 * That's probably ok since this will be very rare.
1466 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
1467 save_nesting_level = cpyint(ab_nesting_level);
1468 if(setjmp(addrbook_changed_unexpectedly)){
1469 if(full_to && *full_to)
1470 fs_give((void **)full_to);
1472 /* TRANSLATORS: This is sort of an error, something unexpected has
1473 happened and alpine is re-initializing the address book. */
1474 q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
1475 dprint((1,
1476 "RESETTING address book... verify_addr(%s)!\n", to ? to : "?"));
1477 addrbook_reset();
1478 ab_nesting_level = *save_nesting_level;
1481 bldto.type = Str;
1482 bldto.arg.str = to;
1484 if(ps_global->remote_abook_validity > 0 &&
1485 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
1486 *mangled |= BUILDER_SCREEN_MANGLED;
1488 ab_nesting_level++;
1490 ret_val = build_address_internal(bldto, full_to, error, NULL, NULL, NULL,
1491 save_and_restore, 1, mangled);
1493 ab_nesting_level--;
1494 if(save_nesting_level)
1495 fs_give((void **)&save_nesting_level);
1497 if(full_to && *full_to && ret_val >= 0)
1498 removing_leading_and_trailing_white_space(*full_to);
1500 /* This is so pico will erase the old message */
1501 if(error != NULL && *error == NULL)
1502 *error = cpystr("");
1504 if(ret_val < 0)
1505 ret_val = -2; /* cause pico to stay on same header line */
1507 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
1508 return(ret_val);
1513 * Call back for pico to prompt the user for exit confirmation
1515 * Returns: either NULL if the user accepts exit, or string containing
1516 * reason why the user declined.
1519 pico_sendexit_for_adrbk(struct headerentry *he, void (*redraw_pico)(void),
1520 int allow_flowed, char **result)
1522 char *rstr = NULL;
1523 void (*redraw)(void) = ps_global->redrawer;
1525 ps_global->redrawer = redraw_pico;
1526 fix_windsize(ps_global);
1528 /* TRANSLATORS: A question */
1529 switch(want_to(_("Exit and save changes "), 'y', 0, NO_HELP, WT_NORM)){
1530 case 'y':
1531 break;
1533 case 'n':
1534 rstr = _("Use ^C to abandon changes you've made");
1535 break;
1538 if(result)
1539 *result = rstr;
1541 ps_global->redrawer = redraw;
1542 return((rstr == NULL) ? 0 : 1);
1547 * Call back for pico to prompt the user for exit confirmation
1549 * Returns: either NULL if the user accepts exit, or string containing
1550 * reason why the user declined.
1552 char *
1553 pico_cancelexit_for_adrbk(char *word, void (*redraw_pico)(void))
1555 char prompt[90];
1556 char *rstr = NULL;
1557 void (*redraw)(void) = ps_global->redrawer;
1559 /* TRANSLATORS: A question. The %s is a noun describing what is being cancelled. */
1560 snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), word);
1561 ps_global->redrawer = redraw_pico;
1562 fix_windsize(ps_global);
1564 switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
1565 case 'y':
1566 rstr = "";
1567 break;
1569 case 'n':
1570 case 'x':
1571 break;
1574 ps_global->redrawer = redraw;
1575 return(rstr);
1579 char *
1580 pico_cancel_for_adrbk_take(void (*redraw_pico)(void))
1582 return(pico_cancelexit_for_adrbk(_("take"), redraw_pico));
1586 char *
1587 pico_cancel_for_adrbk_edit(void (*redraw_pico)(void))
1589 return(pico_cancelexit_for_adrbk(_("changes"), redraw_pico));
1594 prompt::name::help::prwid::maxlen::realaddr::
1595 builder::affected_entry::next_affected::selector::key_label::fileedit::
1596 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
1597 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
1599 static struct headerentry headents_for_add[]={
1600 {"Server Name : ", N_("Server"), h_composer_abook_add_server, 14, 0, NULL,
1601 verify_server_name, NULL, NULL, NULL, NULL, NULL, NULL,
1602 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1603 {"Folder Name : ", N_("Folder"), h_composer_abook_add_folder, 14, 0, NULL,
1604 verify_folder_name, NULL, NULL, NULL, NULL, NULL, NULL,
1605 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1606 {"NickName : ", N_("Nickname"), h_composer_abook_add_nick, 14, 0, NULL,
1607 verify_abook_nick, NULL, NULL, NULL, NULL, NULL, NULL,
1608 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1609 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1610 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
1612 #define NN_SERVER 0
1613 #define NN_FOLDER 1
1614 #define NN_NICK 2
1615 #define NN_END 3
1618 * Args: global -- Add a global address book, not personal.
1619 * add_after_this -- This is the addrbook number which should come
1620 * right before the new addrbook we're adding, if
1621 * that makes sense. If this is -1, append to end
1622 * of the list.
1624 * Returns: addrbook number of new addrbook, or
1625 * -1, no addrbook added
1628 ab_add_abook(int global, int add_after_this)
1630 int ret;
1632 dprint((2, "- ab_add_abook -\n"));
1634 ret = ab_modify_abook_list(0, global, add_after_this, NULL, NULL, NULL);
1636 if(ret >= 0)
1637 q_status_message(SM_ORDER, 0, 3,
1638 _("New address book added. Use \"$\" to adjust order"));
1640 return(ret);
1645 * Args: global -- Add a global address book, not personal.
1646 * abook_num -- Abook num of the entry we are editing.
1647 * serv -- Default server.
1648 * folder -- Default folder.
1649 * nick -- Default nickname.
1651 * Returns: abook_num if successful,
1652 * -1, if not
1655 ab_edit_abook(int global, int abook_num, char *serv, char *folder, char *nick)
1657 dprint((2, "- ab_edit_abook -\n"));
1659 return(ab_modify_abook_list(1, global, abook_num, serv, folder, nick));
1662 static int the_one_were_editing;
1666 * Args: edit -- Edit existing entry
1667 * global -- Add a global address book, not personal.
1668 * abook_num -- This is the addrbook number which should come
1669 * right before the new addrbook we're adding, if
1670 * that makes sense. If this is -1, append to end
1671 * of the list.
1672 * If we are editing instead of adding, this is
1673 * the abook number of the entry we are editing.
1674 * def_serv -- Default server.
1675 * def_fold -- Default folder.
1676 * def_nick -- Default nickname.
1678 * Returns: addrbook number of new addrbook, or
1679 * -1, no addrbook added
1682 ab_modify_abook_list(int edit, int global, int abook_num, char *def_serv, char *def_fold, char *def_nick)
1684 struct headerentry *he;
1685 PICO pbf;
1686 STORE_S *msgso;
1687 int editor_result, i, how_many_in_list, new_abook_num, num_in_list;
1688 int ret = 0;
1689 char *server, *folder, *nickname;
1690 char *new_item = NULL;
1691 EditWhich ew;
1692 AccessType remember_access_result;
1693 PerAddrBook *pab;
1694 char titlebar[100];
1695 char **list, **new_list = NULL;
1696 char tmp[1000+MAXFOLDER];
1697 int abook_indent;
1698 char servpmt[100], foldpmt[100], nickpmt[100];
1699 struct variable *vars = ps_global->vars;
1701 dprint((2, "- ab_modify_abook_list -\n"));
1703 if(ps_global->readonly_pinerc){
1704 if(edit)
1705 /* TRANSLATORS: Change was the name of the command the user was
1706 trying to perform. It is what was cancelled. */
1707 q_status_message(SM_ORDER, 0, 3, _("Change cancelled: config file not changeable"));
1708 else
1709 /* TRANSLATORS: Add was the command that is being cancelled. */
1710 q_status_message(SM_ORDER, 0, 3, _("Add cancelled: config file not changeable"));
1712 return -1;
1715 ew = Main;
1717 if((global && vars[V_GLOB_ADDRBOOK].is_fixed) ||
1718 (!global && vars[V_ADDRESSBOOK].is_fixed)){
1719 if(global)
1720 /* TRANSLATORS: Operation was cancelled because the system management
1721 does not allow the changing of global address books */
1722 q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing global address books"));
1723 else
1724 q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing address books"));
1726 return -1;
1729 init_ab_if_needed();
1731 if(edit){
1732 if((!global &&
1733 (abook_num < 0 || abook_num >= as.how_many_personals)) ||
1734 (global &&
1735 (abook_num < as.how_many_personals ||
1736 abook_num >= as.n_addrbk))){
1737 dprint((1, "Programming botch in ab_modify_abook_list: global=%d abook_num=%d n_addrbk=%d\n", global, abook_num, as.n_addrbk));
1738 q_status_message(SM_ORDER, 0, 3, "Programming botch, bad abook_num");
1739 return -1;
1742 the_one_were_editing = abook_num;
1744 else
1745 the_one_were_editing = -1;
1747 standard_picobuf_setup(&pbf);
1748 pbf.exittest = pico_sendexit_for_adrbk;
1749 pbf.canceltest = pico_cancel_for_adrbk_edit;
1750 if(edit)
1751 /* TRANSLATORS: screen title */
1752 strncpy(titlebar, _("CHANGE ADDRESS BOOK"), sizeof(titlebar));
1753 else
1754 /* TRANSLATORS: screen title */
1755 strncpy(titlebar, _("ADD ADDRESS BOOK"), sizeof(titlebar));
1757 titlebar[sizeof(titlebar)-1] = '\0';
1758 pbf.pine_anchor = set_titlebar(titlebar,
1759 ps_global->mail_stream,
1760 ps_global->context_current,
1761 ps_global->cur_folder,ps_global->msgmap,
1762 0, FolderName, 0, 0, NULL);
1763 pbf.pine_flags |= P_NOBODY;
1765 /* An informational message */
1766 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
1767 int lines_avail;
1768 char *t1 =
1769 /* TRANSLATORS: The next few lines go together to explain how to add
1770 the address book entry the user is working on. */
1771 _(" To add a local address book that will be accessed *only* by Alpine running\n on this machine, leave the server field blank.");
1772 char *t2 =
1773 _(" To add an address book that will be accessed by IMAP, fill in the\n server name.");
1774 char *t3 =
1775 _(" (NOTE: An address book cannot be accessed by IMAP from\n one Alpine and as a local file from another. It is either always accessed\n by IMAP or it is a local address book which is never accessed by IMAP.)");
1776 char *t4 =
1777 _(" In the Folder field, type the remote folder name or local file name.");
1778 char *t5 =
1779 _(" In the Nickname field, give the address book a nickname or leave it blank.");
1780 char *t6 =
1781 _(" To get help specific to an item, press ^G.");
1782 char *t7 =
1783 _(" To exit and save the configuration, press ^X. To cancel, press ^C.");
1785 pbf.msgtext = (void *)so_text(msgso);
1787 * It's nice if we can make it so these lines make sense even if
1788 * they don't all make it on the screen, because the user can't
1789 * scroll down to see them.
1791 * The 3 is the number of fields to be defined, the 1 is for a
1792 * single blank line after the field definitions.
1794 lines_avail = ps_global->ttyo->screen_rows - HEADER_ROWS(ps_global) -
1795 FOOTER_ROWS(ps_global) - 3 - 1;
1797 if(lines_avail >= 15){ /* extra blank line */
1798 so_puts(msgso, "\n");
1799 lines_avail--;
1802 if(lines_avail >= 2){
1803 so_puts(msgso, t1);
1804 lines_avail -= 2;
1807 if(lines_avail >= 5){
1808 so_puts(msgso, "\n\n");
1809 so_puts(msgso, t2);
1810 so_puts(msgso, t3);
1811 lines_avail -= 5;
1813 else if(lines_avail >= 3){
1814 so_puts(msgso, "\n\n");
1815 so_puts(msgso, t2);
1816 lines_avail -= 3;
1819 if(lines_avail >= 2){
1820 so_puts(msgso, "\n\n");
1821 so_puts(msgso, t4);
1822 lines_avail -= 2;
1825 if(lines_avail >= 2){
1826 so_puts(msgso, "\n\n");
1827 so_puts(msgso, t5);
1828 lines_avail -= 2;
1831 if(lines_avail >= 3){
1832 so_puts(msgso, "\n\n");
1833 so_puts(msgso, t6);
1834 so_puts(msgso, "\n");
1835 so_puts(msgso, t7);
1837 else if(lines_avail >= 2){
1838 so_puts(msgso, "\n\n");
1839 so_puts(msgso, t7);
1843 he = (struct headerentry *)fs_get((NN_END+1) * sizeof(struct headerentry));
1844 memset((void *)he, 0, (NN_END+1) * sizeof(struct headerentry));
1845 pbf.headents = he;
1847 abook_indent = utf8_width(_("Server Name")) + 2;
1849 /* make a copy of each field */
1850 server = cpystr(def_serv ? def_serv : "");
1851 he[NN_SERVER] = headents_for_add[NN_SERVER];
1852 he[NN_SERVER].realaddr = &server;
1853 utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", abook_indent, abook_indent, _("Server Name"));
1854 he[NN_SERVER].prompt = servpmt;
1855 he[NN_SERVER].prwid = abook_indent+2;
1857 folder = cpystr(def_fold ? def_fold : "");
1858 he[NN_FOLDER] = headents_for_add[NN_FOLDER];
1859 he[NN_FOLDER].realaddr = &folder;
1860 utf8_snprintf(foldpmt, sizeof(foldpmt), "%-*.*w: ", abook_indent, abook_indent, _("Folder Name"));
1861 he[NN_FOLDER].prompt = foldpmt;
1862 he[NN_FOLDER].prwid = abook_indent+2;
1864 nickname = cpystr(def_nick ? def_nick : "");
1865 he[NN_NICK] = headents_for_add[NN_NICK];
1866 he[NN_NICK].realaddr = &nickname;
1867 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
1868 he[NN_NICK].prompt = nickpmt;
1869 he[NN_NICK].prwid = abook_indent+2;
1871 he[NN_END] = headents_for_add[NN_END];
1873 /* pass to pico and let user change them */
1874 editor_result = pico(&pbf);
1875 standard_picobuf_teardown(&pbf);
1877 if(editor_result & COMP_GOTHUP){
1878 ret = -1;
1879 hup_signal();
1881 else{
1882 fix_windsize(ps_global);
1883 init_signals();
1886 if(editor_result & COMP_CANCEL){
1887 ret = -1;
1888 if(edit)
1889 q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
1890 else
1891 q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
1893 else if(editor_result & COMP_EXIT){
1894 if(edit &&
1895 !strcmp(server, def_serv ? def_serv : "") &&
1896 !strcmp(folder, def_fold ? def_fold : "") &&
1897 !strcmp(nickname, def_nick ? def_nick : "")){
1898 ret = -1;
1899 if(edit)
1900 q_status_message(SM_ORDER, 0, 3, _("No change: Address book change is cancelled"));
1901 else
1902 q_status_message(SM_ORDER, 0, 3, _("No change: Address book add is cancelled"));
1904 else{
1905 if(global){
1906 list = VAR_GLOB_ADDRBOOK;
1907 how_many_in_list = as.n_addrbk - as.how_many_personals;
1908 if(edit)
1909 new_abook_num = abook_num;
1910 else if(abook_num < 0)
1911 new_abook_num = as.n_addrbk;
1912 else
1913 new_abook_num = MAX(MIN(abook_num + 1, as.n_addrbk),
1914 as.how_many_personals);
1916 num_in_list = new_abook_num - as.how_many_personals;
1918 else{
1919 list = VAR_ADDRESSBOOK;
1920 how_many_in_list = as.how_many_personals;
1921 new_abook_num = abook_num;
1922 if(edit)
1923 new_abook_num = abook_num;
1924 else if(abook_num < 0)
1925 new_abook_num = as.how_many_personals;
1926 else
1927 new_abook_num = MIN(abook_num + 1, as.how_many_personals);
1929 num_in_list = new_abook_num;
1932 if(!edit)
1933 how_many_in_list++; /* for new abook */
1935 removing_leading_and_trailing_white_space(server);
1936 removing_leading_and_trailing_white_space(folder);
1937 removing_leading_and_trailing_white_space(nickname);
1939 /* convert nickname to UTF-8 */
1940 if(nickname){
1941 char *conv;
1943 conv = convert_to_utf8(nickname, NULL, 0);
1944 if(conv){
1945 fs_give((void **) &nickname);
1946 nickname = conv;
1950 /* eliminate surrounding brackets */
1951 if(server[0] == '{' && server[strlen(server)-1] == '}'){
1952 char *p;
1954 server[strlen(server)-1] = '\0';
1955 for(p = server; *p; p++)
1956 *p = *(p+1);
1959 snprintf(tmp, sizeof(tmp), "%s%s%s%.*s",
1960 *server ? "{" : "",
1961 *server ? server : "",
1962 *server ? "}" : "",
1963 MAXFOLDER, folder);
1964 tmp[sizeof(tmp)-1] = '\0';
1966 new_item = put_pair(nickname, tmp);
1968 if(!new_item || *new_item == '\0'){
1969 if(edit)
1970 q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
1971 else
1972 q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
1974 ret = -1;
1975 goto get_out;
1978 /* allocate for new list */
1979 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
1981 /* copy old list up to where we will insert new entry */
1982 for(i = 0; i < num_in_list; i++)
1983 new_list[i] = cpystr(list[i]);
1985 /* insert the new entry */
1986 new_list[i++] = cpystr(new_item);
1988 /* copy rest of old list, skip current if editing */
1989 for(; i < how_many_in_list; i++)
1990 new_list[i] = cpystr(list[edit ? i : (i-1)]);
1992 new_list[i] = NULL;
1994 /* this frees old variable contents for us */
1995 if(set_variable_list(global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK,
1996 new_list, TRUE, ew)){
1997 if(edit)
1998 q_status_message(SM_ORDER, 0, 3, _("Change cancelled: couldn't save configuration file"));
1999 else
2000 q_status_message(SM_ORDER, 0, 3, _("Add cancelled: couldn't save configuration file"));
2002 set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
2003 TRUE, FALSE);
2004 ret = -1;
2005 goto get_out;
2008 ret = new_abook_num;
2009 set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
2010 TRUE, FALSE);
2012 addrbook_reset();
2013 init_ab_if_needed();
2016 * Test to see if this definition is going to work.
2017 * Error messages are a good side effect.
2019 pab = &as.adrbks[num_in_list];
2020 init_abook(pab, NoDisplay);
2021 remember_access_result = pab->access;
2022 addrbook_reset();
2023 init_ab_if_needed();
2024 /* if we had trouble, give a clue to user (other than error msg) */
2025 if(remember_access_result == NoAccess){
2026 pab = &as.adrbks[num_in_list];
2027 pab->access = remember_access_result;
2032 get_out:
2034 if(he)
2035 free_headents(&he);
2037 if(new_list)
2038 free_list_array(&new_list);
2040 if(new_item)
2041 fs_give((void **)&new_item);
2043 if(msgso)
2044 so_give(&msgso);
2046 if(server)
2047 fs_give((void **)&server);
2048 if(folder)
2049 fs_give((void **)&folder);
2050 if(nickname)
2051 fs_give((void **)&nickname);
2053 return(ret);
2058 any_addrbooks_to_convert(struct pine *ps)
2060 PerAddrBook *pab;
2061 int i, count = 0;
2063 init_ab_if_needed();
2065 for(i = 0; i < as.n_addrbk; i++){
2066 pab = &as.adrbks[i];
2067 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2068 count++;
2071 return(count);
2076 convert_addrbooks_to_remote(struct pine *ps, char *rem_folder_prefix, size_t len)
2078 PerAddrBook *pab;
2079 int i, count = 0, ret = 0;
2081 init_ab_if_needed();
2083 for(i = 0; i < as.n_addrbk; i++){
2084 pab = &as.adrbks[i];
2085 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2086 count++;
2089 for(i = 0; ret != -1 && i < as.n_addrbk; i++){
2090 pab = &as.adrbks[i];
2091 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2092 ret = convert_abook_to_remote(ps, pab, rem_folder_prefix, len, count);
2095 return(ret);
2100 * Returns -1 if cancelled, -2 on error, 0 otherwise.
2103 convert_abook_to_remote(struct pine *ps, PerAddrBook *pab, char *rem_folder_prefix, size_t len, int count)
2105 #define DEF_ABOOK_NAME "remote_addrbook"
2106 char local_file[MAILTMPLEN];
2107 char rem_abook[MAILTMPLEN+3], prompt[MAILTMPLEN], old_nick[MAILTMPLEN];
2108 char *p = NULL, *err_msg = NULL, *q;
2109 char *serv = NULL, *nick = NULL, *file = NULL, *folder = NULL;
2110 int ans, rc, offset, i, abook_num = -1, flags = OE_APPEND_CURRENT;
2111 HelpType help;
2113 snprintf(old_nick, sizeof(old_nick), "%s%s%s",
2114 count > 1 ? " \"" : "",
2115 count > 1 ? pab->abnick : "",
2116 count > 1 ? "\"" : "");
2117 old_nick[sizeof(old_nick)-1] = '\0';
2119 snprintf(prompt, sizeof(prompt), _("Convert addressbook%s to a remote addrbook "), old_nick);
2120 prompt[sizeof(prompt)-1] = '\0';
2121 if((ans=want_to(prompt, 'y', 'x', h_convert_abook, WT_NORM)) != 'y')
2122 return(ans == 'n' ? 0 : -1);
2124 /* make sure the addrbook has been opened before, so that the file exists */
2125 if(pab->ostatus == Closed || pab->ostatus == HalfOpen){
2126 (void)init_addrbooks(NoDisplay, 0, 0, 0);
2127 (void)init_addrbooks(Closed, 0, 0, 0);
2130 if(pab->filename){
2131 strncpy(local_file, pab->filename, sizeof(local_file)-1);
2132 local_file[sizeof(local_file)-1] = '\0';
2133 #if defined(DOS)
2134 p = strrindex(pab->filename, '\\');
2135 #else
2136 p = strrindex(pab->filename, '/');
2137 #endif
2140 strncpy(rem_abook, rem_folder_prefix, sizeof(rem_abook)-3);
2141 if(!*rem_abook){
2142 /* TRANSLATORS: The user is defining an address book which will be
2143 stored on another server. This is called a remote addrbook and
2144 this is a question asking for the name of the server. */
2145 snprintf(prompt, sizeof(prompt), _("Name of server to contain remote addrbook : "));
2146 prompt[sizeof(prompt)-1] = '\0';
2147 help = NO_HELP;
2148 while(1){
2149 rc = optionally_enter(rem_abook, -FOOTER_ROWS(ps), 0,
2150 sizeof(rem_abook), prompt, NULL,
2151 help, &flags);
2152 removing_leading_and_trailing_white_space(rem_abook);
2153 if(rc == 3){
2154 help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
2156 else if(rc == 1){
2157 cmd_cancelled(NULL);
2158 return(-1);
2160 else if(rc == 0){
2161 if(*rem_abook){
2162 /* add brackets */
2163 offset = strlen(rem_abook);
2164 for(i = offset; i >= 0; i--)
2165 rem_abook[i+1] = rem_abook[i];
2167 rem_abook[0] = '{';
2168 rem_abook[++offset] = '}';
2169 rem_abook[++offset] = '\0';
2170 break;
2176 if(*rem_abook){
2177 if(p && count > 1)
2178 strncat(rem_abook, p+1,
2179 sizeof(rem_abook)-1-strlen(rem_abook));
2180 else
2181 strncat(rem_abook, DEF_ABOOK_NAME,
2182 sizeof(rem_abook)-1-strlen(rem_abook));
2185 if(*rem_abook){
2186 file = cpystr(rem_abook);
2187 if(pab->abnick){
2188 nick = (char *)fs_get((MAX(strlen(pab->abnick),strlen("Address Book"))+8) * sizeof(char));
2189 snprintf(nick, sizeof(nick), "Remote %s",
2190 (pab->abnick && !strcmp(pab->abnick, DF_ADDRESSBOOK))
2191 ? "Address Book" : pab->abnick);
2192 nick[sizeof(nick)-1] = '\0';
2194 else
2195 nick = cpystr("Remote Address Book");
2197 if(file && *file == '{'){
2198 q = file + 1;
2199 if((p = strindex(file, '}'))){
2200 *p = '\0';
2201 serv = q;
2202 folder = p+1;
2204 else if(file)
2205 fs_give((void **)&file);
2207 else
2208 folder = file;
2211 q_status_message(SM_ORDER, 3, 5,
2212 _("You now have a chance to change the name of the remote addrbook..."));
2213 abook_num = ab_modify_abook_list(0, 0, -1, serv, folder, nick);
2215 /* extract folder name of new abook so we can copy to it */
2216 if(abook_num >= 0){
2217 char **lval;
2218 EditWhich ew = Main;
2220 lval = LVAL(&ps->vars[V_ADDRESSBOOK], ew);
2221 get_pair(lval[abook_num], &nick, &file, 0, 0);
2222 if(nick)
2223 fs_give((void **)&nick);
2225 if(file){
2226 strncpy(rem_abook, file, sizeof(rem_abook)-1);
2227 rem_abook[sizeof(rem_abook)-1] = '\0';
2228 fs_give((void **)&file);
2232 /* copy the abook */
2233 if(abook_num >= 0 && copy_abook(local_file, rem_abook, &err_msg)){
2234 if(err_msg){
2235 q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
2236 fs_give((void **)&err_msg);
2239 return(-2);
2241 else if(abook_num >= 0){ /* give user some info */
2242 STORE_S *store;
2243 SCROLL_S sargs;
2244 char *beg, *end;
2247 * Save the hostname in rem_folder_prefix so we can use it again
2248 * for other conversions if needed.
2250 if((beg = rem_abook)
2251 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
2252 && (end = strindex(rem_abook, '}'))){
2253 rem_folder_prefix[0] = '{';
2254 strncpy(rem_folder_prefix+1, beg+1, MIN(end-beg,len-2));
2255 rem_folder_prefix[MIN(end-beg,len-2)] = '}';
2256 rem_folder_prefix[MIN(end-beg+1,len-1)] = '\0';
2259 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2260 q_status_message(SM_ORDER | SM_DING, 7, 10,
2261 _("Error allocating space for message."));
2262 return(-2);
2265 /* TRANSLATORS: Several lines in a row here that go together. */
2266 snprintf(prompt, sizeof(prompt), _("\nYour addressbook%s has been copied to the"), old_nick);
2267 prompt[sizeof(prompt)-1] = '\0';
2268 so_puts(store, prompt);
2269 so_puts(store, _("\nremote folder \""));
2270 so_puts(store, rem_abook);
2271 so_puts(store, "\".");
2272 so_puts(store, _("\nA definition for this remote address book has been added to your list"));
2273 so_puts(store, _("\nof address books. The definition for the address book it was copied"));
2274 so_puts(store, _("\nfrom is also still there. You may want to remove that after you"));
2275 so_puts(store, _("\nare confident that the new address book is complete and working."));
2276 so_puts(store, _("\nUse the Setup/AddressBooks command to do that.\n"));
2278 memset(&sargs, 0, sizeof(SCROLL_S));
2279 sargs.text.text = so_text(store);
2280 sargs.text.src = CharStar;
2281 sargs.text.desc = _("Remote Address Book Information");
2282 /* TRANSLATORS: a screen title */
2283 sargs.bar.title = _("ABOUT REMOTE ABOOK");
2284 sargs.help.text = NO_HELP;
2285 sargs.help.title = NULL;
2287 scrolltool(&sargs);
2289 so_give(&store); /* free resources associated with store */
2290 ps->mangled_screen = 1;
2293 return(0);
2298 any_sigs_to_convert(struct pine *ps)
2300 char *sigfile, *litsig;
2301 long rflags;
2302 PAT_STATE pstate;
2303 PAT_S *pat;
2304 PAT_LINE_S *patline;
2306 /* first check main signature file */
2307 sigfile = ps->VAR_SIGNATURE_FILE;
2308 litsig = ps->VAR_LITERAL_SIG;
2310 if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
2311 !IS_REMOTE(sigfile))
2312 return(1);
2314 rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
2315 if(any_patterns(rflags, &pstate)){
2316 set_pathandle(rflags);
2317 for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
2318 patline; patline = patline->next){
2319 for(pat = patline->first; pat; pat = pat->next){
2322 * See detoken() for when a sig file is used with a role.
2324 sigfile = pat->action ? pat->action->sig : NULL;
2325 litsig = pat->action ? pat->action->litsig : NULL;
2327 if(sigfile && *sigfile && !litsig &&
2328 sigfile[strlen(sigfile)-1] != '|' &&
2329 !IS_REMOTE(sigfile))
2330 return(1);
2335 return(0);
2340 any_rule_files_to_warn_about(struct pine *ps)
2342 long rflags;
2343 PAT_STATE pstate;
2344 PAT_S *pat;
2346 rflags = (ROLE_DO_ROLES | ROLE_DO_INCOLS | ROLE_DO_SCORES |
2347 ROLE_DO_FILTER | ROLE_DO_OTHER | ROLE_DO_SRCH | PAT_USE_MAIN);
2348 if(any_patterns(rflags, &pstate)){
2349 for(pat = first_pattern(&pstate);
2350 pat;
2351 pat = next_pattern(&pstate)){
2352 if(pat->patline && pat->patline->type == File)
2353 break;
2356 if(pat)
2357 return(1);
2360 return(0);
2365 convert_sigs_to_literal(struct pine *ps, int interactive)
2367 EditWhich ew = Main;
2368 char *sigfile, *litsig, *cstring_version, *nick, *src = NULL;
2369 char prompt[MAILTMPLEN];
2370 STORE_S *store;
2371 SCROLL_S sargs;
2372 long rflags;
2373 int ans;
2374 PAT_STATE pstate;
2375 PAT_S *pat;
2376 PAT_LINE_S *patline;
2378 /* first check main signature file */
2379 sigfile = ps->VAR_SIGNATURE_FILE;
2380 litsig = ps->VAR_LITERAL_SIG;
2382 if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
2383 !IS_REMOTE(sigfile)){
2384 if(interactive){
2385 snprintf(prompt,sizeof(prompt),
2386 /* TRANSLATORS: A literal sig is a way to store a signature
2387 in alpine. It isn't a very descriptive name. Instead of
2388 storing it in its own file it is stored in the configuration
2389 file. */
2390 _("Convert signature file \"%s\" to a literal sig "),
2391 sigfile);
2392 prompt[sizeof(prompt)-1] = '\0';
2393 ClearBody();
2394 ps->mangled_body = 1;
2395 if((ans=want_to(prompt, 'y', 'x', h_convert_sig, WT_NORM)) == 'x'){
2396 cmd_cancelled(NULL);
2397 return(-1);
2400 else
2401 ans = 'y';
2403 if(ans == 'y' && (src = get_signature_file(sigfile, 0, 0, 0)) != NULL){
2404 cstring_version = string_to_cstring(src);
2405 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2407 if(cstring_version)
2408 fs_give((void **)&cstring_version);
2410 fs_give((void **)&src);
2412 if(interactive){
2413 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2414 q_status_message(SM_ORDER | SM_DING, 7, 10,
2415 _("Error allocating space for message."));
2416 return(-1);
2419 snprintf(prompt, sizeof(prompt),
2420 /* TRANSLATORS: following lines go together */
2421 _("\nYour signature file \"%s\" has been converted"), sigfile);
2422 prompt[sizeof(prompt)-1] = '\0';
2423 so_puts(store, prompt);
2424 so_puts(store,
2425 _("\nto a literal signature, which means it is contained in your"));
2426 so_puts(store,
2427 _("\nAlpine configuration instead of being in a file of its own."));
2428 so_puts(store,
2429 _("\nIf that configuration is copied to a remote folder then the"));
2430 so_puts(store,
2431 _("\nsignature will be available remotely also."));
2432 so_puts(store,
2433 _("\nChanges to the signature file itself will no longer have any"));
2434 so_puts(store,
2435 _("\neffect on Alpine but you may still edit the signature with the"));
2436 so_puts(store,
2437 _("\nSetup/Signature command.\n"));
2439 memset(&sargs, 0, sizeof(SCROLL_S));
2440 sargs.text.text = so_text(store);
2441 sargs.text.src = CharStar;
2442 sargs.text.desc = _("Literal Signature Information");
2443 /* TRANSLATORS: screen title */
2444 sargs.bar.title = _("ABOUT LITERAL SIG");
2445 sargs.help.text = NO_HELP;
2446 sargs.help.title = NULL;
2448 scrolltool(&sargs);
2450 so_give(&store);
2451 ps->mangled_screen = 1;
2456 rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
2457 if(any_patterns(rflags, &pstate)){
2458 set_pathandle(rflags);
2459 for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
2460 patline; patline = patline->next){
2461 for(pat = patline->first; pat; pat = pat->next){
2464 * See detoken() for when a sig file is used with a role.
2466 sigfile = pat->action ? pat->action->sig : NULL;
2467 litsig = pat->action ? pat->action->litsig : NULL;
2468 nick = (pat->action && pat->action->nick && pat->action->nick[0]) ? pat->action->nick : NULL;
2470 if(sigfile && *sigfile && !litsig &&
2471 sigfile[strlen(sigfile)-1] != '|' &&
2472 !IS_REMOTE(sigfile)){
2473 if(interactive){
2474 snprintf(prompt,sizeof(prompt),
2475 /* TRANSLATORS: asking whether a signature file should be converted to what
2476 we call a literal signature, which is one contained in the regular
2477 configuration file. Think of the set of 4 %s arguments as a
2478 single argument which is the name of the signature file. */
2479 _("Convert signature file \"%s\"%s%s%s to a literal sig "),
2480 sigfile,
2481 nick ? " in role \"" : "",
2482 nick ? nick : "",
2483 nick ? "\"" : "");
2484 prompt[sizeof(prompt)-1] = '\0';
2485 ClearBody();
2486 ps->mangled_body = 1;
2487 if((ans=want_to(prompt, 'y', 'x',
2488 h_convert_sig, WT_NORM)) == 'x'){
2489 cmd_cancelled(NULL);
2490 return(-1);
2493 else
2494 ans = 'y';
2496 if(ans == 'y' &&
2497 (src = get_signature_file(sigfile,0,0,0)) != NULL){
2499 cstring_version = string_to_cstring(src);
2501 if(pat->action->litsig)
2502 fs_give((void **)&pat->action->litsig);
2504 pat->action->litsig = cstring_version;
2505 fs_give((void **)&src);
2507 set_pathandle(rflags);
2508 if(patline->type == Literal)
2509 (*cur_pat_h)->dirtypinerc = 1;
2510 else
2511 patline->dirty = 1;
2513 if(write_patterns(rflags) == 0){
2514 if(interactive){
2516 * Flush out current_vals of anything we've
2517 * possibly changed.
2519 close_patterns(ROLE_DO_ROLES | PAT_USE_CURRENT);
2521 if(!(store=so_get(CharStar,NULL,EDIT_ACCESS))){
2522 q_status_message(SM_ORDER | SM_DING, 7, 10,
2523 _("Error allocating space for message."));
2524 return(-1);
2527 snprintf(prompt, sizeof(prompt),
2528 /* TRANSLATORS: Keep the %s's together, they are sort of
2529 the name of the file. */
2530 _("Your signature file \"%s\"%s%s%s has been converted"),
2531 sigfile,
2532 nick ? " in role \"" : "",
2533 nick ? nick : "",
2534 nick ? "\"" : "");
2535 prompt[sizeof(prompt)-1] = '\0';
2536 so_puts(store, prompt);
2537 so_puts(store,
2538 /* TRANSLATORS: several lines that go together */
2539 _("\nto a literal signature, which means it is contained in your"));
2540 so_puts(store,
2541 _("\nAlpine configuration instead of being in a file of its own."));
2542 so_puts(store,
2543 _("\nIf that configuration is copied to a remote folder then the"));
2544 so_puts(store,
2545 _("\nsignature will be available remotely also."));
2546 so_puts(store,
2547 _("\nChanges to the signature file itself will no longer have any"));
2548 so_puts(store,
2549 _("\neffect on Alpine. You may edit the signature with the"));
2550 so_puts(store,
2551 _("\nSetup/Rules/Roles command.\n"));
2553 memset(&sargs, 0, sizeof(SCROLL_S));
2554 sargs.text.text = so_text(store);
2555 sargs.text.src = CharStar;
2556 sargs.text.desc =
2557 _("Literal Signature Information");
2558 /* TRANSLATORS: a screen title */
2559 sargs.bar.title = _("ABOUT LITERAL SIG");
2560 sargs.help.text = NO_HELP;
2561 sargs.help.title = NULL;
2563 scrolltool(&sargs);
2565 so_give(&store);
2566 ps->mangled_screen = 1;
2569 else if(interactive){
2570 q_status_message(SM_ORDER | SM_DING, 7, 10,
2571 /* TRANSLATORS: config is an abbreviation for configuration */
2572 _("Error writing rules config."));
2574 else{
2575 /* TRANSLATORS: sig is signature */
2576 fprintf(stderr, _("Error converting role sig\n"));
2577 return(-1);
2585 return(0);
2589 void
2590 warn_about_rule_files(struct pine *ps)
2592 STORE_S *store;
2593 SCROLL_S sargs;
2595 if(any_rule_files_to_warn_about(ps)){
2596 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2597 q_status_message(SM_ORDER | SM_DING, 7, 10,
2598 _("Error allocating space for message."));
2599 return;
2602 /* TRANSLATORS: several lines that go together */
2603 so_puts(store, _("\nSome of your Rules are contained in Rule files instead of being directly"));
2604 so_puts(store, _("\ncontained in your Alpine configuration file. To make those rules"));
2605 so_puts(store, _("\navailable remotely you will need to move them out of the files."));
2606 so_puts(store, _("\nThat can be done using the Shuffle command in the appropriate"));
2607 so_puts(store, _("\nSetup/Rules subcommands.\n"));
2609 memset(&sargs, 0, sizeof(SCROLL_S));
2610 sargs.text.text = so_text(store);
2611 sargs.text.src = CharStar;
2612 sargs.text.desc = _("Rule Files Information");
2613 /* TRANSLATORS: a screen title */
2614 sargs.bar.title = _("ABOUT RULE FILES");
2615 sargs.help.text = NO_HELP;
2616 sargs.help.title = NULL;
2618 scrolltool(&sargs);
2620 so_give(&store);
2621 ps->mangled_screen = 1;
2626 void
2627 convert_to_remote_config(struct pine *ps, int edit_exceptions)
2629 char rem_pinerc_prefix[MAILTMPLEN];
2630 char *beg, *end;
2631 CONTEXT_S *context;
2632 int abooks, sigs;
2634 if(edit_exceptions){
2635 /* TRANSLATORS: The exceptions command (X) was typed but it doesn't make sense */
2636 q_status_message(SM_ORDER, 3, 5,
2637 _("eXceptions does not make sense with this command"));
2638 return;
2641 if(!ps->prc)
2642 alpine_panic("NULL prc in convert_to_remote_config");
2644 dprint((2, "convert_to_remote_config\n"));
2646 if(ps->prc->type == RemImap){ /* pinerc is already remote */
2647 char prompt[MAILTMPLEN];
2650 * Check to see if there is anything at all to do. If there are
2651 * address books to convert, sigfiles to convert, or rule files
2652 * to comment on, we have something to do. Otherwise, just bail.
2654 abooks = any_addrbooks_to_convert(ps);
2655 sigs = any_sigs_to_convert(ps);
2657 if(abooks || sigs){
2658 if(abooks && sigs)
2659 /* TRANSLATORS: AddressBooks is Address Books */
2660 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks and signature files "));
2661 else if(abooks)
2662 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks "));
2663 else
2664 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert signature files "));
2666 prompt[sizeof(prompt)-1] = '\0';
2667 if(want_to(prompt, 'y', 'x',
2668 (abooks && sigs) ? h_convert_abooks_and_sigs :
2669 abooks ? h_convert_abooks :
2670 sigs ? h_convert_sigs : NO_HELP,
2671 WT_NORM) != 'y'){
2672 cmd_cancelled(NULL);
2673 return;
2679 * Figure out a good default for where to put the remote config.
2680 * If the default collection is remote we'll take the hostname and
2681 * and modifiers from there. If not, we'll try to get the hostname from
2682 * the inbox-path. In either case, we use the home directory on the
2683 * server, not the directory where the folder collection is (if different).
2684 * If we don't have a clue, we'll ask user.
2686 if((context = default_save_context(ps->context_list)) != NULL &&
2687 IS_REMOTE(context_apply(rem_pinerc_prefix, context, "",
2688 sizeof(rem_pinerc_prefix)))){
2689 /* just use the host from the default collection, not the whole path */
2690 if((end = strrindex(rem_pinerc_prefix, '}')) != NULL)
2691 *(end + 1) = '\0';
2693 else{
2694 /* use host from inbox path */
2695 rem_pinerc_prefix[0] = '\0';
2696 if((beg = ps->VAR_INBOX_PATH)
2697 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
2698 && (end = strindex(ps->VAR_INBOX_PATH, '}'))){
2699 rem_pinerc_prefix[0] = '{';
2700 strncpy(rem_pinerc_prefix+1, beg+1,
2701 MIN(end-beg, sizeof(rem_pinerc_prefix)-2));
2702 rem_pinerc_prefix[MIN(end-beg, sizeof(rem_pinerc_prefix)-2)] = '}';
2703 rem_pinerc_prefix[MIN(end-beg+1,sizeof(rem_pinerc_prefix)-1)]='\0';
2707 /* ask about converting addrbooks to remote abooks */
2708 if(ps->prc->type != RemImap || abooks)
2709 if(convert_addrbooks_to_remote(ps, rem_pinerc_prefix,
2710 sizeof(rem_pinerc_prefix)) == -1){
2711 cmd_cancelled(NULL);
2712 return;
2715 /* ask about converting sigfiles to literal sigs */
2716 if(ps->prc->type != RemImap || sigs)
2717 if(convert_sigs_to_literal(ps, 1) == -1){
2718 cmd_cancelled(NULL);
2719 return;
2722 warn_about_rule_files(ps);
2724 /* finally, copy the config file */
2725 if(ps->prc->type == Loc)
2726 convert_pinerc_to_remote(ps, rem_pinerc_prefix);
2727 else if(!(abooks || sigs))
2728 q_status_message(SM_ORDER, 3, 5,
2729 _("Cannot copy config file since it is already remote."));
2733 void
2734 convert_pinerc_to_remote(struct pine *ps, char *rem_pinerc_prefix)
2736 #define DEF_FOLDER_NAME "remote_pinerc"
2737 char prompt[MAILTMPLEN], rem_pinerc[MAILTMPLEN];
2738 char *err_msg = NULL;
2739 int i, rc, offset;
2740 HelpType help;
2741 int flags = OE_APPEND_CURRENT;
2743 ClearBody();
2744 ps->mangled_body = 1;
2745 strncpy(rem_pinerc, rem_pinerc_prefix, sizeof(rem_pinerc)-1);
2746 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2748 if(*rem_pinerc == '\0'){
2749 snprintf(prompt, sizeof(prompt), _("Name of server to contain remote Alpine config : "));
2750 prompt[sizeof(prompt)-1] = '\0';
2751 help = NO_HELP;
2752 while(1){
2753 rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
2754 sizeof(rem_pinerc), prompt, NULL,
2755 help, &flags);
2756 removing_leading_and_trailing_white_space(rem_pinerc);
2757 if(rc == 3){
2758 help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
2760 else if(rc == 1){
2761 cmd_cancelled(NULL);
2762 return;
2764 else if(rc == 0){
2765 if(*rem_pinerc){
2766 /* add brackets */
2767 offset = strlen(rem_pinerc);
2768 for(i = offset; i >= 0; i--)
2769 if(i+1 < sizeof(rem_pinerc))
2770 rem_pinerc[i+1] = rem_pinerc[i];
2772 rem_pinerc[0] = '{';
2773 if(offset+2 < sizeof(rem_pinerc)){
2774 rem_pinerc[++offset] = '}';
2775 rem_pinerc[++offset] = '\0';
2778 break;
2784 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2787 * Add a default folder name.
2789 if(*rem_pinerc){
2791 * Add /user= to modify hostname so that user won't be asked who they
2792 * are each time they login.
2794 if(!strstr(rem_pinerc, "/user=") && ps->VAR_USER_ID &&
2795 ps->VAR_USER_ID[0]){
2796 char *p;
2798 p = rem_pinerc + strlen(rem_pinerc) - 1;
2799 if(*p == '}') /* this should be the case */
2800 snprintf(p, sizeof(rem_pinerc)-(p-rem_pinerc), "/user=\"%s\"}", ps->VAR_USER_ID);
2802 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2805 strncat(rem_pinerc, DEF_FOLDER_NAME,
2806 sizeof(rem_pinerc) - strlen(rem_pinerc) - 1);
2807 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2810 /* ask user about folder name for remote config */
2811 snprintf(prompt, sizeof(prompt), _("Folder to contain remote config : "));
2812 prompt[sizeof(prompt)-1] = '\0';
2813 help = NO_HELP;
2814 while(1){
2815 rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
2816 sizeof(rem_pinerc), prompt, NULL, help, &flags);
2817 removing_leading_and_trailing_white_space(rem_pinerc);
2818 if(rc == 0 && *rem_pinerc){
2819 break;
2822 if(rc == 3){
2823 help = (help == NO_HELP) ? h_convert_pinerc_folder : NO_HELP;
2825 else if(rc == 1 || rem_pinerc[0] == '\0'){
2826 cmd_cancelled(NULL);
2827 return;
2831 #ifndef _WINDOWS
2833 * If we are on a Unix system, writing to a remote config, we want the
2834 * remote config to work smoothly from a PC, too. If we don't have a
2835 * user-id on the PC then we will be asked for our password.
2836 * So add user-id to the pinerc before we copy it.
2838 if(!ps->vars[V_USER_ID].main_user_val.p && ps->VAR_USER_ID)
2839 ps->vars[V_USER_ID].main_user_val.p = cpystr(ps->VAR_USER_ID);
2841 ps->vars[V_USER_ID].is_used = 1; /* so it will write to pinerc */
2842 ps->prc->outstanding_pinerc_changes = 1;
2843 #endif
2845 if(ps->prc->outstanding_pinerc_changes)
2846 write_pinerc(ps, Main, WRP_NONE);
2848 #ifndef _WINDOWS
2849 ps->vars[V_USER_ID].is_used = 0;
2850 #endif
2852 /* copy the pinerc */
2853 if(copy_pinerc(ps->prc->name, rem_pinerc, &err_msg)){
2854 if(err_msg){
2855 q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
2856 fs_give((void **)&err_msg);
2859 return;
2862 /* tell user about command line flags */
2863 if(ps->prc->type != RemImap){
2864 STORE_S *store;
2865 SCROLL_S sargs;
2867 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2868 q_status_message(SM_ORDER | SM_DING, 7, 10,
2869 _("Error allocating space for message."));
2870 return;
2873 /* TRANSLATORS: several lines that go together */
2874 so_puts(store, _("\nYou may want to save a copy of this information!"));
2875 so_puts(store, _("\n\nYour Alpine configuration data has been copied to"));
2876 so_puts(store, "\n\n ");
2877 so_puts(store, rem_pinerc);
2878 so_puts(store, "\n");
2879 so_puts(store, _("\nTo use that remote configuration from this computer you will"));
2880 so_puts(store, _("\nhave to change the way you start Alpine by using the command line option"));
2881 so_puts(store, _("\n\"alpine -p <remote_folder>\". The command should probably be"));
2882 #ifdef _WINDOWS
2883 so_puts(store, "\n\n ");
2884 so_puts(store, "alpine -p ");
2885 so_puts(store, rem_pinerc);
2886 so_puts(store, "\n");
2887 so_puts(store, _("\nWith PC-Alpine, you may want to create a shortcut which"));
2888 so_puts(store, _("\nhas the required arguments."));
2889 #else
2890 so_puts(store, "\n\n ");
2891 so_puts(store, "alpine -p \"");
2892 so_puts(store, rem_pinerc);
2893 so_puts(store, "\"\n");
2894 so_puts(store, _("\nThe quotes are there around the last argument to protect the special"));
2895 so_puts(store, _("\ncharacters in the folder name (like braces) from the command shell"));
2896 so_puts(store, _("\nyou use. If you are not running Alpine from a command shell which knows"));
2897 so_puts(store, _("\nabout quoting, it is possible you will have to remove those quotes"));
2898 so_puts(store, _("\nfrom the command. For example, if you also use PC-Alpine you will probably"));
2899 so_puts(store, _("\nwant to create a shortcut, and you would not need the quotes there."));
2900 so_puts(store, _("\nWithout the quotes, the command might look like"));
2901 so_puts(store, "\n\n ");
2902 so_puts(store, "alpine -p ");
2903 so_puts(store, rem_pinerc);
2904 so_puts(store, "\n");
2905 so_puts(store, _("\nConsider creating an alias or shell script to execute this command to make"));
2906 so_puts(store, _("\nit more convenient."));
2907 #endif
2908 so_puts(store, _("\n\nIf you want to use your new remote configuration for this session, quit"));
2909 so_puts(store, _("\nAlpine now and restart with the changed command line options mentioned above.\n"));
2911 memset(&sargs, 0, sizeof(SCROLL_S));
2912 sargs.text.text = so_text(store);
2913 sargs.text.src = CharStar;
2914 sargs.text.desc = _("Remote Config Information");
2915 /* TRANSLATORS: a screen title */
2916 sargs.bar.title = _("ABOUT REMOTE CONFIG");
2917 sargs.help.text = NO_HELP;
2918 sargs.help.title = NULL;
2920 scrolltool(&sargs);
2922 so_give(&store); /* free resources associated with store */
2923 ps->mangled_screen = 1;
2929 verify_folder_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2931 char *tmp;
2933 tmp = cpystr(given ? given : "");
2934 removing_leading_and_trailing_white_space(tmp);
2936 if(expanded)
2937 *expanded = tmp;
2938 else
2939 fs_give((void **)&tmp);
2941 if(error)
2942 *error = cpystr("");
2944 return 0;
2949 verify_server_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2951 char *tmp;
2953 tmp = cpystr(given ? given : "");
2954 removing_leading_and_trailing_white_space(tmp);
2956 if(*tmp){
2958 * could try to verify the hostname here
2962 if(expanded)
2963 *expanded = tmp;
2964 else
2965 fs_give((void **)&tmp);
2967 if(error)
2968 *error = cpystr("");
2970 return 0;
2975 verify_abook_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2977 int i;
2978 char *tmp;
2980 tmp = cpystr(given ? given : "");
2981 removing_leading_and_trailing_white_space(tmp);
2983 if(strindex(tmp, '"')){
2984 fs_give((void **)&tmp);
2985 if(error)
2986 /* TRANSLATORS: Double quote refers to the " character */
2987 *error = cpystr(_("Double quote not allowed in nickname"));
2989 return -2;
2992 for(i = 0; i < as.n_addrbk; i++)
2993 if(i != the_one_were_editing && !strcmp(tmp, as.adrbks[i].abnick))
2994 break;
2996 if(i < as.n_addrbk){
2997 fs_give((void **)&tmp);
2999 if(error)
3000 *error = cpystr(_("Nickname is already being used"));
3002 return -2;
3005 if(expanded)
3006 *expanded = tmp;
3007 else
3008 fs_give((void **)&tmp);
3010 if(error)
3011 *error = cpystr("");
3013 return 0;
3018 * Delete an addressbook.
3020 * Args: cur_line -- The current line position (in global display list)
3021 * of cursor
3022 * command_line -- The screen line on which to prompt
3023 * err -- Points to error message
3025 * Returns -- 0, deleted addrbook
3026 * -1, addrbook not deleted
3029 ab_del_abook(long int cur_line, int command_line, char **err)
3031 int abook_num, varnum, delete_data = 0,
3032 num_in_list, how_many_in_list, i, cnt, warn_about_revert = 0;
3033 char **list, **new_list, **t, **lval;
3034 char tmp[200];
3035 PerAddrBook *pab;
3036 struct variable *vars = ps_global->vars;
3037 EditWhich ew;
3038 enum {NotSet,
3039 Modify,
3040 RevertToDefault,
3041 OverRideDefault,
3042 DontChange} modify_config;
3044 /* restrict address book config to normal config file */
3045 ew = Main;
3047 if(ps_global->readonly_pinerc){
3048 if(err)
3049 *err = _("Delete cancelled: config file not changeable");
3051 return -1;
3054 abook_num = adrbk_num_from_lineno(cur_line);
3056 pab = &as.adrbks[abook_num];
3058 dprint((2, "- ab_del_abook(%s) -\n",
3059 pab->abnick ? pab->abnick : "?"));
3061 varnum = (pab->type & GLOBAL) ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK;
3063 if(vars[varnum].is_fixed){
3064 if(err){
3065 if(pab->type & GLOBAL)
3066 *err =
3067 _("Cancelled: Sys. Mgmt. does not allow changing global address book config");
3068 else
3069 *err =
3070 _("Cancelled: Sys. Mgmt. does not allow changing address book config");
3073 return -1;
3077 * Deal with reverting to default values of the address book
3078 * variables, or with user deleting a default value.
3080 modify_config = NotSet;
3082 /* First count how many address books are in the user's config. */
3083 cnt = 0;
3084 lval = LVAL(&vars[varnum], ew);
3085 if(lval && lval[0])
3086 for(t = lval; *t != NULL; t++)
3087 cnt++;
3090 * Easy case, we can just delete one from the user's list.
3092 if(cnt > 1){
3093 modify_config = Modify;
3096 * Also easy. We'll revert to the default if it exists, and warn
3097 * the user about that.
3099 else if(cnt == 1){
3100 modify_config = RevertToDefault;
3101 /* see if there's a default to revert to */
3102 cnt = 0;
3103 if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
3104 for(t = vars[varnum].global_val.l; *t != NULL; t++)
3105 cnt++;
3107 warn_about_revert = cnt;
3110 * User is already using the default. Split it into two cases. If there
3111 * is one address book in default ask user if they want to delete that
3112 * default from their config. If there is more than one, ask them if
3113 * they want to ignore all the defaults or just delete this one.
3115 else{
3116 /* count how many in default */
3117 cnt = 0;
3118 if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
3119 for(t = vars[varnum].global_val.l; *t != NULL; t++)
3120 cnt++;
3122 if(cnt > 1){
3123 static ESCKEY_S opts[] = {
3124 /* TRANSLATORS: Ignore All means ignore all of the default values,
3125 and Remove One means just remove this one default value. */
3126 {'i', 'i', "I", N_("Ignore All")},
3127 {'r', 'r', "R", N_("Remove One")},
3128 {-1, 0, NULL, NULL}};
3130 snprintf(tmp, sizeof(tmp),
3131 /* TRANSLATORS: %s is an adjective modifying address books */
3132 _("Ignore all default %s address books or just remove this one ? "),
3133 /* TRANSLATORS: global or personal address books */
3134 pab->type & GLOBAL ? _("global") : _("personal"));
3135 tmp[sizeof(tmp)-1] = '\0';
3136 switch(radio_buttons(tmp, command_line, opts, 'i', 'x',
3137 h_ab_del_ignore, RB_NORM)){
3138 case 'i':
3139 modify_config = OverRideDefault;
3140 break;
3142 case 'r':
3143 modify_config = Modify;
3144 break;
3146 case 'x':
3147 if(err)
3148 *err = _("Delete cancelled");
3150 return -1;
3153 else{
3154 /* TRANSLATORS: a question */
3155 switch(want_to(_("Delete this default address book from config "),
3156 'n', 'x', h_ab_del_default, WT_NORM)){
3157 case 'n':
3158 case 'x':
3159 if(err)
3160 *err = _("Delete cancelled");
3162 return -1;
3164 case 'y':
3165 modify_config = OverRideDefault;
3166 break;
3172 * ReadWrite means it exists and MaybeRorW means it is remote and we
3173 * haven't selected it yet to know our access permissions. The remote
3174 * folder should have been created, though, unless we didn't even have
3175 * permissions for that, in which case we got some error messages earlier.
3177 if(pab->access == ReadWrite || pab->access == MaybeRorW){
3178 static ESCKEY_S o[] = {
3179 /* TRANSLATORS: user is asked whether to remove just data files, just configuration,
3180 or both for an address book. */
3181 {'d', 'd', "D", N_("Data")},
3182 {'c', 'c', "C", N_("Config")},
3183 {'b', 'b', "B", N_("Both")},
3184 {-1, 0, NULL, NULL}};
3186 switch(radio_buttons(_("Delete data, config, or both ? "),
3187 command_line, o, 'c', 'x',
3188 (modify_config == RevertToDefault)
3189 ? h_ab_del_data_revert
3190 : h_ab_del_data_modify,
3191 RB_NORM)){
3192 case 'b': /* Delete Both */
3193 delete_data = 1;
3194 break;
3196 case 'd': /* Delete only Data */
3197 modify_config = DontChange;
3198 delete_data = 1;
3199 break;
3201 case 'c': /* Delete only Config */
3202 break;
3204 case 'x': /* Cancel */
3205 default:
3206 if(err)
3207 *err = _("Delete cancelled");
3209 return -1;
3212 else{
3214 * Deleting config for address book which doesn't yet exist (hasn't
3215 * ever been opened).
3217 /* TRANSLATORS: a question */
3218 switch(want_to(_("Delete configuration for highlighted addressbook "),
3219 'n', 'x',
3220 (modify_config == RevertToDefault)
3221 ? h_ab_del_config_revert
3222 : h_ab_del_config_modify,
3223 WT_NORM)){
3224 case 'n':
3225 case 'x':
3226 default:
3227 if(err)
3228 *err = _("Delete cancelled");
3230 return -1;
3232 case 'y':
3233 break;
3237 if(delete_data){
3238 char warning[800];
3240 dprint((5, "deleting addrbook data\n"));
3241 warning[0] = '\0';
3244 * In order to delete the address book it is easiest if we open
3245 * it first. That fills in the filenames we want to delete.
3247 if(pab->address_book == NULL){
3248 warning[300] = '\0';
3249 pab->address_book = adrbk_open(pab, ps_global->home_dir,
3250 &warning[300], sizeof(warning)-300,
3251 AB_SORT_RULE_NONE);
3253 * Couldn't get it open.
3255 if(pab->address_book == NULL){
3256 if(warning[300])
3257 /* TRANSLATORS: %s is an error message */
3258 snprintf(warning, 300, _("Can't delete data: %s"), &warning[300]);
3259 else
3260 strncpy(warning, _("Can't delete address book data"), 100);
3265 * If we have it open, set the delete bits and close to get the
3266 * local copies. Delete the remote folder by hand.
3268 if(pab->address_book){
3269 char *file, *origfile = NULL;
3270 int f=0, o=0;
3273 * We're about to destroy addrbook data, better ask again.
3275 if(pab->address_book->count > 0){
3276 char prompt[100];
3278 /* TRANSLATORS: a question */
3279 snprintf(prompt, sizeof(prompt),
3280 _("About to delete the contents of address book (%ld entries), really delete "), (long) adrbk_count(pab->address_book));
3281 prompt[sizeof(prompt)-1] = '\0';
3283 switch(want_to(prompt, 'n', 'n', h_ab_really_delete, WT_NORM)){
3284 case 'y':
3285 break;
3287 case 'n':
3288 default:
3289 if(err)
3290 *err = _("Delete cancelled");
3292 return -1;
3296 pab->address_book->flags |= DEL_FILE;
3297 file = cpystr(pab->address_book->filename);
3298 if(pab->type & REMOTE_VIA_IMAP)
3299 origfile = cpystr(pab->address_book->orig_filename);
3302 * In order to avoid locking problems when we delete the
3303 * remote folder, we need to actually close the remote stream
3304 * instead of just putting it back in the stream pool.
3305 * So we will remove this stream from the re-usable portion
3306 * of the stream pool by clearing the SP_USEPOOL flag.
3307 * Init_abook(pab, TotallyClosed) via rd_close_remdata is
3308 * going to pine_mail_close it.
3310 if(pab->type & REMOTE_VIA_IMAP
3311 && pab->address_book
3312 && pab->address_book->type == Imap
3313 && pab->address_book->rd
3314 && rd_stream_exists(pab->address_book->rd)){
3316 sp_unflag(pab->address_book->rd->t.i.stream, SP_USEPOOL);
3319 /* This deletes the files because of DEL_ bits we set above. */
3320 init_abook(pab, TotallyClosed);
3323 * Delete the remote folder.
3325 if(pab->type & REMOTE_VIA_IMAP){
3326 REMDATA_S *rd;
3327 int exists;
3329 ps_global->c_client_error[0] = '\0';
3330 if(!pine_mail_delete(NULL, origfile) &&
3331 ps_global->c_client_error[0] != '\0'){
3332 dprint((1, "%s: %s\n", origfile ? origfile : "?",
3333 ps_global->c_client_error));
3336 /* delete line from metadata */
3337 rd = rd_new_remdata(RemImap, origfile, NULL);
3338 rd_write_metadata(rd, 1);
3339 rd_close_remdata(&rd);
3341 /* Check to see if it's still there */
3342 if((exists=folder_exists(NULL, origfile)) &&
3343 (exists != FEX_ERROR)){
3344 o++;
3345 dprint((1, "Trouble deleting %s\n",
3346 origfile ? origfile : "?"));
3350 if(can_access(file, ACCESS_EXISTS) == 0){
3351 f++;
3352 dprint((1, "Trouble deleting %s\n",
3353 file ? file : "?"));
3356 if(f || o){
3357 snprintf(warning, sizeof(warning), _("Trouble deleting data %s%s%s%s"),
3358 f ? file : "",
3359 (f && o) ? (o ? ", " : " and ") : "",
3360 o ? " and " : "",
3361 o ? origfile : "");
3362 warning[sizeof(warning)-1] = '\0';
3365 fs_give((void **) &file);
3366 if(origfile)
3367 fs_give((void **) &origfile);
3370 if(*warning){
3371 q_status_message(SM_ORDER, 3, 3, warning);
3372 dprint((1, "%s\n", warning));
3373 display_message(NO_OP_COMMAND);
3375 else if(modify_config == DontChange)
3376 q_status_message(SM_ORDER, 0, 1, _("Addressbook data deleted"));
3379 if(modify_config == DontChange){
3381 * We return -1 to indicate that the addrbook wasn't deleted (as far
3382 * as we're concerned) but we don't fill in err so that no error
3383 * message will be printed.
3384 * Since the addrbook is still an addrbook we need to reinitialize it.
3386 pab->access = adrbk_access(pab);
3387 if(pab->type & GLOBAL && pab->access != NoAccess)
3388 pab->access = ReadOnly;
3390 init_abook(pab, HalfOpen);
3391 return -1;
3393 else if(modify_config == Modify){
3394 list = vars[varnum].current_val.l;
3395 if(pab->type & GLOBAL){
3396 how_many_in_list = as.n_addrbk - as.how_many_personals - 1;
3397 num_in_list = abook_num - as.how_many_personals;
3399 else{
3400 how_many_in_list = as.how_many_personals - 1;
3401 num_in_list = abook_num;
3404 else if(modify_config == OverRideDefault)
3405 how_many_in_list = 1;
3406 else if(modify_config == RevertToDefault)
3407 how_many_in_list = 0;
3408 else
3409 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_del_abook");
3411 /* allocate for new list */
3412 if(how_many_in_list)
3413 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
3414 else
3415 new_list = NULL;
3418 * This case is both for modifying the users user_val and for the
3419 * case where the user wants to modify the global_val default and
3420 * use the modified version for his or her new user_val. We just
3421 * copy from the existing global_val, deleting the one addrbook
3422 * and put the result in user_val.
3424 if(modify_config == Modify){
3425 /* copy old list up to where we will delete entry */
3426 for(i = 0; i < num_in_list; i++)
3427 new_list[i] = cpystr(list[i]);
3429 /* copy rest of old list */
3430 for(; i < how_many_in_list; i++)
3431 new_list[i] = cpystr(list[i+1]);
3433 new_list[i] = NULL;
3435 else if(modify_config == OverRideDefault){
3436 new_list[0] = cpystr("");
3437 new_list[1] = NULL;
3440 /* this also frees old variable contents for us */
3441 if(set_variable_list(varnum, new_list, TRUE, ew)){
3442 if(err)
3443 *err = _("Delete cancelled: couldn't save pine configuration file");
3445 set_current_val(&vars[varnum], TRUE, FALSE);
3446 free_list_array(&new_list);
3448 return -1;
3451 set_current_val(&vars[varnum], TRUE, FALSE);
3453 if(warn_about_revert){
3454 /* TRANSLATORS: the %s may be "global " or nothing */
3455 snprintf(tmp, sizeof(tmp), _("Reverting to default %saddress books"),
3456 pab->type & GLOBAL ? _("global ") : "");
3457 tmp[sizeof(tmp)-1] = '\0';
3458 q_status_message(SM_ORDER, 3, 4, tmp);
3461 free_list_array(&new_list);
3463 return 0;
3468 * Shuffle addrbooks.
3470 * Args: pab -- Pab from current addrbook.
3471 * slide -- return value, tells how far to slide the cursor. If slide
3472 * is negative, slide it up, if positive, slide it down.
3473 * command_line -- The screen line on which to prompt
3474 * msg -- Points to returned message, if any. Should be freed by
3475 * caller.
3477 * Result: Two address books are swapped in the display order. If the shuffle
3478 * crosses the Personal/Global boundary, then instead of swapping
3479 * two address books the highlighted abook is moved from one section
3480 * to the other.
3481 * >= 0 on success.
3482 * < 0 on failure, no changes.
3483 * > 0 If the return value is greater than zero it means that we've
3484 * reverted one of the variables to its default value. That
3485 * means we've added at least one new addrbook, so the caller
3486 * should reset. The value returned is the number of the
3487 * moved addrbook + 1 (+1 so it won't be confused with zero).
3488 * = 0 If the return value is zero we've just moved addrbooks around.
3489 * No reset need be done.
3492 ab_shuffle(PerAddrBook *pab, int *slide, int command_line, char **msg)
3494 ESCKEY_S opts[3];
3495 char tmp[200];
3496 int i, deefault, rv, target = 0;
3497 int up_into_empty = 0, down_into_empty = 0;
3498 HelpType help;
3499 struct variable *vars = ps_global->vars;
3501 dprint((2, "- ab_shuffle() -\n"));
3503 *slide = 0;
3505 if(ps_global->readonly_pinerc){
3506 if(msg)
3507 *msg = cpystr(_("Shuffle cancelled: config file not changeable"));
3509 return -1;
3512 /* Move it up or down? */
3513 i = 0;
3514 opts[i].ch = 'u';
3515 opts[i].rval = 'u';
3516 opts[i].name = "U";
3517 /* TRANSLATORS: shuffle something Up or Down in a list */
3518 opts[i++].label = N_("Up");
3520 opts[i].ch = 'd';
3521 opts[i].rval = 'd';
3522 opts[i].name = "D";
3523 opts[i++].label = N_("Down");
3525 opts[i].ch = -1;
3526 deefault = 'u';
3528 if(pab->type & GLOBAL){
3529 if(vars[V_GLOB_ADDRBOOK].is_fixed){
3530 if(msg)
3531 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3533 return -1;
3536 if(as.cur == 0){
3537 if(as.config)
3538 up_into_empty++;
3539 else{ /* no up */
3540 opts[0].ch = -2;
3541 deefault = 'd';
3545 if(as.cur == as.n_addrbk - 1) /* no down */
3546 opts[1].ch = -2;
3548 else{
3549 if(vars[V_ADDRESSBOOK].is_fixed){
3550 if(msg)
3551 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book config"));
3553 return -1;
3556 if(as.cur == 0){ /* no up */
3557 opts[0].ch = -2;
3558 deefault = 'd';
3561 if(as.cur == as.n_addrbk - 1){
3562 if(as.config)
3563 down_into_empty++;
3564 else
3565 opts[1].ch = -2; /* no down */
3569 snprintf(tmp, sizeof(tmp), _("Shuffle \"%s\" %s%s%s ? "),
3570 pab->abnick,
3571 (opts[0].ch != -2) ? _("UP") : "",
3572 (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
3573 (opts[1].ch != -2) ? _("DOWN") : "");
3574 tmp[sizeof(tmp)-1] = '\0';
3575 help = (opts[0].ch == -2) ? h_ab_shuf_down
3576 : (opts[1].ch == -2) ? h_ab_shuf_up
3577 : h_ab_shuf;
3579 rv = radio_buttons(tmp, command_line, opts, deefault, 'x',
3580 help, RB_NORM);
3582 ps_global->mangled_footer = 1;
3584 if((rv == 'u' && up_into_empty) || (rv == 'd' && down_into_empty))
3585 target = -1;
3586 else
3587 target = as.cur + (rv == 'u' ? -1 : 1);
3589 if(rv == 'x'){
3590 if(msg)
3591 *msg = cpystr(_("Shuffle cancelled"));
3593 return -1;
3595 else
3596 return(do_the_shuffle(slide, as.cur, target, msg));
3601 * Actually shuffle the config variables and address books structures around.
3603 * Args: anum1, anum2 -- The numbers of the address books
3604 * msg -- Points to returned message, if any.
3606 * Returns: >= 0 on success.
3607 * < 0 on failure, no changes.
3608 * > 0 If the return value is greater than zero it means that we've
3609 * reverted one of the variables to its default value. That
3610 * means we've added at least one new addrbook, so the caller
3611 * should reset. The value returned is the number of the
3612 * moved addrbook + 1 (+1 so it won't be confused with zero).
3613 * = 0 If the return value is zero we've just moved addrbooks around.
3614 * No reset need be done.
3616 * Anum1 is the one that we want to move, anum2 is the one that it will be
3617 * swapped with. When anum1 and anum2 are on the opposite sides of the
3618 * Personal/Global boundary then instead of swapping we just move anum1 to
3619 * the other side of the boundary.
3621 * Anum2 of -1 means it is a swap into the other type of address book, which
3622 * is currently empty.
3625 do_the_shuffle(int *slide, int anum1, int anum2, char **msg)
3627 PerAddrBook *pab;
3628 enum {NotSet, Pers, Glob, Empty} type1, type2;
3629 int i, j, retval = -1;
3630 struct variable *vars = ps_global->vars;
3631 char **lval;
3632 EditWhich ew;
3633 char *cancel_msg = _("Shuffle cancelled: couldn't save configuration file");
3635 dprint((5, "- do_the_shuffle(%d, %d) -\n", anum1, anum2));
3637 /* restrict address book config to normal config file */
3638 ew = Main;
3640 if(anum1 == -1)
3641 type1 = Empty;
3642 else{
3643 pab = &as.adrbks[anum1];
3644 type1 = (pab->type & GLOBAL) ? Glob : Pers;
3647 if(type1 == Empty){
3648 if(msg)
3649 *msg =
3650 cpystr(_("Shuffle cancelled: highlight entry you wish to shuffle"));
3652 return(retval);
3655 if(anum2 == -1)
3656 type2 = Empty;
3657 else{
3658 pab = &as.adrbks[anum2];
3659 type2 = (pab->type & GLOBAL) ? Glob : Pers;
3662 if(type2 == Empty)
3663 type2 = (type1 == Pers) ? Glob : Pers;
3665 if((type1 == Pers || type2 == Pers) && vars[V_ADDRESSBOOK].is_fixed){
3666 if(msg)
3667 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book configuration"));
3669 return(retval);
3672 if((type1 == Glob || type2 == Glob) && vars[V_GLOB_ADDRBOOK].is_fixed){
3673 if(msg)
3674 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3676 return(retval);
3680 * There are two cases. If the shuffle is two address books within the
3681 * same variable, then they just swap places. If it is a shuffle of an
3682 * addrbook from one side of the boundary to the other, just that one
3683 * is moved.
3685 if((type1 == Glob && type2 == Glob) ||
3686 (type1 == Pers && type2 == Pers)){
3687 int how_many_in_list, varnum;
3688 int anum1_rel, anum2_rel; /* position in specific list */
3689 char **list, **new_list;
3690 PerAddrBook tmppab;
3692 *slide = (anum1 < anum2) ? LINES_PER_ABOOK : -1 * LINES_PER_ABOOK;
3694 if(type1 == Pers){
3695 how_many_in_list = as.how_many_personals;
3696 list = VAR_ADDRESSBOOK;
3697 varnum = V_ADDRESSBOOK;
3698 anum1_rel = anum1;
3699 anum2_rel = anum2;
3701 else{
3702 how_many_in_list = as.n_addrbk - as.how_many_personals;
3703 list = VAR_GLOB_ADDRBOOK;
3704 varnum = V_GLOB_ADDRBOOK;
3705 anum1_rel = anum1 - as.how_many_personals;
3706 anum2_rel = anum2 - as.how_many_personals;
3709 /* allocate for new list, same size as old list */
3710 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
3712 /* fill in new_list */
3713 for(i = 0; i < how_many_in_list; i++){
3714 /* swap anum1 and anum2 */
3715 if(i == anum1_rel)
3716 j = anum2_rel;
3717 else if(i == anum2_rel)
3718 j = anum1_rel;
3719 else
3720 j = i;
3722 new_list[i] = cpystr(list[j]);
3725 new_list[i] = NULL;
3727 if(set_variable_list(varnum, new_list, TRUE, ew)){
3728 if(msg)
3729 *msg = cpystr(cancel_msg);
3731 /* restore old values */
3732 set_current_val(&vars[varnum], TRUE, FALSE);
3733 free_list_array(&new_list);
3734 return(retval);
3737 retval = 0;
3738 set_current_val(&vars[varnum], TRUE, FALSE);
3739 free_list_array(&new_list);
3741 /* Swap PerAddrBook structs */
3742 tmppab = as.adrbks[anum1];
3743 as.adrbks[anum1] = as.adrbks[anum2];
3744 as.adrbks[anum2] = tmppab;
3746 else if((type1 == Pers && type2 == Glob) ||
3747 (type1 == Glob && type2 == Pers)){
3748 int how_many_in_srclist, how_many_in_dstlist;
3749 int srcvarnum, dstvarnum, srcanum;
3750 int cnt, warn_about_revert = 0;
3751 char **t;
3752 char **new_src, **new_dst, **srclist, **dstlist;
3753 char tmp[200];
3754 enum {NotSet, Modify, RevertToDefault, OverRideDefault} modify_config;
3757 * how_many_in_srclist = # in orig src list (Pers or Glob list).
3758 * how_many_in_dstlist = # in orig dst list
3759 * srcanum = # of highlighted addrbook that is being shuffled
3761 if(type1 == Pers){
3762 how_many_in_srclist = as.how_many_personals;
3763 how_many_in_dstlist = as.n_addrbk - as.how_many_personals;
3764 srclist = VAR_ADDRESSBOOK;
3765 dstlist = VAR_GLOB_ADDRBOOK;
3766 srcvarnum = V_ADDRESSBOOK;
3767 dstvarnum = V_GLOB_ADDRBOOK;
3768 srcanum = as.how_many_personals - 1;
3769 *slide = (how_many_in_srclist == 1)
3770 ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
3771 : XTRA_LINES_BETWEEN;
3773 else{
3774 how_many_in_srclist = as.n_addrbk - as.how_many_personals;
3775 how_many_in_dstlist = as.how_many_personals;
3776 srclist = VAR_GLOB_ADDRBOOK;
3777 dstlist = VAR_ADDRESSBOOK;
3778 srcvarnum = V_GLOB_ADDRBOOK;
3779 dstvarnum = V_ADDRESSBOOK;
3780 srcanum = as.how_many_personals;
3781 *slide = (how_many_in_dstlist == 0)
3782 ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
3783 : XTRA_LINES_BETWEEN;
3784 *slide = -1 * (*slide);
3788 modify_config = Modify;
3789 if(how_many_in_srclist == 1){
3791 * Deal with reverting to default values of the address book
3792 * variables, or with user deleting a default value.
3794 modify_config = NotSet;
3797 * Count how many address books are in the user's config.
3798 * This has to be one or zero, because how_many_in_srclist == 1.
3800 cnt = 0;
3801 lval = LVAL(&vars[srcvarnum], ew);
3802 if(lval && lval[0])
3803 for(t = lval; *t != NULL; t++)
3804 cnt++;
3807 * We'll revert to the default if it exists, and warn
3808 * the user about that.
3810 if(cnt == 1){
3811 modify_config = RevertToDefault;
3812 /* see if there's a default to revert to */
3813 cnt = 0;
3814 if(vars[srcvarnum].global_val.l &&
3815 vars[srcvarnum].global_val.l[0])
3816 for(t = vars[srcvarnum].global_val.l; *t != NULL; t++)
3817 cnt++;
3819 warn_about_revert = cnt;
3820 if(warn_about_revert > 1 && type1 == Pers)
3821 *slide = LINES_PER_ABOOK * warn_about_revert +
3822 XTRA_LINES_BETWEEN;
3825 * User is already using the default.
3827 else if(cnt == 0){
3828 modify_config = OverRideDefault;
3833 * We're adding one to the dstlist, so need how_many + 1 + 1.
3835 new_dst = (char **)fs_get((how_many_in_dstlist + 2) * sizeof(char *));
3836 j = 0;
3839 * Because the Personal list comes before the Global list, when
3840 * we move to Global we're inserting a new first element into
3841 * the global list (the dstlist).
3843 * When we move from Global to Personal, we're appending a new
3844 * last element onto the personal list (the dstlist).
3846 if(type2 == Glob)
3847 new_dst[j++] = cpystr(srclist[how_many_in_srclist-1]);
3849 for(i = 0; i < how_many_in_dstlist; i++)
3850 new_dst[j++] = cpystr(dstlist[i]);
3852 if(type2 == Pers)
3853 new_dst[j++] = cpystr(srclist[0]);
3855 new_dst[j] = NULL;
3858 * The srclist is complicated by the reverting to default
3859 * behaviors.
3861 if(modify_config == Modify){
3863 * In this case we're just removing one from the srclist
3864 * so the new_src is of size how_many -1 +1.
3866 new_src = (char **)fs_get((how_many_in_srclist) * sizeof(char *));
3867 j = 0;
3869 for(i = 0; i < how_many_in_srclist-1; i++)
3870 new_src[j++] = cpystr(srclist[i + ((type1 == Glob) ? 1 : 0)]);
3872 new_src[j] = NULL;
3874 else if(modify_config == OverRideDefault){
3876 * We were using default and will now revert to nothing.
3878 new_src = (char **)fs_get(2 * sizeof(char *));
3879 new_src[0] = cpystr("");
3880 new_src[1] = NULL;
3882 else if(modify_config == RevertToDefault){
3884 * We are moving our last user variable out and reverting
3885 * to the default value for this variable.
3887 new_src = NULL;
3890 if(set_variable_list(dstvarnum, new_dst, TRUE, ew) ||
3891 set_variable_list(srcvarnum, new_src, TRUE, ew)){
3892 if(msg)
3893 *msg = cpystr(cancel_msg);
3895 /* restore old values */
3896 set_current_val(&vars[dstvarnum], TRUE, FALSE);
3897 set_current_val(&vars[srcvarnum], TRUE, FALSE);
3898 free_list_array(&new_dst);
3899 free_list_array(&new_src);
3900 return(retval);
3903 set_current_val(&vars[dstvarnum], TRUE, FALSE);
3904 set_current_val(&vars[srcvarnum], TRUE, FALSE);
3905 free_list_array(&new_dst);
3906 free_list_array(&new_src);
3908 retval = (type1 == Pers && warn_about_revert)
3909 ? (warn_about_revert + 1) : (srcanum + 1);
3912 * This is a tough case. We're adding one or more new address books
3913 * in this case so we need to reset the addrbooks and start over.
3914 * We return the number of the address book we just moved after the
3915 * reset so that the caller can focus attention on the moved one.
3916 * Actually, we return 1+the number so that we can tell it apart
3917 * from a return of zero, which just means everything is ok.
3919 if(warn_about_revert){
3920 snprintf(tmp, sizeof(tmp),
3921 "This address book now %s, reverting to default %s address %s",
3922 (type1 == Glob) ? "Personal" : "Global",
3923 (type1 == Glob) ? "Global" : "Personal",
3924 warn_about_revert > 1 ? "books" : "book");
3925 tmp[sizeof(tmp)-1] = '\0';
3926 if(msg)
3927 *msg = cpystr(tmp);
3929 else{
3931 * Modify PerAddrBook struct and adjust boundary.
3932 * In this case we aren't swapping two addrbooks, but just modifying
3933 * one from being global to personal or the reverse. It will
3934 * still be the same element in the as.adrbks array.
3936 pab = &as.adrbks[srcanum];
3937 if(type2 == Glob){
3938 as.how_many_personals--;
3939 pab->type |= GLOBAL;
3940 if(pab->access != NoAccess)
3941 pab->access = ReadOnly;
3943 else{
3944 as.how_many_personals++;
3945 pab->type &= ~GLOBAL;
3946 if(pab->access != NoAccess && pab->access != MaybeRorW)
3947 pab->access = ReadWrite;
3950 snprintf(tmp, sizeof(tmp),
3951 "This address book now %s",
3952 (type1 == Glob) ? "Personal" : "Global");
3953 tmp[sizeof(tmp)-1] = '\0';
3954 if(msg)
3955 *msg = cpystr(tmp);
3959 return(retval);
3964 ab_compose_to_addr(long int cur_line, int agg, int allow_role)
3966 AddrScrn_Disp *dl;
3967 AdrBk_Entry *abe;
3968 SAVE_STATE_S state;
3969 BuildTo bldto;
3971 dprint((2, "- ab_compose_to_addr -\n"));
3973 save_state(&state);
3975 bldto.type = Str;
3976 bldto.arg.str = NULL;
3978 if(agg){
3979 int i;
3980 size_t incr = 100, avail, alloced;
3981 char *to = NULL;
3983 to = (char *)fs_get(incr);
3984 *to = '\0';
3985 avail = incr;
3986 alloced = incr;
3989 * Run through all of the selected entries
3990 * in all of the address books.
3991 * Put the nicknames together into one long
3992 * string with comma separators.
3994 for(i = 0; i < as.n_addrbk; i++){
3995 adrbk_cntr_t num;
3996 PerAddrBook *pab;
3997 EXPANDED_S *next_one;
3999 pab = &as.adrbks[i];
4000 if(pab->address_book)
4001 next_one = pab->address_book->selects;
4002 else
4003 continue;
4005 while((num = entry_get_next(&next_one)) != NO_NEXT){
4006 char *a_string;
4007 AddrScrn_Disp fake_dl;
4009 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4012 * Since we're picking up address book entries
4013 * directly from the address books and have
4014 * no knowledge of the display lines they came
4015 * from, we don't know the dl's that go with
4016 * them. We need to pass a dl to abe_to_nick
4017 * but it really is only going to use the
4018 * type in this case.
4020 dl = &fake_dl;
4021 dl->type = (abe->tag == Single) ? Simple : ListHead;
4022 a_string = abe_to_nick_or_addr_string(abe, dl, i);
4024 while(abe && avail < (size_t)strlen(a_string)+1){
4025 alloced += incr;
4026 avail += incr;
4027 fs_resize((void **)&to, alloced);
4030 if(!*to){
4031 strncpy(to, a_string, alloced);
4032 to[alloced-1] = '\0';
4034 else{
4035 strncat(to, ",", alloced-strlen(to)-1);
4036 to[alloced-1] = '\0';
4037 strncat(to, a_string, alloced-strlen(to)-1);
4038 to[alloced-1] = '\0';
4041 avail -= (strlen(a_string) + 1);
4042 fs_give((void **)&a_string);
4046 bldto.type = Str;
4047 bldto.arg.str = to;
4049 else{
4050 if(is_addr(cur_line)){
4052 dl = dlist(cur_line);
4053 abe = ae(cur_line);
4055 if(dl->type == ListEnt){
4056 bldto.type = Str;
4057 bldto.arg.str = cpystr(listmem(cur_line));
4059 else{
4060 bldto.type = Abe;
4061 bldto.arg.abe = abe;
4066 if(bldto.type == Str && bldto.arg.str == NULL)
4067 bldto.arg.str = cpystr("");
4069 ab_compose_internal(bldto, allow_role);
4071 restore_state(&state);
4073 if(bldto.type == Str && bldto.arg.str)
4074 fs_give((void **)&bldto.arg.str);
4077 * Window size may have changed in composer.
4078 * Pine_send will have reset the window size correctly,
4079 * but we still have to reset our address book data structures.
4081 ab_resize();
4082 ps_global->mangled_screen = 1;
4083 return(1);
4088 * Used by the two compose routines.
4090 void
4091 ab_compose_internal(BuildTo bldto, int allow_role)
4093 int good_addr;
4094 char *addr, *fcc, *error = NULL;
4095 ACTION_S *role = NULL;
4096 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
4097 (*redraw)(void) = ps_global->redrawer;
4099 if(allow_role)
4100 ps_global->redrawer = NULL;
4102 ps_global->next_screen = SCREEN_FUN_NULL;
4104 fcc = NULL;
4105 addr = NULL;
4107 good_addr = (our_build_address(bldto, &addr, &error, &fcc, NULL) >= 0);
4109 if(error){
4110 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4111 fs_give((void **)&error);
4114 if(!good_addr && addr && *addr)
4115 fs_give((void **)&addr); /* relying on fs_give setting addr to NULL */
4117 if(allow_role){
4118 /* Setup role */
4119 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
4120 cmd_cancelled("Composition");
4121 ps_global->next_screen = prev_screen;
4122 ps_global->redrawer = redraw;
4123 return;
4127 * If default role was selected (NULL) we need to make up a role which
4128 * won't do anything, but will cause compose_mail to think there's
4129 * already a role so that it won't try to confirm the default.
4131 if(role)
4132 role = copy_action(role);
4133 else{
4134 role = (ACTION_S *)fs_get(sizeof(*role));
4135 memset((void *)role, 0, sizeof(*role));
4136 role->nick = cpystr("Default Role");
4140 compose_mail(addr, fcc, role, NULL, NULL);
4142 if(addr)
4143 fs_give((void **)&addr);
4145 if(fcc)
4146 fs_give((void **)&fcc);
4151 * Export addresses into a file.
4153 * Args: cur_line -- The current line position (in global display list)
4154 * of cursor
4155 * command_line -- The screen line on which to prompt
4157 * Returns -- 1 if the export is done
4158 * 0 if not
4161 ab_export(struct pine *ps, long int cur_line, int command_line, int agg)
4163 int ret = 0, i, retflags = GER_NONE;
4164 int r, orig_errno, failure = 0;
4165 struct variable *vars = ps->vars;
4166 char filename[MAXPATH+1], full_filename[MAXPATH+1];
4167 STORE_S *store;
4168 gf_io_t pc;
4169 long start_of_append;
4170 char *addr = NULL, *error = NULL;
4171 BuildTo bldto;
4172 char *p;
4173 int good_addr, plur, vcard = 0, tab = 0;
4174 static HISTORY_S *history = NULL;
4175 AdrBk_Entry *abe;
4176 VCARD_INFO_S *vinfo;
4177 static ESCKEY_S ab_export_opts[] = {
4178 {ctrl('T'), 10, "^T", N_("To Files")},
4179 {-1, 0, NULL, NULL},
4180 {-1, 0, NULL, NULL}};
4181 static ESCKEY_S vcard_or_addresses[] = {
4182 {'a', 'a', "A", N_("Address List")},
4183 {'v', 'v', "V", N_("VCard")},
4184 /* TRANSLATORS: TabSep is a command key label meaning Tab Separated List */
4185 {'t', 't', "T", N_("TabSep")},
4186 {-1, 0, NULL, NULL}};
4189 dprint((2, "- ab_export -\n"));
4191 if(ps->restricted){
4192 q_status_message(SM_ORDER, 0, 3,
4193 "Alpine demo can't export addresses to files");
4194 return(ret);
4197 while(1){
4198 i = radio_buttons(_("Export list of addresses, vCard format, or Tab Separated ? "),
4199 command_line, vcard_or_addresses, 'a', 'x',
4200 NO_HELP, RB_NORM|RB_RET_HELP);
4201 if(i == 3){
4202 /* TRANSLATORS: a screen title */
4203 helper(h_ab_export_vcard, _("HELP FOR EXPORT FORMAT"),
4204 HLPD_SIMPLE);
4205 ps_global->mangled_screen = 1;
4207 else
4208 break;
4211 switch(i){
4212 case 'x':
4213 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
4214 return(ret);
4216 case 'a':
4217 break;
4219 case 'v':
4220 vcard++;
4221 break;
4223 case 't':
4224 tab++;
4225 break;
4227 default:
4228 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_export");
4229 return(ret);
4232 if(agg)
4233 plur = 1;
4234 else{
4235 abe = ae(cur_line);
4236 plur = (abe && abe->tag == List);
4239 filename[0] = '\0';
4240 r = 0;
4242 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
4243 ab_export_opts[++r].ch = ctrl('I');
4244 ab_export_opts[r].rval = 11;
4245 ab_export_opts[r].name = "TAB";
4246 ab_export_opts[r].label = N_("Complete");
4249 ab_export_opts[++r].ch = -1;
4251 r = get_export_filename(ps, filename, NULL, full_filename, sizeof(filename),
4252 plur ? _("addresses") : _("address"),
4253 _("EXPORT"), ab_export_opts,
4254 &retflags, command_line, GE_IS_EXPORT, &history);
4256 if(r < 0){
4257 switch(r){
4258 case -1:
4259 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
4260 break;
4262 case -2:
4263 q_status_message1(SM_ORDER, 0, 2,
4264 _("Can't export to file outside of %s"), VAR_OPER_DIR);
4265 break;
4268 goto fini;
4271 dprint((5, "Opening file \"%s\" for export\n",
4272 full_filename ? full_filename : "?"));
4274 if(!(store = so_get(FileStar, full_filename, WRITE_ACCESS|WRITE_TO_LOCALE))){
4275 q_status_message2(SM_ORDER | SM_DING, 3, 4,
4276 _("Error opening file \"%s\" for address export: %s"),
4277 full_filename, error_description(errno));
4278 goto fini;
4282 * The write_single_vcard_entry function wants a pc.
4284 if(vcard || tab)
4285 gf_set_so_writec(&pc, store);
4287 start_of_append = so_tell(store);
4289 if(agg){
4290 for(i = 0; !failure && i < as.n_addrbk; i++){
4291 adrbk_cntr_t num;
4292 PerAddrBook *pab;
4293 EXPANDED_S *next_one;
4295 pab = &as.adrbks[i];
4296 if(pab->address_book)
4297 next_one = pab->address_book->selects;
4298 else
4299 continue;
4301 while(!failure && (num = entry_get_next(&next_one)) != NO_NEXT){
4303 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4304 if((vcard || tab) && abe){
4306 * There is no place to store the charset information
4307 * so we don't ask for it.
4309 if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
4310 failure++;
4311 else{
4312 if(vcard)
4313 write_single_vcard_entry(ps, pc, vinfo);
4314 else
4315 write_single_tab_entry(pc, vinfo);
4317 free_vcard_info(&vinfo);
4320 else if(abe){
4321 bldto.type = Abe;
4322 bldto.arg.abe = abe;
4323 error = NULL;
4324 addr = NULL;
4325 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4327 if(error){
4328 q_status_message1(SM_ORDER, 0, 4, "%s", error);
4329 fs_give((void **)&error);
4332 /* rfc1522_decode the addr */
4333 if(addr){
4334 size_t len;
4336 len = 4*strlen(addr)+1;
4337 p = (char *)fs_get(len * sizeof(char));
4338 if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
4339 fs_give((void **)&addr);
4340 addr = p;
4342 else
4343 fs_give((void **)&p);
4346 if(good_addr){
4347 int quoted = 0;
4350 * Change the unquoted commas into newlines.
4351 * Not worth it to do complicated quoting,
4352 * just consider double quotes.
4354 for(p = addr; *p; p++){
4355 if(*p == '"')
4356 quoted = !quoted;
4357 else if(!quoted && *p == ','){
4358 *p++ = '\n';
4359 removing_leading_white_space(p);
4360 p--;
4364 if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
4365 orig_errno = errno;
4366 failure = 1;
4370 if(addr)
4371 fs_give((void **)&addr);
4376 else{
4377 AddrScrn_Disp *dl;
4379 dl = dlist(cur_line);
4380 abe = ae(cur_line);
4381 if((vcard || tab) && abe){
4382 if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
4383 failure++;
4384 else{
4385 if(vcard)
4386 write_single_vcard_entry(ps, pc, vinfo);
4387 else
4388 write_single_tab_entry(pc, vinfo);
4390 free_vcard_info(&vinfo);
4393 else{
4395 if(dl->type == ListHead && listmem_count_from_abe(abe) == 0){
4396 error = _("List is empty, nothing to export!");
4397 good_addr = 0;
4399 else if(dl->type == ListEnt){
4400 bldto.type = Str;
4401 bldto.arg.str = listmem(cur_line);
4402 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4404 else{
4405 bldto.type = Abe;
4406 bldto.arg.abe = abe;
4407 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4410 if(error){
4411 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4412 fs_give((void **)&error);
4415 /* Have to rfc1522_decode the addr */
4416 if(addr){
4417 size_t len;
4418 len = 4*strlen(addr)+1;
4419 p = (char *)fs_get(len * sizeof(char));
4420 if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
4421 fs_give((void **)&addr);
4422 addr = p;
4424 else
4425 fs_give((void **)&p);
4428 if(good_addr){
4429 int quoted = 0;
4432 * Change the unquoted commas into newlines.
4433 * Not worth it to do complicated quoting,
4434 * just consider double quotes.
4436 for(p = addr; *p; p++){
4437 if(*p == '"')
4438 quoted = !quoted;
4439 else if(!quoted && *p == ','){
4440 *p++ = '\n';
4441 removing_leading_white_space(p);
4442 p--;
4446 if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
4447 orig_errno = errno;
4448 failure = 1;
4452 if(addr)
4453 fs_give((void **)&addr);
4457 if(vcard || tab)
4458 gf_clear_so_writec(store);
4460 if(so_give(&store)) /* release storage */
4461 failure++;
4463 if(failure){
4464 our_truncate(full_filename, (off_t)start_of_append);
4465 dprint((1, "FAILED Export: file \"%s\" : %s\n",
4466 full_filename ? full_filename : "?",
4467 error_description(orig_errno)));
4468 q_status_message2(SM_ORDER | SM_DING, 3, 4,
4469 _("Error exporting to \"%s\" : %s"),
4470 filename, error_description(orig_errno));
4472 else{
4473 ret = 1;
4474 q_status_message3(SM_ORDER,0,3,
4475 "%s %s to file \"%s\"",
4476 (vcard || tab) ? (agg ? "Entries" : "Entry")
4477 : (plur ? "Addresses" : "Address"),
4478 retflags & GER_OVER
4479 ? "overwritten"
4480 : retflags & GER_APPEND ? "appended" : "exported",
4481 filename);
4484 fini:
4485 ps->mangled_footer = 1;
4486 return(ret);
4491 * Forward an address book entry or entries via email attachment.
4493 * We use the vCard standard to send entries. We group multiple entries
4494 * using the BEGIN/END construct of vCard, not with multiple MIME parts.
4495 * A limitation of vCard is that there can be only one charset for the
4496 * whole group we send, so we might lose that information.
4498 * Args: cur_line -- The current line position (in global display list)
4499 * of cursor
4500 * command_line -- The screen line on which to prompt
4503 ab_forward(struct pine *ps, long int cur_line, int agg)
4505 AddrScrn_Disp *dl;
4506 AdrBk_Entry *abe;
4507 ENVELOPE *outgoing = NULL;
4508 BODY *pb, *body = NULL;
4509 PART **pp;
4510 char *sig;
4511 gf_io_t pc;
4512 int i, ret = 0;
4513 VCARD_INFO_S *vinfo;
4514 ACTION_S *role = NULL;
4516 dprint((2, "- ab_forward -\n"));
4518 if(!agg){
4519 dl = dlist(cur_line);
4520 if(dl->type != ListHead && dl->type != Simple)
4521 return(ret);
4523 abe = ae(cur_line);
4524 if(!abe){
4525 q_status_message(SM_ORDER, 3, 3, _("Trouble accessing current entry"));
4526 return(ret);
4530 outgoing = mail_newenvelope();
4531 outgoing->message_id = generate_message_id();
4532 if(agg && as.selections > 1)
4533 outgoing->subject = cpystr("Forwarded address book entries from Alpine");
4534 else
4535 outgoing->subject = cpystr("Forwarded address book entry from Alpine");
4537 body = mail_newbody();
4538 body->type = TYPEMULTIPART;
4539 /*---- The TEXT part/body ----*/
4540 body->nested.part = mail_newbody_part();
4541 body->nested.part->body.type = TYPETEXT;
4542 /*--- Allocate an object for the body ---*/
4543 if((body->nested.part->body.contents.text.data =
4544 (void *)so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
4545 int did_sig = 0;
4546 long rflags = ROLE_COMPOSE;
4547 PAT_STATE dummy;
4549 pp = &(body->nested.part->next);
4551 if(nonempty_patterns(rflags, &dummy)){
4553 * This is really more like Compose, even though it
4554 * is called Forward.
4556 if(confirm_role(rflags, &role))
4557 role = combine_inherited_role(role);
4558 else{ /* cancel */
4559 role = NULL;
4560 cmd_cancelled("Composition");
4561 goto bomb;
4565 if(role)
4566 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
4567 role->nick);
4569 if((sig = detoken(role, NULL, 2, 0, 1, NULL, NULL)) != NULL){
4570 if(*sig){
4571 so_puts((STORE_S *)body->nested.part->body.contents.text.data,
4572 sig);
4573 did_sig++;
4576 fs_give((void **)&sig);
4579 /* so we don't have an empty part */
4580 if(!did_sig)
4581 so_puts((STORE_S *)body->nested.part->body.contents.text.data, "\n");
4583 else{
4584 q_status_message(SM_ORDER | SM_DING, 3, 4,
4585 _("Problem creating space for message text"));
4586 goto bomb;
4590 /*---- create the attachment, and write abook entry into it ----*/
4591 *pp = mail_newbody_part();
4592 pb = &((*pp)->body);
4593 pb->type = TYPETEXT;
4594 pb->encoding = ENCOTHER; /* let data decide */
4595 pb->id = generate_message_id();
4596 pb->subtype = cpystr("DIRECTORY");
4597 if(agg && as.selections > 1)
4598 pb->description = cpystr("Alpine addressbook entries");
4599 else
4600 pb->description = cpystr("Alpine addressbook entry");
4602 pb->parameter = NULL;
4603 set_parameter(&pb->parameter, "profile", "vCard");
4605 if((pb->contents.text.data = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
4606 int are_some_unqualified = 0, expand_nicks = 0;
4607 adrbk_cntr_t num;
4608 PerAddrBook *pab;
4609 EXPANDED_S *next_one;
4611 gf_set_so_writec(&pc, (STORE_S *) pb->contents.text.data);
4613 if(agg){
4614 for(i = 0; i < as.n_addrbk && !are_some_unqualified; i++){
4616 pab = &as.adrbks[i];
4617 if(pab->address_book)
4618 next_one = pab->address_book->selects;
4619 else
4620 continue;
4622 while((num = entry_get_next(&next_one)) != NO_NEXT &&
4623 !are_some_unqualified){
4625 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4626 if(abe->tag == Single){
4627 if(abe->addr.addr && abe->addr.addr[0]
4628 && !strindex(abe->addr.addr, '@'))
4629 are_some_unqualified++;
4631 else{
4632 char **ll;
4634 for(ll = abe->addr.list; ll && *ll; ll++){
4635 if(!strindex(*ll, '@')){
4636 are_some_unqualified++;
4637 break;
4644 else{
4646 * Search through the addresses to see if there are any
4647 * that are unqualified, and so would be different if
4648 * expanded.
4650 if(abe->tag == Single){
4651 if(abe->addr.addr && abe->addr.addr[0]
4652 && !strindex(abe->addr.addr, '@'))
4653 are_some_unqualified++;
4655 else{
4656 char **ll;
4658 for(ll = abe->addr.list; ll && *ll; ll++){
4659 if(!strindex(*ll, '@')){
4660 are_some_unqualified++;
4661 break;
4667 if(are_some_unqualified){
4668 switch(want_to(_("Expand nicknames"), 'y', 'x', h_ab_forward,WT_NORM)){
4669 case 'x':
4670 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4671 q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
4672 goto bomb;
4674 case 'y':
4675 expand_nicks = 1;
4676 break;
4678 case 'n':
4679 expand_nicks = 0;
4680 break;
4683 ps->mangled_footer = 1;
4686 if(agg){
4687 for(i = 0; i < as.n_addrbk; i++){
4689 pab = &as.adrbks[i];
4690 if(pab->address_book)
4691 next_one = pab->address_book->selects;
4692 else
4693 continue;
4695 while((num = entry_get_next(&next_one)) != NO_NEXT){
4697 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4698 if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
4699 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4700 goto bomb;
4702 else{
4703 write_single_vcard_entry(ps, pc, vinfo);
4704 free_vcard_info(&vinfo);
4709 else{
4710 if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
4711 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4712 goto bomb;
4714 else{
4715 write_single_vcard_entry(ps, pc, vinfo);
4716 free_vcard_info(&vinfo);
4720 /* This sets parameter charset, if necessary, and encoding */
4721 set_mime_type_by_grope(pb);
4722 set_charset_possibly_to_ascii(pb, "UTF-8");
4723 pb->size.bytes =
4724 strlen((char *)so_text((STORE_S *)pb->contents.text.data));
4726 else{
4727 q_status_message(SM_ORDER | SM_DING, 3, 4,
4728 _("Problem creating space for message text"));
4729 goto bomb;
4732 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4734 pine_send(outgoing, &body, _("FORWARDING ADDRESS BOOK ENTRY"), role, NULL,
4735 NULL, NULL, NULL, NULL, 0);
4737 ps->mangled_screen = 1;
4738 ret = 1;
4740 bomb:
4741 if(outgoing)
4742 mail_free_envelope(&outgoing);
4744 if(body)
4745 pine_free_body(&body);
4747 free_action(&role);
4748 return(ret);
4753 * Given an Adrbk_Entry fill in some of the fields in a VCARD_INFO_S
4754 * for use by write_single_vcard_entry. The returned structure is freed
4755 * by the caller.
4757 VCARD_INFO_S *
4758 prepare_abe_for_vcard(struct pine *ps, AdrBk_Entry *abe, int expand_nicks)
4760 VCARD_INFO_S *vinfo = NULL;
4761 char *init_addr = NULL, *addr = NULL, *astring;
4762 int cnt;
4763 ADDRESS *adrlist = NULL;
4765 if(!abe)
4766 return(vinfo);
4768 vinfo = (VCARD_INFO_S *) fs_get(sizeof(*vinfo));
4769 memset((void *) vinfo, 0, sizeof(*vinfo));
4771 if(abe->nickname && abe->nickname[0]){
4772 vinfo->nickname = (char **) fs_get((1+1) * sizeof(char *));
4773 vinfo->nickname[0] = cpystr(abe->nickname);
4774 vinfo->nickname[1] = NULL;
4777 if(abe->fcc && abe->fcc[0]){
4778 vinfo->fcc = (char **) fs_get((1+1) * sizeof(char *));
4779 vinfo->fcc[0] = cpystr(abe->fcc);
4780 vinfo->fcc[1] = NULL;
4783 if(abe->extra && abe->extra[0]){
4784 vinfo->note = (char **) fs_get((1+1) * sizeof(char *));
4785 vinfo->note[0] = cpystr(abe->extra);
4786 vinfo->note[1] = NULL;
4789 if(abe->fullname && abe->fullname[0]){
4790 char *fn, *last = NULL, *middle = NULL, *first = NULL;
4792 fn = adrbk_formatname(abe->fullname, &first, &last);
4793 if(fn){
4794 if(*fn){
4795 vinfo->fullname = (char **)fs_get((1+1) * sizeof(char *));
4796 vinfo->fullname[0] = fn;
4797 vinfo->fullname[1] = NULL;
4799 else
4800 fs_give((void **)&fn);
4803 if(last && *last){
4804 if(first && (middle=strindex(first, ' '))){
4805 *middle++ = '\0';
4806 middle = skip_white_space(middle);
4809 vinfo->last = last;
4810 vinfo->first = first;
4811 vinfo->middle = middle ? cpystr(middle) : NULL;
4812 first = NULL;
4813 last = NULL;
4816 if(last)
4817 fs_give((void **)&last);
4818 if(first)
4819 fs_give((void **)&first);
4822 /* expand nicknames and fully-qualify unqualified names */
4823 if(expand_nicks){
4824 char *error = NULL;
4825 BuildTo bldto;
4826 ADDRESS *a;
4828 if(abe->tag == Single)
4829 init_addr = cpystr(abe->addr.addr);
4830 else{
4831 char **ll;
4832 char *p;
4833 long length;
4835 /* figure out how large a string we need to allocate */
4836 length = 0L;
4837 for(ll = abe->addr.list; ll && *ll; ll++)
4838 length += (strlen(*ll) + 2);
4840 if(length)
4841 length -= 2L;
4843 init_addr = (char *)fs_get((size_t)(length+1L) * sizeof(char));
4844 p = init_addr;
4846 for(ll = abe->addr.list; ll && *ll; ll++){
4847 sstrncpy(&p, *ll, length-(p-init_addr));
4848 if(*(ll+1))
4849 sstrncpy(&p, ", ", length-(p-init_addr));
4852 init_addr[length] = '\0';
4855 bldto.type = Str;
4856 bldto.arg.str = init_addr;
4857 our_build_address(bldto, &addr, &error, NULL, NULL);
4858 if(error){
4859 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4860 fs_give((void **)&error);
4861 free_vcard_info(&vinfo);
4862 return(NULL);
4865 if(addr)
4866 rfc822_parse_adrlist(&adrlist, addr, ps->maildomain);
4868 for(cnt = 0, a = adrlist; a; a = a->next)
4869 cnt++;
4871 vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
4873 for(cnt = 0, a = adrlist; a; a = a->next){
4874 char *bufp;
4875 ADDRESS *next_addr;
4876 size_t len;
4878 next_addr = a->next;
4879 a->next = NULL;
4880 len = est_size(a);
4881 bufp = (char *) fs_get(len * sizeof(char));
4882 astring = addr_string(a, bufp, len);
4883 a->next = next_addr;
4884 vinfo->email[cnt++] = cpystr(astring ? astring : "");
4885 fs_give((void **)&bufp);
4888 vinfo->email[cnt] = NULL;
4890 else{ /* don't expand or qualify */
4891 if(abe->tag == Single){
4892 astring =
4893 (abe->addr.addr && abe->addr.addr[0]) ? abe->addr.addr : "";
4894 vinfo->email = (char **)fs_get((1+1) * sizeof(char *));
4895 vinfo->email[0] = cpystr(astring);
4896 vinfo->email[1] = NULL;
4898 else{
4899 char **ll;
4901 for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
4902 cnt++;
4904 vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
4905 for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
4906 vinfo->email[cnt++] = cpystr(*ll);
4908 vinfo->email[cnt] = NULL;
4912 return(vinfo);
4916 void
4917 free_vcard_info(VCARD_INFO_S **vinfo)
4919 if(vinfo && *vinfo){
4920 if((*vinfo)->nickname)
4921 free_list_array(&(*vinfo)->nickname);
4922 if((*vinfo)->fullname)
4923 free_list_array(&(*vinfo)->fullname);
4924 if((*vinfo)->fcc)
4925 free_list_array(&(*vinfo)->fcc);
4926 if((*vinfo)->note)
4927 free_list_array(&(*vinfo)->note);
4928 if((*vinfo)->title)
4929 free_list_array(&(*vinfo)->title);
4930 if((*vinfo)->tel)
4931 free_list_array(&(*vinfo)->tel);
4932 if((*vinfo)->email)
4933 free_list_array(&(*vinfo)->email);
4935 if((*vinfo)->first)
4936 fs_give((void **)&(*vinfo)->first);
4937 if((*vinfo)->middle)
4938 fs_give((void **)&(*vinfo)->middle);
4939 if((*vinfo)->last)
4940 fs_give((void **)&(*vinfo)->last);
4942 fs_give((void **)vinfo);
4950 void
4951 write_single_vcard_entry(struct pine *ps, gf_io_t pc, VCARD_INFO_S *vinfo)
4953 char *decoded, *tmp2, *tmp = NULL, *hdr;
4954 char **ll;
4955 int i, did_fn = 0, did_n = 0;
4956 int cr;
4957 char eol[3];
4958 #define FOLD_BY 75
4960 if(!vinfo)
4961 return;
4963 #if defined(DOS) || defined(OS2)
4964 cr = 1;
4965 #else
4966 cr = 0;
4967 #endif
4969 if(cr)
4970 strncpy(eol, "\r\n", sizeof(eol));
4971 else
4972 strncpy(eol, "\n", sizeof(eol));
4974 eol[sizeof(eol)-1] = '\0';
4976 gf_puts("BEGIN:VCARD", pc);
4977 gf_puts(eol, pc);
4978 gf_puts("VERSION:3.0", pc);
4979 gf_puts(eol, pc);
4981 for(i = 0; i < 7; i++){
4982 switch(i){
4983 case 0:
4984 ll = vinfo->nickname;
4985 hdr = "NICKNAME:";
4986 break;
4988 case 1:
4989 ll = vinfo->fullname;
4990 hdr = "FN:";
4991 break;
4993 case 2:
4994 ll = vinfo->email;
4995 hdr = "EMAIL:";
4996 break;
4998 case 3:
4999 ll = vinfo->title;
5000 hdr = "TITLE:";
5001 break;
5003 case 4:
5004 ll = vinfo->note;
5005 hdr = "NOTE:";
5006 break;
5008 case 5:
5009 ll = vinfo->fcc;
5010 hdr = "X-FCC:";
5011 break;
5013 case 6:
5014 ll = vinfo->tel;
5015 hdr = "TEL:";
5016 break;
5018 default:
5019 alpine_panic("can't happen in write_single_vcard_entry");
5022 for(; ll && *ll; ll++){
5023 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
5025 tmp = vcard_escape(decoded);
5026 if(tmp){
5027 if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, hdr, " ", FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5028 gf_puts(tmp2, pc);
5029 fs_give((void **)&tmp2);
5030 if(i == 1)
5031 did_fn++;
5034 fs_give((void **)&tmp);
5039 if(vinfo->last && vinfo->last[0]){
5040 char *pl, *pf, *pm;
5042 pl = vcard_escape(vinfo->last);
5043 pf = (vinfo->first && *vinfo->first) ? vcard_escape(vinfo->first)
5044 : NULL;
5045 pm = (vinfo->middle && *vinfo->middle) ? vcard_escape(vinfo->middle)
5046 : NULL;
5047 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
5048 (pl && *pl) ? pl : "",
5049 ((pf && *pf) || (pm && *pm)) ? ";" : "",
5050 (pf && *pf) ? pf : "",
5051 (pm && *pm) ? ";" : "",
5052 (pm && *pm) ? pm : "");
5054 if((tmp2 = fold(tmp_20k_buf, FOLD_BY, FOLD_BY, "N:", " ",
5055 FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5056 gf_puts(tmp2, pc);
5057 fs_give((void **)&tmp2);
5058 did_n++;
5061 if(pl)
5062 fs_give((void **)&pl);
5063 if(pf)
5064 fs_give((void **)&pf);
5065 if(pm)
5066 fs_give((void **)&pm);
5070 * These two types are required in draft-ietf-asid-mime-vcard-06, which
5071 * is April 98 and is in last call.
5073 if(!did_fn || !did_n){
5074 if(did_n){
5075 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
5076 (vinfo->first && *vinfo->first) ? vinfo->first : "",
5077 (vinfo->first && *vinfo->first &&
5078 vinfo->middle && *vinfo->middle) ? " " : "",
5079 (vinfo->middle && *vinfo->middle) ? vinfo->middle : "",
5080 (((vinfo->first && *vinfo->first) ||
5081 (vinfo->middle && *vinfo->middle)) &&
5082 vinfo->last && *vinfo->last) ? " " : "",
5083 (vinfo->last && *vinfo->last) ? vinfo->last : "");
5085 tmp = vcard_escape(tmp_20k_buf);
5086 if(tmp){
5087 if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, "FN:", " ",
5088 FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5089 gf_puts(tmp2, pc);
5090 fs_give((void **)&tmp2);
5091 did_n++;
5094 fs_give((void **)&tmp);
5097 else{
5098 if(!did_fn){
5099 gf_puts("FN:<Unknown>", pc);
5100 gf_puts(eol, pc);
5103 gf_puts("N:<Unknown>", pc);
5104 gf_puts(eol, pc);
5108 gf_puts("END:VCARD", pc);
5109 gf_puts(eol, pc);
5116 void
5117 write_single_tab_entry(gf_io_t pc, VCARD_INFO_S *vinfo)
5119 char *decoded, *tmp = NULL;
5120 char **ll;
5121 int i, first;
5122 char *eol;
5124 if(!vinfo)
5125 return;
5127 #if defined(DOS) || defined(OS2)
5128 eol = "\r\n";
5129 #else
5130 eol = "\n";
5131 #endif
5133 for(i = 0; i < 4; i++){
5134 switch(i){
5135 case 0:
5136 ll = vinfo->nickname;
5137 break;
5139 case 1:
5140 ll = vinfo->fullname;
5141 break;
5143 case 2:
5144 ll = vinfo->email;
5145 break;
5147 case 3:
5148 ll = vinfo->note;
5149 break;
5151 default:
5152 alpine_panic("can't happen in write_single_tab_entry");
5155 if(i)
5156 gf_puts("\t", pc);
5158 for(first = 1; ll && *ll; ll++){
5160 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
5161 tmp = vcard_escape(decoded);
5162 if(tmp){
5163 if(i == 2 && !first)
5164 gf_puts(",", pc);
5165 else
5166 first = 0;
5168 gf_puts(tmp, pc);
5169 fs_give((void **)&tmp);
5174 gf_puts(eol, pc);
5179 * for ab_save percent done
5181 static int total_to_copy;
5182 static int copied_so_far;
5184 percent_done_copying(void)
5186 return((copied_so_far * 100) / total_to_copy);
5190 cmp_action_list(const qsort_t *a1, const qsort_t *a2)
5192 ACTION_LIST_S *x = (ACTION_LIST_S *)a1;
5193 ACTION_LIST_S *y = (ACTION_LIST_S *)a2;
5195 if(x->pab != y->pab)
5196 return((x->pab > y->pab) ? 1 : -1); /* order doesn't matter */
5199 * The only one that matters is when both x and y have dup lit.
5200 * For the others, just need to be consistent so sort will terminate.
5202 if(x->dup){
5203 if(y->dup)
5204 return((x->num_in_dst > y->num_in_dst) ? -1
5205 : (x->num_in_dst == y->num_in_dst) ? 0 : 1);
5206 else
5207 return(-1);
5209 else if(y->dup)
5210 return(1);
5211 else
5212 return((x->num > y->num) ? -1 : (x->num == y->num) ? 0 : 1);
5217 * Copy a bunch of address book entries to a particular address book.
5219 * Args abook -- the current addrbook handle
5220 * cur_line -- the current line the cursor is on
5221 * command_line -- the line to prompt on
5222 * agg -- 1 if this is an aggregate copy
5224 * Returns 1 if successful, 0 if not
5227 ab_save(struct pine *ps, AdrBk *abook, long int cur_line, int command_line, int agg)
5229 PerAddrBook *pab_dst, *pab;
5230 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
5231 int rc, i;
5232 int how_many_dups = 0, how_many_to_copy = 0, skip_dups = 0;
5233 int how_many_no_action = 0, ret = 1;
5234 int err = 0, need_write = 0, we_cancel = 0;
5235 int act_list_size, special_case = 0;
5236 adrbk_cntr_t num, new_entry_num;
5237 char warn[2][MAX_NICKNAME+1];
5238 char warning[MAX_NICKNAME+1];
5239 char tmp[MAX(200,2*MAX_NICKNAME+80)];
5240 ACTION_LIST_S *action_list = NULL, *al;
5241 static ESCKEY_S save_or_export[] = {
5242 {'s', 's', "S", N_("Save")},
5243 {'e', 'e', "E", N_("Export")},
5244 {-1, 0, NULL, NULL}};
5246 if(!agg)
5247 snprintf(tmp, sizeof(tmp), _("Save highlighted entry to address book or Export to filesystem ? "));
5248 else if(as.selections > 1)
5249 snprintf(tmp, sizeof(tmp), _("Save selected entries to address book or Export to filesystem ? "));
5250 else if(as.selections == 1)
5251 snprintf(tmp, sizeof(tmp), _("Save selected entry to address book or Export to filesystem ? "));
5252 else
5253 snprintf(tmp, sizeof(tmp), _("Save to address book or Export to filesystem ? "));
5255 i = radio_buttons(tmp, -FOOTER_ROWS(ps), save_or_export, 's', 'x',
5256 h_ab_save_exp, RB_NORM);
5257 switch(i){
5258 case 'x':
5259 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5260 return(0);
5262 case 'e':
5263 return(ab_export(ps, cur_line, command_line, agg));
5265 case 's':
5266 break;
5268 default:
5269 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_save");
5270 return(0);
5273 pab_dst = setup_for_addrbook_add(&state, command_line, _("Save"));
5274 if(!pab_dst)
5275 goto get_out;
5277 pab = &as.adrbks[as.cur];
5279 dprint((2, "- ab_save: %s -> %s (agg=%d)-\n",
5280 pab->abnick ? pab->abnick : "?",
5281 pab_dst->abnick ? pab_dst->abnick : "?", agg));
5283 if(agg)
5284 act_list_size = as.selections;
5285 else
5286 act_list_size = 1;
5288 action_list = (ACTION_LIST_S *)fs_get((act_list_size+1) *
5289 sizeof(ACTION_LIST_S));
5290 memset((void *)action_list, 0, (act_list_size+1) * sizeof(ACTION_LIST_S));
5291 al = action_list;
5293 if(agg){
5295 for(i = 0; i < as.n_addrbk; i++){
5296 EXPANDED_S *next_one;
5298 pab = &as.adrbks[i];
5299 if(pab->address_book)
5300 next_one = pab->address_book->selects;
5301 else
5302 continue;
5304 while((num = entry_get_next(&next_one)) != NO_NEXT){
5305 if(pab != pab_dst &&
5306 pab->ostatus != Open &&
5307 pab->ostatus != NoDisplay)
5308 init_abook(pab, NoDisplay);
5310 if(pab->ostatus != Open && pab->ostatus != NoDisplay){
5311 q_status_message1(SM_ORDER, 0, 4,
5312 _("Can't re-open address book %s to save from"),
5313 pab->abnick);
5314 err++;
5315 goto get_out;
5318 set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
5319 if(al->skip)
5320 how_many_no_action++;
5321 else{
5322 if(al->dup){
5323 if(how_many_dups < 2 && warning[0]){
5324 strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
5325 warn[how_many_dups][MAX_NICKNAME] = '\0';
5328 how_many_dups++;
5331 how_many_to_copy++;
5334 al++;
5338 else{
5339 if(is_addr(cur_line)){
5340 AddrScrn_Disp *dl;
5342 dl = dlist(cur_line);
5344 if(dl->type == ListEnt)
5345 special_case++;
5347 if(pab && dl){
5348 num = dl->elnum;
5349 set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
5351 else
5352 al->skip = 1;
5354 if(al->skip)
5355 how_many_no_action++;
5356 else{
5357 if(al->dup){
5358 if(how_many_dups < 2 && warning[0]){
5359 strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
5360 warn[how_many_dups][MAX_NICKNAME] = '\0';
5363 how_many_dups++;
5366 how_many_to_copy++;
5369 else{
5370 q_status_message(SM_ORDER, 0, 4, _("No current entry to save"));
5371 goto get_out;
5375 if(how_many_to_copy == 0 && how_many_no_action == 1 && act_list_size == 1)
5376 special_case++;
5378 if(special_case){
5379 TA_STATE_S tas, *tasp;
5381 /* Not going to use the action_list now */
5382 if(action_list)
5383 fs_give((void **)&action_list);
5385 tasp = &tas;
5386 tas.state = state;
5387 tas.pab = pab_dst;
5388 take_this_one_entry(ps, &tasp, abook, cur_line);
5391 * If take_this_one_entry or its children didn't do this for
5392 * us, we do it here.
5394 if(tas.pab)
5395 restore_state(&(tas.state));
5398 * We don't have enough information to know what to return.
5400 return(0);
5403 /* nothing to do (except for special Take case below) */
5404 if(how_many_to_copy == 0){
5405 if(how_many_no_action == 0){
5406 err++;
5407 goto get_out;
5409 else{
5410 restore_state(&state);
5412 if(how_many_no_action > 1)
5413 snprintf(tmp, sizeof(tmp), _("Saved %d entries to %s"), how_many_no_action, pab_dst->abnick);
5414 else
5415 snprintf(tmp, sizeof(tmp), _("Saved %d entry to %s"), how_many_no_action, pab_dst->abnick);
5417 tmp[sizeof(tmp)-1] = '\0';
5418 q_status_message(SM_ORDER, 0, 4, tmp);
5419 if(action_list)
5420 fs_give((void **)&action_list);
5422 return(ret);
5427 * If there are some nicknames which already exist in the selected
5428 * abook, ask user what to do.
5430 if(how_many_dups > 0){
5431 if(how_many_dups == 1)
5432 snprintf(tmp, sizeof(tmp), _("Entry with nickname \"%.*s\" already exists, replace "),
5433 MAX_NICKNAME, warn[0]);
5434 else if(how_many_dups == 2)
5435 snprintf(tmp, sizeof(tmp),
5436 _("Nicknames \"%.*s\" and \"%.*s\" already exist, replace "),
5437 MAX_NICKNAME, warn[0], MAX_NICKNAME, warn[1]);
5438 else
5439 snprintf(tmp, sizeof(tmp), _("%d of the nicknames already exist, replace "),
5440 how_many_dups);
5442 tmp[sizeof(tmp)-1] = '\0';
5444 switch(want_to(tmp, 'n', 'x', h_ab_copy_dups, WT_NORM)){
5445 case 'n':
5446 skip_dups++;
5447 if(how_many_to_copy == how_many_dups){
5448 restore_state(&state);
5449 if(action_list)
5450 fs_give((void **)&action_list);
5452 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5453 return(ret);
5456 break;
5458 case 'y':
5459 break;
5461 case 'x':
5462 err++;
5463 goto get_out;
5468 * Because the deletes happen immediately we have to delete from high
5469 * entry number towards lower entry numbers so that we are deleting
5470 * the correct entries. In order to do that we'll sort the action_list
5471 * to give us a safe order.
5473 if(!skip_dups && how_many_dups > 1)
5474 qsort((qsort_t *)action_list, (size_t)as.selections, sizeof(*action_list),
5475 cmp_action_list);
5478 * Set up the busy alarm percent counters.
5480 total_to_copy = how_many_to_copy - (skip_dups ? how_many_dups : 0);
5481 copied_so_far = 0;
5482 we_cancel = busy_cue(_("Saving entries"),
5483 (total_to_copy > 4) ? percent_done_copying : NULL, 0);
5486 * Add the list of entries to the destination abook.
5488 for(al = action_list; al && al->pab; al++){
5489 AdrBk_Entry *abe;
5491 if(al->skip || (skip_dups && al->dup))
5492 continue;
5494 if(!(abe = adrbk_get_ae(al->pab->address_book, (a_c_arg_t) al->num))){
5495 q_status_message1(SM_ORDER | SM_DING, 3, 5,
5496 _("Error saving entry: %s"),
5497 error_description(errno));
5498 err++;
5499 goto get_out;
5503 * Delete existing dups and replace them.
5505 if(al->dup){
5507 /* delete the existing entry */
5508 rc = 0;
5509 if(adrbk_delete(pab_dst->address_book,
5510 (a_c_arg_t)al->num_in_dst, 1, 0, 0, 0) == 0){
5511 need_write++;
5513 else{
5514 q_status_message2(SM_ORDER | SM_DING, 3, 5,
5515 _("Error replacing entry in %s: %s"),
5516 pab_dst->abnick,
5517 error_description(errno));
5518 err++;
5519 goto get_out;
5524 * Now we have a clean slate to work with.
5525 * Add (sorted in correctly) or append abe to the destination
5526 * address book.
5528 if(total_to_copy <= 1)
5529 rc = adrbk_add(pab_dst->address_book,
5530 NO_NEXT,
5531 abe->nickname,
5532 abe->fullname,
5533 abe->tag == Single ? abe->addr.addr : NULL,
5534 abe->fcc,
5535 abe->extra,
5536 abe->tag,
5537 &new_entry_num,
5538 (int *)NULL,
5542 else
5543 rc = adrbk_append(pab_dst->address_book,
5544 abe->nickname,
5545 abe->fullname,
5546 abe->tag == Single ? abe->addr.addr : NULL,
5547 abe->fcc,
5548 abe->extra,
5549 abe->tag,
5550 &new_entry_num);
5552 if(rc == 0)
5553 need_write++;
5556 * If the entry we copied is a list, we also have to add
5557 * the list members to the copy.
5559 if(rc == 0 && abe->tag == List){
5560 int save_sort_rule;
5563 * We want it to copy the list in the exact order
5564 * without sorting it.
5566 save_sort_rule = pab_dst->address_book->sort_rule;
5567 pab_dst->address_book->sort_rule = AB_SORT_RULE_NONE;
5569 rc = adrbk_nlistadd(pab_dst->address_book,
5570 (a_c_arg_t)new_entry_num, NULL, NULL,
5571 abe->addr.list,
5572 0, 0, 0);
5574 pab_dst->address_book->sort_rule = save_sort_rule;
5577 if(rc != 0){
5578 if(abe && abe->nickname)
5579 q_status_message2(SM_ORDER | SM_DING, 3, 5, _("Error saving %s: %s"), abe->nickname, error_description(errno));
5580 else
5581 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error saving entry: %s"), error_description(errno));
5582 err++;
5583 goto get_out;
5586 copied_so_far++;
5589 if(need_write){
5590 int sort_happened = 0;
5592 if(adrbk_write(pab_dst->address_book, 0, NULL, &sort_happened, 0, 1)){
5593 err++;
5594 goto get_out;
5597 if(sort_happened)
5598 ps_global->mangled_screen = 1;
5601 get_out:
5602 if(we_cancel)
5603 cancel_busy_cue(1);
5605 restore_state(&state);
5606 if(action_list)
5607 fs_give((void **)&action_list);
5609 ps_global->mangled_footer = 1;
5611 if(err){
5612 ret = 0;
5613 if(need_write)
5614 q_status_message(SM_ORDER | SM_DING, 3, 4,
5615 _("Save only partially completed"));
5616 else
5617 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5619 else if (how_many_to_copy + how_many_no_action -
5620 (skip_dups ? how_many_dups : 0) > 0){
5622 ret = 1;
5623 snprintf(tmp, sizeof(tmp), "Saved %d %s to %s",
5624 how_many_to_copy + how_many_no_action -
5625 (skip_dups ? how_many_dups : 0),
5626 ((how_many_to_copy + how_many_no_action -
5627 (skip_dups ? how_many_dups : 0)) > 1) ? "entries" : "entry",
5628 pab_dst->abnick);
5629 tmp[sizeof(tmp)-1] = '\0';
5630 q_status_message(SM_ORDER, 0, 4, tmp);
5633 return(ret);
5638 * Warn should point to an array of size MAX_NICKNAME+1.
5640 void
5641 set_act_list_member(ACTION_LIST_S *al, a_c_arg_t numarg, PerAddrBook *pab_dst, PerAddrBook *pab, char *warn)
5643 AdrBk_Entry *abe1, *abe2;
5644 adrbk_cntr_t num;
5646 num = (adrbk_cntr_t)numarg;
5648 al->pab = pab;
5649 al->num = num;
5651 /* skip if they're copying from and to same addrbook */
5652 if(pab == pab_dst)
5653 al->skip = 1;
5654 else{
5655 abe1 = adrbk_get_ae(pab->address_book, numarg);
5656 if(abe1 && abe1->nickname && abe1->nickname[0]){
5657 adrbk_cntr_t dst_enum;
5659 abe2 = adrbk_lookup_by_nick(pab_dst->address_book,
5660 abe1->nickname, &dst_enum);
5662 * This nickname already exists in the destn address book.
5664 if(abe2){
5665 /* If it isn't different, no problem. Check it out. */
5666 if(abes_are_equal(abe1, abe2))
5667 al->skip = 1;
5668 else{
5669 strncpy(warn, abe1->nickname, MAX_NICKNAME);
5670 warn[MAX_NICKNAME] = '\0';
5671 al->dup = 1;
5672 al->num_in_dst = dst_enum;
5681 * Print out the display list.
5684 ab_print(int agg)
5686 int do_entry = 0, curopen;
5687 char *prompt;
5689 dprint((2, "- ab_print -\n"));
5691 curopen = cur_is_open();
5692 if(!agg && curopen){
5693 static ESCKEY_S prt[] = {
5694 {'a', 'a', "A", N_("AddressBook")},
5695 {'e', 'e', "E", N_("Entry")},
5696 {-1, 0, NULL, NULL}};
5698 prompt = _("Print Address Book or just this Entry? ");
5699 switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global), prt, 'a', 'x',
5700 NO_HELP, RB_NORM)){
5701 case 'x' :
5702 q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
5703 ps_global->mangled_footer = 1;
5704 return 0;
5706 case 'e':
5707 do_entry = 1;
5708 break;
5710 default:
5711 case 'a':
5712 break;
5716 /* TRANSLATORS: This is input for
5717 Print something1 using something2. The thing we're
5718 defining here is something1. */
5719 if(agg)
5720 prompt = _("selected entries");
5721 else{
5722 if(!curopen)
5723 prompt = _("address book list");
5724 else if(do_entry)
5725 prompt = _("entry");
5726 else
5727 prompt = _("address book");
5730 if(open_printer(prompt) == 0){
5731 DL_CACHE_S dlc_buf, *match_dlc;
5732 AddrScrn_Disp *dl;
5733 AdrBk_Entry *abe;
5734 long save_line;
5735 char *addr;
5736 char spaces[100];
5737 char more_spaces[100];
5738 char b[500];
5739 int abook_indent;
5741 save_line = as.top_ent + as.cur_row;
5742 match_dlc = get_dlc(save_line);
5743 dlc_buf = *match_dlc;
5744 match_dlc = &dlc_buf;
5746 if(do_entry){ /* print an individual addrbook entry */
5748 abook_indent = utf8_width(_("Nickname")) + 2;
5750 snprintf(spaces, sizeof(spaces), "%*.*s", abook_indent+2, abook_indent+2, "");
5751 snprintf(more_spaces, sizeof(more_spaces), "%*.*s",
5752 abook_indent+4, abook_indent+4, "");
5754 dl = dlist(save_line);
5755 abe = ae(save_line);
5757 if(abe){
5758 int are_some_unqualified = 0, expand_nicks = 0;
5759 char *string, *tmp;
5760 ADDRESS *adrlist = NULL;
5763 * Search through the addresses to see if there are any
5764 * that are unqualified, and so would be different if
5765 * expanded.
5767 if(abe->tag == Single){
5768 if(abe->addr.addr && abe->addr.addr[0]
5769 && !strindex(abe->addr.addr, '@'))
5770 are_some_unqualified++;
5772 else{
5773 char **ll;
5775 for(ll = abe->addr.list; ll && *ll; ll++){
5776 if(!strindex(*ll, '@')){
5777 are_some_unqualified++;
5778 break;
5783 if(are_some_unqualified){
5784 switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward,
5785 WT_NORM)){
5786 case 'x':
5787 q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
5788 ps_global->mangled_footer = 1;
5789 return 0;
5791 case 'y':
5792 expand_nicks = 1;
5793 break;
5795 case 'n':
5796 expand_nicks = 0;
5797 break;
5801 /* expand nicknames and fully-qualify unqualified names */
5802 if(expand_nicks){
5803 char *error = NULL;
5804 BuildTo bldto;
5805 char *init_addr = NULL;
5807 if(abe->tag == Single)
5808 init_addr = cpystr(abe->addr.addr);
5809 else{
5810 char **ll;
5811 char *p;
5812 long length;
5814 /* figure out how large a string we need to allocate */
5815 length = 0L;
5816 for(ll = abe->addr.list; ll && *ll; ll++)
5817 length += (strlen(*ll) + 2);
5819 if(length)
5820 length -= 2L;
5822 init_addr = (char *)fs_get((size_t)(length+1L) *
5823 sizeof(char));
5824 p = init_addr;
5826 for(ll = abe->addr.list; ll && *ll; ll++){
5827 sstrncpy(&p, *ll, length-(p-init_addr));
5828 if(*(ll+1))
5829 sstrncpy(&p, ", ", length-(p-init_addr));
5832 init_addr[length] = '\0';
5835 bldto.type = Str;
5836 bldto.arg.str = init_addr;
5837 our_build_address(bldto, &addr, &error, NULL, NULL);
5838 if(init_addr)
5839 fs_give((void **)&init_addr);
5841 if(error){
5842 q_status_message1(SM_ORDER, 0, 4, "%s", error);
5843 fs_give((void **)&error);
5845 ps_global->mangled_footer = 1;
5846 return 0;
5849 if(addr){
5850 rfc822_parse_adrlist(&adrlist, addr,
5851 ps_global->maildomain);
5852 fs_give((void **)&addr);
5855 /* Will use adrlist to do the printing below */
5858 tmp = abe->nickname ? abe->nickname : "";
5859 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5860 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
5861 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5862 print_text(tmp);
5863 fs_give((void **)&tmp);
5866 tmp = abe->fullname ? abe->fullname : "";
5867 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5868 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
5869 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5870 print_text(tmp);
5871 fs_give((void **)&tmp);
5874 tmp = abe->fcc ? abe->fcc : "";
5875 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5876 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
5877 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5878 print_text(tmp);
5879 fs_give((void **)&tmp);
5882 tmp = abe->extra ? abe->extra : "";
5883 {unsigned char *p, *bb = NULL;
5884 size_t n, len;
5885 if((n = 4*strlen(tmp)) > SIZEOF_20KBUF-1){
5886 len = n+1;
5887 p = bb = (unsigned char *)fs_get(len * sizeof(char));
5889 else{
5890 len = SIZEOF_20KBUF;
5891 p = (unsigned char *)tmp_20k_buf;
5894 string = (char *)rfc1522_decode_to_utf8(p, len, tmp);
5895 utf8_snprintf(b, len, "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
5896 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5897 print_text(tmp);
5898 fs_give((void **)&tmp);
5901 if(bb)
5902 fs_give((void **)&bb);
5906 * Print addresses
5909 if(expand_nicks){
5910 ADDRESS *a;
5912 for(a = adrlist; a; a = a->next){
5913 char *bufp;
5914 ADDRESS *next_addr;
5915 size_t len;
5917 next_addr = a->next;
5918 a->next = NULL;
5919 len = est_size(a);
5920 bufp = (char *) fs_get(len * sizeof(char));
5921 tmp = addr_string(a, bufp, len);
5922 a->next = next_addr;
5923 string = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf+10000,
5924 SIZEOF_20KBUF-10000, tmp);
5925 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5926 if((tmp = fold(string, 80, 80,
5927 (a == adrlist) ? b : spaces,
5928 more_spaces, FLD_NONE)) != NULL){
5929 print_text(tmp);
5930 fs_give((void **)&tmp);
5933 fs_give((void **)&bufp);
5936 if(adrlist)
5937 mail_free_address(&adrlist);
5938 else{
5939 utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
5940 print_text(b);
5943 else{ /* don't expand or qualify */
5944 if(abe->tag == Single){
5945 tmp = abe->addr.addr ? abe->addr.addr : "";
5946 string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
5947 SIZEOF_20KBUF-10000, tmp);
5948 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5949 if((tmp = fold(string, 80, 80, b,
5950 more_spaces, FLD_NONE)) != NULL){
5951 print_text(tmp);
5952 fs_give((void **)&tmp);
5955 else{
5956 char **ll;
5958 if(!abe->addr.list || !abe->addr.list[0]){
5959 utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
5960 print_text(b);
5963 for(ll = abe->addr.list; ll && *ll; ll++){
5964 string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
5965 SIZEOF_20KBUF-10000, *ll);
5966 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5967 if((tmp = fold(string, 80, 80,
5968 (ll == abe->addr.list)
5969 ? b : spaces,
5970 more_spaces, FLD_NONE)) != NULL){
5971 print_text(tmp);
5972 fs_give((void **)&tmp);
5979 else{
5980 long lineno;
5981 char lbuf[6*MAX_SCREEN_COLS + 1];
5982 char *p;
5983 int i, savecur, savezoomed;
5984 OpenStatus savestatus;
5985 PerAddrBook *pab;
5987 if(agg){ /* print all selected entries */
5988 if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
5989 savezoomed = as.zoomed;
5991 * Fool display code into thinking display is zoomed, so
5992 * we'll skip the unselected entries. Since this feature
5993 * causes all abooks to be displayed we don't have to
5994 * step through each addrbook.
5996 as.zoomed = 1;
5997 warp_to_beginning();
5998 lineno = 0L;
5999 for(dl = dlist(lineno);
6000 dl->type != End;
6001 dl = dlist(++lineno)){
6003 switch(dl->type){
6004 case Beginning:
6005 case ListClickHere:
6006 case ListEmpty:
6007 case ClickHereCmb:
6008 case Text:
6009 case TitleCmb:
6010 case Empty:
6011 case ZoomEmpty:
6012 case AskServer:
6013 continue;
6014 default:
6015 break;
6018 p = get_abook_display_line(lineno, 0, NULL, NULL,
6019 NULL, lbuf, sizeof(lbuf));
6020 print_text1("%s\n", p);
6023 as.zoomed = savezoomed;
6025 else{ /* print all selected entries */
6026 savecur = as.cur;
6027 savezoomed = as.zoomed;
6028 as.zoomed = 1;
6030 for(i = 0; i < as.n_addrbk; i++){
6031 pab = &as.adrbks[i];
6032 if(!(pab->address_book &&
6033 any_selected(pab->address_book->selects)))
6034 continue;
6037 * Print selected entries from addrbook i.
6038 * We have to put addrbook i into Open state so
6039 * that the display code will work right.
6041 as.cur = i;
6042 savestatus = pab->ostatus;
6043 init_abook(pab, Open);
6044 init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
6045 (void)calculate_field_widths();
6046 warp_to_beginning();
6047 lineno = 0L;
6049 for(dl = dlist(lineno);
6050 dl->type != End;
6051 dl = dlist(++lineno)){
6053 switch(dl->type){
6054 case Beginning:
6055 case ListClickHere:
6056 case ClickHereCmb:
6057 continue;
6058 default:
6059 break;
6062 p = get_abook_display_line(lineno, 0, NULL, NULL,
6063 NULL, lbuf,sizeof(lbuf));
6064 print_text1("%s\n", p);
6067 init_abook(pab, savestatus);
6070 as.cur = savecur;
6071 as.zoomed = savezoomed;
6072 /* restore the display for the current addrbook */
6073 init_disp_form(&as.adrbks[as.cur],
6074 ps_global->VAR_ABOOK_FORMATS, as.cur);
6077 else{ /* print either the abook list or a single abook */
6078 int anum;
6079 DL_CACHE_S *dlc;
6081 savezoomed = as.zoomed;
6082 as.zoomed = 0;
6084 if(curopen){ /* print a single address book */
6085 anum = adrbk_num_from_lineno(as.top_ent+as.cur_row);
6086 warp_to_top_of_abook(anum);
6087 if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
6088 lineno = 0L - XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP;
6089 else{
6090 print_text(" ");
6091 print_text(_("ADDRESS BOOK"));
6092 print_text1(" %s\n\n", as.adrbks[as.cur].abnick);
6093 lineno = 0L;
6096 else{ /* print the list of address books */
6097 warp_to_beginning();
6098 lineno = 0L;
6101 for(dl = dlist(lineno);
6102 dl->type != End && (!curopen ||
6103 (anum==adrbk_num_from_lineno(lineno) &&
6104 (as.n_serv == 0 ||
6105 ((dlc=get_dlc(lineno)) &&
6106 dlc->type != DlcDirDelim1))));
6107 dl = dlist(++lineno)){
6109 switch(dl->type){
6110 case Beginning:
6111 case ListClickHere:
6112 case ClickHereCmb:
6113 continue;
6114 default:
6115 break;
6118 p = get_abook_display_line(lineno, 0, NULL, NULL, NULL,
6119 lbuf, sizeof(lbuf));
6120 print_text1("%s\n", p);
6123 as.zoomed = savezoomed;
6127 close_printer();
6130 * jump cache back to where we started so that the next
6131 * request won't cause us to page through the whole thing
6133 if(!do_entry)
6134 warp_to_dlc(match_dlc, save_line);
6136 ps_global->mangled_screen = 1;
6139 ps_global->mangled_footer = 1;
6140 return 1;
6145 * Delete address book entries.
6148 ab_agg_delete(struct pine *ps, int agg)
6150 int ret = 0, i, ch, rc = 0;
6151 PerAddrBook *pab;
6152 adrbk_cntr_t num, ab_count;
6153 char prompt[80];
6155 dprint((2, "- ab_agg_delete -\n"));
6157 if(agg){
6158 snprintf(prompt, sizeof(prompt), _("Really delete %d selected entries"), as.selections);
6159 prompt[sizeof(prompt)-1] = '\0';
6160 ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
6161 if(ch == 'y'){
6162 adrbk_cntr_t newelnum, flushelnum = NO_NEXT;
6163 DL_CACHE_S dlc_save, dlc_restart, *dlc;
6164 int we_cancel = 0;
6165 int top_level_display;
6168 * We want to try to put the cursor in a reasonable position
6169 * on the screen when we're done. If we are in the top-level
6170 * display, then we can leave it the same. If we have an
6171 * addrbook opened, then we want to see if we can get back to
6172 * the same entry we are currently on.
6174 if(!(top_level_display = !any_ab_open())){
6175 dlc = get_dlc(as.top_ent+as.cur_row);
6176 dlc_save = *dlc;
6177 newelnum = dlc_save.dlcelnum;
6180 we_cancel = busy_cue(NULL, NULL, 1);
6182 for(i = 0; i < as.n_addrbk && rc != -5; i++){
6183 int orig_selected, selected;
6185 pab = &as.adrbks[i];
6186 if(!pab->address_book)
6187 continue;
6189 ab_count = adrbk_count(pab->address_book);
6190 rc = 0;
6191 selected = howmany_selected(pab->address_book->selects);
6192 orig_selected = selected;
6194 * Because deleting an entry causes the addrbook to be
6195 * immediately updated, we need to delete from higher entry
6196 * numbers to lower numbers. That way, entry number n is still
6197 * entry number n in the updated address book because we've
6198 * only deleted entries higher than n.
6200 for(num = ab_count-1; selected > 0 && rc == 0; num--){
6201 if(entry_is_selected(pab->address_book->selects,
6202 (a_c_arg_t)num)){
6203 rc = adrbk_delete(pab->address_book, (a_c_arg_t)num,
6204 1, 0, 0, 0);
6206 selected--;
6209 * This is just here to help us reposition the cursor.
6211 if(!top_level_display && as.cur == i && rc == 0){
6212 if(num >= newelnum)
6213 flushelnum = num;
6214 else
6215 newelnum--;
6220 if(rc == 0 && orig_selected > 0){
6221 int sort_happened = 0;
6223 rc = adrbk_write(pab->address_book, 0, NULL, &sort_happened, 1, 0);
6224 if(sort_happened)
6225 ps_global->mangled_screen = 1;
6228 if(rc && rc != -5){
6229 q_status_message2(SM_ORDER | SM_DING, 3, 5,
6230 "Error updating %s: %s",
6231 (as.n_addrbk > 1) ? pab->abnick
6232 : "address book",
6233 error_description(errno));
6234 dprint((1, "Error updating %s: %s\n",
6235 pab->filename ? pab->filename : "?",
6236 error_description(errno)));
6240 if(we_cancel)
6241 cancel_busy_cue(-1);
6243 if(rc == 0){
6244 q_status_message(SM_ORDER, 0, 2, _("Deletions completed"));
6245 ret = 1;
6246 erase_selections();
6249 if(!top_level_display){
6250 int lost = 0;
6251 long new_ent;
6253 if(flushelnum != dlc_save.dlcelnum){
6255 * We didn't delete current so restart there. The elnum
6256 * may have changed if we deleted entries above it.
6258 dlc_restart = dlc_save;
6259 dlc_restart.dlcelnum = newelnum;
6261 else{
6263 * Current was deleted.
6265 dlc_restart.adrbk_num = as.cur;
6266 pab = &as.adrbks[as.cur];
6267 ab_count = adrbk_count(pab->address_book);
6268 if(ab_count == 0)
6269 dlc_restart.type = DlcEmpty;
6270 else{
6271 AdrBk_Entry *abe;
6273 dlc_restart.dlcelnum = MIN(newelnum, ab_count-1);
6274 abe = adrbk_get_ae(pab->address_book,
6275 (a_c_arg_t) dlc_restart.dlcelnum);
6276 if(abe && abe->tag == Single)
6277 dlc_restart.type = DlcSimple;
6278 else if(abe && abe->tag == List)
6279 dlc_restart.type = DlcListHead;
6280 else
6281 lost++;
6285 if(lost){
6286 warp_to_top_of_abook(as.cur);
6287 as.top_ent = 0L;
6288 new_ent = first_selectable_line(0L);
6289 if(new_ent == NO_LINE)
6290 as.cur_row = 0L;
6291 else
6292 as.cur_row = new_ent;
6294 /* if it is off screen */
6295 if(as.cur_row >= as.l_p_page){
6296 as.top_ent += (as.cur_row - as.l_p_page + 1);
6297 as.cur_row = (as.l_p_page - 1);
6300 else if(dlc_restart.type != DlcEmpty &&
6301 dlc_restart.dlcelnum == dlc_save.dlcelnum &&
6302 (F_OFF(F_CMBND_ABOOK_DISP,ps_global) || as.cur == 0)){
6304 * Didn't delete any before this line.
6305 * Leave screen about the same. (May have deleted current.)
6307 warp_to_dlc(&dlc_restart, as.cur_row+as.top_ent);
6309 else{
6310 warp_to_dlc(&dlc_restart, 0L);
6311 /* put in middle of screen */
6312 as.top_ent = first_line(0L - (long)as.l_p_page/2L);
6313 as.cur_row = 0L - as.top_ent;
6316 ps->mangled_body = 1;
6319 else
6320 cmd_cancelled("Apply Delete command");
6323 return(ret);
6328 * Delete an entry from the address book
6330 * Args: abook -- The addrbook handle into access library
6331 * command_line -- The screen line on which to prompt
6332 * cur_line -- The entry number in the display list
6333 * warped -- We warped to a new part of the addrbook
6335 * Result: returns 1 if an entry was deleted, 0 if not.
6337 * The main routine above knows what to repaint because it's always the
6338 * current entry that's deleted. Here confirmation is asked of the user
6339 * and the appropriate adrbklib functions are called.
6342 single_entry_delete(AdrBk *abook, long int cur_line, int *warped)
6344 char ch, *cmd, *dname;
6345 char prompt[200];
6346 int rc;
6347 register AddrScrn_Disp *dl;
6348 AdrBk_Entry *abe;
6349 DL_CACHE_S *dlc_to_flush;
6351 dprint((2, "- single_entry_delete -\n"));
6353 if(warped)
6354 *warped = 0;
6356 dl = dlist(cur_line);
6357 abe = adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
6359 switch(dl->type){
6360 case Simple:
6361 dname = (abe->fullname && abe->fullname[0])
6362 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6363 SIZEOF_20KBUF, abe->fullname)
6364 : abe->nickname ? abe->nickname : "";
6365 cmd = _("Really delete \"%s\"");
6366 break;
6368 case ListHead:
6369 dname = (abe->fullname && abe->fullname[0])
6370 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6371 SIZEOF_20KBUF, abe->fullname)
6372 : abe->nickname ? abe->nickname : "";
6373 cmd = _("Really delete ENTIRE list \"%s\"");
6374 break;
6376 case ListEnt:
6377 dname = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6378 SIZEOF_20KBUF, listmem_from_dl(abook, dl));
6379 cmd = _("Really delete \"%s\" from list");
6380 break;
6382 default:
6383 break;
6386 dname = dname ? dname : "";
6387 cmd = cmd ? cmd : "";
6389 snprintf(prompt, sizeof(prompt), cmd, dname);
6390 prompt[sizeof(prompt)-1] = '\0';
6391 ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
6392 if(ch == 'y'){
6393 dlc_to_flush = get_dlc(cur_line);
6394 if(dl->type == Simple || dl->type == ListHead){
6395 /*--- Kill a single entry or an entire list ---*/
6396 rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 1, 1, 0, 1);
6398 else if(listmem_count_from_abe(abe) > 2){
6399 /*---- Kill an entry out of a list ----*/
6400 rc = adrbk_listdel(abook, (a_c_arg_t)dl->elnum,
6401 listmem_from_dl(abook, dl));
6403 else{
6404 char *nick, *full, *addr, *fcc, *comment;
6405 adrbk_cntr_t new_entry_num = NO_NEXT;
6407 /*---- Convert a List to a Single entry ----*/
6409 /* Save old info to be transferred */
6410 nick = cpystr(abe->nickname);
6411 full = cpystr(abe->fullname);
6412 fcc = cpystr(abe->fcc);
6413 comment = cpystr(abe->extra);
6414 if(listmem_count_from_abe(abe) == 2)
6415 addr = cpystr(abe->addr.list[1 - dl->l_offset]);
6416 else
6417 addr = cpystr("");
6419 rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 0, 1, 0, 0);
6420 if(rc == 0)
6421 adrbk_add(abook,
6422 NO_NEXT,
6423 nick,
6424 full,
6425 addr,
6426 fcc,
6427 comment,
6428 Single,
6429 &new_entry_num,
6430 (int *)NULL,
6435 fs_give((void **)&nick);
6436 fs_give((void **)&full);
6437 fs_give((void **)&fcc);
6438 fs_give((void **)&comment);
6439 fs_give((void **)&addr);
6441 if(rc == 0){
6442 DL_CACHE_S dlc_restart;
6444 dlc_restart.adrbk_num = as.cur;
6445 dlc_restart.dlcelnum = new_entry_num;
6446 dlc_restart.type = DlcSimple;
6447 warp_to_dlc(&dlc_restart, 0L);
6448 *warped = 1;
6449 return 1;
6453 if(rc == 0){
6454 q_status_message(SM_ORDER, 0, 3,
6455 _("Entry deleted, address book updated"));
6456 dprint((5, "abook: Entry %s\n",
6457 (dl->type == Simple || dl->type == ListHead) ? "deleted"
6458 : "modified"));
6460 * Remove deleted line and everything after it from
6461 * the dlc cache. Next time we try to access those lines they
6462 * will get filled in with the right info.
6464 flush_dlc_from_cache(dlc_to_flush);
6465 return 1;
6467 else{
6468 PerAddrBook *pab;
6470 if(rc != -5)
6471 q_status_message1(SM_ORDER | SM_DING, 3, 5,
6472 _("Error updating address book: %s"),
6473 error_description(errno));
6474 pab = &as.adrbks[as.cur];
6475 dprint((1, "Error deleting entry from %s (%s): %s\n",
6476 pab->abnick ? pab->abnick : "?",
6477 pab->filename ? pab->filename : "?",
6478 error_description(errno)));
6481 return 0;
6483 else{
6484 q_status_message(SM_INFO, 0, 2, _("Entry not deleted"));
6485 return 0;
6490 void
6491 free_headents(struct headerentry **head)
6493 struct headerentry *he;
6494 PrivateTop *pt;
6496 if(head && *head){
6497 for(he = *head; he->name; he++)
6498 if(he->bldr_private){
6499 pt = (PrivateTop *)he->bldr_private;
6500 free_privatetop(&pt);
6503 fs_give((void **)head);
6508 #ifdef ENABLE_LDAP
6510 prompt::name::help::prwid::maxlen::realaddr::
6511 builder::affected_entry::next_affected::selector::key_label::fileedit::
6512 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
6513 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
6515 static struct headerentry headents_for_query[]={
6516 {"Normal Search : ", "NormalSearch", h_composer_qserv_qq, 16, 0, NULL,
6517 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6518 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6519 {"Name : ", "Name", h_composer_qserv_cn, 16, 0, NULL,
6520 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6521 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6522 {"Surname : ", "SurName", h_composer_qserv_sn, 16, 0, NULL,
6523 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6524 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6525 {"Given Name : ", "GivenName", h_composer_qserv_gn, 16, 0, NULL,
6526 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6527 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6528 {"Email Address : ", "EmailAddress", h_composer_qserv_mail, 16, 0, NULL,
6529 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6530 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6531 {"Organization : ", "Organization", h_composer_qserv_org, 16, 0, NULL,
6532 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6533 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6534 {"Org Unit : ", "OrganizationalUnit", h_composer_qserv_unit, 16, 0, NULL,
6535 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6536 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6537 {"Country : ", "Country", h_composer_qserv_country, 16, 0, NULL,
6538 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6539 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6540 {"State : ", "State", h_composer_qserv_state, 16, 0, NULL,
6541 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6542 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6543 {"Locality : ", "Locality", h_composer_qserv_locality, 16, 0, NULL,
6544 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6545 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6546 {" ", "BlankLine", NO_HELP, 1, 0, NULL,
6547 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6548 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_NONE},
6549 {"Custom Filter : ", "CustomFilter", h_composer_qserv_custom, 16, 0, NULL,
6550 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6551 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6552 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6553 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
6555 #define QQ_QQ 0
6556 #define QQ_CN 1
6557 #define QQ_SN 2
6558 #define QQ_GN 3
6559 #define QQ_MAIL 4
6560 #define QQ_ORG 5
6561 #define QQ_UNIT 6
6562 #define QQ_COUNTRY 7
6563 #define QQ_STATE 8
6564 #define QQ_LOCALITY 9
6565 #define QQ_BLANK 10
6566 #define QQ_CUSTOM 11
6567 #define QQ_END 12
6569 static SAVED_QUERY_S *saved_params;
6573 * Formulate a query for LDAP servers and send it and view it.
6574 * This is called from the Address Book screen, either from ^T from the
6575 * composer (selecting) or just when browsing in the address book screen
6576 * (selecting == 0).
6578 * Args ps -- Pine struct
6579 * selecting -- This is set if we're just selecting an address, as opposed
6580 * to browsing on an LDAP server
6581 * who -- Tells us which server to query
6582 * error -- An error message allocated here and freed by caller.
6584 * Returns -- Null if not selecting, possibly an address if selecting.
6585 * The address is 1522 decoded and should be freed by the caller.
6587 char *
6588 query_server(struct pine *ps, int selecting, int *exit, int who, char **error)
6590 struct headerentry *he = NULL;
6591 PICO pbf;
6592 STORE_S *msgso = NULL;
6593 int i, lret, editor_result;
6594 int r = 4, flags;
6595 HelpType help = NO_HELP;
6596 #define FILTSIZE 1000
6597 char fbuf[FILTSIZE+1];
6598 char *ret = NULL;
6599 LDAP_CHOOSE_S *winning_e = NULL;
6600 LDAP_SERV_RES_S *free_when_done = NULL;
6601 SAVED_QUERY_S *sq = NULL;
6602 static ESCKEY_S ekey[] = {
6603 /* TRANSLATORS: go to more complex search screen */
6604 {ctrl('T'), 10, "^T", N_("To complex search")},
6605 {-1, 0, NULL, NULL}
6608 dprint((2, "- query_server(%s) -\n", selecting?"Selecting":""));
6610 if(!(ps->VAR_LDAP_SERVERS && ps->VAR_LDAP_SERVERS[0] &&
6611 ps->VAR_LDAP_SERVERS[0][0])){
6612 if(error)
6613 *error = cpystr(_("No LDAP server available for lookup"));
6615 return(ret);
6618 fbuf[0] = '\0';
6619 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
6620 while(r == 4 || r == 3){
6621 /* TRANSLATORS: we're asking for a character string to search for */
6622 r = optionally_enter(fbuf, -FOOTER_ROWS(ps), 0, sizeof(fbuf),
6623 _("String to search for : "),
6624 ekey, help, &flags);
6625 if(r == 3)
6626 help = help == NO_HELP ? h_dir_comp_search : NO_HELP;
6629 /* strip quotes that user typed by mistake */
6630 (void)removing_double_quotes(fbuf);
6632 if(r == 1 || (r != 10 && fbuf[0] == '\0')){
6633 ps->mangled_footer = 1;
6634 if(error)
6635 *error = cpystr(_("Cancelled"));
6637 return(ret);
6640 editor_result = COMP_EXIT; /* just to get right logic below */
6642 memset((void *)&pbf, 0, sizeof(pbf));
6644 if(r == 10){
6645 standard_picobuf_setup(&pbf);
6646 pbf.exittest = pico_simpleexit;
6647 pbf.exit_label = _("Search");
6648 pbf.canceltest = pico_simplecancel;
6649 if(saved_params){
6650 pbf.expander = restore_query_parameters;
6651 pbf.ctrlr_label = _("Restore");
6654 pbf.pine_anchor = set_titlebar(_("SEARCH DIRECTORY SERVER"),
6655 ps_global->mail_stream,
6656 ps_global->context_current,
6657 ps_global->cur_folder,
6658 ps_global->msgmap,
6659 0, FolderName, 0, 0, NULL);
6660 pbf.pine_flags |= P_NOBODY;
6662 /* An informational message */
6663 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
6664 pbf.msgtext = (void *)so_text(msgso);
6666 * It's nice if we can make it so these lines make sense even if
6667 * they don't all make it on the screen, because the user can't
6668 * scroll down to see them. So just make each line a whole sentence
6669 * that doesn't need the others below it to make sense.
6671 so_puts(msgso,
6672 _("\n Fill in some of the fields above to create a query."));
6673 so_puts(msgso,
6674 _("\n The match will be for the exact string unless you include wildcards (*)."));
6675 so_puts(msgso,
6676 _("\n All filled-in fields must match in order to be counted as a match."));
6677 so_puts(msgso,
6678 _("\n Press \"^R\" to restore previous query values (if you've queried previously)."));
6679 so_puts(msgso,
6680 _("\n \"^G\" for help specific to each item. \"^X\" to make the query, or \"^C\" to cancel."));
6683 he = (struct headerentry *)fs_get((QQ_END+1) *
6684 sizeof(struct headerentry));
6685 memset((void *)he, 0, (QQ_END+1) * sizeof(struct headerentry));
6686 for(i = QQ_QQ; i <= QQ_END; i++)
6687 he[i] = headents_for_query[i];
6689 pbf.headents = he;
6691 sq = copy_query_parameters(NULL);
6692 he[QQ_QQ].realaddr = &sq->qq;
6693 he[QQ_CN].realaddr = &sq->cn;
6694 he[QQ_SN].realaddr = &sq->sn;
6695 he[QQ_GN].realaddr = &sq->gn;
6696 he[QQ_MAIL].realaddr = &sq->mail;
6697 he[QQ_ORG].realaddr = &sq->org;
6698 he[QQ_UNIT].realaddr = &sq->unit;
6699 he[QQ_COUNTRY].realaddr = &sq->country;
6700 he[QQ_STATE].realaddr = &sq->state;
6701 he[QQ_LOCALITY].realaddr = &sq->locality;
6702 he[QQ_CUSTOM].realaddr = &sq->custom;
6704 /* pass to pico and let user set them */
6705 editor_result = pico(&pbf);
6706 ps->mangled_screen = 1;
6707 standard_picobuf_teardown(&pbf);
6709 if(editor_result & COMP_GOTHUP)
6710 hup_signal();
6711 else{
6712 fix_windsize(ps_global);
6713 init_signals();
6717 if(editor_result & COMP_EXIT &&
6718 ((r == 0 && *fbuf) ||
6719 (r == 10 && sq &&
6720 (*sq->qq || *sq->cn || *sq->sn || *sq->gn || *sq->mail ||
6721 *sq->org || *sq->unit || *sq->country || *sq->state ||
6722 *sq->locality || *sq->custom)))){
6723 LDAPLookupStyle style;
6724 WP_ERR_S wp_err;
6725 int need_and, mangled;
6726 char *string;
6727 CUSTOM_FILT_S *filter;
6728 SAVED_QUERY_S *s;
6730 s = copy_query_parameters(sq);
6731 save_query_parameters(s);
6733 if(r == 0){
6734 string = fbuf;
6735 filter = NULL;
6737 else{
6738 int categories = 0;
6740 categories = ((*sq->cn != '\0') ? 1 : 0) +
6741 ((*sq->sn != '\0') ? 1 : 0) +
6742 ((*sq->gn != '\0') ? 1 : 0) +
6743 ((*sq->mail != '\0') ? 1 : 0) +
6744 ((*sq->org != '\0') ? 1 : 0) +
6745 ((*sq->unit != '\0') ? 1 : 0) +
6746 ((*sq->country != '\0') ? 1 : 0) +
6747 ((*sq->state != '\0') ? 1 : 0) +
6748 ((*sq->locality != '\0') ? 1 : 0);
6749 need_and = (categories > 1);
6751 if((sq->cn ? strlen(sq->cn) : 0 +
6752 sq->sn ? strlen(sq->sn) : 0 +
6753 sq->gn ? strlen(sq->gn) : 0 +
6754 sq->mail ? strlen(sq->mail) : 0 +
6755 sq->org ? strlen(sq->org) : 0 +
6756 sq->unit ? strlen(sq->unit) : 0 +
6757 sq->country ? strlen(sq->country) : 0 +
6758 sq->state ? strlen(sq->state) : 0 +
6759 sq->locality ? strlen(sq->locality) : 0) > FILTSIZE - 100){
6760 if(error)
6761 *error = cpystr(_("Search strings too long"));
6763 goto all_done;
6766 if(categories > 0){
6768 snprintf(fbuf, sizeof(fbuf),
6769 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
6770 need_and ? "(&" : "",
6771 *sq->cn ? "(cn=" : "",
6772 *sq->cn ? sq->cn : "",
6773 *sq->cn ? ")" : "",
6774 *sq->sn ? "(sn=" : "",
6775 *sq->sn ? sq->sn : "",
6776 *sq->sn ? ")" : "",
6777 *sq->gn ? "(givenname=" : "",
6778 *sq->gn ? sq->gn : "",
6779 *sq->gn ? ")" : "",
6780 *sq->mail ? "(mail=" : "",
6781 *sq->mail ? sq->mail : "",
6782 *sq->mail ? ")" : "",
6783 *sq->org ? "(o=" : "",
6784 *sq->org ? sq->org : "",
6785 *sq->org ? ")" : "",
6786 *sq->unit ? "(ou=" : "",
6787 *sq->unit ? sq->unit : "",
6788 *sq->unit ? ")" : "",
6789 *sq->country ? "(c=" : "",
6790 *sq->country ? sq->country : "",
6791 *sq->country ? ")" : "",
6792 *sq->state ? "(st=" : "",
6793 *sq->state ? sq->state : "",
6794 *sq->state ? ")" : "",
6795 *sq->locality ? "(l=" : "",
6796 *sq->locality ? sq->locality : "",
6797 *sq->locality ? ")" : "",
6798 need_and ? ")" : "");
6799 fbuf[sizeof(fbuf)-1] = '\0';
6802 if(categories > 0 || *sq->custom)
6803 filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
6805 /* combine the configed filters with this filter */
6806 if(*sq->custom){
6807 string = "";
6808 filter->filt = sq->custom;
6809 filter->combine = 0;
6811 else if(*sq->qq && categories > 0){
6812 string = sq->qq;
6813 filter->filt = fbuf;
6814 filter->combine = 1;
6816 else if(categories > 0){
6817 string = "";
6818 filter->filt = fbuf;
6819 filter->combine = 0;
6821 else{
6822 string = sq->qq;
6823 filter = NULL;
6827 mangled = 0;
6828 memset(&wp_err, 0, sizeof(wp_err));
6829 wp_err.mangled = &mangled;
6830 style = selecting ? AlwaysDisplayAndMailRequired : AlwaysDisplay;
6832 /* maybe coming from composer */
6833 fix_windsize(ps_global);
6834 init_sigwinch();
6835 clear_cursor_pos();
6837 lret = ldap_lookup_all(string, who, 0, style, filter, &winning_e,
6838 &wp_err, &free_when_done);
6840 if(filter)
6841 fs_give((void **)&filter);
6843 if(wp_err.mangled)
6844 ps->mangled_screen = 1;
6846 if(wp_err.error){
6847 if(status_message_remaining() && error)
6848 *error = wp_err.error;
6849 else
6850 fs_give((void **)&wp_err.error);
6853 if(lret == 0 && winning_e && selecting){
6854 ADDRESS *addr;
6856 addr = address_from_ldap(winning_e);
6857 if(addr){
6858 if(!addr->host){
6859 addr->host = cpystr("missing-hostname");
6860 if(error){
6861 if(*error)
6862 fs_give((void **)error);
6864 *error = cpystr(_("Missing hostname in LDAP address"));
6868 ret = addr_list_string(addr, NULL, 1);
6869 if(!ret || !ret[0]){
6870 if(ret)
6871 fs_give((void **)&ret);
6873 if(exit)
6874 *exit = 1;
6876 if(error && !*error){
6877 char buf[200];
6879 snprintf(buf, sizeof(buf), _("No email address available for \"%s\""),
6880 (addr->personal && *addr->personal)
6881 ? addr->personal
6882 : "selected entry");
6883 buf[sizeof(buf)-1] = '\0';
6884 *error = cpystr(buf);
6888 mail_free_address(&addr);
6891 else if(lret == -1 && exit)
6892 *exit = 1;
6895 all_done:
6896 if(he)
6897 free_headents(&he);
6899 if(free_when_done)
6900 free_ldap_result_list(&free_when_done);
6902 if(winning_e)
6903 fs_give((void **)&winning_e);
6905 if(msgso)
6906 so_give(&msgso);
6908 if(sq)
6909 free_query_parameters(&sq);
6911 return(ret);
6916 * View all fields of an LDAP entry while browsing.
6918 * Args ps -- Pine struct
6919 * winning_e -- The struct containing the information about the entry
6920 * to be viewed.
6922 void
6923 view_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
6925 STORE_S *srcstore = NULL;
6926 SourceType srctype = CharStar;
6927 SCROLL_S sargs;
6928 HANDLE_S *handles = NULL;
6930 dprint((9, "- view_ldap_entry -\n"));
6932 if((srcstore=prep_ldap_for_viewing(ps,winning_e,srctype,&handles)) != NULL){
6933 memset(&sargs, 0, sizeof(SCROLL_S));
6934 sargs.text.text = so_text(srcstore);
6935 sargs.text.src = srctype;
6936 sargs.text.desc = _("expanded entry");
6937 sargs.text.handles= handles;
6938 sargs.bar.title = _("DIRECTORY ENTRY");
6939 sargs.proc.tool = process_ldap_cmd;
6940 sargs.proc.data.p = (void *) winning_e;
6941 sargs.help.text = h_ldap_view;
6942 sargs.help.title = _("HELP FOR DIRECTORY VIEW");
6943 sargs.keys.menu = &ldap_view_keymenu;
6944 setbitmap(sargs.keys.bitmap);
6946 if(handles)
6947 sargs.keys.menu->how_many = 2;
6948 else{
6949 sargs.keys.menu->how_many = 1;
6950 clrbitn(OTHER_KEY, sargs.keys.bitmap);
6953 scrolltool(&sargs);
6955 ps->mangled_screen = 1;
6956 so_give(&srcstore);
6957 free_handles(&handles);
6959 else
6960 q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
6965 * Compose a message to the email addresses contained in an LDAP entry.
6967 * Args ps -- Pine struct
6968 * winning_e -- The struct containing the information about the entry
6969 * to be viewed.
6971 void
6972 compose_to_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *e, int allow_role)
6974 struct berval **elecmail = NULL,
6975 **mail = NULL,
6976 **cn = NULL,
6977 **sn = NULL,
6978 **givenname = NULL;
6979 int num;
6980 size_t len = 0;
6982 dprint((9, "- compose_to_ldap_entry -\n"));
6984 if(e){
6985 char *a;
6986 BerElement *ber;
6988 for(a = ldap_first_attribute(e->ld, e->selected_entry, &ber);
6989 a != NULL;
6990 a = ldap_next_attribute(e->ld, e->selected_entry, ber)){
6992 if(strcmp(a, e->info_used->mailattr) == 0){
6993 if(!mail)
6994 mail = ldap_get_values_len(e->ld, e->selected_entry, a);
6996 else if(strcmp(a, "electronicmail") == 0){
6997 if(!elecmail)
6998 elecmail = ldap_get_values_len(e->ld, e->selected_entry, a);
7000 else if(strcmp(a, e->info_used->cnattr) == 0){
7001 if(!cn)
7002 cn = ldap_get_values_len(e->ld, e->selected_entry, a);
7004 else if(strcmp(a, e->info_used->gnattr) == 0){
7005 if(!givenname)
7006 givenname = ldap_get_values_len(e->ld, e->selected_entry, a);
7008 else if(strcmp(a, e->info_used->snattr) == 0){
7009 if(!sn)
7010 sn = ldap_get_values_len(e->ld, e->selected_entry, a);
7013 our_ldap_memfree(a);
7017 if(elecmail){
7018 if(ALPINE_LDAP_can_use(elecmail) && !mail)
7019 mail = elecmail;
7020 else
7021 ldap_value_free_len(elecmail);
7023 elecmail = NULL;
7026 for(num = 0; ALPINE_LDAP_usable(mail, num); num++)
7027 len += strlen(mail[num]->bv_val) + 1;
7029 if(len){
7030 char *p, *address, *fn = NULL;
7031 BuildTo bldto;
7032 SAVE_STATE_S state;
7034 address = (char *)fs_get(len * sizeof(char));
7035 p = address;
7036 num = 0;
7037 while(ALPINE_LDAP_usable(mail, num)){
7038 sstrncpy(&p, mail[num]->bv_val, len-(p-address));
7039 num++;
7040 if(mail[num])
7041 sstrncpy(&p, ",", len-(p-address));
7044 address[len-1] = '\0';
7047 * If we have a fullname and there is only a single address and
7048 * the address doesn't seem to have a fullname with it, add it.
7050 if(ALPINE_LDAP_can_use(mail) && !mail[1]){
7051 if(ALPINE_LDAP_can_use(cn))
7052 fn = cpystr(cn[0]->bv_val);
7053 else if(ALPINE_LDAP_can_use(sn) && ALPINE_LDAP_can_use(givenname)){
7054 size_t l;
7056 l = strlen(givenname[0]->bv_val) + strlen(sn[0]->bv_val) + 1;
7057 fn = (char *) fs_get((l+1) * sizeof(char));
7058 snprintf(fn, l+1, "%s %s", givenname[0]->bv_val, sn[0]->bv_val);
7059 fn[l] = '\0';
7063 if(ALPINE_LDAP_can_use(mail) && !mail[1] && fn){
7064 ADDRESS *adrlist = NULL;
7065 char *tmp_a_string;
7067 tmp_a_string = cpystr(address);
7068 rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
7069 fs_give((void **)&tmp_a_string);
7070 if(adrlist && !adrlist->next && !adrlist->personal){
7071 char *new_address;
7072 size_t len;
7073 RFC822BUFFER rbuf;
7075 adrlist->personal = cpystr(fn);
7076 len = est_size(adrlist);
7077 new_address = (char *) fs_get(len * sizeof(char));
7078 new_address[0] ='\0';
7079 rbuf.f = dummy_soutr;
7080 rbuf.s = NULL;
7081 rbuf.beg = new_address;
7082 rbuf.cur = new_address;
7083 rbuf.end = new_address+len-1;
7084 /* this will quote it if it needs quoting */
7085 rfc822_output_address_list(&rbuf, adrlist, 0L, NULL);
7086 *rbuf.cur = '\0';
7087 fs_give((void **)&address);
7088 address = new_address;
7091 if(adrlist)
7092 mail_free_address(&adrlist);
7095 bldto.type = Str;
7096 bldto.arg.str = address;
7098 save_state(&state);
7099 ab_compose_internal(bldto, allow_role);
7100 restore_state(&state);
7101 fs_give((void **)&address);
7102 if(fn)
7103 fs_give((void **)&fn);
7106 * Window size may have changed in composer.
7107 * Pine_send will have reset the window size correctly,
7108 * but we still have to reset our address book data structures.
7110 if(as.initialized)
7111 ab_resize();
7113 ps_global->mangled_screen = 1;
7115 else
7116 q_status_message(SM_ORDER, 0, 4, _("No address to compose to"));
7118 if(mail)
7119 ldap_value_free_len(mail);
7120 if(cn)
7121 ldap_value_free_len(cn);
7122 if(sn)
7123 ldap_value_free_len(sn);
7124 if(givenname)
7125 ldap_value_free_len(givenname);
7130 * Forward the text of an LDAP entry via email (not a vcard attachment)
7132 * Args ps -- Pine struct
7133 * winning_e -- The struct containing the information about the entry
7134 * to be viewed.
7136 void
7137 forward_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
7139 STORE_S *srcstore = NULL;
7140 SourceType srctype = CharStar;
7142 dprint((9, "- forward_ldap_entry -\n"));
7144 if((srcstore = prep_ldap_for_viewing(ps,winning_e,srctype,NULL)) != NULL){
7145 forward_text(ps, so_text(srcstore), srctype);
7146 ps->mangled_screen = 1;
7147 so_give(&srcstore);
7149 else
7150 q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
7154 STORE_S *
7155 prep_ldap_for_viewing(struct pine *ps, LDAP_CHOOSE_S *winning_e, SourceType srctype, HANDLE_S **handlesp)
7157 STORE_S *store = NULL;
7158 char *a, *tmp;
7159 BerElement *ber;
7160 int i, width;
7161 #define W (1000)
7162 #define INDENTHERE (22)
7163 char obuf[W+10];
7164 char hdr[6*INDENTHERE+1], hdr2[6*INDENTHERE+1];
7165 struct berval **cn = NULL;
7166 int indent = INDENTHERE;
7168 if(!(store = so_get(srctype, NULL, EDIT_ACCESS)))
7169 return(store);
7171 /* for mailto handles so user can select individual email addrs */
7172 if(handlesp)
7173 init_handles(handlesp);
7175 width = MAX(ps->ttyo->screen_cols, 25);
7177 snprintf(hdr2, sizeof(hdr2), "%-*.*s: ", indent-2,indent-2, "");
7178 hdr2[sizeof(hdr2)-1] = '\0';
7180 if(sizeof(obuf) > ps->ttyo->screen_cols+1){
7181 memset((void *)obuf, '-', ps->ttyo->screen_cols * sizeof(char));
7182 obuf[ps->ttyo->screen_cols] = '\n';
7183 obuf[ps->ttyo->screen_cols+1] = '\0';
7186 a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
7188 so_puts(store, obuf);
7189 if((tmp = fold(a, width, width, "", " ", FLD_NONE)) != NULL){
7190 so_puts(store, tmp);
7191 fs_give((void **)&tmp);
7194 so_puts(store, obuf);
7195 so_puts(store, "\n");
7197 our_ldap_dn_memfree(a);
7199 for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
7200 a != NULL;
7201 a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
7203 if(a && *a){
7204 struct berval **vals;
7205 char *fn = NULL;
7207 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
7209 /* save this for mailto */
7210 if(handlesp && !cn && !strcmp(a, winning_e->info_used->cnattr))
7211 cn = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
7213 if(vals){
7214 int do_mailto;
7216 do_mailto = (handlesp &&
7217 !strcmp(a, winning_e->info_used->mailattr));
7219 utf8_snprintf(hdr, sizeof(hdr), "%-*.*w: ", indent-2,indent-2,
7220 ldap_translate(a, winning_e->info_used));
7221 hdr[sizeof(hdr)-1] = '\0';
7222 for(i = 0; ALPINE_LDAP_usable(vals, i); i++){
7223 if(do_mailto){
7224 ADDRESS *ad = NULL;
7225 HANDLE_S *h;
7226 char buf[20];
7227 char *tmp_a_string;
7228 char *addr, *new_addr, *enc_addr;
7229 char *path = NULL;
7231 addr = cpystr(vals[i]->bv_val);
7232 if(ALPINE_LDAP_can_use(cn))
7233 fn = cpystr(cn[0]->bv_val);
7235 if(fn){
7236 tmp_a_string = cpystr(addr);
7237 rfc822_parse_adrlist(&ad, tmp_a_string, "@");
7238 fs_give((void **)&tmp_a_string);
7239 if(ad && !ad->next && !ad->personal){
7240 RFC822BUFFER rbuf;
7241 size_t len;
7243 ad->personal = cpystr(fn);
7244 len = est_size(ad);
7245 new_addr = (char *) fs_get(len * sizeof(char));
7246 new_addr[0] = '\0';
7247 /* this will quote it if it needs quoting */
7248 rbuf.f = dummy_soutr;
7249 rbuf.s = NULL;
7250 rbuf.beg = new_addr;
7251 rbuf.cur = new_addr;
7252 rbuf.end = new_addr+len-1;
7253 rfc822_output_address_list(&rbuf, ad, 0L, NULL);
7254 *rbuf.cur = '\0';
7255 fs_give((void **) &addr);
7256 addr = new_addr;
7259 if(ad)
7260 mail_free_address(&ad);
7262 fs_give((void **)&fn);
7265 if((enc_addr = rfc1738_encode_mailto(addr)) != NULL){
7266 size_t l;
7268 l = strlen(enc_addr) + 7;
7269 path = (char *) fs_get((l+1) * sizeof(char));
7270 snprintf(path, l+1, "mailto:%s", enc_addr);
7271 path[l] = '\0';
7272 fs_give((void **)&enc_addr);
7275 fs_give((void **)&addr);
7277 if(path){
7278 h = new_handle(handlesp);
7279 h->type = URL;
7280 h->h.url.path = path;
7281 snprintf(buf, sizeof(buf), "%d", h->key);
7282 buf[sizeof(buf)-1] = '\0';
7285 * Don't try to fold this address. Just put it on
7286 * one line and let scrolltool worry about
7287 * cutting it off before it goes past the
7288 * right hand edge. Otherwise, we have to figure
7289 * out how to handle the wrapped handle.
7291 snprintf(obuf, sizeof(obuf), "%c%c%c%s%s%c%c%c%c",
7292 TAG_EMBED, TAG_HANDLE,
7293 (int) strlen(buf), buf, vals[i]->bv_val,
7294 TAG_EMBED, TAG_BOLDOFF,
7295 TAG_EMBED, TAG_INVOFF);
7296 obuf[sizeof(obuf)-1] = '\0';
7298 so_puts(store, (i==0) ? hdr : hdr2);
7299 so_puts(store, obuf);
7300 so_puts(store, "\n");
7302 else{
7303 snprintf(obuf, sizeof(obuf), "%s", vals[i]->bv_val);
7304 obuf[sizeof(obuf)-1] = '\0';
7306 if((tmp = fold(obuf, width, width,
7307 (i==0) ? hdr : hdr2,
7308 repeat_char(indent+2, SPACE),
7309 FLD_NONE)) != NULL){
7310 so_puts(store, tmp);
7311 fs_give((void **)&tmp);
7315 else{
7316 snprintf(obuf, sizeof(obuf), "%s", vals[i]->bv_val);
7317 obuf[sizeof(obuf)-1] = '\0';
7319 if((tmp = fold(obuf, width, width,
7320 (i==0) ? hdr : hdr2,
7321 repeat_char(indent+2, SPACE),
7322 FLD_NONE)) != NULL){
7323 so_puts(store, tmp);
7324 fs_give((void **)&tmp);
7329 ldap_value_free_len(vals);
7331 else{
7332 utf8_snprintf(obuf, sizeof(obuf), "%-*.*w\n", indent-1,indent-1,
7333 ldap_translate(a, winning_e->info_used));
7334 obuf[sizeof(obuf)-1] = '\0';
7335 so_puts(store, obuf);
7339 our_ldap_memfree(a);
7342 if(cn)
7343 ldap_value_free_len(cn);
7345 return(store);
7350 process_ldap_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
7352 int rv = 1;
7354 ps_global->next_screen = SCREEN_FUN_NULL;
7356 switch(cmd){
7357 case MC_SAVE :
7358 save_ldap_entry(ps_global, sparms->proc.data.p, 0);
7359 rv = 0;
7360 break;
7362 case MC_COMPOSE :
7363 compose_to_ldap_entry(ps_global, sparms->proc.data.p, 0);
7364 rv = 0;
7365 break;
7367 case MC_ROLE :
7368 compose_to_ldap_entry(ps_global, sparms->proc.data.p, 1);
7369 rv = 0;
7370 break;
7372 default:
7373 alpine_panic("Unexpected command in process_ldap_cmd");
7374 break;
7377 return(rv);
7382 pico_simpleexit(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
7383 char **result)
7385 if(result)
7386 *result = NULL;
7388 return(0);
7391 char *
7392 pico_simplecancel(void (*redraw_pico)(void))
7394 return("Cancelled");
7399 * Store query parameters so that they can be recalled by user later with ^R.
7401 void
7402 save_query_parameters(SAVED_QUERY_S *params)
7404 free_saved_query_parameters();
7405 saved_params = params;
7409 SAVED_QUERY_S *
7410 copy_query_parameters(SAVED_QUERY_S *params)
7412 SAVED_QUERY_S *sq;
7414 sq = (SAVED_QUERY_S *)fs_get(sizeof(SAVED_QUERY_S));
7415 memset((void *)sq, 0, sizeof(SAVED_QUERY_S));
7417 if(params && params->qq)
7418 sq->qq = cpystr(params->qq);
7419 else
7420 sq->qq = cpystr("");
7422 if(params && params->cn)
7423 sq->cn = cpystr(params->cn);
7424 else
7425 sq->cn = cpystr("");
7427 if(params && params->sn)
7428 sq->sn = cpystr(params->sn);
7429 else
7430 sq->sn = cpystr("");
7432 if(params && params->gn)
7433 sq->gn = cpystr(params->gn);
7434 else
7435 sq->gn = cpystr("");
7437 if(params && params->mail)
7438 sq->mail = cpystr(params->mail);
7439 else
7440 sq->mail = cpystr("");
7442 if(params && params->org)
7443 sq->org = cpystr(params->org);
7444 else
7445 sq->org = cpystr("");
7447 if(params && params->unit)
7448 sq->unit = cpystr(params->unit);
7449 else
7450 sq->unit = cpystr("");
7452 if(params && params->country)
7453 sq->country = cpystr(params->country);
7454 else
7455 sq->country = cpystr("");
7457 if(params && params->state)
7458 sq->state = cpystr(params->state);
7459 else
7460 sq->state = cpystr("");
7462 if(params && params->locality)
7463 sq->locality = cpystr(params->locality);
7464 else
7465 sq->locality = cpystr("");
7467 if(params && params->custom)
7468 sq->custom = cpystr(params->custom);
7469 else
7470 sq->custom = cpystr("");
7472 return(sq);
7476 void
7477 free_saved_query_parameters(void)
7479 if(saved_params)
7480 free_query_parameters(&saved_params);
7484 void
7485 free_query_parameters(SAVED_QUERY_S **parm)
7487 if(parm){
7488 if(*parm){
7489 if((*parm)->qq)
7490 fs_give((void **)&(*parm)->qq);
7491 if((*parm)->cn)
7492 fs_give((void **)&(*parm)->cn);
7493 if((*parm)->sn)
7494 fs_give((void **)&(*parm)->sn);
7495 if((*parm)->gn)
7496 fs_give((void **)&(*parm)->gn);
7497 if((*parm)->mail)
7498 fs_give((void **)&(*parm)->mail);
7499 if((*parm)->org)
7500 fs_give((void **)&(*parm)->org);
7501 if((*parm)->unit)
7502 fs_give((void **)&(*parm)->unit);
7503 if((*parm)->country)
7504 fs_give((void **)&(*parm)->country);
7505 if((*parm)->state)
7506 fs_give((void **)&(*parm)->state);
7507 if((*parm)->locality)
7508 fs_give((void **)&(*parm)->locality);
7509 if((*parm)->custom)
7510 fs_give((void **)&(*parm)->custom);
7512 fs_give((void **)parm);
7519 * A callback from pico to restore the saved query parameters.
7521 * Args he -- Unused.
7522 * s -- The place to return the allocated array of values.
7524 * Returns -- 1 if there are parameters to return, 0 otherwise.
7527 restore_query_parameters(struct headerentry *he, char ***s)
7529 int retval = 0, i = 0;
7531 if(s)
7532 *s = NULL;
7534 if(saved_params && s){
7535 *s = (char **)fs_get((QQ_END + 1) * sizeof(char *));
7536 (*s)[i++] = cpystr(saved_params->qq ? saved_params->qq : "");
7537 (*s)[i++] = cpystr(saved_params->cn ? saved_params->cn : "");
7538 (*s)[i++] = cpystr(saved_params->sn ? saved_params->sn : "");
7539 (*s)[i++] = cpystr(saved_params->gn ? saved_params->gn : "");
7540 (*s)[i++] = cpystr(saved_params->mail ? saved_params->mail : "");
7541 (*s)[i++] = cpystr(saved_params->org ? saved_params->org : "");
7542 (*s)[i++] = cpystr(saved_params->unit ? saved_params->unit : "");
7543 (*s)[i++] = cpystr(saved_params->country ? saved_params->country : "");
7544 (*s)[i++] = cpystr(saved_params->state ? saved_params->state : "");
7545 (*s)[i++] = cpystr(saved_params->locality ? saved_params->locality :"");
7546 (*s)[i++] = cpystr(saved_params->custom ? saved_params->custom :"");
7547 (*s)[i] = 0;
7548 retval = 1;
7551 return(retval);
7556 * Internal handler for viewing an LDAP url.
7559 url_local_ldap(char *url)
7561 LDAP *ld;
7562 struct timeval t;
7563 int ld_err, mangled = 0, we_cancel, retval = 0, proto = 3;
7564 int we_turned_on = 0;
7565 char ebuf[300];
7566 LDAPMessage *result;
7567 LDAP_SERV_S *info;
7568 LDAP_SERV_RES_S *serv_res = NULL;
7569 LDAPURLDesc *ldapurl = NULL;
7570 WP_ERR_S wp_err;
7572 dprint((2, "url_local_ldap(%s)\n", url ? url : "?"));
7574 ld_err = ldap_url_parse(url, &ldapurl);
7575 if(ld_err || !ldapurl){
7576 snprintf(ebuf, sizeof(ebuf), "URL parse failed for %s", url);
7577 ebuf[sizeof(ebuf)-1] = '\0';
7578 q_status_message(SM_ORDER, 3, 5, ebuf);
7579 return(retval);
7582 if(!ldapurl->lud_host){
7583 /* TRNASLATORS: No host in <url> */
7584 snprintf(ebuf, sizeof(ebuf), _("No host in %s"), url);
7585 ebuf[sizeof(ebuf)-1] = '\0';
7586 q_status_message(SM_ORDER, 3, 5, ebuf);
7587 ldap_free_urldesc(ldapurl);
7588 return(retval);
7591 we_turned_on = intr_handling_on();
7592 we_cancel = busy_cue(_("Searching for LDAP url"), NULL, 0);
7593 ps_global->mangled_footer = 1;
7595 #if (LDAPAPI >= 11)
7596 #ifdef _WINDOWS
7597 if((ld = ldap_init(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7598 #else
7599 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "ldap://%s:%d", ldapurl->lud_host, ldapurl->lud_port);
7600 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
7601 if(ldap_initialize(&ld, tmp_20k_buf) != LDAP_SUCCESS)
7602 #endif
7603 #else
7604 if((ld = ldap_open(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7605 #endif
7607 if(we_cancel){
7608 cancel_busy_cue(-1);
7609 we_cancel = 0;
7612 q_status_message(SM_ORDER,3,5, _("LDAP search failed: can't initialize"));
7614 else if(!ps_global->intr_pending){
7615 if(ldap_v3_is_supported(ld) &&
7616 our_ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) == 0){
7617 dprint((5, "ldap: using version 3 protocol\n"));
7621 * If we don't set RESTART then the select() waiting for the answer
7622 * in libldap will be interrupted and stopped by our busy_cue.
7624 our_ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
7626 t.tv_sec = 30; t.tv_usec = 0;
7627 #ifdef _WINDOWS
7628 ld_err = ldap_search_st(ld, ldapurl->lud_dn, ldapurl->lud_scope,
7629 ldapurl->lud_filter, ldapurl->lud_attrs,
7630 0, &t, &result);
7631 #else
7632 ld_err = ldap_search_ext_s(ld, ldapurl->lud_dn, ldapurl->lud_scope,
7633 ldapurl->lud_filter, ldapurl->lud_attrs, 0,
7634 NULL, NULL,
7635 &t, DEF_LDAP_SIZE, &result);
7636 #endif
7637 if(ld_err != LDAP_SUCCESS){
7638 if(we_cancel){
7639 cancel_busy_cue(-1);
7640 we_cancel = 0;
7643 snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s"), ldap_err2string(ld_err));
7644 ebuf[sizeof(ebuf)-1] = '\0';
7645 q_status_message(SM_ORDER, 3, 5, ebuf);
7646 #ifdef _WINDOWS
7647 ldap_unbind(ld);
7648 #else
7649 ldap_unbind_ext(ld, NULL, NULL);
7650 #endif /* _WINDOWS */
7652 else if(!ps_global->intr_pending){
7653 if(we_cancel){
7654 cancel_busy_cue(-1);
7655 we_cancel = 0;
7658 if(we_turned_on){
7659 intr_handling_off();
7660 we_turned_on = 0;
7663 if(ldap_count_entries(ld, result) == 0){
7664 q_status_message(SM_ORDER, 3, 5, _("No matches found for url"));
7665 #ifdef _WINDOWS
7666 ldap_unbind(ld);
7667 #else
7668 ldap_unbind_ext(ld, NULL, NULL);
7669 #endif /* _WINDOWS */
7670 if(result)
7671 ldap_msgfree(result);
7673 else{
7674 serv_res = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
7675 memset((void *)serv_res, 0, sizeof(*serv_res));
7676 serv_res->ld = ld;
7677 serv_res->res = result;
7678 info = (LDAP_SERV_S *)fs_get(sizeof(LDAP_SERV_S));
7679 memset((void *)info, 0, sizeof(*info));
7680 info->mailattr = cpystr(DEF_LDAP_MAILATTR);
7681 info->snattr = cpystr(DEF_LDAP_SNATTR);
7682 info->gnattr = cpystr(DEF_LDAP_GNATTR);
7683 info->cnattr = cpystr(DEF_LDAP_CNATTR);
7684 serv_res->info_used = info;
7685 memset(&wp_err, 0, sizeof(wp_err));
7686 wp_err.mangled = &mangled;
7688 ask_user_which_entry(serv_res, NULL, NULL, &wp_err, DisplayForURL);
7689 if(wp_err.error){
7690 q_status_message(SM_ORDER, 3, 5, wp_err.error);
7691 fs_give((void **)&wp_err.error);
7694 if(mangled)
7695 ps_global->mangled_screen = 1;
7697 free_ldap_result_list(&serv_res);
7698 retval = 1;
7703 if(we_cancel)
7704 cancel_busy_cue(-1);
7706 if(we_turned_on)
7707 intr_handling_off();
7709 if(ldapurl)
7710 ldap_free_urldesc(ldapurl);
7712 return(retval);
7714 #endif /* ENABLE_LDAP */