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 /*======================================================================
17 Commands called from the addrbook screens.
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 *);
89 typedef struct _saved_query
{
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.
126 view_abook_entry(struct pine
*ps
, long int cur_line
)
129 STORE_S
*in_store
, *out_store
;
130 char *string
, *errstr
;
132 HANDLE_S
*handles
= NULL
;
135 int cmd
, abook_indent
;
139 dprint((5, "- view_abook_entry -\n"));
141 if(is_addr(cur_line
)){
144 q_status_message(SM_ORDER
, 0, 3, _("Error reading entry"));
149 q_status_message(SM_ORDER
, 0, 3, _("Nothing to view"));
153 if(!(in_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
154 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
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
);
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.
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
);
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
);
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
);
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
: "";
203 if(!strncmp(tmp
, QRUN_LDAP
, LEN_QRL
))
209 so_puts(in_store
, string
);
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
));
221 if(!strncmp(*ll
, QRUN_LDAP
, LEN_QRL
))
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
))){
239 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
240 _("Error allocating space."));
244 so_seek(in_store
, 0L, 0);
246 init_handles(&handles
);
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,
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
){
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
);
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
;
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
);
303 sargs
.keys
.menu
->how_many
= 2;
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
;
313 free_handles(&handles
);
315 while(cmd
== MC_RESIZE
);
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. */
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
)
360 if((lp
= strchr(line
, ':')) &&
361 !strncmp(line
, AB_COMMENT_STR
, strlen(AB_COMMENT_STR
)))
362 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
369 process_abook_view_cmd(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
374 static ESCKEY_S text_or_vcard
[] = {
375 /* TRANSLATORS: Text refers to plain old text, probably the text of
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
;
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
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"));
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;
430 if(pab
&& pab
->access
== ReadOnly
){
431 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
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
;
446 dl
= dlist(as
.top_ent
+as
.cur_row
);
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
;
463 rv
= 1; /* causes scrolltool to return */
466 q_status_message(SM_ORDER
, 0, 4,
467 "Something wrong, entry not updateable");
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);
487 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 0);
492 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 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
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
);
508 q_status_message(SM_INFO
, 0, 2, _("Address book forward cancelled"));
513 forward_text(ps_global
, sparms
->text
.text
, sparms
->text
.src
);
517 (void)ab_forward(ps_global
, as
.top_ent
+as
.cur_row
, 0);
521 q_status_message(SM_ORDER
, 3, 3,
522 "can't happen in process_abook_view_cmd");
529 alpine_panic("Unexpected command in process_abook_view_cmd");
538 * Give expanded view of this address entry.
539 * Call scrolltool to do the work.
541 * Args: headents -- The headerentry array from pico.
544 * Returns -- Always 0.
547 expand_addrs_for_pico(struct headerentry
*headents
, char ***s
)
551 char *error
= NULL
, *addr
= NULL
, *fullname
= NULL
, *address
= NULL
;
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
;
560 char fakeaddrpmt
[500];
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"));
571 ps_global
->redrawer
= NULL
;
572 fix_windsize(ps_global
);
578 headents
[j
].name
!= NULL
&& (address_index
< 0 || fullname_index
< 0);
580 if(!strncmp(headents
[j
].name
, "Address", 7) || !strncmp(headents
[j
].name
, _("Address"), strlen(_("Address"))))
582 else if(!strncmp(headents
[j
].name
, "Fullname", 8) || !strncmp(headents
[j
].name
, _("Fullname"), strlen(_("Fullname"))))
586 if(address_index
>= 0)
587 address
= *headents
[address_index
].realaddr
;
589 address_index
= 1000; /* a big number */
593 if(fullname_index
>= 0)
594 fullname
= adrbk_formatname(*headents
[fullname_index
].realaddr
,
597 memset(&abe
, 0, sizeof(abe
));
599 abe
.fullname
= cpystr(fullname
);
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
){
611 for(a
= adrlist
; a
; a
= a
->next
)
614 abe
.addr
.list
= (char **)fs_get((cnt
+1) * sizeof(char *));
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
,
625 abe
.addr
.list
[cnt
] = NULL
;
629 abe
.addr
.addr
= address
;
633 mail_free_address(&adrlist
);
636 bldto
.arg
.abe
= &abe
;
637 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
639 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
640 fs_give((void **)&error
);
644 tmp_a_string
= cpystr(addr
);
645 rfc822_parse_adrlist(&adrlist
, tmp_a_string
, ps_global
->maildomain
);
646 fs_give((void **)&tmp_a_string
);
650 else if(no_a_fld
&& expander_address
){
653 memset(&wp_err
, 0, sizeof(wp_err
));
654 adrlist
= wp_lookups(expander_address
, &wp_err
, 0);
655 if(fullname
&& *fullname
){
657 if(adrlist
->personal
)
658 fs_give((void **)&adrlist
->personal
);
660 adrlist
->personal
= cpystr(fullname
);
665 q_status_message1(SM_ORDER
, 3, 4, "%s", wp_err
.error
);
666 fs_give((void **)&wp_err
.error
);
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
);
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
,
682 repeat_char(headents
[j
].prwid
, SPACE
), FLD_NONE
)) != NULL
){
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
));
693 for(a
= adrlist
; a
; a
= a
->next
){
701 bufp
= (char *) fs_get(len
* sizeof(char));
702 (void) addr_string(a
, bufp
, len
);
706 * Another assumption, all the prwids are the same.
709 ps_global
->ttyo
->screen_cols
,
710 ps_global
->ttyo
->screen_cols
,
711 (a
== adrlist
) ? (no_a_fld
713 : headents
[address_index
].prompt
)
715 tmp3
, FLD_NONE
)) != NULL
){
717 fs_give((void **) &tmp
);
720 fs_give((void **) &bufp
);
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
){
732 fs_give((void **) &tmp
);
736 fs_give((void **)&tmp2
);
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
);
756 restore_state(&state
);
760 fs_give((void **)&addr
);
763 mail_free_address(&adrlist
);
766 fs_give((void **)&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
;
781 * Callback from TakeAddr editing screen to see message that was being
782 * viewed. Call scrolltool to do the work.
785 view_message_for_pico(char **error
)
789 void (*redraw
)(void) = ps_global
->redrawer
;
790 SourceType src
= CharStar
;
793 dprint((5, "- view_message_for_pico -\n"));
795 ps_global
->redrawer
= NULL
;
796 fix_windsize(ps_global
);
802 if(!(store
= so_get(src
, NULL
, EDIT_ACCESS
))){
803 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
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
);
828 ps_global
->redrawer
= redraw
;
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
}
867 #define NNN_COMMENT 3
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
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.)
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
;
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
;
911 char nickpmt
[100], fullpmt
[100], fccpmt
[100], cmtpmt
[100], addrpmt
[100];
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
;
920 abook_saved_for_pico_check
= abook
;
922 add
= (abe
== NULL
); /* doing add or change? */
924 local_abe
.nickname
= "";
925 local_abe
.fullname
= "";
927 local_abe
.extra
= "";
928 local_abe
.addr
.addr
= "";
929 local_abe
.tag
= NotSet
;
931 old_entry_num
= NO_NEXT
;
937 expander_address
= NULL
;
938 if(abe
->tag
== Single
&&
940 !strncmp(abe
->addr
.addr
,QRUN_LDAP
,LEN_QRL
)){
942 expander_address
= cpystr(abe
->addr
.addr
);
943 removing_double_quotes(expander_address
);
945 else if(abe
->tag
== List
&&
948 !abe
->addr
.list
[1] &&
949 !strncmp(abe
->addr
.list
[0],QRUN_LDAP
,LEN_QRL
)){
951 expander_address
= cpystr(abe
->addr
.list
[0]);
952 removing_double_quotes(expander_address
);
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 */
964 /* TRANSLATORS: screen titles */
965 snprintf(titlebar
, sizeof(titlebar
), _("ADDRESS BOOK (View)"));
967 snprintf(titlebar
, sizeof(titlebar
), _("ADDRESS BOOK (%c%s)"),
968 islower((unsigned char)(*cmd
))
969 ? toupper((unsigned char)*cmd
)
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
;
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.
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."));
998 _("\n To form a list, just enter multiple comma-separated addresses."));
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."));
1006 _("\n Since this entry does a directory lookup you may not edit the address field."));
1009 _("\n Additional comma-separated addresses may be entered in the address field."));
1013 _("\n Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."));
1015 _("\n If you want to use quotation marks inside the Fullname field, it is best"));
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
));
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;
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
){
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)
1087 for(q
= orig_addrarray
, p
= abe
->addr
.list
; p
&& *p
; p
++, q
++)
1094 /* figure out how large a string we need to allocate */
1096 for(p
= orig_addrarray
; p
&& *p
; p
++)
1097 length
+= (strlen(*p
) + 2);
1102 pp
= comma_sep_addr
= (char *) fs_get((size_t)(length
+1L) * sizeof(char));
1104 for(p
= orig_addrarray
; p
&& *p
; p
++){
1105 sstrncpy(&pp
, *p
, length
-(pp
-comma_sep_addr
));
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 */
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
)
1137 fix_windsize(ps_global
);
1141 if(editor_result
& COMP_CANCEL
){
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
)
1168 * comma_sep_addr is now the string which has been edited
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 */
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
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){
1195 if(!list_changed
&& he
[NNN_ADDR
].dirty
1196 && ((!(p
&& *p
) && (q
&& *q
)) || ((p
&& *p
) && !(q
&& *q
))))
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
);
1206 /* don't need new_addrarray */
1207 free_list_array(&new_addrarray
);
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.
1227 rc
= adrbk_add(abook
,
1228 (a_c_arg_t
)old_entry_num
,
1230 he
[NNN_FULL
].dirty
? full
: abe
->fullname
,
1231 new_tag
== Single
? (ldap
== 1 ? abe
->addr
.addr
:
1232 ldap
== 2 ? abe
->addr
.list
[0] :
1236 he
[NNN_COMMENT
].dirty
? comment
: abe
->extra
,
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");
1257 && strucmp(nick
, nick_saved_for_pico_check
) != 0
1258 && (editor_result
& COMP_EXIT
)){
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
)
1265 if(added_to
>= as
.n_addrbk
)
1268 fname
= addr_lookup(nick
, &which_addrbook
, added_to
);
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
;
1291 dlc_restart
.type
= DlcSimple
;
1295 dlc_restart
.type
= DlcListHead
;
1302 warp_to_dlc(&dlc_restart
, 0L);
1315 fs_give((void **)&nick
);
1317 fs_give((void **)&full
);
1319 fs_give((void **)&fcc
);
1321 fs_give((void **)&comment
);
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
);
1331 if(expander_address
)
1332 fs_give((void **) &expander_address
);
1339 verify_nick(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
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
);
1351 if(ps_global
->mangled_screen
)
1352 *mangled
|= BUILDER_SCREEN_MANGLED
;
1353 else if(ps_global
->mangled_footer
)
1354 *mangled
|= BUILDER_FOOTER_MANGLED
;
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
;
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
)){
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
);
1379 fs_give((void **)&tmp
);
1381 if(ps_global
->mangled_screen
)
1382 *mangled
|= BUILDER_SCREEN_MANGLED
;
1383 else if(ps_global
->mangled_footer
)
1384 *mangled
|= BUILDER_FOOTER_MANGLED
;
1394 fs_give((void **)&tmp
);
1396 /* This is so pico will erase any old message */
1398 *error
= cpystr("");
1401 if(ps_global
->mangled_screen
)
1402 *mangled
|= BUILDER_SCREEN_MANGLED
;
1403 else if(ps_global
->mangled_footer
)
1404 *mangled
|= BUILDER_FOOTER_MANGLED
;
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
)
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
++)
1446 *full_to
= cpystr(to
? to
: ""); /* because pico does a strcmp() */
1452 *full_to
= (char *)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..."));
1472 "RESETTING address book... verify_addr(%s)!\n", to
? to
: "?"));
1474 ab_nesting_level
= *save_nesting_level
;
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
;
1486 ret_val
= build_address_internal(bldto
, full_to
, error
, NULL
, NULL
, NULL
,
1487 save_and_restore
, 1, mangled
);
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("");
1501 ret_val
= -2; /* cause pico to stay on same header line */
1503 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
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
)
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
)){
1530 rstr
= _("Use ^C to abandon changes you've made");
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.
1549 pico_cancelexit_for_adrbk(char *word
, void (*redraw_pico
)(void))
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
)){
1570 ps_global
->redrawer
= redraw
;
1576 pico_cancel_for_adrbk_take(void (*redraw_pico
)(void))
1578 return(pico_cancelexit_for_adrbk(_("take"), redraw_pico
));
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
}
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
1620 * Returns: addrbook number of new addrbook, or
1621 * -1, no addrbook added
1624 ab_add_abook(int global
, int add_after_this
)
1628 dprint((2, "- ab_add_abook -\n"));
1630 ret
= ab_modify_abook_list(0, global
, add_after_this
, NULL
, NULL
, NULL
);
1633 q_status_message(SM_ORDER
, 0, 3,
1634 _("New address book added. Use \"$\" to adjust order"));
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,
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
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
;
1683 int editor_result
, i
, how_many_in_list
= 0, new_abook_num
, num_in_list
;
1685 char *server
, *folder
, *nickname
;
1686 char *new_item
= NULL
;
1688 AccessType remember_access_result
;
1691 char **list
, **new_list
= NULL
;
1692 char tmp
[1000+MAXFOLDER
];
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
){
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"));
1705 /* TRANSLATORS: Add was the command that is being cancelled. */
1706 q_status_message(SM_ORDER
, 0, 3, _("Add cancelled: config file not changeable"));
1713 if((global
&& vars
[V_GLOB_ADDRBOOK
].is_fixed
) ||
1714 (!global
&& vars
[V_ADDRESSBOOK
].is_fixed
)){
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"));
1720 q_status_message(SM_ORDER
, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing address books"));
1725 init_ab_if_needed();
1729 (abook_num
< 0 || abook_num
>= as
.how_many_personals
)) ||
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");
1738 the_one_were_editing
= abook_num
;
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
;
1747 /* TRANSLATORS: screen title */
1748 strncpy(titlebar
, _("CHANGE ADDRESS BOOK"), sizeof(titlebar
));
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
){
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.");
1769 _(" To add an address book that will be accessed by IMAP, fill in the\n server name.");
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.)");
1773 _(" In the Folder field, type the remote folder name or local file name.");
1775 _(" In the Nickname field, give the address book a nickname or leave it blank.");
1777 _(" To get help specific to an item, press ^G.");
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");
1798 if(lines_avail
>= 2){
1803 if(lines_avail
>= 5){
1804 so_puts(msgso
, "\n\n");
1809 else if(lines_avail
>= 3){
1810 so_puts(msgso
, "\n\n");
1815 if(lines_avail
>= 2){
1816 so_puts(msgso
, "\n\n");
1821 if(lines_avail
>= 2){
1822 so_puts(msgso
, "\n\n");
1827 if(lines_avail
>= 3){
1828 so_puts(msgso
, "\n\n");
1830 so_puts(msgso
, "\n");
1833 else if(lines_avail
>= 2){
1834 so_puts(msgso
, "\n\n");
1839 he
= (struct headerentry
*)fs_get((NN_END
+1) * sizeof(struct headerentry
));
1840 memset((void *)he
, 0, (NN_END
+1) * sizeof(struct headerentry
));
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
){
1878 fix_windsize(ps_global
);
1882 if(editor_result
& COMP_CANCEL
){
1885 q_status_message(SM_ORDER
, 0, 3, _("Address book change is cancelled"));
1887 q_status_message(SM_ORDER
, 0, 3, _("Address book add is cancelled"));
1889 else if(editor_result
& COMP_EXIT
){
1891 !strcmp(server
, def_serv
? def_serv
: "") &&
1892 !strcmp(folder
, def_fold
? def_fold
: "") &&
1893 !strcmp(nickname
, def_nick
? def_nick
: "")){
1896 q_status_message(SM_ORDER
, 0, 3, _("No change: Address book change is cancelled"));
1898 q_status_message(SM_ORDER
, 0, 3, _("No change: Address book add is cancelled"));
1902 list
= VAR_GLOB_ADDRBOOK
;
1903 how_many_in_list
= as
.n_addrbk
- as
.how_many_personals
;
1905 new_abook_num
= abook_num
;
1906 else if(abook_num
< 0)
1907 new_abook_num
= as
.n_addrbk
;
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
;
1915 list
= VAR_ADDRESSBOOK
;
1916 how_many_in_list
= as
.how_many_personals
;
1917 new_abook_num
= abook_num
;
1919 new_abook_num
= abook_num
;
1920 else if(abook_num
< 0)
1921 new_abook_num
= as
.how_many_personals
;
1923 new_abook_num
= MIN(abook_num
+ 1, as
.how_many_personals
);
1925 num_in_list
= new_abook_num
;
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 */
1939 conv
= convert_to_utf8(nickname
, NULL
, 0);
1941 fs_give((void **) &nickname
);
1946 /* eliminate surrounding brackets */
1947 if(server
[0] == '{' && server
[strlen(server
)-1] == '}'){
1950 server
[strlen(server
)-1] = '\0';
1951 for(p
= server
; *p
; p
++)
1955 snprintf(tmp
, sizeof(tmp
), "%s%s%s%.*s",
1957 *server
? server
: "",
1960 tmp
[sizeof(tmp
)-1] = '\0';
1962 new_item
= put_pair(nickname
, tmp
);
1964 if(!new_item
|| *new_item
== '\0'){
1966 q_status_message(SM_ORDER
, 0, 3, _("Address book change is cancelled"));
1968 q_status_message(SM_ORDER
, 0, 3, _("Address book add is cancelled"));
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)]);
1990 /* this frees old variable contents for us */
1991 if(set_variable_list(global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
,
1992 new_list
, TRUE
, ew
)){
1994 q_status_message(SM_ORDER
, 0, 3, _("Change cancelled: couldn't save configuration file"));
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
],
2004 ret
= new_abook_num
;
2005 set_current_val(&vars
[global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
],
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
;
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
;
2034 free_list_array(&new_list
);
2037 fs_give((void **)&new_item
);
2043 fs_give((void **)&server
);
2045 fs_give((void **)&folder
);
2047 fs_give((void **)&nickname
);
2054 any_addrbooks_to_convert(struct pine
*ps
)
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
))
2072 convert_addrbooks_to_remote(struct pine
*ps
, char *rem_folder_prefix
, size_t len
)
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
))
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
);
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
;
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);
2127 strncpy(local_file
, pab
->filename
, sizeof(local_file
)-1);
2128 local_file
[sizeof(local_file
)-1] = '\0';
2130 p
= strrindex(pab
->filename
, '\\');
2132 p
= strrindex(pab
->filename
, '/');
2136 strncpy(rem_abook
, rem_folder_prefix
, sizeof(rem_abook
)-3);
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';
2145 rc
= optionally_enter(rem_abook
, -FOOTER_ROWS(ps
), 0,
2146 sizeof(rem_abook
), prompt
, NULL
,
2148 removing_leading_and_trailing_white_space(rem_abook
);
2150 help
= help
== NO_HELP
? h_convert_pinerc_server
: NO_HELP
;
2153 cmd_cancelled(NULL
);
2159 offset
= strlen(rem_abook
);
2160 for(i
= offset
; i
>= 0; i
--)
2161 rem_abook
[i
+1] = rem_abook
[i
];
2164 rem_abook
[++offset
] = '}';
2165 rem_abook
[++offset
] = '\0';
2174 strncat(rem_abook
, p
+1,
2175 sizeof(rem_abook
)-1-strlen(rem_abook
));
2177 strncat(rem_abook
, DEF_ABOOK_NAME
,
2178 sizeof(rem_abook
)-1-strlen(rem_abook
));
2182 file
= cpystr(rem_abook
);
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
);
2192 nick
= cpystr("Remote Address Book");
2194 if(file
&& *file
== '{'){
2196 if((p
= strindex(file
, '}'))){
2202 fs_give((void **)&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 */
2215 EditWhich ew
= Main
;
2217 lval
= LVAL(&ps
->vars
[V_ADDRESSBOOK
], ew
);
2218 get_pair(lval
[abook_num
], &nick
, &file
, 0, 0);
2220 fs_give((void **)&nick
);
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
)){
2232 q_status_message(SM_ORDER
| SM_DING
, 7, 10, err_msg
);
2233 fs_give((void **)&err_msg
);
2238 else if(abook_num
>= 0){ /* give user some info */
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."));
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
;
2286 so_give(&store
); /* free resources associated with store */
2287 ps
->mangled_screen
= 1;
2295 any_sigs_to_convert(struct pine
*ps
)
2297 char *sigfile
, *litsig
;
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
))
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
))
2337 any_rule_files_to_warn_about(struct pine
*ps
)
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
);
2348 pat
= next_pattern(&pstate
)){
2349 if(pat
->patline
&& pat
->patline
->type
== File
)
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
];
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
)){
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
2387 _("Convert signature file \"%s\" to a literal sig "),
2389 prompt
[sizeof(prompt
)-1] = '\0';
2391 ps
->mangled_body
= 1;
2392 if((ans
=want_to(prompt
, 'y', 'x', h_convert_sig
, WT_NORM
)) == 'x'){
2393 cmd_cancelled(NULL
);
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
);
2405 fs_give((void **)&cstring_version
);
2407 fs_give((void **)&src
);
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."));
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
);
2422 _("\nto a literal signature, which means it is contained in your"));
2424 _("\nAlpine configuration instead of being in a file of its own."));
2426 _("\nIf that configuration is copied to a remote folder then the"));
2428 _("\nsignature will be available remotely also."));
2430 _("\nChanges to the signature file itself will no longer have any"));
2432 _("\neffect on Alpine but you may still edit the signature with the"));
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
;
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
)){
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 "),
2478 nick
? " in role \"" : "",
2481 prompt
[sizeof(prompt
)-1] = '\0';
2483 ps
->mangled_body
= 1;
2484 if((ans
=want_to(prompt
, 'y', 'x',
2485 h_convert_sig
, WT_NORM
)) == 'x'){
2486 cmd_cancelled(NULL
);
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;
2510 if(write_patterns(rflags
) == 0){
2513 * Flush out current_vals of anything we've
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."));
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"),
2529 nick
? " in role \"" : "",
2532 prompt
[sizeof(prompt
)-1] = '\0';
2533 so_puts(store
, prompt
);
2535 /* TRANSLATORS: several lines that go together */
2536 _("\nto a literal signature, which means it is contained in your"));
2538 _("\nAlpine configuration instead of being in a file of its own."));
2540 _("\nIf that configuration is copied to a remote folder then the"));
2542 _("\nsignature will be available remotely also."));
2544 _("\nChanges to the signature file itself will no longer have any"));
2546 _("\neffect on Alpine. You may edit the signature with the"));
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
;
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
;
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."));
2572 /* TRANSLATORS: sig is signature */
2573 fprintf(stderr
, _("Error converting role sig\n"));
2587 warn_about_rule_files(struct pine
*ps
)
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."));
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
;
2618 ps
->mangled_screen
= 1;
2624 convert_to_remote_config(struct pine
*ps
, int edit_exceptions
)
2626 char rem_pinerc_prefix
[MAILTMPLEN
];
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"));
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
);
2656 /* TRANSLATORS: AddressBooks is Address Books */
2657 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert AddressBooks and signature files "));
2659 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert AddressBooks "));
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
,
2669 cmd_cancelled(NULL
);
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
)
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
);
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
);
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."));
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
;
2738 int flags
= OE_APPEND_CURRENT
;
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';
2750 rc
= optionally_enter(rem_pinerc
, -FOOTER_ROWS(ps
), 0,
2751 sizeof(rem_pinerc
), prompt
, NULL
,
2753 removing_leading_and_trailing_white_space(rem_pinerc
);
2755 help
= help
== NO_HELP
? h_convert_pinerc_server
: NO_HELP
;
2758 cmd_cancelled(NULL
);
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';
2781 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2784 * Add a default folder name.
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]){
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';
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
){
2820 help
= (help
== NO_HELP
) ? h_convert_pinerc_folder
: NO_HELP
;
2822 else if(rc
== 1 || rem_pinerc
[0] == '\0'){
2823 cmd_cancelled(NULL
);
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;
2842 if(ps
->prc
->outstanding_pinerc_changes
)
2843 write_pinerc(ps
, Main
, WRP_NONE
);
2846 ps
->vars
[V_USER_ID
].is_used
= 0;
2849 /* copy the pinerc */
2850 if(copy_pinerc(ps
->prc
->name
, rem_pinerc
, &err_msg
)){
2852 q_status_message(SM_ORDER
| SM_DING
, 7, 10, err_msg
);
2853 fs_give((void **)&err_msg
);
2859 /* tell user about command line flags */
2860 if(ps
->prc
->type
!= RemImap
){
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."));
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"));
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."));
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."));
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
;
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
)
2930 tmp
= cpystr(given
? given
: "");
2931 removing_leading_and_trailing_white_space(tmp
);
2936 fs_give((void **)&tmp
);
2939 *error
= cpystr("");
2946 verify_server_name(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2950 tmp
= cpystr(given
? given
: "");
2951 removing_leading_and_trailing_white_space(tmp
);
2955 * could try to verify the hostname here
2962 fs_give((void **)&tmp
);
2965 *error
= cpystr("");
2972 verify_abook_nick(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2977 tmp
= cpystr(given
? given
: "");
2978 removing_leading_and_trailing_white_space(tmp
);
2980 if(strindex(tmp
, '"')){
2981 fs_give((void **)&tmp
);
2983 /* TRANSLATORS: Double quote refers to the " character */
2984 *error
= cpystr(_("Double quote not allowed in nickname"));
2989 for(i
= 0; i
< as
.n_addrbk
; i
++)
2990 if(i
!= the_one_were_editing
&& !strcmp(tmp
, as
.adrbks
[i
].abnick
))
2993 if(i
< as
.n_addrbk
){
2994 fs_give((void **)&tmp
);
2997 *error
= cpystr(_("Nickname is already being used"));
3005 fs_give((void **)&tmp
);
3008 *error
= cpystr("");
3015 * Delete an addressbook.
3017 * Args: cur_line -- The current line position (in global display list)
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
;
3033 struct variable
*vars
= ps_global
->vars
;
3039 DontChange
} modify_config
;
3041 /* restrict address book config to normal config file */
3044 if(ps_global
->readonly_pinerc
){
3046 *err
= _("Delete cancelled: config file not changeable");
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
){
3062 if(pab
->type
& GLOBAL
)
3064 _("Cancelled: Sys. Mgmt. does not allow changing global address book config");
3067 _("Cancelled: Sys. Mgmt. does not allow changing address book config");
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. */
3081 lval
= LVAL(&vars
[varnum
], ew
);
3083 for(t
= lval
; *t
!= NULL
; t
++)
3087 * Easy case, we can just delete one from the user's list.
3090 modify_config
= Modify
;
3093 * Also easy. We'll revert to the default if it exists, and warn
3094 * the user about that.
3097 modify_config
= RevertToDefault
;
3098 /* see if there's a default to revert to */
3100 if(vars
[varnum
].global_val
.l
&& vars
[varnum
].global_val
.l
[0])
3101 for(t
= vars
[varnum
].global_val
.l
; *t
!= NULL
; t
++)
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.
3113 /* count how many in default */
3115 if(vars
[varnum
].global_val
.l
&& vars
[varnum
].global_val
.l
[0])
3116 for(t
= vars
[varnum
].global_val
.l
; *t
!= NULL
; t
++)
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
)){
3136 modify_config
= OverRideDefault
;
3140 modify_config
= Modify
;
3145 *err
= _("Delete cancelled");
3151 /* TRANSLATORS: a question */
3152 switch(want_to(_("Delete this default address book from config "),
3153 'n', 'x', h_ab_del_default
, WT_NORM
)){
3157 *err
= _("Delete cancelled");
3162 modify_config
= OverRideDefault
;
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
,
3189 case 'b': /* Delete Both */
3193 case 'd': /* Delete only Data */
3194 modify_config
= DontChange
;
3198 case 'c': /* Delete only Config */
3201 case 'x': /* Cancel */
3204 *err
= _("Delete cancelled");
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 "),
3217 (modify_config
== RevertToDefault
)
3218 ? h_ab_del_config_revert
3219 : h_ab_del_config_modify
,
3225 *err
= _("Delete cancelled");
3237 dprint((5, "deleting addrbook data\n"));
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,
3250 * Couldn't get it open.
3252 if(pab
->address_book
== NULL
){
3254 /* TRANSLATORS: %s is an error message */
3255 snprintf(warning
, 300, _("Can't delete data: %s"), &warning
[300]);
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
;
3270 * We're about to destroy addrbook data, better ask again.
3272 if(pab
->address_book
->count
> 0){
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
)){
3287 *err
= _("Delete cancelled");
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
){
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
)){
3342 dprint((1, "Trouble deleting %s\n",
3343 origfile
? origfile
: "?"));
3347 if(can_access(file
, ACCESS_EXISTS
) == 0){
3349 dprint((1, "Trouble deleting %s\n",
3350 file
? file
: "?"));
3354 snprintf(warning
, sizeof(warning
), _("Trouble deleting data %s%s%s%s"),
3356 (f
&& o
) ? (o
? ", " : " and ") : "",
3359 warning
[sizeof(warning
)-1] = '\0';
3362 fs_give((void **) &file
);
3364 fs_give((void **) &origfile
);
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
);
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
;
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;
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 *));
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]);
3432 else if(modify_config
== OverRideDefault
){
3433 new_list
[0] = cpystr("");
3437 /* this also frees old variable contents for us */
3438 if(set_variable_list(varnum
, new_list
, TRUE
, ew
)){
3440 *err
= _("Delete cancelled: couldn't save pine configuration file");
3442 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3443 free_list_array(&new_list
);
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
);
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
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
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
)
3493 int i
, deefault
, rv
, target
= 0;
3494 int up_into_empty
= 0, down_into_empty
= 0;
3496 struct variable
*vars
= ps_global
->vars
;
3498 dprint((2, "- ab_shuffle() -\n"));
3502 if(ps_global
->readonly_pinerc
){
3504 *msg
= cpystr(_("Shuffle cancelled: config file not changeable"));
3509 /* Move it up or down? */
3514 /* TRANSLATORS: shuffle something Up or Down in a list */
3515 opts
[i
++].label
= N_("Up");
3520 opts
[i
++].label
= N_("Down");
3525 if(pab
->type
& GLOBAL
){
3526 if(vars
[V_GLOB_ADDRBOOK
].is_fixed
){
3528 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3542 if(as
.cur
== as
.n_addrbk
- 1) /* no down */
3546 if(vars
[V_ADDRESSBOOK
].is_fixed
){
3548 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book config"));
3553 if(as
.cur
== 0){ /* no up */
3558 if(as
.cur
== as
.n_addrbk
- 1){
3562 opts
[1].ch
= -2; /* no down */
3566 snprintf(tmp
, sizeof(tmp
), _("Shuffle \"%s\" %s%s%s ? "),
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
3576 rv
= radio_buttons(tmp
, command_line
, opts
, deefault
, 'x',
3579 ps_global
->mangled_footer
= 1;
3581 if((rv
== 'u' && up_into_empty
) || (rv
== 'd' && down_into_empty
))
3584 target
= as
.cur
+ (rv
== 'u' ? -1 : 1);
3588 *msg
= cpystr(_("Shuffle cancelled"));
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
)
3625 enum {NotSet
, Pers
, Glob
, Empty
} type1
, type2
;
3626 int i
, j
, retval
= -1;
3627 struct variable
*vars
= ps_global
->vars
;
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 */
3640 pab
= &as
.adrbks
[anum1
];
3641 type1
= (pab
->type
& GLOBAL
) ? Glob
: Pers
;
3647 cpystr(_("Shuffle cancelled: highlight entry you wish to shuffle"));
3655 pab
= &as
.adrbks
[anum2
];
3656 type2
= (pab
->type
& GLOBAL
) ? Glob
: Pers
;
3660 type2
= (type1
== Pers
) ? Glob
: Pers
;
3662 if((type1
== Pers
|| type2
== Pers
) && vars
[V_ADDRESSBOOK
].is_fixed
){
3664 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book configuration"));
3669 if((type1
== Glob
|| type2
== Glob
) && vars
[V_GLOB_ADDRBOOK
].is_fixed
){
3671 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
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
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
;
3689 *slide
= (anum1
< anum2
) ? LINES_PER_ABOOK
: -1 * LINES_PER_ABOOK
;
3692 how_many_in_list
= as
.how_many_personals
;
3693 list
= VAR_ADDRESSBOOK
;
3694 varnum
= V_ADDRESSBOOK
;
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 */
3714 else if(i
== anum2_rel
)
3719 new_list
[i
] = cpystr(list
[j
]);
3724 if(set_variable_list(varnum
, new_list
, TRUE
, ew
)){
3726 *msg
= cpystr(cancel_msg
);
3728 /* restore old values */
3729 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3730 free_list_array(&new_list
);
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;
3749 char **new_src
, **new_dst
, **srclist
, **dstlist
;
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
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
;
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.
3798 lval
= LVAL(&vars
[srcvarnum
], ew
);
3800 for(t
= lval
; *t
!= NULL
; t
++)
3804 * We'll revert to the default if it exists, and warn
3805 * the user about that.
3808 modify_config
= RevertToDefault
;
3809 /* see if there's a default to revert to */
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
++)
3816 warn_about_revert
= cnt
;
3817 if(warn_about_revert
> 1 && type1
== Pers
)
3818 *slide
= LINES_PER_ABOOK
* warn_about_revert
+
3822 * User is already using the default.
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 *));
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).
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
]);
3850 new_dst
[j
++] = cpystr(srclist
[0]);
3855 * The srclist is complicated by the reverting to default
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 *));
3866 for(i
= 0; i
< how_many_in_srclist
-1; i
++)
3867 new_src
[j
++] = cpystr(srclist
[i
+ ((type1
== Glob
) ? 1 : 0)]);
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("");
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.
3887 if(set_variable_list(dstvarnum
, new_dst
, TRUE
, ew
) ||
3888 set_variable_list(srcvarnum
, new_src
, TRUE
, ew
)){
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
);
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';
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
];
3935 as
.how_many_personals
--;
3936 pab
->type
|= GLOBAL
;
3937 if(pab
->access
!= NoAccess
)
3938 pab
->access
= ReadOnly
;
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';
3961 ab_compose_to_addr(long int cur_line
, int agg
, int allow_role
)
3968 dprint((2, "- ab_compose_to_addr -\n"));
3973 bldto
.arg
.str
= NULL
;
3977 size_t incr
= 100, avail
, alloced
;
3980 to
= (char *)fs_get(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
++){
3994 EXPANDED_S
*next_one
;
3996 pab
= &as
.adrbks
[i
];
3997 if(pab
->address_book
)
3998 next_one
= pab
->address_book
->selects
;
4002 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
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.
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){
4024 fs_resize((void **)&to
, alloced
);
4028 strncpy(to
, a_string
, alloced
);
4029 to
[alloced
-1] = '\0';
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
);
4047 if(is_addr(cur_line
)){
4049 dl
= dlist(cur_line
);
4052 if(dl
->type
== ListEnt
){
4054 bldto
.arg
.str
= cpystr(listmem(cur_line
));
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.
4079 ps_global
->mangled_screen
= 1;
4085 * Used by the two compose routines.
4088 ab_compose_internal(BuildTo bldto
, int allow_role
)
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
;
4097 ps_global
->redrawer
= NULL
;
4099 ps_global
->next_screen
= SCREEN_FUN_NULL
;
4104 good_addr
= (our_build_address(bldto
, &addr
, &error
, &fcc
, NULL
) >= 0);
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 */
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
;
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.
4129 role
= copy_action(role
);
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
);
4140 fs_give((void **)&addr
);
4143 fs_give((void **)&fcc
);
4148 * Export addresses into a file.
4150 * Args: cur_line -- The current line position (in global display list)
4152 * command_line -- The screen line on which to prompt
4154 * Returns -- 1 if the export is done
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];
4166 long start_of_append
;
4167 char *addr
= NULL
, *error
= NULL
;
4170 int good_addr
, plur
, vcard
= 0, tab
= 0;
4171 static HISTORY_S
*history
= NULL
;
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"));
4189 q_status_message(SM_ORDER
, 0, 3,
4190 "Alpine demo can't export addresses to files");
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
);
4199 /* TRANSLATORS: a screen title */
4200 helper(h_ab_export_vcard
, _("HELP FOR EXPORT FORMAT"),
4202 ps_global
->mangled_screen
= 1;
4210 q_status_message(SM_INFO
, 0, 2, _("Address book export cancelled"));
4225 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_export");
4233 plur
= (abe
&& abe
->tag
== List
);
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
);
4256 q_status_message(SM_INFO
, 0, 2, _("Address book export cancelled"));
4260 q_status_message1(SM_ORDER
, 0, 2,
4261 _("Can't export to file outside of %s"), VAR_OPER_DIR
);
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
));
4279 * The write_single_vcard_entry function wants a pc.
4282 gf_set_so_writec(&pc
, store
);
4284 start_of_append
= so_tell(store
);
4287 for(i
= 0; !failure
&& i
< as
.n_addrbk
; i
++){
4290 EXPANDED_S
*next_one
;
4292 pab
= &as
.adrbks
[i
];
4293 if(pab
->address_book
)
4294 next_one
= pab
->address_book
->selects
;
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)))
4310 write_single_vcard_entry(ps
, pc
, vinfo
);
4312 write_single_tab_entry(pc
, vinfo
);
4314 free_vcard_info(&vinfo
);
4319 bldto
.arg
.abe
= abe
;
4322 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4325 q_status_message1(SM_ORDER
, 0, 4, "%s", error
);
4326 fs_give((void **)&error
);
4329 /* rfc1522_decode the addr */
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
);
4340 fs_give((void **)&p
);
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
++){
4354 else if(!quoted
&& *p
== ','){
4356 removing_leading_white_space(p
);
4361 if(!so_puts(store
, addr
) || !so_puts(store
, NEWLINE
)){
4368 fs_give((void **)&addr
);
4376 dl
= dlist(cur_line
);
4378 if((vcard
|| tab
) && abe
){
4379 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, 1)))
4383 write_single_vcard_entry(ps
, pc
, vinfo
);
4385 write_single_tab_entry(pc
, vinfo
);
4387 free_vcard_info(&vinfo
);
4392 if(dl
->type
== ListHead
&& listmem_count_from_abe(abe
) == 0){
4393 error
= _("List is empty, nothing to export!");
4396 else if(dl
->type
== ListEnt
){
4398 bldto
.arg
.str
= listmem(cur_line
);
4399 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4403 bldto
.arg
.abe
= abe
;
4404 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4408 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4409 fs_give((void **)&error
);
4412 /* Have to rfc1522_decode the addr */
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
);
4422 fs_give((void **)&p
);
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
++){
4436 else if(!quoted
&& *p
== ','){
4438 removing_leading_white_space(p
);
4443 if(!so_puts(store
, addr
) || !so_puts(store
, NEWLINE
)){
4450 fs_give((void **)&addr
);
4455 gf_clear_so_writec(store
);
4457 if(so_give(&store
)) /* release storage */
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
));
4471 q_status_message3(SM_ORDER
,0,3,
4472 "%s %s to file \"%s\"",
4473 (vcard
|| tab
) ? (agg
? "Entries" : "Entry")
4474 : (plur
? "Addresses" : "Address"),
4477 : retflags
& GER_APPEND
? "appended" : "exported",
4482 ps
->mangled_footer
= 1;
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)
4497 * command_line -- The screen line on which to prompt
4500 ab_forward(struct pine
*ps
, long int cur_line
, int agg
)
4503 AdrBk_Entry
*abe
= NULL
;
4504 ENVELOPE
*outgoing
= NULL
;
4505 BODY
*pb
, *body
= NULL
;
4510 VCARD_INFO_S
*vinfo
;
4511 ACTION_S
*role
= NULL
;
4513 dprint((2, "- ab_forward -\n"));
4516 dl
= dlist(cur_line
);
4517 if(dl
->type
!= ListHead
&& dl
->type
!= Simple
)
4522 q_status_message(SM_ORDER
, 3, 3, _("Trouble accessing current entry"));
4527 outgoing
= mail_newenvelope();
4528 if(agg
&& as
.selections
> 1)
4529 outgoing
->subject
= cpystr("Forwarded address book entries from Alpine");
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
){
4542 long rflags
= ROLE_COMPOSE
;
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
);
4556 cmd_cancelled("Composition");
4562 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
4565 if((sig
= detoken(role
, NULL
, 2, 0, 1, NULL
, NULL
)) != NULL
){
4567 so_puts((STORE_S
*)body
->nested
.part
->body
.contents
.text
.data
,
4572 fs_give((void **)&sig
);
4575 /* so we don't have an empty part */
4577 so_puts((STORE_S
*)body
->nested
.part
->body
.contents
.text
.data
, "\n");
4580 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4581 _("Problem creating space for message text"));
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");
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;
4606 EXPANDED_S
*next_one
;
4608 gf_set_so_writec(&pc
, (STORE_S
*) pb
->contents
.text
.data
);
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
;
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
++;
4631 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4632 if(!strindex(*ll
, '@')){
4633 are_some_unqualified
++;
4643 * Search through the addresses to see if there are any
4644 * that are unqualified, and so would be different if
4647 if(abe
->tag
== Single
){
4648 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
4649 && !strindex(abe
->addr
.addr
, '@'))
4650 are_some_unqualified
++;
4655 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4656 if(!strindex(*ll
, '@')){
4657 are_some_unqualified
++;
4664 if(are_some_unqualified
){
4665 switch(want_to(_("Expand nicknames"), 'y', 'x', h_ab_forward
,WT_NORM
)){
4667 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4668 q_status_message(SM_INFO
, 0, 2, _("Address book forward cancelled"));
4680 ps
->mangled_footer
= 1;
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
;
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
);
4700 write_single_vcard_entry(ps
, pc
, vinfo
);
4701 free_vcard_info(&vinfo
);
4707 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, expand_nicks
))){
4708 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
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");
4721 strlen((char *)so_text((STORE_S
*)pb
->contents
.text
.data
));
4724 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4725 _("Problem creating space for message text"));
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;
4739 mail_free_envelope(&outgoing
);
4742 pine_free_body(&body
);
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
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
;
4760 ADDRESS
*adrlist
= NULL
;
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
);
4792 vinfo
->fullname
= (char **)fs_get((1+1) * sizeof(char *));
4793 vinfo
->fullname
[0] = fn
;
4794 vinfo
->fullname
[1] = NULL
;
4797 fs_give((void **)&fn
);
4801 if(first
&& (middle
=strindex(first
, ' '))){
4803 middle
= skip_white_space(middle
);
4807 vinfo
->first
= first
;
4808 vinfo
->middle
= middle
? cpystr(middle
) : NULL
;
4814 fs_give((void **)&last
);
4816 fs_give((void **)&first
);
4819 /* expand nicknames and fully-qualify unqualified names */
4825 if(abe
->tag
== Single
)
4826 init_addr
= cpystr(abe
->addr
.addr
);
4832 /* figure out how large a string we need to allocate */
4834 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
4835 length
+= (strlen(*ll
) + 2);
4840 init_addr
= (char *)fs_get((size_t)(length
+1L) * sizeof(char));
4843 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4844 sstrncpy(&p
, *ll
, length
-(p
-init_addr
));
4846 sstrncpy(&p
, ", ", length
-(p
-init_addr
));
4849 init_addr
[length
] = '\0';
4853 bldto
.arg
.str
= init_addr
;
4854 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
4856 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4857 fs_give((void **)&error
);
4858 free_vcard_info(&vinfo
);
4863 rfc822_parse_adrlist(&adrlist
, addr
, ps
->maildomain
);
4865 for(cnt
= 0, a
= adrlist
; a
; a
= a
->next
)
4868 vinfo
->email
= (char **)fs_get((cnt
+1) * sizeof(char *));
4870 for(cnt
= 0, a
= adrlist
; a
; a
= a
->next
){
4875 next_addr
= a
->next
;
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
){
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
;
4898 for(cnt
= 0, ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
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
;
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
);
4922 free_list_array(&(*vinfo
)->fcc
);
4924 free_list_array(&(*vinfo
)->note
);
4926 free_list_array(&(*vinfo
)->title
);
4928 free_list_array(&(*vinfo
)->tel
);
4930 free_list_array(&(*vinfo
)->email
);
4933 fs_give((void **)&(*vinfo
)->first
);
4934 if((*vinfo
)->middle
)
4935 fs_give((void **)&(*vinfo
)->middle
);
4937 fs_give((void **)&(*vinfo
)->last
);
4939 fs_give((void **)vinfo
);
4948 write_single_vcard_entry(struct pine
*ps
, gf_io_t pc
, VCARD_INFO_S
*vinfo
)
4950 char *decoded
, *tmp2
, *tmp
= NULL
, *hdr
;
4952 int i
, did_fn
= 0, did_n
= 0;
4960 #if defined(DOS) || defined(OS2)
4967 strncpy(eol
, "\r\n", sizeof(eol
));
4969 strncpy(eol
, "\n", sizeof(eol
));
4971 eol
[sizeof(eol
)-1] = '\0';
4973 gf_puts("BEGIN:VCARD", pc
);
4975 gf_puts("VERSION:3.0", pc
);
4978 for(i
= 0; i
< 7; i
++){
4981 ll
= vinfo
->nickname
;
4986 ll
= vinfo
->fullname
;
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
);
5024 if((tmp2
= fold(tmp
, FOLD_BY
, FOLD_BY
, hdr
, " ", FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5026 fs_give((void **)&tmp2
);
5031 fs_give((void **)&tmp
);
5036 if(vinfo
->last
&& vinfo
->last
[0]){
5039 pl
= vcard_escape(vinfo
->last
);
5040 pf
= (vinfo
->first
&& *vinfo
->first
) ? vcard_escape(vinfo
->first
)
5042 pm
= (vinfo
->middle
&& *vinfo
->middle
) ? vcard_escape(vinfo
->middle
)
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
){
5054 fs_give((void **)&tmp2
);
5059 fs_give((void **)&pl
);
5061 fs_give((void **)&pf
);
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
){
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
);
5084 if((tmp2
= fold(tmp
, FOLD_BY
, FOLD_BY
, "FN:", " ",
5085 FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5087 fs_give((void **)&tmp2
);
5091 fs_give((void **)&tmp
);
5096 gf_puts("FN:<Unknown>", pc
);
5100 gf_puts("N:<Unknown>", pc
);
5105 gf_puts("END:VCARD", pc
);
5114 write_single_tab_entry(gf_io_t pc
, VCARD_INFO_S
*vinfo
)
5116 char *decoded
, *tmp
= NULL
;
5124 #if defined(DOS) || defined(OS2)
5130 for(i
= 0; i
< 4; i
++){
5133 ll
= vinfo
->nickname
;
5137 ll
= vinfo
->fullname
;
5149 alpine_panic("can't happen in write_single_tab_entry");
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
);
5160 if(i
== 2 && !first
)
5166 fs_give((void **)&tmp
);
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.
5201 return((x
->num_in_dst
> y
->num_in_dst
) ? -1
5202 : (x
->num_in_dst
== y
->num_in_dst
) ? 0 : 1);
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 */
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
}};
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 ? "));
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
);
5256 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
5260 return(ab_export(ps
, cur_line
, command_line
, agg
));
5266 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_save");
5270 pab_dst
= setup_for_addrbook_add(&state
, command_line
, _("Save"));
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
));
5281 act_list_size
= as
.selections
;
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
));
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
;
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"),
5315 set_act_list_member(al
, (a_c_arg_t
)num
, pab_dst
, pab
, warning
);
5317 how_many_no_action
++;
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';
5336 if(is_addr(cur_line
)){
5339 dl
= dlist(cur_line
);
5341 if(dl
->type
== ListEnt
)
5346 set_act_list_member(al
, (a_c_arg_t
)num
, pab_dst
, pab
, warning
);
5352 how_many_no_action
++;
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';
5367 q_status_message(SM_ORDER
, 0, 4, _("No current entry to save"));
5372 if(how_many_to_copy
== 0 && how_many_no_action
== 1 && act_list_size
== 1)
5376 TA_STATE_S tas
, *tasp
;
5378 /* Not going to use the action_list now */
5380 fs_give((void **)&action_list
);
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.
5392 restore_state(&(tas
.state
));
5395 * We don't have enough information to know what to return.
5400 /* nothing to do (except for special Take case below) */
5401 if(how_many_to_copy
== 0){
5402 if(how_many_no_action
== 0){
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
);
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
);
5417 fs_give((void **)&action_list
);
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]);
5436 snprintf(tmp
, sizeof(tmp
), _("%d of the nicknames already exist, replace "),
5439 tmp
[sizeof(tmp
)-1] = '\0';
5441 switch(want_to(tmp
, 'n', 'x', h_ab_copy_dups
, WT_NORM
)){
5444 if(how_many_to_copy
== how_many_dups
){
5445 restore_state(&state
);
5447 fs_give((void **)&action_list
);
5449 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
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
),
5475 * Set up the busy alarm percent counters.
5477 total_to_copy
= how_many_to_copy
- (skip_dups
? how_many_dups
: 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
++){
5488 if(al
->skip
|| (skip_dups
&& al
->dup
))
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
));
5500 * Delete existing dups and replace them.
5504 /* delete the existing entry */
5506 if(adrbk_delete(pab_dst
->address_book
,
5507 (a_c_arg_t
)al
->num_in_dst
, 1, 0, 0, 0) == 0){
5511 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
5512 _("Error replacing entry in %s: %s"),
5514 error_description(errno
));
5521 * Now we have a clean slate to work with.
5522 * Add (sorted in correctly) or append abe to the destination
5525 if(total_to_copy
<= 1)
5526 rc
= adrbk_add(pab_dst
->address_book
,
5530 abe
->tag
== Single
? abe
->addr
.addr
: NULL
,
5540 rc
= adrbk_append(pab_dst
->address_book
,
5543 abe
->tag
== Single
? abe
->addr
.addr
: NULL
,
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
){
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
,
5571 pab_dst
->address_book
->sort_rule
= save_sort_rule
;
5575 if(abe
&& abe
->nickname
)
5576 q_status_message2(SM_ORDER
| SM_DING
, 3, 5, _("Error saving %s: %s"), abe
->nickname
, error_description(errno
));
5578 q_status_message1(SM_ORDER
| SM_DING
, 3, 5, _("Error saving entry: %s"), error_description(errno
));
5587 int sort_happened
= 0;
5589 if(adrbk_write(pab_dst
->address_book
, 0, NULL
, &sort_happened
, 0, 1)){
5595 ps_global
->mangled_screen
= 1;
5602 restore_state(&state
);
5604 fs_give((void **)&action_list
);
5606 ps_global
->mangled_footer
= 1;
5611 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
5612 _("Save only partially completed"));
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){
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",
5626 tmp
[sizeof(tmp
)-1] = '\0';
5627 q_status_message(SM_ORDER
, 0, 4, tmp
);
5635 * Warn should point to an array of size MAX_NICKNAME+1.
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
;
5643 num
= (adrbk_cntr_t
)numarg
;
5648 /* skip if they're copying from and to same addrbook */
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.
5662 /* If it isn't different, no problem. Check it out. */
5663 if(abes_are_equal(abe1
, abe2
))
5666 strncpy(warn
, abe1
->nickname
, MAX_NICKNAME
);
5667 warn
[MAX_NICKNAME
] = '\0';
5669 al
->num_in_dst
= dst_enum
;
5678 * Print out the display list.
5683 int do_entry
= 0, curopen
;
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',
5699 q_status_message(SM_INFO
, 0, 2, _("Address book print cancelled"));
5700 ps_global
->mangled_footer
= 1;
5713 /* TRANSLATORS: This is input for
5714 Print something1 using something2. The thing we're
5715 defining here is something1. */
5717 prompt
= _("selected entries");
5720 prompt
= _("address book list");
5722 prompt
= _("entry");
5724 prompt
= _("address book");
5727 if(open_printer(prompt
) == 0){
5728 DL_CACHE_S dlc_buf
, *match_dlc
;
5734 char more_spaces
[100];
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
);
5755 int are_some_unqualified
= 0, expand_nicks
= 0;
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
5764 if(abe
->tag
== Single
){
5765 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
5766 && !strindex(abe
->addr
.addr
, '@'))
5767 are_some_unqualified
++;
5772 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5773 if(!strindex(*ll
, '@')){
5774 are_some_unqualified
++;
5780 if(are_some_unqualified
){
5781 switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward
,
5784 q_status_message(SM_INFO
, 0, 2, _("Address book print cancelled"));
5785 ps_global
->mangled_footer
= 1;
5798 /* expand nicknames and fully-qualify unqualified names */
5802 char *init_addr
= NULL
;
5804 if(abe
->tag
== Single
)
5805 init_addr
= cpystr(abe
->addr
.addr
);
5811 /* figure out how large a string we need to allocate */
5813 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
5814 length
+= (strlen(*ll
) + 2);
5819 init_addr
= (char *)fs_get((size_t)(length
+1L) *
5823 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5824 sstrncpy(&p
, *ll
, length
-(p
-init_addr
));
5826 sstrncpy(&p
, ", ", length
-(p
-init_addr
));
5829 init_addr
[length
] = '\0';
5833 bldto
.arg
.str
= init_addr
;
5834 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
5836 fs_give((void **)&init_addr
);
5839 q_status_message1(SM_ORDER
, 0, 4, "%s", error
);
5840 fs_give((void **)&error
);
5842 ps_global
->mangled_footer
= 1;
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
){
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
){
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
){
5876 fs_give((void **)&tmp
);
5879 tmp
= abe
->extra
? abe
->extra
: "";
5880 {unsigned char *p
, *bb
= NULL
;
5882 if((n
= 4*strlen(tmp
)) > SIZEOF_20KBUF
-1){
5884 p
= bb
= (unsigned char *)fs_get(len
* sizeof(char));
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
){
5895 fs_give((void **)&tmp
);
5899 fs_give((void **)&bb
);
5909 for(a
= adrlist
; a
; a
= a
->next
){
5914 next_addr
= a
->next
;
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
){
5927 fs_give((void **)&tmp
);
5930 fs_give((void **)&bufp
);
5934 mail_free_address(&adrlist
);
5936 utf8_snprintf(b
, sizeof(b
), "%-*.*w:\n", abook_indent
, abook_indent
, _("Addresses"));
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
){
5949 fs_give((void **)&tmp
);
5955 if(!abe
->addr
.list
|| !abe
->addr
.list
[0]){
5956 utf8_snprintf(b
, sizeof(b
), "%-*.*w:\n", abook_indent
, abook_indent
, _("Addresses"));
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
)
5967 more_spaces
, FLD_NONE
)) != NULL
){
5969 fs_give((void **)&tmp
);
5978 char lbuf
[6*MAX_SCREEN_COLS
+ 1];
5980 int i
, savecur
, savezoomed
;
5981 OpenStatus savestatus
;
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.
5994 warp_to_beginning();
5996 for(dl
= dlist(lineno
);
5998 dl
= dlist(++lineno
)){
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 */
6024 savezoomed
= as
.zoomed
;
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
)))
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.
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();
6046 for(dl
= dlist(lineno
);
6048 dl
= dlist(++lineno
)){
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
);
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 */
6078 savezoomed
= as
.zoomed
;
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
;
6088 print_text(_("ADDRESS BOOK"));
6089 print_text1(" %s\n\n", as
.adrbks
[as
.cur
].abnick
);
6093 else{ /* print the list of address books */
6094 warp_to_beginning();
6098 for(dl
= dlist(lineno
);
6099 dl
->type
!= End
&& (!curopen
||
6100 (anum
==adrbk_num_from_lineno(lineno
) &&
6102 ((dlc
=get_dlc(lineno
)) &&
6103 dlc
->type
!= DlcDirDelim1
))));
6104 dl
= dlist(++lineno
)){
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
;
6127 * jump cache back to where we started so that the next
6128 * request won't cause us to page through the whole thing
6131 warp_to_dlc(match_dlc
, save_line
);
6133 ps_global
->mangled_screen
= 1;
6136 ps_global
->mangled_footer
= 1;
6142 * Delete address book entries.
6145 ab_agg_delete(struct pine
*ps
, int agg
)
6147 int ret
= 0, i
, ch
, rc
= 0;
6149 adrbk_cntr_t num
, ab_count
;
6152 dprint((2, "- ab_agg_delete -\n"));
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
);
6159 adrbk_cntr_t newelnum
= NO_NEXT
, flushelnum
= NO_NEXT
;
6160 DL_CACHE_S dlc_save
, dlc_restart
, *dlc
;
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
);
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
)
6186 ab_count
= adrbk_count(pab
->address_book
);
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
,
6200 rc
= adrbk_delete(pab
->address_book
, (a_c_arg_t
)num
,
6206 * This is just here to help us reposition the cursor.
6208 if(!top_level_display
&& as
.cur
== i
&& rc
== 0){
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);
6222 ps_global
->mangled_screen
= 1;
6226 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
6227 "Error updating %s: %s",
6228 (as
.n_addrbk
> 1) ? pab
->abnick
6230 error_description(errno
));
6231 dprint((1, "Error updating %s: %s\n",
6232 pab
->filename
? pab
->filename
: "?",
6233 error_description(errno
)));
6238 cancel_busy_cue(-1);
6241 q_status_message(SM_ORDER
, 0, 2, _("Deletions completed"));
6246 if(!top_level_display
){
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
;
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
);
6266 dlc_restart
.type
= DlcEmpty
;
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
;
6283 warp_to_top_of_abook(as
.cur
);
6285 new_ent
= first_selectable_line(0L);
6286 if(new_ent
== NO_LINE
)
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
);
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;
6317 cmd_cancelled("Apply Delete command");
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
;
6344 register AddrScrn_Disp
*dl
;
6346 DL_CACHE_S
*dlc_to_flush
;
6348 dprint((2, "- single_entry_delete -\n"));
6353 dl
= dlist(cur_line
);
6354 abe
= adrbk_get_ae(abook
, (a_c_arg_t
) dl
->elnum
);
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\"");
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\"");
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");
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
);
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
));
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
]);
6416 rc
= adrbk_delete(abook
, (a_c_arg_t
)dl
->elnum
, 0, 1, 0, 0);
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
);
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);
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"
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
);
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
)));
6481 q_status_message(SM_INFO
, 0, 2, _("Entry not deleted"));
6488 free_headents(struct headerentry
**head
)
6490 struct headerentry
*he
;
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
);
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
}
6559 #define QQ_COUNTRY 7
6561 #define QQ_LOCALITY 9
6563 #define QQ_CUSTOM 11
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
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.
6585 query_server(struct pine
*ps
, int selecting
, int *exit
, int who
, char **error
)
6587 struct headerentry
*he
= NULL
;
6589 STORE_S
*msgso
= NULL
;
6590 int i
, lret
, editor_result
;
6592 HelpType help
= NO_HELP
;
6593 #define FILTSIZE 1000
6594 char fbuf
[FILTSIZE
+1];
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")},
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])){
6610 *error
= cpystr(_("No LDAP server available for lookup"));
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
);
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;
6632 *error
= cpystr(_("Cancelled"));
6637 editor_result
= COMP_EXIT
; /* just to get right logic below */
6639 memset((void *)&pbf
, 0, sizeof(pbf
));
6642 standard_picobuf_setup(&pbf
);
6643 pbf
.exittest
= pico_simpleexit
;
6644 pbf
.exit_label
= _("Search");
6645 pbf
.canceltest
= pico_simplecancel
;
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
,
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.
6669 _("\n Fill in some of the fields above to create a query."));
6671 _("\n The match will be for the exact string unless you include wildcards (*)."));
6673 _("\n All filled-in fields must match in order to be counted as a match."));
6675 _("\n Press \"^R\" to restore previous query values (if you've queried previously)."));
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
];
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
)
6709 fix_windsize(ps_global
);
6714 if(editor_result
& COMP_EXIT
&&
6715 ((r
== 0 && *fbuf
) ||
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
;
6722 int need_and
, mangled
;
6724 CUSTOM_FILT_S
*filter
;
6727 s
= copy_query_parameters(sq
);
6728 save_query_parameters(s
);
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){
6758 *error
= cpystr(_("Search strings too long"));
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
: "",
6771 *sq
->sn
? "(sn=" : "",
6772 *sq
->sn
? sq
->sn
: "",
6774 *sq
->gn
? "(givenname=" : "",
6775 *sq
->gn
? 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 */
6805 filter
->filt
= sq
->custom
;
6806 filter
->combine
= 0;
6808 else if(*sq
->qq
&& categories
> 0){
6810 filter
->filt
= fbuf
;
6811 filter
->combine
= 1;
6813 else if(categories
> 0){
6815 filter
->filt
= fbuf
;
6816 filter
->combine
= 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
);
6834 lret
= ldap_lookup_all(string
, who
, 0, style
, filter
, &winning_e
,
6835 &wp_err
, &free_when_done
);
6838 fs_give((void **)&filter
);
6841 ps
->mangled_screen
= 1;
6844 if(status_message_remaining() && error
)
6845 *error
= wp_err
.error
;
6847 fs_give((void **)&wp_err
.error
);
6850 if(lret
== 0 && winning_e
&& selecting
){
6853 addr
= address_from_ldap(winning_e
);
6856 addr
->host
= cpystr("missing-hostname");
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]){
6868 fs_give((void **)&ret
);
6873 if(error
&& !*error
){
6876 snprintf(buf
, sizeof(buf
), _("No email address available for \"%s\""),
6877 (addr
->personal
&& *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
)
6897 free_ldap_result_list(&free_when_done
);
6900 fs_give((void **)&winning_e
);
6906 free_query_parameters(&sq
);
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
6920 view_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
)
6922 STORE_S
*srcstore
= NULL
;
6923 SourceType srctype
= CharStar
;
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
);
6944 sargs
.keys
.menu
->how_many
= 2;
6946 sargs
.keys
.menu
->how_many
= 1;
6947 clrbitn(OTHER_KEY
, sargs
.keys
.bitmap
);
6952 ps
->mangled_screen
= 1;
6954 free_handles(&handles
);
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
6969 compose_to_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*e
, int allow_role
)
6971 struct berval
**elecmail
= NULL
,
6979 dprint((9, "- compose_to_ldap_entry -\n"));
6985 for(a
= ldap_first_attribute(e
->ld
, e
->selected_entry
, &ber
);
6987 a
= ldap_next_attribute(e
->ld
, e
->selected_entry
, ber
)){
6989 if(strcmp(a
, e
->info_used
->mailattr
) == 0){
6991 mail
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
6993 else if(strcmp(a
, "electronicmail") == 0){
6995 elecmail
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
6997 else if(strcmp(a
, e
->info_used
->cnattr
) == 0){
6999 cn
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7001 else if(strcmp(a
, e
->info_used
->gnattr
) == 0){
7003 givenname
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7005 else if(strcmp(a
, e
->info_used
->snattr
) == 0){
7007 sn
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7010 our_ldap_memfree(a
);
7015 if(ALPINE_LDAP_can_use(elecmail
) && !mail
)
7018 ldap_value_free_len(elecmail
);
7023 for(num
= 0; ALPINE_LDAP_usable(mail
, num
); num
++)
7024 len
+= strlen(mail
[num
]->bv_val
) + 1;
7027 char *p
, *address
, *fn
= NULL
;
7031 address
= (char *)fs_get(len
* sizeof(char));
7034 while(ALPINE_LDAP_usable(mail
, num
)){
7035 sstrncpy(&p
, mail
[num
]->bv_val
, len
-(p
-address
));
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
)){
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
);
7060 if(ALPINE_LDAP_can_use(mail
) && !mail
[1] && fn
){
7061 ADDRESS
*adrlist
= NULL
;
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
){
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
;
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
);
7084 fs_give((void **)&address
);
7085 address
= new_address
;
7089 mail_free_address(&adrlist
);
7093 bldto
.arg
.str
= address
;
7096 ab_compose_internal(bldto
, allow_role
);
7097 restore_state(&state
);
7098 fs_give((void **)&address
);
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.
7110 ps_global
->mangled_screen
= 1;
7113 q_status_message(SM_ORDER
, 0, 4, _("No address to compose to"));
7116 ldap_value_free_len(mail
);
7118 ldap_value_free_len(cn
);
7120 ldap_value_free_len(sn
);
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
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;
7147 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
7152 prep_ldap_for_viewing(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
, SourceType srctype
, HANDLE_S
**handlesp
)
7154 STORE_S
*store
= NULL
;
7159 #define INDENTHERE (22)
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
)))
7168 /* for mailto handles so user can select individual email addrs */
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
);
7198 a
= ldap_next_attribute(winning_e
->ld
, winning_e
->selected_entry
, ber
)){
7201 struct berval
**vals
;
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
);
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
++){
7225 char *addr
, *new_addr
, *enc_addr
;
7228 addr
= cpystr(vals
[i
]->bv_val
);
7229 if(ALPINE_LDAP_can_use(cn
))
7230 fn
= cpystr(cn
[0]->bv_val
);
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
){
7240 ad
->personal
= cpystr(fn
);
7242 new_addr
= (char *) fs_get(len
* sizeof(char));
7244 /* this will quote it if it needs quoting */
7245 rbuf
.f
= dummy_soutr
;
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
);
7252 fs_give((void **) &addr
);
7257 mail_free_address(&ad
);
7259 fs_give((void **)&fn
);
7262 if((enc_addr
= rfc1738_encode_mailto(addr
)) != NULL
){
7265 l
= strlen(enc_addr
) + 7;
7266 path
= (char *) fs_get((l
+1) * sizeof(char));
7267 snprintf(path
, l
+1, "mailto:%s", enc_addr
);
7269 fs_give((void **)&enc_addr
);
7272 fs_give((void **)&addr
);
7275 h
= new_handle(handlesp
);
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");
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
);
7316 for(tmp
= strchr(a
, ';'); tmp
!= NULL
; tmp
= tmp2
){
7317 tmp2
= strchr(++tmp
, ';');
7320 is_binary
= !strucmp(tmp
, "binary");
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
);
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
);
7356 ldap_value_free_len(cn
);
7363 process_ldap_cmd(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
7367 ps_global
->next_screen
= SCREEN_FUN_NULL
;
7371 save_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 0);
7376 compose_to_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 0);
7381 compose_to_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 1);
7386 alpine_panic("Unexpected command in process_ldap_cmd");
7395 pico_simpleexit(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
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.
7415 save_query_parameters(SAVED_QUERY_S
*params
)
7417 free_saved_query_parameters();
7418 saved_params
= params
;
7423 copy_query_parameters(SAVED_QUERY_S
*params
)
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
);
7433 sq
->qq
= cpystr("");
7435 if(params
&& params
->cn
)
7436 sq
->cn
= cpystr(params
->cn
);
7438 sq
->cn
= cpystr("");
7440 if(params
&& params
->sn
)
7441 sq
->sn
= cpystr(params
->sn
);
7443 sq
->sn
= cpystr("");
7445 if(params
&& params
->gn
)
7446 sq
->gn
= cpystr(params
->gn
);
7448 sq
->gn
= cpystr("");
7450 if(params
&& params
->mail
)
7451 sq
->mail
= cpystr(params
->mail
);
7453 sq
->mail
= cpystr("");
7455 if(params
&& params
->org
)
7456 sq
->org
= cpystr(params
->org
);
7458 sq
->org
= cpystr("");
7460 if(params
&& params
->unit
)
7461 sq
->unit
= cpystr(params
->unit
);
7463 sq
->unit
= cpystr("");
7465 if(params
&& params
->country
)
7466 sq
->country
= cpystr(params
->country
);
7468 sq
->country
= cpystr("");
7470 if(params
&& params
->state
)
7471 sq
->state
= cpystr(params
->state
);
7473 sq
->state
= cpystr("");
7475 if(params
&& params
->locality
)
7476 sq
->locality
= cpystr(params
->locality
);
7478 sq
->locality
= cpystr("");
7480 if(params
&& params
->custom
)
7481 sq
->custom
= cpystr(params
->custom
);
7483 sq
->custom
= cpystr("");
7490 free_saved_query_parameters(void)
7493 free_query_parameters(&saved_params
);
7498 free_query_parameters(SAVED_QUERY_S
**parm
)
7503 fs_give((void **)&(*parm
)->qq
);
7505 fs_give((void **)&(*parm
)->cn
);
7507 fs_give((void **)&(*parm
)->sn
);
7509 fs_give((void **)&(*parm
)->gn
);
7511 fs_give((void **)&(*parm
)->mail
);
7513 fs_give((void **)&(*parm
)->org
);
7515 fs_give((void **)&(*parm
)->unit
);
7516 if((*parm
)->country
)
7517 fs_give((void **)&(*parm
)->country
);
7519 fs_give((void **)&(*parm
)->state
);
7520 if((*parm
)->locality
)
7521 fs_give((void **)&(*parm
)->locality
);
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;
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
:"");
7569 * Internal handler for viewing an LDAP url.
7572 url_local_ldap(char *url
)
7576 int ld_err
, mangled
= 0, we_cancel
, retval
= 0, proto
= 3;
7577 int we_turned_on
= 0;
7579 LDAPMessage
*result
;
7581 LDAP_SERV_RES_S
*serv_res
= NULL
;
7582 LDAPURLDesc
*ldapurl
= NULL
;
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
);
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
);
7604 we_turned_on
= intr_handling_on();
7605 we_cancel
= busy_cue(_("Searching for LDAP url"), NULL
, 0);
7606 ps_global
->mangled_footer
= 1;
7609 if((ld
= ldap_init(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7613 if((ld
= ldap_init(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
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 */
7620 if((ld
= ldap_open(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7621 #endif /* LDAPAPI >= 11 */
7622 #endif /* _SOLARIS_SDK */
7625 cancel_busy_cue(-1);
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;
7645 ld_err
= ldap_search_st(ld
, ldapurl
->lud_dn
, ldapurl
->lud_scope
,
7646 ldapurl
->lud_filter
, ldapurl
->lud_attrs
,
7649 ld_err
= ldap_search_ext_s(ld
, ldapurl
->lud_dn
, ldapurl
->lud_scope
,
7650 ldapurl
->lud_filter
, ldapurl
->lud_attrs
, 0,
7652 &t
, DEF_LDAP_SIZE
, &result
);
7654 if(ld_err
!= LDAP_SUCCESS
){
7656 cancel_busy_cue(-1);
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
);
7666 ldap_unbind_ext(ld
, NULL
, NULL
);
7667 #endif /* _WINDOWS */
7669 else if(!ps_global
->intr_pending
){
7671 cancel_busy_cue(-1);
7676 intr_handling_off();
7680 if(ldap_count_entries(ld
, result
) == 0){
7681 q_status_message(SM_ORDER
, 3, 5, _("No matches found for url"));
7685 ldap_unbind_ext(ld
, NULL
, NULL
);
7686 #endif /* _WINDOWS */
7688 ldap_msgfree(result
);
7691 serv_res
= (LDAP_SERV_RES_S
*)fs_get(sizeof(LDAP_SERV_RES_S
));
7692 memset((void *)serv_res
, 0, sizeof(*serv_res
));
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
);
7707 q_status_message(SM_ORDER
, 3, 5, wp_err
.error
);
7708 fs_give((void **)&wp_err
.error
);
7712 ps_global
->mangled_screen
= 1;
7714 free_ldap_result_list(&serv_res
);
7721 cancel_busy_cue(-1);
7724 intr_handling_off();
7727 ldap_free_urldesc(ldapurl
);
7731 #endif /* ENABLE_LDAP */