Add support for tab-completion when selecting by rule
[alpine.git] / alpine / adrbkcmd.c
blobe9af59150ce41873e860b9dfda8eed475561ae5d
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_io_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_io_t pc, gc;
135 int cmd, abook_indent;
136 long offset = 0L;
137 char b[500];
139 dprint((5, "- view_abook_entry -\n"));
141 if(is_addr(cur_line)){
142 abe = ae(cur_line);
143 if(!abe){
144 q_status_message(SM_ORDER, 0, 3, _("Error reading entry"));
145 return;
148 else{
149 q_status_message(SM_ORDER, 0, 3, _("Nothing to view"));
150 return;
153 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
154 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
155 return;
158 abook_indent = utf8_width(_("Nickname")) + 2;
160 /* TRANSLATORS: Nickname is a shorthand name for something */
161 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
162 so_puts(in_store, b);
163 if(abe->nickname)
164 so_puts(in_store, abe->nickname);
166 so_puts(in_store, "\015\012");
168 /* TRANSLATORS: Full name is the name that goes with an email address.
169 For example, in
170 Fred Flintstone <fred@bedrock.org>
171 Fred Flintstone is the Full Name. */
172 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
173 so_puts(in_store, b);
174 if(abe->fullname)
175 so_puts(in_store, abe->fullname);
177 so_puts(in_store, "\015\012");
179 /* TRANSLATORS: Fcc is an abbreviation for File carbon copy. It is like
180 a cc which is a copy of a message that goes to somebody other than the
181 main recipient, only this is a copy of a message which is put into a
182 file on the user's computer. */
183 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
184 so_puts(in_store, b);
185 if(abe->fcc)
186 so_puts(in_store, abe->fcc);
188 so_puts(in_store, "\015\012");
190 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, AB_COMMENT_STR);
191 so_puts(in_store, b);
192 if(abe->extra)
193 so_puts(in_store, abe->extra);
195 so_puts(in_store, "\015\012");
197 /* TRANSLATORS: Addresses refers to email Addresses */
198 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
199 so_puts(in_store, b);
200 if(abe->tag == Single){
201 char *tmp = abe->addr.addr ? abe->addr.addr : "";
202 #ifdef ENABLE_LDAP
203 if(!strncmp(tmp, QRUN_LDAP, LEN_QRL))
204 string = LDAP_DISP;
205 else
206 #endif
207 string = tmp;
209 so_puts(in_store, string);
211 else{
212 char **ll;
214 for(ll = abe->addr.list; ll && *ll; ll++){
215 if(ll != abe->addr.list){
216 so_puts(in_store, "\015\012");
217 so_puts(in_store, repeat_char(abook_indent+2, SPACE));
220 #ifdef ENABLE_LDAP
221 if(!strncmp(*ll, QRUN_LDAP, LEN_QRL))
222 string = LDAP_DISP;
223 else
224 #endif
225 string = *ll;
227 so_puts(in_store, string);
229 if(*(ll+1)) /* not the last one */
230 so_puts(in_store, ",");
234 so_puts(in_store, "\015\012");
237 if(!(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
238 so_give(&in_store);
239 q_status_message(SM_ORDER | SM_DING, 3, 3,
240 _("Error allocating space."));
241 return;
244 so_seek(in_store, 0L, 0);
246 init_handles(&handles);
247 gf_filter_init();
249 if(F_ON(F_VIEW_SEL_URL, ps_global)
250 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
251 || F_ON(F_SCAN_ADDR, ps_global))
252 gf_link_filter(gf_line_test,
253 gf_line_test_opt(url_hilite_abook,
254 gf_url_hilite_opt(&uh,&handles,0)));
256 gf_link_filter(gf_wrap, gf_wrap_filter_opt(ps->ttyo->screen_cols - 4,
257 ps->ttyo->screen_cols,
258 NULL, abook_indent+2,
259 GFW_HANDLES));
260 gf_link_filter(gf_nvtnl_local, NULL);
262 gf_set_so_readc(&gc, in_store);
263 gf_set_so_writec(&pc, out_store);
265 if((errstr = gf_pipe(gc, pc)) != NULL){
266 so_give(&in_store);
267 so_give(&out_store);
268 free_handles(&handles);
269 q_status_message1(SM_ORDER | SM_DING, 3, 3,
270 /* TRANSLATORS: %s is the error message */
271 _("Can't format entry: %s"), errstr);
272 return;
275 gf_clear_so_writec(out_store);
276 gf_clear_so_readc(in_store);
278 memset(&sargs, 0, sizeof(SCROLL_S));
279 sargs.text.text = so_text(out_store);
280 sargs.text.src = CharStar;
281 sargs.text.desc = _("expanded entry");
282 sargs.text.handles = handles;
284 if(offset){ /* resize? preserve paging! */
285 sargs.start.on = Offset;
286 sargs.start.loc.offset = offset;
287 offset = 0L;
290 /* TRANSLATORS: a screen title. We are viewing an address book */
291 sargs.bar.title = _("ADDRESS BOOK (View)");
292 sargs.bar.style = TextPercent;
293 sargs.proc.tool = process_abook_view_cmd;
294 sargs.proc.data.i = VIEW_ABOOK_NONE;
295 sargs.resize_exit = 1;
296 sargs.help.text = h_abook_view;
297 /* TRANSLATORS: help screen title */
298 sargs.help.title = _("HELP FOR ADDRESS BOOK VIEW");
299 sargs.keys.menu = &abook_view_keymenu;
300 setbitmap(sargs.keys.bitmap);
302 if(handles)
303 sargs.keys.menu->how_many = 2;
304 else{
305 sargs.keys.menu->how_many = 1;
306 clrbitn(OTHER_KEY, sargs.keys.bitmap);
309 if((cmd = scrolltool(&sargs)) == MC_RESIZE)
310 offset = sargs.start.loc.offset;
312 so_give(&out_store);
313 free_handles(&handles);
315 while(cmd == MC_RESIZE);
317 so_give(&in_store);
319 if(sargs.proc.data.i != VIEW_ABOOK_NONE){
320 long old_l_p_p = 0, old_top_ent = 0, old_cur_row = 0;
322 if(sargs.proc.data.i == VIEW_ABOOK_WARPED){
324 * Warped means we got plopped down somewhere in the display
325 * list so that we don't know where we are relative to where
326 * we were before we warped. The current line number will
327 * be zero, since that is what the warp would have set.
329 as.top_ent = first_line(0L - as.l_p_page/2L);
330 as.cur_row = 0L - as.top_ent;
332 else if(sargs.proc.data.i == VIEW_ABOOK_EDITED){
333 old_l_p_p = as.l_p_page;
334 old_top_ent = as.top_ent;
335 old_cur_row = as.cur_row;
338 /* Window size may have changed while in pico. */
339 ab_resize();
341 /* fix up what ab_resize messed up */
342 if(sargs.proc.data.i != VIEW_ABOOK_WARPED && old_l_p_p == as.l_p_page){
343 as.top_ent = old_top_ent;
344 as.cur_row = old_cur_row;
345 as.old_cur_row = old_cur_row;
348 cur_line = as.top_ent+as.cur_row;
351 ps->mangled_screen = 1;
356 url_hilite_abook(long int linenum, char *line, LT_INS_S **ins, void *local)
358 register char *lp;
360 if((lp = strchr(line, ':')) &&
361 !strncmp(line, AB_COMMENT_STR, strlen(AB_COMMENT_STR)))
362 (void) url_hilite(linenum, lp + 1, ins, local);
364 return(0);
369 process_abook_view_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
371 int rv = 1, i;
372 PerAddrBook *pab;
373 AddrScrn_Disp *dl;
374 static ESCKEY_S text_or_vcard[] = {
375 /* TRANSLATORS: Text refers to plain old text, probably the text of
376 an email message */
377 {'t', 't', "T", N_("Text")},
378 /* TRANSLATORS: A VCard is kind of like an electronic business card. It is not
379 something specific to alpine, it is universal. */
380 {'v', 'v', "V", N_("VCard")},
381 {-1, 0, NULL, NULL}};
383 ps_global->next_screen = SCREEN_FUN_NULL;
385 switch(cmd){
386 case MC_EDIT :
388 * MC_EDIT works a little differently from the other cmds here.
389 * The others return 0 to scrolltool so that we are still in
390 * the view screen. This one is different because we may have
391 * changed what we're viewing. We handle that by returning 1
392 * to scrolltool and setting the sparms opt union's gint
393 * to the value below.
395 * (Late breaking news. Now we're going to return 1 from all these
396 * commands and then in the caller we're going to bounce back out
397 * to the index view instead of the view of the individual entry.
398 * So there is some dead code around for now.)
400 * Then, in the view_abook_entry function we check the value
401 * of this field on scrolltool's return and if it is one of
402 * the two special values below view_abook_entry resets the
403 * current line if necessary, flushes the display cache (in
404 * ab_resize) and loops back and starts over, effectively
405 * putting us back in the view screen but with updated
406 * contents. A side effect is that the screen above that (the
407 * abook index) will also have been flushed and corrected by
408 * the ab_resize.
410 pab = &as.adrbks[cur_addr_book()];
411 if(pab && pab->access == ReadOnly){
412 /* TRANSLATORS: Address book can be viewed but not changed */
413 q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
414 rv = 0;
415 break;
418 if(adrbk_check_all_validity_now()){
419 if(resync_screen(pab, AddrBookScreen, 0)){
420 q_status_message(SM_ORDER | SM_DING, 3, 4,
421 /* TRANSLATORS: The address book was changed by some other
422 process. The user is being told that their change (their
423 Update) has been canceled and they should try again. */
424 _("Address book changed. Update cancelled. Try again."));
425 ps_global->mangled_screen = 1;
426 break;
430 if(pab && pab->access == ReadOnly){
431 q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
432 rv = 0;
433 break;
437 * Arguments all come from globals, not from the arguments to
438 * process_abook_view_cmd. It would be a little cleaner if the
439 * information was contained in att, I suppose.
441 if(is_addr(as.cur_row+as.top_ent)){
442 AdrBk_Entry *abe, *abe_copy;
443 a_c_arg_t entry;
444 int warped = 0;
446 dl = dlist(as.top_ent+as.cur_row);
447 entry = dl->elnum;
448 abe = adrbk_get_ae(pab->address_book, entry);
449 abe_copy = copy_ae(abe);
450 dprint((9,"Calling edit_entry to edit from view\n"));
451 /* TRANSLATORS: update as in update an address book entry */
452 edit_entry(pab->address_book, abe_copy, entry,
453 abe->tag, 0, &warped, _("update"));
455 * The ABOOK_EDITED case doesn't mean that we necessarily
456 * changed something, just that we might have but we know
457 * we didn't change the sort order (causing the warp).
459 sparms->proc.data.i = warped
460 ? VIEW_ABOOK_WARPED : VIEW_ABOOK_EDITED;
462 free_ae(&abe_copy);
463 rv = 1; /* causes scrolltool to return */
465 else{
466 q_status_message(SM_ORDER, 0, 4,
467 "Something wrong, entry not updateable");
468 rv = 0;
469 break;
472 break;
474 case MC_SAVE :
475 pab = &as.adrbks[cur_addr_book()];
477 * Arguments all come from globals, not from the arguments to
478 * process_abook_view_cmd. It would be a little cleaner if the
479 * information was contained in att, I suppose.
481 (void)ab_save(ps_global, pab ? pab->address_book : NULL,
482 as.top_ent+as.cur_row, -FOOTER_ROWS(ps_global), 0);
483 rv = 1;
484 break;
486 case MC_COMPOSE :
487 (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 0);
488 rv = 1;
489 break;
491 case MC_ROLE :
492 (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 1);
493 rv = 1;
494 break;
496 case MC_FORWARD :
497 rv = 1;
498 /* TRANSLATORS: A question with two choices for the answer. Forward
499 as text means to include the text of the message being forwarded
500 in the new message. Forward as a Vcard attachment means to
501 attach it to the message in a special format so it is recognizable
502 as a Vcard. */
503 i = radio_buttons(_("Forward as text or forward as Vcard attachment ? "),
504 -FOOTER_ROWS(ps_global), text_or_vcard, 't', 'x',
505 h_ab_text_or_vcard, RB_NORM);
506 switch(i){
507 case 'x':
508 q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
509 rv = 0;
510 break;
512 case 't':
513 forward_text(ps_global, sparms->text.text, sparms->text.src);
514 break;
516 case 'v':
517 (void)ab_forward(ps_global, as.top_ent+as.cur_row, 0);
518 break;
520 default:
521 q_status_message(SM_ORDER, 3, 3,
522 "can't happen in process_abook_view_cmd");
523 break;
526 break;
528 default:
529 alpine_panic("Unexpected command in process_abook_view_cmd");
530 break;
533 return(rv);
538 * Give expanded view of this address entry.
539 * Call scrolltool to do the work.
541 * Args: headents -- The headerentry array from pico.
542 * s -- Unused here.
544 * Returns -- Always 0.
547 expand_addrs_for_pico(struct headerentry *headents, char ***s)
549 BuildTo bldto;
550 STORE_S *store;
551 char *error = NULL, *addr = NULL, *fullname = NULL, *address = NULL;
552 SAVE_STATE_S state;
553 ADDRESS *adrlist = NULL, *a;
554 int j, address_index = -1, fullname_index = -1, no_a_fld = 0;
555 void (*redraw)(void) = ps_global->redrawer;
557 char *tmp, *tmp2, *tmp3;
558 SCROLL_S sargs;
559 AdrBk_Entry abe;
560 char fakeaddrpmt[500];
561 int abook_indent;
563 dprint((5, "- expand_addrs_for_pico -\n"));
565 abook_indent = utf8_width(_("Nickname")) + 2;
566 utf8_snprintf(fakeaddrpmt, sizeof(fakeaddrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Address"));
568 if(s)
569 *s = NULL;
571 ps_global->redrawer = NULL;
572 fix_windsize(ps_global);
574 ab_nesting_level++;
575 save_state(&state);
577 for(j=0;
578 headents[j].name != NULL && (address_index < 0 || fullname_index < 0);
579 j++){
580 if(!strncmp(headents[j].name, "Address", 7) || !strncmp(headents[j].name, _("Address"), strlen(_("Address"))))
581 address_index = j;
582 else if(!strncmp(headents[j].name, "Fullname", 8) || !strncmp(headents[j].name, _("Fullname"), strlen(_("Fullname"))))
583 fullname_index = j;
586 if(address_index >= 0)
587 address = *headents[address_index].realaddr;
588 else{
589 address_index = 1000; /* a big number */
590 no_a_fld++;
593 if(fullname_index >= 0)
594 fullname = adrbk_formatname(*headents[fullname_index].realaddr,
595 NULL, NULL);
597 memset(&abe, 0, sizeof(abe));
598 if(fullname)
599 abe.fullname = cpystr(fullname);
601 if(address){
602 char *tmp_a_string;
604 tmp_a_string = cpystr(address);
605 rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
606 fs_give((void **)&tmp_a_string);
607 if(adrlist && adrlist->next){
608 int cnt = 0;
610 abe.tag = List;
611 for(a = adrlist; a; a = a->next)
612 cnt++;
614 abe.addr.list = (char **)fs_get((cnt+1) * sizeof(char *));
615 cnt = 0;
616 for(a = adrlist; a; a = a->next){
617 if(a->host && a->host[0] == '@')
618 abe.addr.list[cnt++] = cpystr(a->mailbox);
619 else if(a->host && a->host[0] && a->mailbox && a->mailbox[0])
620 abe.addr.list[cnt++] =
621 cpystr(simple_addr_string(a, tmp_20k_buf,
622 SIZEOF_20KBUF));
625 abe.addr.list[cnt] = NULL;
627 else{
628 abe.tag = Single;
629 abe.addr.addr = address;
632 if(adrlist)
633 mail_free_address(&adrlist);
635 bldto.type = Abe;
636 bldto.arg.abe = &abe;
637 our_build_address(bldto, &addr, &error, NULL, NULL);
638 if(error){
639 q_status_message1(SM_ORDER, 3, 4, "%s", error);
640 fs_give((void **)&error);
643 if(addr){
644 tmp_a_string = cpystr(addr);
645 rfc822_parse_adrlist(&adrlist, tmp_a_string, ps_global->maildomain);
646 fs_give((void **)&tmp_a_string);
649 #ifdef ENABLE_LDAP
650 else if(no_a_fld && expander_address){
651 WP_ERR_S wp_err;
653 memset(&wp_err, 0, sizeof(wp_err));
654 adrlist = wp_lookups(expander_address, &wp_err, 0);
655 if(fullname && *fullname){
656 if(adrlist){
657 if(adrlist->personal)
658 fs_give((void **)&adrlist->personal);
660 adrlist->personal = cpystr(fullname);
664 if(wp_err.error){
665 q_status_message1(SM_ORDER, 3, 4, "%s", wp_err.error);
666 fs_give((void **)&wp_err.error);
669 #endif
671 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
672 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
673 restore_state(&state);
674 return(0);
677 for(j = 0; j < address_index && headents[j].name != NULL; j++){
678 if((tmp = fold(*headents[j].realaddr,
679 ps_global->ttyo->screen_cols,
680 ps_global->ttyo->screen_cols,
681 headents[j].prompt,
682 repeat_char(headents[j].prwid, SPACE), FLD_NONE)) != NULL){
683 so_puts(store, tmp);
684 fs_give((void **)&tmp);
689 * There is an assumption that Addresses is the last field.
691 tmp3 = cpystr(repeat_char(headents[0].prwid + 2, SPACE));
692 if(adrlist){
693 for(a = adrlist; a; a = a->next){
694 ADDRESS *next_addr;
695 char *bufp;
696 size_t len;
698 next_addr = a->next;
699 a->next = NULL;
700 len = est_size(a);
701 bufp = (char *) fs_get(len * sizeof(char));
702 (void) addr_string(a, bufp, len);
703 a->next = next_addr;
706 * Another assumption, all the prwids are the same.
708 if((tmp = fold(bufp,
709 ps_global->ttyo->screen_cols,
710 ps_global->ttyo->screen_cols,
711 (a == adrlist) ? (no_a_fld
712 ? fakeaddrpmt
713 : headents[address_index].prompt)
714 : tmp3+2,
715 tmp3, FLD_NONE)) != NULL){
716 so_puts(store, tmp);
717 fs_give((void **) &tmp);
720 fs_give((void **) &bufp);
723 else{
724 tmp2 = NULL;
725 if((tmp = fold(tmp2=cpystr("<none>"),
726 ps_global->ttyo->screen_cols,
727 ps_global->ttyo->screen_cols,
728 no_a_fld ? fakeaddrpmt
729 : headents[address_index].prompt,
730 tmp3, FLD_NONE)) != NULL){
731 so_puts(store, tmp);
732 fs_give((void **) &tmp);
735 if(tmp2)
736 fs_give((void **)&tmp2);
739 if(tmp3)
740 fs_give((void **)&tmp3);
742 memset(&sargs, 0, sizeof(SCROLL_S));
743 sargs.text.text = so_text(store);
744 sargs.text.src = CharStar;
745 sargs.text.desc = _("expanded entry");
746 /* TRANSLATORS: screen title for viewing an address book entry in Rich
747 view mode. Rich just means it is expanded to include everything. */
748 sargs.bar.title = _("ADDRESS BOOK (Rich View)");
749 sargs.bar.style = TextPercent;
750 sargs.keys.menu = &abook_text_km;
751 setbitmap(sargs.keys.bitmap);
753 scrolltool(&sargs);
754 so_give(&store);
756 restore_state(&state);
757 ab_nesting_level--;
759 if(addr)
760 fs_give((void **)&addr);
762 if(adrlist)
763 mail_free_address(&adrlist);
765 if(fullname)
766 fs_give((void **)&fullname);
768 if(abe.fullname)
769 fs_give((void **)&abe.fullname);
771 if(abe.tag == List && abe.addr.list)
772 free_list_array(&(abe.addr.list));
774 ps_global->redrawer = redraw;
776 return(0);
781 * Callback from TakeAddr editing screen to see message that was being
782 * viewed. Call scrolltool to do the work.
784 char *
785 view_message_for_pico(char **error)
787 STORE_S *store;
788 gf_io_t pc;
789 void (*redraw)(void) = ps_global->redrawer;
790 SourceType src = CharStar;
791 SCROLL_S sargs;
793 dprint((5, "- view_message_for_pico -\n"));
795 ps_global->redrawer = NULL;
796 fix_windsize(ps_global);
798 #ifdef DOS
799 src = TmpFileStar;
800 #endif
802 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
803 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
804 return(NULL);
807 gf_set_so_writec(&pc, store);
809 format_message(msgno_for_pico_callback, env_for_pico_callback,
810 body_for_pico_callback, NULL, FM_NEW_MESS | FM_DISPLAY, pc);
812 gf_clear_so_writec(store);
814 memset(&sargs, 0, sizeof(SCROLL_S));
815 sargs.text.text = so_text(store);
816 sargs.text.src = src;
817 sargs.text.desc = _("expanded entry");
818 /* TRANSLATORS: this is a screen title */
819 sargs.bar.title = _("MESSAGE TEXT");
820 sargs.bar.style = TextPercent;
821 sargs.keys.menu = &abook_text_km;
822 setbitmap(sargs.keys.bitmap);
824 scrolltool(&sargs);
826 so_give(&store);
828 ps_global->redrawer = redraw;
830 return(NULL);
835 prompt::name::help::prwid::maxlen::realaddr::
836 builder::affected_entry::next_affected::selector::key_label::fileedit::nickcmpl
837 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
838 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
840 static struct headerentry headents_for_edit[]={
841 {"Nickname : ", N_("Nickname"), h_composer_abook_nick, 12, 0, NULL,
842 /* TRANSLATORS: To AddrBk is a command that takes the user to
843 the address book screen to select an entry from there. */
844 verify_nick, NULL, NULL, addr_book_nick_for_edit, N_("To AddrBk"), NULL, abook_nickname_complete,
845 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
846 {"Fullname : ", N_("Fullname"), h_composer_abook_full, 12, 0, NULL,
847 NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
848 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
849 /* TRANSLATORS: a File Copy is a copy of a sent message saved in a regular
850 file on the computer's disk */
851 {"Fcc : ", N_("FileCopy"), h_composer_abook_fcc, 12, 0, NULL,
852 /* TRANSLATORS: To Folders */
853 NULL, NULL, NULL, folders_for_fcc, N_("To Fldrs"), NULL, NULL,
854 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
855 {"Comment : ", N_("Comment"), h_composer_abook_comment, 12, 0, NULL,
856 NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
857 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
858 {"Addresses : ", N_("Addresses"), h_composer_abook_addrs, 12, 0, NULL,
859 verify_addr, NULL, NULL, addr_book_change_list, N_("To AddrBk"), NULL, abook_nickname_complete,
860 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
861 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
862 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
864 #define NNN_NICK 0
865 #define NNN_FULL 1
866 #define NNN_FCC 2
867 #define NNN_COMMENT 3
868 #define NNN_ADDR 4
869 #define NNN_END 5
871 static char *nick_saved_for_pico_check;
872 static AdrBk *abook_saved_for_pico_check;
875 * Args: abook -- Address book handle
876 * abe -- AdrBk_Entry of old entry to work on. If NULL, this will
877 * be a new entry. This has to be a pointer to a copy of
878 * an abe that won't go away until we finish this function.
879 * In other words, don't pass in a pointer to an abe in
880 * the cache, copy it first. The tag on this abe is only
881 * used to decide whether to read abe->addr.list or
882 * abe->addr.addr, not to determine what the final result
883 * will be. That's determined solely by how many addresses
884 * there are after the user edits.
885 * entry -- The entry number of the old entry that we will be changing.
886 * old_tag -- If we're changing an old entry, then this is the tag of
887 * that old entry.
888 * readonly -- Call pico with readonly flag
889 * warped -- We warped to a new part of the addrbook
890 * (We also overload warped in a couple places and use it's
891 * being set as an indicator of whether we are Taking or
892 * not. It will be NULL if we are Taking.)
894 void
895 edit_entry(AdrBk *abook, AdrBk_Entry *abe, a_c_arg_t entry, Tag old_tag, int readonly, int *warped, char *cmd)
897 AdrBk_Entry local_abe;
898 struct headerentry *he;
899 PICO pbf;
900 STORE_S *msgso;
901 adrbk_cntr_t old_entry_num, new_entry_num = NO_NEXT;
902 int rc = 0, resort_happened = 0, list_changed = 0, which_addrbook;
903 int editor_result, i = 0, add, n_end, ldap = 0;
904 char *nick, *full, *fcc, *comment, *fname, *pp;
905 char **orig_addrarray = NULL;
906 char **new_addrarray = NULL;
907 char *comma_sep_addr = NULL;
908 char **p, **q;
909 Tag new_tag;
910 char titlebar[40];
911 char nickpmt[100], fullpmt[100], fccpmt[100], cmtpmt[100], addrpmt[100];
912 int abook_indent;
913 long length;
914 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
916 dprint((2, "- edit_entry -\n"));
918 old_entry_num = (adrbk_cntr_t) entry;
919 save_state(&state);
920 abook_saved_for_pico_check = abook;
922 add = (abe == NULL); /* doing add or change? */
923 if(add){
924 local_abe.nickname = "";
925 local_abe.fullname = "";
926 local_abe.fcc = "";
927 local_abe.extra = "";
928 local_abe.addr.addr = "";
929 local_abe.tag = NotSet;
930 abe = &local_abe;
931 old_entry_num = NO_NEXT;
934 new_tag = abe->tag;
936 #ifdef ENABLE_LDAP
937 expander_address = NULL;
938 if(abe->tag == Single &&
939 abe->addr.addr &&
940 !strncmp(abe->addr.addr,QRUN_LDAP,LEN_QRL)){
941 ldap = 1;
942 expander_address = cpystr(abe->addr.addr);
943 removing_double_quotes(expander_address);
945 else if(abe->tag == List &&
946 abe->addr.list &&
947 abe->addr.list[0] &&
948 !abe->addr.list[1] &&
949 !strncmp(abe->addr.list[0],QRUN_LDAP,LEN_QRL)){
950 ldap = 2;
951 expander_address = cpystr(abe->addr.list[0]);
952 removing_double_quotes(expander_address);
954 #endif
956 standard_picobuf_setup(&pbf);
957 pbf.exittest = pico_sendexit_for_adrbk;
958 pbf.canceltest = warped ? pico_cancel_for_adrbk_edit
959 : pico_cancel_for_adrbk_take;
960 pbf.expander = expand_addrs_for_pico;
961 pbf.ctrlr_label = _("RichView");
962 /* xgettext: c-format */
963 if(readonly)
964 /* TRANSLATORS: screen titles */
965 snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (View)"));
966 else
967 snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (%c%s)"),
968 islower((unsigned char)(*cmd))
969 ? toupper((unsigned char)*cmd)
970 : *cmd, (cmd+1));
972 pbf.pine_anchor = set_titlebar(titlebar,
973 ps_global->mail_stream,
974 ps_global->context_current,
975 ps_global->cur_folder,ps_global->msgmap,
976 0, FolderName, 0, 0, NULL);
977 pbf.pine_flags |= P_NOBODY;
978 if(readonly)
979 pbf.pine_flags |= P_VIEW;
981 /* An informational message */
982 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
983 pbf.msgtext = (void *)so_text(msgso);
985 * It's nice if we can make it so these lines make sense even if
986 * they don't all make it on the screen, because the user can't
987 * scroll down to see them. So just make each line a whole sentence
988 * that doesn't need the others below it to make sense.
990 if(add){
991 so_puts(msgso,
993 * TRANSLATORS: The following lines go together to form a screen of
994 * explanation about how to edit an address book entry.
996 _("\n Fill in the fields. It is ok to leave fields blank."));
997 so_puts(msgso,
998 _("\n To form a list, just enter multiple comma-separated addresses."));
1000 else{
1001 so_puts(msgso,
1002 /* TRANSLATORS: Same here, but a different version of the screen. */
1003 _("\n Change any of the fields. It is ok to leave fields blank."));
1004 if(ldap)
1005 so_puts(msgso,
1006 _("\n Since this entry does a directory lookup you may not edit the address field."));
1007 else
1008 so_puts(msgso,
1009 _("\n Additional comma-separated addresses may be entered in the address field."));
1012 so_puts(msgso,
1013 _("\n Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."));
1014 so_puts(msgso,
1015 _("\n If you want to use quotation marks inside the Fullname field, it is best"));
1016 so_puts(msgso,
1017 _("\n to use single quotation marks; for example: George 'Husky' Washington."));
1020 he = (struct headerentry *) fs_get((NNN_END+1) * sizeof(struct headerentry));
1021 memset((void *)he, 0, (NNN_END+1) * sizeof(struct headerentry));
1022 pbf.headents = he;
1024 abook_indent = utf8_width(_("Nickname")) + 2;
1026 /* make a copy of each field */
1027 nick = cpystr(abe->nickname ? abe->nickname : "");
1028 removing_leading_and_trailing_white_space(nick);
1029 nick_saved_for_pico_check = cpystr(nick);
1030 he[NNN_NICK] = headents_for_edit[NNN_NICK];
1031 he[NNN_NICK].realaddr = &nick;
1032 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
1033 he[NNN_NICK].prompt = nickpmt;
1034 he[NNN_NICK].prwid = abook_indent+2;
1035 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
1036 he[NNN_NICK].nickcmpl = NULL;
1038 full = cpystr(abe->fullname ? abe->fullname : "");
1039 removing_leading_and_trailing_white_space(full);
1040 he[NNN_FULL] = headents_for_edit[NNN_FULL];
1041 he[NNN_FULL].realaddr = &full;
1042 utf8_snprintf(fullpmt, sizeof(fullpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
1043 he[NNN_FULL].prompt = fullpmt;
1044 he[NNN_FULL].prwid = abook_indent+2;
1046 fcc = cpystr(abe->fcc ? abe->fcc : "");
1047 removing_leading_and_trailing_white_space(fcc);
1048 he[NNN_FCC] = headents_for_edit[NNN_FCC];
1049 he[NNN_FCC].realaddr = &fcc;
1050 utf8_snprintf(fccpmt, sizeof(fccpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
1051 he[NNN_FCC].prompt = fccpmt;
1052 he[NNN_FCC].prwid = abook_indent+2;
1054 comment = cpystr(abe->extra ? abe->extra : "");
1055 removing_leading_and_trailing_white_space(comment);
1056 he[NNN_COMMENT] = headents_for_edit[NNN_COMMENT];
1057 he[NNN_COMMENT].realaddr = &comment;
1058 utf8_snprintf(cmtpmt, sizeof(cmtpmt), "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
1059 he[NNN_COMMENT].prompt = cmtpmt;
1060 he[NNN_COMMENT].prwid = abook_indent+2;
1062 n_end = NNN_END;
1063 if(ldap)
1064 n_end--;
1066 if(!ldap){
1067 he[NNN_ADDR] = headents_for_edit[NNN_ADDR];
1068 he[NNN_ADDR].realaddr = &comma_sep_addr;
1069 utf8_snprintf(addrpmt, sizeof(addrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
1070 he[NNN_ADDR].prompt = addrpmt;
1071 he[NNN_ADDR].prwid = abook_indent+2;
1072 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
1073 he[NNN_NICK].nickcmpl = NULL;
1075 if(abe->tag == Single){
1076 if(abe->addr.addr){
1077 orig_addrarray = (char **) fs_get(2 * sizeof(char *));
1078 orig_addrarray[0] = cpystr(abe->addr.addr);
1079 orig_addrarray[1] = NULL;
1082 else if(abe->tag == List){
1083 if(listmem_count_from_abe(abe) > 0){
1084 orig_addrarray = (char **) fs_get(
1085 (size_t)(listmem_count_from_abe(abe) + 1)
1086 * sizeof(char *));
1087 for(q = orig_addrarray, p = abe->addr.list; p && *p; p++, q++)
1088 *q = cpystr(*p);
1090 *q = NULL;
1094 /* figure out how large a string we need to allocate */
1095 length = 0L;
1096 for(p = orig_addrarray; p && *p; p++)
1097 length += (strlen(*p) + 2);
1099 if(length)
1100 length -= 2L;
1102 pp = comma_sep_addr = (char *) fs_get((size_t)(length+1L) * sizeof(char));
1103 *pp = '\0';
1104 for(p = orig_addrarray; p && *p; p++){
1105 sstrncpy(&pp, *p, length-(pp-comma_sep_addr));
1106 if(*(p+1))
1107 sstrncpy(&pp, ", ", length-(pp-comma_sep_addr));
1110 comma_sep_addr[length] = '\0';
1112 if(verify_addr(comma_sep_addr, NULL, NULL, NULL, NULL) < 0)
1113 he[NNN_ADDR].start_here = 1;
1116 he[n_end] = headents_for_edit[NNN_END];
1117 for(i = 0; i < n_end; i++){
1118 /* no callbacks in some cases */
1119 if(readonly || ((i == NNN_FULL || i == NNN_COMMENT) && !env_for_pico_callback)){
1120 he[i].selector = NULL;
1121 he[i].key_label = NULL;
1124 /* no builders for readonly */
1125 if(readonly)
1126 he[i].builder = NULL;
1129 /* pass to pico and let user change them */
1130 editor_result = pico(&pbf);
1131 ps_global->mangled_screen = 1;
1132 standard_picobuf_teardown(&pbf);
1134 if(editor_result & COMP_GOTHUP)
1135 hup_signal();
1136 else{
1137 fix_windsize(ps_global);
1138 init_signals();
1141 if(editor_result & COMP_CANCEL){
1142 if(!readonly)
1143 /* TRANSLATOR: Something like
1144 Address book save cancelled */
1145 q_status_message1(SM_INFO, 0, 2, _("Address book %s cancelled"), cmd);
1147 else if(editor_result & COMP_EXIT){
1148 if(pico_usingcolor())
1149 clear_index_cache(ps_global->mail_stream, 0);
1150 removing_leading_and_trailing_white_space(nick);
1151 removing_leading_and_trailing_white_space(full);
1152 removing_leading_and_trailing_white_space(fcc);
1153 removing_leading_and_trailing_white_space(comment);
1154 removing_leading_and_trailing_white_space(comma_sep_addr);
1156 /* not needed if pico is returning UTF-8 */
1157 convert_possibly_encoded_str_to_utf8(&nick);
1158 convert_possibly_encoded_str_to_utf8(&full);
1159 convert_possibly_encoded_str_to_utf8(&fcc);
1160 convert_possibly_encoded_str_to_utf8(&comment);
1161 convert_possibly_encoded_str_to_utf8(&comma_sep_addr);
1163 /* don't allow adding null entry */
1164 if(add && !*nick && !*full && !*fcc && !*comment && !*comma_sep_addr)
1165 goto outtahere;
1168 * comma_sep_addr is now the string which has been edited
1170 if(comma_sep_addr)
1171 new_addrarray = parse_addrlist(comma_sep_addr);
1173 if(!ldap && (!new_addrarray || !new_addrarray[0]))
1174 q_status_message(SM_ORDER, 3, 5, _("Warning: entry has no addresses"));
1176 if(!new_addrarray || !new_addrarray[0] || !new_addrarray[1])
1177 new_tag = Single; /* one or zero addresses means its a Single */
1178 else
1179 new_tag = List; /* more than one addresses means its a List */
1181 if(new_tag == List && old_tag == List){
1183 * If Taking, make sure we write it even if user didn't edit
1184 * it any further.
1186 if(!warped)
1187 list_changed++;
1188 else if(he[NNN_ADDR].dirty)
1189 for(q = orig_addrarray, p = new_addrarray; p && *p && q && *q; p++, q++)
1190 if(strcmp(*p, *q) != 0){
1191 list_changed++;
1192 break;
1195 if(!list_changed && he[NNN_ADDR].dirty
1196 && ((!(p && *p) && (q && *q)) || ((p && *p) && !(q && *q))))
1197 list_changed++;
1199 if(list_changed){
1201 * need to delete old list members and add new members below
1203 rc = adrbk_listdel_all(abook, (a_c_arg_t) old_entry_num);
1205 else{
1206 /* don't need new_addrarray */
1207 free_list_array(&new_addrarray);
1210 if(comma_sep_addr)
1211 fs_give((void **) &comma_sep_addr);
1213 else if((new_tag == List && old_tag == Single)
1214 || (new_tag == Single && old_tag == List)){
1215 /* delete old entry */
1216 rc = adrbk_delete(abook, (a_c_arg_t) old_entry_num, 0, 0, 0, 0);
1217 old_entry_num = NO_NEXT;
1218 if(comma_sep_addr && new_tag == List)
1219 fs_give((void **) &comma_sep_addr);
1223 * This will be an edit in the cases where the tag didn't change
1224 * and an add in the cases where it did.
1226 if(rc == 0)
1227 rc = adrbk_add(abook,
1228 (a_c_arg_t)old_entry_num,
1229 nick,
1230 he[NNN_FULL].dirty ? full : abe->fullname,
1231 new_tag == Single ? (ldap == 1 ? abe->addr.addr :
1232 ldap == 2 ? abe->addr.list[0] :
1233 comma_sep_addr)
1234 : NULL,
1235 fcc,
1236 he[NNN_COMMENT].dirty ? comment : abe->extra,
1237 new_tag,
1238 &new_entry_num,
1239 &resort_happened,
1242 (new_tag != List || !new_addrarray));
1245 if(rc == 0 && new_tag == List && new_addrarray)
1246 rc = adrbk_nlistadd(abook, (a_c_arg_t) new_entry_num, &new_entry_num,
1247 &resort_happened, new_addrarray, 1, 0, 1);
1249 restore_state(&state);
1251 if(rc == -2 || rc == -3){
1252 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1253 _("Error updating address book: %s"),
1254 rc == -2 ? error_description(errno) : "Alpine bug");
1256 else if(rc == 0
1257 && strucmp(nick, nick_saved_for_pico_check) != 0
1258 && (editor_result & COMP_EXIT)){
1259 int added_to;
1261 for(added_to = 0; added_to < as.n_addrbk; added_to++)
1262 if(abook_saved_for_pico_check == as.adrbks[added_to].address_book)
1263 break;
1265 if(added_to >= as.n_addrbk)
1266 added_to = -1;
1268 fname = addr_lookup(nick, &which_addrbook, added_to);
1269 if(fname){
1270 q_status_message4(SM_ORDER, 5, 9,
1271 /* TRANSLATORS: The first %s is the nickname the user is
1272 trying to use that exists in another address book.
1273 The second %s is the name of the other address book.
1274 The third %s is " as " because it will say something
1275 like also exists in <name> as <description>. */
1276 _("Warning! Nickname %s also exists in \"%s\"%s%s"),
1277 nick, as.adrbks[which_addrbook].abnick,
1278 (fname && *fname) ? _(" as ") : "",
1279 (fname && *fname) ? fname : "");
1280 fs_give((void **)&fname);
1284 if(resort_happened || list_changed){
1285 DL_CACHE_S dlc_restart;
1287 dlc_restart.adrbk_num = as.cur;
1288 dlc_restart.dlcelnum = new_entry_num;
1289 switch(new_tag){
1290 case Single:
1291 dlc_restart.type = DlcSimple;
1292 break;
1294 case List:
1295 dlc_restart.type = DlcListHead;
1296 break;
1298 default:
1299 break;
1302 warp_to_dlc(&dlc_restart, 0L);
1303 if(warped)
1304 *warped = 1;
1307 outtahere:
1308 if(he)
1309 free_headents(&he);
1311 if(msgso)
1312 so_give(&msgso);
1314 if(nick)
1315 fs_give((void **)&nick);
1316 if(full)
1317 fs_give((void **)&full);
1318 if(fcc)
1319 fs_give((void **)&fcc);
1320 if(comment)
1321 fs_give((void **)&comment);
1323 if(comma_sep_addr)
1324 fs_give((void **)&comma_sep_addr);
1325 if(nick_saved_for_pico_check)
1326 fs_give((void **)&nick_saved_for_pico_check);
1328 free_list_array(&orig_addrarray);
1329 free_list_array(&new_addrarray);
1330 #ifdef ENABLE_LDAP
1331 if(expander_address)
1332 fs_give((void **) &expander_address);
1333 #endif
1337 /*ARGSUSED*/
1339 verify_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
1341 char *tmp;
1343 dprint((7, "- verify_nick - (%s)\n", given ? given : "nul"));
1345 tmp = cpystr(given);
1346 removing_leading_and_trailing_white_space(tmp);
1348 if(nickname_check(tmp, error)){
1349 fs_give((void **)&tmp);
1350 if(mangled){
1351 if(ps_global->mangled_screen)
1352 *mangled |= BUILDER_SCREEN_MANGLED;
1353 else if(ps_global->mangled_footer)
1354 *mangled |= BUILDER_FOOTER_MANGLED;
1357 return -2;
1360 if(ps_global->remote_abook_validity > 0 &&
1361 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
1362 *mangled |= BUILDER_SCREEN_MANGLED;
1364 ab_nesting_level++;
1365 if(strucmp(tmp, nick_saved_for_pico_check) != 0
1366 && adrbk_lookup_by_nick(abook_saved_for_pico_check,
1367 tmp, (adrbk_cntr_t *)NULL)){
1368 if(error){
1369 char buf[MAX_NICKNAME + 80];
1371 /* TRANSLATORS: The %s is the nickname of an entry that is already
1372 in the address book */
1373 snprintf(buf, sizeof(buf), _("\"%s\" already in address book."), tmp);
1374 buf[sizeof(buf)-1] = '\0';
1375 *error = cpystr(buf);
1378 ab_nesting_level--;
1379 fs_give((void **)&tmp);
1380 if(mangled){
1381 if(ps_global->mangled_screen)
1382 *mangled |= BUILDER_SCREEN_MANGLED;
1383 else if(ps_global->mangled_footer)
1384 *mangled |= BUILDER_FOOTER_MANGLED;
1387 return -2;
1390 ab_nesting_level--;
1391 if(expanded)
1392 *expanded = tmp;
1393 else
1394 fs_give((void **)&tmp);
1396 /* This is so pico will erase any old message */
1397 if(error)
1398 *error = cpystr("");
1400 if(mangled){
1401 if(ps_global->mangled_screen)
1402 *mangled |= BUILDER_SCREEN_MANGLED;
1403 else if(ps_global->mangled_footer)
1404 *mangled |= BUILDER_FOOTER_MANGLED;
1407 return 0;
1412 * Args: to -- the passed in line to parse
1413 * full_to -- Address of a pointer to return the full address in.
1414 * This will be allocated here and freed by the caller.
1415 * However, this function is just going to copy "to".
1416 * (special case for the route-addr-hack in build_address_int)
1417 * We're just looking for the error messages.
1418 * error -- Address of a pointer to return an error message in.
1419 * This will be allocated here and freed by the caller.
1420 * fcc -- This should be passed in NULL.
1421 * This builder doesn't support affected_entry's.
1423 * Result: 0 is returned if address was OK,
1424 * -2 if address wasn't OK.
1426 * Side effect: Can flush addrbook entry cache entries so they need to be
1427 * re-fetched afterwords.
1430 verify_addr(char *to, char **full_to, char **error, BUILDER_ARG *fcc, int *mangled)
1432 register char *p;
1433 int ret_val;
1434 BuildTo bldto;
1435 jmp_buf save_jmp_buf;
1436 int *save_nesting_level;
1438 dprint((7, "- verify_addr - (%s)\n", to ? to : "nul"));
1440 /* check to see if to string is empty to avoid work */
1441 for(p = to; p && *p && isspace((unsigned char)(*p)); p++)
1442 ;/* do nothing */
1444 if(!p || !*p){
1445 if(full_to)
1446 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
1448 return 0;
1451 if(full_to != NULL)
1452 *full_to = (char *)NULL;
1454 if(error != NULL)
1455 *error = (char *)NULL;
1458 * If we end up jumping back here because somebody else changed one of
1459 * our addrbooks out from underneath us, we may well leak some memory.
1460 * That's probably ok since this will be very rare.
1462 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
1463 save_nesting_level = cpyint(ab_nesting_level);
1464 if(setjmp(addrbook_changed_unexpectedly)){
1465 if(full_to && *full_to)
1466 fs_give((void **)full_to);
1468 /* TRANSLATORS: This is sort of an error, something unexpected has
1469 happened and alpine is re-initializing the address book. */
1470 q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
1471 dprint((1,
1472 "RESETTING address book... verify_addr(%s)!\n", to ? to : "?"));
1473 addrbook_reset();
1474 ab_nesting_level = *save_nesting_level;
1477 bldto.type = Str;
1478 bldto.arg.str = to;
1480 if(ps_global->remote_abook_validity > 0 &&
1481 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
1482 *mangled |= BUILDER_SCREEN_MANGLED;
1484 ab_nesting_level++;
1486 ret_val = build_address_internal(bldto, full_to, error, NULL, NULL, NULL,
1487 save_and_restore, 1, mangled);
1489 ab_nesting_level--;
1490 if(save_nesting_level)
1491 fs_give((void **)&save_nesting_level);
1493 if(full_to && *full_to && ret_val >= 0)
1494 removing_leading_and_trailing_white_space(*full_to);
1496 /* This is so pico will erase the old message */
1497 if(error != NULL && *error == NULL)
1498 *error = cpystr("");
1500 if(ret_val < 0)
1501 ret_val = -2; /* cause pico to stay on same header line */
1503 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
1504 return(ret_val);
1509 * Call back for pico to prompt the user for exit confirmation
1511 * Returns: either NULL if the user accepts exit, or string containing
1512 * reason why the user declined.
1515 pico_sendexit_for_adrbk(struct headerentry *he, void (*redraw_pico)(void),
1516 int allow_flowed, char **result)
1518 char *rstr = NULL;
1519 void (*redraw)(void) = ps_global->redrawer;
1521 ps_global->redrawer = redraw_pico;
1522 fix_windsize(ps_global);
1524 /* TRANSLATORS: A question */
1525 switch(want_to(_("Exit and save changes "), 'y', 0, NO_HELP, WT_NORM)){
1526 case 'y':
1527 break;
1529 case 'n':
1530 rstr = _("Use ^C to abandon changes you've made");
1531 break;
1534 if(result)
1535 *result = rstr;
1537 ps_global->redrawer = redraw;
1538 return((rstr == NULL) ? 0 : 1);
1543 * Call back for pico to prompt the user for exit confirmation
1545 * Returns: either NULL if the user accepts exit, or string containing
1546 * reason why the user declined.
1548 char *
1549 pico_cancelexit_for_adrbk(char *word, void (*redraw_pico)(void))
1551 char prompt[90];
1552 char *rstr = NULL;
1553 void (*redraw)(void) = ps_global->redrawer;
1555 /* TRANSLATORS: A question. The %s is a noun describing what is being cancelled. */
1556 snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), word);
1557 ps_global->redrawer = redraw_pico;
1558 fix_windsize(ps_global);
1560 switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
1561 case 'y':
1562 rstr = "";
1563 break;
1565 case 'n':
1566 case 'x':
1567 break;
1570 ps_global->redrawer = redraw;
1571 return(rstr);
1575 char *
1576 pico_cancel_for_adrbk_take(void (*redraw_pico)(void))
1578 return(pico_cancelexit_for_adrbk(_("take"), redraw_pico));
1582 char *
1583 pico_cancel_for_adrbk_edit(void (*redraw_pico)(void))
1585 return(pico_cancelexit_for_adrbk(_("changes"), redraw_pico));
1590 prompt::name::help::prwid::maxlen::realaddr::
1591 builder::affected_entry::next_affected::selector::key_label::fileedit::
1592 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
1593 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
1595 static struct headerentry headents_for_add[]={
1596 {"Server Name : ", N_("Server"), h_composer_abook_add_server, 14, 0, NULL,
1597 verify_server_name, NULL, NULL, NULL, NULL, NULL, NULL,
1598 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1599 {"Folder Name : ", N_("Folder"), h_composer_abook_add_folder, 14, 0, NULL,
1600 verify_folder_name, NULL, NULL, NULL, NULL, NULL, NULL,
1601 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1602 {"NickName : ", N_("Nickname"), h_composer_abook_add_nick, 14, 0, NULL,
1603 verify_abook_nick, NULL, NULL, NULL, NULL, NULL, NULL,
1604 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1605 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1606 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
1608 #define NN_SERVER 0
1609 #define NN_FOLDER 1
1610 #define NN_NICK 2
1611 #define NN_END 3
1614 * Args: global -- Add a global address book, not personal.
1615 * add_after_this -- This is the addrbook number which should come
1616 * right before the new addrbook we're adding, if
1617 * that makes sense. If this is -1, append to end
1618 * of the list.
1620 * Returns: addrbook number of new addrbook, or
1621 * -1, no addrbook added
1624 ab_add_abook(int global, int add_after_this)
1626 int ret;
1628 dprint((2, "- ab_add_abook -\n"));
1630 ret = ab_modify_abook_list(0, global, add_after_this, NULL, NULL, NULL);
1632 if(ret >= 0)
1633 q_status_message(SM_ORDER, 0, 3,
1634 _("New address book added. Use \"$\" to adjust order"));
1636 return(ret);
1641 * Args: global -- Add a global address book, not personal.
1642 * abook_num -- Abook num of the entry we are editing.
1643 * serv -- Default server.
1644 * folder -- Default folder.
1645 * nick -- Default nickname.
1647 * Returns: abook_num if successful,
1648 * -1, if not
1651 ab_edit_abook(int global, int abook_num, char *serv, char *folder, char *nick)
1653 dprint((2, "- ab_edit_abook -\n"));
1655 return(ab_modify_abook_list(1, global, abook_num, serv, folder, nick));
1658 static int the_one_were_editing;
1662 * Args: edit -- Edit existing entry
1663 * global -- Add a global address book, not personal.
1664 * abook_num -- This is the addrbook number which should come
1665 * right before the new addrbook we're adding, if
1666 * that makes sense. If this is -1, append to end
1667 * of the list.
1668 * If we are editing instead of adding, this is
1669 * the abook number of the entry we are editing.
1670 * def_serv -- Default server.
1671 * def_fold -- Default folder.
1672 * def_nick -- Default nickname.
1674 * Returns: addrbook number of new addrbook, or
1675 * -1, no addrbook added
1678 ab_modify_abook_list(int edit, int global, int abook_num, char *def_serv, char *def_fold, char *def_nick)
1680 struct headerentry *he;
1681 PICO pbf;
1682 STORE_S *msgso;
1683 int editor_result, i, how_many_in_list = 0, new_abook_num, num_in_list;
1684 int ret = 0;
1685 char *server, *folder, *nickname;
1686 char *new_item = NULL;
1687 EditWhich ew;
1688 AccessType remember_access_result;
1689 PerAddrBook *pab;
1690 char titlebar[100];
1691 char **list, **new_list = NULL;
1692 char tmp[1000+MAXFOLDER];
1693 int abook_indent;
1694 char servpmt[100], foldpmt[100], nickpmt[100];
1695 struct variable *vars = ps_global->vars;
1697 dprint((2, "- ab_modify_abook_list -\n"));
1699 if(ps_global->readonly_pinerc){
1700 if(edit)
1701 /* TRANSLATORS: Change was the name of the command the user was
1702 trying to perform. It is what was cancelled. */
1703 q_status_message(SM_ORDER, 0, 3, _("Change cancelled: config file not changeable"));
1704 else
1705 /* TRANSLATORS: Add was the command that is being cancelled. */
1706 q_status_message(SM_ORDER, 0, 3, _("Add cancelled: config file not changeable"));
1708 return -1;
1711 ew = Main;
1713 if((global && vars[V_GLOB_ADDRBOOK].is_fixed) ||
1714 (!global && vars[V_ADDRESSBOOK].is_fixed)){
1715 if(global)
1716 /* TRANSLATORS: Operation was cancelled because the system management
1717 does not allow the changing of global address books */
1718 q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing global address books"));
1719 else
1720 q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing address books"));
1722 return -1;
1725 init_ab_if_needed();
1727 if(edit){
1728 if((!global &&
1729 (abook_num < 0 || abook_num >= as.how_many_personals)) ||
1730 (global &&
1731 (abook_num < as.how_many_personals ||
1732 abook_num >= as.n_addrbk))){
1733 dprint((1, "Programming botch in ab_modify_abook_list: global=%d abook_num=%d n_addrbk=%d\n", global, abook_num, as.n_addrbk));
1734 q_status_message(SM_ORDER, 0, 3, "Programming botch, bad abook_num");
1735 return -1;
1738 the_one_were_editing = abook_num;
1740 else
1741 the_one_were_editing = -1;
1743 standard_picobuf_setup(&pbf);
1744 pbf.exittest = pico_sendexit_for_adrbk;
1745 pbf.canceltest = pico_cancel_for_adrbk_edit;
1746 if(edit)
1747 /* TRANSLATORS: screen title */
1748 strncpy(titlebar, _("CHANGE ADDRESS BOOK"), sizeof(titlebar));
1749 else
1750 /* TRANSLATORS: screen title */
1751 strncpy(titlebar, _("ADD ADDRESS BOOK"), sizeof(titlebar));
1753 titlebar[sizeof(titlebar)-1] = '\0';
1754 pbf.pine_anchor = set_titlebar(titlebar,
1755 ps_global->mail_stream,
1756 ps_global->context_current,
1757 ps_global->cur_folder,ps_global->msgmap,
1758 0, FolderName, 0, 0, NULL);
1759 pbf.pine_flags |= P_NOBODY;
1761 /* An informational message */
1762 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
1763 int lines_avail;
1764 char *t1 =
1765 /* TRANSLATORS: The next few lines go together to explain how to add
1766 the address book entry the user is working on. */
1767 _(" To add a local address book that will be accessed *only* by Alpine running\n on this machine, leave the server field blank.");
1768 char *t2 =
1769 _(" To add an address book that will be accessed by IMAP, fill in the\n server name.");
1770 char *t3 =
1771 _(" (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.)");
1772 char *t4 =
1773 _(" In the Folder field, type the remote folder name or local file name.");
1774 char *t5 =
1775 _(" In the Nickname field, give the address book a nickname or leave it blank.");
1776 char *t6 =
1777 _(" To get help specific to an item, press ^G.");
1778 char *t7 =
1779 _(" To exit and save the configuration, press ^X. To cancel, press ^C.");
1781 pbf.msgtext = (void *)so_text(msgso);
1783 * It's nice if we can make it so these lines make sense even if
1784 * they don't all make it on the screen, because the user can't
1785 * scroll down to see them.
1787 * The 3 is the number of fields to be defined, the 1 is for a
1788 * single blank line after the field definitions.
1790 lines_avail = ps_global->ttyo->screen_rows - HEADER_ROWS(ps_global) -
1791 FOOTER_ROWS(ps_global) - 3 - 1;
1793 if(lines_avail >= 15){ /* extra blank line */
1794 so_puts(msgso, "\n");
1795 lines_avail--;
1798 if(lines_avail >= 2){
1799 so_puts(msgso, t1);
1800 lines_avail -= 2;
1803 if(lines_avail >= 5){
1804 so_puts(msgso, "\n\n");
1805 so_puts(msgso, t2);
1806 so_puts(msgso, t3);
1807 lines_avail -= 5;
1809 else if(lines_avail >= 3){
1810 so_puts(msgso, "\n\n");
1811 so_puts(msgso, t2);
1812 lines_avail -= 3;
1815 if(lines_avail >= 2){
1816 so_puts(msgso, "\n\n");
1817 so_puts(msgso, t4);
1818 lines_avail -= 2;
1821 if(lines_avail >= 2){
1822 so_puts(msgso, "\n\n");
1823 so_puts(msgso, t5);
1824 lines_avail -= 2;
1827 if(lines_avail >= 3){
1828 so_puts(msgso, "\n\n");
1829 so_puts(msgso, t6);
1830 so_puts(msgso, "\n");
1831 so_puts(msgso, t7);
1833 else if(lines_avail >= 2){
1834 so_puts(msgso, "\n\n");
1835 so_puts(msgso, t7);
1839 he = (struct headerentry *)fs_get((NN_END+1) * sizeof(struct headerentry));
1840 memset((void *)he, 0, (NN_END+1) * sizeof(struct headerentry));
1841 pbf.headents = he;
1843 abook_indent = utf8_width(_("Server Name")) + 2;
1845 /* make a copy of each field */
1846 server = cpystr(def_serv ? def_serv : "");
1847 he[NN_SERVER] = headents_for_add[NN_SERVER];
1848 he[NN_SERVER].realaddr = &server;
1849 utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", abook_indent, abook_indent, _("Server Name"));
1850 he[NN_SERVER].prompt = servpmt;
1851 he[NN_SERVER].prwid = abook_indent+2;
1853 folder = cpystr(def_fold ? def_fold : "");
1854 he[NN_FOLDER] = headents_for_add[NN_FOLDER];
1855 he[NN_FOLDER].realaddr = &folder;
1856 utf8_snprintf(foldpmt, sizeof(foldpmt), "%-*.*w: ", abook_indent, abook_indent, _("Folder Name"));
1857 he[NN_FOLDER].prompt = foldpmt;
1858 he[NN_FOLDER].prwid = abook_indent+2;
1860 nickname = cpystr(def_nick ? def_nick : "");
1861 he[NN_NICK] = headents_for_add[NN_NICK];
1862 he[NN_NICK].realaddr = &nickname;
1863 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
1864 he[NN_NICK].prompt = nickpmt;
1865 he[NN_NICK].prwid = abook_indent+2;
1867 he[NN_END] = headents_for_add[NN_END];
1869 /* pass to pico and let user change them */
1870 editor_result = pico(&pbf);
1871 standard_picobuf_teardown(&pbf);
1873 if(editor_result & COMP_GOTHUP){
1874 ret = -1;
1875 hup_signal();
1877 else{
1878 fix_windsize(ps_global);
1879 init_signals();
1882 if(editor_result & COMP_CANCEL){
1883 ret = -1;
1884 if(edit)
1885 q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
1886 else
1887 q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
1889 else if(editor_result & COMP_EXIT){
1890 if(edit &&
1891 !strcmp(server, def_serv ? def_serv : "") &&
1892 !strcmp(folder, def_fold ? def_fold : "") &&
1893 !strcmp(nickname, def_nick ? def_nick : "")){
1894 ret = -1;
1895 if(edit)
1896 q_status_message(SM_ORDER, 0, 3, _("No change: Address book change is cancelled"));
1897 else
1898 q_status_message(SM_ORDER, 0, 3, _("No change: Address book add is cancelled"));
1900 else{
1901 if(global){
1902 list = VAR_GLOB_ADDRBOOK;
1903 how_many_in_list = as.n_addrbk - as.how_many_personals;
1904 if(edit)
1905 new_abook_num = abook_num;
1906 else if(abook_num < 0)
1907 new_abook_num = as.n_addrbk;
1908 else
1909 new_abook_num = MAX(MIN(abook_num + 1, as.n_addrbk),
1910 as.how_many_personals);
1912 num_in_list = new_abook_num - as.how_many_personals;
1914 else{
1915 list = VAR_ADDRESSBOOK;
1916 how_many_in_list = as.how_many_personals;
1917 new_abook_num = abook_num;
1918 if(edit)
1919 new_abook_num = abook_num;
1920 else if(abook_num < 0)
1921 new_abook_num = as.how_many_personals;
1922 else
1923 new_abook_num = MIN(abook_num + 1, as.how_many_personals);
1925 num_in_list = new_abook_num;
1928 if(!edit)
1929 how_many_in_list++; /* for new abook */
1931 removing_leading_and_trailing_white_space(server);
1932 removing_leading_and_trailing_white_space(folder);
1933 removing_leading_and_trailing_white_space(nickname);
1935 /* convert nickname to UTF-8 */
1936 if(nickname){
1937 char *conv;
1939 conv = convert_to_utf8(nickname, NULL, 0);
1940 if(conv){
1941 fs_give((void **) &nickname);
1942 nickname = conv;
1946 /* eliminate surrounding brackets */
1947 if(server[0] == '{' && server[strlen(server)-1] == '}'){
1948 char *p;
1950 server[strlen(server)-1] = '\0';
1951 for(p = server; *p; p++)
1952 *p = *(p+1);
1955 snprintf(tmp, sizeof(tmp), "%s%s%s%.*s",
1956 *server ? "{" : "",
1957 *server ? server : "",
1958 *server ? "}" : "",
1959 MAXFOLDER, folder);
1960 tmp[sizeof(tmp)-1] = '\0';
1962 new_item = put_pair(nickname, tmp);
1964 if(!new_item || *new_item == '\0'){
1965 if(edit)
1966 q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
1967 else
1968 q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
1970 ret = -1;
1971 goto get_out;
1974 /* allocate for new list */
1975 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
1977 /* copy old list up to where we will insert new entry */
1978 for(i = 0; i < num_in_list; i++)
1979 new_list[i] = cpystr(list[i]);
1981 /* insert the new entry */
1982 new_list[i++] = cpystr(new_item);
1984 /* copy rest of old list, skip current if editing */
1985 for(; i < how_many_in_list; i++)
1986 new_list[i] = cpystr(list[edit ? i : (i-1)]);
1988 new_list[i] = NULL;
1990 /* this frees old variable contents for us */
1991 if(set_variable_list(global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK,
1992 new_list, TRUE, ew)){
1993 if(edit)
1994 q_status_message(SM_ORDER, 0, 3, _("Change cancelled: couldn't save configuration file"));
1995 else
1996 q_status_message(SM_ORDER, 0, 3, _("Add cancelled: couldn't save configuration file"));
1998 set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
1999 TRUE, FALSE);
2000 ret = -1;
2001 goto get_out;
2004 ret = new_abook_num;
2005 set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
2006 TRUE, FALSE);
2008 addrbook_reset();
2009 init_ab_if_needed();
2012 * Test to see if this definition is going to work.
2013 * Error messages are a good side effect.
2015 pab = &as.adrbks[num_in_list];
2016 init_abook(pab, NoDisplay);
2017 remember_access_result = pab->access;
2018 addrbook_reset();
2019 init_ab_if_needed();
2020 /* if we had trouble, give a clue to user (other than error msg) */
2021 if(remember_access_result == NoAccess){
2022 pab = &as.adrbks[num_in_list];
2023 pab->access = remember_access_result;
2028 get_out:
2030 if(he)
2031 free_headents(&he);
2033 if(new_list)
2034 free_list_array(&new_list);
2036 if(new_item)
2037 fs_give((void **)&new_item);
2039 if(msgso)
2040 so_give(&msgso);
2042 if(server)
2043 fs_give((void **)&server);
2044 if(folder)
2045 fs_give((void **)&folder);
2046 if(nickname)
2047 fs_give((void **)&nickname);
2049 return(ret);
2054 any_addrbooks_to_convert(struct pine *ps)
2056 PerAddrBook *pab;
2057 int i, count = 0;
2059 init_ab_if_needed();
2061 for(i = 0; i < as.n_addrbk; i++){
2062 pab = &as.adrbks[i];
2063 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2064 count++;
2067 return(count);
2072 convert_addrbooks_to_remote(struct pine *ps, char *rem_folder_prefix, size_t len)
2074 PerAddrBook *pab;
2075 int i, count = 0, ret = 0;
2077 init_ab_if_needed();
2079 for(i = 0; i < as.n_addrbk; i++){
2080 pab = &as.adrbks[i];
2081 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2082 count++;
2085 for(i = 0; ret != -1 && i < as.n_addrbk; i++){
2086 pab = &as.adrbks[i];
2087 if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
2088 ret = convert_abook_to_remote(ps, pab, rem_folder_prefix, len, count);
2091 return(ret);
2096 * Returns -1 if cancelled, -2 on error, 0 otherwise.
2099 convert_abook_to_remote(struct pine *ps, PerAddrBook *pab, char *rem_folder_prefix, size_t len, int count)
2101 #define DEF_ABOOK_NAME "remote_addrbook"
2102 char local_file[MAILTMPLEN];
2103 char rem_abook[MAILTMPLEN+3], prompt[MAILTMPLEN], old_nick[MAILTMPLEN];
2104 char *p = NULL, *err_msg = NULL, *q;
2105 char *serv = NULL, *nick = NULL, *file = NULL, *folder = NULL;
2106 int ans, rc, offset, i, abook_num = -1, flags = OE_APPEND_CURRENT;
2107 HelpType help;
2109 snprintf(old_nick, sizeof(old_nick), "%s%s%s",
2110 count > 1 ? " \"" : "",
2111 count > 1 ? pab->abnick : "",
2112 count > 1 ? "\"" : "");
2113 old_nick[sizeof(old_nick)-1] = '\0';
2115 snprintf(prompt, sizeof(prompt), _("Convert addressbook%s to a remote addrbook "), old_nick);
2116 prompt[sizeof(prompt)-1] = '\0';
2117 if((ans=want_to(prompt, 'y', 'x', h_convert_abook, WT_NORM)) != 'y')
2118 return(ans == 'n' ? 0 : -1);
2120 /* make sure the addrbook has been opened before, so that the file exists */
2121 if(pab->ostatus == Closed || pab->ostatus == HalfOpen){
2122 (void)init_addrbooks(NoDisplay, 0, 0, 0);
2123 (void)init_addrbooks(Closed, 0, 0, 0);
2126 if(pab->filename){
2127 strncpy(local_file, pab->filename, sizeof(local_file)-1);
2128 local_file[sizeof(local_file)-1] = '\0';
2129 #if defined(DOS)
2130 p = strrindex(pab->filename, '\\');
2131 #else
2132 p = strrindex(pab->filename, '/');
2133 #endif
2136 strncpy(rem_abook, rem_folder_prefix, sizeof(rem_abook)-3);
2137 if(!*rem_abook){
2138 /* TRANSLATORS: The user is defining an address book which will be
2139 stored on another server. This is called a remote addrbook and
2140 this is a question asking for the name of the server. */
2141 snprintf(prompt, sizeof(prompt), _("Name of server to contain remote addrbook : "));
2142 prompt[sizeof(prompt)-1] = '\0';
2143 help = NO_HELP;
2144 while(1){
2145 rc = optionally_enter(rem_abook, -FOOTER_ROWS(ps), 0,
2146 sizeof(rem_abook), prompt, NULL,
2147 help, &flags);
2148 removing_leading_and_trailing_white_space(rem_abook);
2149 if(rc == 3){
2150 help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
2152 else if(rc == 1){
2153 cmd_cancelled(NULL);
2154 return(-1);
2156 else if(rc == 0){
2157 if(*rem_abook){
2158 /* add brackets */
2159 offset = strlen(rem_abook);
2160 for(i = offset; i >= 0; i--)
2161 rem_abook[i+1] = rem_abook[i];
2163 rem_abook[0] = '{';
2164 rem_abook[++offset] = '}';
2165 rem_abook[++offset] = '\0';
2166 break;
2172 if(*rem_abook){
2173 if(p && count > 1)
2174 strncat(rem_abook, p+1,
2175 sizeof(rem_abook)-1-strlen(rem_abook));
2176 else
2177 strncat(rem_abook, DEF_ABOOK_NAME,
2178 sizeof(rem_abook)-1-strlen(rem_abook));
2181 if(*rem_abook){
2182 file = cpystr(rem_abook);
2183 if(pab->abnick){
2184 int len = MAX(strlen(pab->abnick),strlen("Address Book"))+8;
2185 nick = (char *)fs_get(len * sizeof(char));
2186 snprintf(nick, len, "Remote %s",
2187 (pab->abnick && !strcmp(pab->abnick, DF_ADDRESSBOOK))
2188 ? "Address Book" : pab->abnick);
2189 nick[len-1] = '\0';
2191 else
2192 nick = cpystr("Remote Address Book");
2194 if(file && *file == '{'){
2195 q = file + 1;
2196 if((p = strindex(file, '}'))){
2197 *p = '\0';
2198 serv = q;
2199 folder = p+1;
2201 else if(file)
2202 fs_give((void **)&file);
2204 else
2205 folder = file;
2208 q_status_message(SM_ORDER, 3, 5,
2209 _("You now have a chance to change the name of the remote addrbook..."));
2210 abook_num = ab_modify_abook_list(0, 0, -1, serv, folder, nick);
2212 /* extract folder name of new abook so we can copy to it */
2213 if(abook_num >= 0){
2214 char **lval;
2215 EditWhich ew = Main;
2217 lval = LVAL(&ps->vars[V_ADDRESSBOOK], ew);
2218 get_pair(lval[abook_num], &nick, &file, 0, 0);
2219 if(nick)
2220 fs_give((void **)&nick);
2222 if(file){
2223 strncpy(rem_abook, file, sizeof(rem_abook)-1);
2224 rem_abook[sizeof(rem_abook)-1] = '\0';
2225 fs_give((void **)&file);
2229 /* copy the abook */
2230 if(abook_num >= 0 && copy_abook(local_file, rem_abook, &err_msg)){
2231 if(err_msg){
2232 q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
2233 fs_give((void **)&err_msg);
2236 return(-2);
2238 else if(abook_num >= 0){ /* give user some info */
2239 STORE_S *store;
2240 SCROLL_S sargs;
2241 char *beg, *end;
2244 * Save the hostname in rem_folder_prefix so we can use it again
2245 * for other conversions if needed.
2247 if((beg = rem_abook)
2248 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
2249 && (end = strindex(rem_abook, '}'))){
2250 rem_folder_prefix[0] = '{';
2251 strncpy(rem_folder_prefix+1, beg+1, MIN(end-beg,len-2));
2252 rem_folder_prefix[MIN(end-beg,len-2)] = '}';
2253 rem_folder_prefix[MIN(end-beg+1,len-1)] = '\0';
2256 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2257 q_status_message(SM_ORDER | SM_DING, 7, 10,
2258 _("Error allocating space for message."));
2259 return(-2);
2262 /* TRANSLATORS: Several lines in a row here that go together. */
2263 snprintf(prompt, sizeof(prompt), _("\nYour addressbook%s has been copied to the"), old_nick);
2264 prompt[sizeof(prompt)-1] = '\0';
2265 so_puts(store, prompt);
2266 so_puts(store, _("\nremote folder \""));
2267 so_puts(store, rem_abook);
2268 so_puts(store, "\".");
2269 so_puts(store, _("\nA definition for this remote address book has been added to your list"));
2270 so_puts(store, _("\nof address books. The definition for the address book it was copied"));
2271 so_puts(store, _("\nfrom is also still there. You may want to remove that after you"));
2272 so_puts(store, _("\nare confident that the new address book is complete and working."));
2273 so_puts(store, _("\nUse the Setup/AddressBooks command to do that.\n"));
2275 memset(&sargs, 0, sizeof(SCROLL_S));
2276 sargs.text.text = so_text(store);
2277 sargs.text.src = CharStar;
2278 sargs.text.desc = _("Remote Address Book Information");
2279 /* TRANSLATORS: a screen title */
2280 sargs.bar.title = _("ABOUT REMOTE ABOOK");
2281 sargs.help.text = NO_HELP;
2282 sargs.help.title = NULL;
2284 scrolltool(&sargs);
2286 so_give(&store); /* free resources associated with store */
2287 ps->mangled_screen = 1;
2290 return(0);
2295 any_sigs_to_convert(struct pine *ps)
2297 char *sigfile, *litsig;
2298 long rflags;
2299 PAT_STATE pstate;
2300 PAT_S *pat;
2301 PAT_LINE_S *patline;
2303 /* first check main signature file */
2304 sigfile = ps->VAR_SIGNATURE_FILE;
2305 litsig = ps->VAR_LITERAL_SIG;
2307 if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
2308 !IS_REMOTE(sigfile))
2309 return(1);
2311 rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
2312 if(any_patterns(rflags, &pstate)){
2313 set_pathandle(rflags);
2314 for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
2315 patline; patline = patline->next){
2316 for(pat = patline->first; pat; pat = pat->next){
2319 * See detoken() for when a sig file is used with a role.
2321 sigfile = pat->action ? pat->action->sig : NULL;
2322 litsig = pat->action ? pat->action->litsig : NULL;
2324 if(sigfile && *sigfile && !litsig &&
2325 sigfile[strlen(sigfile)-1] != '|' &&
2326 !IS_REMOTE(sigfile))
2327 return(1);
2332 return(0);
2337 any_rule_files_to_warn_about(struct pine *ps)
2339 long rflags;
2340 PAT_STATE pstate;
2341 PAT_S *pat;
2343 rflags = (ROLE_DO_ROLES | ROLE_DO_INCOLS | ROLE_DO_SCORES |
2344 ROLE_DO_FILTER | ROLE_DO_OTHER | ROLE_DO_SRCH | PAT_USE_MAIN);
2345 if(any_patterns(rflags, &pstate)){
2346 for(pat = first_pattern(&pstate);
2347 pat;
2348 pat = next_pattern(&pstate)){
2349 if(pat->patline && pat->patline->type == File)
2350 break;
2353 if(pat)
2354 return(1);
2357 return(0);
2362 convert_sigs_to_literal(struct pine *ps, int interactive)
2364 EditWhich ew = Main;
2365 char *sigfile, *litsig, *cstring_version, *nick, *src = NULL;
2366 char prompt[MAILTMPLEN];
2367 STORE_S *store;
2368 SCROLL_S sargs;
2369 long rflags;
2370 int ans;
2371 PAT_STATE pstate;
2372 PAT_S *pat;
2373 PAT_LINE_S *patline;
2375 /* first check main signature file */
2376 sigfile = ps->VAR_SIGNATURE_FILE;
2377 litsig = ps->VAR_LITERAL_SIG;
2379 if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
2380 !IS_REMOTE(sigfile)){
2381 if(interactive){
2382 snprintf(prompt,sizeof(prompt),
2383 /* TRANSLATORS: A literal sig is a way to store a signature
2384 in alpine. It isn't a very descriptive name. Instead of
2385 storing it in its own file it is stored in the configuration
2386 file. */
2387 _("Convert signature file \"%s\" to a literal sig "),
2388 sigfile);
2389 prompt[sizeof(prompt)-1] = '\0';
2390 ClearBody();
2391 ps->mangled_body = 1;
2392 if((ans=want_to(prompt, 'y', 'x', h_convert_sig, WT_NORM)) == 'x'){
2393 cmd_cancelled(NULL);
2394 return(-1);
2397 else
2398 ans = 'y';
2400 if(ans == 'y' && (src = get_signature_file(sigfile, 0, 0, 0)) != NULL){
2401 cstring_version = string_to_cstring(src);
2402 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2404 if(cstring_version)
2405 fs_give((void **)&cstring_version);
2407 fs_give((void **)&src);
2409 if(interactive){
2410 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2411 q_status_message(SM_ORDER | SM_DING, 7, 10,
2412 _("Error allocating space for message."));
2413 return(-1);
2416 snprintf(prompt, sizeof(prompt),
2417 /* TRANSLATORS: following lines go together */
2418 _("\nYour signature file \"%s\" has been converted"), sigfile);
2419 prompt[sizeof(prompt)-1] = '\0';
2420 so_puts(store, prompt);
2421 so_puts(store,
2422 _("\nto a literal signature, which means it is contained in your"));
2423 so_puts(store,
2424 _("\nAlpine configuration instead of being in a file of its own."));
2425 so_puts(store,
2426 _("\nIf that configuration is copied to a remote folder then the"));
2427 so_puts(store,
2428 _("\nsignature will be available remotely also."));
2429 so_puts(store,
2430 _("\nChanges to the signature file itself will no longer have any"));
2431 so_puts(store,
2432 _("\neffect on Alpine but you may still edit the signature with the"));
2433 so_puts(store,
2434 _("\nSetup/Signature command.\n"));
2436 memset(&sargs, 0, sizeof(SCROLL_S));
2437 sargs.text.text = so_text(store);
2438 sargs.text.src = CharStar;
2439 sargs.text.desc = _("Literal Signature Information");
2440 /* TRANSLATORS: screen title */
2441 sargs.bar.title = _("ABOUT LITERAL SIG");
2442 sargs.help.text = NO_HELP;
2443 sargs.help.title = NULL;
2445 scrolltool(&sargs);
2447 so_give(&store);
2448 ps->mangled_screen = 1;
2453 rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
2454 if(any_patterns(rflags, &pstate)){
2455 set_pathandle(rflags);
2456 for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
2457 patline; patline = patline->next){
2458 for(pat = patline->first; pat; pat = pat->next){
2461 * See detoken() for when a sig file is used with a role.
2463 sigfile = pat->action ? pat->action->sig : NULL;
2464 litsig = pat->action ? pat->action->litsig : NULL;
2465 nick = (pat->action && pat->action->nick && pat->action->nick[0]) ? pat->action->nick : NULL;
2467 if(sigfile && *sigfile && !litsig &&
2468 sigfile[strlen(sigfile)-1] != '|' &&
2469 !IS_REMOTE(sigfile)){
2470 if(interactive){
2471 snprintf(prompt,sizeof(prompt),
2472 /* TRANSLATORS: asking whether a signature file should be converted to what
2473 we call a literal signature, which is one contained in the regular
2474 configuration file. Think of the set of 4 %s arguments as a
2475 single argument which is the name of the signature file. */
2476 _("Convert signature file \"%s\"%s%s%s to a literal sig "),
2477 sigfile,
2478 nick ? " in role \"" : "",
2479 nick ? nick : "",
2480 nick ? "\"" : "");
2481 prompt[sizeof(prompt)-1] = '\0';
2482 ClearBody();
2483 ps->mangled_body = 1;
2484 if((ans=want_to(prompt, 'y', 'x',
2485 h_convert_sig, WT_NORM)) == 'x'){
2486 cmd_cancelled(NULL);
2487 return(-1);
2490 else
2491 ans = 'y';
2493 if(ans == 'y' &&
2494 (src = get_signature_file(sigfile,0,0,0)) != NULL){
2496 cstring_version = string_to_cstring(src);
2498 if(pat->action->litsig)
2499 fs_give((void **)&pat->action->litsig);
2501 pat->action->litsig = cstring_version;
2502 fs_give((void **)&src);
2504 set_pathandle(rflags);
2505 if(patline->type == Literal)
2506 (*cur_pat_h)->dirtypinerc = 1;
2507 else
2508 patline->dirty = 1;
2510 if(write_patterns(rflags) == 0){
2511 if(interactive){
2513 * Flush out current_vals of anything we've
2514 * possibly changed.
2516 close_patterns(ROLE_DO_ROLES | PAT_USE_CURRENT);
2518 if(!(store=so_get(CharStar,NULL,EDIT_ACCESS))){
2519 q_status_message(SM_ORDER | SM_DING, 7, 10,
2520 _("Error allocating space for message."));
2521 return(-1);
2524 snprintf(prompt, sizeof(prompt),
2525 /* TRANSLATORS: Keep the %s's together, they are sort of
2526 the name of the file. */
2527 _("Your signature file \"%s\"%s%s%s has been converted"),
2528 sigfile,
2529 nick ? " in role \"" : "",
2530 nick ? nick : "",
2531 nick ? "\"" : "");
2532 prompt[sizeof(prompt)-1] = '\0';
2533 so_puts(store, prompt);
2534 so_puts(store,
2535 /* TRANSLATORS: several lines that go together */
2536 _("\nto a literal signature, which means it is contained in your"));
2537 so_puts(store,
2538 _("\nAlpine configuration instead of being in a file of its own."));
2539 so_puts(store,
2540 _("\nIf that configuration is copied to a remote folder then the"));
2541 so_puts(store,
2542 _("\nsignature will be available remotely also."));
2543 so_puts(store,
2544 _("\nChanges to the signature file itself will no longer have any"));
2545 so_puts(store,
2546 _("\neffect on Alpine. You may edit the signature with the"));
2547 so_puts(store,
2548 _("\nSetup/Rules/Roles command.\n"));
2550 memset(&sargs, 0, sizeof(SCROLL_S));
2551 sargs.text.text = so_text(store);
2552 sargs.text.src = CharStar;
2553 sargs.text.desc =
2554 _("Literal Signature Information");
2555 /* TRANSLATORS: a screen title */
2556 sargs.bar.title = _("ABOUT LITERAL SIG");
2557 sargs.help.text = NO_HELP;
2558 sargs.help.title = NULL;
2560 scrolltool(&sargs);
2562 so_give(&store);
2563 ps->mangled_screen = 1;
2566 else if(interactive){
2567 q_status_message(SM_ORDER | SM_DING, 7, 10,
2568 /* TRANSLATORS: config is an abbreviation for configuration */
2569 _("Error writing rules config."));
2571 else{
2572 /* TRANSLATORS: sig is signature */
2573 fprintf(stderr, _("Error converting role sig\n"));
2574 return(-1);
2582 return(0);
2586 void
2587 warn_about_rule_files(struct pine *ps)
2589 STORE_S *store;
2590 SCROLL_S sargs;
2592 if(any_rule_files_to_warn_about(ps)){
2593 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2594 q_status_message(SM_ORDER | SM_DING, 7, 10,
2595 _("Error allocating space for message."));
2596 return;
2599 /* TRANSLATORS: several lines that go together */
2600 so_puts(store, _("\nSome of your Rules are contained in Rule files instead of being directly"));
2601 so_puts(store, _("\ncontained in your Alpine configuration file. To make those rules"));
2602 so_puts(store, _("\navailable remotely you will need to move them out of the files."));
2603 so_puts(store, _("\nThat can be done using the Shuffle command in the appropriate"));
2604 so_puts(store, _("\nSetup/Rules subcommands.\n"));
2606 memset(&sargs, 0, sizeof(SCROLL_S));
2607 sargs.text.text = so_text(store);
2608 sargs.text.src = CharStar;
2609 sargs.text.desc = _("Rule Files Information");
2610 /* TRANSLATORS: a screen title */
2611 sargs.bar.title = _("ABOUT RULE FILES");
2612 sargs.help.text = NO_HELP;
2613 sargs.help.title = NULL;
2615 scrolltool(&sargs);
2617 so_give(&store);
2618 ps->mangled_screen = 1;
2623 void
2624 convert_to_remote_config(struct pine *ps, int edit_exceptions)
2626 char rem_pinerc_prefix[MAILTMPLEN];
2627 char *beg, *end;
2628 CONTEXT_S *context;
2629 int abooks = 0, sigs = 0;
2631 if(edit_exceptions){
2632 /* TRANSLATORS: The exceptions command (X) was typed but it doesn't make sense */
2633 q_status_message(SM_ORDER, 3, 5,
2634 _("eXceptions does not make sense with this command"));
2635 return;
2638 if(!ps->prc)
2639 alpine_panic("NULL prc in convert_to_remote_config");
2641 dprint((2, "convert_to_remote_config\n"));
2643 if(ps->prc->type == RemImap){ /* pinerc is already remote */
2644 char prompt[MAILTMPLEN];
2647 * Check to see if there is anything at all to do. If there are
2648 * address books to convert, sigfiles to convert, or rule files
2649 * to comment on, we have something to do. Otherwise, just bail.
2651 abooks = any_addrbooks_to_convert(ps);
2652 sigs = any_sigs_to_convert(ps);
2654 if(abooks || sigs){
2655 if(abooks && sigs)
2656 /* TRANSLATORS: AddressBooks is Address Books */
2657 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks and signature files "));
2658 else if(abooks)
2659 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks "));
2660 else
2661 snprintf(prompt, sizeof(prompt), _("Config is already remote, convert signature files "));
2663 prompt[sizeof(prompt)-1] = '\0';
2664 if(want_to(prompt, 'y', 'x',
2665 (abooks && sigs) ? h_convert_abooks_and_sigs :
2666 abooks ? h_convert_abooks :
2667 sigs ? h_convert_sigs : NO_HELP,
2668 WT_NORM) != 'y'){
2669 cmd_cancelled(NULL);
2670 return;
2676 * Figure out a good default for where to put the remote config.
2677 * If the default collection is remote we'll take the hostname and
2678 * and modifiers from there. If not, we'll try to get the hostname from
2679 * the inbox-path. In either case, we use the home directory on the
2680 * server, not the directory where the folder collection is (if different).
2681 * If we don't have a clue, we'll ask user.
2683 if((context = default_save_context(ps->context_list)) != NULL &&
2684 IS_REMOTE(context_apply(rem_pinerc_prefix, context, "",
2685 sizeof(rem_pinerc_prefix)))){
2686 /* just use the host from the default collection, not the whole path */
2687 if((end = strrindex(rem_pinerc_prefix, '}')) != NULL)
2688 *(end + 1) = '\0';
2690 else{
2691 /* use host from inbox path */
2692 rem_pinerc_prefix[0] = '\0';
2693 if((beg = ps->VAR_INBOX_PATH)
2694 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
2695 && (end = strindex(ps->VAR_INBOX_PATH, '}'))){
2696 rem_pinerc_prefix[0] = '{';
2697 strncpy(rem_pinerc_prefix+1, beg+1,
2698 MIN(end-beg, sizeof(rem_pinerc_prefix)-2));
2699 rem_pinerc_prefix[MIN(end-beg, sizeof(rem_pinerc_prefix)-2)] = '}';
2700 rem_pinerc_prefix[MIN(end-beg+1,sizeof(rem_pinerc_prefix)-1)]='\0';
2704 /* ask about converting addrbooks to remote abooks */
2705 if(ps->prc->type != RemImap || abooks)
2706 if(convert_addrbooks_to_remote(ps, rem_pinerc_prefix,
2707 sizeof(rem_pinerc_prefix)) == -1){
2708 cmd_cancelled(NULL);
2709 return;
2712 /* ask about converting sigfiles to literal sigs */
2713 if(ps->prc->type != RemImap || sigs)
2714 if(convert_sigs_to_literal(ps, 1) == -1){
2715 cmd_cancelled(NULL);
2716 return;
2719 warn_about_rule_files(ps);
2721 /* finally, copy the config file */
2722 if(ps->prc->type == Loc)
2723 convert_pinerc_to_remote(ps, rem_pinerc_prefix);
2724 else if(!(abooks || sigs))
2725 q_status_message(SM_ORDER, 3, 5,
2726 _("Cannot copy config file since it is already remote."));
2730 void
2731 convert_pinerc_to_remote(struct pine *ps, char *rem_pinerc_prefix)
2733 #define DEF_FOLDER_NAME "remote_pinerc"
2734 char prompt[MAILTMPLEN], rem_pinerc[MAILTMPLEN];
2735 char *err_msg = NULL;
2736 int i, rc, offset;
2737 HelpType help;
2738 int flags = OE_APPEND_CURRENT;
2740 ClearBody();
2741 ps->mangled_body = 1;
2742 strncpy(rem_pinerc, rem_pinerc_prefix, sizeof(rem_pinerc)-1);
2743 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2745 if(*rem_pinerc == '\0'){
2746 snprintf(prompt, sizeof(prompt), _("Name of server to contain remote Alpine config : "));
2747 prompt[sizeof(prompt)-1] = '\0';
2748 help = NO_HELP;
2749 while(1){
2750 rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
2751 sizeof(rem_pinerc), prompt, NULL,
2752 help, &flags);
2753 removing_leading_and_trailing_white_space(rem_pinerc);
2754 if(rc == 3){
2755 help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
2757 else if(rc == 1){
2758 cmd_cancelled(NULL);
2759 return;
2761 else if(rc == 0){
2762 if(*rem_pinerc){
2763 /* add brackets */
2764 offset = strlen(rem_pinerc);
2765 for(i = offset; i >= 0; i--)
2766 if(i+1 < sizeof(rem_pinerc))
2767 rem_pinerc[i+1] = rem_pinerc[i];
2769 rem_pinerc[0] = '{';
2770 if(offset+2 < sizeof(rem_pinerc)){
2771 rem_pinerc[++offset] = '}';
2772 rem_pinerc[++offset] = '\0';
2775 break;
2781 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2784 * Add a default folder name.
2786 if(*rem_pinerc){
2788 * Add /user= to modify hostname so that user won't be asked who they
2789 * are each time they login.
2791 if(!strstr(rem_pinerc, "/user=") && ps->VAR_USER_ID &&
2792 ps->VAR_USER_ID[0]){
2793 char *p;
2795 p = rem_pinerc + strlen(rem_pinerc) - 1;
2796 if(*p == '}') /* this should be the case */
2797 snprintf(p, sizeof(rem_pinerc)-(p-rem_pinerc), "/user=\"%s\"}", ps->VAR_USER_ID);
2799 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2802 strncat(rem_pinerc, DEF_FOLDER_NAME,
2803 sizeof(rem_pinerc) - strlen(rem_pinerc) - 1);
2804 rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
2807 /* ask user about folder name for remote config */
2808 snprintf(prompt, sizeof(prompt), _("Folder to contain remote config : "));
2809 prompt[sizeof(prompt)-1] = '\0';
2810 help = NO_HELP;
2811 while(1){
2812 rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
2813 sizeof(rem_pinerc), prompt, NULL, help, &flags);
2814 removing_leading_and_trailing_white_space(rem_pinerc);
2815 if(rc == 0 && *rem_pinerc){
2816 break;
2819 if(rc == 3){
2820 help = (help == NO_HELP) ? h_convert_pinerc_folder : NO_HELP;
2822 else if(rc == 1 || rem_pinerc[0] == '\0'){
2823 cmd_cancelled(NULL);
2824 return;
2828 #ifndef _WINDOWS
2830 * If we are on a Unix system, writing to a remote config, we want the
2831 * remote config to work smoothly from a PC, too. If we don't have a
2832 * user-id on the PC then we will be asked for our password.
2833 * So add user-id to the pinerc before we copy it.
2835 if(!ps->vars[V_USER_ID].main_user_val.p && ps->VAR_USER_ID)
2836 ps->vars[V_USER_ID].main_user_val.p = cpystr(ps->VAR_USER_ID);
2838 ps->vars[V_USER_ID].is_used = 1; /* so it will write to pinerc */
2839 ps->prc->outstanding_pinerc_changes = 1;
2840 #endif
2842 if(ps->prc->outstanding_pinerc_changes)
2843 write_pinerc(ps, Main, WRP_NONE);
2845 #ifndef _WINDOWS
2846 ps->vars[V_USER_ID].is_used = 0;
2847 #endif
2849 /* copy the pinerc */
2850 if(copy_pinerc(ps->prc->name, rem_pinerc, &err_msg)){
2851 if(err_msg){
2852 q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
2853 fs_give((void **)&err_msg);
2856 return;
2859 /* tell user about command line flags */
2860 if(ps->prc->type != RemImap){
2861 STORE_S *store;
2862 SCROLL_S sargs;
2864 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2865 q_status_message(SM_ORDER | SM_DING, 7, 10,
2866 _("Error allocating space for message."));
2867 return;
2870 /* TRANSLATORS: several lines that go together */
2871 so_puts(store, _("\nYou may want to save a copy of this information!"));
2872 so_puts(store, _("\n\nYour Alpine configuration data has been copied to"));
2873 so_puts(store, "\n\n ");
2874 so_puts(store, rem_pinerc);
2875 so_puts(store, "\n");
2876 so_puts(store, _("\nTo use that remote configuration from this computer you will"));
2877 so_puts(store, _("\nhave to change the way you start Alpine by using the command line option"));
2878 so_puts(store, _("\n\"alpine -p <remote_folder>\". The command should probably be"));
2879 #ifdef _WINDOWS
2880 so_puts(store, "\n\n ");
2881 so_puts(store, "alpine -p ");
2882 so_puts(store, rem_pinerc);
2883 so_puts(store, "\n");
2884 so_puts(store, _("\nWith PC-Alpine, you may want to create a shortcut which"));
2885 so_puts(store, _("\nhas the required arguments."));
2886 #else
2887 so_puts(store, "\n\n ");
2888 so_puts(store, "alpine -p \"");
2889 so_puts(store, rem_pinerc);
2890 so_puts(store, "\"\n");
2891 so_puts(store, _("\nThe quotes are there around the last argument to protect the special"));
2892 so_puts(store, _("\ncharacters in the folder name (like braces) from the command shell"));
2893 so_puts(store, _("\nyou use. If you are not running Alpine from a command shell which knows"));
2894 so_puts(store, _("\nabout quoting, it is possible you will have to remove those quotes"));
2895 so_puts(store, _("\nfrom the command. For example, if you also use PC-Alpine you will probably"));
2896 so_puts(store, _("\nwant to create a shortcut, and you would not need the quotes there."));
2897 so_puts(store, _("\nWithout the quotes, the command might look like"));
2898 so_puts(store, "\n\n ");
2899 so_puts(store, "alpine -p ");
2900 so_puts(store, rem_pinerc);
2901 so_puts(store, "\n");
2902 so_puts(store, _("\nConsider creating an alias or shell script to execute this command to make"));
2903 so_puts(store, _("\nit more convenient."));
2904 #endif
2905 so_puts(store, _("\n\nIf you want to use your new remote configuration for this session, quit"));
2906 so_puts(store, _("\nAlpine now and restart with the changed command line options mentioned above.\n"));
2908 memset(&sargs, 0, sizeof(SCROLL_S));
2909 sargs.text.text = so_text(store);
2910 sargs.text.src = CharStar;
2911 sargs.text.desc = _("Remote Config Information");
2912 /* TRANSLATORS: a screen title */
2913 sargs.bar.title = _("ABOUT REMOTE CONFIG");
2914 sargs.help.text = NO_HELP;
2915 sargs.help.title = NULL;
2917 scrolltool(&sargs);
2919 so_give(&store); /* free resources associated with store */
2920 ps->mangled_screen = 1;
2926 verify_folder_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2928 char *tmp;
2930 tmp = cpystr(given ? given : "");
2931 removing_leading_and_trailing_white_space(tmp);
2933 if(expanded)
2934 *expanded = tmp;
2935 else
2936 fs_give((void **)&tmp);
2938 if(error)
2939 *error = cpystr("");
2941 return 0;
2946 verify_server_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2948 char *tmp;
2950 tmp = cpystr(given ? given : "");
2951 removing_leading_and_trailing_white_space(tmp);
2953 if(*tmp){
2955 * could try to verify the hostname here
2959 if(expanded)
2960 *expanded = tmp;
2961 else
2962 fs_give((void **)&tmp);
2964 if(error)
2965 *error = cpystr("");
2967 return 0;
2972 verify_abook_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
2974 int i;
2975 char *tmp;
2977 tmp = cpystr(given ? given : "");
2978 removing_leading_and_trailing_white_space(tmp);
2980 if(strindex(tmp, '"')){
2981 fs_give((void **)&tmp);
2982 if(error)
2983 /* TRANSLATORS: Double quote refers to the " character */
2984 *error = cpystr(_("Double quote not allowed in nickname"));
2986 return -2;
2989 for(i = 0; i < as.n_addrbk; i++)
2990 if(i != the_one_were_editing && !strcmp(tmp, as.adrbks[i].abnick))
2991 break;
2993 if(i < as.n_addrbk){
2994 fs_give((void **)&tmp);
2996 if(error)
2997 *error = cpystr(_("Nickname is already being used"));
2999 return -2;
3002 if(expanded)
3003 *expanded = tmp;
3004 else
3005 fs_give((void **)&tmp);
3007 if(error)
3008 *error = cpystr("");
3010 return 0;
3015 * Delete an addressbook.
3017 * Args: cur_line -- The current line position (in global display list)
3018 * of cursor
3019 * command_line -- The screen line on which to prompt
3020 * err -- Points to error message
3022 * Returns -- 0, deleted addrbook
3023 * -1, addrbook not deleted
3026 ab_del_abook(long int cur_line, int command_line, char **err)
3028 int abook_num, varnum, delete_data = 0,
3029 num_in_list, how_many_in_list = 0, i, cnt, warn_about_revert = 0;
3030 char **list, **new_list, **t, **lval;
3031 char tmp[200];
3032 PerAddrBook *pab;
3033 struct variable *vars = ps_global->vars;
3034 EditWhich ew;
3035 enum {NotSet,
3036 Modify,
3037 RevertToDefault,
3038 OverRideDefault,
3039 DontChange} modify_config;
3041 /* restrict address book config to normal config file */
3042 ew = Main;
3044 if(ps_global->readonly_pinerc){
3045 if(err)
3046 *err = _("Delete cancelled: config file not changeable");
3048 return -1;
3051 abook_num = adrbk_num_from_lineno(cur_line);
3053 pab = &as.adrbks[abook_num];
3055 dprint((2, "- ab_del_abook(%s) -\n",
3056 pab->abnick ? pab->abnick : "?"));
3058 varnum = (pab->type & GLOBAL) ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK;
3060 if(vars[varnum].is_fixed){
3061 if(err){
3062 if(pab->type & GLOBAL)
3063 *err =
3064 _("Cancelled: Sys. Mgmt. does not allow changing global address book config");
3065 else
3066 *err =
3067 _("Cancelled: Sys. Mgmt. does not allow changing address book config");
3070 return -1;
3074 * Deal with reverting to default values of the address book
3075 * variables, or with user deleting a default value.
3077 modify_config = NotSet;
3079 /* First count how many address books are in the user's config. */
3080 cnt = 0;
3081 lval = LVAL(&vars[varnum], ew);
3082 if(lval && lval[0])
3083 for(t = lval; *t != NULL; t++)
3084 cnt++;
3087 * Easy case, we can just delete one from the user's list.
3089 if(cnt > 1){
3090 modify_config = Modify;
3093 * Also easy. We'll revert to the default if it exists, and warn
3094 * the user about that.
3096 else if(cnt == 1){
3097 modify_config = RevertToDefault;
3098 /* see if there's a default to revert to */
3099 cnt = 0;
3100 if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
3101 for(t = vars[varnum].global_val.l; *t != NULL; t++)
3102 cnt++;
3104 warn_about_revert = cnt;
3107 * User is already using the default. Split it into two cases. If there
3108 * is one address book in default ask user if they want to delete that
3109 * default from their config. If there is more than one, ask them if
3110 * they want to ignore all the defaults or just delete this one.
3112 else{
3113 /* count how many in default */
3114 cnt = 0;
3115 if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
3116 for(t = vars[varnum].global_val.l; *t != NULL; t++)
3117 cnt++;
3119 if(cnt > 1){
3120 static ESCKEY_S opts[] = {
3121 /* TRANSLATORS: Ignore All means ignore all of the default values,
3122 and Remove One means just remove this one default value. */
3123 {'i', 'i', "I", N_("Ignore All")},
3124 {'r', 'r', "R", N_("Remove One")},
3125 {-1, 0, NULL, NULL}};
3127 snprintf(tmp, sizeof(tmp),
3128 /* TRANSLATORS: %s is an adjective modifying address books */
3129 _("Ignore all default %s address books or just remove this one ? "),
3130 /* TRANSLATORS: global or personal address books */
3131 pab->type & GLOBAL ? _("global") : _("personal"));
3132 tmp[sizeof(tmp)-1] = '\0';
3133 switch(radio_buttons(tmp, command_line, opts, 'i', 'x',
3134 h_ab_del_ignore, RB_NORM)){
3135 case 'i':
3136 modify_config = OverRideDefault;
3137 break;
3139 case 'r':
3140 modify_config = Modify;
3141 break;
3143 case 'x':
3144 if(err)
3145 *err = _("Delete cancelled");
3147 return -1;
3150 else{
3151 /* TRANSLATORS: a question */
3152 switch(want_to(_("Delete this default address book from config "),
3153 'n', 'x', h_ab_del_default, WT_NORM)){
3154 case 'n':
3155 case 'x':
3156 if(err)
3157 *err = _("Delete cancelled");
3159 return -1;
3161 case 'y':
3162 modify_config = OverRideDefault;
3163 break;
3169 * ReadWrite means it exists and MaybeRorW means it is remote and we
3170 * haven't selected it yet to know our access permissions. The remote
3171 * folder should have been created, though, unless we didn't even have
3172 * permissions for that, in which case we got some error messages earlier.
3174 if(pab->access == ReadWrite || pab->access == MaybeRorW){
3175 static ESCKEY_S o[] = {
3176 /* TRANSLATORS: user is asked whether to remove just data files, just configuration,
3177 or both for an address book. */
3178 {'d', 'd', "D", N_("Data")},
3179 {'c', 'c', "C", N_("Config")},
3180 {'b', 'b', "B", N_("Both")},
3181 {-1, 0, NULL, NULL}};
3183 switch(radio_buttons(_("Delete data, config, or both ? "),
3184 command_line, o, 'c', 'x',
3185 (modify_config == RevertToDefault)
3186 ? h_ab_del_data_revert
3187 : h_ab_del_data_modify,
3188 RB_NORM)){
3189 case 'b': /* Delete Both */
3190 delete_data = 1;
3191 break;
3193 case 'd': /* Delete only Data */
3194 modify_config = DontChange;
3195 delete_data = 1;
3196 break;
3198 case 'c': /* Delete only Config */
3199 break;
3201 case 'x': /* Cancel */
3202 default:
3203 if(err)
3204 *err = _("Delete cancelled");
3206 return -1;
3209 else{
3211 * Deleting config for address book which doesn't yet exist (hasn't
3212 * ever been opened).
3214 /* TRANSLATORS: a question */
3215 switch(want_to(_("Delete configuration for highlighted addressbook "),
3216 'n', 'x',
3217 (modify_config == RevertToDefault)
3218 ? h_ab_del_config_revert
3219 : h_ab_del_config_modify,
3220 WT_NORM)){
3221 case 'n':
3222 case 'x':
3223 default:
3224 if(err)
3225 *err = _("Delete cancelled");
3227 return -1;
3229 case 'y':
3230 break;
3234 if(delete_data){
3235 char warning[800];
3237 dprint((5, "deleting addrbook data\n"));
3238 warning[0] = '\0';
3241 * In order to delete the address book it is easiest if we open
3242 * it first. That fills in the filenames we want to delete.
3244 if(pab->address_book == NULL){
3245 warning[300] = '\0';
3246 pab->address_book = adrbk_open(pab, ps_global->home_dir,
3247 &warning[300], sizeof(warning)-300,
3248 AB_SORT_RULE_NONE);
3250 * Couldn't get it open.
3252 if(pab->address_book == NULL){
3253 if(warning[300])
3254 /* TRANSLATORS: %s is an error message */
3255 snprintf(warning, 300, _("Can't delete data: %s"), &warning[300]);
3256 else
3257 strncpy(warning, _("Can't delete address book data"), 100);
3262 * If we have it open, set the delete bits and close to get the
3263 * local copies. Delete the remote folder by hand.
3265 if(pab->address_book){
3266 char *file, *origfile = NULL;
3267 int f=0, o=0;
3270 * We're about to destroy addrbook data, better ask again.
3272 if(pab->address_book->count > 0){
3273 char prompt[100];
3275 /* TRANSLATORS: a question */
3276 snprintf(prompt, sizeof(prompt),
3277 _("About to delete the contents of address book (%ld entries), really delete "), (long) adrbk_count(pab->address_book));
3278 prompt[sizeof(prompt)-1] = '\0';
3280 switch(want_to(prompt, 'n', 'n', h_ab_really_delete, WT_NORM)){
3281 case 'y':
3282 break;
3284 case 'n':
3285 default:
3286 if(err)
3287 *err = _("Delete cancelled");
3289 return -1;
3293 pab->address_book->flags |= DEL_FILE;
3294 file = cpystr(pab->address_book->filename);
3295 if(pab->type & REMOTE_VIA_IMAP)
3296 origfile = cpystr(pab->address_book->orig_filename);
3299 * In order to avoid locking problems when we delete the
3300 * remote folder, we need to actually close the remote stream
3301 * instead of just putting it back in the stream pool.
3302 * So we will remove this stream from the re-usable portion
3303 * of the stream pool by clearing the SP_USEPOOL flag.
3304 * Init_abook(pab, TotallyClosed) via rd_close_remdata is
3305 * going to pine_mail_close it.
3307 if(pab->type & REMOTE_VIA_IMAP
3308 && pab->address_book
3309 && pab->address_book->type == Imap
3310 && pab->address_book->rd
3311 && rd_stream_exists(pab->address_book->rd)){
3313 sp_unflag(pab->address_book->rd->t.i.stream, SP_USEPOOL);
3316 /* This deletes the files because of DEL_ bits we set above. */
3317 init_abook(pab, TotallyClosed);
3320 * Delete the remote folder.
3322 if(pab->type & REMOTE_VIA_IMAP){
3323 REMDATA_S *rd;
3324 int exists;
3326 ps_global->c_client_error[0] = '\0';
3327 if(!pine_mail_delete(NULL, origfile) &&
3328 ps_global->c_client_error[0] != '\0'){
3329 dprint((1, "%s: %s\n", origfile ? origfile : "?",
3330 ps_global->c_client_error));
3333 /* delete line from metadata */
3334 rd = rd_new_remdata(RemImap, origfile, NULL);
3335 rd_write_metadata(rd, 1);
3336 rd_close_remdata(&rd);
3338 /* Check to see if it's still there */
3339 if((exists=folder_exists(NULL, origfile)) &&
3340 (exists != FEX_ERROR)){
3341 o++;
3342 dprint((1, "Trouble deleting %s\n",
3343 origfile ? origfile : "?"));
3347 if(can_access(file, ACCESS_EXISTS) == 0){
3348 f++;
3349 dprint((1, "Trouble deleting %s\n",
3350 file ? file : "?"));
3353 if(f || o){
3354 snprintf(warning, sizeof(warning), _("Trouble deleting data %s%s%s%s"),
3355 f ? file : "",
3356 (f && o) ? (o ? ", " : " and ") : "",
3357 o ? " and " : "",
3358 o ? origfile : "");
3359 warning[sizeof(warning)-1] = '\0';
3362 fs_give((void **) &file);
3363 if(origfile)
3364 fs_give((void **) &origfile);
3367 if(*warning){
3368 q_status_message(SM_ORDER, 3, 3, warning);
3369 dprint((1, "%s\n", warning));
3370 display_message(NO_OP_COMMAND);
3372 else if(modify_config == DontChange)
3373 q_status_message(SM_ORDER, 0, 1, _("Addressbook data deleted"));
3376 if(modify_config == DontChange){
3378 * We return -1 to indicate that the addrbook wasn't deleted (as far
3379 * as we're concerned) but we don't fill in err so that no error
3380 * message will be printed.
3381 * Since the addrbook is still an addrbook we need to reinitialize it.
3383 pab->access = adrbk_access(pab);
3384 if(pab->type & GLOBAL && pab->access != NoAccess)
3385 pab->access = ReadOnly;
3387 init_abook(pab, HalfOpen);
3388 return -1;
3390 else if(modify_config == Modify){
3391 list = vars[varnum].current_val.l;
3392 if(pab->type & GLOBAL){
3393 how_many_in_list = as.n_addrbk - as.how_many_personals - 1;
3394 num_in_list = abook_num - as.how_many_personals;
3396 else{
3397 how_many_in_list = as.how_many_personals - 1;
3398 num_in_list = abook_num;
3401 else if(modify_config == OverRideDefault)
3402 how_many_in_list = 1;
3403 else if(modify_config == RevertToDefault)
3404 how_many_in_list = 0;
3405 else
3406 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_del_abook");
3408 /* allocate for new list */
3409 if(how_many_in_list)
3410 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
3411 else
3412 new_list = NULL;
3415 * This case is both for modifying the users user_val and for the
3416 * case where the user wants to modify the global_val default and
3417 * use the modified version for his or her new user_val. We just
3418 * copy from the existing global_val, deleting the one addrbook
3419 * and put the result in user_val.
3421 if(modify_config == Modify){
3422 /* copy old list up to where we will delete entry */
3423 for(i = 0; i < num_in_list; i++)
3424 new_list[i] = cpystr(list[i]);
3426 /* copy rest of old list */
3427 for(; i < how_many_in_list; i++)
3428 new_list[i] = cpystr(list[i+1]);
3430 new_list[i] = NULL;
3432 else if(modify_config == OverRideDefault){
3433 new_list[0] = cpystr("");
3434 new_list[1] = NULL;
3437 /* this also frees old variable contents for us */
3438 if(set_variable_list(varnum, new_list, TRUE, ew)){
3439 if(err)
3440 *err = _("Delete cancelled: couldn't save pine configuration file");
3442 set_current_val(&vars[varnum], TRUE, FALSE);
3443 free_list_array(&new_list);
3445 return -1;
3448 set_current_val(&vars[varnum], TRUE, FALSE);
3450 if(warn_about_revert){
3451 /* TRANSLATORS: the %s may be "global " or nothing */
3452 snprintf(tmp, sizeof(tmp), _("Reverting to default %saddress books"),
3453 pab->type & GLOBAL ? _("global ") : "");
3454 tmp[sizeof(tmp)-1] = '\0';
3455 q_status_message(SM_ORDER, 3, 4, tmp);
3458 free_list_array(&new_list);
3460 return 0;
3465 * Shuffle addrbooks.
3467 * Args: pab -- Pab from current addrbook.
3468 * slide -- return value, tells how far to slide the cursor. If slide
3469 * is negative, slide it up, if positive, slide it down.
3470 * command_line -- The screen line on which to prompt
3471 * msg -- Points to returned message, if any. Should be freed by
3472 * caller.
3474 * Result: Two address books are swapped in the display order. If the shuffle
3475 * crosses the Personal/Global boundary, then instead of swapping
3476 * two address books the highlighted abook is moved from one section
3477 * to the other.
3478 * >= 0 on success.
3479 * < 0 on failure, no changes.
3480 * > 0 If the return value is greater than zero it means that we've
3481 * reverted one of the variables to its default value. That
3482 * means we've added at least one new addrbook, so the caller
3483 * should reset. The value returned is the number of the
3484 * moved addrbook + 1 (+1 so it won't be confused with zero).
3485 * = 0 If the return value is zero we've just moved addrbooks around.
3486 * No reset need be done.
3489 ab_shuffle(PerAddrBook *pab, int *slide, int command_line, char **msg)
3491 ESCKEY_S opts[3];
3492 char tmp[200];
3493 int i, deefault, rv, target = 0;
3494 int up_into_empty = 0, down_into_empty = 0;
3495 HelpType help;
3496 struct variable *vars = ps_global->vars;
3498 dprint((2, "- ab_shuffle() -\n"));
3500 *slide = 0;
3502 if(ps_global->readonly_pinerc){
3503 if(msg)
3504 *msg = cpystr(_("Shuffle cancelled: config file not changeable"));
3506 return -1;
3509 /* Move it up or down? */
3510 i = 0;
3511 opts[i].ch = 'u';
3512 opts[i].rval = 'u';
3513 opts[i].name = "U";
3514 /* TRANSLATORS: shuffle something Up or Down in a list */
3515 opts[i++].label = N_("Up");
3517 opts[i].ch = 'd';
3518 opts[i].rval = 'd';
3519 opts[i].name = "D";
3520 opts[i++].label = N_("Down");
3522 opts[i].ch = -1;
3523 deefault = 'u';
3525 if(pab->type & GLOBAL){
3526 if(vars[V_GLOB_ADDRBOOK].is_fixed){
3527 if(msg)
3528 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3530 return -1;
3533 if(as.cur == 0){
3534 if(as.config)
3535 up_into_empty++;
3536 else{ /* no up */
3537 opts[0].ch = -2;
3538 deefault = 'd';
3542 if(as.cur == as.n_addrbk - 1) /* no down */
3543 opts[1].ch = -2;
3545 else{
3546 if(vars[V_ADDRESSBOOK].is_fixed){
3547 if(msg)
3548 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book config"));
3550 return -1;
3553 if(as.cur == 0){ /* no up */
3554 opts[0].ch = -2;
3555 deefault = 'd';
3558 if(as.cur == as.n_addrbk - 1){
3559 if(as.config)
3560 down_into_empty++;
3561 else
3562 opts[1].ch = -2; /* no down */
3566 snprintf(tmp, sizeof(tmp), _("Shuffle \"%s\" %s%s%s ? "),
3567 pab->abnick,
3568 (opts[0].ch != -2) ? _("UP") : "",
3569 (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
3570 (opts[1].ch != -2) ? _("DOWN") : "");
3571 tmp[sizeof(tmp)-1] = '\0';
3572 help = (opts[0].ch == -2) ? h_ab_shuf_down
3573 : (opts[1].ch == -2) ? h_ab_shuf_up
3574 : h_ab_shuf;
3576 rv = radio_buttons(tmp, command_line, opts, deefault, 'x',
3577 help, RB_NORM);
3579 ps_global->mangled_footer = 1;
3581 if((rv == 'u' && up_into_empty) || (rv == 'd' && down_into_empty))
3582 target = -1;
3583 else
3584 target = as.cur + (rv == 'u' ? -1 : 1);
3586 if(rv == 'x'){
3587 if(msg)
3588 *msg = cpystr(_("Shuffle cancelled"));
3590 return -1;
3592 else
3593 return(do_the_shuffle(slide, as.cur, target, msg));
3598 * Actually shuffle the config variables and address books structures around.
3600 * Args: anum1, anum2 -- The numbers of the address books
3601 * msg -- Points to returned message, if any.
3603 * Returns: >= 0 on success.
3604 * < 0 on failure, no changes.
3605 * > 0 If the return value is greater than zero it means that we've
3606 * reverted one of the variables to its default value. That
3607 * means we've added at least one new addrbook, so the caller
3608 * should reset. The value returned is the number of the
3609 * moved addrbook + 1 (+1 so it won't be confused with zero).
3610 * = 0 If the return value is zero we've just moved addrbooks around.
3611 * No reset need be done.
3613 * Anum1 is the one that we want to move, anum2 is the one that it will be
3614 * swapped with. When anum1 and anum2 are on the opposite sides of the
3615 * Personal/Global boundary then instead of swapping we just move anum1 to
3616 * the other side of the boundary.
3618 * Anum2 of -1 means it is a swap into the other type of address book, which
3619 * is currently empty.
3622 do_the_shuffle(int *slide, int anum1, int anum2, char **msg)
3624 PerAddrBook *pab;
3625 enum {NotSet, Pers, Glob, Empty} type1, type2;
3626 int i, j, retval = -1;
3627 struct variable *vars = ps_global->vars;
3628 char **lval;
3629 EditWhich ew;
3630 char *cancel_msg = _("Shuffle cancelled: couldn't save configuration file");
3632 dprint((5, "- do_the_shuffle(%d, %d) -\n", anum1, anum2));
3634 /* restrict address book config to normal config file */
3635 ew = Main;
3637 if(anum1 == -1)
3638 type1 = Empty;
3639 else{
3640 pab = &as.adrbks[anum1];
3641 type1 = (pab->type & GLOBAL) ? Glob : Pers;
3644 if(type1 == Empty){
3645 if(msg)
3646 *msg =
3647 cpystr(_("Shuffle cancelled: highlight entry you wish to shuffle"));
3649 return(retval);
3652 if(anum2 == -1)
3653 type2 = Empty;
3654 else{
3655 pab = &as.adrbks[anum2];
3656 type2 = (pab->type & GLOBAL) ? Glob : Pers;
3659 if(type2 == Empty)
3660 type2 = (type1 == Pers) ? Glob : Pers;
3662 if((type1 == Pers || type2 == Pers) && vars[V_ADDRESSBOOK].is_fixed){
3663 if(msg)
3664 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book configuration"));
3666 return(retval);
3669 if((type1 == Glob || type2 == Glob) && vars[V_GLOB_ADDRBOOK].is_fixed){
3670 if(msg)
3671 *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3673 return(retval);
3677 * There are two cases. If the shuffle is two address books within the
3678 * same variable, then they just swap places. If it is a shuffle of an
3679 * addrbook from one side of the boundary to the other, just that one
3680 * is moved.
3682 if((type1 == Glob && type2 == Glob) ||
3683 (type1 == Pers && type2 == Pers)){
3684 int how_many_in_list, varnum;
3685 int anum1_rel, anum2_rel; /* position in specific list */
3686 char **list, **new_list;
3687 PerAddrBook tmppab;
3689 *slide = (anum1 < anum2) ? LINES_PER_ABOOK : -1 * LINES_PER_ABOOK;
3691 if(type1 == Pers){
3692 how_many_in_list = as.how_many_personals;
3693 list = VAR_ADDRESSBOOK;
3694 varnum = V_ADDRESSBOOK;
3695 anum1_rel = anum1;
3696 anum2_rel = anum2;
3698 else{
3699 how_many_in_list = as.n_addrbk - as.how_many_personals;
3700 list = VAR_GLOB_ADDRBOOK;
3701 varnum = V_GLOB_ADDRBOOK;
3702 anum1_rel = anum1 - as.how_many_personals;
3703 anum2_rel = anum2 - as.how_many_personals;
3706 /* allocate for new list, same size as old list */
3707 new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
3709 /* fill in new_list */
3710 for(i = 0; i < how_many_in_list; i++){
3711 /* swap anum1 and anum2 */
3712 if(i == anum1_rel)
3713 j = anum2_rel;
3714 else if(i == anum2_rel)
3715 j = anum1_rel;
3716 else
3717 j = i;
3719 new_list[i] = cpystr(list[j]);
3722 new_list[i] = NULL;
3724 if(set_variable_list(varnum, new_list, TRUE, ew)){
3725 if(msg)
3726 *msg = cpystr(cancel_msg);
3728 /* restore old values */
3729 set_current_val(&vars[varnum], TRUE, FALSE);
3730 free_list_array(&new_list);
3731 return(retval);
3734 retval = 0;
3735 set_current_val(&vars[varnum], TRUE, FALSE);
3736 free_list_array(&new_list);
3738 /* Swap PerAddrBook structs */
3739 tmppab = as.adrbks[anum1];
3740 as.adrbks[anum1] = as.adrbks[anum2];
3741 as.adrbks[anum2] = tmppab;
3743 else if((type1 == Pers && type2 == Glob) ||
3744 (type1 == Glob && type2 == Pers)){
3745 int how_many_in_srclist, how_many_in_dstlist;
3746 int srcvarnum, dstvarnum, srcanum;
3747 int cnt, warn_about_revert = 0;
3748 char **t;
3749 char **new_src, **new_dst, **srclist, **dstlist;
3750 char tmp[200];
3751 enum {NotSet, Modify, RevertToDefault, OverRideDefault} modify_config;
3754 * how_many_in_srclist = # in orig src list (Pers or Glob list).
3755 * how_many_in_dstlist = # in orig dst list
3756 * srcanum = # of highlighted addrbook that is being shuffled
3758 if(type1 == Pers){
3759 how_many_in_srclist = as.how_many_personals;
3760 how_many_in_dstlist = as.n_addrbk - as.how_many_personals;
3761 srclist = VAR_ADDRESSBOOK;
3762 dstlist = VAR_GLOB_ADDRBOOK;
3763 srcvarnum = V_ADDRESSBOOK;
3764 dstvarnum = V_GLOB_ADDRBOOK;
3765 srcanum = as.how_many_personals - 1;
3766 *slide = (how_many_in_srclist == 1)
3767 ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
3768 : XTRA_LINES_BETWEEN;
3770 else{
3771 how_many_in_srclist = as.n_addrbk - as.how_many_personals;
3772 how_many_in_dstlist = as.how_many_personals;
3773 srclist = VAR_GLOB_ADDRBOOK;
3774 dstlist = VAR_ADDRESSBOOK;
3775 srcvarnum = V_GLOB_ADDRBOOK;
3776 dstvarnum = V_ADDRESSBOOK;
3777 srcanum = as.how_many_personals;
3778 *slide = (how_many_in_dstlist == 0)
3779 ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
3780 : XTRA_LINES_BETWEEN;
3781 *slide = -1 * (*slide);
3785 modify_config = Modify;
3786 if(how_many_in_srclist == 1){
3788 * Deal with reverting to default values of the address book
3789 * variables, or with user deleting a default value.
3791 modify_config = NotSet;
3794 * Count how many address books are in the user's config.
3795 * This has to be one or zero, because how_many_in_srclist == 1.
3797 cnt = 0;
3798 lval = LVAL(&vars[srcvarnum], ew);
3799 if(lval && lval[0])
3800 for(t = lval; *t != NULL; t++)
3801 cnt++;
3804 * We'll revert to the default if it exists, and warn
3805 * the user about that.
3807 if(cnt == 1){
3808 modify_config = RevertToDefault;
3809 /* see if there's a default to revert to */
3810 cnt = 0;
3811 if(vars[srcvarnum].global_val.l &&
3812 vars[srcvarnum].global_val.l[0])
3813 for(t = vars[srcvarnum].global_val.l; *t != NULL; t++)
3814 cnt++;
3816 warn_about_revert = cnt;
3817 if(warn_about_revert > 1 && type1 == Pers)
3818 *slide = LINES_PER_ABOOK * warn_about_revert +
3819 XTRA_LINES_BETWEEN;
3822 * User is already using the default.
3824 else if(cnt == 0){
3825 modify_config = OverRideDefault;
3830 * We're adding one to the dstlist, so need how_many + 1 + 1.
3832 new_dst = (char **)fs_get((how_many_in_dstlist + 2) * sizeof(char *));
3833 j = 0;
3836 * Because the Personal list comes before the Global list, when
3837 * we move to Global we're inserting a new first element into
3838 * the global list (the dstlist).
3840 * When we move from Global to Personal, we're appending a new
3841 * last element onto the personal list (the dstlist).
3843 if(type2 == Glob)
3844 new_dst[j++] = cpystr(srclist[how_many_in_srclist-1]);
3846 for(i = 0; i < how_many_in_dstlist; i++)
3847 new_dst[j++] = cpystr(dstlist[i]);
3849 if(type2 == Pers)
3850 new_dst[j++] = cpystr(srclist[0]);
3852 new_dst[j] = NULL;
3855 * The srclist is complicated by the reverting to default
3856 * behaviors.
3858 if(modify_config == Modify){
3860 * In this case we're just removing one from the srclist
3861 * so the new_src is of size how_many -1 +1.
3863 new_src = (char **)fs_get((how_many_in_srclist) * sizeof(char *));
3864 j = 0;
3866 for(i = 0; i < how_many_in_srclist-1; i++)
3867 new_src[j++] = cpystr(srclist[i + ((type1 == Glob) ? 1 : 0)]);
3869 new_src[j] = NULL;
3871 else if(modify_config == OverRideDefault){
3873 * We were using default and will now revert to nothing.
3875 new_src = (char **)fs_get(2 * sizeof(char *));
3876 new_src[0] = cpystr("");
3877 new_src[1] = NULL;
3879 else if(modify_config == RevertToDefault){
3881 * We are moving our last user variable out and reverting
3882 * to the default value for this variable.
3884 new_src = NULL;
3887 if(set_variable_list(dstvarnum, new_dst, TRUE, ew) ||
3888 set_variable_list(srcvarnum, new_src, TRUE, ew)){
3889 if(msg)
3890 *msg = cpystr(cancel_msg);
3892 /* restore old values */
3893 set_current_val(&vars[dstvarnum], TRUE, FALSE);
3894 set_current_val(&vars[srcvarnum], TRUE, FALSE);
3895 free_list_array(&new_dst);
3896 free_list_array(&new_src);
3897 return(retval);
3900 set_current_val(&vars[dstvarnum], TRUE, FALSE);
3901 set_current_val(&vars[srcvarnum], TRUE, FALSE);
3902 free_list_array(&new_dst);
3903 free_list_array(&new_src);
3905 retval = (type1 == Pers && warn_about_revert)
3906 ? (warn_about_revert + 1) : (srcanum + 1);
3909 * This is a tough case. We're adding one or more new address books
3910 * in this case so we need to reset the addrbooks and start over.
3911 * We return the number of the address book we just moved after the
3912 * reset so that the caller can focus attention on the moved one.
3913 * Actually, we return 1+the number so that we can tell it apart
3914 * from a return of zero, which just means everything is ok.
3916 if(warn_about_revert){
3917 snprintf(tmp, sizeof(tmp),
3918 "This address book now %s, reverting to default %s address %s",
3919 (type1 == Glob) ? "Personal" : "Global",
3920 (type1 == Glob) ? "Global" : "Personal",
3921 warn_about_revert > 1 ? "books" : "book");
3922 tmp[sizeof(tmp)-1] = '\0';
3923 if(msg)
3924 *msg = cpystr(tmp);
3926 else{
3928 * Modify PerAddrBook struct and adjust boundary.
3929 * In this case we aren't swapping two addrbooks, but just modifying
3930 * one from being global to personal or the reverse. It will
3931 * still be the same element in the as.adrbks array.
3933 pab = &as.adrbks[srcanum];
3934 if(type2 == Glob){
3935 as.how_many_personals--;
3936 pab->type |= GLOBAL;
3937 if(pab->access != NoAccess)
3938 pab->access = ReadOnly;
3940 else{
3941 as.how_many_personals++;
3942 pab->type &= ~GLOBAL;
3943 if(pab->access != NoAccess && pab->access != MaybeRorW)
3944 pab->access = ReadWrite;
3947 snprintf(tmp, sizeof(tmp),
3948 "This address book now %s",
3949 (type1 == Glob) ? "Personal" : "Global");
3950 tmp[sizeof(tmp)-1] = '\0';
3951 if(msg)
3952 *msg = cpystr(tmp);
3956 return(retval);
3961 ab_compose_to_addr(long int cur_line, int agg, int allow_role)
3963 AddrScrn_Disp *dl;
3964 AdrBk_Entry *abe;
3965 SAVE_STATE_S state;
3966 BuildTo bldto;
3968 dprint((2, "- ab_compose_to_addr -\n"));
3970 save_state(&state);
3972 bldto.type = Str;
3973 bldto.arg.str = NULL;
3975 if(agg){
3976 int i;
3977 size_t incr = 100, avail, alloced;
3978 char *to = NULL;
3980 to = (char *)fs_get(incr);
3981 *to = '\0';
3982 avail = incr;
3983 alloced = incr;
3986 * Run through all of the selected entries
3987 * in all of the address books.
3988 * Put the nicknames together into one long
3989 * string with comma separators.
3991 for(i = 0; i < as.n_addrbk; i++){
3992 adrbk_cntr_t num;
3993 PerAddrBook *pab;
3994 EXPANDED_S *next_one;
3996 pab = &as.adrbks[i];
3997 if(pab->address_book)
3998 next_one = pab->address_book->selects;
3999 else
4000 continue;
4002 while((num = entry_get_next(&next_one)) != NO_NEXT){
4003 char *a_string;
4004 AddrScrn_Disp fake_dl;
4006 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4009 * Since we're picking up address book entries
4010 * directly from the address books and have
4011 * no knowledge of the display lines they came
4012 * from, we don't know the dl's that go with
4013 * them. We need to pass a dl to abe_to_nick
4014 * but it really is only going to use the
4015 * type in this case.
4017 dl = &fake_dl;
4018 dl->type = (abe->tag == Single) ? Simple : ListHead;
4019 a_string = abe_to_nick_or_addr_string(abe, dl, i);
4021 while(abe && avail < (size_t)strlen(a_string)+1){
4022 alloced += incr;
4023 avail += incr;
4024 fs_resize((void **)&to, alloced);
4027 if(!*to){
4028 strncpy(to, a_string, alloced);
4029 to[alloced-1] = '\0';
4031 else{
4032 strncat(to, ",", alloced-strlen(to)-1);
4033 to[alloced-1] = '\0';
4034 strncat(to, a_string, alloced-strlen(to)-1);
4035 to[alloced-1] = '\0';
4038 avail -= (strlen(a_string) + 1);
4039 fs_give((void **)&a_string);
4043 bldto.type = Str;
4044 bldto.arg.str = to;
4046 else{
4047 if(is_addr(cur_line)){
4049 dl = dlist(cur_line);
4050 abe = ae(cur_line);
4052 if(dl->type == ListEnt){
4053 bldto.type = Str;
4054 bldto.arg.str = cpystr(listmem(cur_line));
4056 else{
4057 bldto.type = Abe;
4058 bldto.arg.abe = abe;
4063 if(bldto.type == Str && bldto.arg.str == NULL)
4064 bldto.arg.str = cpystr("");
4066 ab_compose_internal(bldto, allow_role);
4068 restore_state(&state);
4070 if(bldto.type == Str && bldto.arg.str)
4071 fs_give((void **)&bldto.arg.str);
4074 * Window size may have changed in composer.
4075 * Pine_send will have reset the window size correctly,
4076 * but we still have to reset our address book data structures.
4078 ab_resize();
4079 ps_global->mangled_screen = 1;
4080 return(1);
4085 * Used by the two compose routines.
4087 void
4088 ab_compose_internal(BuildTo bldto, int allow_role)
4090 int good_addr;
4091 char *addr, *fcc, *error = NULL;
4092 ACTION_S *role = NULL;
4093 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
4094 (*redraw)(void) = ps_global->redrawer;
4096 if(allow_role)
4097 ps_global->redrawer = NULL;
4099 ps_global->next_screen = SCREEN_FUN_NULL;
4101 fcc = NULL;
4102 addr = NULL;
4104 good_addr = (our_build_address(bldto, &addr, &error, &fcc, NULL) >= 0);
4106 if(error){
4107 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4108 fs_give((void **)&error);
4111 if(!good_addr && addr && *addr)
4112 fs_give((void **)&addr); /* relying on fs_give setting addr to NULL */
4114 if(allow_role){
4115 /* Setup role */
4116 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
4117 cmd_cancelled("Composition");
4118 ps_global->next_screen = prev_screen;
4119 ps_global->redrawer = redraw;
4120 return;
4124 * If default role was selected (NULL) we need to make up a role which
4125 * won't do anything, but will cause compose_mail to think there's
4126 * already a role so that it won't try to confirm the default.
4128 if(role)
4129 role = copy_action(role);
4130 else{
4131 role = (ACTION_S *)fs_get(sizeof(*role));
4132 memset((void *)role, 0, sizeof(*role));
4133 role->nick = cpystr("Default Role");
4137 compose_mail(addr, fcc, role, NULL, NULL);
4139 if(addr)
4140 fs_give((void **)&addr);
4142 if(fcc)
4143 fs_give((void **)&fcc);
4148 * Export addresses into a file.
4150 * Args: cur_line -- The current line position (in global display list)
4151 * of cursor
4152 * command_line -- The screen line on which to prompt
4154 * Returns -- 1 if the export is done
4155 * 0 if not
4158 ab_export(struct pine *ps, long int cur_line, int command_line, int agg)
4160 int ret = 0, i, retflags = GER_NONE;
4161 int r, orig_errno = 0, failure = 0;
4162 struct variable *vars = ps->vars;
4163 char filename[MAXPATH+1], full_filename[MAXPATH+1];
4164 STORE_S *store;
4165 gf_io_t pc;
4166 long start_of_append;
4167 char *addr = NULL, *error = NULL;
4168 BuildTo bldto;
4169 char *p;
4170 int good_addr, plur, vcard = 0, tab = 0;
4171 static HISTORY_S *history = NULL;
4172 AdrBk_Entry *abe;
4173 VCARD_INFO_S *vinfo;
4174 static ESCKEY_S ab_export_opts[] = {
4175 {ctrl('T'), 10, "^T", N_("To Files")},
4176 {-1, 0, NULL, NULL},
4177 {-1, 0, NULL, NULL}};
4178 static ESCKEY_S vcard_or_addresses[] = {
4179 {'a', 'a', "A", N_("Address List")},
4180 {'v', 'v', "V", N_("VCard")},
4181 /* TRANSLATORS: TabSep is a command key label meaning Tab Separated List */
4182 {'t', 't', "T", N_("TabSep")},
4183 {-1, 0, NULL, NULL}};
4186 dprint((2, "- ab_export -\n"));
4188 if(ps->restricted){
4189 q_status_message(SM_ORDER, 0, 3,
4190 "Alpine demo can't export addresses to files");
4191 return(ret);
4194 while(1){
4195 i = radio_buttons(_("Export list of addresses, vCard format, or Tab Separated ? "),
4196 command_line, vcard_or_addresses, 'a', 'x',
4197 NO_HELP, RB_NORM|RB_RET_HELP);
4198 if(i == 3){
4199 /* TRANSLATORS: a screen title */
4200 helper(h_ab_export_vcard, _("HELP FOR EXPORT FORMAT"),
4201 HLPD_SIMPLE);
4202 ps_global->mangled_screen = 1;
4204 else
4205 break;
4208 switch(i){
4209 case 'x':
4210 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
4211 return(ret);
4213 case 'a':
4214 break;
4216 case 'v':
4217 vcard++;
4218 break;
4220 case 't':
4221 tab++;
4222 break;
4224 default:
4225 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_export");
4226 return(ret);
4229 if(agg)
4230 plur = 1;
4231 else{
4232 abe = ae(cur_line);
4233 plur = (abe && abe->tag == List);
4236 filename[0] = '\0';
4237 r = 0;
4239 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
4240 ab_export_opts[++r].ch = ctrl('I');
4241 ab_export_opts[r].rval = 11;
4242 ab_export_opts[r].name = "TAB";
4243 ab_export_opts[r].label = N_("Complete");
4246 ab_export_opts[++r].ch = -1;
4248 r = get_export_filename(ps, filename, NULL, full_filename, sizeof(filename),
4249 plur ? _("addresses") : _("address"),
4250 _("EXPORT"), ab_export_opts,
4251 &retflags, command_line, GE_IS_EXPORT, &history);
4253 if(r < 0){
4254 switch(r){
4255 case -1:
4256 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
4257 break;
4259 case -2:
4260 q_status_message1(SM_ORDER, 0, 2,
4261 _("Can't export to file outside of %s"), VAR_OPER_DIR);
4262 break;
4265 goto fini;
4268 dprint((5, "Opening file \"%s\" for export\n",
4269 full_filename ? full_filename : "?"));
4271 if(!(store = so_get(FileStar, full_filename, WRITE_ACCESS|WRITE_TO_LOCALE))){
4272 q_status_message2(SM_ORDER | SM_DING, 3, 4,
4273 _("Error opening file \"%s\" for address export: %s"),
4274 full_filename, error_description(errno));
4275 goto fini;
4279 * The write_single_vcard_entry function wants a pc.
4281 if(vcard || tab)
4282 gf_set_so_writec(&pc, store);
4284 start_of_append = so_tell(store);
4286 if(agg){
4287 for(i = 0; !failure && i < as.n_addrbk; i++){
4288 adrbk_cntr_t num;
4289 PerAddrBook *pab;
4290 EXPANDED_S *next_one;
4292 pab = &as.adrbks[i];
4293 if(pab->address_book)
4294 next_one = pab->address_book->selects;
4295 else
4296 continue;
4298 while(!failure && (num = entry_get_next(&next_one)) != NO_NEXT){
4300 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4301 if((vcard || tab) && abe){
4303 * There is no place to store the charset information
4304 * so we don't ask for it.
4306 if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
4307 failure++;
4308 else{
4309 if(vcard)
4310 write_single_vcard_entry(ps, pc, vinfo);
4311 else
4312 write_single_tab_entry(pc, vinfo);
4314 free_vcard_info(&vinfo);
4317 else if(abe){
4318 bldto.type = Abe;
4319 bldto.arg.abe = abe;
4320 error = NULL;
4321 addr = NULL;
4322 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4324 if(error){
4325 q_status_message1(SM_ORDER, 0, 4, "%s", error);
4326 fs_give((void **)&error);
4329 /* rfc1522_decode the addr */
4330 if(addr){
4331 size_t len;
4333 len = 4*strlen(addr)+1;
4334 p = (char *)fs_get(len * sizeof(char));
4335 if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
4336 fs_give((void **)&addr);
4337 addr = p;
4339 else
4340 fs_give((void **)&p);
4343 if(good_addr){
4344 int quoted = 0;
4347 * Change the unquoted commas into newlines.
4348 * Not worth it to do complicated quoting,
4349 * just consider double quotes.
4351 for(p = addr; *p; p++){
4352 if(*p == '"')
4353 quoted = !quoted;
4354 else if(!quoted && *p == ','){
4355 *p++ = '\n';
4356 removing_leading_white_space(p);
4357 p--;
4361 if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
4362 orig_errno = errno;
4363 failure = 1;
4367 if(addr)
4368 fs_give((void **)&addr);
4373 else{
4374 AddrScrn_Disp *dl;
4376 dl = dlist(cur_line);
4377 abe = ae(cur_line);
4378 if((vcard || tab) && abe){
4379 if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
4380 failure++;
4381 else{
4382 if(vcard)
4383 write_single_vcard_entry(ps, pc, vinfo);
4384 else
4385 write_single_tab_entry(pc, vinfo);
4387 free_vcard_info(&vinfo);
4390 else{
4392 if(dl->type == ListHead && listmem_count_from_abe(abe) == 0){
4393 error = _("List is empty, nothing to export!");
4394 good_addr = 0;
4396 else if(dl->type == ListEnt){
4397 bldto.type = Str;
4398 bldto.arg.str = listmem(cur_line);
4399 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4401 else{
4402 bldto.type = Abe;
4403 bldto.arg.abe = abe;
4404 good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
4407 if(error){
4408 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4409 fs_give((void **)&error);
4412 /* Have to rfc1522_decode the addr */
4413 if(addr){
4414 size_t len;
4415 len = 4*strlen(addr)+1;
4416 p = (char *)fs_get(len * sizeof(char));
4417 if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
4418 fs_give((void **)&addr);
4419 addr = p;
4421 else
4422 fs_give((void **)&p);
4425 if(good_addr){
4426 int quoted = 0;
4429 * Change the unquoted commas into newlines.
4430 * Not worth it to do complicated quoting,
4431 * just consider double quotes.
4433 for(p = addr; *p; p++){
4434 if(*p == '"')
4435 quoted = !quoted;
4436 else if(!quoted && *p == ','){
4437 *p++ = '\n';
4438 removing_leading_white_space(p);
4439 p--;
4443 if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
4444 orig_errno = errno;
4445 failure = 1;
4449 if(addr)
4450 fs_give((void **)&addr);
4454 if(vcard || tab)
4455 gf_clear_so_writec(store);
4457 if(so_give(&store)) /* release storage */
4458 failure++;
4460 if(failure){
4461 our_truncate(full_filename, (off_t)start_of_append);
4462 dprint((1, "FAILED Export: file \"%s\" : %s\n",
4463 full_filename ? full_filename : "?",
4464 error_description(orig_errno)));
4465 q_status_message2(SM_ORDER | SM_DING, 3, 4,
4466 _("Error exporting to \"%s\" : %s"),
4467 filename, error_description(orig_errno));
4469 else{
4470 ret = 1;
4471 q_status_message3(SM_ORDER,0,3,
4472 "%s %s to file \"%s\"",
4473 (vcard || tab) ? (agg ? "Entries" : "Entry")
4474 : (plur ? "Addresses" : "Address"),
4475 retflags & GER_OVER
4476 ? "overwritten"
4477 : retflags & GER_APPEND ? "appended" : "exported",
4478 filename);
4481 fini:
4482 ps->mangled_footer = 1;
4483 return(ret);
4488 * Forward an address book entry or entries via email attachment.
4490 * We use the vCard standard to send entries. We group multiple entries
4491 * using the BEGIN/END construct of vCard, not with multiple MIME parts.
4492 * A limitation of vCard is that there can be only one charset for the
4493 * whole group we send, so we might lose that information.
4495 * Args: cur_line -- The current line position (in global display list)
4496 * of cursor
4497 * command_line -- The screen line on which to prompt
4500 ab_forward(struct pine *ps, long int cur_line, int agg)
4502 AddrScrn_Disp *dl;
4503 AdrBk_Entry *abe = NULL;
4504 ENVELOPE *outgoing = NULL;
4505 BODY *pb, *body = NULL;
4506 PART **pp;
4507 char *sig;
4508 gf_io_t pc;
4509 int i, ret = 0;
4510 VCARD_INFO_S *vinfo;
4511 ACTION_S *role = NULL;
4513 dprint((2, "- ab_forward -\n"));
4515 if(!agg){
4516 dl = dlist(cur_line);
4517 if(dl->type != ListHead && dl->type != Simple)
4518 return(ret);
4520 abe = ae(cur_line);
4521 if(!abe){
4522 q_status_message(SM_ORDER, 3, 3, _("Trouble accessing current entry"));
4523 return(ret);
4527 outgoing = mail_newenvelope();
4528 if(agg && as.selections > 1)
4529 outgoing->subject = cpystr("Forwarded address book entries from Alpine");
4530 else
4531 outgoing->subject = cpystr("Forwarded address book entry from Alpine");
4533 body = mail_newbody();
4534 body->type = TYPEMULTIPART;
4535 /*---- The TEXT part/body ----*/
4536 body->nested.part = mail_newbody_part();
4537 body->nested.part->body.type = TYPETEXT;
4538 /*--- Allocate an object for the body ---*/
4539 if((body->nested.part->body.contents.text.data =
4540 (void *)so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
4541 int did_sig = 0;
4542 long rflags = ROLE_COMPOSE;
4543 PAT_STATE dummy;
4545 pp = &(body->nested.part->next);
4547 if(nonempty_patterns(rflags, &dummy)){
4549 * This is really more like Compose, even though it
4550 * is called Forward.
4552 if(confirm_role(rflags, &role))
4553 role = combine_inherited_role(role);
4554 else{ /* cancel */
4555 role = NULL;
4556 cmd_cancelled("Composition");
4557 goto bomb;
4561 if(role)
4562 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
4563 role->nick);
4565 if((sig = detoken(role, NULL, 2, 0, 1, NULL, NULL)) != NULL){
4566 if(*sig){
4567 so_puts((STORE_S *)body->nested.part->body.contents.text.data,
4568 sig);
4569 did_sig++;
4572 fs_give((void **)&sig);
4575 /* so we don't have an empty part */
4576 if(!did_sig)
4577 so_puts((STORE_S *)body->nested.part->body.contents.text.data, "\n");
4579 else{
4580 q_status_message(SM_ORDER | SM_DING, 3, 4,
4581 _("Problem creating space for message text"));
4582 goto bomb;
4585 outgoing->message_id = generate_message_id(role);
4587 /*---- create the attachment, and write abook entry into it ----*/
4588 *pp = mail_newbody_part();
4589 pb = &((*pp)->body);
4590 pb->type = TYPETEXT;
4591 pb->encoding = ENCOTHER; /* let data decide */
4592 pb->id = generate_message_id(role);
4593 pb->subtype = cpystr("DIRECTORY");
4594 if(agg && as.selections > 1)
4595 pb->description = cpystr("Alpine addressbook entries");
4596 else
4597 pb->description = cpystr("Alpine addressbook entry");
4599 pb->parameter = NULL;
4600 set_parameter(&pb->parameter, "profile", "vCard");
4602 if((pb->contents.text.data = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
4603 int are_some_unqualified = 0, expand_nicks = 0;
4604 adrbk_cntr_t num;
4605 PerAddrBook *pab;
4606 EXPANDED_S *next_one;
4608 gf_set_so_writec(&pc, (STORE_S *) pb->contents.text.data);
4610 if(agg){
4611 for(i = 0; i < as.n_addrbk && !are_some_unqualified; i++){
4613 pab = &as.adrbks[i];
4614 if(pab->address_book)
4615 next_one = pab->address_book->selects;
4616 else
4617 continue;
4619 while((num = entry_get_next(&next_one)) != NO_NEXT &&
4620 !are_some_unqualified){
4622 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4623 if(abe->tag == Single){
4624 if(abe->addr.addr && abe->addr.addr[0]
4625 && !strindex(abe->addr.addr, '@'))
4626 are_some_unqualified++;
4628 else{
4629 char **ll;
4631 for(ll = abe->addr.list; ll && *ll; ll++){
4632 if(!strindex(*ll, '@')){
4633 are_some_unqualified++;
4634 break;
4641 else{
4643 * Search through the addresses to see if there are any
4644 * that are unqualified, and so would be different if
4645 * expanded.
4647 if(abe->tag == Single){
4648 if(abe->addr.addr && abe->addr.addr[0]
4649 && !strindex(abe->addr.addr, '@'))
4650 are_some_unqualified++;
4652 else{
4653 char **ll;
4655 for(ll = abe->addr.list; ll && *ll; ll++){
4656 if(!strindex(*ll, '@')){
4657 are_some_unqualified++;
4658 break;
4664 if(are_some_unqualified){
4665 switch(want_to(_("Expand nicknames"), 'y', 'x', h_ab_forward,WT_NORM)){
4666 case 'x':
4667 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4668 q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
4669 goto bomb;
4671 case 'y':
4672 expand_nicks = 1;
4673 break;
4675 case 'n':
4676 expand_nicks = 0;
4677 break;
4680 ps->mangled_footer = 1;
4683 if(agg){
4684 for(i = 0; i < as.n_addrbk; i++){
4686 pab = &as.adrbks[i];
4687 if(pab->address_book)
4688 next_one = pab->address_book->selects;
4689 else
4690 continue;
4692 while((num = entry_get_next(&next_one)) != NO_NEXT){
4694 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
4695 if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
4696 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4697 goto bomb;
4699 else{
4700 write_single_vcard_entry(ps, pc, vinfo);
4701 free_vcard_info(&vinfo);
4706 else{
4707 if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
4708 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4709 goto bomb;
4711 else{
4712 write_single_vcard_entry(ps, pc, vinfo);
4713 free_vcard_info(&vinfo);
4717 /* This sets parameter charset, if necessary, and encoding */
4718 set_mime_type_by_grope(pb);
4719 set_charset_possibly_to_ascii(pb, "UTF-8");
4720 pb->size.bytes =
4721 strlen((char *)so_text((STORE_S *)pb->contents.text.data));
4723 else{
4724 q_status_message(SM_ORDER | SM_DING, 3, 4,
4725 _("Problem creating space for message text"));
4726 goto bomb;
4729 gf_clear_so_writec((STORE_S *) pb->contents.text.data);
4731 pine_send(outgoing, &body, _("FORWARDING ADDRESS BOOK ENTRY"), role, NULL,
4732 NULL, NULL, NULL, NULL, 0);
4734 ps->mangled_screen = 1;
4735 ret = 1;
4737 bomb:
4738 if(outgoing)
4739 mail_free_envelope(&outgoing);
4741 if(body)
4742 pine_free_body(&body);
4744 free_action(&role);
4745 return(ret);
4750 * Given an Adrbk_Entry fill in some of the fields in a VCARD_INFO_S
4751 * for use by write_single_vcard_entry. The returned structure is freed
4752 * by the caller.
4754 VCARD_INFO_S *
4755 prepare_abe_for_vcard(struct pine *ps, AdrBk_Entry *abe, int expand_nicks)
4757 VCARD_INFO_S *vinfo = NULL;
4758 char *init_addr = NULL, *addr = NULL, *astring;
4759 int cnt;
4760 ADDRESS *adrlist = NULL;
4762 if(!abe)
4763 return(vinfo);
4765 vinfo = (VCARD_INFO_S *) fs_get(sizeof(*vinfo));
4766 memset((void *) vinfo, 0, sizeof(*vinfo));
4768 if(abe->nickname && abe->nickname[0]){
4769 vinfo->nickname = (char **) fs_get((1+1) * sizeof(char *));
4770 vinfo->nickname[0] = cpystr(abe->nickname);
4771 vinfo->nickname[1] = NULL;
4774 if(abe->fcc && abe->fcc[0]){
4775 vinfo->fcc = (char **) fs_get((1+1) * sizeof(char *));
4776 vinfo->fcc[0] = cpystr(abe->fcc);
4777 vinfo->fcc[1] = NULL;
4780 if(abe->extra && abe->extra[0]){
4781 vinfo->note = (char **) fs_get((1+1) * sizeof(char *));
4782 vinfo->note[0] = cpystr(abe->extra);
4783 vinfo->note[1] = NULL;
4786 if(abe->fullname && abe->fullname[0]){
4787 char *fn, *last = NULL, *middle = NULL, *first = NULL;
4789 fn = adrbk_formatname(abe->fullname, &first, &last);
4790 if(fn){
4791 if(*fn){
4792 vinfo->fullname = (char **)fs_get((1+1) * sizeof(char *));
4793 vinfo->fullname[0] = fn;
4794 vinfo->fullname[1] = NULL;
4796 else
4797 fs_give((void **)&fn);
4800 if(last && *last){
4801 if(first && (middle=strindex(first, ' '))){
4802 *middle++ = '\0';
4803 middle = skip_white_space(middle);
4806 vinfo->last = last;
4807 vinfo->first = first;
4808 vinfo->middle = middle ? cpystr(middle) : NULL;
4809 first = NULL;
4810 last = NULL;
4813 if(last)
4814 fs_give((void **)&last);
4815 if(first)
4816 fs_give((void **)&first);
4819 /* expand nicknames and fully-qualify unqualified names */
4820 if(expand_nicks){
4821 char *error = NULL;
4822 BuildTo bldto;
4823 ADDRESS *a;
4825 if(abe->tag == Single)
4826 init_addr = cpystr(abe->addr.addr);
4827 else{
4828 char **ll;
4829 char *p;
4830 long length;
4832 /* figure out how large a string we need to allocate */
4833 length = 0L;
4834 for(ll = abe->addr.list; ll && *ll; ll++)
4835 length += (strlen(*ll) + 2);
4837 if(length)
4838 length -= 2L;
4840 init_addr = (char *)fs_get((size_t)(length+1L) * sizeof(char));
4841 p = init_addr;
4843 for(ll = abe->addr.list; ll && *ll; ll++){
4844 sstrncpy(&p, *ll, length-(p-init_addr));
4845 if(*(ll+1))
4846 sstrncpy(&p, ", ", length-(p-init_addr));
4849 init_addr[length] = '\0';
4852 bldto.type = Str;
4853 bldto.arg.str = init_addr;
4854 our_build_address(bldto, &addr, &error, NULL, NULL);
4855 if(error){
4856 q_status_message1(SM_ORDER, 3, 4, "%s", error);
4857 fs_give((void **)&error);
4858 free_vcard_info(&vinfo);
4859 return(NULL);
4862 if(addr)
4863 rfc822_parse_adrlist(&adrlist, addr, ps->maildomain);
4865 for(cnt = 0, a = adrlist; a; a = a->next)
4866 cnt++;
4868 vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
4870 for(cnt = 0, a = adrlist; a; a = a->next){
4871 char *bufp;
4872 ADDRESS *next_addr;
4873 size_t len;
4875 next_addr = a->next;
4876 a->next = NULL;
4877 len = est_size(a);
4878 bufp = (char *) fs_get(len * sizeof(char));
4879 astring = addr_string(a, bufp, len);
4880 a->next = next_addr;
4881 vinfo->email[cnt++] = cpystr(astring ? astring : "");
4882 fs_give((void **)&bufp);
4885 vinfo->email[cnt] = NULL;
4887 else{ /* don't expand or qualify */
4888 if(abe->tag == Single){
4889 astring =
4890 (abe->addr.addr && abe->addr.addr[0]) ? abe->addr.addr : "";
4891 vinfo->email = (char **)fs_get((1+1) * sizeof(char *));
4892 vinfo->email[0] = cpystr(astring);
4893 vinfo->email[1] = NULL;
4895 else{
4896 char **ll;
4898 for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
4899 cnt++;
4901 vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
4902 for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
4903 vinfo->email[cnt++] = cpystr(*ll);
4905 vinfo->email[cnt] = NULL;
4909 return(vinfo);
4913 void
4914 free_vcard_info(VCARD_INFO_S **vinfo)
4916 if(vinfo && *vinfo){
4917 if((*vinfo)->nickname)
4918 free_list_array(&(*vinfo)->nickname);
4919 if((*vinfo)->fullname)
4920 free_list_array(&(*vinfo)->fullname);
4921 if((*vinfo)->fcc)
4922 free_list_array(&(*vinfo)->fcc);
4923 if((*vinfo)->note)
4924 free_list_array(&(*vinfo)->note);
4925 if((*vinfo)->title)
4926 free_list_array(&(*vinfo)->title);
4927 if((*vinfo)->tel)
4928 free_list_array(&(*vinfo)->tel);
4929 if((*vinfo)->email)
4930 free_list_array(&(*vinfo)->email);
4932 if((*vinfo)->first)
4933 fs_give((void **)&(*vinfo)->first);
4934 if((*vinfo)->middle)
4935 fs_give((void **)&(*vinfo)->middle);
4936 if((*vinfo)->last)
4937 fs_give((void **)&(*vinfo)->last);
4939 fs_give((void **)vinfo);
4947 void
4948 write_single_vcard_entry(struct pine *ps, gf_io_t pc, VCARD_INFO_S *vinfo)
4950 char *decoded, *tmp2, *tmp = NULL, *hdr;
4951 char **ll;
4952 int i, did_fn = 0, did_n = 0;
4953 int cr;
4954 char eol[3];
4955 #define FOLD_BY 75
4957 if(!vinfo)
4958 return;
4960 #if defined(DOS) || defined(OS2)
4961 cr = 1;
4962 #else
4963 cr = 0;
4964 #endif
4966 if(cr)
4967 strncpy(eol, "\r\n", sizeof(eol));
4968 else
4969 strncpy(eol, "\n", sizeof(eol));
4971 eol[sizeof(eol)-1] = '\0';
4973 gf_puts("BEGIN:VCARD", pc);
4974 gf_puts(eol, pc);
4975 gf_puts("VERSION:3.0", pc);
4976 gf_puts(eol, pc);
4978 for(i = 0; i < 7; i++){
4979 switch(i){
4980 case 0:
4981 ll = vinfo->nickname;
4982 hdr = "NICKNAME:";
4983 break;
4985 case 1:
4986 ll = vinfo->fullname;
4987 hdr = "FN:";
4988 break;
4990 case 2:
4991 ll = vinfo->email;
4992 hdr = "EMAIL:";
4993 break;
4995 case 3:
4996 ll = vinfo->title;
4997 hdr = "TITLE:";
4998 break;
5000 case 4:
5001 ll = vinfo->note;
5002 hdr = "NOTE:";
5003 break;
5005 case 5:
5006 ll = vinfo->fcc;
5007 hdr = "X-FCC:";
5008 break;
5010 case 6:
5011 ll = vinfo->tel;
5012 hdr = "TEL:";
5013 break;
5015 default:
5016 alpine_panic("can't happen in write_single_vcard_entry");
5019 for(; ll && *ll; ll++){
5020 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
5022 tmp = vcard_escape(decoded);
5023 if(tmp){
5024 if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, hdr, " ", FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5025 gf_puts(tmp2, pc);
5026 fs_give((void **)&tmp2);
5027 if(i == 1)
5028 did_fn++;
5031 fs_give((void **)&tmp);
5036 if(vinfo->last && vinfo->last[0]){
5037 char *pl, *pf, *pm;
5039 pl = vcard_escape(vinfo->last);
5040 pf = (vinfo->first && *vinfo->first) ? vcard_escape(vinfo->first)
5041 : NULL;
5042 pm = (vinfo->middle && *vinfo->middle) ? vcard_escape(vinfo->middle)
5043 : NULL;
5044 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
5045 (pl && *pl) ? pl : "",
5046 ((pf && *pf) || (pm && *pm)) ? ";" : "",
5047 (pf && *pf) ? pf : "",
5048 (pm && *pm) ? ";" : "",
5049 (pm && *pm) ? pm : "");
5051 if((tmp2 = fold(tmp_20k_buf, FOLD_BY, FOLD_BY, "N:", " ",
5052 FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5053 gf_puts(tmp2, pc);
5054 fs_give((void **)&tmp2);
5055 did_n++;
5058 if(pl)
5059 fs_give((void **)&pl);
5060 if(pf)
5061 fs_give((void **)&pf);
5062 if(pm)
5063 fs_give((void **)&pm);
5067 * These two types are required in draft-ietf-asid-mime-vcard-06, which
5068 * is April 98 and is in last call.
5070 if(!did_fn || !did_n){
5071 if(did_n){
5072 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
5073 (vinfo->first && *vinfo->first) ? vinfo->first : "",
5074 (vinfo->first && *vinfo->first &&
5075 vinfo->middle && *vinfo->middle) ? " " : "",
5076 (vinfo->middle && *vinfo->middle) ? vinfo->middle : "",
5077 (((vinfo->first && *vinfo->first) ||
5078 (vinfo->middle && *vinfo->middle)) &&
5079 vinfo->last && *vinfo->last) ? " " : "",
5080 (vinfo->last && *vinfo->last) ? vinfo->last : "");
5082 tmp = vcard_escape(tmp_20k_buf);
5083 if(tmp){
5084 if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, "FN:", " ",
5085 FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
5086 gf_puts(tmp2, pc);
5087 fs_give((void **)&tmp2);
5088 did_n++;
5091 fs_give((void **)&tmp);
5094 else{
5095 if(!did_fn){
5096 gf_puts("FN:<Unknown>", pc);
5097 gf_puts(eol, pc);
5100 gf_puts("N:<Unknown>", pc);
5101 gf_puts(eol, pc);
5105 gf_puts("END:VCARD", pc);
5106 gf_puts(eol, pc);
5113 void
5114 write_single_tab_entry(gf_io_t pc, VCARD_INFO_S *vinfo)
5116 char *decoded, *tmp = NULL;
5117 char **ll;
5118 int i, first;
5119 char *eol;
5121 if(!vinfo)
5122 return;
5124 #if defined(DOS) || defined(OS2)
5125 eol = "\r\n";
5126 #else
5127 eol = "\n";
5128 #endif
5130 for(i = 0; i < 4; i++){
5131 switch(i){
5132 case 0:
5133 ll = vinfo->nickname;
5134 break;
5136 case 1:
5137 ll = vinfo->fullname;
5138 break;
5140 case 2:
5141 ll = vinfo->email;
5142 break;
5144 case 3:
5145 ll = vinfo->note;
5146 break;
5148 default:
5149 alpine_panic("can't happen in write_single_tab_entry");
5152 if(i)
5153 gf_puts("\t", pc);
5155 for(first = 1; ll && *ll; ll++){
5157 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
5158 tmp = vcard_escape(decoded);
5159 if(tmp){
5160 if(i == 2 && !first)
5161 gf_puts(",", pc);
5162 else
5163 first = 0;
5165 gf_puts(tmp, pc);
5166 fs_give((void **)&tmp);
5171 gf_puts(eol, pc);
5176 * for ab_save percent done
5178 static int total_to_copy;
5179 static int copied_so_far;
5181 percent_done_copying(void)
5183 return((copied_so_far * 100) / total_to_copy);
5187 cmp_action_list(const qsort_t *a1, const qsort_t *a2)
5189 ACTION_LIST_S *x = (ACTION_LIST_S *)a1;
5190 ACTION_LIST_S *y = (ACTION_LIST_S *)a2;
5192 if(x->pab != y->pab)
5193 return((x->pab > y->pab) ? 1 : -1); /* order doesn't matter */
5196 * The only one that matters is when both x and y have dup lit.
5197 * For the others, just need to be consistent so sort will terminate.
5199 if(x->dup){
5200 if(y->dup)
5201 return((x->num_in_dst > y->num_in_dst) ? -1
5202 : (x->num_in_dst == y->num_in_dst) ? 0 : 1);
5203 else
5204 return(-1);
5206 else if(y->dup)
5207 return(1);
5208 else
5209 return((x->num > y->num) ? -1 : (x->num == y->num) ? 0 : 1);
5214 * Copy a bunch of address book entries to a particular address book.
5216 * Args abook -- the current addrbook handle
5217 * cur_line -- the current line the cursor is on
5218 * command_line -- the line to prompt on
5219 * agg -- 1 if this is an aggregate copy
5221 * Returns 1 if successful, 0 if not
5224 ab_save(struct pine *ps, AdrBk *abook, long int cur_line, int command_line, int agg)
5226 PerAddrBook *pab_dst, *pab;
5227 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
5228 int rc, i;
5229 int how_many_dups = 0, how_many_to_copy = 0, skip_dups = 0;
5230 int how_many_no_action = 0, ret = 1;
5231 int err = 0, need_write = 0, we_cancel = 0;
5232 int act_list_size, special_case = 0;
5233 adrbk_cntr_t num, new_entry_num;
5234 char warn[2][MAX_NICKNAME+1];
5235 char warning[MAX_NICKNAME+1];
5236 char tmp[MAX(200,2*MAX_NICKNAME+80)];
5237 ACTION_LIST_S *action_list = NULL, *al;
5238 static ESCKEY_S save_or_export[] = {
5239 {'s', 's', "S", N_("Save")},
5240 {'e', 'e', "E", N_("Export")},
5241 {-1, 0, NULL, NULL}};
5243 if(!agg)
5244 snprintf(tmp, sizeof(tmp), _("Save highlighted entry to address book or Export to filesystem ? "));
5245 else if(as.selections > 1)
5246 snprintf(tmp, sizeof(tmp), _("Save selected entries to address book or Export to filesystem ? "));
5247 else if(as.selections == 1)
5248 snprintf(tmp, sizeof(tmp), _("Save selected entry to address book or Export to filesystem ? "));
5249 else
5250 snprintf(tmp, sizeof(tmp), _("Save to address book or Export to filesystem ? "));
5252 i = radio_buttons(tmp, -FOOTER_ROWS(ps), save_or_export, 's', 'x',
5253 h_ab_save_exp, RB_NORM);
5254 switch(i){
5255 case 'x':
5256 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5257 return(0);
5259 case 'e':
5260 return(ab_export(ps, cur_line, command_line, agg));
5262 case 's':
5263 break;
5265 default:
5266 q_status_message(SM_ORDER, 3, 3, "can't happen in ab_save");
5267 return(0);
5270 pab_dst = setup_for_addrbook_add(&state, command_line, _("Save"));
5271 if(!pab_dst)
5272 goto get_out;
5274 pab = &as.adrbks[as.cur];
5276 dprint((2, "- ab_save: %s -> %s (agg=%d)-\n",
5277 pab->abnick ? pab->abnick : "?",
5278 pab_dst->abnick ? pab_dst->abnick : "?", agg));
5280 if(agg)
5281 act_list_size = as.selections;
5282 else
5283 act_list_size = 1;
5285 action_list = (ACTION_LIST_S *)fs_get((act_list_size+1) *
5286 sizeof(ACTION_LIST_S));
5287 memset((void *)action_list, 0, (act_list_size+1) * sizeof(ACTION_LIST_S));
5288 al = action_list;
5290 if(agg){
5292 for(i = 0; i < as.n_addrbk; i++){
5293 EXPANDED_S *next_one;
5295 pab = &as.adrbks[i];
5296 if(pab->address_book)
5297 next_one = pab->address_book->selects;
5298 else
5299 continue;
5301 while((num = entry_get_next(&next_one)) != NO_NEXT){
5302 if(pab != pab_dst &&
5303 pab->ostatus != Open &&
5304 pab->ostatus != NoDisplay)
5305 init_abook(pab, NoDisplay);
5307 if(pab->ostatus != Open && pab->ostatus != NoDisplay){
5308 q_status_message1(SM_ORDER, 0, 4,
5309 _("Can't re-open address book %s to save from"),
5310 pab->abnick);
5311 err++;
5312 goto get_out;
5315 set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
5316 if(al->skip)
5317 how_many_no_action++;
5318 else{
5319 if(al->dup){
5320 if(how_many_dups < 2 && warning[0]){
5321 strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
5322 warn[how_many_dups][MAX_NICKNAME] = '\0';
5325 how_many_dups++;
5328 how_many_to_copy++;
5331 al++;
5335 else{
5336 if(is_addr(cur_line)){
5337 AddrScrn_Disp *dl;
5339 dl = dlist(cur_line);
5341 if(dl->type == ListEnt)
5342 special_case++;
5344 if(pab && dl){
5345 num = dl->elnum;
5346 set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
5348 else
5349 al->skip = 1;
5351 if(al->skip)
5352 how_many_no_action++;
5353 else{
5354 if(al->dup){
5355 if(how_many_dups < 2 && warning[0]){
5356 strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
5357 warn[how_many_dups][MAX_NICKNAME] = '\0';
5360 how_many_dups++;
5363 how_many_to_copy++;
5366 else{
5367 q_status_message(SM_ORDER, 0, 4, _("No current entry to save"));
5368 goto get_out;
5372 if(how_many_to_copy == 0 && how_many_no_action == 1 && act_list_size == 1)
5373 special_case++;
5375 if(special_case){
5376 TA_STATE_S tas, *tasp;
5378 /* Not going to use the action_list now */
5379 if(action_list)
5380 fs_give((void **)&action_list);
5382 tasp = &tas;
5383 tas.state = state;
5384 tas.pab = pab_dst;
5385 take_this_one_entry(ps, &tasp, abook, cur_line);
5388 * If take_this_one_entry or its children didn't do this for
5389 * us, we do it here.
5391 if(tas.pab)
5392 restore_state(&(tas.state));
5395 * We don't have enough information to know what to return.
5397 return(0);
5400 /* nothing to do (except for special Take case below) */
5401 if(how_many_to_copy == 0){
5402 if(how_many_no_action == 0){
5403 err++;
5404 goto get_out;
5406 else{
5407 restore_state(&state);
5409 if(how_many_no_action > 1)
5410 snprintf(tmp, sizeof(tmp), _("Saved %d entries to %s"), how_many_no_action, pab_dst->abnick);
5411 else
5412 snprintf(tmp, sizeof(tmp), _("Saved %d entry to %s"), how_many_no_action, pab_dst->abnick);
5414 tmp[sizeof(tmp)-1] = '\0';
5415 q_status_message(SM_ORDER, 0, 4, tmp);
5416 if(action_list)
5417 fs_give((void **)&action_list);
5419 return(ret);
5424 * If there are some nicknames which already exist in the selected
5425 * abook, ask user what to do.
5427 if(how_many_dups > 0){
5428 if(how_many_dups == 1)
5429 snprintf(tmp, sizeof(tmp), _("Entry with nickname \"%.*s\" already exists, replace "),
5430 MAX_NICKNAME, warn[0]);
5431 else if(how_many_dups == 2)
5432 snprintf(tmp, sizeof(tmp),
5433 _("Nicknames \"%.*s\" and \"%.*s\" already exist, replace "),
5434 MAX_NICKNAME, warn[0], MAX_NICKNAME, warn[1]);
5435 else
5436 snprintf(tmp, sizeof(tmp), _("%d of the nicknames already exist, replace "),
5437 how_many_dups);
5439 tmp[sizeof(tmp)-1] = '\0';
5441 switch(want_to(tmp, 'n', 'x', h_ab_copy_dups, WT_NORM)){
5442 case 'n':
5443 skip_dups++;
5444 if(how_many_to_copy == how_many_dups){
5445 restore_state(&state);
5446 if(action_list)
5447 fs_give((void **)&action_list);
5449 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5450 return(ret);
5453 break;
5455 case 'y':
5456 break;
5458 case 'x':
5459 err++;
5460 goto get_out;
5465 * Because the deletes happen immediately we have to delete from high
5466 * entry number towards lower entry numbers so that we are deleting
5467 * the correct entries. In order to do that we'll sort the action_list
5468 * to give us a safe order.
5470 if(!skip_dups && how_many_dups > 1)
5471 qsort((qsort_t *)action_list, (size_t)as.selections, sizeof(*action_list),
5472 cmp_action_list);
5475 * Set up the busy alarm percent counters.
5477 total_to_copy = how_many_to_copy - (skip_dups ? how_many_dups : 0);
5478 copied_so_far = 0;
5479 we_cancel = busy_cue(_("Saving entries"),
5480 (total_to_copy > 4) ? percent_done_copying : NULL, 0);
5483 * Add the list of entries to the destination abook.
5485 for(al = action_list; al && al->pab; al++){
5486 AdrBk_Entry *abe;
5488 if(al->skip || (skip_dups && al->dup))
5489 continue;
5491 if(!(abe = adrbk_get_ae(al->pab->address_book, (a_c_arg_t) al->num))){
5492 q_status_message1(SM_ORDER | SM_DING, 3, 5,
5493 _("Error saving entry: %s"),
5494 error_description(errno));
5495 err++;
5496 goto get_out;
5500 * Delete existing dups and replace them.
5502 if(al->dup){
5504 /* delete the existing entry */
5505 rc = 0;
5506 if(adrbk_delete(pab_dst->address_book,
5507 (a_c_arg_t)al->num_in_dst, 1, 0, 0, 0) == 0){
5508 need_write++;
5510 else{
5511 q_status_message2(SM_ORDER | SM_DING, 3, 5,
5512 _("Error replacing entry in %s: %s"),
5513 pab_dst->abnick,
5514 error_description(errno));
5515 err++;
5516 goto get_out;
5521 * Now we have a clean slate to work with.
5522 * Add (sorted in correctly) or append abe to the destination
5523 * address book.
5525 if(total_to_copy <= 1)
5526 rc = adrbk_add(pab_dst->address_book,
5527 NO_NEXT,
5528 abe->nickname,
5529 abe->fullname,
5530 abe->tag == Single ? abe->addr.addr : NULL,
5531 abe->fcc,
5532 abe->extra,
5533 abe->tag,
5534 &new_entry_num,
5535 (int *)NULL,
5539 else
5540 rc = adrbk_append(pab_dst->address_book,
5541 abe->nickname,
5542 abe->fullname,
5543 abe->tag == Single ? abe->addr.addr : NULL,
5544 abe->fcc,
5545 abe->extra,
5546 abe->tag,
5547 &new_entry_num);
5549 if(rc == 0)
5550 need_write++;
5553 * If the entry we copied is a list, we also have to add
5554 * the list members to the copy.
5556 if(rc == 0 && abe->tag == List){
5557 int save_sort_rule;
5560 * We want it to copy the list in the exact order
5561 * without sorting it.
5563 save_sort_rule = pab_dst->address_book->sort_rule;
5564 pab_dst->address_book->sort_rule = AB_SORT_RULE_NONE;
5566 rc = adrbk_nlistadd(pab_dst->address_book,
5567 (a_c_arg_t)new_entry_num, NULL, NULL,
5568 abe->addr.list,
5569 0, 0, 0);
5571 pab_dst->address_book->sort_rule = save_sort_rule;
5574 if(rc != 0){
5575 if(abe && abe->nickname)
5576 q_status_message2(SM_ORDER | SM_DING, 3, 5, _("Error saving %s: %s"), abe->nickname, error_description(errno));
5577 else
5578 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error saving entry: %s"), error_description(errno));
5579 err++;
5580 goto get_out;
5583 copied_so_far++;
5586 if(need_write){
5587 int sort_happened = 0;
5589 if(adrbk_write(pab_dst->address_book, 0, NULL, &sort_happened, 0, 1)){
5590 err++;
5591 goto get_out;
5594 if(sort_happened)
5595 ps_global->mangled_screen = 1;
5598 get_out:
5599 if(we_cancel)
5600 cancel_busy_cue(1);
5602 restore_state(&state);
5603 if(action_list)
5604 fs_give((void **)&action_list);
5606 ps_global->mangled_footer = 1;
5608 if(err){
5609 ret = 0;
5610 if(need_write)
5611 q_status_message(SM_ORDER | SM_DING, 3, 4,
5612 _("Save only partially completed"));
5613 else
5614 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
5616 else if (how_many_to_copy + how_many_no_action -
5617 (skip_dups ? how_many_dups : 0) > 0){
5619 ret = 1;
5620 snprintf(tmp, sizeof(tmp), "Saved %d %s to %s",
5621 how_many_to_copy + how_many_no_action -
5622 (skip_dups ? how_many_dups : 0),
5623 ((how_many_to_copy + how_many_no_action -
5624 (skip_dups ? how_many_dups : 0)) > 1) ? "entries" : "entry",
5625 pab_dst->abnick);
5626 tmp[sizeof(tmp)-1] = '\0';
5627 q_status_message(SM_ORDER, 0, 4, tmp);
5630 return(ret);
5635 * Warn should point to an array of size MAX_NICKNAME+1.
5637 void
5638 set_act_list_member(ACTION_LIST_S *al, a_c_arg_t numarg, PerAddrBook *pab_dst, PerAddrBook *pab, char *warn)
5640 AdrBk_Entry *abe1, *abe2;
5641 adrbk_cntr_t num;
5643 num = (adrbk_cntr_t)numarg;
5645 al->pab = pab;
5646 al->num = num;
5648 /* skip if they're copying from and to same addrbook */
5649 if(pab == pab_dst)
5650 al->skip = 1;
5651 else{
5652 abe1 = adrbk_get_ae(pab->address_book, numarg);
5653 if(abe1 && abe1->nickname && abe1->nickname[0]){
5654 adrbk_cntr_t dst_enum;
5656 abe2 = adrbk_lookup_by_nick(pab_dst->address_book,
5657 abe1->nickname, &dst_enum);
5659 * This nickname already exists in the destn address book.
5661 if(abe2){
5662 /* If it isn't different, no problem. Check it out. */
5663 if(abes_are_equal(abe1, abe2))
5664 al->skip = 1;
5665 else{
5666 strncpy(warn, abe1->nickname, MAX_NICKNAME);
5667 warn[MAX_NICKNAME] = '\0';
5668 al->dup = 1;
5669 al->num_in_dst = dst_enum;
5678 * Print out the display list.
5681 ab_print(int agg)
5683 int do_entry = 0, curopen;
5684 char *prompt;
5686 dprint((2, "- ab_print -\n"));
5688 curopen = cur_is_open();
5689 if(!agg && curopen){
5690 static ESCKEY_S prt[] = {
5691 {'a', 'a', "A", N_("AddressBook")},
5692 {'e', 'e', "E", N_("Entry")},
5693 {-1, 0, NULL, NULL}};
5695 prompt = _("Print Address Book or just this Entry? ");
5696 switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global), prt, 'a', 'x',
5697 NO_HELP, RB_NORM)){
5698 case 'x' :
5699 q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
5700 ps_global->mangled_footer = 1;
5701 return 0;
5703 case 'e':
5704 do_entry = 1;
5705 break;
5707 default:
5708 case 'a':
5709 break;
5713 /* TRANSLATORS: This is input for
5714 Print something1 using something2. The thing we're
5715 defining here is something1. */
5716 if(agg)
5717 prompt = _("selected entries");
5718 else{
5719 if(!curopen)
5720 prompt = _("address book list");
5721 else if(do_entry)
5722 prompt = _("entry");
5723 else
5724 prompt = _("address book");
5727 if(open_printer(prompt) == 0){
5728 DL_CACHE_S dlc_buf, *match_dlc;
5729 AddrScrn_Disp *dl;
5730 AdrBk_Entry *abe;
5731 long save_line;
5732 char *addr;
5733 char spaces[100];
5734 char more_spaces[100];
5735 char b[500];
5736 int abook_indent;
5738 save_line = as.top_ent + as.cur_row;
5739 match_dlc = get_dlc(save_line);
5740 dlc_buf = *match_dlc;
5741 match_dlc = &dlc_buf;
5743 if(do_entry){ /* print an individual addrbook entry */
5745 abook_indent = utf8_width(_("Nickname")) + 2;
5747 snprintf(spaces, sizeof(spaces), "%*.*s", abook_indent+2, abook_indent+2, "");
5748 snprintf(more_spaces, sizeof(more_spaces), "%*.*s",
5749 abook_indent+4, abook_indent+4, "");
5751 dl = dlist(save_line);
5752 abe = ae(save_line);
5754 if(abe){
5755 int are_some_unqualified = 0, expand_nicks = 0;
5756 char *string, *tmp;
5757 ADDRESS *adrlist = NULL;
5760 * Search through the addresses to see if there are any
5761 * that are unqualified, and so would be different if
5762 * expanded.
5764 if(abe->tag == Single){
5765 if(abe->addr.addr && abe->addr.addr[0]
5766 && !strindex(abe->addr.addr, '@'))
5767 are_some_unqualified++;
5769 else{
5770 char **ll;
5772 for(ll = abe->addr.list; ll && *ll; ll++){
5773 if(!strindex(*ll, '@')){
5774 are_some_unqualified++;
5775 break;
5780 if(are_some_unqualified){
5781 switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward,
5782 WT_NORM)){
5783 case 'x':
5784 q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
5785 ps_global->mangled_footer = 1;
5786 return 0;
5788 case 'y':
5789 expand_nicks = 1;
5790 break;
5792 case 'n':
5793 expand_nicks = 0;
5794 break;
5798 /* expand nicknames and fully-qualify unqualified names */
5799 if(expand_nicks){
5800 char *error = NULL;
5801 BuildTo bldto;
5802 char *init_addr = NULL;
5804 if(abe->tag == Single)
5805 init_addr = cpystr(abe->addr.addr);
5806 else{
5807 char **ll;
5808 char *p;
5809 long length;
5811 /* figure out how large a string we need to allocate */
5812 length = 0L;
5813 for(ll = abe->addr.list; ll && *ll; ll++)
5814 length += (strlen(*ll) + 2);
5816 if(length)
5817 length -= 2L;
5819 init_addr = (char *)fs_get((size_t)(length+1L) *
5820 sizeof(char));
5821 p = init_addr;
5823 for(ll = abe->addr.list; ll && *ll; ll++){
5824 sstrncpy(&p, *ll, length-(p-init_addr));
5825 if(*(ll+1))
5826 sstrncpy(&p, ", ", length-(p-init_addr));
5829 init_addr[length] = '\0';
5832 bldto.type = Str;
5833 bldto.arg.str = init_addr;
5834 our_build_address(bldto, &addr, &error, NULL, NULL);
5835 if(init_addr)
5836 fs_give((void **)&init_addr);
5838 if(error){
5839 q_status_message1(SM_ORDER, 0, 4, "%s", error);
5840 fs_give((void **)&error);
5842 ps_global->mangled_footer = 1;
5843 return 0;
5846 if(addr){
5847 rfc822_parse_adrlist(&adrlist, addr,
5848 ps_global->maildomain);
5849 fs_give((void **)&addr);
5852 /* Will use adrlist to do the printing below */
5855 tmp = abe->nickname ? abe->nickname : "";
5856 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5857 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
5858 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5859 print_text(tmp);
5860 fs_give((void **)&tmp);
5863 tmp = abe->fullname ? abe->fullname : "";
5864 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5865 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
5866 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5867 print_text(tmp);
5868 fs_give((void **)&tmp);
5871 tmp = abe->fcc ? abe->fcc : "";
5872 string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
5873 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
5874 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5875 print_text(tmp);
5876 fs_give((void **)&tmp);
5879 tmp = abe->extra ? abe->extra : "";
5880 {unsigned char *p, *bb = NULL;
5881 size_t n, len;
5882 if((n = 4*strlen(tmp)) > SIZEOF_20KBUF-1){
5883 len = n+1;
5884 p = bb = (unsigned char *)fs_get(len * sizeof(char));
5886 else{
5887 len = SIZEOF_20KBUF;
5888 p = (unsigned char *)tmp_20k_buf;
5891 string = (char *)rfc1522_decode_to_utf8(p, len, tmp);
5892 utf8_snprintf(b, len, "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
5893 if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
5894 print_text(tmp);
5895 fs_give((void **)&tmp);
5898 if(bb)
5899 fs_give((void **)&bb);
5903 * Print addresses
5906 if(expand_nicks){
5907 ADDRESS *a;
5909 for(a = adrlist; a; a = a->next){
5910 char *bufp;
5911 ADDRESS *next_addr;
5912 size_t len;
5914 next_addr = a->next;
5915 a->next = NULL;
5916 len = est_size(a);
5917 bufp = (char *) fs_get(len * sizeof(char));
5918 tmp = addr_string(a, bufp, len);
5919 a->next = next_addr;
5920 string = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf+10000,
5921 SIZEOF_20KBUF-10000, tmp);
5922 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5923 if((tmp = fold(string, 80, 80,
5924 (a == adrlist) ? b : spaces,
5925 more_spaces, FLD_NONE)) != NULL){
5926 print_text(tmp);
5927 fs_give((void **)&tmp);
5930 fs_give((void **)&bufp);
5933 if(adrlist)
5934 mail_free_address(&adrlist);
5935 else{
5936 utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
5937 print_text(b);
5940 else{ /* don't expand or qualify */
5941 if(abe->tag == Single){
5942 tmp = abe->addr.addr ? abe->addr.addr : "";
5943 string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
5944 SIZEOF_20KBUF-10000, tmp);
5945 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5946 if((tmp = fold(string, 80, 80, b,
5947 more_spaces, FLD_NONE)) != NULL){
5948 print_text(tmp);
5949 fs_give((void **)&tmp);
5952 else{
5953 char **ll;
5955 if(!abe->addr.list || !abe->addr.list[0]){
5956 utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
5957 print_text(b);
5960 for(ll = abe->addr.list; ll && *ll; ll++){
5961 string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
5962 SIZEOF_20KBUF-10000, *ll);
5963 utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
5964 if((tmp = fold(string, 80, 80,
5965 (ll == abe->addr.list)
5966 ? b : spaces,
5967 more_spaces, FLD_NONE)) != NULL){
5968 print_text(tmp);
5969 fs_give((void **)&tmp);
5976 else{
5977 long lineno;
5978 char lbuf[6*MAX_SCREEN_COLS + 1];
5979 char *p;
5980 int i, savecur, savezoomed;
5981 OpenStatus savestatus;
5982 PerAddrBook *pab;
5984 if(agg){ /* print all selected entries */
5985 if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
5986 savezoomed = as.zoomed;
5988 * Fool display code into thinking display is zoomed, so
5989 * we'll skip the unselected entries. Since this feature
5990 * causes all abooks to be displayed we don't have to
5991 * step through each addrbook.
5993 as.zoomed = 1;
5994 warp_to_beginning();
5995 lineno = 0L;
5996 for(dl = dlist(lineno);
5997 dl->type != End;
5998 dl = dlist(++lineno)){
6000 switch(dl->type){
6001 case Beginning:
6002 case ListClickHere:
6003 case ListEmpty:
6004 case ClickHereCmb:
6005 case Text:
6006 case TitleCmb:
6007 case Empty:
6008 case ZoomEmpty:
6009 case AskServer:
6010 continue;
6011 default:
6012 break;
6015 p = get_abook_display_line(lineno, 0, NULL, NULL,
6016 NULL, lbuf, sizeof(lbuf));
6017 print_text1("%s\n", p);
6020 as.zoomed = savezoomed;
6022 else{ /* print all selected entries */
6023 savecur = as.cur;
6024 savezoomed = as.zoomed;
6025 as.zoomed = 1;
6027 for(i = 0; i < as.n_addrbk; i++){
6028 pab = &as.adrbks[i];
6029 if(!(pab->address_book &&
6030 any_selected(pab->address_book->selects)))
6031 continue;
6034 * Print selected entries from addrbook i.
6035 * We have to put addrbook i into Open state so
6036 * that the display code will work right.
6038 as.cur = i;
6039 savestatus = pab->ostatus;
6040 init_abook(pab, Open);
6041 init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
6042 (void)calculate_field_widths();
6043 warp_to_beginning();
6044 lineno = 0L;
6046 for(dl = dlist(lineno);
6047 dl->type != End;
6048 dl = dlist(++lineno)){
6050 switch(dl->type){
6051 case Beginning:
6052 case ListClickHere:
6053 case ClickHereCmb:
6054 continue;
6055 default:
6056 break;
6059 p = get_abook_display_line(lineno, 0, NULL, NULL,
6060 NULL, lbuf,sizeof(lbuf));
6061 print_text1("%s\n", p);
6064 init_abook(pab, savestatus);
6067 as.cur = savecur;
6068 as.zoomed = savezoomed;
6069 /* restore the display for the current addrbook */
6070 init_disp_form(&as.adrbks[as.cur],
6071 ps_global->VAR_ABOOK_FORMATS, as.cur);
6074 else{ /* print either the abook list or a single abook */
6075 int anum;
6076 DL_CACHE_S *dlc;
6078 savezoomed = as.zoomed;
6079 as.zoomed = 0;
6081 if(curopen){ /* print a single address book */
6082 anum = adrbk_num_from_lineno(as.top_ent+as.cur_row);
6083 warp_to_top_of_abook(anum);
6084 if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
6085 lineno = 0L - XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP;
6086 else{
6087 print_text(" ");
6088 print_text(_("ADDRESS BOOK"));
6089 print_text1(" %s\n\n", as.adrbks[as.cur].abnick);
6090 lineno = 0L;
6093 else{ /* print the list of address books */
6094 warp_to_beginning();
6095 lineno = 0L;
6098 for(dl = dlist(lineno);
6099 dl->type != End && (!curopen ||
6100 (anum==adrbk_num_from_lineno(lineno) &&
6101 (as.n_serv == 0 ||
6102 ((dlc=get_dlc(lineno)) &&
6103 dlc->type != DlcDirDelim1))));
6104 dl = dlist(++lineno)){
6106 switch(dl->type){
6107 case Beginning:
6108 case ListClickHere:
6109 case ClickHereCmb:
6110 continue;
6111 default:
6112 break;
6115 p = get_abook_display_line(lineno, 0, NULL, NULL, NULL,
6116 lbuf, sizeof(lbuf));
6117 print_text1("%s\n", p);
6120 as.zoomed = savezoomed;
6124 close_printer();
6127 * jump cache back to where we started so that the next
6128 * request won't cause us to page through the whole thing
6130 if(!do_entry)
6131 warp_to_dlc(match_dlc, save_line);
6133 ps_global->mangled_screen = 1;
6136 ps_global->mangled_footer = 1;
6137 return 1;
6142 * Delete address book entries.
6145 ab_agg_delete(struct pine *ps, int agg)
6147 int ret = 0, i, ch, rc = 0;
6148 PerAddrBook *pab;
6149 adrbk_cntr_t num, ab_count;
6150 char prompt[80];
6152 dprint((2, "- ab_agg_delete -\n"));
6154 if(agg){
6155 snprintf(prompt, sizeof(prompt), _("Really delete %d selected entries"), as.selections);
6156 prompt[sizeof(prompt)-1] = '\0';
6157 ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
6158 if(ch == 'y'){
6159 adrbk_cntr_t newelnum = NO_NEXT, flushelnum = NO_NEXT;
6160 DL_CACHE_S dlc_save, dlc_restart, *dlc;
6161 int we_cancel = 0;
6162 int top_level_display;
6165 * We want to try to put the cursor in a reasonable position
6166 * on the screen when we're done. If we are in the top-level
6167 * display, then we can leave it the same. If we have an
6168 * addrbook opened, then we want to see if we can get back to
6169 * the same entry we are currently on.
6171 if(!(top_level_display = !any_ab_open())){
6172 dlc = get_dlc(as.top_ent+as.cur_row);
6173 dlc_save = *dlc;
6174 newelnum = dlc_save.dlcelnum;
6177 we_cancel = busy_cue(NULL, NULL, 1);
6179 for(i = 0; i < as.n_addrbk && rc != -5; i++){
6180 int orig_selected, selected;
6182 pab = &as.adrbks[i];
6183 if(!pab->address_book)
6184 continue;
6186 ab_count = adrbk_count(pab->address_book);
6187 rc = 0;
6188 selected = howmany_selected(pab->address_book->selects);
6189 orig_selected = selected;
6191 * Because deleting an entry causes the addrbook to be
6192 * immediately updated, we need to delete from higher entry
6193 * numbers to lower numbers. That way, entry number n is still
6194 * entry number n in the updated address book because we've
6195 * only deleted entries higher than n.
6197 for(num = ab_count-1; selected > 0 && rc == 0; num--){
6198 if(entry_is_selected(pab->address_book->selects,
6199 (a_c_arg_t)num)){
6200 rc = adrbk_delete(pab->address_book, (a_c_arg_t)num,
6201 1, 0, 0, 0);
6203 selected--;
6206 * This is just here to help us reposition the cursor.
6208 if(!top_level_display && as.cur == i && rc == 0){
6209 if(num >= newelnum)
6210 flushelnum = num;
6211 else
6212 newelnum--;
6217 if(rc == 0 && orig_selected > 0){
6218 int sort_happened = 0;
6220 rc = adrbk_write(pab->address_book, 0, NULL, &sort_happened, 1, 0);
6221 if(sort_happened)
6222 ps_global->mangled_screen = 1;
6225 if(rc && rc != -5){
6226 q_status_message2(SM_ORDER | SM_DING, 3, 5,
6227 "Error updating %s: %s",
6228 (as.n_addrbk > 1) ? pab->abnick
6229 : "address book",
6230 error_description(errno));
6231 dprint((1, "Error updating %s: %s\n",
6232 pab->filename ? pab->filename : "?",
6233 error_description(errno)));
6237 if(we_cancel)
6238 cancel_busy_cue(-1);
6240 if(rc == 0){
6241 q_status_message(SM_ORDER, 0, 2, _("Deletions completed"));
6242 ret = 1;
6243 erase_selections();
6246 if(!top_level_display){
6247 int lost = 0;
6248 long new_ent;
6250 if(flushelnum != dlc_save.dlcelnum){
6252 * We didn't delete current so restart there. The elnum
6253 * may have changed if we deleted entries above it.
6255 dlc_restart = dlc_save;
6256 dlc_restart.dlcelnum = newelnum;
6258 else{
6260 * Current was deleted.
6262 dlc_restart.adrbk_num = as.cur;
6263 pab = &as.adrbks[as.cur];
6264 ab_count = adrbk_count(pab->address_book);
6265 if(ab_count == 0)
6266 dlc_restart.type = DlcEmpty;
6267 else{
6268 AdrBk_Entry *abe;
6270 dlc_restart.dlcelnum = MIN(newelnum, ab_count-1);
6271 abe = adrbk_get_ae(pab->address_book,
6272 (a_c_arg_t) dlc_restart.dlcelnum);
6273 if(abe && abe->tag == Single)
6274 dlc_restart.type = DlcSimple;
6275 else if(abe && abe->tag == List)
6276 dlc_restart.type = DlcListHead;
6277 else
6278 lost++;
6282 if(lost){
6283 warp_to_top_of_abook(as.cur);
6284 as.top_ent = 0L;
6285 new_ent = first_selectable_line(0L);
6286 if(new_ent == NO_LINE)
6287 as.cur_row = 0L;
6288 else
6289 as.cur_row = new_ent;
6291 /* if it is off screen */
6292 if(as.cur_row >= as.l_p_page){
6293 as.top_ent += (as.cur_row - as.l_p_page + 1);
6294 as.cur_row = (as.l_p_page - 1);
6297 else if(dlc_restart.type != DlcEmpty &&
6298 dlc_restart.dlcelnum == dlc_save.dlcelnum &&
6299 (F_OFF(F_CMBND_ABOOK_DISP,ps_global) || as.cur == 0)){
6301 * Didn't delete any before this line.
6302 * Leave screen about the same. (May have deleted current.)
6304 warp_to_dlc(&dlc_restart, as.cur_row+as.top_ent);
6306 else{
6307 warp_to_dlc(&dlc_restart, 0L);
6308 /* put in middle of screen */
6309 as.top_ent = first_line(0L - (long)as.l_p_page/2L);
6310 as.cur_row = 0L - as.top_ent;
6313 ps->mangled_body = 1;
6316 else
6317 cmd_cancelled("Apply Delete command");
6320 return(ret);
6325 * Delete an entry from the address book
6327 * Args: abook -- The addrbook handle into access library
6328 * command_line -- The screen line on which to prompt
6329 * cur_line -- The entry number in the display list
6330 * warped -- We warped to a new part of the addrbook
6332 * Result: returns 1 if an entry was deleted, 0 if not.
6334 * The main routine above knows what to repaint because it's always the
6335 * current entry that's deleted. Here confirmation is asked of the user
6336 * and the appropriate adrbklib functions are called.
6339 single_entry_delete(AdrBk *abook, long int cur_line, int *warped)
6341 char ch, *cmd = NULL, *dname = NULL;
6342 char prompt[200];
6343 int rc;
6344 register AddrScrn_Disp *dl;
6345 AdrBk_Entry *abe;
6346 DL_CACHE_S *dlc_to_flush;
6348 dprint((2, "- single_entry_delete -\n"));
6350 if(warped)
6351 *warped = 0;
6353 dl = dlist(cur_line);
6354 abe = adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
6356 switch(dl->type){
6357 case Simple:
6358 dname = (abe->fullname && abe->fullname[0])
6359 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6360 SIZEOF_20KBUF, abe->fullname)
6361 : abe->nickname ? abe->nickname : "";
6362 cmd = _("Really delete \"%s\"");
6363 break;
6365 case ListHead:
6366 dname = (abe->fullname && abe->fullname[0])
6367 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6368 SIZEOF_20KBUF, abe->fullname)
6369 : abe->nickname ? abe->nickname : "";
6370 cmd = _("Really delete ENTIRE list \"%s\"");
6371 break;
6373 case ListEnt:
6374 dname = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
6375 SIZEOF_20KBUF, listmem_from_dl(abook, dl));
6376 cmd = _("Really delete \"%s\" from list");
6377 break;
6379 default:
6380 break;
6383 dname = dname ? dname : "";
6384 cmd = cmd ? cmd : "";
6386 snprintf(prompt, sizeof(prompt), cmd, dname);
6387 prompt[sizeof(prompt)-1] = '\0';
6388 ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
6389 if(ch == 'y'){
6390 dlc_to_flush = get_dlc(cur_line);
6391 if(dl->type == Simple || dl->type == ListHead){
6392 /*--- Kill a single entry or an entire list ---*/
6393 rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 1, 1, 0, 1);
6395 else if(listmem_count_from_abe(abe) > 2){
6396 /*---- Kill an entry out of a list ----*/
6397 rc = adrbk_listdel(abook, (a_c_arg_t)dl->elnum,
6398 listmem_from_dl(abook, dl));
6400 else{
6401 char *nick, *full, *addr, *fcc, *comment;
6402 adrbk_cntr_t new_entry_num = NO_NEXT;
6404 /*---- Convert a List to a Single entry ----*/
6406 /* Save old info to be transferred */
6407 nick = cpystr(abe->nickname);
6408 full = cpystr(abe->fullname);
6409 fcc = cpystr(abe->fcc);
6410 comment = cpystr(abe->extra);
6411 if(listmem_count_from_abe(abe) == 2)
6412 addr = cpystr(abe->addr.list[1 - dl->l_offset]);
6413 else
6414 addr = cpystr("");
6416 rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 0, 1, 0, 0);
6417 if(rc == 0)
6418 adrbk_add(abook,
6419 NO_NEXT,
6420 nick,
6421 full,
6422 addr,
6423 fcc,
6424 comment,
6425 Single,
6426 &new_entry_num,
6427 (int *)NULL,
6432 fs_give((void **)&nick);
6433 fs_give((void **)&full);
6434 fs_give((void **)&fcc);
6435 fs_give((void **)&comment);
6436 fs_give((void **)&addr);
6438 if(rc == 0){
6439 DL_CACHE_S dlc_restart;
6441 dlc_restart.adrbk_num = as.cur;
6442 dlc_restart.dlcelnum = new_entry_num;
6443 dlc_restart.type = DlcSimple;
6444 warp_to_dlc(&dlc_restart, 0L);
6445 *warped = 1;
6446 return 1;
6450 if(rc == 0){
6451 q_status_message(SM_ORDER, 0, 3,
6452 _("Entry deleted, address book updated"));
6453 dprint((5, "abook: Entry %s\n",
6454 (dl->type == Simple || dl->type == ListHead) ? "deleted"
6455 : "modified"));
6457 * Remove deleted line and everything after it from
6458 * the dlc cache. Next time we try to access those lines they
6459 * will get filled in with the right info.
6461 flush_dlc_from_cache(dlc_to_flush);
6462 return 1;
6464 else{
6465 PerAddrBook *pab;
6467 if(rc != -5)
6468 q_status_message1(SM_ORDER | SM_DING, 3, 5,
6469 _("Error updating address book: %s"),
6470 error_description(errno));
6471 pab = &as.adrbks[as.cur];
6472 dprint((1, "Error deleting entry from %s (%s): %s\n",
6473 pab->abnick ? pab->abnick : "?",
6474 pab->filename ? pab->filename : "?",
6475 error_description(errno)));
6478 return 0;
6480 else{
6481 q_status_message(SM_INFO, 0, 2, _("Entry not deleted"));
6482 return 0;
6487 void
6488 free_headents(struct headerentry **head)
6490 struct headerentry *he;
6491 PrivateTop *pt;
6493 if(head && *head){
6494 for(he = *head; he->name; he++)
6495 if(he->bldr_private){
6496 pt = (PrivateTop *)he->bldr_private;
6497 free_privatetop(&pt);
6500 fs_give((void **)head);
6505 #ifdef ENABLE_LDAP
6507 prompt::name::help::prwid::maxlen::realaddr::
6508 builder::affected_entry::next_affected::selector::key_label::fileedit::
6509 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
6510 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
6512 static struct headerentry headents_for_query[]={
6513 {"Normal Search : ", "NormalSearch", h_composer_qserv_qq, 16, 0, NULL,
6514 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6515 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6516 {"Name : ", "Name", h_composer_qserv_cn, 16, 0, NULL,
6517 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6518 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6519 {"Surname : ", "SurName", h_composer_qserv_sn, 16, 0, NULL,
6520 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6521 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6522 {"Given Name : ", "GivenName", h_composer_qserv_gn, 16, 0, NULL,
6523 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6524 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6525 {"Email Address : ", "EmailAddress", h_composer_qserv_mail, 16, 0, NULL,
6526 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6527 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6528 {"Organization : ", "Organization", h_composer_qserv_org, 16, 0, NULL,
6529 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6530 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6531 {"Org Unit : ", "OrganizationalUnit", h_composer_qserv_unit, 16, 0, NULL,
6532 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6533 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6534 {"Country : ", "Country", h_composer_qserv_country, 16, 0, NULL,
6535 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6536 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6537 {"State : ", "State", h_composer_qserv_state, 16, 0, NULL,
6538 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6539 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6540 {"Locality : ", "Locality", h_composer_qserv_locality, 16, 0, NULL,
6541 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6542 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6543 {" ", "BlankLine", NO_HELP, 1, 0, NULL,
6544 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6545 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_NONE},
6546 {"Custom Filter : ", "CustomFilter", h_composer_qserv_custom, 16, 0, NULL,
6547 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6548 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
6549 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
6550 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
6552 #define QQ_QQ 0
6553 #define QQ_CN 1
6554 #define QQ_SN 2
6555 #define QQ_GN 3
6556 #define QQ_MAIL 4
6557 #define QQ_ORG 5
6558 #define QQ_UNIT 6
6559 #define QQ_COUNTRY 7
6560 #define QQ_STATE 8
6561 #define QQ_LOCALITY 9
6562 #define QQ_BLANK 10
6563 #define QQ_CUSTOM 11
6564 #define QQ_END 12
6566 static SAVED_QUERY_S *saved_params;
6570 * Formulate a query for LDAP servers and send it and view it.
6571 * This is called from the Address Book screen, either from ^T from the
6572 * composer (selecting) or just when browsing in the address book screen
6573 * (selecting == 0).
6575 * Args ps -- Pine struct
6576 * selecting -- This is set if we're just selecting an address, as opposed
6577 * to browsing on an LDAP server
6578 * who -- Tells us which server to query
6579 * error -- An error message allocated here and freed by caller.
6581 * Returns -- Null if not selecting, possibly an address if selecting.
6582 * The address is 1522 decoded and should be freed by the caller.
6584 char *
6585 query_server(struct pine *ps, int selecting, int *exit, int who, char **error)
6587 struct headerentry *he = NULL;
6588 PICO pbf;
6589 STORE_S *msgso = NULL;
6590 int i, lret, editor_result;
6591 int r = 4, flags;
6592 HelpType help = NO_HELP;
6593 #define FILTSIZE 1000
6594 char fbuf[FILTSIZE+1];
6595 char *ret = NULL;
6596 LDAP_CHOOSE_S *winning_e = NULL;
6597 LDAP_SERV_RES_S *free_when_done = NULL;
6598 SAVED_QUERY_S *sq = NULL;
6599 static ESCKEY_S ekey[] = {
6600 /* TRANSLATORS: go to more complex search screen */
6601 {ctrl('T'), 10, "^T", N_("To complex search")},
6602 {-1, 0, NULL, NULL}
6605 dprint((2, "- query_server(%s) -\n", selecting?"Selecting":""));
6607 if(!(ps->VAR_LDAP_SERVERS && ps->VAR_LDAP_SERVERS[0] &&
6608 ps->VAR_LDAP_SERVERS[0][0])){
6609 if(error)
6610 *error = cpystr(_("No LDAP server available for lookup"));
6612 return(ret);
6615 fbuf[0] = '\0';
6616 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
6617 while(r == 4 || r == 3){
6618 /* TRANSLATORS: we're asking for a character string to search for */
6619 r = optionally_enter(fbuf, -FOOTER_ROWS(ps), 0, sizeof(fbuf),
6620 _("String to search for : "),
6621 ekey, help, &flags);
6622 if(r == 3)
6623 help = help == NO_HELP ? h_dir_comp_search : NO_HELP;
6626 /* strip quotes that user typed by mistake */
6627 (void)removing_double_quotes(fbuf);
6629 if(r == 1 || (r != 10 && fbuf[0] == '\0')){
6630 ps->mangled_footer = 1;
6631 if(error)
6632 *error = cpystr(_("Cancelled"));
6634 return(ret);
6637 editor_result = COMP_EXIT; /* just to get right logic below */
6639 memset((void *)&pbf, 0, sizeof(pbf));
6641 if(r == 10){
6642 standard_picobuf_setup(&pbf);
6643 pbf.exittest = pico_simpleexit;
6644 pbf.exit_label = _("Search");
6645 pbf.canceltest = pico_simplecancel;
6646 if(saved_params){
6647 pbf.expander = restore_query_parameters;
6648 pbf.ctrlr_label = _("Restore");
6651 pbf.pine_anchor = set_titlebar(_("SEARCH DIRECTORY SERVER"),
6652 ps_global->mail_stream,
6653 ps_global->context_current,
6654 ps_global->cur_folder,
6655 ps_global->msgmap,
6656 0, FolderName, 0, 0, NULL);
6657 pbf.pine_flags |= P_NOBODY;
6659 /* An informational message */
6660 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
6661 pbf.msgtext = (void *)so_text(msgso);
6663 * It's nice if we can make it so these lines make sense even if
6664 * they don't all make it on the screen, because the user can't
6665 * scroll down to see them. So just make each line a whole sentence
6666 * that doesn't need the others below it to make sense.
6668 so_puts(msgso,
6669 _("\n Fill in some of the fields above to create a query."));
6670 so_puts(msgso,
6671 _("\n The match will be for the exact string unless you include wildcards (*)."));
6672 so_puts(msgso,
6673 _("\n All filled-in fields must match in order to be counted as a match."));
6674 so_puts(msgso,
6675 _("\n Press \"^R\" to restore previous query values (if you've queried previously)."));
6676 so_puts(msgso,
6677 _("\n \"^G\" for help specific to each item. \"^X\" to make the query, or \"^C\" to cancel."));
6680 he = (struct headerentry *)fs_get((QQ_END+1) *
6681 sizeof(struct headerentry));
6682 memset((void *)he, 0, (QQ_END+1) * sizeof(struct headerentry));
6683 for(i = QQ_QQ; i <= QQ_END; i++)
6684 he[i] = headents_for_query[i];
6686 pbf.headents = he;
6688 sq = copy_query_parameters(NULL);
6689 he[QQ_QQ].realaddr = &sq->qq;
6690 he[QQ_CN].realaddr = &sq->cn;
6691 he[QQ_SN].realaddr = &sq->sn;
6692 he[QQ_GN].realaddr = &sq->gn;
6693 he[QQ_MAIL].realaddr = &sq->mail;
6694 he[QQ_ORG].realaddr = &sq->org;
6695 he[QQ_UNIT].realaddr = &sq->unit;
6696 he[QQ_COUNTRY].realaddr = &sq->country;
6697 he[QQ_STATE].realaddr = &sq->state;
6698 he[QQ_LOCALITY].realaddr = &sq->locality;
6699 he[QQ_CUSTOM].realaddr = &sq->custom;
6701 /* pass to pico and let user set them */
6702 editor_result = pico(&pbf);
6703 ps->mangled_screen = 1;
6704 standard_picobuf_teardown(&pbf);
6706 if(editor_result & COMP_GOTHUP)
6707 hup_signal();
6708 else{
6709 fix_windsize(ps_global);
6710 init_signals();
6714 if(editor_result & COMP_EXIT &&
6715 ((r == 0 && *fbuf) ||
6716 (r == 10 && sq &&
6717 (*sq->qq || *sq->cn || *sq->sn || *sq->gn || *sq->mail ||
6718 *sq->org || *sq->unit || *sq->country || *sq->state ||
6719 *sq->locality || *sq->custom)))){
6720 LDAPLookupStyle style;
6721 WP_ERR_S wp_err;
6722 int need_and, mangled;
6723 char *string;
6724 CUSTOM_FILT_S *filter;
6725 SAVED_QUERY_S *s;
6727 s = copy_query_parameters(sq);
6728 save_query_parameters(s);
6730 if(r == 0){
6731 string = fbuf;
6732 filter = NULL;
6734 else{
6735 int categories = 0;
6737 categories = ((*sq->cn != '\0') ? 1 : 0) +
6738 ((*sq->sn != '\0') ? 1 : 0) +
6739 ((*sq->gn != '\0') ? 1 : 0) +
6740 ((*sq->mail != '\0') ? 1 : 0) +
6741 ((*sq->org != '\0') ? 1 : 0) +
6742 ((*sq->unit != '\0') ? 1 : 0) +
6743 ((*sq->country != '\0') ? 1 : 0) +
6744 ((*sq->state != '\0') ? 1 : 0) +
6745 ((*sq->locality != '\0') ? 1 : 0);
6746 need_and = (categories > 1);
6748 if(((sq->cn ? strlen(sq->cn) : 0) +
6749 (sq->sn ? strlen(sq->sn) : 0) +
6750 (sq->gn ? strlen(sq->gn) : 0) +
6751 (sq->mail ? strlen(sq->mail) : 0) +
6752 (sq->org ? strlen(sq->org) : 0) +
6753 (sq->unit ? strlen(sq->unit) : 0) +
6754 (sq->country ? strlen(sq->country) : 0) +
6755 (sq->state ? strlen(sq->state) : 0) +
6756 (sq->locality ? strlen(sq->locality) : 0)) > FILTSIZE - 100){
6757 if(error)
6758 *error = cpystr(_("Search strings too long"));
6760 goto all_done;
6763 if(categories > 0){
6765 snprintf(fbuf, sizeof(fbuf),
6766 "%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",
6767 need_and ? "(&" : "",
6768 *sq->cn ? "(cn=" : "",
6769 *sq->cn ? sq->cn : "",
6770 *sq->cn ? ")" : "",
6771 *sq->sn ? "(sn=" : "",
6772 *sq->sn ? sq->sn : "",
6773 *sq->sn ? ")" : "",
6774 *sq->gn ? "(givenname=" : "",
6775 *sq->gn ? sq->gn : "",
6776 *sq->gn ? ")" : "",
6777 *sq->mail ? "(mail=" : "",
6778 *sq->mail ? sq->mail : "",
6779 *sq->mail ? ")" : "",
6780 *sq->org ? "(o=" : "",
6781 *sq->org ? sq->org : "",
6782 *sq->org ? ")" : "",
6783 *sq->unit ? "(ou=" : "",
6784 *sq->unit ? sq->unit : "",
6785 *sq->unit ? ")" : "",
6786 *sq->country ? "(c=" : "",
6787 *sq->country ? sq->country : "",
6788 *sq->country ? ")" : "",
6789 *sq->state ? "(st=" : "",
6790 *sq->state ? sq->state : "",
6791 *sq->state ? ")" : "",
6792 *sq->locality ? "(l=" : "",
6793 *sq->locality ? sq->locality : "",
6794 *sq->locality ? ")" : "",
6795 need_and ? ")" : "");
6796 fbuf[sizeof(fbuf)-1] = '\0';
6799 if(categories > 0 || *sq->custom)
6800 filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
6802 /* combine the configured filters with this filter */
6803 if(*sq->custom){
6804 string = "";
6805 filter->filt = sq->custom;
6806 filter->combine = 0;
6808 else if(*sq->qq && categories > 0){
6809 string = sq->qq;
6810 filter->filt = fbuf;
6811 filter->combine = 1;
6813 else if(categories > 0){
6814 string = "";
6815 filter->filt = fbuf;
6816 filter->combine = 0;
6818 else{
6819 string = sq->qq;
6820 filter = NULL;
6824 mangled = 0;
6825 memset(&wp_err, 0, sizeof(wp_err));
6826 wp_err.mangled = &mangled;
6827 style = selecting ? AlwaysDisplayAndMailRequired : AlwaysDisplay;
6829 /* maybe coming from composer */
6830 fix_windsize(ps_global);
6831 init_sigwinch();
6832 clear_cursor_pos();
6834 lret = ldap_lookup_all(string, who, 0, style, filter, &winning_e,
6835 &wp_err, &free_when_done);
6837 if(filter)
6838 fs_give((void **)&filter);
6840 if(wp_err.mangled)
6841 ps->mangled_screen = 1;
6843 if(wp_err.error){
6844 if(status_message_remaining() && error)
6845 *error = wp_err.error;
6846 else
6847 fs_give((void **)&wp_err.error);
6850 if(lret == 0 && winning_e && selecting){
6851 ADDRESS *addr;
6853 addr = address_from_ldap(winning_e);
6854 if(addr){
6855 if(!addr->host){
6856 addr->host = cpystr("missing-hostname");
6857 if(error){
6858 if(*error)
6859 fs_give((void **)error);
6861 *error = cpystr(_("Missing hostname in LDAP address"));
6865 ret = addr_list_string(addr, NULL, 1);
6866 if(!ret || !ret[0]){
6867 if(ret)
6868 fs_give((void **)&ret);
6870 if(exit)
6871 *exit = 1;
6873 if(error && !*error){
6874 char buf[200];
6876 snprintf(buf, sizeof(buf), _("No email address available for \"%s\""),
6877 (addr->personal && *addr->personal)
6878 ? addr->personal
6879 : "selected entry");
6880 buf[sizeof(buf)-1] = '\0';
6881 *error = cpystr(buf);
6885 mail_free_address(&addr);
6888 else if(lret == -1 && exit)
6889 *exit = 1;
6892 all_done:
6893 if(he)
6894 free_headents(&he);
6896 if(free_when_done)
6897 free_ldap_result_list(&free_when_done);
6899 if(winning_e)
6900 fs_give((void **)&winning_e);
6902 if(msgso)
6903 so_give(&msgso);
6905 if(sq)
6906 free_query_parameters(&sq);
6908 return(ret);
6913 * View all fields of an LDAP entry while browsing.
6915 * Args ps -- Pine struct
6916 * winning_e -- The struct containing the information about the entry
6917 * to be viewed.
6919 void
6920 view_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
6922 STORE_S *srcstore = NULL;
6923 SourceType srctype = CharStar;
6924 SCROLL_S sargs;
6925 HANDLE_S *handles = NULL;
6927 dprint((9, "- view_ldap_entry -\n"));
6929 if((srcstore=prep_ldap_for_viewing(ps,winning_e,srctype,&handles)) != NULL){
6930 memset(&sargs, 0, sizeof(SCROLL_S));
6931 sargs.text.text = so_text(srcstore);
6932 sargs.text.src = srctype;
6933 sargs.text.desc = _("expanded entry");
6934 sargs.text.handles= handles;
6935 sargs.bar.title = _("DIRECTORY ENTRY");
6936 sargs.proc.tool = process_ldap_cmd;
6937 sargs.proc.data.p = (void *) winning_e;
6938 sargs.help.text = h_ldap_view;
6939 sargs.help.title = _("HELP FOR DIRECTORY VIEW");
6940 sargs.keys.menu = &ldap_view_keymenu;
6941 setbitmap(sargs.keys.bitmap);
6943 if(handles)
6944 sargs.keys.menu->how_many = 2;
6945 else{
6946 sargs.keys.menu->how_many = 1;
6947 clrbitn(OTHER_KEY, sargs.keys.bitmap);
6950 scrolltool(&sargs);
6952 ps->mangled_screen = 1;
6953 so_give(&srcstore);
6954 free_handles(&handles);
6956 else
6957 q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
6962 * Compose a message to the email addresses contained in an LDAP entry.
6964 * Args ps -- Pine struct
6965 * winning_e -- The struct containing the information about the entry
6966 * to be viewed.
6968 void
6969 compose_to_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *e, int allow_role)
6971 struct berval **elecmail = NULL,
6972 **mail = NULL,
6973 **cn = NULL,
6974 **sn = NULL,
6975 **givenname = NULL;
6976 int num;
6977 size_t len = 0;
6979 dprint((9, "- compose_to_ldap_entry -\n"));
6981 if(e){
6982 char *a;
6983 BerElement *ber;
6985 for(a = ldap_first_attribute(e->ld, e->selected_entry, &ber);
6986 a != NULL;
6987 a = ldap_next_attribute(e->ld, e->selected_entry, ber)){
6989 if(strcmp(a, e->info_used->mailattr) == 0){
6990 if(!mail)
6991 mail = ldap_get_values_len(e->ld, e->selected_entry, a);
6993 else if(strcmp(a, "electronicmail") == 0){
6994 if(!elecmail)
6995 elecmail = ldap_get_values_len(e->ld, e->selected_entry, a);
6997 else if(strcmp(a, e->info_used->cnattr) == 0){
6998 if(!cn)
6999 cn = ldap_get_values_len(e->ld, e->selected_entry, a);
7001 else if(strcmp(a, e->info_used->gnattr) == 0){
7002 if(!givenname)
7003 givenname = ldap_get_values_len(e->ld, e->selected_entry, a);
7005 else if(strcmp(a, e->info_used->snattr) == 0){
7006 if(!sn)
7007 sn = ldap_get_values_len(e->ld, e->selected_entry, a);
7010 our_ldap_memfree(a);
7014 if(elecmail){
7015 if(ALPINE_LDAP_can_use(elecmail) && !mail)
7016 mail = elecmail;
7017 else
7018 ldap_value_free_len(elecmail);
7020 elecmail = NULL;
7023 for(num = 0; ALPINE_LDAP_usable(mail, num); num++)
7024 len += strlen(mail[num]->bv_val) + 1;
7026 if(len){
7027 char *p, *address, *fn = NULL;
7028 BuildTo bldto;
7029 SAVE_STATE_S state;
7031 address = (char *)fs_get(len * sizeof(char));
7032 p = address;
7033 num = 0;
7034 while(ALPINE_LDAP_usable(mail, num)){
7035 sstrncpy(&p, mail[num]->bv_val, len-(p-address));
7036 num++;
7037 if(mail[num])
7038 sstrncpy(&p, ",", len-(p-address));
7041 address[len-1] = '\0';
7044 * If we have a fullname and there is only a single address and
7045 * the address doesn't seem to have a fullname with it, add it.
7047 if(ALPINE_LDAP_can_use(mail) && !mail[1]){
7048 if(ALPINE_LDAP_can_use(cn))
7049 fn = cpystr(cn[0]->bv_val);
7050 else if(ALPINE_LDAP_can_use(sn) && ALPINE_LDAP_can_use(givenname)){
7051 size_t l;
7053 l = strlen(givenname[0]->bv_val) + strlen(sn[0]->bv_val) + 1;
7054 fn = (char *) fs_get((l+1) * sizeof(char));
7055 snprintf(fn, l+1, "%s %s", givenname[0]->bv_val, sn[0]->bv_val);
7056 fn[l] = '\0';
7060 if(ALPINE_LDAP_can_use(mail) && !mail[1] && fn){
7061 ADDRESS *adrlist = NULL;
7062 char *tmp_a_string;
7064 tmp_a_string = cpystr(address);
7065 rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
7066 fs_give((void **)&tmp_a_string);
7067 if(adrlist && !adrlist->next && !adrlist->personal){
7068 char *new_address;
7069 size_t len;
7070 RFC822BUFFER rbuf;
7072 adrlist->personal = cpystr(fn);
7073 len = est_size(adrlist);
7074 new_address = (char *) fs_get(len * sizeof(char));
7075 new_address[0] ='\0';
7076 rbuf.f = dummy_soutr;
7077 rbuf.s = NULL;
7078 rbuf.beg = new_address;
7079 rbuf.cur = new_address;
7080 rbuf.end = new_address+len-1;
7081 /* this will quote it if it needs quoting */
7082 rfc822_output_address_list(&rbuf, adrlist, 0L, NULL);
7083 *rbuf.cur = '\0';
7084 fs_give((void **)&address);
7085 address = new_address;
7088 if(adrlist)
7089 mail_free_address(&adrlist);
7092 bldto.type = Str;
7093 bldto.arg.str = address;
7095 save_state(&state);
7096 ab_compose_internal(bldto, allow_role);
7097 restore_state(&state);
7098 fs_give((void **)&address);
7099 if(fn)
7100 fs_give((void **)&fn);
7103 * Window size may have changed in composer.
7104 * Pine_send will have reset the window size correctly,
7105 * but we still have to reset our address book data structures.
7107 if(as.initialized)
7108 ab_resize();
7110 ps_global->mangled_screen = 1;
7112 else
7113 q_status_message(SM_ORDER, 0, 4, _("No address to compose to"));
7115 if(mail)
7116 ldap_value_free_len(mail);
7117 if(cn)
7118 ldap_value_free_len(cn);
7119 if(sn)
7120 ldap_value_free_len(sn);
7121 if(givenname)
7122 ldap_value_free_len(givenname);
7127 * Forward the text of an LDAP entry via email (not a vcard attachment)
7129 * Args ps -- Pine struct
7130 * winning_e -- The struct containing the information about the entry
7131 * to be viewed.
7133 void
7134 forward_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
7136 STORE_S *srcstore = NULL;
7137 SourceType srctype = CharStar;
7139 dprint((9, "- forward_ldap_entry -\n"));
7141 if((srcstore = prep_ldap_for_viewing(ps,winning_e,srctype,NULL)) != NULL){
7142 forward_text(ps, so_text(srcstore), srctype);
7143 ps->mangled_screen = 1;
7144 so_give(&srcstore);
7146 else
7147 q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
7151 STORE_S *
7152 prep_ldap_for_viewing(struct pine *ps, LDAP_CHOOSE_S *winning_e, SourceType srctype, HANDLE_S **handlesp)
7154 STORE_S *store = NULL;
7155 char *a, *tmp;
7156 BerElement *ber;
7157 int i, width;
7158 #define W (1000)
7159 #define INDENTHERE (22)
7160 char obuf[W+10];
7161 char hdr[6*INDENTHERE+1], hdr2[6*INDENTHERE+1];
7162 struct berval **cn = NULL;
7163 int indent = INDENTHERE;
7165 if(!(store = so_get(srctype, NULL, EDIT_ACCESS)))
7166 return(store);
7168 /* for mailto handles so user can select individual email addrs */
7169 if(handlesp)
7170 init_handles(handlesp);
7172 width = MAX(ps->ttyo->screen_cols, 25);
7174 snprintf(hdr2, sizeof(hdr2), "%-*.*s: ", indent-2,indent-2, "");
7175 hdr2[sizeof(hdr2)-1] = '\0';
7177 if(sizeof(obuf) > ps->ttyo->screen_cols+1){
7178 memset((void *)obuf, '-', ps->ttyo->screen_cols * sizeof(char));
7179 obuf[ps->ttyo->screen_cols] = '\n';
7180 obuf[ps->ttyo->screen_cols+1] = '\0';
7183 a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
7185 so_puts(store, obuf);
7186 if((tmp = fold(a, width, width, "", " ", FLD_NONE)) != NULL){
7187 so_puts(store, tmp);
7188 fs_give((void **)&tmp);
7191 so_puts(store, obuf);
7192 so_puts(store, "\n");
7194 our_ldap_dn_memfree(a);
7196 for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
7197 a != NULL;
7198 a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
7200 if(a && *a){
7201 struct berval **vals;
7202 char *fn = NULL;
7204 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
7206 /* save this for mailto */
7207 if(handlesp && !cn && !strcmp(a, winning_e->info_used->cnattr))
7208 cn = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
7210 if(vals){
7211 int do_mailto;
7213 do_mailto = (handlesp &&
7214 !strcmp(a, winning_e->info_used->mailattr));
7216 utf8_snprintf(hdr, sizeof(hdr), "%-*.*w: ", indent-2,indent-2,
7217 ldap_translate(a, winning_e->info_used));
7218 hdr[sizeof(hdr)-1] = '\0';
7219 for(i = 0; ALPINE_LDAP_usable(vals, i); i++){
7220 if(do_mailto){
7221 ADDRESS *ad = NULL;
7222 HANDLE_S *h;
7223 char buf[20];
7224 char *tmp_a_string;
7225 char *addr, *new_addr, *enc_addr;
7226 char *path = NULL;
7228 addr = cpystr(vals[i]->bv_val);
7229 if(ALPINE_LDAP_can_use(cn))
7230 fn = cpystr(cn[0]->bv_val);
7232 if(fn){
7233 tmp_a_string = cpystr(addr);
7234 rfc822_parse_adrlist(&ad, tmp_a_string, "@");
7235 fs_give((void **)&tmp_a_string);
7236 if(ad && !ad->next && !ad->personal){
7237 RFC822BUFFER rbuf;
7238 size_t len;
7240 ad->personal = cpystr(fn);
7241 len = est_size(ad);
7242 new_addr = (char *) fs_get(len * sizeof(char));
7243 new_addr[0] = '\0';
7244 /* this will quote it if it needs quoting */
7245 rbuf.f = dummy_soutr;
7246 rbuf.s = NULL;
7247 rbuf.beg = new_addr;
7248 rbuf.cur = new_addr;
7249 rbuf.end = new_addr+len-1;
7250 rfc822_output_address_list(&rbuf, ad, 0L, NULL);
7251 *rbuf.cur = '\0';
7252 fs_give((void **) &addr);
7253 addr = new_addr;
7256 if(ad)
7257 mail_free_address(&ad);
7259 fs_give((void **)&fn);
7262 if((enc_addr = rfc1738_encode_mailto(addr)) != NULL){
7263 size_t l;
7265 l = strlen(enc_addr) + 7;
7266 path = (char *) fs_get((l+1) * sizeof(char));
7267 snprintf(path, l+1, "mailto:%s", enc_addr);
7268 path[l] = '\0';
7269 fs_give((void **)&enc_addr);
7272 fs_give((void **)&addr);
7274 if(path){
7275 h = new_handle(handlesp);
7276 h->type = URL;
7277 h->h.url.path = path;
7278 snprintf(buf, sizeof(buf), "%d", h->key);
7279 buf[sizeof(buf)-1] = '\0';
7282 * Don't try to fold this address. Just put it on
7283 * one line and let scrolltool worry about
7284 * cutting it off before it goes past the
7285 * right hand edge. Otherwise, we have to figure
7286 * out how to handle the wrapped handle.
7288 snprintf(obuf, sizeof(obuf), "%c%c%c%s%s%c%c%c%c",
7289 TAG_EMBED, TAG_HANDLE,
7290 (int) strlen(buf), buf, vals[i]->bv_val,
7291 TAG_EMBED, TAG_BOLDOFF,
7292 TAG_EMBED, TAG_INVOFF);
7293 obuf[sizeof(obuf)-1] = '\0';
7295 so_puts(store, (i==0) ? hdr : hdr2);
7296 so_puts(store, obuf);
7297 so_puts(store, "\n");
7299 else{
7300 snprintf(obuf, sizeof(obuf), "%s", vals[i]->bv_val);
7301 obuf[sizeof(obuf)-1] = '\0';
7303 if((tmp = fold(obuf, width, width,
7304 (i==0) ? hdr : hdr2,
7305 repeat_char(indent+2, SPACE),
7306 FLD_NONE)) != NULL){
7307 so_puts(store, tmp);
7308 fs_give((void **)&tmp);
7312 else{
7313 int is_binary = 0;
7314 char *tmp2;
7316 for(tmp = strchr(a, ';'); tmp != NULL; tmp = tmp2){
7317 tmp2 = strchr(++tmp, ';');
7318 if(tmp2)
7319 *tmp2 = '\0';
7320 is_binary = !strucmp(tmp, "binary");
7321 if(tmp2)
7322 *tmp2 = ';';
7323 if(is_binary)
7324 break;
7327 snprintf(obuf, sizeof(obuf), "%s", (is_binary &&
7328 vals[i]->bv_val && vals[i]->bv_val[0] != '\0') ?
7329 _("[ Binary Data ]") : vals[i]->bv_val);
7330 obuf[sizeof(obuf)-1] = '\0';
7332 if((tmp = fold(obuf, width, width,
7333 (i==0) ? hdr : hdr2,
7334 repeat_char(indent+2, SPACE),
7335 FLD_NONE)) != NULL){
7336 so_puts(store, tmp);
7337 fs_give((void **)&tmp);
7342 ldap_value_free_len(vals);
7344 else{
7345 utf8_snprintf(obuf, sizeof(obuf), "%-*.*w\n", indent-1,indent-1,
7346 ldap_translate(a, winning_e->info_used));
7347 obuf[sizeof(obuf)-1] = '\0';
7348 so_puts(store, obuf);
7352 our_ldap_memfree(a);
7355 if(cn)
7356 ldap_value_free_len(cn);
7358 return(store);
7363 process_ldap_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
7365 int rv = 1;
7367 ps_global->next_screen = SCREEN_FUN_NULL;
7369 switch(cmd){
7370 case MC_SAVE :
7371 save_ldap_entry(ps_global, sparms->proc.data.p, 0);
7372 rv = 0;
7373 break;
7375 case MC_COMPOSE :
7376 compose_to_ldap_entry(ps_global, sparms->proc.data.p, 0);
7377 rv = 0;
7378 break;
7380 case MC_ROLE :
7381 compose_to_ldap_entry(ps_global, sparms->proc.data.p, 1);
7382 rv = 0;
7383 break;
7385 default:
7386 alpine_panic("Unexpected command in process_ldap_cmd");
7387 break;
7390 return(rv);
7395 pico_simpleexit(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
7396 char **result)
7398 if(result)
7399 *result = NULL;
7401 return(0);
7404 char *
7405 pico_simplecancel(void (*redraw_pico)(void))
7407 return("Cancelled");
7412 * Store query parameters so that they can be recalled by user later with ^R.
7414 void
7415 save_query_parameters(SAVED_QUERY_S *params)
7417 free_saved_query_parameters();
7418 saved_params = params;
7422 SAVED_QUERY_S *
7423 copy_query_parameters(SAVED_QUERY_S *params)
7425 SAVED_QUERY_S *sq;
7427 sq = (SAVED_QUERY_S *)fs_get(sizeof(SAVED_QUERY_S));
7428 memset((void *)sq, 0, sizeof(SAVED_QUERY_S));
7430 if(params && params->qq)
7431 sq->qq = cpystr(params->qq);
7432 else
7433 sq->qq = cpystr("");
7435 if(params && params->cn)
7436 sq->cn = cpystr(params->cn);
7437 else
7438 sq->cn = cpystr("");
7440 if(params && params->sn)
7441 sq->sn = cpystr(params->sn);
7442 else
7443 sq->sn = cpystr("");
7445 if(params && params->gn)
7446 sq->gn = cpystr(params->gn);
7447 else
7448 sq->gn = cpystr("");
7450 if(params && params->mail)
7451 sq->mail = cpystr(params->mail);
7452 else
7453 sq->mail = cpystr("");
7455 if(params && params->org)
7456 sq->org = cpystr(params->org);
7457 else
7458 sq->org = cpystr("");
7460 if(params && params->unit)
7461 sq->unit = cpystr(params->unit);
7462 else
7463 sq->unit = cpystr("");
7465 if(params && params->country)
7466 sq->country = cpystr(params->country);
7467 else
7468 sq->country = cpystr("");
7470 if(params && params->state)
7471 sq->state = cpystr(params->state);
7472 else
7473 sq->state = cpystr("");
7475 if(params && params->locality)
7476 sq->locality = cpystr(params->locality);
7477 else
7478 sq->locality = cpystr("");
7480 if(params && params->custom)
7481 sq->custom = cpystr(params->custom);
7482 else
7483 sq->custom = cpystr("");
7485 return(sq);
7489 void
7490 free_saved_query_parameters(void)
7492 if(saved_params)
7493 free_query_parameters(&saved_params);
7497 void
7498 free_query_parameters(SAVED_QUERY_S **parm)
7500 if(parm){
7501 if(*parm){
7502 if((*parm)->qq)
7503 fs_give((void **)&(*parm)->qq);
7504 if((*parm)->cn)
7505 fs_give((void **)&(*parm)->cn);
7506 if((*parm)->sn)
7507 fs_give((void **)&(*parm)->sn);
7508 if((*parm)->gn)
7509 fs_give((void **)&(*parm)->gn);
7510 if((*parm)->mail)
7511 fs_give((void **)&(*parm)->mail);
7512 if((*parm)->org)
7513 fs_give((void **)&(*parm)->org);
7514 if((*parm)->unit)
7515 fs_give((void **)&(*parm)->unit);
7516 if((*parm)->country)
7517 fs_give((void **)&(*parm)->country);
7518 if((*parm)->state)
7519 fs_give((void **)&(*parm)->state);
7520 if((*parm)->locality)
7521 fs_give((void **)&(*parm)->locality);
7522 if((*parm)->custom)
7523 fs_give((void **)&(*parm)->custom);
7525 fs_give((void **)parm);
7532 * A callback from pico to restore the saved query parameters.
7534 * Args he -- Unused.
7535 * s -- The place to return the allocated array of values.
7537 * Returns -- 1 if there are parameters to return, 0 otherwise.
7540 restore_query_parameters(struct headerentry *he, char ***s)
7542 int retval = 0, i = 0;
7544 if(s)
7545 *s = NULL;
7547 if(saved_params && s){
7548 *s = (char **)fs_get((QQ_END + 1) * sizeof(char *));
7549 (*s)[i++] = cpystr(saved_params->qq ? saved_params->qq : "");
7550 (*s)[i++] = cpystr(saved_params->cn ? saved_params->cn : "");
7551 (*s)[i++] = cpystr(saved_params->sn ? saved_params->sn : "");
7552 (*s)[i++] = cpystr(saved_params->gn ? saved_params->gn : "");
7553 (*s)[i++] = cpystr(saved_params->mail ? saved_params->mail : "");
7554 (*s)[i++] = cpystr(saved_params->org ? saved_params->org : "");
7555 (*s)[i++] = cpystr(saved_params->unit ? saved_params->unit : "");
7556 (*s)[i++] = cpystr(saved_params->country ? saved_params->country : "");
7557 (*s)[i++] = cpystr(saved_params->state ? saved_params->state : "");
7558 (*s)[i++] = cpystr(saved_params->locality ? saved_params->locality :"");
7559 (*s)[i++] = cpystr(saved_params->custom ? saved_params->custom :"");
7560 (*s)[i] = 0;
7561 retval = 1;
7564 return(retval);
7569 * Internal handler for viewing an LDAP url.
7572 url_local_ldap(char *url)
7574 LDAP *ld;
7575 struct timeval t;
7576 int ld_err, mangled = 0, we_cancel, retval = 0, proto = 3;
7577 int we_turned_on = 0;
7578 char ebuf[300];
7579 LDAPMessage *result;
7580 LDAP_SERV_S *info;
7581 LDAP_SERV_RES_S *serv_res = NULL;
7582 LDAPURLDesc *ldapurl = NULL;
7583 WP_ERR_S wp_err;
7585 dprint((2, "url_local_ldap(%s)\n", url ? url : "?"));
7587 ld_err = ldap_url_parse(url, &ldapurl);
7588 if(ld_err || !ldapurl){
7589 snprintf(ebuf, sizeof(ebuf), "URL parse failed for %s", url);
7590 ebuf[sizeof(ebuf)-1] = '\0';
7591 q_status_message(SM_ORDER, 3, 5, ebuf);
7592 return(retval);
7595 if(!ldapurl->lud_host){
7596 /* TRNASLATORS: No host in <url> */
7597 snprintf(ebuf, sizeof(ebuf), _("No host in %s"), url);
7598 ebuf[sizeof(ebuf)-1] = '\0';
7599 q_status_message(SM_ORDER, 3, 5, ebuf);
7600 ldap_free_urldesc(ldapurl);
7601 return(retval);
7604 we_turned_on = intr_handling_on();
7605 we_cancel = busy_cue(_("Searching for LDAP url"), NULL, 0);
7606 ps_global->mangled_footer = 1;
7608 #ifdef _SOLARIS_SDK
7609 if((ld = ldap_init(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7610 #else
7611 #if (LDAPAPI >= 11)
7612 #ifdef _WINDOWS
7613 if((ld = ldap_init(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7614 #else
7615 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "ldap://%s:%d", ldapurl->lud_host, ldapurl->lud_port);
7616 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
7617 if(ldap_initialize(&ld, tmp_20k_buf) != LDAP_SUCCESS)
7618 #endif /* _WINDOWS */
7619 #else
7620 if((ld = ldap_open(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
7621 #endif /* LDAPAPI >= 11 */
7622 #endif /* _SOLARIS_SDK */
7624 if(we_cancel){
7625 cancel_busy_cue(-1);
7626 we_cancel = 0;
7629 q_status_message(SM_ORDER,3,5, _("LDAP search failed: can't initialize"));
7631 else if(!ps_global->intr_pending){
7632 if(ldap_v3_is_supported(ld) &&
7633 our_ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) == 0){
7634 dprint((5, "ldap: using version 3 protocol\n"));
7638 * If we don't set RESTART then the select() waiting for the answer
7639 * in libldap will be interrupted and stopped by our busy_cue.
7641 our_ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
7643 t.tv_sec = 30; t.tv_usec = 0;
7644 #ifdef _WINDOWS
7645 ld_err = ldap_search_st(ld, ldapurl->lud_dn, ldapurl->lud_scope,
7646 ldapurl->lud_filter, ldapurl->lud_attrs,
7647 0, &t, &result);
7648 #else
7649 ld_err = ldap_search_ext_s(ld, ldapurl->lud_dn, ldapurl->lud_scope,
7650 ldapurl->lud_filter, ldapurl->lud_attrs, 0,
7651 NULL, NULL,
7652 &t, DEF_LDAP_SIZE, &result);
7653 #endif
7654 if(ld_err != LDAP_SUCCESS){
7655 if(we_cancel){
7656 cancel_busy_cue(-1);
7657 we_cancel = 0;
7660 snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s"), ldap_err2string(ld_err));
7661 ebuf[sizeof(ebuf)-1] = '\0';
7662 q_status_message(SM_ORDER, 3, 5, ebuf);
7663 #ifdef _WINDOWS
7664 ldap_unbind(ld);
7665 #else
7666 ldap_unbind_ext(ld, NULL, NULL);
7667 #endif /* _WINDOWS */
7669 else if(!ps_global->intr_pending){
7670 if(we_cancel){
7671 cancel_busy_cue(-1);
7672 we_cancel = 0;
7675 if(we_turned_on){
7676 intr_handling_off();
7677 we_turned_on = 0;
7680 if(ldap_count_entries(ld, result) == 0){
7681 q_status_message(SM_ORDER, 3, 5, _("No matches found for url"));
7682 #ifdef _WINDOWS
7683 ldap_unbind(ld);
7684 #else
7685 ldap_unbind_ext(ld, NULL, NULL);
7686 #endif /* _WINDOWS */
7687 if(result)
7688 ldap_msgfree(result);
7690 else{
7691 serv_res = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
7692 memset((void *)serv_res, 0, sizeof(*serv_res));
7693 serv_res->ld = ld;
7694 serv_res->res = result;
7695 info = (LDAP_SERV_S *)fs_get(sizeof(LDAP_SERV_S));
7696 memset((void *)info, 0, sizeof(*info));
7697 info->mailattr = cpystr(DEF_LDAP_MAILATTR);
7698 info->snattr = cpystr(DEF_LDAP_SNATTR);
7699 info->gnattr = cpystr(DEF_LDAP_GNATTR);
7700 info->cnattr = cpystr(DEF_LDAP_CNATTR);
7701 serv_res->info_used = info;
7702 memset(&wp_err, 0, sizeof(wp_err));
7703 wp_err.mangled = &mangled;
7705 ask_user_which_entry(serv_res, NULL, NULL, &wp_err, DisplayForURL);
7706 if(wp_err.error){
7707 q_status_message(SM_ORDER, 3, 5, wp_err.error);
7708 fs_give((void **)&wp_err.error);
7711 if(mangled)
7712 ps_global->mangled_screen = 1;
7714 free_ldap_result_list(&serv_res);
7715 retval = 1;
7720 if(we_cancel)
7721 cancel_busy_cue(-1);
7723 if(we_turned_on)
7724 intr_handling_off();
7726 if(ldapurl)
7727 ldap_free_urldesc(ldapurl);
7729 return(retval);
7731 #endif /* ENABLE_LDAP */