* Changes in the source code of Alpine to define internal prototypes
[alpine.git] / alpine / adrbkcmd.c
blobf36b6956a62b53d7e3e139fe2d88a0b38b480783
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 adrbkcmds.c
17 Commands called from the addrbook screens.
18 ====*/
21 #include "headers.h"
22 #include "adrbkcmd.h"
23 #include "addrbook.h"
24 #include "takeaddr.h"
25 #include "status.h"
26 #include "keymenu.h"
27 #include "mailview.h"
28 #include "mailcmd.h"
29 #include "mailindx.h"
30 #include "radio.h"
31 #include "folder.h"
32 #include "reply.h"
33 #include "help.h"
34 #include "titlebar.h"
35 #include "signal.h"
36 #include "roleconf.h"
37 #include "send.h"
38 #include "../pith/adrbklib.h"
39 #include "../pith/addrbook.h"
40 #include "../pith/abdlc.h"
41 #include "../pith/ablookup.h"
42 #include "../pith/bldaddr.h"
43 #include "../pith/ldap.h"
44 #include "../pith/state.h"
45 #include "../pith/filter.h"
46 #include "../pith/conf.h"
47 #include "../pith/msgno.h"
48 #include "../pith/addrstring.h"
49 #include "../pith/remote.h"
50 #include "../pith/url.h"
51 #include "../pith/util.h"
52 #include "../pith/detoken.h"
53 #include "../pith/stream.h"
54 #include "../pith/send.h"
55 #include "../pith/list.h"
56 #include "../pith/busy.h"
57 #include "../pith/icache.h"
58 #include "../pith/osdep/color.h"
61 /* internal prototypes */
62 int url_hilite_abook(long, char *, LT_INS_S **, void *);
63 int process_abook_view_cmd(int, MSGNO_S *, SCROLL_S *);
64 int expand_addrs_for_pico(struct headerentry *, char ***);
65 char *view_message_for_pico(char **);
66 int verify_nick(char *, char **, char **, BUILDER_ARG *, int *);
67 int verify_addr(char *, char **, char **, BUILDER_ARG *, int *);
68 int pico_sendexit_for_adrbk(struct headerentry *, void(*)(void), int, char **);
69 char *pico_cancelexit_for_adrbk(char *, void (*)(void));
70 char *pico_cancel_for_adrbk_take(void (*)(void));
71 char *pico_cancel_for_adrbk_edit(void (*)(void));
72 int ab_modify_abook_list(int, int, int, char *, char *, char *);
73 int convert_abook_to_remote(struct pine *, PerAddrBook *, char *, size_t, int);
74 int any_rule_files_to_warn_about(struct pine *);
75 int verify_folder_name(char *,char **,char **,BUILDER_ARG *, int *);
76 int verify_server_name(char *,char **,char **,BUILDER_ARG *, int *);
77 int verify_abook_nick(char *, char **,char **,BUILDER_ARG *, int *);
78 int do_the_shuffle(int *, int, int, char **);
79 void ab_compose_internal(BuildTo, int);
80 int ab_export(struct pine *, long, int, int);
81 VCARD_INFO_S *prepare_abe_for_vcard(struct pine *, AdrBk_Entry *, int);
82 void write_single_tab_entry(gf_o_t, VCARD_INFO_S *);
83 int percent_done_copying(void);
84 int cmp_action_list(const qsort_t *, const qsort_t *);
85 void set_act_list_member(ACTION_LIST_S *, a_c_arg_t, PerAddrBook *, PerAddrBook *, char *);
86 void convert_pinerc_to_remote(struct pine *, char *);
88 #ifdef ENABLE_LDAP
89 typedef struct _saved_query {
90 char *qq,
91 *cn,
92 *sn,
93 *gn,
94 *mail,
95 *org,
96 *unit,
97 *country,
98 *state,
99 *locality,
100 *custom;
101 } SAVED_QUERY_S;
103 int process_ldap_cmd(int, MSGNO_S *, SCROLL_S *);
104 int pico_simpleexit(struct headerentry *, void (*)(void), int, char **);
105 char *pico_simplecancel(void (*)(void));
106 void save_query_parameters(SAVED_QUERY_S *);
107 SAVED_QUERY_S *copy_query_parameters(SAVED_QUERY_S *);
108 void free_query_parameters(SAVED_QUERY_S **);
109 int restore_query_parameters(struct headerentry *, char ***);
111 static char *expander_address;
112 #endif /* ENABLE_LDAP */
114 static char *fakedomain = "@";
117 #define VIEW_ABOOK_NONE 0
118 #define VIEW_ABOOK_EDITED 1
119 #define VIEW_ABOOK_WARPED 2
122 * View an addrbook entry.
123 * Call scrolltool to do the work.
125 void
126 view_abook_entry(struct pine *ps, long int cur_line)
128 AdrBk_Entry *abe;
129 STORE_S *in_store, *out_store;
130 char *string, *errstr;
131 SCROLL_S sargs;
132 HANDLE_S *handles = NULL;
133 URL_HILITE_S uh;
134 gf_o_t pc;
135 gf_i_t gc;
136 int cmd, abook_indent;
137 long offset = 0L;
138 char b[500];
140 dprint((5, "- view_abook_entry -\n"));
142 if(is_addr(cur_line)){
143 abe = ae(cur_line);
144 if(!abe){
145 q_status_message(SM_ORDER, 0, 3, _("Error reading entry"));
146 return;
149 else{
150 q_status_message(SM_ORDER, 0, 3, _("Nothing to view"));
151 return;
154 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
155 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
156 return;
159 abook_indent = utf8_width(_("Nickname")) + 2;
161 /* TRANSLATORS: Nickname is a shorthand name for something */
162 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
163 so_puts(in_store, b);
164 if(abe->nickname)
165 so_puts(in_store, abe->nickname);
167 so_puts(in_store, "\015\012");
169 /* TRANSLATORS: Full name is the name that goes with an email address.
170 For example, in
171 Fred Flintstone <fred@bedrock.org>
172 Fred Flintstone is the Full Name. */
173 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
174 so_puts(in_store, b);
175 if(abe->fullname)
176 so_puts(in_store, abe->fullname);
178 so_puts(in_store, "\015\012");
180 /* TRANSLATORS: Fcc is an abbreviation for File carbon copy. It is like
181 a cc which is a copy of a message that goes to somebody other than the
182 main recipient, only this is a copy of a message which is put into a
183 file on the user's computer. */
184 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
185 so_puts(in_store, b);
186 if(abe->fcc)
187 so_puts(in_store, abe->fcc);
189 so_puts(in_store, "\015\012");
191 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, AB_COMMENT_STR);
192 so_puts(in_store, b);
193 if(abe->extra)
194 so_puts(in_store, abe->extra);
196 so_puts(in_store, "\015\012");
198 /* TRANSLATORS: Addresses refers to email Addresses */
199 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
200 so_puts(in_store, b);
201 if(abe->tag == Single){
202 char *tmp = abe->addr.addr ? abe->addr.addr : "";
203 #ifdef ENABLE_LDAP
204 if(!strncmp(tmp, QRUN_LDAP, LEN_QRL))
205 string = LDAP_DISP;
206 else
207 #endif
208 string = tmp;
210 so_puts(in_store, string);
212 else{
213 char **ll;
215 for(ll = abe->addr.list; ll && *ll; ll++){
216 if(ll != abe->addr.list){
217 so_puts(in_store, "\015\012");
218 so_puts(in_store, repeat_char(abook_indent+2, SPACE));
221 #ifdef ENABLE_LDAP
222 if(!strncmp(*ll, QRUN_LDAP, LEN_QRL))
223 string = LDAP_DISP;
224 else
225 #endif
226 string = *ll;
228 so_puts(in_store, string);
230 if(*(ll+1)) /* not the last one */
231 so_puts(in_store, ",");
235 so_puts(in_store, "\015\012");
238 if(!(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
239 so_give(&in_store);
240 q_status_message(SM_ORDER | SM_DING, 3, 3,
241 _("Error allocating space."));
242 return;
245 so_seek(in_store, 0L, 0);
247 init_handles(&handles);
248 gf_filter_init();
250 if(F_ON(F_VIEW_SEL_URL, ps_global)
251 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
252 || F_ON(F_SCAN_ADDR, ps_global))
253 gf_link_filter(gf_line_test,
254 gf_line_test_opt(url_hilite_abook,
255 gf_url_hilite_opt(&uh,&handles,0)));
257 gf_link_filter(gf_wrap, gf_wrap_filter_opt(ps->ttyo->screen_cols - 4,
258 ps->ttyo->screen_cols,
259 NULL, abook_indent+2,
260 GFW_HANDLES));
261 gf_link_filter(gf_nvtnl_local, NULL);
263 gf_set_so_readc(&gc, in_store);
264 gf_set_so_writec(&pc, out_store);
266 if((errstr = gf_pipe(gc, pc)) != NULL){
267 so_give(&in_store);
268 so_give(&out_store);
269 free_handles(&handles);
270 q_status_message1(SM_ORDER | SM_DING, 3, 3,
271 /* TRANSLATORS: %s is the error message */
272 _("Can't format entry: %s"), errstr);
273 return;
276 gf_clear_so_writec(out_store);
277 gf_clear_so_readc(in_store);
279 memset(&sargs, 0, sizeof(SCROLL_S));
280 sargs.text.text = so_text(out_store);
281 sargs.text.src = CharStar;
282 sargs.text.desc = _("expanded entry");
283 sargs.text.handles = handles;
285 if(offset){ /* resize? preserve paging! */
286 sargs.start.on = Offset;
287 sargs.start.loc.offset = offset;
288 offset = 0L;
291 /* TRANSLATORS: a screen title. We are viewing an address book */
292 sargs.bar.title = _("ADDRESS BOOK (View)");
293 sargs.bar.style = TextPercent;
294 sargs.proc.tool = process_abook_view_cmd;
295 sargs.proc.data.i = VIEW_ABOOK_NONE;
296 sargs.resize_exit = 1;
297 sargs.help.text = h_abook_view;
298 /* TRANSLATORS: help screen title */
299 sargs.help.title = _("HELP FOR ADDRESS BOOK VIEW");
300 sargs.keys.menu = &abook_view_keymenu;
301 setbitmap(sargs.keys.bitmap);
303 if(handles)
304 sargs.keys.menu->how_many = 2;
305 else{
306 sargs.keys.menu->how_many = 1;
307 clrbitn(OTHER_KEY, sargs.keys.bitmap);
310 if((cmd = scrolltool(&sargs)) == MC_RESIZE)
311 offset = sargs.start.loc.offset;
313 so_give(&out_store);
314 free_handles(&handles);
316 while(cmd == MC_RESIZE);
318 so_give(&in_store);
320 if(sargs.proc.data.i != VIEW_ABOOK_NONE){
321 long old_l_p_p = 0, old_top_ent = 0, old_cur_row = 0;
323 if(sargs.proc.data.i == VIEW_ABOOK_WARPED){
325 * Warped means we got plopped down somewhere in the display
326 * list so that we don't know where we are relative to where
327 * we were before we warped. The current line number will
328 * be zero, since that is what the warp would have set.
330 as.top_ent = first_line(0L - as.l_p_page/2L);
331 as.cur_row = 0L - as.top_ent;
333 else if(sargs.proc.data.i == VIEW_ABOOK_EDITED){
334 old_l_p_p = as.l_p_page;
335 old_top_ent = as.top_ent;
336 old_cur_row = as.cur_row;
339 /* Window size may have changed while in pico. */
340 ab_resize();
342 /* fix up what ab_resize messed up */
343 if(sargs.proc.data.i != VIEW_ABOOK_WARPED && old_l_p_p == as.l_p_page){
344 as.top_ent = old_top_ent;
345 as.cur_row = old_cur_row;
346 as.old_cur_row = old_cur_row;
349 cur_line = as.top_ent+as.cur_row;
352 ps->mangled_screen = 1;
357 url_hilite_abook(long int linenum, char *line, LT_INS_S **ins, void *local)
359 register char *lp;
361 if((lp = strchr(line, ':')) &&
362 !strncmp(line, AB_COMMENT_STR, strlen(AB_COMMENT_STR)))
363 (void) url_hilite(linenum, lp + 1, ins, local);
365 return(0);
370 process_abook_view_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
372 int rv = 1, i;
373 PerAddrBook *pab;
374 AddrScrn_Disp *dl;
375 static ESCKEY_S text_or_vcard[] = {
376 /* TRANSLATORS: Text refers to plain old text, probably the text of
377 an email message */
378 {'t', 't', "T", N_("Text")},
379 /* TRANSLATORS: A VCard is kind of like an electronic business card. It is not
380 something specific to alpine, it is universal. */
381 {'v', 'v', "V", N_("VCard")},
382 {-1, 0, NULL, NULL}};
384 ps_global->next_screen = SCREEN_FUN_NULL;
386 switch(cmd){
387 case MC_EDIT :
389 * MC_EDIT works a little differently from the other cmds here.
390 * The others return 0 to scrolltool so that we are still in
391 * the view screen. This one is different because we may have
392 * changed what we're viewing. We handle that by returning 1
393 * to scrolltool and setting the sparms opt union's gint
394 * to the value below.
396 * (Late breaking news. Now we're going to return 1 from all these
397 * commands and then in the caller we're going to bounce back out
398 * to the index view instead of the view of the individual entry.
399 * So there is some dead code around for now.)
401 * Then, in the view_abook_entry function we check the value
402 * of this field on scrolltool's return and if it is one of
403 * the two special values below view_abook_entry resets the
404 * current line if necessary, flushes the display cache (in
405 * ab_resize) and loops back and starts over, effectively
406 * putting us back in the view screen but with updated
407 * contents. A side effect is that the screen above that (the
408 * abook index) will also have been flushed and corrected by
409 * the ab_resize.
411 pab = &as.adrbks[cur_addr_book()];
412 if(pab && pab->access == ReadOnly){
413 /* TRANSLATORS: Address book can be viewed but not changed */
414 q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
415 rv = 0;
416 break;
419 if(adrbk_check_all_validity_now()){
420 if(resync_screen(pab, AddrBookScreen, 0)){
421 q_status_message(SM_ORDER | SM_DING, 3, 4,
422 /* TRANSLATORS: The address book was changed by some other
423 process. The user is being told that their change (their
424 Update) has been canceled and they should try again. */
425 _("Address book changed. Update cancelled. Try again."));
426 ps_global->mangled_screen = 1;
427 break;
431 if(pab && pab->access == ReadOnly){
432 q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
433 rv = 0;
434 break;
438 * Arguments all come from globals, not from the arguments to
439 * process_abook_view_cmd. It would be a little cleaner if the
440 * information was contained in att, I suppose.
442 if(is_addr(as.cur_row+as.top_ent)){
443 AdrBk_Entry *abe, *abe_copy;
444 a_c_arg_t entry;
445 int warped = 0;
447 dl = dlist(as.top_ent+as.cur_row);
448 entry = dl->elnum;
449 abe = adrbk_get_ae(pab->address_book, entry);
450 abe_copy = copy_ae(abe);
451 dprint((9,"Calling edit_entry to edit from view\n"));
452 /* TRANSLATORS: update as in update an address book entry */
453 edit_entry(pab->address_book, abe_copy, entry,
454 abe->tag, 0, &warped, _("update"));
456 * The ABOOK_EDITED case doesn't mean that we necessarily
457 * changed something, just that we might have but we know
458 * we didn't change the sort order (causing the warp).
460 sparms->proc.data.i = warped
461 ? VIEW_ABOOK_WARPED : VIEW_ABOOK_EDITED;
463 free_ae(&abe_copy);
464 rv = 1; /* causes scrolltool to return */
466 else{
467 q_status_message(SM_ORDER, 0, 4,
468 "Something wrong, entry not updateable");
469 rv = 0;
470 break;
473 break;
475 case MC_SAVE :
476 pab = &as.adrbks[cur_addr_book()];
478 * Arguments all come from globals, not from the arguments to
479 * process_abook_view_cmd. It would be a little cleaner if the
480 * information was contained in att, I suppose.
482 (void)ab_save(ps_global, pab ? pab->address_book : NULL,
483 as.top_ent+as.cur_row, -FOOTER_ROWS(ps_global), 0);
484 rv = 1;
485 break;
487 case MC_COMPOSE :
488 (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 0);
489 rv = 1;
490 break;
492 case MC_ROLE :
493 (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 1);
494 rv = 1;
495 break;
497 case MC_FORWARD :
498 rv = 1;
499 /* TRANSLATORS: A question with two choices for the answer. Forward
500 as text means to include the text of the message being forwarded
501 in the new message. Forward as a Vcard attachment means to
502 attach it to the message in a special format so it is recognizable
503 as a Vcard. */
504 i = radio_buttons(_("Forward as text or forward as Vcard attachment ? "),
505 -FOOTER_ROWS(ps_global), text_or_vcard, 't', 'x',
506 h_ab_text_or_vcard, RB_NORM);
507 switch(i){
508 case 'x':
509 q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
510 rv = 0;
511 break;
513 case 't':
514 forward_text(ps_global, sparms->text.text, sparms->text.src);
515 break;
517 case 'v':
518 (void)ab_forward(ps_global, as.top_ent+as.cur_row, 0);
519 break;
521 default:
522 q_status_message(SM_ORDER, 3, 3,
523 "can't happen in process_abook_view_cmd");
524 break;
527 break;
529 default:
530 alpine_panic("Unexpected command in process_abook_view_cmd");
531 break;
534 return(rv);
539 * Give expanded view of this address entry.
540 * Call scrolltool to do the work.
542 * Args: headents -- The headerentry array from pico.
543 * s -- Unused here.
545 * Returns -- Always 0.
548 expand_addrs_for_pico(struct headerentry *headents, char ***s)
550 BuildTo bldto;
551 STORE_S *store;
552 char *error = NULL, *addr = NULL, *fullname = NULL, *address = NULL;
553 SAVE_STATE_S state;
554 ADDRESS *adrlist = NULL, *a;
555 int j, address_index = -1, fullname_index = -1, no_a_fld = 0;
556 void (*redraw)(void) = ps_global->redrawer;
558 char *tmp, *tmp2, *tmp3;
559 SCROLL_S sargs;
560 AdrBk_Entry abe;
561 char fakeaddrpmt[500];
562 int abook_indent;
564 dprint((5, "- expand_addrs_for_pico -\n"));
566 abook_indent = utf8_width(_("Nickname")) + 2;
567 utf8_snprintf(fakeaddrpmt, sizeof(fakeaddrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Address"));
569 if(s)
570 *s = NULL;
572 ps_global->redrawer = NULL;
573 fix_windsize(ps_global);
575 ab_nesting_level++;
576 save_state(&state);
578 for(j=0;
579 headents[j].name != NULL && (address_index < 0 || fullname_index < 0);
580 j++){
581 if(!strncmp(headents[j].name, "Address", 7) || !strncmp(headents[j].name, _("Address"), strlen(_("Address"))))
582 address_index = j;
583 else if(!strncmp(headents[j].name, "Fullname", 8) || !strncmp(headents[j].name, _("Fullname"), strlen(_("Fullname"))))
584 fullname_index = j;
587 if(address_index >= 0)
588 address = *headents[address_index].realaddr;
589 else{
590 address_index = 1000; /* a big number */
591 no_a_fld++;
594 if(fullname_index >= 0)
595 fullname = adrbk_formatname(*headents[fullname_index].realaddr,
596 NULL, NULL);
598 memset(&abe, 0, sizeof(abe));
599 if(fullname)
600 abe.fullname = cpystr(fullname);
602 if(address){
603 char *tmp_a_string;
605 tmp_a_string = cpystr(address);
606 rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
607 fs_give((void **)&tmp_a_string);
608 if(adrlist && adrlist->next){
609 int cnt = 0;
611 abe.tag = List;
612 for(a = adrlist; a; a = a->next)
613 cnt++;
615 abe.addr.list = (char **)fs_get((cnt+1) * sizeof(char *));
616 cnt = 0;
617 for(a = adrlist; a; a = a->next){
618 if(a->host && a->host[0] == '@')
619 abe.addr.list[cnt++] = cpystr(a->mailbox);
620 else if(a->host && a->host[0] && a->mailbox && a->mailbox[0])
621 abe.addr.list[cnt++] =
622 cpystr(simple_addr_string(a, tmp_20k_buf,
623 SIZEOF_20KBUF));
626 abe.addr.list[cnt] = NULL;
628 else{
629 abe.tag = Single;
630 abe.addr.addr = address;
633 if(adrlist)
634 mail_free_address(&adrlist);
636 bldto.type = Abe;
637 bldto.arg.abe = &abe;
638 our_build_address(bldto, &addr, &error, NULL, NULL);
639 if(error){
640 q_status_message1(SM_ORDER, 3, 4, "%s", error);
641 fs_give((void **)&error);
644 if(addr){
645 tmp_a_string = cpystr(addr);
646 rfc822_parse_adrlist(&adrlist, tmp_a_string, ps_global->maildomain);
647 fs_give((void **)&tmp_a_string);
650 #ifdef ENABLE_LDAP
651 else if(no_a_fld && expander_address){
652 WP_ERR_S wp_err;
654 memset(&wp_err, 0, sizeof(wp_err));
655 adrlist = wp_lookups(expander_address, &wp_err, 0);
656 if(fullname && *fullname){
657 if(adrlist){
658 if(adrlist->personal)
659 fs_give((void **)&adrlist->personal);
661 adrlist->personal = cpystr(fullname);
665 if(wp_err.error){
666 q_status_message1(SM_ORDER, 3, 4, "%s", wp_err.error);
667 fs_give((void **)&wp_err.error);
670 #endif
672 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
673 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
674 restore_state(&state);
675 return(0);
678 for(j = 0; j < address_index && headents[j].name != NULL; j++){
679 if((tmp = fold(*headents[j].realaddr,
680 ps_global->ttyo->screen_cols,
681 ps_global->ttyo->screen_cols,
682 headents[j].prompt,
683 repeat_char(headents[j].prwid, SPACE), FLD_NONE)) != NULL){
684 so_puts(store, tmp);
685 fs_give((void **)&tmp);
690 * There is an assumption that Addresses is the last field.
692 tmp3 = cpystr(repeat_char(headents[0].prwid + 2, SPACE));
693 if(adrlist){
694 for(a = adrlist; a; a = a->next){
695 ADDRESS *next_addr;
696 char *bufp;
697 size_t len;
699 next_addr = a->next;
700 a->next = NULL;
701 len = est_size(a);
702 bufp = (char *) fs_get(len * sizeof(char));
703 (void) addr_string(a, bufp, len);
704 a->next = next_addr;
707 * Another assumption, all the prwids are the same.
709 if((tmp = fold(bufp,
710 ps_global->ttyo->screen_cols,
711 ps_global->ttyo->screen_cols,
712 (a == adrlist) ? (no_a_fld
713 ? fakeaddrpmt
714 : headents[address_index].prompt)
715 : tmp3+2,
716 tmp3, FLD_NONE)) != NULL){
717 so_puts(store, tmp);
718 fs_give((void **) &tmp);
721 fs_give((void **) &bufp);
724 else{
725 tmp2 = NULL;
726 if((tmp = fold(tmp2=cpystr("<none>"),
727 ps_global->ttyo->screen_cols,
728 ps_global->ttyo->screen_cols,
729 no_a_fld ? fakeaddrpmt
730 : headents[address_index].prompt,
731 tmp3, FLD_NONE)) != NULL){
732 so_puts(store, tmp);
733 fs_give((void **) &tmp);
736 if(tmp2)
737 fs_give((void **)&tmp2);
740 if(tmp3)
741 fs_give((void **)&tmp3);
743 memset(&sargs, 0, sizeof(SCROLL_S));
744 sargs.text.text = so_text(store);
745 sargs.text.src = CharStar;
746 sargs.text.desc = _("expanded entry");
747 /* TRANSLATORS: screen title for viewing an address book entry in Rich
748 view mode. Rich just means it is expanded to include everything. */
749 sargs.bar.title = _("ADDRESS BOOK (Rich View)");
750 sargs.bar.style = TextPercent;
751 sargs.keys.menu = &abook_text_km;
752 setbitmap(sargs.keys.bitmap);
754 scrolltool(&sargs);
755 so_give(&store);
757 restore_state(&state);
758 ab_nesting_level--;
760 if(addr)
761 fs_give((void **)&addr);
763 if(adrlist)
764 mail_free_address(&adrlist);
766 if(fullname)
767 fs_give((void **)&fullname);
769 if(abe.fullname)
770 fs_give((void **)&abe.fullname);
772 if(abe.tag == List && abe.addr.list)
773 free_list_array(&(abe.addr.list));
775 ps_global->redrawer = redraw;
777 return(0);
782 * Callback from TakeAddr editing screen to see message that was being
783 * viewed. Call scrolltool to do the work.
785 char *
786 view_message_for_pico(char **error)
788 STORE_S *store;
789 gf_o_t pc;
790 void (*redraw)(void) = ps_global->redrawer;
791 SourceType src = CharStar;
792 SCROLL_S sargs;
794 dprint((5, "- view_message_for_pico -\n"));
796 ps_global->redrawer = NULL;
797 fix_windsize(ps_global);
799 #ifdef DOS
800 src = TmpFileStar;
801 #endif
803 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
804 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
805 return(NULL);
808 gf_set_so_writec(&pc, store);
810 format_message(msgno_for_pico_callback, env_for_pico_callback,
811 body_for_pico_callback, NULL, FM_NEW_MESS | FM_DISPLAY, pc);
813 gf_clear_so_writec(store);
815 memset(&sargs, 0, sizeof(SCROLL_S));
816 sargs.text.text = so_text(store);
817 sargs.text.src = src;
818 sargs.text.desc = _("expanded entry");
819 /* TRANSLATORS: this is a screen title */
820 sargs.bar.title = _("MESSAGE TEXT");
821 sargs.bar.style = TextPercent;
822 sargs.keys.menu = &abook_text_km;
823 setbitmap(sargs.keys.bitmap);
825 scrolltool(&sargs);
827 so_give(&store);
829 ps_global->redrawer = redraw;
831 return(NULL);
836 prompt::name::help::prwid::maxlen::realaddr::
837 builder::affected_entry::next_affected::selector::key_label::fileedit::nickcmpl
838 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
839 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
841 static struct headerentry headents_for_edit[]={
842 {"Nickname : ", N_("Nickname"), h_composer_abook_nick, 12, 0, NULL,
843 /* TRANSLATORS: To AddrBk is a command that takes the user to
844 the address book screen to select an entry from there. */
845 verify_nick, NULL, NULL, addr_book_nick_for_edit, N_("To AddrBk"), NULL, abook_nickname_complete,
846 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
847 {"Fullname : ", N_("Fullname"), h_composer_abook_full, 12, 0, NULL,
848 NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
849 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
850 /* TRANSLATORS: a File Copy is a copy of a sent message saved in a regular
851 file on the computer's disk */
852 {"Fcc : ", N_("FileCopy"), h_composer_abook_fcc, 12, 0, NULL,
853 /* TRANSLATORS: To Folders */
854 NULL, NULL, NULL, folders_for_fcc, N_("To Fldrs"), NULL, NULL,
855 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
856 {"Comment : ", N_("Comment"), h_composer_abook_comment, 12, 0, NULL,
857 NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
858 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
859 {"Addresses : ", N_("Addresses"), h_composer_abook_addrs, 12, 0, NULL,
860 verify_addr, NULL, NULL, addr_book_change_list, N_("To AddrBk"), NULL, abook_nickname_complete,
861 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
862 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
863 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
865 #define NNN_NICK 0
866 #define NNN_FULL 1
867 #define NNN_FCC 2
868 #define NNN_COMMENT 3
869 #define NNN_ADDR 4
870 #define NNN_END 5
872 static char *nick_saved_for_pico_check;
873 static AdrBk *abook_saved_for_pico_check;
876 * Args: abook -- Address book handle
877 * abe -- AdrBk_Entry of old entry to work on. If NULL, this will
878 * be a new entry. This has to be a pointer to a copy of
879 * an abe that won't go away until we finish this function.
880 * In other words, don't pass in a pointer to an abe in
881 * the cache, copy it first. The tag on this abe is only
882 * used to decide whether to read abe->addr.list or
883 * abe->addr.addr, not to determine what the final result
884 * will be. That's determined solely by how many addresses
885 * there are after the user edits.
886 * entry -- The entry number of the old entry that we will be changing.
887 * old_tag -- If we're changing an old entry, then this is the tag of
888 * that old entry.
889 * readonly -- Call pico with readonly flag
890 * warped -- We warped to a new part of the addrbook
891 * (We also overload warped in a couple places and use it's
892 * being set as an indicator of whether we are Taking or
893 * not. It will be NULL if we are Taking.)
895 void
896 edit_entry(AdrBk *abook, AdrBk_Entry *abe, a_c_arg_t entry, Tag old_tag, int readonly, int *warped, char *cmd)
898 AdrBk_Entry local_abe;
899 struct headerentry *he;
900 PICO pbf;
901 STORE_S *msgso;
902 adrbk_cntr_t old_entry_num, new_entry_num = NO_NEXT;
903 int rc = 0, resort_happened = 0, list_changed = 0, which_addrbook;
904 int editor_result, i = 0, add, n_end, ldap = 0;
905 char *nick, *full, *fcc, *comment, *fname, *pp;
906 char **orig_addrarray = NULL;
907 char **new_addrarray = NULL;
908 char *comma_sep_addr = NULL;
909 char **p, **q;
910 Tag new_tag;
911 char titlebar[40];
912 char nickpmt[100], fullpmt[100], fccpmt[100], cmtpmt[100], addrpmt[100];
913 int abook_indent;
914 long length;
915 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
917 dprint((2, "- edit_entry -\n"));
919 old_entry_num = (adrbk_cntr_t) entry;
920 save_state(&state);
921 abook_saved_for_pico_check = abook;
923 add = (abe == NULL); /* doing add or change? */
924 if(add){
925 local_abe.nickname = "";
926 local_abe.fullname = "";
927 local_abe.fcc = "";
928 local_abe.extra = "";
929 local_abe.addr.addr = "";
930 local_abe.tag = NotSet;
931 abe = &local_abe;
932 old_entry_num = NO_NEXT;
935 new_tag = abe->tag;
937 #ifdef ENABLE_LDAP
938 expander_address = NULL;
939 if(abe->tag == Single &&
940 abe->addr.addr &&
941 !strncmp(abe->addr.addr,QRUN_LDAP,LEN_QRL)){
942 ldap = 1;
943 expander_address = cpystr(abe->addr.addr);
944 removing_double_quotes(expander_address);
946 else if(abe->tag == List &&
947 abe->addr.list &&
948 abe->addr.list[0] &&
949 !abe->addr.list[1] &&
950 !strncmp(abe->addr.list[0],QRUN_LDAP,LEN_QRL)){
951 ldap = 2;
952 expander_address = cpystr(abe->addr.list[0]);
953 removing_double_quotes(expander_address);
955 #endif
957 standard_picobuf_setup(&pbf);
958 pbf.exittest = pico_sendexit_for_adrbk;
959 pbf.canceltest = warped ? pico_cancel_for_adrbk_edit
960 : pico_cancel_for_adrbk_take;
961 pbf.expander = expand_addrs_for_pico;
962 pbf.ctrlr_label = _("RichView");
963 /* xgettext: c-format */
964 if(readonly)
965 /* TRANSLATORS: screen titles */
966 snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (View)"));
967 else
968 snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (%c%s)"),
969 islower((unsigned char)(*cmd))
970 ? toupper((unsigned char)*cmd)
971 : *cmd, (cmd+1));
973 pbf.pine_anchor = set_titlebar(titlebar,
974 ps_global->mail_stream,
975 ps_global->context_current,
976 ps_global->cur_folder,ps_global->msgmap,
977 0, FolderName, 0, 0, NULL);
978 pbf.pine_flags |= P_NOBODY;
979 if(readonly)
980 pbf.pine_flags |= P_VIEW;
982 /* An informational message */
983 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
984 pbf.msgtext = (void *)so_text(msgso);
986 * It's nice if we can make it so these lines make sense even if
987 * they don't all make it on the screen, because the user can't
988 * scroll down to see them. So just make each line a whole sentence
989 * that doesn't need the others below it to make sense.
991 if(add){
992 so_puts(msgso,
994 * TRANSLATORS: The following lines go together to form a screen of
995 * explanation about how to edit an address book entry.
997 _("\n Fill in the fields. It is ok to leave fields blank."));
998 so_puts(msgso,
999 _("\n To form a list, just enter multiple comma-separated addresses."));
1001 else{
1002 so_puts(msgso,
1003 /* TRANSLATORS: Same here, but a different version of the screen. */
1004 _("\n Change any of the fields. It is ok to leave fields blank."));
1005 if(ldap)
1006 so_puts(msgso,
1007 _("\n Since this entry does a directory lookup you may not edit the address field."));
1008 else
1009 so_puts(msgso,
1010 _("\n Additional comma-separated addresses may be entered in the address field."));
1013 so_puts(msgso,
1014 _("\n Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."));
1015 so_puts(msgso,
1016 _("\n If you want to use quotation marks inside the Fullname field, it is best"));
1017 so_puts(msgso,
1018 _("\n to use single quotation marks; for example: George 'Husky' Washington."));
1021 he = (struct headerentry *) fs_get((NNN_END+1) * sizeof(struct headerentry));
1022 memset((void *)he, 0, (NNN_END+1) * sizeof(struct headerentry));
1023 pbf.headents = he;
1025 abook_indent = utf8_width(_("Nickname")) + 2;
1027 /* make a copy of each field */
1028 nick = cpystr(abe->nickname ? abe->nickname : "");
1029 removing_leading_and_trailing_white_space(nick);
1030 nick_saved_for_pico_check = cpystr(nick);
1031 he[NNN_NICK] = headents_for_edit[NNN_NICK];
1032 he[NNN_NICK].realaddr = &nick;
1033 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
1034 he[NNN_NICK].prompt = nickpmt;
1035 he[NNN_NICK].prwid = abook_indent+2;
1036 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
1037 he[NNN_NICK].nickcmpl = NULL;
1039 full = cpystr(abe->fullname ? abe->fullname : "");
1040 removing_leading_and_trailing_white_space(full);
1041 he[NNN_FULL] = headents_for_edit[NNN_FULL];
1042 he[NNN_FULL].realaddr = &full;
1043 utf8_snprintf(fullpmt, sizeof(fullpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
1044 he[NNN_FULL].prompt = fullpmt;
1045 he[NNN_FULL].prwid = abook_indent+2;
1047 fcc = cpystr(abe->fcc ? abe->fcc : "");
1048 removing_leading_and_trailing_white_space(fcc);
1049 he[NNN_FCC] = headents_for_edit[NNN_FCC];
1050 he[NNN_FCC].realaddr = &fcc;
1051 utf8_snprintf(fccpmt, sizeof(fccpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
1052 he[NNN_FCC].prompt = fccpmt;
1053 he[NNN_FCC].prwid = abook_indent+2;
1055 comment = cpystr(abe->extra ? abe->extra : "");
1056 removing_leading_and_trailing_white_space(comment);
1057 he[NNN_COMMENT] = headents_for_edit[NNN_COMMENT];
1058 he[NNN_COMMENT].realaddr = &comment;
1059 utf8_snprintf(cmtpmt, sizeof(cmtpmt), "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
1060 he[NNN_COMMENT].prompt = cmtpmt;
1061 he[NNN_COMMENT].prwid = abook_indent+2;
1063 n_end = NNN_END;
1064 if(ldap)
1065 n_end--;
1067 if(!ldap){
1068 he[NNN_ADDR] = headents_for_edit[NNN_ADDR];
1069 he[NNN_ADDR].realaddr = &comma_sep_addr;
1070 utf8_snprintf(addrpmt, sizeof(addrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
1071 he[NNN_ADDR].prompt = addrpmt;
1072 he[NNN_ADDR].prwid = abook_indent+2;
1073 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
1074 he[NNN_NICK].nickcmpl = NULL;
1076 if(abe->tag == Single){
1077 if(abe->addr.addr){
1078 orig_addrarray = (char **) fs_get(2 * sizeof(char *));
1079 orig_addrarray[0] = cpystr(abe->addr.addr);
1080 orig_addrarray[1] = NULL;
1083 else if(abe->tag == List){
1084 if(listmem_count_from_abe(abe) > 0){
1085 orig_addrarray = (char **) fs_get(
1086 (size_t)(listmem_count_from_abe(abe) + 1)
1087 * sizeof(char *));
1088 for(q = orig_addrarray, p = abe->addr.list; p && *p; p++, q++)
1089 *q = cpystr(*p);
1091 *q = NULL;
1095 /* figure out how large a string we need to allocate */
1096 length = 0L;
1097 for(p = orig_addrarray; p && *p; p++)
1098 length += (strlen(*p) + 2);
1100 if(length)
1101 length -= 2L;
1103 pp = comma_sep_addr = (char *) fs_get((size_t)(length+1L) * sizeof(char));
1104 *pp = '\0';
1105 for(p = orig_addrarray; p && *p; p++){
1106 sstrncpy(&pp, *p, length-(pp-comma_sep_addr));
1107 if(*(p+1))
1108 sstrncpy(&pp, ", ", length-(pp-comma_sep_addr));
1111 comma_sep_addr[length] = '\0';
1113 if(verify_addr(comma_sep_addr, NULL, NULL, NULL, NULL) < 0)
1114 he[NNN_ADDR].start_here = 1;
1117 he[n_end] = headents_for_edit[NNN_END];
1118 for(i = 0; i < n_end; i++){
1119 /* no callbacks in some cases */
1120 if(readonly || ((i == NNN_FULL || i == NNN_COMMENT) && !env_for_pico_callback)){
1121 he[i].selector = NULL;
1122 he[i].key_label = NULL;
1125 /* no builders for readonly */
1126 if(readonly)
1127 he[i].builder = NULL;
1130 /* pass to pico and let user change them */
1131 editor_result = pico(&pbf);
1132 ps_global->mangled_screen = 1;
1133 standard_picobuf_teardown(&pbf);
1135 if(editor_result & COMP_GOTHUP)
1136 hup_signal(0);
1137 else{
1138 fix_windsize(ps_global);
1139 init_signals();
1142 if(editor_result & COMP_CANCEL){
1143 if(!readonly)
1144 /* TRANSLATOR: Something like
1145 Address book save cancelled */
1146 q_status_message1(SM_INFO, 0, 2, _("Address book %s cancelled"), cmd);
1148 else if(editor_result & COMP_EXIT){
1149 if(pico_usingcolor())
1150 clear_index_cache(ps_global->mail_stream, 0);
1151 removing_leading_and_trailing_white_space(nick);
1152 removing_leading_and_trailing_white_space(full);
1153 removing_leading_and_trailing_white_space(fcc);
1154 removing_leading_and_trailing_white_space(comment);
1155 removing_leading_and_trailing_white_space(comma_sep_addr);
1157 /* not needed if pico is returning UTF-8 */
1158 convert_possibly_encoded_str_to_utf8(&nick);
1159 convert_possibly_encoded_str_to_utf8(&full);
1160 convert_possibly_encoded_str_to_utf8(&fcc);
1161 convert_possibly_encoded_str_to_utf8(&comment);
1162 convert_possibly_encoded_str_to_utf8(&comma_sep_addr);
1164 /* don't allow adding null entry */
1165 if(add && !*nick && !*full && !*fcc && !*comment && !*comma_sep_addr)
1166 goto outtahere;
1169 * comma_sep_addr is now the string which has been edited
1171 if(comma_sep_addr)
1172 new_addrarray = parse_addrlist(comma_sep_addr);
1174 if(!ldap && (!new_addrarray || !new_addrarray[0]))
1175 q_status_message(SM_ORDER, 3, 5, _("Warning: entry has no addresses"));
1177 if(!new_addrarray || !new_addrarray[0] || !new_addrarray[1])
1178 new_tag = Single; /* one or zero addresses means its a Single */
1179 else
1180 new_tag = List; /* more than one addresses means its a List */
1182 if(new_tag == List && old_tag == List){
1184 * If Taking, make sure we write it even if user didn't edit
1185 * it any further.
1187 if(!warped)
1188 list_changed++;
1189 else if(he[NNN_ADDR].dirty)
1190 for(q = orig_addrarray, p = new_addrarray; p && *p && q && *q; p++, q++)
1191 if(strcmp(*p, *q) != 0){
1192 list_changed++;
1193 break;
1196 if(!list_changed && he[NNN_ADDR].dirty
1197 && ((!(p && *p) && (q && *q)) || ((p && *p) && !(q && *q))))
1198 list_changed++;
1200 if(list_changed){
1202 * need to delete old list members and add new members below
1204 rc = adrbk_listdel_all(abook, (a_c_arg_t) old_entry_num);
1206 else{
1207 /* don't need new_addrarray */
1208 free_list_array(&new_addrarray);
1211 if(comma_sep_addr)
1212 fs_give((void **) &comma_sep_addr);
1214 else if((new_tag == List && old_tag == Single)
1215 || (new_tag == Single && old_tag == List)){
1216 /* delete old entry */
1217 rc = adrbk_delete(abook, (a_c_arg_t) old_entry_num, 0, 0, 0, 0);
1218 old_entry_num = NO_NEXT;
1219 if(comma_sep_addr && new_tag == List)
1220 fs_give((void **) &comma_sep_addr);
1224 * This will be an edit in the cases where the tag didn't change
1225 * and an add in the cases where it did.
1227 if(rc == 0)
1228 rc = adrbk_add(abook,
1229 (a_c_arg_t)old_entry_num,
1230 nick,
1231 he[NNN_FULL].dirty ? full : abe->fullname,
1232 new_tag == Single ? (ldap == 1 ? abe->addr.addr :
1233 ldap == 2 ? abe->addr.list[0] :
1234 comma_sep_addr)
1235 : NULL,
1236 fcc,
1237 he[NNN_COMMENT].dirty ? comment : abe->extra,
1238 new_tag,
1239 &new_entry_num,
1240 &resort_happened,
1243 (new_tag != List || !new_addrarray));
1246 if(rc == 0 && new_tag == List && new_addrarray)
1247 rc = adrbk_nlistadd(abook, (a_c_arg_t) new_entry_num, &new_entry_num,
1248 &resort_happened, new_addrarray, 1, 0, 1);
1250 restore_state(&state);
1252 if(rc == -2 || rc == -3){
1253 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1254 _("Error updating address book: %s"),
1255 rc == -2 ? error_description(errno) : "Alpine bug");
1257 else if(rc == 0
1258 && strucmp(nick, nick_saved_for_pico_check) != 0
1259 && (editor_result & COMP_EXIT)){
1260 int added_to;
1262 for(added_to = 0; added_to < as.n_addrbk; added_to++)
1263 if(abook_saved_for_pico_check == as.adrbks[added_to].address_book)
1264 break;
1266 if(added_to >= as.n_addrbk)
1267 added_to = -1;
1269 fname = addr_lookup(nick, &which_addrbook, added_to);
1270 if(fname){
1271 q_status_message4(SM_ORDER, 5, 9,
1272 /* TRANSLATORS: The first %s is the nickname the user is
1273 trying to use that exists in another address book.
1274 The second %s is the name of the other address book.
1275 The third %s is " as " because it will say something
1276 like also exists in <name> as <description>. */
1277 _("Warning! Nickname %s also exists in \"%s\"%s%s"),
1278 nick, as.adrbks[which_addrbook].abnick,
1279 (fname && *fname) ? _(" as ") : "",
1280 (fname && *fname) ? fname : "");
1281 fs_give((void **)&fname);
1285 if(resort_happened || list_changed){
1286 DL_CACHE_S dlc_restart;
1288 dlc_restart.adrbk_num = as.cur;
1289 dlc_restart.dlcelnum = new_entry_num;
1290 switch(new_tag){
1291 case Single:
1292 dlc_restart.type = DlcSimple;
1293 break;
1295 case List:
1296 dlc_restart.type = DlcListHead;
1297 break;
1299 default:
1300 break;
1303 warp_to_dlc(&dlc_restart, 0L);
1304 if(warped)
1305 *warped = 1;
1308 outtahere:
1309 if(he)
1310 free_headents(&he);
1312 if(msgso)
1313 so_give(&msgso);
1315 if(nick)
1316 fs_give((void **)&nick);
1317 if(full)
1318 fs_give((void **)&full);
1319 if(fcc)
1320 fs_give((void **)&fcc);
1321 if(comment)
1322 fs_give((void **)&comment);
1324 if(comma_sep_addr)
1325 fs_give((void **)&comma_sep_addr);
1326 if(nick_saved_for_pico_check)
1327 fs_give((void **)&nick_saved_for_pico_check);
1329 free_list_array(&orig_addrarray);
1330 free_list_array(&new_addrarray);
1331 #ifdef ENABLE_LDAP
1332 if(expander_address)
1333 fs_give((void **) &expander_address);
1334 #endif
1338 /*ARGSUSED*/
1340 verify_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
1342 char *tmp;
1344 dprint((7, "- verify_nick - (%s)\n", given ? given : "nul"));
1346 tmp = cpystr(given);
1347 removing_leading_and_trailing_white_space(tmp);
1349 if(nickname_check(tmp, error)){
1350 fs_give((void **)&tmp);
1351 if(mangled){
1352 if(ps_global->mangled_screen)
1353 *mangled |= BUILDER_SCREEN_MANGLED;
1354 else if(ps_global->mangled_footer)
1355 *mangled |= BUILDER_FOOTER_MANGLED;
1358 return -2;
1361 if(ps_global->remote_abook_validity > 0 &&
1362 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
1363 *mangled |= BUILDER_SCREEN_MANGLED;
1365 ab_nesting_level++;
1366 if(strucmp(tmp, nick_saved_for_pico_check) != 0
1367 && adrbk_lookup_by_nick(abook_saved_for_pico_check,
1368 tmp, (adrbk_cntr_t *)NULL)){
1369 if(error){
1370 char buf[MAX_NICKNAME + 80];
1372 /* TRANSLATORS: The %s is the nickname of an entry that is already
1373 in the address book */
1374 snprintf(buf, sizeof(buf), _("\"%s\" already in address book."), tmp);
1375 buf[sizeof(buf)-1] = '\0';
1376 *error = cpystr(buf);
1379 ab_nesting_level--;
1380 fs_give((void **)&tmp);
1381 if(mangled){
1382 if(ps_global->mangled_screen)
1383 *mangled |= BUILDER_SCREEN_MANGLED;
1384 else if(ps_global->mangled_footer)
1385 *mangled |= BUILDER_FOOTER_MANGLED;
1388 return -2;
1391 ab_nesting_level--;
1392 if(expanded)
1393 *expanded = tmp;
1394 else
1395 fs_give((void **)&tmp);
1397 /* This is so pico will erase any old message */
1398 if(error)
1399 *error = cpystr("");
1401 if(mangled){
1402 if(ps_global->mangled_screen)
1403 *mangled |= BUILDER_SCREEN_MANGLED;
1404 else if(ps_global->mangled_footer)
1405 *mangled |= BUILDER_FOOTER_MANGLED;
1408 return 0;
1413 * Args: to -- the passed in line to parse
1414 * full_to -- Address of a pointer to return the full address in.
1415 * This will be allocated here and freed by the caller.
1416 * However, this function is just going to copy "to".
1417 * (special case for the route-addr-hack in build_address_int)
1418 * We're just looking for the error messages.
1419 * error -- Address of a pointer to return an error message in.
1420 * This will be allocated here and freed by the caller.
1421 * fcc -- This should be passed in NULL.
1422 * This builder doesn't support affected_entry's.
1424 * Result: 0 is returned if address was OK,
1425 * -2 if address wasn't OK.
1427 * Side effect: Can flush addrbook entry cache entries so they need to be
1428 * re-fetched afterwords.
1431 verify_addr(char *to, char **full_to, char **error, BUILDER_ARG *fcc, int *mangled)
1433 register char *p;
1434 int ret_val;
1435 BuildTo bldto;
1436 jmp_buf save_jmp_buf;
1437 int *save_nesting_level;
1439 dprint((7, "- verify_addr - (%s)\n", to ? to : "nul"));
1441 /* check to see if to string is empty to avoid work */
1442 for(p = to; p && *p && isspace((unsigned char)(*p)); p++)
1443 ;/* do nothing */
1445 if(!p || !*p){
1446 if(full_to)
1447 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
1449 return 0;
1452 if(full_to != NULL)
1453 *full_to = (char *)NULL;
1455 if(error != NULL)
1456 *error = (char *)NULL;
1459 * If we end up jumping back here because somebody else changed one of
1460 * our addrbooks out from underneath us, we may well leak some memory.
1461 * That's probably ok since this will be very rare.
1463 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
1464 save_nesting_level = cpyint(ab_nesting_level);
1465 if(setjmp(addrbook_changed_unexpectedly)){
1466 if(full_to && *full_to)
1467 fs_give((void **)full_to);
1469 /* TRANSLATORS: This is sort of an error, something unexpected has
1470 happened and alpine is re-initializing the address book. */
1471 q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
1472 dprint((1,
1473 "RESETTING address book... verify_addr(%s)!\n", to ? to : "?"));
1474 addrbook_reset();
1475 ab_nesting_level = *save_nesting_level;
1478 bldto.type = Str;
1479 bldto.arg.str = to;
1481 if(ps_global->remote_abook_validity > 0 &&
1482 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
1483 *mangled |= BUILDER_SCREEN_MANGLED;
1485 ab_nesting_level++;
1487 ret_val = build_address_internal(bldto, full_to, error, NULL, NULL, NULL,
1488 save_and_restore, 1, mangled);
1490 ab_nesting_level--;
1491 if(save_nesting_level)
1492 fs_give((void **)&save_nesting_level);
1494 if(full_to && *full_to && ret_val >= 0)
1495 removing_leading_and_trailing_white_space(*full_to);
1497 /* This is so pico will erase the old message */
1498 if(error != NULL && *error == NULL)
1499 *error = cpystr("");
1501 if(ret_val < 0)
1502 ret_val = -2; /* cause pico to stay on same header line */
1504 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
1505 return(ret_val);
1510 * Call back for pico to prompt the user for exit confirmation
1512 * Returns: either NULL if the user accepts exit, or string containing
1513 * reason why the user declined.
1516 pico_sendexit_for_adrbk(struct headerentry *he, void (*redraw_pico)(void),
1517 int allow_flowed, char **result)
1519 char *rstr = NULL;
1520 void (*redraw)(void) = ps_global->redrawer;
1522 ps_global->redrawer = redraw_pico;
1523 fix_windsize(ps_global);
1525 /* TRANSLATORS: A question */
1526 switch(want_to(_("Exit and save changes "), 'y', 0, NO_HELP, WT_NORM)){
1527 case 'y':
1528 break;
1530 case 'n':
1531 rstr = _("Use ^C to abandon changes you've made");
1532 break;
1535 if(result)
1536 *result = rstr;
1538 ps_global->redrawer = redraw;
1539 return((rstr == NULL) ? 0 : 1);
1544 * Call back for pico to prompt the user for exit confirmation
1546 * Returns: either NULL if the user accepts exit, or string containing
1547 * reason why the user declined.
1549 char *
1550 pico_cancelexit_for_adrbk(char *word, void (*redraw_pico)(void))
1552 char prompt[90];
1553 char *rstr = NULL;
1554 void (*redraw)(void) = ps_global->redrawer;
1556 /* TRANSLATORS: A question. The %s is a noun describing what is being cancelled. */
1557 snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), word);
1558 ps_global->redrawer = redraw_pico;
1559 fix_windsize(ps_global);
1561 switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
1562 case 'y':
1563 rstr = "";
1564 break;
1566 case 'n':
1567 case 'x':
1568 break;
1571 ps_global->redrawer = redraw;
1572 return(rstr);
1576 char *
1577 pico_cancel_for_adrbk_take(void (*redraw_pico)(void))
1579 return(pico_cancelexit_for_adrbk(_("take"), redraw_pico));
1583 char *
1584 pico_cancel_for_adrbk_edit(void (*redraw_pico)(void))
1586 return(pico_cancelexit_for_adrbk(_("changes"), redraw_pico));
1591 prompt::name::help::prwid::maxlen::realaddr::
1592 builder::affected_entry::next_affected::selector::key_label::fileedit::
1593 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
1594 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
1596 static struct headerentry headents_for_add[]={
1597 {"Server Name : ", N_("Server"), h_composer_abook_add_server, 14, 0, NULL,
1598 verify_server_name, NULL, NULL, NULL, NULL, NULL, NULL,
1599 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1600 {"Folder Name : ", N_("Folder"), h_composer_abook_add_folder, 14, 0, NULL,
1601 verify_folder_name, NULL, NULL, NULL, NULL, NULL, NULL,
1602 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1603 {"NickName : ", N_("Nickname"), h_composer_abook_add_nick, 14, 0, NULL,
1604 verify_abook_nick, NULL, NULL, NULL, NULL, NULL, NULL,
1605 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1606 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1607 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
1609 #define NN_SERVER 0
1610 #define NN_FOLDER 1
1611 #define NN_NICK 2
1612 #define NN_END 3
1615 * Args: global -- Add a global address book, not personal.
1616 * add_after_this -- This is the addrbook number which should come
1617 * right before the new addrbook we're adding, if
1618 * that makes sense. If this is -1, append to end
1619 * of the list.
1621 * Returns: addrbook number of new addrbook, or
1622 * -1, no addrbook added
1625 ab_add_abook(int global, int add_after_this)
1627 int ret;
1629 dprint((2, "- ab_add_abook -\n"));
1631 ret = ab_modify_abook_list(0, global, add_after_this, NULL, NULL, NULL);
1633 if(ret >= 0)
1634 q_status_message(SM_ORDER, 0, 3,
1635 _("New address book added. Use \"$\" to adjust order"));
1637 return(ret);
1642 * Args: global -- Add a global address book, not personal.
1643 * abook_num -- Abook num of the entry we are editing.
1644 * serv -- Default server.
1645 * folder -- Default folder.
1646 * nick -- Default nickname.
1648 * Returns: abook_num if successful,
1649 * -1, if not
1652 ab_edit_abook(int global, int abook_num, char *serv, char *folder, char *nick)
1654 dprint((2, "- ab_edit_abook -\n"));
1656 return(ab_modify_abook_list(1, global, abook_num, serv, folder, nick));
1659 static int the_one_were_editing;
1663 * Args: edit -- Edit existing entry
1664 * global -- Add a global address book, not personal.
1665 * abook_num -- This is the addrbook number which should come
1666 * right before the new addrbook we're adding, if
1667 * that makes sense. If this is -1, append to end
1668 * of the list.
1669 * If we are editing instead of adding, this is
1670 * the abook number of the entry we are editing.
1671 * def_serv -- Default server.
1672 * def_fold -- Default folder.
1673 * def_nick -- Default nickname.
1675 * Returns: addrbook number of new addrbook, or
1676 * -1, no addrbook added
1679 ab_modify_abook_list(int edit, int global, int abook_num, char *def_serv, char *def_fold, char *def_nick)
1681 struct headerentry *he;
1682 PICO pbf;
1683 STORE_S *msgso;
1684 int editor_result, i, how_many_in_list = 0, new_abook_num, num_in_list;
1685 int ret = 0;
1686 char *server, *folder, *nickname;
1687 char *new_item = NULL;
1688 EditWhich ew;
1689 AccessType remember_access_result;
1690 PerAddrBook *pab;
1691 char titlebar[100];
1692 char **list, **new_list = NULL;
1693 char tmp[1000+MAXFOLDER];
1694 int abook_indent;
1695 char servpmt[100], foldpmt[100], nickpmt[100];
1696 struct variable *vars = ps_global->vars;
1698 dprint((2, "- ab_modify_abook_list -\n"));
1700 if(ps_global->readonly_pinerc){
1701 if(edit)
1702 /* TRANSLATORS: Change was the name of the command the user was
1703 trying to perform. It is what was cancelled. */
1704 q_status_message(SM_ORDER, 0, 3, _("Change cancelled: config file not changeable"));
1705 else
1706 /* TRANSLATORS: Add was the command that is being cancelled. */
1707 q_status_message(SM_ORDER, 0, 3, _("Add cancelled: config file not changeable"));
1709 return -1;
1712 ew = Main;
1714 if((global && vars[V_GLOB_ADDRBOOK].is_fixed) ||
1715 (!global && vars[V_ADDRESSBOOK].is_fixed)){
1716 if(global)
1717 /* TRANSLATORS: Operation was cancelled because the system management
1718 does not allow the changing of global address books */
1719 q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing global address books"));
1720 else
1721 q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing address books"));
1723 return -1;
1726 init_ab_if_needed();
1728 if(edit){
1729 if((!global &&
1730 (abook_num < 0 || abook_num >= as.how_many_personals)) ||
1731 (global &&
1732 (abook_num < as.how_many_personals ||
1733 abook_num >= as.n_addrbk))){
1734 dprint((1, "Programming botch in ab_modify_abook_list: global=%d abook_num=%d n_addrbk=%d\n", global, abook_num, as.n_addrbk));
1735 q_status_message(SM_ORDER, 0, 3, "Programming botch, bad abook_num");
1736 return -1;
1739 the_one_were_editing = abook_num;
1741 else
1742 the_one_were_editing = -1;
1744 standard_picobuf_setup(&pbf);
1745 pbf.exittest = pico_sendexit_for_adrbk;
1746 pbf.canceltest = pico_cancel_for_adrbk_edit;
1747 if(edit)
1748 /* TRANSLATORS: screen title */
1749 strncpy(titlebar, _("CHANGE ADDRESS BOOK"), sizeof(titlebar));
1750 else
1751 /* TRANSLATORS: screen title */
1752 strncpy(titlebar, _("ADD ADDRESS BOOK"), sizeof(titlebar));
1754 titlebar[sizeof(titlebar)-1] = '\0';
1755 pbf.pine_anchor = set_titlebar(titlebar,
1756 ps_global->mail_stream,
1757 ps_global->context_current,
1758 ps_global->cur_folder,ps_global->msgmap,
1759 0, FolderName, 0, 0, NULL);
1760 pbf.pine_flags |= P_NOBODY;
1762 /* An informational message */
1763 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
1764 int lines_avail;
1765 char *t1 =
1766 /* TRANSLATORS: The next few lines go together to explain how to add
1767 the address book entry the user is working on. */
1768 _(" To add a local address book that will be accessed *only* by Alpine running\n on this machine, leave the server field blank.");
1769 char *t2 =
1770 _(" To add an address book that will be accessed by IMAP, fill in the\n server name.");
1771 char *t3 =
1772 _(" (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.)");
1773 char *t4 =
1774 _(" In the Folder field, type the remote folder name or local file name.");
1775 char *t5 =
1776 _(" In the Nickname field, give the address book a nickname or leave it blank.");
1777 char *t6 =
1778 _(" To get help specific to an item, press ^G.");
1779 char *t7 =
1780 _(" To exit and save the configuration, press ^X. To cancel, press ^C.");
1782 pbf.msgtext = (void *)so_text(msgso);
1784 * It's nice if we can make it so these lines make sense even if
1785 * they don't all make it on the screen, because the user can't
1786 * scroll down to see them.
1788 * The 3 is the number of fields to be defined, the 1 is for a
1789 * single blank line after the field definitions.
1791 lines_avail = ps_global->ttyo->screen_rows - HEADER_ROWS(ps_global) -
1792 FOOTER_ROWS(ps_global) - 3 - 1;
1794 if(lines_avail >= 15){ /* extra blank line */
1795 so_puts(msgso, "\n");
1796 lines_avail--;
1799 if(lines_avail >= 2){
1800 so_puts(msgso, t1);
1801 lines_avail -= 2;
1804 if(lines_avail >= 5){
1805 so_puts(msgso, "\n\n");
1806 so_puts(msgso, t2);
1807 so_puts(msgso, t3);
1808 lines_avail -= 5;
1810 else if(lines_avail >= 3){
1811 so_puts(msgso, "\n\n");
1812 so_puts(msgso, t2);
1813 lines_avail -= 3;
1816 if(lines_avail >= 2){
1817 so_puts(msgso, "\n\n");
1818 so_puts(msgso, t4);
1819 lines_avail -= 2;
1822 if(lines_avail >= 2){
1823 so_puts(msgso, "\n\n");
1824 so_puts(msgso, t5);
1825 lines_avail -= 2;
1828 if(lines_avail >= 3){
1829 so_puts(msgso, "\n\n");
1830 so_puts(msgso, t6);
1831 so_puts(msgso, "\n");
1832 so_puts(msgso, t7);
1834 else if(lines_avail >= 2){
1835 so_puts(msgso, "\n\n");
1836 so_puts(msgso, t7);
1840 he = (struct headerentry *)fs_get((NN_END+1) * sizeof(struct headerentry));
1841 memset((void *)he, 0, (NN_END+1) * sizeof(struct headerentry));
1842 pbf.headents = he;
1844 abook_indent = utf8_width(_("Server Name")) + 2;
1846 /* make a copy of each field */
1847 server = cpystr(def_serv ? def_serv : "");
1848 he[NN_SERVER] = headents_for_add[NN_SERVER];
1849 he[NN_SERVER].realaddr = &server;
1850 utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", abook_indent, abook_indent, _("Server Name"));
1851 he[NN_SERVER].prompt = servpmt;
1852 he[NN_SERVER].prwid = abook_indent+2;
1854 folder = cpystr(def_fold ? def_fold : "");
1855 he[NN_FOLDER] = headents_for_add[NN_FOLDER];
1856 he[NN_FOLDER].realaddr = &folder;
1857 utf8_snprintf(foldpmt, sizeof(foldpmt), "%-*.*w: ", abook_indent, abook_indent, _("Folder Name"));
1858 he[NN_FOLDER].prompt = foldpmt;
1859 he[NN_FOLDER].prwid = abook_indent+2;
1861 nickname = cpystr(def_nick ? def_nick : "");
1862 he[NN_NICK] = headents_for_add[NN_NICK];
1863 he[NN_NICK].realaddr = &nickname;
1864 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
1865 he[NN_NICK].prompt = nickpmt;
1866 he[NN_NICK].prwid = abook_indent+2;
1868 he[NN_END] = headents_for_add[NN_END];
1870 /* pass to pico and let user change them */
1871 editor_result = pico(&pbf);
1872 standard_picobuf_teardown(&pbf);
1874 if(editor_result & COMP_GOTHUP){
1875 ret = -1;
1876 hup_signal(0);
1878 else{
1879 fix_windsize(ps_global);
1880 init_signals();
1883 if(editor_result & COMP_CANCEL){
1884 ret = -1;
1885 if(edit)
1886 q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
1887 else
1888 q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
1890 else if(editor_result & COMP_EXIT){
1891 if(edit &&
1892 !strcmp(server, def_serv ? def_serv : "") &&
1893 !strcmp(folder, def_fold ? def_fold : "") &&
1894 !strcmp(nickname, def_nick ? def_nick : "")){
1895 ret = -1;
1896 if(edit)
1897 q_status_message(SM_ORDER, 0, 3, _("No change: Address book change is cancelled"));
1898 else
1899 q_status_message(SM_ORDER, 0, 3, _("No change: Address book add is cancelled"));
1901 else{
1902 if(global){
1903 list = VAR_GLOB_ADDRBOOK;
1904 how_many_in_list = as.n_addrbk - as.how_many_personals;
1905 if(edit)
1906 new_abook_num = abook_num;
1907 else if(abook_num < 0)
1908 new_abook_num = as.n_addrbk;
1909 else
1910 new_abook_num = MAX(MIN(abook_num + 1, as.n_addrbk),
1911 as.how_many_personals);
1913 num_in_list = new_abook_num - as.how_many_personals;
1915 else{
1916 list = VAR_ADDRESSBOOK;
1917 how_many_in_list = as.how_many_personals;
1918 new_abook_num = abook_num;
1919 if(edit)
1920 new_abook_num = abook_num;
1921 else if(abook_num < 0)
1922 new_abook_num = as.how_many_personals;
1923 else
1924 new_abook_num = MIN(abook_num + 1, as.how_many_personals);
1926 num_in_list = new_abook_num;
1929 if(!edit)
1930 how_many_in_list++; /* for new abook */
1932 removing_leading_and_trailing_white_space(server);
1933 removing_leading_and_trailing_white_space(folder);
1934 removing_leading_and_trailing_white_space(nickname);
1936 /* convert nickname to UTF-8 */
1937 if(nickname){
1938 char *conv;
1940 conv = convert_to_utf8(nickname, NULL, 0);
1941 if(conv){
1942 fs_give((void **) &nickname);
1943 nickname = conv;
1947 /* eliminate surrounding brackets */
1948 if(server[0] == '{' && server[strlen(server)-1] == '}'){
1949 char *p;
1951 server[strlen(server)-1] = '\0';
1952 for(p = server; *p; p++)
1953 *p = *(p+1);
1956 snprintf(tmp, sizeof(tmp), "%s%s%s%.*s",
1957 *server ? "{" : "",
1958 *server ? server : "",
1959 *server ? "}" : "",
1960 MAXFOLDER, folder);
1961 tmp[sizeof(tmp)-1] = '\0';
1963 new_item = put_pair(nickname, tmp);
1965 if(!new_item || *new_item == '\0'){
1966 if(edit)
1967 q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
1968 else
1969 q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
1971 ret = -1;
1972 goto get_out;
1975 /* allocate for new list */
1976 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
1978 /* copy old list up to where we will insert new entry */
1979 for(i = 0; i < num_in_list; i++)
1980 new_list[i] = cpystr(list[i]);
1982 /* insert the new entry */
1983 new_list[i++] = cpystr(new_item);
1985 /* copy rest of old list, skip current if editing */
1986 for(; i < how_many_in_list; i++)
1987 new_list[i] = cpystr(list[edit ? i : (i-1)]);
1989 new_list[i] = NULL;
1991 /* this frees old variable contents for us */
1992 if(set_variable_list(global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK,
1993 new_list, TRUE, ew)){
1994 if(edit)
1995 q_status_message(SM_ORDER, 0, 3, _("Change cancelled: couldn't save configuration file"));
1996 else
1997 q_status_message(SM_ORDER, 0, 3, _("Add cancelled: couldn't save configuration file"));
1999 set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
2000 TRUE, FALSE);
2001 ret = -1;
2002 goto get_out;
2005 ret = new_abook_num;
2006 set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
2007 TRUE, FALSE);
2009 addrbook_reset();
2010 init_ab_if_needed();
2013 * Test to see if this definition is going to work.
2014 * Error messages are a good side effect.
2016 pab = &as.adrbks[num_in_list];
2017 init_abook(pab, NoDisplay);
2018 remember_access_result = pab->access;
2019 addrbook_reset();
2020 init_ab_if_needed();
2021 /* if we had trouble, give a clue to user (other than error msg) */
2022 if(remember_access_result == NoAccess){
2023 pab = &as.adrbks[num_in_list];
2024 pab->access = remember_access_result;
2029 get_out:
2031 if(he)
2032 free_headents(&he);
2034 if(new_list)
2035 free_list_array(&new_list);
2037 if(new_item)
2038 fs_give((void **)&new_item);
2040 if(msgso)
2041 so_give(&msgso);
2043 if(server)
2044 fs_give((void **)&server);
2045 if(folder)
2046 fs_give((void **)&folder);
2047 if(nickname)
2048 fs_give((void **)&nickname);
2050 return(ret);
2055 any_addrbooks_to_convert(struct pine *ps)
2057 PerAddrBook *pab;
2058 int i, count = 0;
2060 init_ab_if_needed();
2062 for(i = 0; i < as.n_addrbk; i++){
2063 pab = &as.adrbks[i];
2064 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2065 count++;
2068 return(count);
2073 convert_addrbooks_to_remote(struct pine *ps, char *rem_folder_prefix, size_t len)
2075 PerAddrBook *pab;
2076 int i, count = 0, ret = 0;
2078 init_ab_if_needed();
2080 for(i = 0; i < as.n_addrbk; i++){
2081 pab = &as.adrbks[i];
2082 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2083 count++;
2086 for(i = 0; ret != -1 && i < as.n_addrbk; i++){
2087 pab = &as.adrbks[i];
2088 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2089 ret = convert_abook_to_remote(ps, pab, rem_folder_prefix, len, count);
2092 return(ret);
2097 * Returns -1 if cancelled, -2 on error, 0 otherwise.
2100 convert_abook_to_remote(struct pine *ps, PerAddrBook *pab, char *rem_folder_prefix, size_t len, int count)
2102 #define DEF_ABOOK_NAME "remote_addrbook"
2103 char local_file[MAILTMPLEN];
2104 char rem_abook[MAILTMPLEN+3], prompt[MAILTMPLEN], old_nick[MAILTMPLEN];
2105 char *p = NULL, *err_msg = NULL, *q;
2106 char *serv = NULL, *nick = NULL, *file = NULL, *folder = NULL;
2107 int ans, rc, offset, i, abook_num = -1, flags = OE_APPEND_CURRENT;
2108 HelpType help;
2110 snprintf(old_nick, sizeof(old_nick), "%s%s%s",
2111 count > 1 ? " \"" : "",
2112 count > 1 ? pab->abnick : "",
2113 count > 1 ? "\"" : "");
2114 old_nick[sizeof(old_nick)-1] = '\0';
2116 snprintf(prompt, sizeof(prompt), _("Convert addressbook%s to a remote addrbook "), old_nick);
2117 prompt[sizeof(prompt)-1] = '\0';
2118 if((ans=want_to(prompt, 'y', 'x', h_convert_abook, WT_NORM)) != 'y')
2119 return(ans == 'n' ? 0 : -1);
2121 /* make sure the addrbook has been opened before, so that the file exists */
2122 if(pab->ostatus == Closed || pab->ostatus == HalfOpen){
2123 (void)init_addrbooks(NoDisplay, 0, 0, 0);
2124 (void)init_addrbooks(Closed, 0, 0, 0);
2127 if(pab->filename){
2128 strncpy(local_file, pab->filename, sizeof(local_file)-1);
2129 local_file[sizeof(local_file)-1] = '\0';
2130 #if defined(DOS)
2131 p = strrindex(pab->filename, '\\');
2132 #else
2133 p = strrindex(pab->filename, '/');
2134 #endif
2137 strncpy(rem_abook, rem_folder_prefix, sizeof(rem_abook)-3);
2138 if(!*rem_abook){
2139 /* TRANSLATORS: The user is defining an address book which will be
2140 stored on another server. This is called a remote addrbook and
2141 this is a question asking for the name of the server. */
2142 snprintf(prompt, sizeof(prompt), _("Name of server to contain remote addrbook : "));
2143 prompt[sizeof(prompt)-1] = '\0';
2144 help = NO_HELP;
2145 while(1){
2146 rc = optionally_enter(rem_abook, -FOOTER_ROWS(ps), 0,
2147 sizeof(rem_abook), prompt, NULL,
2148 help, &flags);
2149 removing_leading_and_trailing_white_space(rem_abook);
2150 if(rc == 3){
2151 help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
2153 else if(rc == 1){
2154 cmd_cancelled(NULL);
2155 return(-1);
2157 else if(rc == 0){
2158 if(*rem_abook){
2159 /* add brackets */
2160 offset = strlen(rem_abook);
2161 for(i = offset; i >= 0; i--)
2162 rem_abook[i+1] = rem_abook[i];
2164 rem_abook[0] = '{';
2165 rem_abook[++offset] = '}';
2166 rem_abook[++offset] = '\0';
2167 break;
2173 if(*rem_abook){
2174 if(p && count > 1)
2175 strncat(rem_abook, p+1,
2176 sizeof(rem_abook)-1-strlen(rem_abook));
2177 else
2178 strncat(rem_abook, DEF_ABOOK_NAME,
2179 sizeof(rem_abook)-1-strlen(rem_abook));
2182 if(*rem_abook){
2183 file = cpystr(rem_abook);
2184 if(pab->abnick){
2185 int len = MAX(strlen(pab->abnick),strlen("Address Book"))+8;
2186 nick = (char *)fs_get(len * sizeof(char));
2187 snprintf(nick, len, "Remote %s",
2188 (pab->abnick && !strcmp(pab->abnick, DF_ADDRESSBOOK))
2189 ? "Address Book" : pab->abnick);
2190 nick[len-1] = '\0';
2192 else
2193 nick = cpystr("Remote Address Book");
2195 if(file && *file == '{'){
2196 q = file + 1;
2197 if((p = strindex(file, '}'))){
2198 *p = '\0';
2199 serv = q;
2200 folder = p+1;
2202 else if(file)
2203 fs_give((void **)&file);
2205 else
2206 folder = file;
2209 q_status_message(SM_ORDER, 3, 5,
2210 _("You now have a chance to change the name of the remote addrbook..."));
2211 abook_num = ab_modify_abook_list(0, 0, -1, serv, folder, nick);
2213 /* extract folder name of new abook so we can copy to it */
2214 if(abook_num >= 0){
2215 char **lval;
2216 EditWhich ew = Main;
2218 lval = LVAL(&ps->vars[V_ADDRESSBOOK], ew);
2219 get_pair(lval[abook_num], &nick, &file, 0, 0);
2220 if(nick)
2221 fs_give((void **)&nick);
2223 if(file){
2224 strncpy(rem_abook, file, sizeof(rem_abook)-1);
2225 rem_abook[sizeof(rem_abook)-1] = '\0';
2226 fs_give((void **)&file);
2230 /* copy the abook */
2231 if(abook_num >= 0 && copy_abook(local_file, rem_abook, &err_msg)){
2232 if(err_msg){
2233 q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
2234 fs_give((void **)&err_msg);
2237 return(-2);
2239 else if(abook_num >= 0){ /* give user some info */
2240 STORE_S *store;
2241 SCROLL_S sargs;
2242 char *beg, *end;
2245 * Save the hostname in rem_folder_prefix so we can use it again
2246 * for other conversions if needed.
2248 if((beg = rem_abook)
2249 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
2250 && (end = strindex(rem_abook, '}'))){
2251 rem_folder_prefix[0] = '{';
2252 strncpy(rem_folder_prefix+1, beg+1, MIN(end-beg,len-2));
2253 rem_folder_prefix[MIN(end-beg,len-2)] = '}';
2254 rem_folder_prefix[MIN(end-beg+1,len-1)] = '\0';
2257 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2258 q_status_message(SM_ORDER | SM_DING, 7, 10,
2259 _("Error allocating space for message."));
2260 return(-2);
2263 /* TRANSLATORS: Several lines in a row here that go together. */
2264 snprintf(prompt, sizeof(prompt), _("\nYour addressbook%s has been copied to the"), old_nick);
2265 prompt[sizeof(prompt)-1] = '\0';
2266 so_puts(store, prompt);
2267 so_puts(store, _("\nremote folder \""));
2268 so_puts(store, rem_abook);
2269 so_puts(store, "\".");
2270 so_puts(store, _("\nA definition for this remote address book has been added to your list"));
2271 so_puts(store, _("\nof address books. The definition for the address book it was copied"));
2272 so_puts(store, _("\nfrom is also still there. You may want to remove that after you"));
2273 so_puts(store, _("\nare confident that the new address book is complete and working."));
2274 so_puts(store, _("\nUse the Setup/AddressBooks command to do that.\n"));
2276 memset(&sargs, 0, sizeof(SCROLL_S));
2277 sargs.text.text = so_text(store);
2278 sargs.text.src = CharStar;
2279 sargs.text.desc = _("Remote Address Book Information");
2280 /* TRANSLATORS: a screen title */
2281 sargs.bar.title = _("ABOUT REMOTE ABOOK");
2282 sargs.help.text = NO_HELP;
2283 sargs.help.title = NULL;
2285 scrolltool(&sargs);
2287 so_give(&store); /* free resources associated with store */
2288 ps->mangled_screen = 1;
2291 return(0);
2296 any_sigs_to_convert(struct pine *ps)
2298 char *sigfile, *litsig;
2299 long rflags;
2300 PAT_STATE pstate;
2301 PAT_S *pat;
2302 PAT_LINE_S *patline;
2304 /* first check main signature file */
2305 sigfile = ps->VAR_SIGNATURE_FILE;
2306 litsig = ps->VAR_LITERAL_SIG;
2308 if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
2309 !IS_REMOTE(sigfile))
2310 return(1);
2312 rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
2313 if(any_patterns(rflags, &pstate)){
2314 set_pathandle(rflags);
2315 for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
2316 patline; patline = patline->next){
2317 for(pat = patline->first; pat; pat = pat->next){
2320 * See detoken() for when a sig file is used with a role.
2322 sigfile = pat->action ? pat->action->sig : NULL;
2323 litsig = pat->action ? pat->action->litsig : NULL;
2325 if(sigfile && *sigfile && !litsig &&
2326 sigfile[strlen(sigfile)-1] != '|' &&
2327 !IS_REMOTE(sigfile))
2328 return(1);
2333 return(0);
2338 any_rule_files_to_warn_about(struct pine *ps)
2340 long rflags;
2341 PAT_STATE pstate;
2342 PAT_S *pat;
2344 rflags = (ROLE_DO_ROLES | ROLE_DO_INCOLS | ROLE_DO_SCORES |
2345 ROLE_DO_FILTER | ROLE_DO_OTHER | ROLE_DO_SRCH | PAT_USE_MAIN);
2346 if(any_patterns(rflags, &pstate)){
2347 for(pat = first_pattern(&pstate);
2348 pat;
2349 pat = next_pattern(&pstate)){
2350 if(pat->patline && pat->patline->type == File)
2351 break;
2354 if(pat)
2355 return(1);
2358 return(0);
2363 convert_sigs_to_literal(struct pine *ps, int interactive)
2365 EditWhich ew = Main;
2366 char *sigfile, *litsig, *cstring_version, *nick, *src = NULL;
2367 char prompt[MAILTMPLEN];
2368 STORE_S *store;
2369 SCROLL_S sargs;
2370 long rflags;
2371 int ans;
2372 PAT_STATE pstate;
2373 PAT_S *pat;
2374 PAT_LINE_S *patline;
2376 /* first check main signature file */
2377 sigfile = ps->VAR_SIGNATURE_FILE;
2378 litsig = ps->VAR_LITERAL_SIG;
2380 if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
2381 !IS_REMOTE(sigfile)){
2382 if(interactive){
2383 snprintf(prompt,sizeof(prompt),
2384 /* TRANSLATORS: A literal sig is a way to store a signature
2385 in alpine. It isn't a very descriptive name. Instead of
2386 storing it in its own file it is stored in the configuration
2387 file. */
2388 _("Convert signature file \"%s\" to a literal sig "),
2389 sigfile);
2390 prompt[sizeof(prompt)-1] = '\0';
2391 ClearBody();
2392 ps->mangled_body = 1;
2393 if((ans=want_to(prompt, 'y', 'x', h_convert_sig, WT_NORM)) == 'x'){
2394 cmd_cancelled(NULL);
2395 return(-1);
2398 else
2399 ans = 'y';
2401 if(ans == 'y' && (src = get_signature_file(sigfile, 0, 0, 0)) != NULL){
2402 cstring_version = string_to_cstring(src);
2403 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2405 if(cstring_version)
2406 fs_give((void **)&cstring_version);
2408 fs_give((void **)&src);
2410 if(interactive){
2411 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2412 q_status_message(SM_ORDER | SM_DING, 7, 10,
2413 _("Error allocating space for message."));
2414 return(-1);
2417 snprintf(prompt, sizeof(prompt),
2418 /* TRANSLATORS: following lines go together */
2419 _("\nYour signature file \"%s\" has been converted"), sigfile);
2420 prompt[sizeof(prompt)-1] = '\0';
2421 so_puts(store, prompt);
2422 so_puts(store,
2423 _("\nto a literal signature, which means it is contained in your"));
2424 so_puts(store,
2425 _("\nAlpine configuration instead of being in a file of its own."));
2426 so_puts(store,
2427 _("\nIf that configuration is copied to a remote folder then the"));
2428 so_puts(store,
2429 _("\nsignature will be available remotely also."));
2430 so_puts(store,
2431 _("\nChanges to the signature file itself will no longer have any"));
2432 so_puts(store,
2433 _("\neffect on Alpine but you may still edit the signature with the"));
2434 so_puts(store,
2435 _("\nSetup/Signature command.\n"));
2437 memset(&sargs, 0, sizeof(SCROLL_S));
2438 sargs.text.text = so_text(store);
2439 sargs.text.src = CharStar;
2440 sargs.text.desc = _("Literal Signature Information");
2441 /* TRANSLATORS: screen title */
2442 sargs.bar.title = _("ABOUT LITERAL SIG");
2443 sargs.help.text = NO_HELP;
2444 sargs.help.title = NULL;
2446 scrolltool(&sargs);
2448 so_give(&store);
2449 ps->mangled_screen = 1;
2454 rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
2455 if(any_patterns(rflags, &pstate)){
2456 set_pathandle(rflags);
2457 for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
2458 patline; patline = patline->next){
2459 for(pat = patline->first; pat; pat = pat->next){
2462 * See detoken() for when a sig file is used with a role.
2464 sigfile = pat->action ? pat->action->sig : NULL;
2465 litsig = pat->action ? pat->action->litsig : NULL;
2466 nick = (pat->action && pat->action->nick && pat->action->nick[0]) ? pat->action->nick : NULL;
2468 if(sigfile && *sigfile && !litsig &&
2469 sigfile[strlen(sigfile)-1] != '|' &&
2470 !IS_REMOTE(sigfile)){
2471 if(interactive){
2472 snprintf(prompt,sizeof(prompt),
2473 /* TRANSLATORS: asking whether a signature file should be converted to what
2474 we call a literal signature, which is one contained in the regular
2475 configuration file. Think of the set of 4 %s arguments as a
2476 single argument which is the name of the signature file. */
2477 _("Convert signature file \"%s\"%s%s%s to a literal sig "),
2478 sigfile,
2479 nick ? " in role \"" : "",
2480 nick ? nick : "",
2481 nick ? "\"" : "");
2482 prompt[sizeof(prompt)-1] = '\0';
2483 ClearBody();
2484 ps->mangled_body = 1;
2485 if((ans=want_to(prompt, 'y', 'x',
2486 h_convert_sig, WT_NORM)) == 'x'){
2487 cmd_cancelled(NULL);
2488 return(-1);
2491 else
2492 ans = 'y';
2494 if(ans == 'y' &&
2495 (src = get_signature_file(sigfile,0,0,0)) != NULL){
2497 cstring_version = string_to_cstring(src);
2499 if(pat->action->litsig)
2500 fs_give((void **)&pat->action->litsig);
2502 pat->action->litsig = cstring_version;
2503 fs_give((void **)&src);
2505 set_pathandle(rflags);
2506 if(patline->type == Literal)
2507 (*cur_pat_h)->dirtypinerc = 1;
2508 else
2509 patline->dirty = 1;
2511 if(write_patterns(rflags) == 0){
2512 if(interactive){
2514 * Flush out current_vals of anything we've
2515 * possibly changed.
2517 close_patterns(ROLE_DO_ROLES | PAT_USE_CURRENT);
2519 if(!(store=so_get(CharStar,NULL,EDIT_ACCESS))){
2520 q_status_message(SM_ORDER | SM_DING, 7, 10,
2521 _("Error allocating space for message."));
2522 return(-1);
2525 snprintf(prompt, sizeof(prompt),
2526 /* TRANSLATORS: Keep the %s's together, they are sort of
2527 the name of the file. */
2528 _("Your signature file \"%s\"%s%s%s has been converted"),
2529 sigfile,
2530 nick ? " in role \"" : "",
2531 nick ? nick : "",
2532 nick ? "\"" : "");
2533 prompt[sizeof(prompt)-1] = '\0';
2534 so_puts(store, prompt);
2535 so_puts(store,
2536 /* TRANSLATORS: several lines that go together */
2537 _("\nto a literal signature, which means it is contained in your"));
2538 so_puts(store,
2539 _("\nAlpine configuration instead of being in a file of its own."));
2540 so_puts(store,
2541 _("\nIf that configuration is copied to a remote folder then the"));
2542 so_puts(store,
2543 _("\nsignature will be available remotely also."));
2544 so_puts(store,
2545 _("\nChanges to the signature file itself will no longer have any"));
2546 so_puts(store,
2547 _("\neffect on Alpine. You may edit the signature with the"));
2548 so_puts(store,
2549 _("\nSetup/Rules/Roles command.\n"));
2551 memset(&sargs, 0, sizeof(SCROLL_S));
2552 sargs.text.text = so_text(store);
2553 sargs.text.src = CharStar;
2554 sargs.text.desc =
2555 _("Literal Signature Information");
2556 /* TRANSLATORS: a screen title */
2557 sargs.bar.title = _("ABOUT LITERAL SIG");
2558 sargs.help.text = NO_HELP;
2559 sargs.help.title = NULL;
2561 scrolltool(&sargs);
2563 so_give(&store);
2564 ps->mangled_screen = 1;
2567 else if(interactive){
2568 q_status_message(SM_ORDER | SM_DING, 7, 10,
2569 /* TRANSLATORS: config is an abbreviation for configuration */
2570 _("Error writing rules config."));
2572 else{
2573 /* TRANSLATORS: sig is signature */
2574 fprintf(stderr, _("Error converting role sig\n"));
2575 return(-1);
2583 return(0);
2587 void
2588 warn_about_rule_files(struct pine *ps)
2590 STORE_S *store;
2591 SCROLL_S sargs;
2593 if(any_rule_files_to_warn_about(ps)){
2594 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2595 q_status_message(SM_ORDER | SM_DING, 7, 10,
2596 _("Error allocating space for message."));
2597 return;
2600 /* TRANSLATORS: several lines that go together */
2601 so_puts(store, _("\nSome of your Rules are contained in Rule files instead of being directly"));
2602 so_puts(store, _("\ncontained in your Alpine configuration file. To make those rules"));
2603 so_puts(store, _("\navailable remotely you will need to move them out of the files."));
2604 so_puts(store, _("\nThat can be done using the Shuffle command in the appropriate"));
2605 so_puts(store, _("\nSetup/Rules subcommands.\n"));
2607 memset(&sargs, 0, sizeof(SCROLL_S));
2608 sargs.text.text = so_text(store);
2609 sargs.text.src = CharStar;
2610 sargs.text.desc = _("Rule Files Information");
2611 /* TRANSLATORS: a screen title */
2612 sargs.bar.title = _("ABOUT RULE FILES");
2613 sargs.help.text = NO_HELP;
2614 sargs.help.title = NULL;
2616 scrolltool(&sargs);
2618 so_give(&store);
2619 ps->mangled_screen = 1;
2624 void
2625 convert_to_remote_config(struct pine *ps, int edit_exceptions)
2627 char rem_pinerc_prefix[MAILTMPLEN];
2628 char *beg, *end;
2629 CONTEXT_S *context;
2630 int abooks = 0, sigs = 0;
2632 if(edit_exceptions){
2633 /* TRANSLATORS: The exceptions command (X) was typed but it doesn't make sense */
2634 q_status_message(SM_ORDER, 3, 5,
2635 _("eXceptions does not make sense with this command"));
2636 return;
2639 if(!ps->prc)
2640 alpine_panic("NULL prc in convert_to_remote_config");
2642 dprint((2, "convert_to_remote_config\n"));
2644 if(ps->prc->type == RemImap){ /* pinerc is already remote */
2645 char prompt[MAILTMPLEN];
2648 * Check to see if there is anything at all to do. If there are
2649 * address books to convert, sigfiles to convert, or rule files
2650 * to comment on, we have something to do. Otherwise, just bail.
2652 abooks = any_addrbooks_to_convert(ps);
2653 sigs = any_sigs_to_convert(ps);
2655 if(abooks || sigs){
2656 if(abooks && sigs)
2657 /* TRANSLATORS: AddressBooks is Address Books */
2658 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks and signature files "));
2659 else if(abooks)
2660 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks "));
2661 else
2662 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert signature files "));
2664 prompt[sizeof(prompt)-1] = '\0';
2665 if(want_to(prompt, 'y', 'x',
2666 (abooks && sigs) ? h_convert_abooks_and_sigs :
2667 abooks ? h_convert_abooks :
2668 sigs ? h_convert_sigs : NO_HELP,
2669 WT_NORM) != 'y'){
2670 cmd_cancelled(NULL);
2671 return;
2677 * Figure out a good default for where to put the remote config.
2678 * If the default collection is remote we'll take the hostname and
2679 * and modifiers from there. If not, we'll try to get the hostname from
2680 * the inbox-path. In either case, we use the home directory on the
2681 * server, not the directory where the folder collection is (if different).
2682 * If we don't have a clue, we'll ask user.
2684 if((context = default_save_context(ps->context_list)) != NULL &&
2685 IS_REMOTE(context_apply(rem_pinerc_prefix, context, "",
2686 sizeof(rem_pinerc_prefix)))){
2687 /* just use the host from the default collection, not the whole path */
2688 if((end = strrindex(rem_pinerc_prefix, '}')) != NULL)
2689 *(end + 1) = '\0';
2691 else{
2692 /* use host from inbox path */
2693 rem_pinerc_prefix[0] = '\0';
2694 if((beg = ps->VAR_INBOX_PATH)
2695 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
2696 && (end = strindex(ps->VAR_INBOX_PATH, '}'))){
2697 rem_pinerc_prefix[0] = '{';
2698 strncpy(rem_pinerc_prefix+1, beg+1,
2699 MIN(end-beg, sizeof(rem_pinerc_prefix)-2));
2700 rem_pinerc_prefix[MIN(end-beg, sizeof(rem_pinerc_prefix)-2)] = '}';
2701 rem_pinerc_prefix[MIN(end-beg+1,sizeof(rem_pinerc_prefix)-1)]='\0';
2705 /* ask about converting addrbooks to remote abooks */
2706 if(ps->prc->type != RemImap || abooks)
2707 if(convert_addrbooks_to_remote(ps, rem_pinerc_prefix,
2708 sizeof(rem_pinerc_prefix)) == -1){
2709 cmd_cancelled(NULL);
2710 return;
2713 /* ask about converting sigfiles to literal sigs */
2714 if(ps->prc->type != RemImap || sigs)
2715 if(convert_sigs_to_literal(ps, 1) == -1){
2716 cmd_cancelled(NULL);
2717 return;
2720 warn_about_rule_files(ps);
2722 /* finally, copy the config file */
2723 if(ps->prc->type == Loc)
2724 convert_pinerc_to_remote(ps, rem_pinerc_prefix);
2725 else if(!(abooks || sigs))
2726 q_status_message(SM_ORDER, 3, 5,
2727 _("Cannot copy config file since it is already remote."));
2731 void
2732 convert_pinerc_to_remote(struct pine *ps, char *rem_pinerc_prefix)
2734 #define DEF_FOLDER_NAME "remote_pinerc"
2735 char prompt[MAILTMPLEN], rem_pinerc[MAILTMPLEN];
2736 char *err_msg = NULL;
2737 int i, rc, offset;
2738 HelpType help;
2739 int flags = OE_APPEND_CURRENT;
2741 ClearBody();
2742 ps->mangled_body = 1;
2743 strncpy(rem_pinerc, rem_pinerc_prefix, sizeof(rem_pinerc)-1);
2744 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2746 if(*rem_pinerc == '\0'){
2747 snprintf(prompt, sizeof(prompt), _("Name of server to contain remote Alpine config : "));
2748 prompt[sizeof(prompt)-1] = '\0';
2749 help = NO_HELP;
2750 while(1){
2751 rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
2752 sizeof(rem_pinerc), prompt, NULL,
2753 help, &flags);
2754 removing_leading_and_trailing_white_space(rem_pinerc);
2755 if(rc == 3){
2756 help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
2758 else if(rc == 1){
2759 cmd_cancelled(NULL);
2760 return;
2762 else if(rc == 0){
2763 if(*rem_pinerc){
2764 /* add brackets */
2765 offset = strlen(rem_pinerc);
2766 for(i = offset; i >= 0; i--)
2767 if(i+1 < sizeof(rem_pinerc))
2768 rem_pinerc[i+1] = rem_pinerc[i];
2770 rem_pinerc[0] = '{';
2771 if(offset+2 < sizeof(rem_pinerc)){
2772 rem_pinerc[++offset] = '}';
2773 rem_pinerc[++offset] = '\0';
2776 break;
2782 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2785 * Add a default folder name.
2787 if(*rem_pinerc){
2789 * Add /user= to modify hostname so that user won't be asked who they
2790 * are each time they login.
2792 if(!strstr(rem_pinerc, "/user=") && ps->VAR_USER_ID &&
2793 ps->VAR_USER_ID[0]){
2794 char *p;
2796 p = rem_pinerc + strlen(rem_pinerc) - 1;
2797 if(*p == '}') /* this should be the case */
2798 snprintf(p, sizeof(rem_pinerc)-(p-rem_pinerc), "/user=\"%s\"}", ps->VAR_USER_ID);
2800 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2803 strncat(rem_pinerc, DEF_FOLDER_NAME,
2804 sizeof(rem_pinerc) - strlen(rem_pinerc) - 1);
2805 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2808 /* ask user about folder name for remote config */
2809 snprintf(prompt, sizeof(prompt), _("Folder to contain remote config : "));
2810 prompt[sizeof(prompt)-1] = '\0';
2811 help = NO_HELP;
2812 while(1){
2813 rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
2814 sizeof(rem_pinerc), prompt, NULL, help, &flags);
2815 removing_leading_and_trailing_white_space(rem_pinerc);
2816 if(rc == 0 && *rem_pinerc){
2817 break;
2820 if(rc == 3){
2821 help = (help == NO_HELP) ? h_convert_pinerc_folder : NO_HELP;
2823 else if(rc == 1 || rem_pinerc[0] == '\0'){
2824 cmd_cancelled(NULL);
2825 return;
2829 #ifndef _WINDOWS
2831 * If we are on a Unix system, writing to a remote config, we want the
2832 * remote config to work smoothly from a PC, too. If we don't have a
2833 * user-id on the PC then we will be asked for our password.
2834 * So add user-id to the pinerc before we copy it.
2836 if(!ps->vars[V_USER_ID].main_user_val.p && ps->VAR_USER_ID)
2837 ps->vars[V_USER_ID].main_user_val.p = cpystr(ps->VAR_USER_ID);
2839 ps->vars[V_USER_ID].is_used = 1; /* so it will write to pinerc */
2840 ps->prc->outstanding_pinerc_changes = 1;
2841 #endif
2843 if(ps->prc->outstanding_pinerc_changes)
2844 write_pinerc(ps, Main, WRP_NONE);
2846 #ifndef _WINDOWS
2847 ps->vars[V_USER_ID].is_used = 0;
2848 #endif
2850 /* copy the pinerc */
2851 if(copy_pinerc(ps->prc->name, rem_pinerc, &err_msg)){
2852 if(err_msg){
2853 q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
2854 fs_give((void **)&err_msg);
2857 return;
2860 /* tell user about command line flags */
2861 if(ps->prc->type != RemImap){
2862 STORE_S *store;
2863 SCROLL_S sargs;
2865 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2866 q_status_message(SM_ORDER | SM_DING, 7, 10,
2867 _("Error allocating space for message."));
2868 return;
2871 /* TRANSLATORS: several lines that go together */
2872 so_puts(store, _("\nYou may want to save a copy of this information!"));
2873 so_puts(store, _("\n\nYour Alpine configuration data has been copied to"));
2874 so_puts(store, "\n\n ");
2875 so_puts(store, rem_pinerc);
2876 so_puts(store, "\n");
2877 so_puts(store, _("\nTo use that remote configuration from this computer you will"));
2878 so_puts(store, _("\nhave to change the way you start Alpine by using the command line option"));
2879 so_puts(store, _("\n\"alpine -p <remote_folder>\". The command should probably be"));
2880 #ifdef _WINDOWS
2881 so_puts(store, "\n\n ");
2882 so_puts(store, "alpine -p ");
2883 so_puts(store, rem_pinerc);
2884 so_puts(store, "\n");
2885 so_puts(store, _("\nWith PC-Alpine, you may want to create a shortcut which"));
2886 so_puts(store, _("\nhas the required arguments."));
2887 #else
2888 so_puts(store, "\n\n ");
2889 so_puts(store, "alpine -p \"");
2890 so_puts(store, rem_pinerc);
2891 so_puts(store, "\"\n");
2892 so_puts(store, _("\nThe quotes are there around the last argument to protect the special"));
2893 so_puts(store, _("\ncharacters in the folder name (like braces) from the command shell"));
2894 so_puts(store, _("\nyou use. If you are not running Alpine from a command shell which knows"));
2895 so_puts(store, _("\nabout quoting, it is possible you will have to remove those quotes"));
2896 so_puts(store, _("\nfrom the command. For example, if you also use PC-Alpine you will probably"));
2897 so_puts(store, _("\nwant to create a shortcut, and you would not need the quotes there."));
2898 so_puts(store, _("\nWithout the quotes, the command might look like"));
2899 so_puts(store, "\n\n ");
2900 so_puts(store, "alpine -p ");
2901 so_puts(store, rem_pinerc);
2902 so_puts(store, "\n");
2903 so_puts(store, _("\nConsider creating an alias or shell script to execute this command to make"));
2904 so_puts(store, _("\nit more convenient."));
2905 #endif
2906 so_puts(store, _("\n\nIf you want to use your new remote configuration for this session, quit"));
2907 so_puts(store, _("\nAlpine now and restart with the changed command line options mentioned above.\n"));
2909 memset(&sargs, 0, sizeof(SCROLL_S));
2910 sargs.text.text = so_text(store);
2911 sargs.text.src = CharStar;
2912 sargs.text.desc = _("Remote Config Information");
2913 /* TRANSLATORS: a screen title */
2914 sargs.bar.title = _("ABOUT REMOTE CONFIG");
2915 sargs.help.text = NO_HELP;
2916 sargs.help.title = NULL;
2918 scrolltool(&sargs);
2920 so_give(&store); /* free resources associated with store */
2921 ps->mangled_screen = 1;
2927 verify_folder_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2929 char *tmp;
2931 tmp = cpystr(given ? given : "");
2932 removing_leading_and_trailing_white_space(tmp);
2934 if(expanded)
2935 *expanded = tmp;
2936 else
2937 fs_give((void **)&tmp);
2939 if(error)
2940 *error = cpystr("");
2942 return 0;
2947 verify_server_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2949 char *tmp;
2951 tmp = cpystr(given ? given : "");
2952 removing_leading_and_trailing_white_space(tmp);
2954 if(*tmp){
2956 * could try to verify the hostname here
2960 if(expanded)
2961 *expanded = tmp;
2962 else
2963 fs_give((void **)&tmp);
2965 if(error)
2966 *error = cpystr("");
2968 return 0;
2973 verify_abook_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2975 int i;
2976 char *tmp;
2978 tmp = cpystr(given ? given : "");
2979 removing_leading_and_trailing_white_space(tmp);
2981 if(strindex(tmp, '"')){
2982 fs_give((void **)&tmp);
2983 if(error)
2984 /* TRANSLATORS: Double quote refers to the " character */
2985 *error = cpystr(_("Double quote not allowed in nickname"));
2987 return -2;
2990 for(i = 0; i < as.n_addrbk; i++)
2991 if(i != the_one_were_editing && !strcmp(tmp, as.adrbks[i].abnick))
2992 break;
2994 if(i < as.n_addrbk){
2995 fs_give((void **)&tmp);
2997 if(error)
2998 *error = cpystr(_("Nickname is already being used"));
3000 return -2;
3003 if(expanded)
3004 *expanded = tmp;
3005 else
3006 fs_give((void **)&tmp);
3008 if(error)
3009 *error = cpystr("");
3011 return 0;
3016 * Delete an addressbook.
3018 * Args: cur_line -- The current line position (in global display list)
3019 * of cursor
3020 * command_line -- The screen line on which to prompt
3021 * err -- Points to error message
3023 * Returns -- 0, deleted addrbook
3024 * -1, addrbook not deleted
3027 ab_del_abook(long int cur_line, int command_line, char **err)
3029 int abook_num, varnum, delete_data = 0,
3030 num_in_list, how_many_in_list = 0, i, cnt, warn_about_revert = 0;
3031 char **list, **new_list, **t, **lval;
3032 char tmp[200];
3033 PerAddrBook *pab;
3034 struct variable *vars = ps_global->vars;
3035 EditWhich ew;
3036 enum {NotSet,
3037 Modify,
3038 RevertToDefault,
3039 OverRideDefault,
3040 DontChange} modify_config;
3042 /* restrict address book config to normal config file */
3043 ew = Main;
3045 if(ps_global->readonly_pinerc){
3046 if(err)
3047 *err = _("Delete cancelled: config file not changeable");
3049 return -1;
3052 abook_num = adrbk_num_from_lineno(cur_line);
3054 pab = &as.adrbks[abook_num];
3056 dprint((2, "- ab_del_abook(%s) -\n",
3057 pab->abnick ? pab->abnick : "?"));
3059 varnum = (pab->type & GLOBAL) ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK;
3061 if(vars[varnum].is_fixed){
3062 if(err){
3063 if(pab->type & GLOBAL)
3064 *err =
3065 _("Cancelled: Sys. Mgmt. does not allow changing global address book config");
3066 else
3067 *err =
3068 _("Cancelled: Sys. Mgmt. does not allow changing address book config");
3071 return -1;
3075 * Deal with reverting to default values of the address book
3076 * variables, or with user deleting a default value.
3078 modify_config = NotSet;
3080 /* First count how many address books are in the user's config. */
3081 cnt = 0;
3082 lval = LVAL(&vars[varnum], ew);
3083 if(lval && lval[0])
3084 for(t = lval; *t != NULL; t++)
3085 cnt++;
3088 * Easy case, we can just delete one from the user's list.
3090 if(cnt > 1){
3091 modify_config = Modify;
3094 * Also easy. We'll revert to the default if it exists, and warn
3095 * the user about that.
3097 else if(cnt == 1){
3098 modify_config = RevertToDefault;
3099 /* see if there's a default to revert to */
3100 cnt = 0;
3101 if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
3102 for(t = vars[varnum].global_val.l; *t != NULL; t++)
3103 cnt++;
3105 warn_about_revert = cnt;
3108 * User is already using the default. Split it into two cases. If there
3109 * is one address book in default ask user if they want to delete that
3110 * default from their config. If there is more than one, ask them if
3111 * they want to ignore all the defaults or just delete this one.
3113 else{
3114 /* count how many in default */
3115 cnt = 0;
3116 if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
3117 for(t = vars[varnum].global_val.l; *t != NULL; t++)
3118 cnt++;
3120 if(cnt > 1){
3121 static ESCKEY_S opts[] = {
3122 /* TRANSLATORS: Ignore All means ignore all of the default values,
3123 and Remove One means just remove this one default value. */
3124 {'i', 'i', "I", N_("Ignore All")},
3125 {'r', 'r', "R", N_("Remove One")},
3126 {-1, 0, NULL, NULL}};
3128 snprintf(tmp, sizeof(tmp),
3129 /* TRANSLATORS: %s is an adjective modifying address books */
3130 _("Ignore all default %s address books or just remove this one ? "),
3131 /* TRANSLATORS: global or personal address books */
3132 pab->type & GLOBAL ? _("global") : _("personal"));
3133 tmp[sizeof(tmp)-1] = '\0';
3134 switch(radio_buttons(tmp, command_line, opts, 'i', 'x',
3135 h_ab_del_ignore, RB_NORM)){
3136 case 'i':
3137 modify_config = OverRideDefault;
3138 break;
3140 case 'r':
3141 modify_config = Modify;
3142 break;
3144 case 'x':
3145 if(err)
3146 *err = _("Delete cancelled");
3148 return -1;
3151 else{
3152 /* TRANSLATORS: a question */
3153 switch(want_to(_("Delete this default address book from config "),
3154 'n', 'x', h_ab_del_default, WT_NORM)){
3155 case 'n':
3156 case 'x':
3157 if(err)
3158 *err = _("Delete cancelled");
3160 return -1;
3162 case 'y':
3163 modify_config = OverRideDefault;
3164 break;
3170 * ReadWrite means it exists and MaybeRorW means it is remote and we
3171 * haven't selected it yet to know our access permissions. The remote
3172 * folder should have been created, though, unless we didn't even have
3173 * permissions for that, in which case we got some error messages earlier.
3175 if(pab->access == ReadWrite || pab->access == MaybeRorW){
3176 static ESCKEY_S o[] = {
3177 /* TRANSLATORS: user is asked whether to remove just data files, just configuration,
3178 or both for an address book. */
3179 {'d', 'd', "D", N_("Data")},
3180 {'c', 'c', "C", N_("Config")},
3181 {'b', 'b', "B", N_("Both")},
3182 {-1, 0, NULL, NULL}};
3184 switch(radio_buttons(_("Delete data, config, or both ? "),
3185 command_line, o, 'c', 'x',
3186 (modify_config == RevertToDefault)
3187 ? h_ab_del_data_revert
3188 : h_ab_del_data_modify,
3189 RB_NORM)){
3190 case 'b': /* Delete Both */
3191 delete_data = 1;
3192 break;
3194 case 'd': /* Delete only Data */
3195 modify_config = DontChange;
3196 delete_data = 1;
3197 break;
3199 case 'c': /* Delete only Config */
3200 break;
3202 case 'x': /* Cancel */
3203 default:
3204 if(err)
3205 *err = _("Delete cancelled");
3207 return -1;
3210 else{
3212 * Deleting config for address book which doesn't yet exist (hasn't
3213 * ever been opened).
3215 /* TRANSLATORS: a question */
3216 switch(want_to(_("Delete configuration for highlighted addressbook "),
3217 'n', 'x',
3218 (modify_config == RevertToDefault)
3219 ? h_ab_del_config_revert
3220 : h_ab_del_config_modify,
3221 WT_NORM)){
3222 case 'n':
3223 case 'x':
3224 default:
3225 if(err)
3226 *err = _("Delete cancelled");
3228 return -1;
3230 case 'y':
3231 break;
3235 if(delete_data){
3236 char warning[800];
3238 dprint((5, "deleting addrbook data\n"));
3239 warning[0] = '\0';
3242 * In order to delete the address book it is easiest if we open
3243 * it first. That fills in the filenames we want to delete.
3245 if(pab->address_book == NULL){
3246 warning[300] = '\0';
3247 pab->address_book = adrbk_open(pab, ps_global->home_dir,
3248 &warning[300], sizeof(warning)-300,
3249 AB_SORT_RULE_NONE);
3251 * Couldn't get it open.
3253 if(pab->address_book == NULL){
3254 if(warning[300])
3255 /* TRANSLATORS: %s is an error message */
3256 snprintf(warning, 300, _("Can't delete data: %s"), &warning[300]);
3257 else
3258 strncpy(warning, _("Can't delete address book data"), 100);
3263 * If we have it open, set the delete bits and close to get the
3264 * local copies. Delete the remote folder by hand.
3266 if(pab->address_book){
3267 char *file, *origfile = NULL;
3268 int f=0, o=0;
3271 * We're about to destroy addrbook data, better ask again.
3273 if(pab->address_book->count > 0){
3274 char prompt[100];
3276 /* TRANSLATORS: a question */
3277 snprintf(prompt, sizeof(prompt),
3278 _("About to delete the contents of address book (%ld entries), really delete "), (long) adrbk_count(pab->address_book));
3279 prompt[sizeof(prompt)-1] = '\0';
3281 switch(want_to(prompt, 'n', 'n', h_ab_really_delete, WT_NORM)){
3282 case 'y':
3283 break;
3285 case 'n':
3286 default:
3287 if(err)
3288 *err = _("Delete cancelled");
3290 return -1;
3294 pab->address_book->flags |= DEL_FILE;
3295 file = cpystr(pab->address_book->filename);
3296 if(pab->type & REMOTE_VIA_IMAP)
3297 origfile = cpystr(pab->address_book->orig_filename);
3300 * In order to avoid locking problems when we delete the
3301 * remote folder, we need to actually close the remote stream
3302 * instead of just putting it back in the stream pool.
3303 * So we will remove this stream from the re-usable portion
3304 * of the stream pool by clearing the SP_USEPOOL flag.
3305 * Init_abook(pab, TotallyClosed) via rd_close_remdata is
3306 * going to pine_mail_close it.
3308 if(pab->type & REMOTE_VIA_IMAP
3309 && pab->address_book
3310 && pab->address_book->type == Imap
3311 && pab->address_book->rd
3312 && rd_stream_exists(pab->address_book->rd)){
3314 sp_unflag(pab->address_book->rd->t.i.stream, SP_USEPOOL);
3317 /* This deletes the files because of DEL_ bits we set above. */
3318 init_abook(pab, TotallyClosed);
3321 * Delete the remote folder.
3323 if(pab->type & REMOTE_VIA_IMAP){
3324 REMDATA_S *rd;
3325 int exists;
3327 ps_global->c_client_error[0] = '\0';
3328 if(!pine_mail_delete(NULL, origfile) &&
3329 ps_global->c_client_error[0] != '\0'){
3330 dprint((1, "%s: %s\n", origfile ? origfile : "?",
3331 ps_global->c_client_error));
3334 /* delete line from metadata */
3335 rd = rd_new_remdata(RemImap, origfile, NULL);
3336 rd_write_metadata(rd, 1);
3337 rd_close_remdata(&rd);
3339 /* Check to see if it's still there */
3340 if((exists=folder_exists(NULL, origfile)) &&
3341 (exists != FEX_ERROR)){
3342 o++;
3343 dprint((1, "Trouble deleting %s\n",
3344 origfile ? origfile : "?"));
3348 if(can_access(file, ACCESS_EXISTS) == 0){
3349 f++;
3350 dprint((1, "Trouble deleting %s\n",
3351 file ? file : "?"));
3354 if(f || o){
3355 snprintf(warning, sizeof(warning), _("Trouble deleting data %s%s%s%s"),
3356 f ? file : "",
3357 (f && o) ? (o ? ", " : " and ") : "",
3358 o ? " and " : "",
3359 o ? origfile : "");
3360 warning[sizeof(warning)-1] = '\0';
3363 fs_give((void **) &file);
3364 if(origfile)
3365 fs_give((void **) &origfile);
3368 if(*warning){
3369 q_status_message(SM_ORDER, 3, 3, warning);
3370 dprint((1, "%s\n", warning));
3371 display_message(NO_OP_COMMAND);
3373 else if(modify_config == DontChange)
3374 q_status_message(SM_ORDER, 0, 1, _("Addressbook data deleted"));
3377 if(modify_config == DontChange){
3379 * We return -1 to indicate that the addrbook wasn't deleted (as far
3380 * as we're concerned) but we don't fill in err so that no error
3381 * message will be printed.
3382 * Since the addrbook is still an addrbook we need to reinitialize it.
3384 pab->access = adrbk_access(pab);
3385 if(pab->type & GLOBAL && pab->access != NoAccess)
3386 pab->access = ReadOnly;
3388 init_abook(pab, HalfOpen);
3389 return -1;
3391 else if(modify_config == Modify){
3392 list = vars[varnum].current_val.l;
3393 if(pab->type & GLOBAL){
3394 how_many_in_list = as.n_addrbk - as.how_many_personals - 1;
3395 num_in_list = abook_num - as.how_many_personals;
3397 else{
3398 how_many_in_list = as.how_many_personals - 1;
3399 num_in_list = abook_num;
3402 else if(modify_config == OverRideDefault)
3403 how_many_in_list = 1;
3404 else if(modify_config == RevertToDefault)
3405 how_many_in_list = 0;
3406 else
3407 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_del_abook");
3409 /* allocate for new list */
3410 if(how_many_in_list)
3411 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
3412 else
3413 new_list = NULL;
3416 * This case is both for modifying the users user_val and for the
3417 * case where the user wants to modify the global_val default and
3418 * use the modified version for his or her new user_val. We just
3419 * copy from the existing global_val, deleting the one addrbook
3420 * and put the result in user_val.
3422 if(modify_config == Modify){
3423 /* copy old list up to where we will delete entry */
3424 for(i = 0; i < num_in_list; i++)
3425 new_list[i] = cpystr(list[i]);
3427 /* copy rest of old list */
3428 for(; i < how_many_in_list; i++)
3429 new_list[i] = cpystr(list[i+1]);
3431 new_list[i] = NULL;
3433 else if(modify_config == OverRideDefault){
3434 new_list[0] = cpystr("");
3435 new_list[1] = NULL;
3438 /* this also frees old variable contents for us */
3439 if(set_variable_list(varnum, new_list, TRUE, ew)){
3440 if(err)
3441 *err = _("Delete cancelled: couldn't save pine configuration file");
3443 set_current_val(&vars[varnum], TRUE, FALSE);
3444 free_list_array(&new_list);
3446 return -1;
3449 set_current_val(&vars[varnum], TRUE, FALSE);
3451 if(warn_about_revert){
3452 /* TRANSLATORS: the %s may be "global " or nothing */
3453 snprintf(tmp, sizeof(tmp), _("Reverting to default %saddress books"),
3454 pab->type & GLOBAL ? _("global ") : "");
3455 tmp[sizeof(tmp)-1] = '\0';
3456 q_status_message(SM_ORDER, 3, 4, tmp);
3459 free_list_array(&new_list);
3461 return 0;
3466 * Shuffle addrbooks.
3468 * Args: pab -- Pab from current addrbook.
3469 * slide -- return value, tells how far to slide the cursor. If slide
3470 * is negative, slide it up, if positive, slide it down.
3471 * command_line -- The screen line on which to prompt
3472 * msg -- Points to returned message, if any. Should be freed by
3473 * caller.
3475 * Result: Two address books are swapped in the display order. If the shuffle
3476 * crosses the Personal/Global boundary, then instead of swapping
3477 * two address books the highlighted abook is moved from one section
3478 * to the other.
3479 * >= 0 on success.
3480 * < 0 on failure, no changes.
3481 * > 0 If the return value is greater than zero it means that we've
3482 * reverted one of the variables to its default value. That
3483 * means we've added at least one new addrbook, so the caller
3484 * should reset. The value returned is the number of the
3485 * moved addrbook + 1 (+1 so it won't be confused with zero).
3486 * = 0 If the return value is zero we've just moved addrbooks around.
3487 * No reset need be done.
3490 ab_shuffle(PerAddrBook *pab, int *slide, int command_line, char **msg)
3492 ESCKEY_S opts[3];
3493 char tmp[200];
3494 int i, deefault, rv, target = 0;
3495 int up_into_empty = 0, down_into_empty = 0;
3496 HelpType help;
3497 struct variable *vars = ps_global->vars;
3499 dprint((2, "- ab_shuffle() -\n"));
3501 *slide = 0;
3503 if(ps_global->readonly_pinerc){
3504 if(msg)
3505 *msg = cpystr(_("Shuffle cancelled: config file not changeable"));
3507 return -1;
3510 /* Move it up or down? */
3511 i = 0;
3512 opts[i].ch = 'u';
3513 opts[i].rval = 'u';
3514 opts[i].name = "U";
3515 /* TRANSLATORS: shuffle something Up or Down in a list */
3516 opts[i++].label = N_("Up");
3518 opts[i].ch = 'd';
3519 opts[i].rval = 'd';
3520 opts[i].name = "D";
3521 opts[i++].label = N_("Down");
3523 opts[i].ch = -1;
3524 deefault = 'u';
3526 if(pab->type & GLOBAL){
3527 if(vars[V_GLOB_ADDRBOOK].is_fixed){
3528 if(msg)
3529 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3531 return -1;
3534 if(as.cur == 0){
3535 if(as.config)
3536 up_into_empty++;
3537 else{ /* no up */
3538 opts[0].ch = -2;
3539 deefault = 'd';
3543 if(as.cur == as.n_addrbk - 1) /* no down */
3544 opts[1].ch = -2;
3546 else{
3547 if(vars[V_ADDRESSBOOK].is_fixed){
3548 if(msg)
3549 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book config"));
3551 return -1;
3554 if(as.cur == 0){ /* no up */
3555 opts[0].ch = -2;
3556 deefault = 'd';
3559 if(as.cur == as.n_addrbk - 1){
3560 if(as.config)
3561 down_into_empty++;
3562 else
3563 opts[1].ch = -2; /* no down */
3567 snprintf(tmp, sizeof(tmp), _("Shuffle \"%s\" %s%s%s ? "),
3568 pab->abnick,
3569 (opts[0].ch != -2) ? _("UP") : "",
3570 (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
3571 (opts[1].ch != -2) ? _("DOWN") : "");
3572 tmp[sizeof(tmp)-1] = '\0';
3573 help = (opts[0].ch == -2) ? h_ab_shuf_down
3574 : (opts[1].ch == -2) ? h_ab_shuf_up
3575 : h_ab_shuf;
3577 rv = radio_buttons(tmp, command_line, opts, deefault, 'x',
3578 help, RB_NORM);
3580 ps_global->mangled_footer = 1;
3582 if((rv == 'u' && up_into_empty) || (rv == 'd' && down_into_empty))
3583 target = -1;
3584 else
3585 target = as.cur + (rv == 'u' ? -1 : 1);
3587 if(rv == 'x'){
3588 if(msg)
3589 *msg = cpystr(_("Shuffle cancelled"));
3591 return -1;
3593 else
3594 return(do_the_shuffle(slide, as.cur, target, msg));
3599 * Actually shuffle the config variables and address books structures around.
3601 * Args: anum1, anum2 -- The numbers of the address books
3602 * msg -- Points to returned message, if any.
3604 * Returns: >= 0 on success.
3605 * < 0 on failure, no changes.
3606 * > 0 If the return value is greater than zero it means that we've
3607 * reverted one of the variables to its default value. That
3608 * means we've added at least one new addrbook, so the caller
3609 * should reset. The value returned is the number of the
3610 * moved addrbook + 1 (+1 so it won't be confused with zero).
3611 * = 0 If the return value is zero we've just moved addrbooks around.
3612 * No reset need be done.
3614 * Anum1 is the one that we want to move, anum2 is the one that it will be
3615 * swapped with. When anum1 and anum2 are on the opposite sides of the
3616 * Personal/Global boundary then instead of swapping we just move anum1 to
3617 * the other side of the boundary.
3619 * Anum2 of -1 means it is a swap into the other type of address book, which
3620 * is currently empty.
3623 do_the_shuffle(int *slide, int anum1, int anum2, char **msg)
3625 PerAddrBook *pab;
3626 enum {NotSet, Pers, Glob, Empty} type1, type2;
3627 int i, j, retval = -1;
3628 struct variable *vars = ps_global->vars;
3629 char **lval;
3630 EditWhich ew;
3631 char *cancel_msg = _("Shuffle cancelled: couldn't save configuration file");
3633 dprint((5, "- do_the_shuffle(%d, %d) -\n", anum1, anum2));
3635 /* restrict address book config to normal config file */
3636 ew = Main;
3638 if(anum1 == -1)
3639 type1 = Empty;
3640 else{
3641 pab = &as.adrbks[anum1];
3642 type1 = (pab->type & GLOBAL) ? Glob : Pers;
3645 if(type1 == Empty){
3646 if(msg)
3647 *msg =
3648 cpystr(_("Shuffle cancelled: highlight entry you wish to shuffle"));
3650 return(retval);
3653 if(anum2 == -1)
3654 type2 = Empty;
3655 else{
3656 pab = &as.adrbks[anum2];
3657 type2 = (pab->type & GLOBAL) ? Glob : Pers;
3660 if(type2 == Empty)
3661 type2 = (type1 == Pers) ? Glob : Pers;
3663 if((type1 == Pers || type2 == Pers) && vars[V_ADDRESSBOOK].is_fixed){
3664 if(msg)
3665 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book configuration"));
3667 return(retval);
3670 if((type1 == Glob || type2 == Glob) && vars[V_GLOB_ADDRBOOK].is_fixed){
3671 if(msg)
3672 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3674 return(retval);
3678 * There are two cases. If the shuffle is two address books within the
3679 * same variable, then they just swap places. If it is a shuffle of an
3680 * addrbook from one side of the boundary to the other, just that one
3681 * is moved.
3683 if((type1 == Glob && type2 == Glob) ||
3684 (type1 == Pers && type2 == Pers)){
3685 int how_many_in_list, varnum;
3686 int anum1_rel, anum2_rel; /* position in specific list */
3687 char **list, **new_list;
3688 PerAddrBook tmppab;
3690 *slide = (anum1 < anum2) ? LINES_PER_ABOOK : -1 * LINES_PER_ABOOK;
3692 if(type1 == Pers){
3693 how_many_in_list = as.how_many_personals;
3694 list = VAR_ADDRESSBOOK;
3695 varnum = V_ADDRESSBOOK;
3696 anum1_rel = anum1;
3697 anum2_rel = anum2;
3699 else{
3700 how_many_in_list = as.n_addrbk - as.how_many_personals;
3701 list = VAR_GLOB_ADDRBOOK;
3702 varnum = V_GLOB_ADDRBOOK;
3703 anum1_rel = anum1 - as.how_many_personals;
3704 anum2_rel = anum2 - as.how_many_personals;
3707 /* allocate for new list, same size as old list */
3708 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
3710 /* fill in new_list */
3711 for(i = 0; i < how_many_in_list; i++){
3712 /* swap anum1 and anum2 */
3713 if(i == anum1_rel)
3714 j = anum2_rel;
3715 else if(i == anum2_rel)
3716 j = anum1_rel;
3717 else
3718 j = i;
3720 new_list[i] = cpystr(list[j]);
3723 new_list[i] = NULL;
3725 if(set_variable_list(varnum, new_list, TRUE, ew)){
3726 if(msg)
3727 *msg = cpystr(cancel_msg);
3729 /* restore old values */
3730 set_current_val(&vars[varnum], TRUE, FALSE);
3731 free_list_array(&new_list);
3732 return(retval);
3735 retval = 0;
3736 set_current_val(&vars[varnum], TRUE, FALSE);
3737 free_list_array(&new_list);
3739 /* Swap PerAddrBook structs */
3740 tmppab = as.adrbks[anum1];
3741 as.adrbks[anum1] = as.adrbks[anum2];
3742 as.adrbks[anum2] = tmppab;
3744 else if((type1 == Pers && type2 == Glob) ||
3745 (type1 == Glob && type2 == Pers)){
3746 int how_many_in_srclist, how_many_in_dstlist;
3747 int srcvarnum, dstvarnum, srcanum;
3748 int cnt, warn_about_revert = 0;
3749 char **t;
3750 char **new_src, **new_dst, **srclist, **dstlist;
3751 char tmp[200];
3752 enum {NotSet, Modify, RevertToDefault, OverRideDefault} modify_config;
3755 * how_many_in_srclist = # in orig src list (Pers or Glob list).
3756 * how_many_in_dstlist = # in orig dst list
3757 * srcanum = # of highlighted addrbook that is being shuffled
3759 if(type1 == Pers){
3760 how_many_in_srclist = as.how_many_personals;
3761 how_many_in_dstlist = as.n_addrbk - as.how_many_personals;
3762 srclist = VAR_ADDRESSBOOK;
3763 dstlist = VAR_GLOB_ADDRBOOK;
3764 srcvarnum = V_ADDRESSBOOK;
3765 dstvarnum = V_GLOB_ADDRBOOK;
3766 srcanum = as.how_many_personals - 1;
3767 *slide = (how_many_in_srclist == 1)
3768 ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
3769 : XTRA_LINES_BETWEEN;
3771 else{
3772 how_many_in_srclist = as.n_addrbk - as.how_many_personals;
3773 how_many_in_dstlist = as.how_many_personals;
3774 srclist = VAR_GLOB_ADDRBOOK;
3775 dstlist = VAR_ADDRESSBOOK;
3776 srcvarnum = V_GLOB_ADDRBOOK;
3777 dstvarnum = V_ADDRESSBOOK;
3778 srcanum = as.how_many_personals;
3779 *slide = (how_many_in_dstlist == 0)
3780 ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
3781 : XTRA_LINES_BETWEEN;
3782 *slide = -1 * (*slide);
3786 modify_config = Modify;
3787 if(how_many_in_srclist == 1){
3789 * Deal with reverting to default values of the address book
3790 * variables, or with user deleting a default value.
3792 modify_config = NotSet;
3795 * Count how many address books are in the user's config.
3796 * This has to be one or zero, because how_many_in_srclist == 1.
3798 cnt = 0;
3799 lval = LVAL(&vars[srcvarnum], ew);
3800 if(lval && lval[0])
3801 for(t = lval; *t != NULL; t++)
3802 cnt++;
3805 * We'll revert to the default if it exists, and warn
3806 * the user about that.
3808 if(cnt == 1){
3809 modify_config = RevertToDefault;
3810 /* see if there's a default to revert to */
3811 cnt = 0;
3812 if(vars[srcvarnum].global_val.l &&
3813 vars[srcvarnum].global_val.l[0])
3814 for(t = vars[srcvarnum].global_val.l; *t != NULL; t++)
3815 cnt++;
3817 warn_about_revert = cnt;
3818 if(warn_about_revert > 1 && type1 == Pers)
3819 *slide = LINES_PER_ABOOK * warn_about_revert +
3820 XTRA_LINES_BETWEEN;
3823 * User is already using the default.
3825 else if(cnt == 0){
3826 modify_config = OverRideDefault;
3831 * We're adding one to the dstlist, so need how_many + 1 + 1.
3833 new_dst = (char **)fs_get((how_many_in_dstlist + 2) * sizeof(char *));
3834 j = 0;
3837 * Because the Personal list comes before the Global list, when
3838 * we move to Global we're inserting a new first element into
3839 * the global list (the dstlist).
3841 * When we move from Global to Personal, we're appending a new
3842 * last element onto the personal list (the dstlist).
3844 if(type2 == Glob)
3845 new_dst[j++] = cpystr(srclist[how_many_in_srclist-1]);
3847 for(i = 0; i < how_many_in_dstlist; i++)
3848 new_dst[j++] = cpystr(dstlist[i]);
3850 if(type2 == Pers)
3851 new_dst[j++] = cpystr(srclist[0]);
3853 new_dst[j] = NULL;
3856 * The srclist is complicated by the reverting to default
3857 * behaviors.
3859 if(modify_config == Modify){
3861 * In this case we're just removing one from the srclist
3862 * so the new_src is of size how_many -1 +1.
3864 new_src = (char **)fs_get((how_many_in_srclist) * sizeof(char *));
3865 j = 0;
3867 for(i = 0; i < how_many_in_srclist-1; i++)
3868 new_src[j++] = cpystr(srclist[i + ((type1 == Glob) ? 1 : 0)]);
3870 new_src[j] = NULL;
3872 else if(modify_config == OverRideDefault){
3874 * We were using default and will now revert to nothing.
3876 new_src = (char **)fs_get(2 * sizeof(char *));
3877 new_src[0] = cpystr("");
3878 new_src[1] = NULL;
3880 else if(modify_config == RevertToDefault){
3882 * We are moving our last user variable out and reverting
3883 * to the default value for this variable.
3885 new_src = NULL;
3888 if(set_variable_list(dstvarnum, new_dst, TRUE, ew) ||
3889 set_variable_list(srcvarnum, new_src, TRUE, ew)){
3890 if(msg)
3891 *msg = cpystr(cancel_msg);
3893 /* restore old values */
3894 set_current_val(&vars[dstvarnum], TRUE, FALSE);
3895 set_current_val(&vars[srcvarnum], TRUE, FALSE);
3896 free_list_array(&new_dst);
3897 free_list_array(&new_src);
3898 return(retval);
3901 set_current_val(&vars[dstvarnum], TRUE, FALSE);
3902 set_current_val(&vars[srcvarnum], TRUE, FALSE);
3903 free_list_array(&new_dst);
3904 free_list_array(&new_src);
3906 retval = (type1 == Pers && warn_about_revert)
3907 ? (warn_about_revert + 1) : (srcanum + 1);
3910 * This is a tough case. We're adding one or more new address books
3911 * in this case so we need to reset the addrbooks and start over.
3912 * We return the number of the address book we just moved after the
3913 * reset so that the caller can focus attention on the moved one.
3914 * Actually, we return 1+the number so that we can tell it apart
3915 * from a return of zero, which just means everything is ok.
3917 if(warn_about_revert){
3918 snprintf(tmp, sizeof(tmp),
3919 "This address book now %s, reverting to default %s address %s",
3920 (type1 == Glob) ? "Personal" : "Global",
3921 (type1 == Glob) ? "Global" : "Personal",
3922 warn_about_revert > 1 ? "books" : "book");
3923 tmp[sizeof(tmp)-1] = '\0';
3924 if(msg)
3925 *msg = cpystr(tmp);
3927 else{
3929 * Modify PerAddrBook struct and adjust boundary.
3930 * In this case we aren't swapping two addrbooks, but just modifying
3931 * one from being global to personal or the reverse. It will
3932 * still be the same element in the as.adrbks array.
3934 pab = &as.adrbks[srcanum];
3935 if(type2 == Glob){
3936 as.how_many_personals--;
3937 pab->type |= GLOBAL;
3938 if(pab->access != NoAccess)
3939 pab->access = ReadOnly;
3941 else{
3942 as.how_many_personals++;
3943 pab->type &= ~GLOBAL;
3944 if(pab->access != NoAccess && pab->access != MaybeRorW)
3945 pab->access = ReadWrite;
3948 snprintf(tmp, sizeof(tmp),
3949 "This address book now %s",
3950 (type1 == Glob) ? "Personal" : "Global");
3951 tmp[sizeof(tmp)-1] = '\0';
3952 if(msg)
3953 *msg = cpystr(tmp);
3957 return(retval);
3962 ab_compose_to_addr(long int cur_line, int agg, int allow_role)
3964 AddrScrn_Disp *dl;
3965 AdrBk_Entry *abe;
3966 SAVE_STATE_S state;
3967 BuildTo bldto;
3969 dprint((2, "- ab_compose_to_addr -\n"));
3971 save_state(&state);
3973 bldto.type = Str;
3974 bldto.arg.str = NULL;
3976 if(agg){
3977 int i;
3978 size_t incr = 100, avail, alloced;
3979 char *to = NULL;
3981 to = (char *)fs_get(incr);
3982 *to = '\0';
3983 avail = incr;
3984 alloced = incr;
3987 * Run through all of the selected entries
3988 * in all of the address books.
3989 * Put the nicknames together into one long
3990 * string with comma separators.
3992 for(i = 0; i < as.n_addrbk; i++){
3993 adrbk_cntr_t num;
3994 PerAddrBook *pab;
3995 EXPANDED_S *next_one;
3997 pab = &as.adrbks[i];
3998 if(pab->address_book)
3999 next_one = pab->address_book->selects;
4000 else
4001 continue;
4003 while((num = entry_get_next(&next_one)) != NO_NEXT){
4004 char *a_string;
4005 AddrScrn_Disp fake_dl;
4007 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4010 * Since we're picking up address book entries
4011 * directly from the address books and have
4012 * no knowledge of the display lines they came
4013 * from, we don't know the dl's that go with
4014 * them. We need to pass a dl to abe_to_nick
4015 * but it really is only going to use the
4016 * type in this case.
4018 dl = &fake_dl;
4019 dl->type = (abe->tag == Single) ? Simple : ListHead;
4020 a_string = abe_to_nick_or_addr_string(abe, dl, i);
4022 while(abe && avail < (size_t)strlen(a_string)+1){
4023 alloced += incr;
4024 avail += incr;
4025 fs_resize((void **)&to, alloced);
4028 if(!*to){
4029 strncpy(to, a_string, alloced);
4030 to[alloced-1] = '\0';
4032 else{
4033 strncat(to, ",", alloced-strlen(to)-1);
4034 to[alloced-1] = '\0';
4035 strncat(to, a_string, alloced-strlen(to)-1);
4036 to[alloced-1] = '\0';
4039 avail -= (strlen(a_string) + 1);
4040 fs_give((void **)&a_string);
4044 bldto.type = Str;
4045 bldto.arg.str = to;
4047 else{
4048 if(is_addr(cur_line)){
4050 dl = dlist(cur_line);
4051 abe = ae(cur_line);
4053 if(dl->type == ListEnt){
4054 bldto.type = Str;
4055 bldto.arg.str = cpystr(listmem(cur_line));
4057 else{
4058 bldto.type = Abe;
4059 bldto.arg.abe = abe;
4064 if(bldto.type == Str && bldto.arg.str == NULL)
4065 bldto.arg.str = cpystr("");
4067 ab_compose_internal(bldto, allow_role);
4069 restore_state(&state);
4071 if(bldto.type == Str && bldto.arg.str)
4072 fs_give((void **)&bldto.arg.str);
4075 * Window size may have changed in composer.
4076 * Pine_send will have reset the window size correctly,
4077 * but we still have to reset our address book data structures.
4079 ab_resize();
4080 ps_global->mangled_screen = 1;
4081 return(1);
4086 * Used by the two compose routines.
4088 void
4089 ab_compose_internal(BuildTo bldto, int allow_role)
4091 int good_addr;
4092 char *addr, *fcc, *error = NULL;
4093 ACTION_S *role = NULL;
4094 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
4095 (*redraw)(void) = ps_global->redrawer;
4097 if(allow_role)
4098 ps_global->redrawer = NULL;
4100 ps_global->next_screen = SCREEN_FUN_NULL;
4102 fcc = NULL;
4103 addr = NULL;
4105 good_addr = (our_build_address(bldto, &addr, &error, &fcc, NULL) >= 0);
4107 if(error){
4108 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4109 fs_give((void **)&error);
4112 if(!good_addr && addr && *addr)
4113 fs_give((void **)&addr); /* relying on fs_give setting addr to NULL */
4115 if(allow_role){
4116 /* Setup role */
4117 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
4118 cmd_cancelled("Composition");
4119 ps_global->next_screen = prev_screen;
4120 ps_global->redrawer = redraw;
4121 return;
4125 * If default role was selected (NULL) we need to make up a role which
4126 * won't do anything, but will cause compose_mail to think there's
4127 * already a role so that it won't try to confirm the default.
4129 if(role)
4130 role = copy_action(role);
4131 else{
4132 role = (ACTION_S *)fs_get(sizeof(*role));
4133 memset((void *)role, 0, sizeof(*role));
4134 role->nick = cpystr("Default Role");
4138 compose_mail(addr, fcc, role, NULL, NULL);
4140 if(addr)
4141 fs_give((void **)&addr);
4143 if(fcc)
4144 fs_give((void **)&fcc);
4149 * Export addresses into a file.
4151 * Args: cur_line -- The current line position (in global display list)
4152 * of cursor
4153 * command_line -- The screen line on which to prompt
4155 * Returns -- 1 if the export is done
4156 * 0 if not
4159 ab_export(struct pine *ps, long int cur_line, int command_line, int agg)
4161 int ret = 0, i, retflags = GER_NONE;
4162 int r, orig_errno = 0, failure = 0;
4163 struct variable *vars = ps->vars;
4164 char filename[MAXPATH+1], full_filename[MAXPATH+1];
4165 STORE_S *store;
4166 gf_o_t pc;
4167 long start_of_append;
4168 char *addr = NULL, *error = NULL;
4169 BuildTo bldto;
4170 char *p;
4171 int good_addr, plur, vcard = 0, tab = 0;
4172 static HISTORY_S *history = NULL;
4173 AdrBk_Entry *abe;
4174 VCARD_INFO_S *vinfo;
4175 static ESCKEY_S ab_export_opts[] = {
4176 {ctrl('T'), 10, "^T", N_("To Files")},
4177 {-1, 0, NULL, NULL},
4178 {-1, 0, NULL, NULL}};
4179 static ESCKEY_S vcard_or_addresses[] = {
4180 {'a', 'a', "A", N_("Address List")},
4181 {'v', 'v', "V", N_("VCard")},
4182 /* TRANSLATORS: TabSep is a command key label meaning Tab Separated List */
4183 {'t', 't', "T", N_("TabSep")},
4184 {-1, 0, NULL, NULL}};
4187 dprint((2, "- ab_export -\n"));
4189 if(ps->restricted){
4190 q_status_message(SM_ORDER, 0, 3,
4191 "Alpine demo can't export addresses to files");
4192 return(ret);
4195 while(1){
4196 i = radio_buttons(_("Export list of addresses, vCard format, or Tab Separated ? "),
4197 command_line, vcard_or_addresses, 'a', 'x',
4198 NO_HELP, RB_NORM|RB_RET_HELP);
4199 if(i == 3){
4200 /* TRANSLATORS: a screen title */
4201 helper(h_ab_export_vcard, _("HELP FOR EXPORT FORMAT"),
4202 HLPD_SIMPLE);
4203 ps_global->mangled_screen = 1;
4205 else
4206 break;
4209 switch(i){
4210 case 'x':
4211 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
4212 return(ret);
4214 case 'a':
4215 break;
4217 case 'v':
4218 vcard++;
4219 break;
4221 case 't':
4222 tab++;
4223 break;
4225 default:
4226 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_export");
4227 return(ret);
4230 if(agg)
4231 plur = 1;
4232 else{
4233 abe = ae(cur_line);
4234 plur = (abe && abe->tag == List);
4237 filename[0] = '\0';
4238 r = 0;
4240 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
4241 ab_export_opts[++r].ch = ctrl('I');
4242 ab_export_opts[r].rval = 11;
4243 ab_export_opts[r].name = "TAB";
4244 ab_export_opts[r].label = N_("Complete");
4247 ab_export_opts[++r].ch = -1;
4249 r = get_export_filename(ps, filename, NULL, full_filename, sizeof(filename),
4250 plur ? _("addresses") : _("address"),
4251 _("EXPORT"), ab_export_opts,
4252 &retflags, command_line, GE_IS_EXPORT, &history);
4254 if(r < 0){
4255 switch(r){
4256 case -1:
4257 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
4258 break;
4260 case -2:
4261 q_status_message1(SM_ORDER, 0, 2,
4262 _("Can't export to file outside of %s"), VAR_OPER_DIR);
4263 break;
4266 goto fini;
4269 dprint((5, "Opening file \"%s\" for export\n",
4270 full_filename ? full_filename : "?"));
4272 if(!(store = so_get(FileStar, full_filename, WRITE_ACCESS|WRITE_TO_LOCALE))){
4273 q_status_message2(SM_ORDER | SM_DING, 3, 4,
4274 _("Error opening file \"%s\" for address export: %s"),
4275 full_filename, error_description(errno));
4276 goto fini;
4280 * The write_single_vcard_entry function wants a pc.
4282 if(vcard || tab)
4283 gf_set_so_writec(&pc, store);
4285 start_of_append = so_tell(store);
4287 if(agg){
4288 for(i = 0; !failure && i < as.n_addrbk; i++){
4289 adrbk_cntr_t num;
4290 PerAddrBook *pab;
4291 EXPANDED_S *next_one;
4293 pab = &as.adrbks[i];
4294 if(pab->address_book)
4295 next_one = pab->address_book->selects;
4296 else
4297 continue;
4299 while(!failure && (num = entry_get_next(&next_one)) != NO_NEXT){
4301 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4302 if((vcard || tab) && abe){
4304 * There is no place to store the charset information
4305 * so we don't ask for it.
4307 if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
4308 failure++;
4309 else{
4310 if(vcard)
4311 write_single_vcard_entry(ps, pc, vinfo);
4312 else
4313 write_single_tab_entry(pc, vinfo);
4315 free_vcard_info(&vinfo);
4318 else if(abe){
4319 bldto.type = Abe;
4320 bldto.arg.abe = abe;
4321 error = NULL;
4322 addr = NULL;
4323 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4325 if(error){
4326 q_status_message1(SM_ORDER, 0, 4, "%s", error);
4327 fs_give((void **)&error);
4330 /* rfc1522_decode the addr */
4331 if(addr){
4332 size_t len;
4334 len = 4*strlen(addr)+1;
4335 p = (char *)fs_get(len * sizeof(char));
4336 if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
4337 fs_give((void **)&addr);
4338 addr = p;
4340 else
4341 fs_give((void **)&p);
4344 if(good_addr){
4345 int quoted = 0;
4348 * Change the unquoted commas into newlines.
4349 * Not worth it to do complicated quoting,
4350 * just consider double quotes.
4352 for(p = addr; *p; p++){
4353 if(*p == '"')
4354 quoted = !quoted;
4355 else if(!quoted && *p == ','){
4356 *p++ = '\n';
4357 removing_leading_white_space(p);
4358 p--;
4362 if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
4363 orig_errno = errno;
4364 failure = 1;
4368 if(addr)
4369 fs_give((void **)&addr);
4374 else{
4375 AddrScrn_Disp *dl;
4377 dl = dlist(cur_line);
4378 abe = ae(cur_line);
4379 if((vcard || tab) && abe){
4380 if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
4381 failure++;
4382 else{
4383 if(vcard)
4384 write_single_vcard_entry(ps, pc, vinfo);
4385 else
4386 write_single_tab_entry(pc, vinfo);
4388 free_vcard_info(&vinfo);
4391 else{
4393 if(dl->type == ListHead && listmem_count_from_abe(abe) == 0){
4394 error = _("List is empty, nothing to export!");
4395 good_addr = 0;
4397 else if(dl->type == ListEnt){
4398 bldto.type = Str;
4399 bldto.arg.str = listmem(cur_line);
4400 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4402 else{
4403 bldto.type = Abe;
4404 bldto.arg.abe = abe;
4405 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4408 if(error){
4409 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4410 fs_give((void **)&error);
4413 /* Have to rfc1522_decode the addr */
4414 if(addr){
4415 size_t len;
4416 len = 4*strlen(addr)+1;
4417 p = (char *)fs_get(len * sizeof(char));
4418 if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
4419 fs_give((void **)&addr);
4420 addr = p;
4422 else
4423 fs_give((void **)&p);
4426 if(good_addr){
4427 int quoted = 0;
4430 * Change the unquoted commas into newlines.
4431 * Not worth it to do complicated quoting,
4432 * just consider double quotes.
4434 for(p = addr; *p; p++){
4435 if(*p == '"')
4436 quoted = !quoted;
4437 else if(!quoted && *p == ','){
4438 *p++ = '\n';
4439 removing_leading_white_space(p);
4440 p--;
4444 if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
4445 orig_errno = errno;
4446 failure = 1;
4450 if(addr)
4451 fs_give((void **)&addr);
4455 if(vcard || tab)
4456 gf_clear_so_writec(store);
4458 if(so_give(&store)) /* release storage */
4459 failure++;
4461 if(failure){
4462 our_truncate(full_filename, (off_t)start_of_append);
4463 dprint((1, "FAILED Export: file \"%s\" : %s\n",
4464 full_filename ? full_filename : "?",
4465 error_description(orig_errno)));
4466 q_status_message2(SM_ORDER | SM_DING, 3, 4,
4467 _("Error exporting to \"%s\" : %s"),
4468 filename, error_description(orig_errno));
4470 else{
4471 ret = 1;
4472 q_status_message3(SM_ORDER,0,3,
4473 "%s %s to file \"%s\"",
4474 (vcard || tab) ? (agg ? "Entries" : "Entry")
4475 : (plur ? "Addresses" : "Address"),
4476 retflags & GER_OVER
4477 ? "overwritten"
4478 : retflags & GER_APPEND ? "appended" : "exported",
4479 filename);
4482 fini:
4483 ps->mangled_footer = 1;
4484 return(ret);
4489 * Forward an address book entry or entries via email attachment.
4491 * We use the vCard standard to send entries. We group multiple entries
4492 * using the BEGIN/END construct of vCard, not with multiple MIME parts.
4493 * A limitation of vCard is that there can be only one charset for the
4494 * whole group we send, so we might lose that information.
4496 * Args: cur_line -- The current line position (in global display list)
4497 * of cursor
4498 * command_line -- The screen line on which to prompt
4501 ab_forward(struct pine *ps, long int cur_line, int agg)
4503 AddrScrn_Disp *dl;
4504 AdrBk_Entry *abe = NULL;
4505 ENVELOPE *outgoing = NULL;
4506 BODY *pb, *body = NULL;
4507 PART **pp;
4508 char *sig;
4509 gf_o_t pc;
4510 int i, ret = 0;
4511 VCARD_INFO_S *vinfo;
4512 ACTION_S *role = NULL;
4514 dprint((2, "- ab_forward -\n"));
4516 if(!agg){
4517 dl = dlist(cur_line);
4518 if(dl->type != ListHead && dl->type != Simple)
4519 return(ret);
4521 abe = ae(cur_line);
4522 if(!abe){
4523 q_status_message(SM_ORDER, 3, 3, _("Trouble accessing current entry"));
4524 return(ret);
4528 outgoing = mail_newenvelope();
4529 if(agg && as.selections > 1)
4530 outgoing->subject = cpystr("Forwarded address book entries from Alpine");
4531 else
4532 outgoing->subject = cpystr("Forwarded address book entry from Alpine");
4534 body = mail_newbody();
4535 body->type = TYPEMULTIPART;
4536 /*---- The TEXT part/body ----*/
4537 body->nested.part = mail_newbody_part();
4538 body->nested.part->body.type = TYPETEXT;
4539 /*--- Allocate an object for the body ---*/
4540 if((body->nested.part->body.contents.text.data =
4541 (void *)so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
4542 int did_sig = 0;
4543 long rflags = ROLE_COMPOSE;
4544 PAT_STATE dummy;
4546 pp = &(body->nested.part->next);
4548 if(nonempty_patterns(rflags, &dummy)){
4550 * This is really more like Compose, even though it
4551 * is called Forward.
4553 if(confirm_role(rflags, &role))
4554 role = combine_inherited_role(role);
4555 else{ /* cancel */
4556 role = NULL;
4557 cmd_cancelled("Composition");
4558 goto bomb;
4562 if(role)
4563 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
4564 role->nick);
4566 if((sig = detoken(role, NULL, 2, 0, 1, NULL, NULL)) != NULL){
4567 if(*sig){
4568 so_puts((STORE_S *)body->nested.part->body.contents.text.data,
4569 sig);
4570 did_sig++;
4573 fs_give((void **)&sig);
4576 /* so we don't have an empty part */
4577 if(!did_sig)
4578 so_puts((STORE_S *)body->nested.part->body.contents.text.data, "\n");
4580 else{
4581 q_status_message(SM_ORDER | SM_DING, 3, 4,
4582 _("Problem creating space for message text"));
4583 goto bomb;
4586 outgoing->message_id = generate_message_id(role);
4588 /*---- create the attachment, and write abook entry into it ----*/
4589 *pp = mail_newbody_part();
4590 pb = &((*pp)->body);
4591 pb->type = TYPETEXT;
4592 pb->encoding = ENCOTHER; /* let data decide */
4593 pb->id = generate_message_id(role);
4594 pb->subtype = cpystr("DIRECTORY");
4595 if(agg && as.selections > 1)
4596 pb->description = cpystr("Alpine addressbook entries");
4597 else
4598 pb->description = cpystr("Alpine addressbook entry");
4600 pb->parameter = NULL;
4601 set_parameter(&pb->parameter, "profile", "vCard");
4603 if((pb->contents.text.data = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
4604 int are_some_unqualified = 0, expand_nicks = 0;
4605 adrbk_cntr_t num;
4606 PerAddrBook *pab;
4607 EXPANDED_S *next_one;
4609 gf_set_so_writec(&pc, (STORE_S *) pb->contents.text.data);
4611 if(agg){
4612 for(i = 0; i < as.n_addrbk && !are_some_unqualified; i++){
4614 pab = &as.adrbks[i];
4615 if(pab->address_book)
4616 next_one = pab->address_book->selects;
4617 else
4618 continue;
4620 while((num = entry_get_next(&next_one)) != NO_NEXT &&
4621 !are_some_unqualified){
4623 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4624 if(abe->tag == Single){
4625 if(abe->addr.addr && abe->addr.addr[0]
4626 && !strindex(abe->addr.addr, '@'))
4627 are_some_unqualified++;
4629 else{
4630 char **ll;
4632 for(ll = abe->addr.list; ll && *ll; ll++){
4633 if(!strindex(*ll, '@')){
4634 are_some_unqualified++;
4635 break;
4642 else{
4644 * Search through the addresses to see if there are any
4645 * that are unqualified, and so would be different if
4646 * expanded.
4648 if(abe->tag == Single){
4649 if(abe->addr.addr && abe->addr.addr[0]
4650 && !strindex(abe->addr.addr, '@'))
4651 are_some_unqualified++;
4653 else{
4654 char **ll;
4656 for(ll = abe->addr.list; ll && *ll; ll++){
4657 if(!strindex(*ll, '@')){
4658 are_some_unqualified++;
4659 break;
4665 if(are_some_unqualified){
4666 switch(want_to(_("Expand nicknames"), 'y', 'x', h_ab_forward,WT_NORM)){
4667 case 'x':
4668 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4669 q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
4670 goto bomb;
4672 case 'y':
4673 expand_nicks = 1;
4674 break;
4676 case 'n':
4677 expand_nicks = 0;
4678 break;
4681 ps->mangled_footer = 1;
4684 if(agg){
4685 for(i = 0; i < as.n_addrbk; i++){
4687 pab = &as.adrbks[i];
4688 if(pab->address_book)
4689 next_one = pab->address_book->selects;
4690 else
4691 continue;
4693 while((num = entry_get_next(&next_one)) != NO_NEXT){
4695 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4696 if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
4697 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4698 goto bomb;
4700 else{
4701 write_single_vcard_entry(ps, pc, vinfo);
4702 free_vcard_info(&vinfo);
4707 else{
4708 if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
4709 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4710 goto bomb;
4712 else{
4713 write_single_vcard_entry(ps, pc, vinfo);
4714 free_vcard_info(&vinfo);
4718 /* This sets parameter charset, if necessary, and encoding */
4719 set_mime_type_by_grope(pb);
4720 set_charset_possibly_to_ascii(pb, "UTF-8");
4721 pb->size.bytes =
4722 strlen((char *)so_text((STORE_S *)pb->contents.text.data));
4724 else{
4725 q_status_message(SM_ORDER | SM_DING, 3, 4,
4726 _("Problem creating space for message text"));
4727 goto bomb;
4730 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4732 pine_send(outgoing, &body, _("FORWARDING ADDRESS BOOK ENTRY"), role, NULL,
4733 NULL, NULL, NULL, NULL, 0);
4735 ps->mangled_screen = 1;
4736 ret = 1;
4738 bomb:
4739 if(outgoing)
4740 mail_free_envelope(&outgoing);
4742 if(body)
4743 pine_free_body(&body);
4745 free_action(&role);
4746 return(ret);
4751 * Given an Adrbk_Entry fill in some of the fields in a VCARD_INFO_S
4752 * for use by write_single_vcard_entry. The returned structure is freed
4753 * by the caller.
4755 VCARD_INFO_S *
4756 prepare_abe_for_vcard(struct pine *ps, AdrBk_Entry *abe, int expand_nicks)
4758 VCARD_INFO_S *vinfo = NULL;
4759 char *init_addr = NULL, *addr = NULL, *astring;
4760 int cnt;
4761 ADDRESS *adrlist = NULL;
4763 if(!abe)
4764 return(vinfo);
4766 vinfo = (VCARD_INFO_S *) fs_get(sizeof(*vinfo));
4767 memset((void *) vinfo, 0, sizeof(*vinfo));
4769 if(abe->nickname && abe->nickname[0]){
4770 vinfo->nickname = (char **) fs_get((1+1) * sizeof(char *));
4771 vinfo->nickname[0] = cpystr(abe->nickname);
4772 vinfo->nickname[1] = NULL;
4775 if(abe->fcc && abe->fcc[0]){
4776 vinfo->fcc = (char **) fs_get((1+1) * sizeof(char *));
4777 vinfo->fcc[0] = cpystr(abe->fcc);
4778 vinfo->fcc[1] = NULL;
4781 if(abe->extra && abe->extra[0]){
4782 vinfo->note = (char **) fs_get((1+1) * sizeof(char *));
4783 vinfo->note[0] = cpystr(abe->extra);
4784 vinfo->note[1] = NULL;
4787 if(abe->fullname && abe->fullname[0]){
4788 char *fn, *last = NULL, *middle = NULL, *first = NULL;
4790 fn = adrbk_formatname(abe->fullname, &first, &last);
4791 if(fn){
4792 if(*fn){
4793 vinfo->fullname = (char **)fs_get((1+1) * sizeof(char *));
4794 vinfo->fullname[0] = fn;
4795 vinfo->fullname[1] = NULL;
4797 else
4798 fs_give((void **)&fn);
4801 if(last && *last){
4802 if(first && (middle=strindex(first, ' '))){
4803 *middle++ = '\0';
4804 middle = skip_white_space(middle);
4807 vinfo->last = last;
4808 vinfo->first = first;
4809 vinfo->middle = middle ? cpystr(middle) : NULL;
4810 first = NULL;
4811 last = NULL;
4814 if(last)
4815 fs_give((void **)&last);
4816 if(first)
4817 fs_give((void **)&first);
4820 /* expand nicknames and fully-qualify unqualified names */
4821 if(expand_nicks){
4822 char *error = NULL;
4823 BuildTo bldto;
4824 ADDRESS *a;
4826 if(abe->tag == Single)
4827 init_addr = cpystr(abe->addr.addr);
4828 else{
4829 char **ll;
4830 char *p;
4831 long length;
4833 /* figure out how large a string we need to allocate */
4834 length = 0L;
4835 for(ll = abe->addr.list; ll && *ll; ll++)
4836 length += (strlen(*ll) + 2);
4838 if(length)
4839 length -= 2L;
4841 init_addr = (char *)fs_get((size_t)(length+1L) * sizeof(char));
4842 p = init_addr;
4844 for(ll = abe->addr.list; ll && *ll; ll++){
4845 sstrncpy(&p, *ll, length-(p-init_addr));
4846 if(*(ll+1))
4847 sstrncpy(&p, ", ", length-(p-init_addr));
4850 init_addr[length] = '\0';
4853 bldto.type = Str;
4854 bldto.arg.str = init_addr;
4855 our_build_address(bldto, &addr, &error, NULL, NULL);
4856 if(error){
4857 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4858 fs_give((void **)&error);
4859 free_vcard_info(&vinfo);
4860 return(NULL);
4863 if(addr)
4864 rfc822_parse_adrlist(&adrlist, addr, ps->maildomain);
4866 for(cnt = 0, a = adrlist; a; a = a->next)
4867 cnt++;
4869 vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
4871 for(cnt = 0, a = adrlist; a; a = a->next){
4872 char *bufp;
4873 ADDRESS *next_addr;
4874 size_t len;
4876 next_addr = a->next;
4877 a->next = NULL;
4878 len = est_size(a);
4879 bufp = (char *) fs_get(len * sizeof(char));
4880 astring = addr_string(a, bufp, len);
4881 a->next = next_addr;
4882 vinfo->email[cnt++] = cpystr(astring ? astring : "");
4883 fs_give((void **)&bufp);
4886 vinfo->email[cnt] = NULL;
4888 else{ /* don't expand or qualify */
4889 if(abe->tag == Single){
4890 astring =
4891 (abe->addr.addr && abe->addr.addr[0]) ? abe->addr.addr : "";
4892 vinfo->email = (char **)fs_get((1+1) * sizeof(char *));
4893 vinfo->email[0] = cpystr(astring);
4894 vinfo->email[1] = NULL;
4896 else{
4897 char **ll;
4899 for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
4900 cnt++;
4902 vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
4903 for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
4904 vinfo->email[cnt++] = cpystr(*ll);
4906 vinfo->email[cnt] = NULL;
4910 return(vinfo);
4914 void
4915 free_vcard_info(VCARD_INFO_S **vinfo)
4917 if(vinfo && *vinfo){
4918 if((*vinfo)->nickname)
4919 free_list_array(&(*vinfo)->nickname);
4920 if((*vinfo)->fullname)
4921 free_list_array(&(*vinfo)->fullname);
4922 if((*vinfo)->fcc)
4923 free_list_array(&(*vinfo)->fcc);
4924 if((*vinfo)->note)
4925 free_list_array(&(*vinfo)->note);
4926 if((*vinfo)->title)
4927 free_list_array(&(*vinfo)->title);
4928 if((*vinfo)->tel)
4929 free_list_array(&(*vinfo)->tel);
4930 if((*vinfo)->email)
4931 free_list_array(&(*vinfo)->email);
4933 if((*vinfo)->first)
4934 fs_give((void **)&(*vinfo)->first);
4935 if((*vinfo)->middle)
4936 fs_give((void **)&(*vinfo)->middle);
4937 if((*vinfo)->last)
4938 fs_give((void **)&(*vinfo)->last);
4940 fs_give((void **)vinfo);
4948 void
4949 write_single_vcard_entry(struct pine *ps, gf_o_t pc, VCARD_INFO_S *vinfo)
4951 char *decoded, *tmp2, *tmp = NULL, *hdr;
4952 char **ll;
4953 int i, did_fn = 0, did_n = 0;
4954 int cr;
4955 char eol[3];
4956 #define FOLD_BY 75
4958 if(!vinfo)
4959 return;
4961 #if defined(DOS) || defined(OS2)
4962 cr = 1;
4963 #else
4964 cr = 0;
4965 #endif
4967 if(cr)
4968 strncpy(eol, "\r\n", sizeof(eol));
4969 else
4970 strncpy(eol, "\n", sizeof(eol));
4972 eol[sizeof(eol)-1] = '\0';
4974 gf_puts("BEGIN:VCARD", pc);
4975 gf_puts(eol, pc);
4976 gf_puts("VERSION:3.0", pc);
4977 gf_puts(eol, pc);
4979 for(i = 0; i < 7; i++){
4980 switch(i){
4981 case 0:
4982 ll = vinfo->nickname;
4983 hdr = "NICKNAME:";
4984 break;
4986 case 1:
4987 ll = vinfo->fullname;
4988 hdr = "FN:";
4989 break;
4991 case 2:
4992 ll = vinfo->email;
4993 hdr = "EMAIL:";
4994 break;
4996 case 3:
4997 ll = vinfo->title;
4998 hdr = "TITLE:";
4999 break;
5001 case 4:
5002 ll = vinfo->note;
5003 hdr = "NOTE:";
5004 break;
5006 case 5:
5007 ll = vinfo->fcc;
5008 hdr = "X-FCC:";
5009 break;
5011 case 6:
5012 ll = vinfo->tel;
5013 hdr = "TEL:";
5014 break;
5016 default:
5017 alpine_panic("can't happen in write_single_vcard_entry");
5020 for(; ll && *ll; ll++){
5021 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
5023 tmp = vcard_escape(decoded);
5024 if(tmp){
5025 if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, hdr, " ", FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5026 gf_puts(tmp2, pc);
5027 fs_give((void **)&tmp2);
5028 if(i == 1)
5029 did_fn++;
5032 fs_give((void **)&tmp);
5037 if(vinfo->last && vinfo->last[0]){
5038 char *pl, *pf, *pm;
5040 pl = vcard_escape(vinfo->last);
5041 pf = (vinfo->first && *vinfo->first) ? vcard_escape(vinfo->first)
5042 : NULL;
5043 pm = (vinfo->middle && *vinfo->middle) ? vcard_escape(vinfo->middle)
5044 : NULL;
5045 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
5046 (pl && *pl) ? pl : "",
5047 ((pf && *pf) || (pm && *pm)) ? ";" : "",
5048 (pf && *pf) ? pf : "",
5049 (pm && *pm) ? ";" : "",
5050 (pm && *pm) ? pm : "");
5052 if((tmp2 = fold(tmp_20k_buf, FOLD_BY, FOLD_BY, "N:", " ",
5053 FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5054 gf_puts(tmp2, pc);
5055 fs_give((void **)&tmp2);
5056 did_n++;
5059 if(pl)
5060 fs_give((void **)&pl);
5061 if(pf)
5062 fs_give((void **)&pf);
5063 if(pm)
5064 fs_give((void **)&pm);
5068 * These two types are required in draft-ietf-asid-mime-vcard-06, which
5069 * is April 98 and is in last call.
5071 if(!did_fn || !did_n){
5072 if(did_n){
5073 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
5074 (vinfo->first && *vinfo->first) ? vinfo->first : "",
5075 (vinfo->first && *vinfo->first &&
5076 vinfo->middle && *vinfo->middle) ? " " : "",
5077 (vinfo->middle && *vinfo->middle) ? vinfo->middle : "",
5078 (((vinfo->first && *vinfo->first) ||
5079 (vinfo->middle && *vinfo->middle)) &&
5080 vinfo->last && *vinfo->last) ? " " : "",
5081 (vinfo->last && *vinfo->last) ? vinfo->last : "");
5083 tmp = vcard_escape(tmp_20k_buf);
5084 if(tmp){
5085 if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, "FN:", " ",
5086 FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5087 gf_puts(tmp2, pc);
5088 fs_give((void **)&tmp2);
5089 did_n++;
5092 fs_give((void **)&tmp);
5095 else{
5096 if(!did_fn){
5097 gf_puts("FN:<Unknown>", pc);
5098 gf_puts(eol, pc);
5101 gf_puts("N:<Unknown>", pc);
5102 gf_puts(eol, pc);
5106 gf_puts("END:VCARD", pc);
5107 gf_puts(eol, pc);
5114 void
5115 write_single_tab_entry(gf_o_t pc, VCARD_INFO_S *vinfo)
5117 char *decoded, *tmp = NULL;
5118 char **ll;
5119 int i, first;
5120 char *eol;
5122 if(!vinfo)
5123 return;
5125 #if defined(DOS) || defined(OS2)
5126 eol = "\r\n";
5127 #else
5128 eol = "\n";
5129 #endif
5131 for(i = 0; i < 4; i++){
5132 switch(i){
5133 case 0:
5134 ll = vinfo->nickname;
5135 break;
5137 case 1:
5138 ll = vinfo->fullname;
5139 break;
5141 case 2:
5142 ll = vinfo->email;
5143 break;
5145 case 3:
5146 ll = vinfo->note;
5147 break;
5149 default:
5150 alpine_panic("can't happen in write_single_tab_entry");
5153 if(i)
5154 gf_puts("\t", pc);
5156 for(first = 1; ll && *ll; ll++){
5158 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
5159 tmp = vcard_escape(decoded);
5160 if(tmp){
5161 if(i == 2 && !first)
5162 gf_puts(",", pc);
5163 else
5164 first = 0;
5166 gf_puts(tmp, pc);
5167 fs_give((void **)&tmp);
5172 gf_puts(eol, pc);
5177 * for ab_save percent done
5179 static int total_to_copy;
5180 static int copied_so_far;
5182 percent_done_copying(void)
5184 return((copied_so_far * 100) / total_to_copy);
5188 cmp_action_list(const qsort_t *a1, const qsort_t *a2)
5190 ACTION_LIST_S *x = (ACTION_LIST_S *)a1;
5191 ACTION_LIST_S *y = (ACTION_LIST_S *)a2;
5193 if(x->pab != y->pab)
5194 return((x->pab > y->pab) ? 1 : -1); /* order doesn't matter */
5197 * The only one that matters is when both x and y have dup lit.
5198 * For the others, just need to be consistent so sort will terminate.
5200 if(x->dup){
5201 if(y->dup)
5202 return((x->num_in_dst > y->num_in_dst) ? -1
5203 : (x->num_in_dst == y->num_in_dst) ? 0 : 1);
5204 else
5205 return(-1);
5207 else if(y->dup)
5208 return(1);
5209 else
5210 return((x->num > y->num) ? -1 : (x->num == y->num) ? 0 : 1);
5215 * Copy a bunch of address book entries to a particular address book.
5217 * Args abook -- the current addrbook handle
5218 * cur_line -- the current line the cursor is on
5219 * command_line -- the line to prompt on
5220 * agg -- 1 if this is an aggregate copy
5222 * Returns 1 if successful, 0 if not
5225 ab_save(struct pine *ps, AdrBk *abook, long int cur_line, int command_line, int agg)
5227 PerAddrBook *pab_dst, *pab;
5228 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
5229 int rc, i;
5230 int how_many_dups = 0, how_many_to_copy = 0, skip_dups = 0;
5231 int how_many_no_action = 0, ret = 1;
5232 int err = 0, need_write = 0, we_cancel = 0;
5233 int act_list_size, special_case = 0;
5234 adrbk_cntr_t num, new_entry_num;
5235 char warn[2][MAX_NICKNAME+1];
5236 char warning[MAX_NICKNAME+1];
5237 char tmp[MAX(200,2*MAX_NICKNAME+80)];
5238 ACTION_LIST_S *action_list = NULL, *al;
5239 static ESCKEY_S save_or_export[] = {
5240 {'s', 's', "S", N_("Save")},
5241 {'e', 'e', "E", N_("Export")},
5242 {-1, 0, NULL, NULL}};
5244 if(!agg)
5245 snprintf(tmp, sizeof(tmp), _("Save highlighted entry to address book or Export to filesystem ? "));
5246 else if(as.selections > 1)
5247 snprintf(tmp, sizeof(tmp), _("Save selected entries to address book or Export to filesystem ? "));
5248 else if(as.selections == 1)
5249 snprintf(tmp, sizeof(tmp), _("Save selected entry to address book or Export to filesystem ? "));
5250 else
5251 snprintf(tmp, sizeof(tmp), _("Save to address book or Export to filesystem ? "));
5253 i = radio_buttons(tmp, -FOOTER_ROWS(ps), save_or_export, 's', 'x',
5254 h_ab_save_exp, RB_NORM);
5255 switch(i){
5256 case 'x':
5257 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5258 return(0);
5260 case 'e':
5261 return(ab_export(ps, cur_line, command_line, agg));
5263 case 's':
5264 break;
5266 default:
5267 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_save");
5268 return(0);
5271 pab_dst = setup_for_addrbook_add(&state, command_line, _("Save"));
5272 if(!pab_dst)
5273 goto get_out;
5275 pab = &as.adrbks[as.cur];
5277 dprint((2, "- ab_save: %s -> %s (agg=%d)-\n",
5278 pab->abnick ? pab->abnick : "?",
5279 pab_dst->abnick ? pab_dst->abnick : "?", agg));
5281 if(agg)
5282 act_list_size = as.selections;
5283 else
5284 act_list_size = 1;
5286 action_list = (ACTION_LIST_S *)fs_get((act_list_size+1) *
5287 sizeof(ACTION_LIST_S));
5288 memset((void *)action_list, 0, (act_list_size+1) * sizeof(ACTION_LIST_S));
5289 al = action_list;
5291 if(agg){
5293 for(i = 0; i < as.n_addrbk; i++){
5294 EXPANDED_S *next_one;
5296 pab = &as.adrbks[i];
5297 if(pab->address_book)
5298 next_one = pab->address_book->selects;
5299 else
5300 continue;
5302 while((num = entry_get_next(&next_one)) != NO_NEXT){
5303 if(pab != pab_dst &&
5304 pab->ostatus != Open &&
5305 pab->ostatus != NoDisplay)
5306 init_abook(pab, NoDisplay);
5308 if(pab->ostatus != Open && pab->ostatus != NoDisplay){
5309 q_status_message1(SM_ORDER, 0, 4,
5310 _("Can't re-open address book %s to save from"),
5311 pab->abnick);
5312 err++;
5313 goto get_out;
5316 set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
5317 if(al->skip)
5318 how_many_no_action++;
5319 else{
5320 if(al->dup){
5321 if(how_many_dups < 2 && warning[0]){
5322 strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
5323 warn[how_many_dups][MAX_NICKNAME] = '\0';
5326 how_many_dups++;
5329 how_many_to_copy++;
5332 al++;
5336 else{
5337 if(is_addr(cur_line)){
5338 AddrScrn_Disp *dl;
5340 dl = dlist(cur_line);
5342 if(dl->type == ListEnt)
5343 special_case++;
5345 if(pab && dl){
5346 num = dl->elnum;
5347 set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
5349 else
5350 al->skip = 1;
5352 if(al->skip)
5353 how_many_no_action++;
5354 else{
5355 if(al->dup){
5356 if(how_many_dups < 2 && warning[0]){
5357 strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
5358 warn[how_many_dups][MAX_NICKNAME] = '\0';
5361 how_many_dups++;
5364 how_many_to_copy++;
5367 else{
5368 q_status_message(SM_ORDER, 0, 4, _("No current entry to save"));
5369 goto get_out;
5373 if(how_many_to_copy == 0 && how_many_no_action == 1 && act_list_size == 1)
5374 special_case++;
5376 if(special_case){
5377 TA_STATE_S tas, *tasp;
5379 /* Not going to use the action_list now */
5380 if(action_list)
5381 fs_give((void **)&action_list);
5383 tasp = &tas;
5384 tas.state = state;
5385 tas.pab = pab_dst;
5386 take_this_one_entry(ps, &tasp, abook, cur_line);
5389 * If take_this_one_entry or its children didn't do this for
5390 * us, we do it here.
5392 if(tas.pab)
5393 restore_state(&(tas.state));
5396 * We don't have enough information to know what to return.
5398 return(0);
5401 /* nothing to do (except for special Take case below) */
5402 if(how_many_to_copy == 0){
5403 if(how_many_no_action == 0){
5404 err++;
5405 goto get_out;
5407 else{
5408 restore_state(&state);
5410 if(how_many_no_action > 1)
5411 snprintf(tmp, sizeof(tmp), _("Saved %d entries to %s"), how_many_no_action, pab_dst->abnick);
5412 else
5413 snprintf(tmp, sizeof(tmp), _("Saved %d entry to %s"), how_many_no_action, pab_dst->abnick);
5415 tmp[sizeof(tmp)-1] = '\0';
5416 q_status_message(SM_ORDER, 0, 4, tmp);
5417 if(action_list)
5418 fs_give((void **)&action_list);
5420 return(ret);
5425 * If there are some nicknames which already exist in the selected
5426 * abook, ask user what to do.
5428 if(how_many_dups > 0){
5429 if(how_many_dups == 1)
5430 snprintf(tmp, sizeof(tmp), _("Entry with nickname \"%.*s\" already exists, replace "),
5431 MAX_NICKNAME, warn[0]);
5432 else if(how_many_dups == 2)
5433 snprintf(tmp, sizeof(tmp),
5434 _("Nicknames \"%.*s\" and \"%.*s\" already exist, replace "),
5435 MAX_NICKNAME, warn[0], MAX_NICKNAME, warn[1]);
5436 else
5437 snprintf(tmp, sizeof(tmp), _("%d of the nicknames already exist, replace "),
5438 how_many_dups);
5440 tmp[sizeof(tmp)-1] = '\0';
5442 switch(want_to(tmp, 'n', 'x', h_ab_copy_dups, WT_NORM)){
5443 case 'n':
5444 skip_dups++;
5445 if(how_many_to_copy == how_many_dups){
5446 restore_state(&state);
5447 if(action_list)
5448 fs_give((void **)&action_list);
5450 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5451 return(ret);
5454 break;
5456 case 'y':
5457 break;
5459 case 'x':
5460 err++;
5461 goto get_out;
5466 * Because the deletes happen immediately we have to delete from high
5467 * entry number towards lower entry numbers so that we are deleting
5468 * the correct entries. In order to do that we'll sort the action_list
5469 * to give us a safe order.
5471 if(!skip_dups && how_many_dups > 1)
5472 qsort((qsort_t *)action_list, (size_t)as.selections, sizeof(*action_list),
5473 cmp_action_list);
5476 * Set up the busy alarm percent counters.
5478 total_to_copy = how_many_to_copy - (skip_dups ? how_many_dups : 0);
5479 copied_so_far = 0;
5480 we_cancel = busy_cue(_("Saving entries"),
5481 (total_to_copy > 4) ? percent_done_copying : NULL, 0);
5484 * Add the list of entries to the destination abook.
5486 for(al = action_list; al && al->pab; al++){
5487 AdrBk_Entry *abe;
5489 if(al->skip || (skip_dups && al->dup))
5490 continue;
5492 if(!(abe = adrbk_get_ae(al->pab->address_book, (a_c_arg_t) al->num))){
5493 q_status_message1(SM_ORDER | SM_DING, 3, 5,
5494 _("Error saving entry: %s"),
5495 error_description(errno));
5496 err++;
5497 goto get_out;
5501 * Delete existing dups and replace them.
5503 if(al->dup){
5505 /* delete the existing entry */
5506 rc = 0;
5507 if(adrbk_delete(pab_dst->address_book,
5508 (a_c_arg_t)al->num_in_dst, 1, 0, 0, 0) == 0){
5509 need_write++;
5511 else{
5512 q_status_message2(SM_ORDER | SM_DING, 3, 5,
5513 _("Error replacing entry in %s: %s"),
5514 pab_dst->abnick,
5515 error_description(errno));
5516 err++;
5517 goto get_out;
5522 * Now we have a clean slate to work with.
5523 * Add (sorted in correctly) or append abe to the destination
5524 * address book.
5526 if(total_to_copy <= 1)
5527 rc = adrbk_add(pab_dst->address_book,
5528 NO_NEXT,
5529 abe->nickname,
5530 abe->fullname,
5531 abe->tag == Single ? abe->addr.addr : NULL,
5532 abe->fcc,
5533 abe->extra,
5534 abe->tag,
5535 &new_entry_num,
5536 (int *)NULL,
5540 else
5541 rc = adrbk_append(pab_dst->address_book,
5542 abe->nickname,
5543 abe->fullname,
5544 abe->tag == Single ? abe->addr.addr : NULL,
5545 abe->fcc,
5546 abe->extra,
5547 abe->tag,
5548 &new_entry_num);
5550 if(rc == 0)
5551 need_write++;
5554 * If the entry we copied is a list, we also have to add
5555 * the list members to the copy.
5557 if(rc == 0 && abe->tag == List){
5558 int save_sort_rule;
5561 * We want it to copy the list in the exact order
5562 * without sorting it.
5564 save_sort_rule = pab_dst->address_book->sort_rule;
5565 pab_dst->address_book->sort_rule = AB_SORT_RULE_NONE;
5567 rc = adrbk_nlistadd(pab_dst->address_book,
5568 (a_c_arg_t)new_entry_num, NULL, NULL,
5569 abe->addr.list,
5570 0, 0, 0);
5572 pab_dst->address_book->sort_rule = save_sort_rule;
5575 if(rc != 0){
5576 if(abe && abe->nickname)
5577 q_status_message2(SM_ORDER | SM_DING, 3, 5, _("Error saving %s: %s"), abe->nickname, error_description(errno));
5578 else
5579 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error saving entry: %s"), error_description(errno));
5580 err++;
5581 goto get_out;
5584 copied_so_far++;
5587 if(need_write){
5588 int sort_happened = 0;
5590 if(adrbk_write(pab_dst->address_book, 0, NULL, &sort_happened, 0, 1)){
5591 err++;
5592 goto get_out;
5595 if(sort_happened)
5596 ps_global->mangled_screen = 1;
5599 get_out:
5600 if(we_cancel)
5601 cancel_busy_cue(1);
5603 restore_state(&state);
5604 if(action_list)
5605 fs_give((void **)&action_list);
5607 ps_global->mangled_footer = 1;
5609 if(err){
5610 ret = 0;
5611 if(need_write)
5612 q_status_message(SM_ORDER | SM_DING, 3, 4,
5613 _("Save only partially completed"));
5614 else
5615 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5617 else if (how_many_to_copy + how_many_no_action -
5618 (skip_dups ? how_many_dups : 0) > 0){
5620 ret = 1;
5621 snprintf(tmp, sizeof(tmp), "Saved %d %s to %s",
5622 how_many_to_copy + how_many_no_action -
5623 (skip_dups ? how_many_dups : 0),
5624 ((how_many_to_copy + how_many_no_action -
5625 (skip_dups ? how_many_dups : 0)) > 1) ? "entries" : "entry",
5626 pab_dst->abnick);
5627 tmp[sizeof(tmp)-1] = '\0';
5628 q_status_message(SM_ORDER, 0, 4, tmp);
5631 return(ret);
5636 * Warn should point to an array of size MAX_NICKNAME+1.
5638 void
5639 set_act_list_member(ACTION_LIST_S *al, a_c_arg_t numarg, PerAddrBook *pab_dst, PerAddrBook *pab, char *warn)
5641 AdrBk_Entry *abe1, *abe2;
5642 adrbk_cntr_t num;
5644 num = (adrbk_cntr_t)numarg;
5646 al->pab = pab;
5647 al->num = num;
5649 /* skip if they're copying from and to same addrbook */
5650 if(pab == pab_dst)
5651 al->skip = 1;
5652 else{
5653 abe1 = adrbk_get_ae(pab->address_book, numarg);
5654 if(abe1 && abe1->nickname && abe1->nickname[0]){
5655 adrbk_cntr_t dst_enum;
5657 abe2 = adrbk_lookup_by_nick(pab_dst->address_book,
5658 abe1->nickname, &dst_enum);
5660 * This nickname already exists in the destn address book.
5662 if(abe2){
5663 /* If it isn't different, no problem. Check it out. */
5664 if(abes_are_equal(abe1, abe2))
5665 al->skip = 1;
5666 else{
5667 strncpy(warn, abe1->nickname, MAX_NICKNAME);
5668 warn[MAX_NICKNAME] = '\0';
5669 al->dup = 1;
5670 al->num_in_dst = dst_enum;
5679 * Print out the display list.
5682 ab_print(int agg)
5684 int do_entry = 0, curopen;
5685 char *prompt;
5687 dprint((2, "- ab_print -\n"));
5689 curopen = cur_is_open();
5690 if(!agg && curopen){
5691 static ESCKEY_S prt[] = {
5692 {'a', 'a', "A", N_("AddressBook")},
5693 {'e', 'e', "E", N_("Entry")},
5694 {-1, 0, NULL, NULL}};
5696 prompt = _("Print Address Book or just this Entry? ");
5697 switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global), prt, 'a', 'x',
5698 NO_HELP, RB_NORM)){
5699 case 'x' :
5700 q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
5701 ps_global->mangled_footer = 1;
5702 return 0;
5704 case 'e':
5705 do_entry = 1;
5706 break;
5708 default:
5709 case 'a':
5710 break;
5714 /* TRANSLATORS: This is input for
5715 Print something1 using something2. The thing we're
5716 defining here is something1. */
5717 if(agg)
5718 prompt = _("selected entries");
5719 else{
5720 if(!curopen)
5721 prompt = _("address book list");
5722 else if(do_entry)
5723 prompt = _("entry");
5724 else
5725 prompt = _("address book");
5728 if(open_printer(prompt) == 0){
5729 DL_CACHE_S dlc_buf, *match_dlc;
5730 AddrScrn_Disp *dl;
5731 AdrBk_Entry *abe;
5732 long save_line;
5733 char *addr;
5734 char spaces[100];
5735 char more_spaces[100];
5736 char b[500];
5737 int abook_indent;
5739 save_line = as.top_ent + as.cur_row;
5740 match_dlc = get_dlc(save_line);
5741 dlc_buf = *match_dlc;
5742 match_dlc = &dlc_buf;
5744 if(do_entry){ /* print an individual addrbook entry */
5746 abook_indent = utf8_width(_("Nickname")) + 2;
5748 snprintf(spaces, sizeof(spaces), "%*.*s", abook_indent+2, abook_indent+2, "");
5749 snprintf(more_spaces, sizeof(more_spaces), "%*.*s",
5750 abook_indent+4, abook_indent+4, "");
5752 dl = dlist(save_line);
5753 abe = ae(save_line);
5755 if(abe){
5756 int are_some_unqualified = 0, expand_nicks = 0;
5757 char *string, *tmp;
5758 ADDRESS *adrlist = NULL;
5761 * Search through the addresses to see if there are any
5762 * that are unqualified, and so would be different if
5763 * expanded.
5765 if(abe->tag == Single){
5766 if(abe->addr.addr && abe->addr.addr[0]
5767 && !strindex(abe->addr.addr, '@'))
5768 are_some_unqualified++;
5770 else{
5771 char **ll;
5773 for(ll = abe->addr.list; ll && *ll; ll++){
5774 if(!strindex(*ll, '@')){
5775 are_some_unqualified++;
5776 break;
5781 if(are_some_unqualified){
5782 switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward,
5783 WT_NORM)){
5784 case 'x':
5785 q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
5786 ps_global->mangled_footer = 1;
5787 return 0;
5789 case 'y':
5790 expand_nicks = 1;
5791 break;
5793 case 'n':
5794 expand_nicks = 0;
5795 break;
5799 /* expand nicknames and fully-qualify unqualified names */
5800 if(expand_nicks){
5801 char *error = NULL;
5802 BuildTo bldto;
5803 char *init_addr = NULL;
5805 if(abe->tag == Single)
5806 init_addr = cpystr(abe->addr.addr);
5807 else{
5808 char **ll;
5809 char *p;
5810 long length;
5812 /* figure out how large a string we need to allocate */
5813 length = 0L;
5814 for(ll = abe->addr.list; ll && *ll; ll++)
5815 length += (strlen(*ll) + 2);
5817 if(length)
5818 length -= 2L;
5820 init_addr = (char *)fs_get((size_t)(length+1L) *
5821 sizeof(char));
5822 p = init_addr;
5824 for(ll = abe->addr.list; ll && *ll; ll++){
5825 sstrncpy(&p, *ll, length-(p-init_addr));
5826 if(*(ll+1))
5827 sstrncpy(&p, ", ", length-(p-init_addr));
5830 init_addr[length] = '\0';
5833 bldto.type = Str;
5834 bldto.arg.str = init_addr;
5835 our_build_address(bldto, &addr, &error, NULL, NULL);
5836 if(init_addr)
5837 fs_give((void **)&init_addr);
5839 if(error){
5840 q_status_message1(SM_ORDER, 0, 4, "%s", error);
5841 fs_give((void **)&error);
5843 ps_global->mangled_footer = 1;
5844 return 0;
5847 if(addr){
5848 rfc822_parse_adrlist(&adrlist, addr,
5849 ps_global->maildomain);
5850 fs_give((void **)&addr);
5853 /* Will use adrlist to do the printing below */
5856 tmp = abe->nickname ? abe->nickname : "";
5857 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5858 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
5859 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5860 print_text(tmp);
5861 fs_give((void **)&tmp);
5864 tmp = abe->fullname ? abe->fullname : "";
5865 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5866 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
5867 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5868 print_text(tmp);
5869 fs_give((void **)&tmp);
5872 tmp = abe->fcc ? abe->fcc : "";
5873 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5874 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
5875 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5876 print_text(tmp);
5877 fs_give((void **)&tmp);
5880 tmp = abe->extra ? abe->extra : "";
5881 {unsigned char *p, *bb = NULL;
5882 size_t n, len;
5883 if((n = 4*strlen(tmp)) > SIZEOF_20KBUF-1){
5884 len = n+1;
5885 p = bb = (unsigned char *)fs_get(len * sizeof(char));
5887 else{
5888 len = SIZEOF_20KBUF;
5889 p = (unsigned char *)tmp_20k_buf;
5892 string = (char *)rfc1522_decode_to_utf8(p, len, tmp);
5893 utf8_snprintf(b, len, "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
5894 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5895 print_text(tmp);
5896 fs_give((void **)&tmp);
5899 if(bb)
5900 fs_give((void **)&bb);
5904 * Print addresses
5907 if(expand_nicks){
5908 ADDRESS *a;
5910 for(a = adrlist; a; a = a->next){
5911 char *bufp;
5912 ADDRESS *next_addr;
5913 size_t len;
5915 next_addr = a->next;
5916 a->next = NULL;
5917 len = est_size(a);
5918 bufp = (char *) fs_get(len * sizeof(char));
5919 tmp = addr_string(a, bufp, len);
5920 a->next = next_addr;
5921 string = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf+10000,
5922 SIZEOF_20KBUF-10000, tmp);
5923 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5924 if((tmp = fold(string, 80, 80,
5925 (a == adrlist) ? b : spaces,
5926 more_spaces, FLD_NONE)) != NULL){
5927 print_text(tmp);
5928 fs_give((void **)&tmp);
5931 fs_give((void **)&bufp);
5934 if(adrlist)
5935 mail_free_address(&adrlist);
5936 else{
5937 utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
5938 print_text(b);
5941 else{ /* don't expand or qualify */
5942 if(abe->tag == Single){
5943 tmp = abe->addr.addr ? abe->addr.addr : "";
5944 string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
5945 SIZEOF_20KBUF-10000, tmp);
5946 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5947 if((tmp = fold(string, 80, 80, b,
5948 more_spaces, FLD_NONE)) != NULL){
5949 print_text(tmp);
5950 fs_give((void **)&tmp);
5953 else{
5954 char **ll;
5956 if(!abe->addr.list || !abe->addr.list[0]){
5957 utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
5958 print_text(b);
5961 for(ll = abe->addr.list; ll && *ll; ll++){
5962 string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
5963 SIZEOF_20KBUF-10000, *ll);
5964 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5965 if((tmp = fold(string, 80, 80,
5966 (ll == abe->addr.list)
5967 ? b : spaces,
5968 more_spaces, FLD_NONE)) != NULL){
5969 print_text(tmp);
5970 fs_give((void **)&tmp);
5977 else{
5978 long lineno;
5979 char lbuf[6*MAX_SCREEN_COLS + 1];
5980 char *p;
5981 int i, savecur, savezoomed;
5982 OpenStatus savestatus;
5983 PerAddrBook *pab;
5985 if(agg){ /* print all selected entries */
5986 if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
5987 savezoomed = as.zoomed;
5989 * Fool display code into thinking display is zoomed, so
5990 * we'll skip the unselected entries. Since this feature
5991 * causes all abooks to be displayed we don't have to
5992 * step through each addrbook.
5994 as.zoomed = 1;
5995 warp_to_beginning();
5996 lineno = 0L;
5997 for(dl = dlist(lineno);
5998 dl->type != End;
5999 dl = dlist(++lineno)){
6001 switch(dl->type){
6002 case Beginning:
6003 case ListClickHere:
6004 case ListEmpty:
6005 case ClickHereCmb:
6006 case Text:
6007 case TitleCmb:
6008 case Empty:
6009 case ZoomEmpty:
6010 case AskServer:
6011 continue;
6012 default:
6013 break;
6016 p = get_abook_display_line(lineno, 0, NULL, NULL,
6017 NULL, lbuf, sizeof(lbuf));
6018 print_text1("%s\n", p);
6021 as.zoomed = savezoomed;
6023 else{ /* print all selected entries */
6024 savecur = as.cur;
6025 savezoomed = as.zoomed;
6026 as.zoomed = 1;
6028 for(i = 0; i < as.n_addrbk; i++){
6029 pab = &as.adrbks[i];
6030 if(!(pab->address_book &&
6031 any_selected(pab->address_book->selects)))
6032 continue;
6035 * Print selected entries from addrbook i.
6036 * We have to put addrbook i into Open state so
6037 * that the display code will work right.
6039 as.cur = i;
6040 savestatus = pab->ostatus;
6041 init_abook(pab, Open);
6042 init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
6043 (void)calculate_field_widths();
6044 warp_to_beginning();
6045 lineno = 0L;
6047 for(dl = dlist(lineno);
6048 dl->type != End;
6049 dl = dlist(++lineno)){
6051 switch(dl->type){
6052 case Beginning:
6053 case ListClickHere:
6054 case ClickHereCmb:
6055 continue;
6056 default:
6057 break;
6060 p = get_abook_display_line(lineno, 0, NULL, NULL,
6061 NULL, lbuf,sizeof(lbuf));
6062 print_text1("%s\n", p);
6065 init_abook(pab, savestatus);
6068 as.cur = savecur;
6069 as.zoomed = savezoomed;
6070 /* restore the display for the current addrbook */
6071 init_disp_form(&as.adrbks[as.cur],
6072 ps_global->VAR_ABOOK_FORMATS, as.cur);
6075 else{ /* print either the abook list or a single abook */
6076 int anum;
6077 DL_CACHE_S *dlc;
6079 savezoomed = as.zoomed;
6080 as.zoomed = 0;
6082 if(curopen){ /* print a single address book */
6083 anum = adrbk_num_from_lineno(as.top_ent+as.cur_row);
6084 warp_to_top_of_abook(anum);
6085 if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
6086 lineno = 0L - XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP;
6087 else{
6088 print_text(" ");
6089 print_text(_("ADDRESS BOOK"));
6090 print_text1(" %s\n\n", as.adrbks[as.cur].abnick);
6091 lineno = 0L;
6094 else{ /* print the list of address books */
6095 warp_to_beginning();
6096 lineno = 0L;
6099 for(dl = dlist(lineno);
6100 dl->type != End && (!curopen ||
6101 (anum==adrbk_num_from_lineno(lineno) &&
6102 (as.n_serv == 0 ||
6103 ((dlc=get_dlc(lineno)) &&
6104 dlc->type != DlcDirDelim1))));
6105 dl = dlist(++lineno)){
6107 switch(dl->type){
6108 case Beginning:
6109 case ListClickHere:
6110 case ClickHereCmb:
6111 continue;
6112 default:
6113 break;
6116 p = get_abook_display_line(lineno, 0, NULL, NULL, NULL,
6117 lbuf, sizeof(lbuf));
6118 print_text1("%s\n", p);
6121 as.zoomed = savezoomed;
6125 close_printer();
6128 * jump cache back to where we started so that the next
6129 * request won't cause us to page through the whole thing
6131 if(!do_entry)
6132 warp_to_dlc(match_dlc, save_line);
6134 ps_global->mangled_screen = 1;
6137 ps_global->mangled_footer = 1;
6138 return 1;
6143 * Delete address book entries.
6146 ab_agg_delete(struct pine *ps, int agg)
6148 int ret = 0, i, ch, rc = 0;
6149 PerAddrBook *pab;
6150 adrbk_cntr_t num, ab_count;
6151 char prompt[80];
6153 dprint((2, "- ab_agg_delete -\n"));
6155 if(agg){
6156 snprintf(prompt, sizeof(prompt), _("Really delete %d selected entries"), as.selections);
6157 prompt[sizeof(prompt)-1] = '\0';
6158 ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
6159 if(ch == 'y'){
6160 adrbk_cntr_t newelnum = NO_NEXT, flushelnum = NO_NEXT;
6161 DL_CACHE_S dlc_save, dlc_restart, *dlc;
6162 int we_cancel = 0;
6163 int top_level_display;
6166 * We want to try to put the cursor in a reasonable position
6167 * on the screen when we're done. If we are in the top-level
6168 * display, then we can leave it the same. If we have an
6169 * addrbook opened, then we want to see if we can get back to
6170 * the same entry we are currently on.
6172 if(!(top_level_display = !any_ab_open())){
6173 dlc = get_dlc(as.top_ent+as.cur_row);
6174 dlc_save = *dlc;
6175 newelnum = dlc_save.dlcelnum;
6178 we_cancel = busy_cue(NULL, NULL, 1);
6180 for(i = 0; i < as.n_addrbk && rc != -5; i++){
6181 int orig_selected, selected;
6183 pab = &as.adrbks[i];
6184 if(!pab->address_book)
6185 continue;
6187 ab_count = adrbk_count(pab->address_book);
6188 rc = 0;
6189 selected = howmany_selected(pab->address_book->selects);
6190 orig_selected = selected;
6192 * Because deleting an entry causes the addrbook to be
6193 * immediately updated, we need to delete from higher entry
6194 * numbers to lower numbers. That way, entry number n is still
6195 * entry number n in the updated address book because we've
6196 * only deleted entries higher than n.
6198 for(num = ab_count-1; selected > 0 && rc == 0; num--){
6199 if(entry_is_selected(pab->address_book->selects,
6200 (a_c_arg_t)num)){
6201 rc = adrbk_delete(pab->address_book, (a_c_arg_t)num,
6202 1, 0, 0, 0);
6204 selected--;
6207 * This is just here to help us reposition the cursor.
6209 if(!top_level_display && as.cur == i && rc == 0){
6210 if(num >= newelnum)
6211 flushelnum = num;
6212 else
6213 newelnum--;
6218 if(rc == 0 && orig_selected > 0){
6219 int sort_happened = 0;
6221 rc = adrbk_write(pab->address_book, 0, NULL, &sort_happened, 1, 0);
6222 if(sort_happened)
6223 ps_global->mangled_screen = 1;
6226 if(rc && rc != -5){
6227 q_status_message2(SM_ORDER | SM_DING, 3, 5,
6228 "Error updating %s: %s",
6229 (as.n_addrbk > 1) ? pab->abnick
6230 : "address book",
6231 error_description(errno));
6232 dprint((1, "Error updating %s: %s\n",
6233 pab->filename ? pab->filename : "?",
6234 error_description(errno)));
6238 if(we_cancel)
6239 cancel_busy_cue(-1);
6241 if(rc == 0){
6242 q_status_message(SM_ORDER, 0, 2, _("Deletions completed"));
6243 ret = 1;
6244 erase_selections();
6247 if(!top_level_display){
6248 int lost = 0;
6249 long new_ent;
6251 if(flushelnum != dlc_save.dlcelnum){
6253 * We didn't delete current so restart there. The elnum
6254 * may have changed if we deleted entries above it.
6256 dlc_restart = dlc_save;
6257 dlc_restart.dlcelnum = newelnum;
6259 else{
6261 * Current was deleted.
6263 dlc_restart.adrbk_num = as.cur;
6264 pab = &as.adrbks[as.cur];
6265 ab_count = adrbk_count(pab->address_book);
6266 if(ab_count == 0)
6267 dlc_restart.type = DlcEmpty;
6268 else{
6269 AdrBk_Entry *abe;
6271 dlc_restart.dlcelnum = MIN(newelnum, ab_count-1);
6272 abe = adrbk_get_ae(pab->address_book,
6273 (a_c_arg_t) dlc_restart.dlcelnum);
6274 if(abe && abe->tag == Single)
6275 dlc_restart.type = DlcSimple;
6276 else if(abe && abe->tag == List)
6277 dlc_restart.type = DlcListHead;
6278 else
6279 lost++;
6283 if(lost){
6284 warp_to_top_of_abook(as.cur);
6285 as.top_ent = 0L;
6286 new_ent = first_selectable_line(0L);
6287 if(new_ent == NO_LINE)
6288 as.cur_row = 0L;
6289 else
6290 as.cur_row = new_ent;
6292 /* if it is off screen */
6293 if(as.cur_row >= as.l_p_page){
6294 as.top_ent += (as.cur_row - as.l_p_page + 1);
6295 as.cur_row = (as.l_p_page - 1);
6298 else if(dlc_restart.type != DlcEmpty &&
6299 dlc_restart.dlcelnum == dlc_save.dlcelnum &&
6300 (F_OFF(F_CMBND_ABOOK_DISP,ps_global) || as.cur == 0)){
6302 * Didn't delete any before this line.
6303 * Leave screen about the same. (May have deleted current.)
6305 warp_to_dlc(&dlc_restart, as.cur_row+as.top_ent);
6307 else{
6308 warp_to_dlc(&dlc_restart, 0L);
6309 /* put in middle of screen */
6310 as.top_ent = first_line(0L - (long)as.l_p_page/2L);
6311 as.cur_row = 0L - as.top_ent;
6314 ps->mangled_body = 1;
6317 else
6318 cmd_cancelled("Apply Delete command");
6321 return(ret);
6326 * Delete an entry from the address book
6328 * Args: abook -- The addrbook handle into access library
6329 * command_line -- The screen line on which to prompt
6330 * cur_line -- The entry number in the display list
6331 * warped -- We warped to a new part of the addrbook
6333 * Result: returns 1 if an entry was deleted, 0 if not.
6335 * The main routine above knows what to repaint because it's always the
6336 * current entry that's deleted. Here confirmation is asked of the user
6337 * and the appropriate adrbklib functions are called.
6340 single_entry_delete(AdrBk *abook, long int cur_line, int *warped)
6342 char ch, *cmd = NULL, *dname = NULL;
6343 char prompt[200];
6344 int rc;
6345 register AddrScrn_Disp *dl;
6346 AdrBk_Entry *abe;
6347 DL_CACHE_S *dlc_to_flush;
6349 dprint((2, "- single_entry_delete -\n"));
6351 if(warped)
6352 *warped = 0;
6354 dl = dlist(cur_line);
6355 abe = adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
6357 switch(dl->type){
6358 case Simple:
6359 dname = (abe->fullname && abe->fullname[0])
6360 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6361 SIZEOF_20KBUF, abe->fullname)
6362 : abe->nickname ? abe->nickname : "";
6363 cmd = _("Really delete \"%s\"");
6364 break;
6366 case ListHead:
6367 dname = (abe->fullname && abe->fullname[0])
6368 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6369 SIZEOF_20KBUF, abe->fullname)
6370 : abe->nickname ? abe->nickname : "";
6371 cmd = _("Really delete ENTIRE list \"%s\"");
6372 break;
6374 case ListEnt:
6375 dname = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6376 SIZEOF_20KBUF, listmem_from_dl(abook, dl));
6377 cmd = _("Really delete \"%s\" from list");
6378 break;
6380 default:
6381 break;
6384 dname = dname ? dname : "";
6385 cmd = cmd ? cmd : "";
6387 snprintf(prompt, sizeof(prompt), cmd, dname);
6388 prompt[sizeof(prompt)-1] = '\0';
6389 ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
6390 if(ch == 'y'){
6391 dlc_to_flush = get_dlc(cur_line);
6392 if(dl->type == Simple || dl->type == ListHead){
6393 /*--- Kill a single entry or an entire list ---*/
6394 rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 1, 1, 0, 1);
6396 else if(listmem_count_from_abe(abe) > 2){
6397 /*---- Kill an entry out of a list ----*/
6398 rc = adrbk_listdel(abook, (a_c_arg_t)dl->elnum,
6399 listmem_from_dl(abook, dl));
6401 else{
6402 char *nick, *full, *addr, *fcc, *comment;
6403 adrbk_cntr_t new_entry_num = NO_NEXT;
6405 /*---- Convert a List to a Single entry ----*/
6407 /* Save old info to be transferred */
6408 nick = cpystr(abe->nickname);
6409 full = cpystr(abe->fullname);
6410 fcc = cpystr(abe->fcc);
6411 comment = cpystr(abe->extra);
6412 if(listmem_count_from_abe(abe) == 2)
6413 addr = cpystr(abe->addr.list[1 - dl->l_offset]);
6414 else
6415 addr = cpystr("");
6417 rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 0, 1, 0, 0);
6418 if(rc == 0)
6419 adrbk_add(abook,
6420 NO_NEXT,
6421 nick,
6422 full,
6423 addr,
6424 fcc,
6425 comment,
6426 Single,
6427 &new_entry_num,
6428 (int *)NULL,
6433 fs_give((void **)&nick);
6434 fs_give((void **)&full);
6435 fs_give((void **)&fcc);
6436 fs_give((void **)&comment);
6437 fs_give((void **)&addr);
6439 if(rc == 0){
6440 DL_CACHE_S dlc_restart;
6442 dlc_restart.adrbk_num = as.cur;
6443 dlc_restart.dlcelnum = new_entry_num;
6444 dlc_restart.type = DlcSimple;
6445 warp_to_dlc(&dlc_restart, 0L);
6446 *warped = 1;
6447 return 1;
6451 if(rc == 0){
6452 q_status_message(SM_ORDER, 0, 3,
6453 _("Entry deleted, address book updated"));
6454 dprint((5, "abook: Entry %s\n",
6455 (dl->type == Simple || dl->type == ListHead) ? "deleted"
6456 : "modified"));
6458 * Remove deleted line and everything after it from
6459 * the dlc cache. Next time we try to access those lines they
6460 * will get filled in with the right info.
6462 flush_dlc_from_cache(dlc_to_flush);
6463 return 1;
6465 else{
6466 PerAddrBook *pab;
6468 if(rc != -5)
6469 q_status_message1(SM_ORDER | SM_DING, 3, 5,
6470 _("Error updating address book: %s"),
6471 error_description(errno));
6472 pab = &as.adrbks[as.cur];
6473 dprint((1, "Error deleting entry from %s (%s): %s\n",
6474 pab->abnick ? pab->abnick : "?",
6475 pab->filename ? pab->filename : "?",
6476 error_description(errno)));
6479 return 0;
6481 else{
6482 q_status_message(SM_INFO, 0, 2, _("Entry not deleted"));
6483 return 0;
6488 void
6489 free_headents(struct headerentry **head)
6491 struct headerentry *he;
6492 PrivateTop *pt;
6494 if(head && *head){
6495 for(he = *head; he->name; he++)
6496 if(he->bldr_private){
6497 pt = (PrivateTop *)he->bldr_private;
6498 free_privatetop(&pt);
6501 fs_give((void **)head);
6506 #ifdef ENABLE_LDAP
6508 prompt::name::help::prwid::maxlen::realaddr::
6509 builder::affected_entry::next_affected::selector::key_label::fileedit::
6510 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
6511 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
6513 static struct headerentry headents_for_query[]={
6514 {"Normal Search : ", "NormalSearch", h_composer_qserv_qq, 16, 0, NULL,
6515 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6516 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6517 {"Name : ", "Name", h_composer_qserv_cn, 16, 0, NULL,
6518 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6519 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6520 {"Surname : ", "SurName", h_composer_qserv_sn, 16, 0, NULL,
6521 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6522 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6523 {"Given Name : ", "GivenName", h_composer_qserv_gn, 16, 0, NULL,
6524 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6525 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6526 {"Email Address : ", "EmailAddress", h_composer_qserv_mail, 16, 0, NULL,
6527 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6528 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6529 {"Organization : ", "Organization", h_composer_qserv_org, 16, 0, NULL,
6530 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6531 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6532 {"Org Unit : ", "OrganizationalUnit", h_composer_qserv_unit, 16, 0, NULL,
6533 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6534 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6535 {"Country : ", "Country", h_composer_qserv_country, 16, 0, NULL,
6536 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6537 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6538 {"State : ", "State", h_composer_qserv_state, 16, 0, NULL,
6539 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6540 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6541 {"Locality : ", "Locality", h_composer_qserv_locality, 16, 0, NULL,
6542 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6543 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6544 {" ", "BlankLine", NO_HELP, 1, 0, NULL,
6545 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6546 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_NONE},
6547 {"Custom Filter : ", "CustomFilter", h_composer_qserv_custom, 16, 0, NULL,
6548 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6549 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6550 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6551 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
6553 #define QQ_QQ 0
6554 #define QQ_CN 1
6555 #define QQ_SN 2
6556 #define QQ_GN 3
6557 #define QQ_MAIL 4
6558 #define QQ_ORG 5
6559 #define QQ_UNIT 6
6560 #define QQ_COUNTRY 7
6561 #define QQ_STATE 8
6562 #define QQ_LOCALITY 9
6563 #define QQ_BLANK 10
6564 #define QQ_CUSTOM 11
6565 #define QQ_END 12
6567 static SAVED_QUERY_S *saved_params;
6571 * Formulate a query for LDAP servers and send it and view it.
6572 * This is called from the Address Book screen, either from ^T from the
6573 * composer (selecting) or just when browsing in the address book screen
6574 * (selecting == 0).
6576 * Args ps -- Pine struct
6577 * selecting -- This is set if we're just selecting an address, as opposed
6578 * to browsing on an LDAP server
6579 * who -- Tells us which server to query
6580 * error -- An error message allocated here and freed by caller.
6582 * Returns -- Null if not selecting, possibly an address if selecting.
6583 * The address is 1522 decoded and should be freed by the caller.
6585 char *
6586 query_server(struct pine *ps, int selecting, int *exit, int who, char **error)
6588 struct headerentry *he = NULL;
6589 PICO pbf;
6590 STORE_S *msgso = NULL;
6591 int i, lret, editor_result;
6592 int r = 4, flags;
6593 HelpType help = NO_HELP;
6594 #define FILTSIZE 1000
6595 char fbuf[FILTSIZE+1];
6596 char *ret = NULL;
6597 LDAP_CHOOSE_S *winning_e = NULL;
6598 LDAP_SERV_RES_S *free_when_done = NULL;
6599 SAVED_QUERY_S *sq = NULL;
6600 static ESCKEY_S ekey[] = {
6601 /* TRANSLATORS: go to more complex search screen */
6602 {ctrl('T'), 10, "^T", N_("To complex search")},
6603 {-1, 0, NULL, NULL}
6606 dprint((2, "- query_server(%s) -\n", selecting?"Selecting":""));
6608 if(!(ps->VAR_LDAP_SERVERS && ps->VAR_LDAP_SERVERS[0] &&
6609 ps->VAR_LDAP_SERVERS[0][0])){
6610 if(error)
6611 *error = cpystr(_("No LDAP server available for lookup"));
6613 return(ret);
6616 fbuf[0] = '\0';
6617 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
6618 while(r == 4 || r == 3){
6619 /* TRANSLATORS: we're asking for a character string to search for */
6620 r = optionally_enter(fbuf, -FOOTER_ROWS(ps), 0, sizeof(fbuf),
6621 _("String to search for : "),
6622 ekey, help, &flags);
6623 if(r == 3)
6624 help = help == NO_HELP ? h_dir_comp_search : NO_HELP;
6627 /* strip quotes that user typed by mistake */
6628 (void)removing_double_quotes(fbuf);
6630 if(r == 1 || (r != 10 && fbuf[0] == '\0')){
6631 ps->mangled_footer = 1;
6632 if(error)
6633 *error = cpystr(_("Cancelled"));
6635 return(ret);
6638 editor_result = COMP_EXIT; /* just to get right logic below */
6640 memset((void *)&pbf, 0, sizeof(pbf));
6642 if(r == 10){
6643 standard_picobuf_setup(&pbf);
6644 pbf.exittest = pico_simpleexit;
6645 pbf.exit_label = _("Search");
6646 pbf.canceltest = pico_simplecancel;
6647 if(saved_params){
6648 pbf.expander = restore_query_parameters;
6649 pbf.ctrlr_label = _("Restore");
6652 pbf.pine_anchor = set_titlebar(_("SEARCH DIRECTORY SERVER"),
6653 ps_global->mail_stream,
6654 ps_global->context_current,
6655 ps_global->cur_folder,
6656 ps_global->msgmap,
6657 0, FolderName, 0, 0, NULL);
6658 pbf.pine_flags |= P_NOBODY;
6660 /* An informational message */
6661 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
6662 pbf.msgtext = (void *)so_text(msgso);
6664 * It's nice if we can make it so these lines make sense even if
6665 * they don't all make it on the screen, because the user can't
6666 * scroll down to see them. So just make each line a whole sentence
6667 * that doesn't need the others below it to make sense.
6669 so_puts(msgso,
6670 _("\n Fill in some of the fields above to create a query."));
6671 so_puts(msgso,
6672 _("\n The match will be for the exact string unless you include wildcards (*)."));
6673 so_puts(msgso,
6674 _("\n All filled-in fields must match in order to be counted as a match."));
6675 so_puts(msgso,
6676 _("\n Press \"^R\" to restore previous query values (if you've queried previously)."));
6677 so_puts(msgso,
6678 _("\n \"^G\" for help specific to each item. \"^X\" to make the query, or \"^C\" to cancel."));
6681 he = (struct headerentry *)fs_get((QQ_END+1) *
6682 sizeof(struct headerentry));
6683 memset((void *)he, 0, (QQ_END+1) * sizeof(struct headerentry));
6684 for(i = QQ_QQ; i <= QQ_END; i++)
6685 he[i] = headents_for_query[i];
6687 pbf.headents = he;
6689 sq = copy_query_parameters(NULL);
6690 he[QQ_QQ].realaddr = &sq->qq;
6691 he[QQ_CN].realaddr = &sq->cn;
6692 he[QQ_SN].realaddr = &sq->sn;
6693 he[QQ_GN].realaddr = &sq->gn;
6694 he[QQ_MAIL].realaddr = &sq->mail;
6695 he[QQ_ORG].realaddr = &sq->org;
6696 he[QQ_UNIT].realaddr = &sq->unit;
6697 he[QQ_COUNTRY].realaddr = &sq->country;
6698 he[QQ_STATE].realaddr = &sq->state;
6699 he[QQ_LOCALITY].realaddr = &sq->locality;
6700 he[QQ_CUSTOM].realaddr = &sq->custom;
6702 /* pass to pico and let user set them */
6703 editor_result = pico(&pbf);
6704 ps->mangled_screen = 1;
6705 standard_picobuf_teardown(&pbf);
6707 if(editor_result & COMP_GOTHUP)
6708 hup_signal(0);
6709 else{
6710 fix_windsize(ps_global);
6711 init_signals();
6715 if(editor_result & COMP_EXIT &&
6716 ((r == 0 && *fbuf) ||
6717 (r == 10 && sq &&
6718 (*sq->qq || *sq->cn || *sq->sn || *sq->gn || *sq->mail ||
6719 *sq->org || *sq->unit || *sq->country || *sq->state ||
6720 *sq->locality || *sq->custom)))){
6721 LDAPLookupStyle style;
6722 WP_ERR_S wp_err;
6723 int need_and, mangled;
6724 char *string;
6725 CUSTOM_FILT_S *filter;
6726 SAVED_QUERY_S *s;
6728 s = copy_query_parameters(sq);
6729 save_query_parameters(s);
6731 if(r == 0){
6732 string = fbuf;
6733 filter = NULL;
6735 else{
6736 int categories = 0;
6738 categories = ((*sq->cn != '\0') ? 1 : 0) +
6739 ((*sq->sn != '\0') ? 1 : 0) +
6740 ((*sq->gn != '\0') ? 1 : 0) +
6741 ((*sq->mail != '\0') ? 1 : 0) +
6742 ((*sq->org != '\0') ? 1 : 0) +
6743 ((*sq->unit != '\0') ? 1 : 0) +
6744 ((*sq->country != '\0') ? 1 : 0) +
6745 ((*sq->state != '\0') ? 1 : 0) +
6746 ((*sq->locality != '\0') ? 1 : 0);
6747 need_and = (categories > 1);
6749 if(((sq->cn ? strlen(sq->cn) : 0) +
6750 (sq->sn ? strlen(sq->sn) : 0) +
6751 (sq->gn ? strlen(sq->gn) : 0) +
6752 (sq->mail ? strlen(sq->mail) : 0) +
6753 (sq->org ? strlen(sq->org) : 0) +
6754 (sq->unit ? strlen(sq->unit) : 0) +
6755 (sq->country ? strlen(sq->country) : 0) +
6756 (sq->state ? strlen(sq->state) : 0) +
6757 (sq->locality ? strlen(sq->locality) : 0)) > FILTSIZE - 100){
6758 if(error)
6759 *error = cpystr(_("Search strings too long"));
6761 goto all_done;
6764 if(categories > 0){
6766 snprintf(fbuf, sizeof(fbuf),
6767 "%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",
6768 need_and ? "(&" : "",
6769 *sq->cn ? "(cn=" : "",
6770 *sq->cn ? sq->cn : "",
6771 *sq->cn ? ")" : "",
6772 *sq->sn ? "(sn=" : "",
6773 *sq->sn ? sq->sn : "",
6774 *sq->sn ? ")" : "",
6775 *sq->gn ? "(givenname=" : "",
6776 *sq->gn ? sq->gn : "",
6777 *sq->gn ? ")" : "",
6778 *sq->mail ? "(mail=" : "",
6779 *sq->mail ? sq->mail : "",
6780 *sq->mail ? ")" : "",
6781 *sq->org ? "(o=" : "",
6782 *sq->org ? sq->org : "",
6783 *sq->org ? ")" : "",
6784 *sq->unit ? "(ou=" : "",
6785 *sq->unit ? sq->unit : "",
6786 *sq->unit ? ")" : "",
6787 *sq->country ? "(c=" : "",
6788 *sq->country ? sq->country : "",
6789 *sq->country ? ")" : "",
6790 *sq->state ? "(st=" : "",
6791 *sq->state ? sq->state : "",
6792 *sq->state ? ")" : "",
6793 *sq->locality ? "(l=" : "",
6794 *sq->locality ? sq->locality : "",
6795 *sq->locality ? ")" : "",
6796 need_and ? ")" : "");
6797 fbuf[sizeof(fbuf)-1] = '\0';
6800 if(categories > 0 || *sq->custom)
6801 filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
6803 /* combine the configured filters with this filter */
6804 if(*sq->custom){
6805 string = "";
6806 filter->filt = sq->custom;
6807 filter->combine = 0;
6809 else if(*sq->qq && categories > 0){
6810 string = sq->qq;
6811 filter->filt = fbuf;
6812 filter->combine = 1;
6814 else if(categories > 0){
6815 string = "";
6816 filter->filt = fbuf;
6817 filter->combine = 0;
6819 else{
6820 string = sq->qq;
6821 filter = NULL;
6825 mangled = 0;
6826 memset(&wp_err, 0, sizeof(wp_err));
6827 wp_err.mangled = &mangled;
6828 style = selecting ? AlwaysDisplayAndMailRequired : AlwaysDisplay;
6830 /* maybe coming from composer */
6831 fix_windsize(ps_global);
6832 init_sigwinch();
6833 clear_cursor_pos();
6835 lret = ldap_lookup_all(string, who, 0, style, filter, &winning_e,
6836 &wp_err, &free_when_done);
6838 if(filter)
6839 fs_give((void **)&filter);
6841 if(wp_err.mangled)
6842 ps->mangled_screen = 1;
6844 if(wp_err.error){
6845 if(status_message_remaining() && error)
6846 *error = wp_err.error;
6847 else
6848 fs_give((void **)&wp_err.error);
6851 if(lret == 0 && winning_e && selecting){
6852 ADDRESS *addr;
6854 addr = address_from_ldap(winning_e);
6855 if(addr){
6856 if(!addr->host){
6857 addr->host = cpystr("missing-hostname");
6858 if(error){
6859 if(*error)
6860 fs_give((void **)error);
6862 *error = cpystr(_("Missing hostname in LDAP address"));
6866 ret = addr_list_string(addr, NULL, 1);
6867 if(!ret || !ret[0]){
6868 if(ret)
6869 fs_give((void **)&ret);
6871 if(exit)
6872 *exit = 1;
6874 if(error && !*error){
6875 char buf[200];
6877 snprintf(buf, sizeof(buf), _("No email address available for \"%s\""),
6878 (addr->personal && *addr->personal)
6879 ? addr->personal
6880 : "selected entry");
6881 buf[sizeof(buf)-1] = '\0';
6882 *error = cpystr(buf);
6886 mail_free_address(&addr);
6889 else if(lret == -1 && exit)
6890 *exit = 1;
6893 all_done:
6894 if(he)
6895 free_headents(&he);
6897 if(free_when_done)
6898 free_ldap_result_list(&free_when_done);
6900 if(winning_e)
6901 fs_give((void **)&winning_e);
6903 if(msgso)
6904 so_give(&msgso);
6906 if(sq)
6907 free_query_parameters(&sq);
6909 return(ret);
6914 * View all fields of an LDAP entry while browsing.
6916 * Args ps -- Pine struct
6917 * winning_e -- The struct containing the information about the entry
6918 * to be viewed.
6920 void
6921 view_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
6923 STORE_S *srcstore = NULL;
6924 SourceType srctype = CharStar;
6925 SCROLL_S sargs;
6926 HANDLE_S *handles = NULL;
6928 dprint((9, "- view_ldap_entry -\n"));
6930 if((srcstore=prep_ldap_for_viewing(ps,winning_e,srctype,&handles)) != NULL){
6931 memset(&sargs, 0, sizeof(SCROLL_S));
6932 sargs.text.text = so_text(srcstore);
6933 sargs.text.src = srctype;
6934 sargs.text.desc = _("expanded entry");
6935 sargs.text.handles= handles;
6936 sargs.bar.title = _("DIRECTORY ENTRY");
6937 sargs.proc.tool = process_ldap_cmd;
6938 sargs.proc.data.p = (void *) winning_e;
6939 sargs.help.text = h_ldap_view;
6940 sargs.help.title = _("HELP FOR DIRECTORY VIEW");
6941 sargs.keys.menu = &ldap_view_keymenu;
6942 setbitmap(sargs.keys.bitmap);
6944 if(handles)
6945 sargs.keys.menu->how_many = 2;
6946 else{
6947 sargs.keys.menu->how_many = 1;
6948 clrbitn(OTHER_KEY, sargs.keys.bitmap);
6951 scrolltool(&sargs);
6953 ps->mangled_screen = 1;
6954 so_give(&srcstore);
6955 free_handles(&handles);
6957 else
6958 q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
6963 * Compose a message to the email addresses contained in an LDAP entry.
6965 * Args ps -- Pine struct
6966 * winning_e -- The struct containing the information about the entry
6967 * to be viewed.
6969 void
6970 compose_to_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *e, int allow_role)
6972 struct berval **elecmail = NULL,
6973 **mail = NULL,
6974 **cn = NULL,
6975 **sn = NULL,
6976 **givenname = NULL;
6977 int num;
6978 size_t len = 0;
6980 dprint((9, "- compose_to_ldap_entry -\n"));
6982 if(e){
6983 char *a;
6984 BerElement *ber;
6986 for(a = ldap_first_attribute(e->ld, e->selected_entry, &ber);
6987 a != NULL;
6988 a = ldap_next_attribute(e->ld, e->selected_entry, ber)){
6990 if(strcmp(a, e->info_used->mailattr) == 0){
6991 if(!mail)
6992 mail = ldap_get_values_len(e->ld, e->selected_entry, a);
6994 else if(strcmp(a, "electronicmail") == 0){
6995 if(!elecmail)
6996 elecmail = ldap_get_values_len(e->ld, e->selected_entry, a);
6998 else if(strcmp(a, e->info_used->cnattr) == 0){
6999 if(!cn)
7000 cn = ldap_get_values_len(e->ld, e->selected_entry, a);
7002 else if(strcmp(a, e->info_used->gnattr) == 0){
7003 if(!givenname)
7004 givenname = ldap_get_values_len(e->ld, e->selected_entry, a);
7006 else if(strcmp(a, e->info_used->snattr) == 0){
7007 if(!sn)
7008 sn = ldap_get_values_len(e->ld, e->selected_entry, a);
7011 our_ldap_memfree(a);
7015 if(elecmail){
7016 if(ALPINE_LDAP_can_use(elecmail) && !mail)
7017 mail = elecmail;
7018 else
7019 ldap_value_free_len(elecmail);
7021 elecmail = NULL;
7024 for(num = 0; ALPINE_LDAP_usable(mail, num); num++)
7025 len += strlen(mail[num]->bv_val) + 1;
7027 if(len){
7028 char *p, *address, *fn = NULL;
7029 BuildTo bldto;
7030 SAVE_STATE_S state;
7032 address = (char *)fs_get(len * sizeof(char));
7033 p = address;
7034 num = 0;
7035 while(ALPINE_LDAP_usable(mail, num)){
7036 sstrncpy(&p, mail[num]->bv_val, len-(p-address));
7037 num++;
7038 if(mail[num])
7039 sstrncpy(&p, ",", len-(p-address));
7042 address[len-1] = '\0';
7045 * If we have a fullname and there is only a single address and
7046 * the address doesn't seem to have a fullname with it, add it.
7048 if(ALPINE_LDAP_can_use(mail) && !mail[1]){
7049 if(ALPINE_LDAP_can_use(cn))
7050 fn = cpystr(cn[0]->bv_val);
7051 else if(ALPINE_LDAP_can_use(sn) && ALPINE_LDAP_can_use(givenname)){
7052 size_t l;
7054 l = strlen(givenname[0]->bv_val) + strlen(sn[0]->bv_val) + 1;
7055 fn = (char *) fs_get((l+1) * sizeof(char));
7056 snprintf(fn, l+1, "%s %s", givenname[0]->bv_val, sn[0]->bv_val);
7057 fn[l] = '\0';
7061 if(ALPINE_LDAP_can_use(mail) && !mail[1] && fn){
7062 ADDRESS *adrlist = NULL;
7063 char *tmp_a_string;
7065 tmp_a_string = cpystr(address);
7066 rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
7067 fs_give((void **)&tmp_a_string);
7068 if(adrlist && !adrlist->next && !adrlist->personal){
7069 char *new_address;
7070 size_t len;
7071 RFC822BUFFER rbuf;
7073 adrlist->personal = cpystr(fn);
7074 len = est_size(adrlist);
7075 new_address = (char *) fs_get(len * sizeof(char));
7076 new_address[0] ='\0';
7077 rbuf.f = dummy_soutr;
7078 rbuf.s = NULL;
7079 rbuf.beg = new_address;
7080 rbuf.cur = new_address;
7081 rbuf.end = new_address+len-1;
7082 /* this will quote it if it needs quoting */
7083 rfc822_output_address_list(&rbuf, adrlist, 0L, NULL);
7084 *rbuf.cur = '\0';
7085 fs_give((void **)&address);
7086 address = new_address;
7089 if(adrlist)
7090 mail_free_address(&adrlist);
7093 bldto.type = Str;
7094 bldto.arg.str = address;
7096 save_state(&state);
7097 ab_compose_internal(bldto, allow_role);
7098 restore_state(&state);
7099 fs_give((void **)&address);
7100 if(fn)
7101 fs_give((void **)&fn);
7104 * Window size may have changed in composer.
7105 * Pine_send will have reset the window size correctly,
7106 * but we still have to reset our address book data structures.
7108 if(as.initialized)
7109 ab_resize();
7111 ps_global->mangled_screen = 1;
7113 else
7114 q_status_message(SM_ORDER, 0, 4, _("No address to compose to"));
7116 if(mail)
7117 ldap_value_free_len(mail);
7118 if(cn)
7119 ldap_value_free_len(cn);
7120 if(sn)
7121 ldap_value_free_len(sn);
7122 if(givenname)
7123 ldap_value_free_len(givenname);
7128 * Forward the text of an LDAP entry via email (not a vcard attachment)
7130 * Args ps -- Pine struct
7131 * winning_e -- The struct containing the information about the entry
7132 * to be viewed.
7134 void
7135 forward_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
7137 STORE_S *srcstore = NULL;
7138 SourceType srctype = CharStar;
7140 dprint((9, "- forward_ldap_entry -\n"));
7142 if((srcstore = prep_ldap_for_viewing(ps,winning_e,srctype,NULL)) != NULL){
7143 forward_text(ps, so_text(srcstore), srctype);
7144 ps->mangled_screen = 1;
7145 so_give(&srcstore);
7147 else
7148 q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
7152 STORE_S *
7153 prep_ldap_for_viewing(struct pine *ps, LDAP_CHOOSE_S *winning_e, SourceType srctype, HANDLE_S **handlesp)
7155 STORE_S *store = NULL;
7156 char *a, *tmp;
7157 BerElement *ber;
7158 int i, width;
7159 #define W (1000)
7160 #define INDENTHERE (22)
7161 char obuf[W+10];
7162 char hdr[6*INDENTHERE+1], hdr2[6*INDENTHERE+1];
7163 struct berval **cn = NULL;
7164 int indent = INDENTHERE;
7166 if(!(store = so_get(srctype, NULL, EDIT_ACCESS)))
7167 return(store);
7169 /* for mailto handles so user can select individual email addrs */
7170 if(handlesp)
7171 init_handles(handlesp);
7173 width = MAX(ps->ttyo->screen_cols, 25);
7175 snprintf(hdr2, sizeof(hdr2), "%-*.*s: ", indent-2,indent-2, "");
7176 hdr2[sizeof(hdr2)-1] = '\0';
7178 if(sizeof(obuf) > ps->ttyo->screen_cols+1){
7179 memset((void *)obuf, '-', ps->ttyo->screen_cols * sizeof(char));
7180 obuf[ps->ttyo->screen_cols] = '\n';
7181 obuf[ps->ttyo->screen_cols+1] = '\0';
7184 a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
7186 so_puts(store, obuf);
7187 if((tmp = fold(a, width, width, "", " ", FLD_NONE)) != NULL){
7188 so_puts(store, tmp);
7189 fs_give((void **)&tmp);
7192 so_puts(store, obuf);
7193 so_puts(store, "\n");
7195 our_ldap_dn_memfree(a);
7197 for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
7198 a != NULL;
7199 a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
7201 if(a && *a){
7202 struct berval **vals;
7203 char *fn = NULL;
7205 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
7207 /* save this for mailto */
7208 if(handlesp && !cn && !strcmp(a, winning_e->info_used->cnattr))
7209 cn = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
7211 if(vals){
7212 int do_mailto;
7214 do_mailto = (handlesp &&
7215 !strcmp(a, winning_e->info_used->mailattr));
7217 utf8_snprintf(hdr, sizeof(hdr), "%-*.*w: ", indent-2,indent-2,
7218 ldap_translate(a, winning_e->info_used));
7219 hdr[sizeof(hdr)-1] = '\0';
7220 for(i = 0; ALPINE_LDAP_usable(vals, i); i++){
7221 if(do_mailto){
7222 ADDRESS *ad = NULL;
7223 HANDLE_S *h;
7224 char buf[20];
7225 char *tmp_a_string;
7226 char *addr, *new_addr, *enc_addr;
7227 char *path = NULL;
7229 addr = cpystr(vals[i]->bv_val);
7230 if(ALPINE_LDAP_can_use(cn))
7231 fn = cpystr(cn[0]->bv_val);
7233 if(fn){
7234 tmp_a_string = cpystr(addr);
7235 rfc822_parse_adrlist(&ad, tmp_a_string, "@");
7236 fs_give((void **)&tmp_a_string);
7237 if(ad && !ad->next && !ad->personal){
7238 RFC822BUFFER rbuf;
7239 size_t len;
7241 ad->personal = cpystr(fn);
7242 len = est_size(ad);
7243 new_addr = (char *) fs_get(len * sizeof(char));
7244 new_addr[0] = '\0';
7245 /* this will quote it if it needs quoting */
7246 rbuf.f = dummy_soutr;
7247 rbuf.s = NULL;
7248 rbuf.beg = new_addr;
7249 rbuf.cur = new_addr;
7250 rbuf.end = new_addr+len-1;
7251 rfc822_output_address_list(&rbuf, ad, 0L, NULL);
7252 *rbuf.cur = '\0';
7253 fs_give((void **) &addr);
7254 addr = new_addr;
7257 if(ad)
7258 mail_free_address(&ad);
7260 fs_give((void **)&fn);
7263 if((enc_addr = rfc1738_encode_mailto(addr)) != NULL){
7264 size_t l;
7266 l = strlen(enc_addr) + 7;
7267 path = (char *) fs_get((l+1) * sizeof(char));
7268 snprintf(path, l+1, "mailto:%s", enc_addr);
7269 path[l] = '\0';
7270 fs_give((void **)&enc_addr);
7273 fs_give((void **)&addr);
7275 if(path){
7276 h = new_handle(handlesp);
7277 h->type = URL;
7278 h->h.url.path = path;
7279 snprintf(buf, sizeof(buf), "%d", h->key);
7280 buf[sizeof(buf)-1] = '\0';
7283 * Don't try to fold this address. Just put it on
7284 * one line and let scrolltool worry about
7285 * cutting it off before it goes past the
7286 * right hand edge. Otherwise, we have to figure
7287 * out how to handle the wrapped handle.
7289 snprintf(obuf, sizeof(obuf), "%c%c%c%s%s%c%c%c%c",
7290 TAG_EMBED, TAG_HANDLE,
7291 (int) strlen(buf), buf, vals[i]->bv_val,
7292 TAG_EMBED, TAG_BOLDOFF,
7293 TAG_EMBED, TAG_INVOFF);
7294 obuf[sizeof(obuf)-1] = '\0';
7296 so_puts(store, (i==0) ? hdr : hdr2);
7297 so_puts(store, obuf);
7298 so_puts(store, "\n");
7300 else{
7301 snprintf(obuf, sizeof(obuf), "%s", vals[i]->bv_val);
7302 obuf[sizeof(obuf)-1] = '\0';
7304 if((tmp = fold(obuf, width, width,
7305 (i==0) ? hdr : hdr2,
7306 repeat_char(indent+2, SPACE),
7307 FLD_NONE)) != NULL){
7308 so_puts(store, tmp);
7309 fs_give((void **)&tmp);
7313 else{
7314 int is_binary = 0;
7315 char *tmp2;
7317 for(tmp = strchr(a, ';'); tmp != NULL; tmp = tmp2){
7318 tmp2 = strchr(++tmp, ';');
7319 if(tmp2)
7320 *tmp2 = '\0';
7321 is_binary = !strucmp(tmp, "binary");
7322 if(tmp2)
7323 *tmp2 = ';';
7324 if(is_binary)
7325 break;
7328 snprintf(obuf, sizeof(obuf), "%s", (is_binary &&
7329 vals[i]->bv_val && vals[i]->bv_val[0] != '\0') ?
7330 _("[ Binary Data ]") : vals[i]->bv_val);
7331 obuf[sizeof(obuf)-1] = '\0';
7333 if((tmp = fold(obuf, width, width,
7334 (i==0) ? hdr : hdr2,
7335 repeat_char(indent+2, SPACE),
7336 FLD_NONE)) != NULL){
7337 so_puts(store, tmp);
7338 fs_give((void **)&tmp);
7343 ldap_value_free_len(vals);
7345 else{
7346 utf8_snprintf(obuf, sizeof(obuf), "%-*.*w\n", indent-1,indent-1,
7347 ldap_translate(a, winning_e->info_used));
7348 obuf[sizeof(obuf)-1] = '\0';
7349 so_puts(store, obuf);
7353 our_ldap_memfree(a);
7356 if(cn)
7357 ldap_value_free_len(cn);
7359 return(store);
7364 process_ldap_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
7366 int rv = 1;
7368 ps_global->next_screen = SCREEN_FUN_NULL;
7370 switch(cmd){
7371 case MC_SAVE :
7372 save_ldap_entry(ps_global, sparms->proc.data.p, 0);
7373 rv = 0;
7374 break;
7376 case MC_COMPOSE :
7377 compose_to_ldap_entry(ps_global, sparms->proc.data.p, 0);
7378 rv = 0;
7379 break;
7381 case MC_ROLE :
7382 compose_to_ldap_entry(ps_global, sparms->proc.data.p, 1);
7383 rv = 0;
7384 break;
7386 default:
7387 alpine_panic("Unexpected command in process_ldap_cmd");
7388 break;
7391 return(rv);
7396 pico_simpleexit(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
7397 char **result)
7399 if(result)
7400 *result = NULL;
7402 return(0);
7405 char *
7406 pico_simplecancel(void (*redraw_pico)(void))
7408 return("Cancelled");
7413 * Store query parameters so that they can be recalled by user later with ^R.
7415 void
7416 save_query_parameters(SAVED_QUERY_S *params)
7418 free_saved_query_parameters();
7419 saved_params = params;
7423 SAVED_QUERY_S *
7424 copy_query_parameters(SAVED_QUERY_S *params)
7426 SAVED_QUERY_S *sq;
7428 sq = (SAVED_QUERY_S *)fs_get(sizeof(SAVED_QUERY_S));
7429 memset((void *)sq, 0, sizeof(SAVED_QUERY_S));
7431 if(params && params->qq)
7432 sq->qq = cpystr(params->qq);
7433 else
7434 sq->qq = cpystr("");
7436 if(params && params->cn)
7437 sq->cn = cpystr(params->cn);
7438 else
7439 sq->cn = cpystr("");
7441 if(params && params->sn)
7442 sq->sn = cpystr(params->sn);
7443 else
7444 sq->sn = cpystr("");
7446 if(params && params->gn)
7447 sq->gn = cpystr(params->gn);
7448 else
7449 sq->gn = cpystr("");
7451 if(params && params->mail)
7452 sq->mail = cpystr(params->mail);
7453 else
7454 sq->mail = cpystr("");
7456 if(params && params->org)
7457 sq->org = cpystr(params->org);
7458 else
7459 sq->org = cpystr("");
7461 if(params && params->unit)
7462 sq->unit = cpystr(params->unit);
7463 else
7464 sq->unit = cpystr("");
7466 if(params && params->country)
7467 sq->country = cpystr(params->country);
7468 else
7469 sq->country = cpystr("");
7471 if(params && params->state)
7472 sq->state = cpystr(params->state);
7473 else
7474 sq->state = cpystr("");
7476 if(params && params->locality)
7477 sq->locality = cpystr(params->locality);
7478 else
7479 sq->locality = cpystr("");
7481 if(params && params->custom)
7482 sq->custom = cpystr(params->custom);
7483 else
7484 sq->custom = cpystr("");
7486 return(sq);
7490 void
7491 free_saved_query_parameters(void)
7493 if(saved_params)
7494 free_query_parameters(&saved_params);
7498 void
7499 free_query_parameters(SAVED_QUERY_S **parm)
7501 if(parm){
7502 if(*parm){
7503 if((*parm)->qq)
7504 fs_give((void **)&(*parm)->qq);
7505 if((*parm)->cn)
7506 fs_give((void **)&(*parm)->cn);
7507 if((*parm)->sn)
7508 fs_give((void **)&(*parm)->sn);
7509 if((*parm)->gn)
7510 fs_give((void **)&(*parm)->gn);
7511 if((*parm)->mail)
7512 fs_give((void **)&(*parm)->mail);
7513 if((*parm)->org)
7514 fs_give((void **)&(*parm)->org);
7515 if((*parm)->unit)
7516 fs_give((void **)&(*parm)->unit);
7517 if((*parm)->country)
7518 fs_give((void **)&(*parm)->country);
7519 if((*parm)->state)
7520 fs_give((void **)&(*parm)->state);
7521 if((*parm)->locality)
7522 fs_give((void **)&(*parm)->locality);
7523 if((*parm)->custom)
7524 fs_give((void **)&(*parm)->custom);
7526 fs_give((void **)parm);
7533 * A callback from pico to restore the saved query parameters.
7535 * Args he -- Unused.
7536 * s -- The place to return the allocated array of values.
7538 * Returns -- 1 if there are parameters to return, 0 otherwise.
7541 restore_query_parameters(struct headerentry *he, char ***s)
7543 int retval = 0, i = 0;
7545 if(s)
7546 *s = NULL;
7548 if(saved_params && s){
7549 *s = (char **)fs_get((QQ_END + 1) * sizeof(char *));
7550 (*s)[i++] = cpystr(saved_params->qq ? saved_params->qq : "");
7551 (*s)[i++] = cpystr(saved_params->cn ? saved_params->cn : "");
7552 (*s)[i++] = cpystr(saved_params->sn ? saved_params->sn : "");
7553 (*s)[i++] = cpystr(saved_params->gn ? saved_params->gn : "");
7554 (*s)[i++] = cpystr(saved_params->mail ? saved_params->mail : "");
7555 (*s)[i++] = cpystr(saved_params->org ? saved_params->org : "");
7556 (*s)[i++] = cpystr(saved_params->unit ? saved_params->unit : "");
7557 (*s)[i++] = cpystr(saved_params->country ? saved_params->country : "");
7558 (*s)[i++] = cpystr(saved_params->state ? saved_params->state : "");
7559 (*s)[i++] = cpystr(saved_params->locality ? saved_params->locality :"");
7560 (*s)[i++] = cpystr(saved_params->custom ? saved_params->custom :"");
7561 (*s)[i] = 0;
7562 retval = 1;
7565 return(retval);
7570 * Internal handler for viewing an LDAP url.
7573 url_local_ldap(char *url)
7575 LDAP *ld;
7576 struct timeval t;
7577 int ld_err, mangled = 0, we_cancel, retval = 0, proto = 3;
7578 int we_turned_on = 0;
7579 char ebuf[300];
7580 LDAPMessage *result;
7581 LDAP_SERV_S *info;
7582 LDAP_SERV_RES_S *serv_res = NULL;
7583 LDAPURLDesc *ldapurl = NULL;
7584 WP_ERR_S wp_err;
7586 dprint((2, "url_local_ldap(%s)\n", url ? url : "?"));
7588 ld_err = ldap_url_parse(url, &ldapurl);
7589 if(ld_err || !ldapurl){
7590 snprintf(ebuf, sizeof(ebuf), "URL parse failed for %s", url);
7591 ebuf[sizeof(ebuf)-1] = '\0';
7592 q_status_message(SM_ORDER, 3, 5, ebuf);
7593 return(retval);
7596 if(!ldapurl->lud_host){
7597 /* TRNASLATORS: No host in <url> */
7598 snprintf(ebuf, sizeof(ebuf), _("No host in %s"), url);
7599 ebuf[sizeof(ebuf)-1] = '\0';
7600 q_status_message(SM_ORDER, 3, 5, ebuf);
7601 ldap_free_urldesc(ldapurl);
7602 return(retval);
7605 we_turned_on = intr_handling_on();
7606 we_cancel = busy_cue(_("Searching for LDAP url"), NULL, 0);
7607 ps_global->mangled_footer = 1;
7609 #ifdef _SOLARIS_SDK
7610 if((ld = ldap_init(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7611 #else
7612 #if (LDAPAPI >= 11)
7613 #ifdef _WINDOWS
7614 if((ld = ldap_init(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7615 #else
7616 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "ldap://%s:%d", ldapurl->lud_host, ldapurl->lud_port);
7617 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
7618 if(ldap_initialize(&ld, tmp_20k_buf) != LDAP_SUCCESS)
7619 #endif /* _WINDOWS */
7620 #else
7621 if((ld = ldap_open(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7622 #endif /* LDAPAPI >= 11 */
7623 #endif /* _SOLARIS_SDK */
7625 if(we_cancel){
7626 cancel_busy_cue(-1);
7627 we_cancel = 0;
7630 q_status_message(SM_ORDER,3,5, _("LDAP search failed: can't initialize"));
7632 else if(!ps_global->intr_pending){
7633 if(ldap_v3_is_supported(ld) &&
7634 our_ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) == 0){
7635 dprint((5, "ldap: using version 3 protocol\n"));
7639 * If we don't set RESTART then the select() waiting for the answer
7640 * in libldap will be interrupted and stopped by our busy_cue.
7642 our_ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
7644 t.tv_sec = 30; t.tv_usec = 0;
7645 #ifdef _WINDOWS
7646 ld_err = ldap_search_st(ld, ldapurl->lud_dn, ldapurl->lud_scope,
7647 ldapurl->lud_filter, ldapurl->lud_attrs,
7648 0, &t, &result);
7649 #else
7650 ld_err = ldap_search_ext_s(ld, ldapurl->lud_dn, ldapurl->lud_scope,
7651 ldapurl->lud_filter, ldapurl->lud_attrs, 0,
7652 NULL, NULL,
7653 &t, DEF_LDAP_SIZE, &result);
7654 #endif
7655 if(ld_err != LDAP_SUCCESS){
7656 if(we_cancel){
7657 cancel_busy_cue(-1);
7658 we_cancel = 0;
7661 snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s"), ldap_err2string(ld_err));
7662 ebuf[sizeof(ebuf)-1] = '\0';
7663 q_status_message(SM_ORDER, 3, 5, ebuf);
7664 #ifdef _WINDOWS
7665 ldap_unbind(ld);
7666 #else
7667 ldap_unbind_ext(ld, NULL, NULL);
7668 #endif /* _WINDOWS */
7670 else if(!ps_global->intr_pending){
7671 if(we_cancel){
7672 cancel_busy_cue(-1);
7673 we_cancel = 0;
7676 if(we_turned_on){
7677 intr_handling_off();
7678 we_turned_on = 0;
7681 if(ldap_count_entries(ld, result) == 0){
7682 q_status_message(SM_ORDER, 3, 5, _("No matches found for url"));
7683 #ifdef _WINDOWS
7684 ldap_unbind(ld);
7685 #else
7686 ldap_unbind_ext(ld, NULL, NULL);
7687 #endif /* _WINDOWS */
7688 if(result)
7689 ldap_msgfree(result);
7691 else{
7692 serv_res = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
7693 memset((void *)serv_res, 0, sizeof(*serv_res));
7694 serv_res->ld = ld;
7695 serv_res->res = result;
7696 info = (LDAP_SERV_S *)fs_get(sizeof(LDAP_SERV_S));
7697 memset((void *)info, 0, sizeof(*info));
7698 info->mailattr = cpystr(DEF_LDAP_MAILATTR);
7699 info->snattr = cpystr(DEF_LDAP_SNATTR);
7700 info->gnattr = cpystr(DEF_LDAP_GNATTR);
7701 info->cnattr = cpystr(DEF_LDAP_CNATTR);
7702 serv_res->info_used = info;
7703 memset(&wp_err, 0, sizeof(wp_err));
7704 wp_err.mangled = &mangled;
7706 ask_user_which_entry(serv_res, NULL, NULL, &wp_err, DisplayForURL);
7707 if(wp_err.error){
7708 q_status_message(SM_ORDER, 3, 5, wp_err.error);
7709 fs_give((void **)&wp_err.error);
7712 if(mangled)
7713 ps_global->mangled_screen = 1;
7715 free_ldap_result_list(&serv_res);
7716 retval = 1;
7721 if(we_cancel)
7722 cancel_busy_cue(-1);
7724 if(we_turned_on)
7725 intr_handling_off();
7727 if(ldapurl)
7728 ldap_free_urldesc(ldapurl);
7730 return(retval);
7732 #endif /* ENABLE_LDAP */