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_o_t
, VCARD_INFO_S
*);
83 int percent_done_copying(void);
84 int cmp_action_list(const qsort_t
*, const qsort_t
*);
85 void set_act_list_member(ACTION_LIST_S
*, a_c_arg_t
, PerAddrBook
*, PerAddrBook
*, char *);
86 void convert_pinerc_to_remote(struct pine
*, char *);
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
;
136 int cmd
, abook_indent
;
140 dprint((5, "- view_abook_entry -\n"));
142 if(is_addr(cur_line
)){
145 q_status_message(SM_ORDER
, 0, 3, _("Error reading entry"));
150 q_status_message(SM_ORDER
, 0, 3, _("Nothing to view"));
154 if(!(in_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
155 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
159 abook_indent
= utf8_width(_("Nickname")) + 2;
161 /* TRANSLATORS: Nickname is a shorthand name for something */
162 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
163 so_puts(in_store
, b
);
165 so_puts(in_store
, abe
->nickname
);
167 so_puts(in_store
, "\015\012");
169 /* TRANSLATORS: Full name is the name that goes with an email address.
171 Fred Flintstone <fred@bedrock.org>
172 Fred Flintstone is the Full Name. */
173 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fullname"));
174 so_puts(in_store
, b
);
176 so_puts(in_store
, abe
->fullname
);
178 so_puts(in_store
, "\015\012");
180 /* TRANSLATORS: Fcc is an abbreviation for File carbon copy. It is like
181 a cc which is a copy of a message that goes to somebody other than the
182 main recipient, only this is a copy of a message which is put into a
183 file on the user's computer. */
184 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fcc"));
185 so_puts(in_store
, b
);
187 so_puts(in_store
, abe
->fcc
);
189 so_puts(in_store
, "\015\012");
191 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, AB_COMMENT_STR
);
192 so_puts(in_store
, b
);
194 so_puts(in_store
, abe
->extra
);
196 so_puts(in_store
, "\015\012");
198 /* TRANSLATORS: Addresses refers to email Addresses */
199 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
200 so_puts(in_store
, b
);
201 if(abe
->tag
== Single
){
202 char *tmp
= abe
->addr
.addr
? abe
->addr
.addr
: "";
204 if(!strncmp(tmp
, QRUN_LDAP
, LEN_QRL
))
210 so_puts(in_store
, string
);
215 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
216 if(ll
!= abe
->addr
.list
){
217 so_puts(in_store
, "\015\012");
218 so_puts(in_store
, repeat_char(abook_indent
+2, SPACE
));
222 if(!strncmp(*ll
, QRUN_LDAP
, LEN_QRL
))
228 so_puts(in_store
, string
);
230 if(*(ll
+1)) /* not the last one */
231 so_puts(in_store
, ",");
235 so_puts(in_store
, "\015\012");
238 if(!(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
240 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
241 _("Error allocating space."));
245 so_seek(in_store
, 0L, 0);
247 init_handles(&handles
);
250 if(F_ON(F_VIEW_SEL_URL
, ps_global
)
251 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
252 || F_ON(F_SCAN_ADDR
, ps_global
))
253 gf_link_filter(gf_line_test
,
254 gf_line_test_opt(url_hilite_abook
,
255 gf_url_hilite_opt(&uh
,&handles
,0)));
257 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(ps
->ttyo
->screen_cols
- 4,
258 ps
->ttyo
->screen_cols
,
259 NULL
, abook_indent
+2,
261 gf_link_filter(gf_nvtnl_local
, NULL
);
263 gf_set_so_readc(&gc
, in_store
);
264 gf_set_so_writec(&pc
, out_store
);
266 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
269 free_handles(&handles
);
270 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
271 /* TRANSLATORS: %s is the error message */
272 _("Can't format entry: %s"), errstr
);
276 gf_clear_so_writec(out_store
);
277 gf_clear_so_readc(in_store
);
279 memset(&sargs
, 0, sizeof(SCROLL_S
));
280 sargs
.text
.text
= so_text(out_store
);
281 sargs
.text
.src
= CharStar
;
282 sargs
.text
.desc
= _("expanded entry");
283 sargs
.text
.handles
= handles
;
285 if(offset
){ /* resize? preserve paging! */
286 sargs
.start
.on
= Offset
;
287 sargs
.start
.loc
.offset
= offset
;
291 /* TRANSLATORS: a screen title. We are viewing an address book */
292 sargs
.bar
.title
= _("ADDRESS BOOK (View)");
293 sargs
.bar
.style
= TextPercent
;
294 sargs
.proc
.tool
= process_abook_view_cmd
;
295 sargs
.proc
.data
.i
= VIEW_ABOOK_NONE
;
296 sargs
.resize_exit
= 1;
297 sargs
.help
.text
= h_abook_view
;
298 /* TRANSLATORS: help screen title */
299 sargs
.help
.title
= _("HELP FOR ADDRESS BOOK VIEW");
300 sargs
.keys
.menu
= &abook_view_keymenu
;
301 setbitmap(sargs
.keys
.bitmap
);
304 sargs
.keys
.menu
->how_many
= 2;
306 sargs
.keys
.menu
->how_many
= 1;
307 clrbitn(OTHER_KEY
, sargs
.keys
.bitmap
);
310 if((cmd
= scrolltool(&sargs
)) == MC_RESIZE
)
311 offset
= sargs
.start
.loc
.offset
;
314 free_handles(&handles
);
316 while(cmd
== MC_RESIZE
);
320 if(sargs
.proc
.data
.i
!= VIEW_ABOOK_NONE
){
321 long old_l_p_p
= 0, old_top_ent
= 0, old_cur_row
= 0;
323 if(sargs
.proc
.data
.i
== VIEW_ABOOK_WARPED
){
325 * Warped means we got plopped down somewhere in the display
326 * list so that we don't know where we are relative to where
327 * we were before we warped. The current line number will
328 * be zero, since that is what the warp would have set.
330 as
.top_ent
= first_line(0L - as
.l_p_page
/2L);
331 as
.cur_row
= 0L - as
.top_ent
;
333 else if(sargs
.proc
.data
.i
== VIEW_ABOOK_EDITED
){
334 old_l_p_p
= as
.l_p_page
;
335 old_top_ent
= as
.top_ent
;
336 old_cur_row
= as
.cur_row
;
339 /* Window size may have changed while in pico. */
342 /* fix up what ab_resize messed up */
343 if(sargs
.proc
.data
.i
!= VIEW_ABOOK_WARPED
&& old_l_p_p
== as
.l_p_page
){
344 as
.top_ent
= old_top_ent
;
345 as
.cur_row
= old_cur_row
;
346 as
.old_cur_row
= old_cur_row
;
349 cur_line
= as
.top_ent
+as
.cur_row
;
352 ps
->mangled_screen
= 1;
357 url_hilite_abook(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
361 if((lp
= strchr(line
, ':')) &&
362 !strncmp(line
, AB_COMMENT_STR
, strlen(AB_COMMENT_STR
)))
363 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
370 process_abook_view_cmd(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
375 static ESCKEY_S text_or_vcard
[] = {
376 /* TRANSLATORS: Text refers to plain old text, probably the text of
378 {'t', 't', "T", N_("Text")},
379 /* TRANSLATORS: A VCard is kind of like an electronic business card. It is not
380 something specific to alpine, it is universal. */
381 {'v', 'v', "V", N_("VCard")},
382 {-1, 0, NULL
, NULL
}};
384 ps_global
->next_screen
= SCREEN_FUN_NULL
;
389 * MC_EDIT works a little differently from the other cmds here.
390 * The others return 0 to scrolltool so that we are still in
391 * the view screen. This one is different because we may have
392 * changed what we're viewing. We handle that by returning 1
393 * to scrolltool and setting the sparms opt union's gint
394 * to the value below.
396 * (Late breaking news. Now we're going to return 1 from all these
397 * commands and then in the caller we're going to bounce back out
398 * to the index view instead of the view of the individual entry.
399 * So there is some dead code around for now.)
401 * Then, in the view_abook_entry function we check the value
402 * of this field on scrolltool's return and if it is one of
403 * the two special values below view_abook_entry resets the
404 * current line if necessary, flushes the display cache (in
405 * ab_resize) and loops back and starts over, effectively
406 * putting us back in the view screen but with updated
407 * contents. A side effect is that the screen above that (the
408 * abook index) will also have been flushed and corrected by
411 pab
= &as
.adrbks
[cur_addr_book()];
412 if(pab
&& pab
->access
== ReadOnly
){
413 /* TRANSLATORS: Address book can be viewed but not changed */
414 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
419 if(adrbk_check_all_validity_now()){
420 if(resync_screen(pab
, AddrBookScreen
, 0)){
421 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
422 /* TRANSLATORS: The address book was changed by some other
423 process. The user is being told that their change (their
424 Update) has been canceled and they should try again. */
425 _("Address book changed. Update cancelled. Try again."));
426 ps_global
->mangled_screen
= 1;
431 if(pab
&& pab
->access
== ReadOnly
){
432 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
438 * Arguments all come from globals, not from the arguments to
439 * process_abook_view_cmd. It would be a little cleaner if the
440 * information was contained in att, I suppose.
442 if(is_addr(as
.cur_row
+as
.top_ent
)){
443 AdrBk_Entry
*abe
, *abe_copy
;
447 dl
= dlist(as
.top_ent
+as
.cur_row
);
449 abe
= adrbk_get_ae(pab
->address_book
, entry
);
450 abe_copy
= copy_ae(abe
);
451 dprint((9,"Calling edit_entry to edit from view\n"));
452 /* TRANSLATORS: update as in update an address book entry */
453 edit_entry(pab
->address_book
, abe_copy
, entry
,
454 abe
->tag
, 0, &warped
, _("update"));
456 * The ABOOK_EDITED case doesn't mean that we necessarily
457 * changed something, just that we might have but we know
458 * we didn't change the sort order (causing the warp).
460 sparms
->proc
.data
.i
= warped
461 ? VIEW_ABOOK_WARPED
: VIEW_ABOOK_EDITED
;
464 rv
= 1; /* causes scrolltool to return */
467 q_status_message(SM_ORDER
, 0, 4,
468 "Something wrong, entry not updateable");
476 pab
= &as
.adrbks
[cur_addr_book()];
478 * Arguments all come from globals, not from the arguments to
479 * process_abook_view_cmd. It would be a little cleaner if the
480 * information was contained in att, I suppose.
482 (void)ab_save(ps_global
, pab
? pab
->address_book
: NULL
,
483 as
.top_ent
+as
.cur_row
, -FOOTER_ROWS(ps_global
), 0);
488 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 0);
493 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 1);
499 /* TRANSLATORS: A question with two choices for the answer. Forward
500 as text means to include the text of the message being forwarded
501 in the new message. Forward as a Vcard attachment means to
502 attach it to the message in a special format so it is recognizable
504 i
= radio_buttons(_("Forward as text or forward as Vcard attachment ? "),
505 -FOOTER_ROWS(ps_global
), text_or_vcard
, 't', 'x',
506 h_ab_text_or_vcard
, RB_NORM
);
509 q_status_message(SM_INFO
, 0, 2, _("Address book forward cancelled"));
514 forward_text(ps_global
, sparms
->text
.text
, sparms
->text
.src
);
518 (void)ab_forward(ps_global
, as
.top_ent
+as
.cur_row
, 0);
522 q_status_message(SM_ORDER
, 3, 3,
523 "can't happen in process_abook_view_cmd");
530 alpine_panic("Unexpected command in process_abook_view_cmd");
539 * Give expanded view of this address entry.
540 * Call scrolltool to do the work.
542 * Args: headents -- The headerentry array from pico.
545 * Returns -- Always 0.
548 expand_addrs_for_pico(struct headerentry
*headents
, char ***s
)
552 char *error
= NULL
, *addr
= NULL
, *fullname
= NULL
, *address
= NULL
;
554 ADDRESS
*adrlist
= NULL
, *a
;
555 int j
, address_index
= -1, fullname_index
= -1, no_a_fld
= 0;
556 void (*redraw
)(void) = ps_global
->redrawer
;
558 char *tmp
, *tmp2
, *tmp3
;
561 char fakeaddrpmt
[500];
564 dprint((5, "- expand_addrs_for_pico -\n"));
566 abook_indent
= utf8_width(_("Nickname")) + 2;
567 utf8_snprintf(fakeaddrpmt
, sizeof(fakeaddrpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Address"));
572 ps_global
->redrawer
= NULL
;
573 fix_windsize(ps_global
);
579 headents
[j
].name
!= NULL
&& (address_index
< 0 || fullname_index
< 0);
581 if(!strncmp(headents
[j
].name
, "Address", 7) || !strncmp(headents
[j
].name
, _("Address"), strlen(_("Address"))))
583 else if(!strncmp(headents
[j
].name
, "Fullname", 8) || !strncmp(headents
[j
].name
, _("Fullname"), strlen(_("Fullname"))))
587 if(address_index
>= 0)
588 address
= *headents
[address_index
].realaddr
;
590 address_index
= 1000; /* a big number */
594 if(fullname_index
>= 0)
595 fullname
= adrbk_formatname(*headents
[fullname_index
].realaddr
,
598 memset(&abe
, 0, sizeof(abe
));
600 abe
.fullname
= cpystr(fullname
);
605 tmp_a_string
= cpystr(address
);
606 rfc822_parse_adrlist(&adrlist
, tmp_a_string
, fakedomain
);
607 fs_give((void **)&tmp_a_string
);
608 if(adrlist
&& adrlist
->next
){
612 for(a
= adrlist
; a
; a
= a
->next
)
615 abe
.addr
.list
= (char **)fs_get((cnt
+1) * sizeof(char *));
617 for(a
= adrlist
; a
; a
= a
->next
){
618 if(a
->host
&& a
->host
[0] == '@')
619 abe
.addr
.list
[cnt
++] = cpystr(a
->mailbox
);
620 else if(a
->host
&& a
->host
[0] && a
->mailbox
&& a
->mailbox
[0])
621 abe
.addr
.list
[cnt
++] =
622 cpystr(simple_addr_string(a
, tmp_20k_buf
,
626 abe
.addr
.list
[cnt
] = NULL
;
630 abe
.addr
.addr
= address
;
634 mail_free_address(&adrlist
);
637 bldto
.arg
.abe
= &abe
;
638 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
640 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
641 fs_give((void **)&error
);
645 tmp_a_string
= cpystr(addr
);
646 rfc822_parse_adrlist(&adrlist
, tmp_a_string
, ps_global
->maildomain
);
647 fs_give((void **)&tmp_a_string
);
651 else if(no_a_fld
&& expander_address
){
654 memset(&wp_err
, 0, sizeof(wp_err
));
655 adrlist
= wp_lookups(expander_address
, &wp_err
, 0);
656 if(fullname
&& *fullname
){
658 if(adrlist
->personal
)
659 fs_give((void **)&adrlist
->personal
);
661 adrlist
->personal
= cpystr(fullname
);
666 q_status_message1(SM_ORDER
, 3, 4, "%s", wp_err
.error
);
667 fs_give((void **)&wp_err
.error
);
672 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
673 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
674 restore_state(&state
);
678 for(j
= 0; j
< address_index
&& headents
[j
].name
!= NULL
; j
++){
679 if((tmp
= fold(*headents
[j
].realaddr
,
680 ps_global
->ttyo
->screen_cols
,
681 ps_global
->ttyo
->screen_cols
,
683 repeat_char(headents
[j
].prwid
, SPACE
), FLD_NONE
)) != NULL
){
685 fs_give((void **)&tmp
);
690 * There is an assumption that Addresses is the last field.
692 tmp3
= cpystr(repeat_char(headents
[0].prwid
+ 2, SPACE
));
694 for(a
= adrlist
; a
; a
= a
->next
){
702 bufp
= (char *) fs_get(len
* sizeof(char));
703 (void) addr_string(a
, bufp
, len
);
707 * Another assumption, all the prwids are the same.
710 ps_global
->ttyo
->screen_cols
,
711 ps_global
->ttyo
->screen_cols
,
712 (a
== adrlist
) ? (no_a_fld
714 : headents
[address_index
].prompt
)
716 tmp3
, FLD_NONE
)) != NULL
){
718 fs_give((void **) &tmp
);
721 fs_give((void **) &bufp
);
726 if((tmp
= fold(tmp2
=cpystr("<none>"),
727 ps_global
->ttyo
->screen_cols
,
728 ps_global
->ttyo
->screen_cols
,
729 no_a_fld
? fakeaddrpmt
730 : headents
[address_index
].prompt
,
731 tmp3
, FLD_NONE
)) != NULL
){
733 fs_give((void **) &tmp
);
737 fs_give((void **)&tmp2
);
741 fs_give((void **)&tmp3
);
743 memset(&sargs
, 0, sizeof(SCROLL_S
));
744 sargs
.text
.text
= so_text(store
);
745 sargs
.text
.src
= CharStar
;
746 sargs
.text
.desc
= _("expanded entry");
747 /* TRANSLATORS: screen title for viewing an address book entry in Rich
748 view mode. Rich just means it is expanded to include everything. */
749 sargs
.bar
.title
= _("ADDRESS BOOK (Rich View)");
750 sargs
.bar
.style
= TextPercent
;
751 sargs
.keys
.menu
= &abook_text_km
;
752 setbitmap(sargs
.keys
.bitmap
);
757 restore_state(&state
);
761 fs_give((void **)&addr
);
764 mail_free_address(&adrlist
);
767 fs_give((void **)&fullname
);
770 fs_give((void **)&abe
.fullname
);
772 if(abe
.tag
== List
&& abe
.addr
.list
)
773 free_list_array(&(abe
.addr
.list
));
775 ps_global
->redrawer
= redraw
;
782 * Callback from TakeAddr editing screen to see message that was being
783 * viewed. Call scrolltool to do the work.
786 view_message_for_pico(char **error
)
790 void (*redraw
)(void) = ps_global
->redrawer
;
791 SourceType src
= CharStar
;
794 dprint((5, "- view_message_for_pico -\n"));
796 ps_global
->redrawer
= NULL
;
797 fix_windsize(ps_global
);
803 if(!(store
= so_get(src
, NULL
, EDIT_ACCESS
))){
804 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
808 gf_set_so_writec(&pc
, store
);
810 format_message(msgno_for_pico_callback
, env_for_pico_callback
,
811 body_for_pico_callback
, NULL
, FM_NEW_MESS
| FM_DISPLAY
, pc
);
813 gf_clear_so_writec(store
);
815 memset(&sargs
, 0, sizeof(SCROLL_S
));
816 sargs
.text
.text
= so_text(store
);
817 sargs
.text
.src
= src
;
818 sargs
.text
.desc
= _("expanded entry");
819 /* TRANSLATORS: this is a screen title */
820 sargs
.bar
.title
= _("MESSAGE TEXT");
821 sargs
.bar
.style
= TextPercent
;
822 sargs
.keys
.menu
= &abook_text_km
;
823 setbitmap(sargs
.keys
.bitmap
);
829 ps_global
->redrawer
= redraw
;
836 prompt::name::help::prwid::maxlen::realaddr::
837 builder::affected_entry::next_affected::selector::key_label::fileedit::nickcmpl
838 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
839 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
841 static struct headerentry headents_for_edit
[]={
842 {"Nickname : ", N_("Nickname"), h_composer_abook_nick
, 12, 0, NULL
,
843 /* TRANSLATORS: To AddrBk is a command that takes the user to
844 the address book screen to select an entry from there. */
845 verify_nick
, NULL
, NULL
, addr_book_nick_for_edit
, N_("To AddrBk"), NULL
, abook_nickname_complete
,
846 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
847 {"Fullname : ", N_("Fullname"), h_composer_abook_full
, 12, 0, NULL
,
848 NULL
, NULL
, NULL
, view_message_for_pico
, N_("To Message"), NULL
, NULL
,
849 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
850 /* TRANSLATORS: a File Copy is a copy of a sent message saved in a regular
851 file on the computer's disk */
852 {"Fcc : ", N_("FileCopy"), h_composer_abook_fcc
, 12, 0, NULL
,
853 /* TRANSLATORS: To Folders */
854 NULL
, NULL
, NULL
, folders_for_fcc
, N_("To Fldrs"), NULL
, NULL
,
855 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
856 {"Comment : ", N_("Comment"), h_composer_abook_comment
, 12, 0, NULL
,
857 NULL
, NULL
, NULL
, view_message_for_pico
, N_("To Message"), NULL
, NULL
,
858 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
859 {"Addresses : ", N_("Addresses"), h_composer_abook_addrs
, 12, 0, NULL
,
860 verify_addr
, NULL
, NULL
, addr_book_change_list
, N_("To AddrBk"), NULL
, abook_nickname_complete
,
861 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
862 {NULL
, NULL
, NO_HELP
, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
863 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
868 #define NNN_COMMENT 3
872 static char *nick_saved_for_pico_check
;
873 static AdrBk
*abook_saved_for_pico_check
;
876 * Args: abook -- Address book handle
877 * abe -- AdrBk_Entry of old entry to work on. If NULL, this will
878 * be a new entry. This has to be a pointer to a copy of
879 * an abe that won't go away until we finish this function.
880 * In other words, don't pass in a pointer to an abe in
881 * the cache, copy it first. The tag on this abe is only
882 * used to decide whether to read abe->addr.list or
883 * abe->addr.addr, not to determine what the final result
884 * will be. That's determined solely by how many addresses
885 * there are after the user edits.
886 * entry -- The entry number of the old entry that we will be changing.
887 * old_tag -- If we're changing an old entry, then this is the tag of
889 * readonly -- Call pico with readonly flag
890 * warped -- We warped to a new part of the addrbook
891 * (We also overload warped in a couple places and use it's
892 * being set as an indicator of whether we are Taking or
893 * not. It will be NULL if we are Taking.)
896 edit_entry(AdrBk
*abook
, AdrBk_Entry
*abe
, a_c_arg_t entry
, Tag old_tag
, int readonly
, int *warped
, char *cmd
)
898 AdrBk_Entry local_abe
;
899 struct headerentry
*he
;
902 adrbk_cntr_t old_entry_num
, new_entry_num
= NO_NEXT
;
903 int rc
= 0, resort_happened
= 0, list_changed
= 0, which_addrbook
;
904 int editor_result
, i
= 0, add
, n_end
, ldap
= 0;
905 char *nick
, *full
, *fcc
, *comment
, *fname
, *pp
;
906 char **orig_addrarray
= NULL
;
907 char **new_addrarray
= NULL
;
908 char *comma_sep_addr
= NULL
;
912 char nickpmt
[100], fullpmt
[100], fccpmt
[100], cmtpmt
[100], addrpmt
[100];
915 SAVE_STATE_S state
; /* For saving state of addrbooks temporarily */
917 dprint((2, "- edit_entry -\n"));
919 old_entry_num
= (adrbk_cntr_t
) entry
;
921 abook_saved_for_pico_check
= abook
;
923 add
= (abe
== NULL
); /* doing add or change? */
925 local_abe
.nickname
= "";
926 local_abe
.fullname
= "";
928 local_abe
.extra
= "";
929 local_abe
.addr
.addr
= "";
930 local_abe
.tag
= NotSet
;
932 old_entry_num
= NO_NEXT
;
938 expander_address
= NULL
;
939 if(abe
->tag
== Single
&&
941 !strncmp(abe
->addr
.addr
,QRUN_LDAP
,LEN_QRL
)){
943 expander_address
= cpystr(abe
->addr
.addr
);
944 removing_double_quotes(expander_address
);
946 else if(abe
->tag
== List
&&
949 !abe
->addr
.list
[1] &&
950 !strncmp(abe
->addr
.list
[0],QRUN_LDAP
,LEN_QRL
)){
952 expander_address
= cpystr(abe
->addr
.list
[0]);
953 removing_double_quotes(expander_address
);
957 standard_picobuf_setup(&pbf
);
958 pbf
.exittest
= pico_sendexit_for_adrbk
;
959 pbf
.canceltest
= warped
? pico_cancel_for_adrbk_edit
960 : pico_cancel_for_adrbk_take
;
961 pbf
.expander
= expand_addrs_for_pico
;
962 pbf
.ctrlr_label
= _("RichView");
963 /* xgettext: c-format */
965 /* TRANSLATORS: screen titles */
966 snprintf(titlebar
, sizeof(titlebar
), _("ADDRESS BOOK (View)"));
968 snprintf(titlebar
, sizeof(titlebar
), _("ADDRESS BOOK (%c%s)"),
969 islower((unsigned char)(*cmd
))
970 ? toupper((unsigned char)*cmd
)
973 pbf
.pine_anchor
= set_titlebar(titlebar
,
974 ps_global
->mail_stream
,
975 ps_global
->context_current
,
976 ps_global
->cur_folder
,ps_global
->msgmap
,
977 0, FolderName
, 0, 0, NULL
);
978 pbf
.pine_flags
|= P_NOBODY
;
980 pbf
.pine_flags
|= P_VIEW
;
982 /* An informational message */
983 if((msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
984 pbf
.msgtext
= (void *)so_text(msgso
);
986 * It's nice if we can make it so these lines make sense even if
987 * they don't all make it on the screen, because the user can't
988 * scroll down to see them. So just make each line a whole sentence
989 * that doesn't need the others below it to make sense.
994 * TRANSLATORS: The following lines go together to form a screen of
995 * explanation about how to edit an address book entry.
997 _("\n Fill in the fields. It is ok to leave fields blank."));
999 _("\n To form a list, just enter multiple comma-separated addresses."));
1003 /* TRANSLATORS: Same here, but a different version of the screen. */
1004 _("\n Change any of the fields. It is ok to leave fields blank."));
1007 _("\n Since this entry does a directory lookup you may not edit the address field."));
1010 _("\n Additional comma-separated addresses may be entered in the address field."));
1014 _("\n Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."));
1016 _("\n If you want to use quotation marks inside the Fullname field, it is best"));
1018 _("\n to use single quotation marks; for example: George 'Husky' Washington."));
1021 he
= (struct headerentry
*) fs_get((NNN_END
+1) * sizeof(struct headerentry
));
1022 memset((void *)he
, 0, (NNN_END
+1) * sizeof(struct headerentry
));
1025 abook_indent
= utf8_width(_("Nickname")) + 2;
1027 /* make a copy of each field */
1028 nick
= cpystr(abe
->nickname
? abe
->nickname
: "");
1029 removing_leading_and_trailing_white_space(nick
);
1030 nick_saved_for_pico_check
= cpystr(nick
);
1031 he
[NNN_NICK
] = headents_for_edit
[NNN_NICK
];
1032 he
[NNN_NICK
].realaddr
= &nick
;
1033 utf8_snprintf(nickpmt
, sizeof(nickpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
1034 he
[NNN_NICK
].prompt
= nickpmt
;
1035 he
[NNN_NICK
].prwid
= abook_indent
+2;
1036 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
1037 he
[NNN_NICK
].nickcmpl
= NULL
;
1039 full
= cpystr(abe
->fullname
? abe
->fullname
: "");
1040 removing_leading_and_trailing_white_space(full
);
1041 he
[NNN_FULL
] = headents_for_edit
[NNN_FULL
];
1042 he
[NNN_FULL
].realaddr
= &full
;
1043 utf8_snprintf(fullpmt
, sizeof(fullpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fullname"));
1044 he
[NNN_FULL
].prompt
= fullpmt
;
1045 he
[NNN_FULL
].prwid
= abook_indent
+2;
1047 fcc
= cpystr(abe
->fcc
? abe
->fcc
: "");
1048 removing_leading_and_trailing_white_space(fcc
);
1049 he
[NNN_FCC
] = headents_for_edit
[NNN_FCC
];
1050 he
[NNN_FCC
].realaddr
= &fcc
;
1051 utf8_snprintf(fccpmt
, sizeof(fccpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fcc"));
1052 he
[NNN_FCC
].prompt
= fccpmt
;
1053 he
[NNN_FCC
].prwid
= abook_indent
+2;
1055 comment
= cpystr(abe
->extra
? abe
->extra
: "");
1056 removing_leading_and_trailing_white_space(comment
);
1057 he
[NNN_COMMENT
] = headents_for_edit
[NNN_COMMENT
];
1058 he
[NNN_COMMENT
].realaddr
= &comment
;
1059 utf8_snprintf(cmtpmt
, sizeof(cmtpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Comment"));
1060 he
[NNN_COMMENT
].prompt
= cmtpmt
;
1061 he
[NNN_COMMENT
].prwid
= abook_indent
+2;
1068 he
[NNN_ADDR
] = headents_for_edit
[NNN_ADDR
];
1069 he
[NNN_ADDR
].realaddr
= &comma_sep_addr
;
1070 utf8_snprintf(addrpmt
, sizeof(addrpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
1071 he
[NNN_ADDR
].prompt
= addrpmt
;
1072 he
[NNN_ADDR
].prwid
= abook_indent
+2;
1073 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
1074 he
[NNN_NICK
].nickcmpl
= NULL
;
1076 if(abe
->tag
== Single
){
1078 orig_addrarray
= (char **) fs_get(2 * sizeof(char *));
1079 orig_addrarray
[0] = cpystr(abe
->addr
.addr
);
1080 orig_addrarray
[1] = NULL
;
1083 else if(abe
->tag
== List
){
1084 if(listmem_count_from_abe(abe
) > 0){
1085 orig_addrarray
= (char **) fs_get(
1086 (size_t)(listmem_count_from_abe(abe
) + 1)
1088 for(q
= orig_addrarray
, p
= abe
->addr
.list
; p
&& *p
; p
++, q
++)
1095 /* figure out how large a string we need to allocate */
1097 for(p
= orig_addrarray
; p
&& *p
; p
++)
1098 length
+= (strlen(*p
) + 2);
1103 pp
= comma_sep_addr
= (char *) fs_get((size_t)(length
+1L) * sizeof(char));
1105 for(p
= orig_addrarray
; p
&& *p
; p
++){
1106 sstrncpy(&pp
, *p
, length
-(pp
-comma_sep_addr
));
1108 sstrncpy(&pp
, ", ", length
-(pp
-comma_sep_addr
));
1111 comma_sep_addr
[length
] = '\0';
1113 if(verify_addr(comma_sep_addr
, NULL
, NULL
, NULL
, NULL
) < 0)
1114 he
[NNN_ADDR
].start_here
= 1;
1117 he
[n_end
] = headents_for_edit
[NNN_END
];
1118 for(i
= 0; i
< n_end
; i
++){
1119 /* no callbacks in some cases */
1120 if(readonly
|| ((i
== NNN_FULL
|| i
== NNN_COMMENT
) && !env_for_pico_callback
)){
1121 he
[i
].selector
= NULL
;
1122 he
[i
].key_label
= NULL
;
1125 /* no builders for readonly */
1127 he
[i
].builder
= NULL
;
1130 /* pass to pico and let user change them */
1131 editor_result
= pico(&pbf
);
1132 ps_global
->mangled_screen
= 1;
1133 standard_picobuf_teardown(&pbf
);
1135 if(editor_result
& COMP_GOTHUP
)
1138 fix_windsize(ps_global
);
1142 if(editor_result
& COMP_CANCEL
){
1144 /* TRANSLATOR: Something like
1145 Address book save cancelled */
1146 q_status_message1(SM_INFO
, 0, 2, _("Address book %s cancelled"), cmd
);
1148 else if(editor_result
& COMP_EXIT
){
1149 if(pico_usingcolor())
1150 clear_index_cache(ps_global
->mail_stream
, 0);
1151 removing_leading_and_trailing_white_space(nick
);
1152 removing_leading_and_trailing_white_space(full
);
1153 removing_leading_and_trailing_white_space(fcc
);
1154 removing_leading_and_trailing_white_space(comment
);
1155 removing_leading_and_trailing_white_space(comma_sep_addr
);
1157 /* not needed if pico is returning UTF-8 */
1158 convert_possibly_encoded_str_to_utf8(&nick
);
1159 convert_possibly_encoded_str_to_utf8(&full
);
1160 convert_possibly_encoded_str_to_utf8(&fcc
);
1161 convert_possibly_encoded_str_to_utf8(&comment
);
1162 convert_possibly_encoded_str_to_utf8(&comma_sep_addr
);
1164 /* don't allow adding null entry */
1165 if(add
&& !*nick
&& !*full
&& !*fcc
&& !*comment
&& !*comma_sep_addr
)
1169 * comma_sep_addr is now the string which has been edited
1172 new_addrarray
= parse_addrlist(comma_sep_addr
);
1174 if(!ldap
&& (!new_addrarray
|| !new_addrarray
[0]))
1175 q_status_message(SM_ORDER
, 3, 5, _("Warning: entry has no addresses"));
1177 if(!new_addrarray
|| !new_addrarray
[0] || !new_addrarray
[1])
1178 new_tag
= Single
; /* one or zero addresses means its a Single */
1180 new_tag
= List
; /* more than one addresses means its a List */
1182 if(new_tag
== List
&& old_tag
== List
){
1184 * If Taking, make sure we write it even if user didn't edit
1189 else if(he
[NNN_ADDR
].dirty
)
1190 for(q
= orig_addrarray
, p
= new_addrarray
; p
&& *p
&& q
&& *q
; p
++, q
++)
1191 if(strcmp(*p
, *q
) != 0){
1196 if(!list_changed
&& he
[NNN_ADDR
].dirty
1197 && ((!(p
&& *p
) && (q
&& *q
)) || ((p
&& *p
) && !(q
&& *q
))))
1202 * need to delete old list members and add new members below
1204 rc
= adrbk_listdel_all(abook
, (a_c_arg_t
) old_entry_num
);
1207 /* don't need new_addrarray */
1208 free_list_array(&new_addrarray
);
1212 fs_give((void **) &comma_sep_addr
);
1214 else if((new_tag
== List
&& old_tag
== Single
)
1215 || (new_tag
== Single
&& old_tag
== List
)){
1216 /* delete old entry */
1217 rc
= adrbk_delete(abook
, (a_c_arg_t
) old_entry_num
, 0, 0, 0, 0);
1218 old_entry_num
= NO_NEXT
;
1219 if(comma_sep_addr
&& new_tag
== List
)
1220 fs_give((void **) &comma_sep_addr
);
1224 * This will be an edit in the cases where the tag didn't change
1225 * and an add in the cases where it did.
1228 rc
= adrbk_add(abook
,
1229 (a_c_arg_t
)old_entry_num
,
1231 he
[NNN_FULL
].dirty
? full
: abe
->fullname
,
1232 new_tag
== Single
? (ldap
== 1 ? abe
->addr
.addr
:
1233 ldap
== 2 ? abe
->addr
.list
[0] :
1237 he
[NNN_COMMENT
].dirty
? comment
: abe
->extra
,
1243 (new_tag
!= List
|| !new_addrarray
));
1246 if(rc
== 0 && new_tag
== List
&& new_addrarray
)
1247 rc
= adrbk_nlistadd(abook
, (a_c_arg_t
) new_entry_num
, &new_entry_num
,
1248 &resort_happened
, new_addrarray
, 1, 0, 1);
1250 restore_state(&state
);
1252 if(rc
== -2 || rc
== -3){
1253 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1254 _("Error updating address book: %s"),
1255 rc
== -2 ? error_description(errno
) : "Alpine bug");
1258 && strucmp(nick
, nick_saved_for_pico_check
) != 0
1259 && (editor_result
& COMP_EXIT
)){
1262 for(added_to
= 0; added_to
< as
.n_addrbk
; added_to
++)
1263 if(abook_saved_for_pico_check
== as
.adrbks
[added_to
].address_book
)
1266 if(added_to
>= as
.n_addrbk
)
1269 fname
= addr_lookup(nick
, &which_addrbook
, added_to
);
1271 q_status_message4(SM_ORDER
, 5, 9,
1272 /* TRANSLATORS: The first %s is the nickname the user is
1273 trying to use that exists in another address book.
1274 The second %s is the name of the other address book.
1275 The third %s is " as " because it will say something
1276 like also exists in <name> as <description>. */
1277 _("Warning! Nickname %s also exists in \"%s\"%s%s"),
1278 nick
, as
.adrbks
[which_addrbook
].abnick
,
1279 (fname
&& *fname
) ? _(" as ") : "",
1280 (fname
&& *fname
) ? fname
: "");
1281 fs_give((void **)&fname
);
1285 if(resort_happened
|| list_changed
){
1286 DL_CACHE_S dlc_restart
;
1288 dlc_restart
.adrbk_num
= as
.cur
;
1289 dlc_restart
.dlcelnum
= new_entry_num
;
1292 dlc_restart
.type
= DlcSimple
;
1296 dlc_restart
.type
= DlcListHead
;
1303 warp_to_dlc(&dlc_restart
, 0L);
1316 fs_give((void **)&nick
);
1318 fs_give((void **)&full
);
1320 fs_give((void **)&fcc
);
1322 fs_give((void **)&comment
);
1325 fs_give((void **)&comma_sep_addr
);
1326 if(nick_saved_for_pico_check
)
1327 fs_give((void **)&nick_saved_for_pico_check
);
1329 free_list_array(&orig_addrarray
);
1330 free_list_array(&new_addrarray
);
1332 if(expander_address
)
1333 fs_give((void **) &expander_address
);
1340 verify_nick(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
1344 dprint((7, "- verify_nick - (%s)\n", given
? given
: "nul"));
1346 tmp
= cpystr(given
);
1347 removing_leading_and_trailing_white_space(tmp
);
1349 if(nickname_check(tmp
, error
)){
1350 fs_give((void **)&tmp
);
1352 if(ps_global
->mangled_screen
)
1353 *mangled
|= BUILDER_SCREEN_MANGLED
;
1354 else if(ps_global
->mangled_footer
)
1355 *mangled
|= BUILDER_FOOTER_MANGLED
;
1361 if(ps_global
->remote_abook_validity
> 0 &&
1362 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
1363 *mangled
|= BUILDER_SCREEN_MANGLED
;
1366 if(strucmp(tmp
, nick_saved_for_pico_check
) != 0
1367 && adrbk_lookup_by_nick(abook_saved_for_pico_check
,
1368 tmp
, (adrbk_cntr_t
*)NULL
)){
1370 char buf
[MAX_NICKNAME
+ 80];
1372 /* TRANSLATORS: The %s is the nickname of an entry that is already
1373 in the address book */
1374 snprintf(buf
, sizeof(buf
), _("\"%s\" already in address book."), tmp
);
1375 buf
[sizeof(buf
)-1] = '\0';
1376 *error
= cpystr(buf
);
1380 fs_give((void **)&tmp
);
1382 if(ps_global
->mangled_screen
)
1383 *mangled
|= BUILDER_SCREEN_MANGLED
;
1384 else if(ps_global
->mangled_footer
)
1385 *mangled
|= BUILDER_FOOTER_MANGLED
;
1395 fs_give((void **)&tmp
);
1397 /* This is so pico will erase any old message */
1399 *error
= cpystr("");
1402 if(ps_global
->mangled_screen
)
1403 *mangled
|= BUILDER_SCREEN_MANGLED
;
1404 else if(ps_global
->mangled_footer
)
1405 *mangled
|= BUILDER_FOOTER_MANGLED
;
1413 * Args: to -- the passed in line to parse
1414 * full_to -- Address of a pointer to return the full address in.
1415 * This will be allocated here and freed by the caller.
1416 * However, this function is just going to copy "to".
1417 * (special case for the route-addr-hack in build_address_int)
1418 * We're just looking for the error messages.
1419 * error -- Address of a pointer to return an error message in.
1420 * This will be allocated here and freed by the caller.
1421 * fcc -- This should be passed in NULL.
1422 * This builder doesn't support affected_entry's.
1424 * Result: 0 is returned if address was OK,
1425 * -2 if address wasn't OK.
1427 * Side effect: Can flush addrbook entry cache entries so they need to be
1428 * re-fetched afterwords.
1431 verify_addr(char *to
, char **full_to
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
1436 jmp_buf save_jmp_buf
;
1437 int *save_nesting_level
;
1439 dprint((7, "- verify_addr - (%s)\n", to
? to
: "nul"));
1441 /* check to see if to string is empty to avoid work */
1442 for(p
= to
; p
&& *p
&& isspace((unsigned char)(*p
)); p
++)
1447 *full_to
= cpystr(to
? to
: ""); /* because pico does a strcmp() */
1453 *full_to
= (char *)NULL
;
1456 *error
= (char *)NULL
;
1459 * If we end up jumping back here because somebody else changed one of
1460 * our addrbooks out from underneath us, we may well leak some memory.
1461 * That's probably ok since this will be very rare.
1463 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1464 save_nesting_level
= cpyint(ab_nesting_level
);
1465 if(setjmp(addrbook_changed_unexpectedly
)){
1466 if(full_to
&& *full_to
)
1467 fs_give((void **)full_to
);
1469 /* TRANSLATORS: This is sort of an error, something unexpected has
1470 happened and alpine is re-initializing the address book. */
1471 q_status_message(SM_ORDER
, 3, 5, _("Resetting address book..."));
1473 "RESETTING address book... verify_addr(%s)!\n", to
? to
: "?"));
1475 ab_nesting_level
= *save_nesting_level
;
1481 if(ps_global
->remote_abook_validity
> 0 &&
1482 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
1483 *mangled
|= BUILDER_SCREEN_MANGLED
;
1487 ret_val
= build_address_internal(bldto
, full_to
, error
, NULL
, NULL
, NULL
,
1488 save_and_restore
, 1, mangled
);
1491 if(save_nesting_level
)
1492 fs_give((void **)&save_nesting_level
);
1494 if(full_to
&& *full_to
&& ret_val
>= 0)
1495 removing_leading_and_trailing_white_space(*full_to
);
1497 /* This is so pico will erase the old message */
1498 if(error
!= NULL
&& *error
== NULL
)
1499 *error
= cpystr("");
1502 ret_val
= -2; /* cause pico to stay on same header line */
1504 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1510 * Call back for pico to prompt the user for exit confirmation
1512 * Returns: either NULL if the user accepts exit, or string containing
1513 * reason why the user declined.
1516 pico_sendexit_for_adrbk(struct headerentry
*he
, void (*redraw_pico
)(void),
1517 int allow_flowed
, char **result
)
1520 void (*redraw
)(void) = ps_global
->redrawer
;
1522 ps_global
->redrawer
= redraw_pico
;
1523 fix_windsize(ps_global
);
1525 /* TRANSLATORS: A question */
1526 switch(want_to(_("Exit and save changes "), 'y', 0, NO_HELP
, WT_NORM
)){
1531 rstr
= _("Use ^C to abandon changes you've made");
1538 ps_global
->redrawer
= redraw
;
1539 return((rstr
== NULL
) ? 0 : 1);
1544 * Call back for pico to prompt the user for exit confirmation
1546 * Returns: either NULL if the user accepts exit, or string containing
1547 * reason why the user declined.
1550 pico_cancelexit_for_adrbk(char *word
, void (*redraw_pico
)(void))
1554 void (*redraw
)(void) = ps_global
->redrawer
;
1556 /* TRANSLATORS: A question. The %s is a noun describing what is being cancelled. */
1557 snprintf(prompt
, sizeof(prompt
), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), word
);
1558 ps_global
->redrawer
= redraw_pico
;
1559 fix_windsize(ps_global
);
1561 switch(want_to(prompt
, 'y', 'x', NO_HELP
, WT_NORM
)){
1571 ps_global
->redrawer
= redraw
;
1577 pico_cancel_for_adrbk_take(void (*redraw_pico
)(void))
1579 return(pico_cancelexit_for_adrbk(_("take"), redraw_pico
));
1584 pico_cancel_for_adrbk_edit(void (*redraw_pico
)(void))
1586 return(pico_cancelexit_for_adrbk(_("changes"), redraw_pico
));
1591 prompt::name::help::prwid::maxlen::realaddr::
1592 builder::affected_entry::next_affected::selector::key_label::fileedit::
1593 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
1594 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
1596 static struct headerentry headents_for_add
[]={
1597 {"Server Name : ", N_("Server"), h_composer_abook_add_server
, 14, 0, NULL
,
1598 verify_server_name
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1599 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
1600 {"Folder Name : ", N_("Folder"), h_composer_abook_add_folder
, 14, 0, NULL
,
1601 verify_folder_name
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1602 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
1603 {"NickName : ", N_("Nickname"), h_composer_abook_add_nick
, 14, 0, NULL
,
1604 verify_abook_nick
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1605 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
1606 {NULL
, NULL
, NO_HELP
, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1607 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
1615 * Args: global -- Add a global address book, not personal.
1616 * add_after_this -- This is the addrbook number which should come
1617 * right before the new addrbook we're adding, if
1618 * that makes sense. If this is -1, append to end
1621 * Returns: addrbook number of new addrbook, or
1622 * -1, no addrbook added
1625 ab_add_abook(int global
, int add_after_this
)
1629 dprint((2, "- ab_add_abook -\n"));
1631 ret
= ab_modify_abook_list(0, global
, add_after_this
, NULL
, NULL
, NULL
);
1634 q_status_message(SM_ORDER
, 0, 3,
1635 _("New address book added. Use \"$\" to adjust order"));
1642 * Args: global -- Add a global address book, not personal.
1643 * abook_num -- Abook num of the entry we are editing.
1644 * serv -- Default server.
1645 * folder -- Default folder.
1646 * nick -- Default nickname.
1648 * Returns: abook_num if successful,
1652 ab_edit_abook(int global
, int abook_num
, char *serv
, char *folder
, char *nick
)
1654 dprint((2, "- ab_edit_abook -\n"));
1656 return(ab_modify_abook_list(1, global
, abook_num
, serv
, folder
, nick
));
1659 static int the_one_were_editing
;
1663 * Args: edit -- Edit existing entry
1664 * global -- Add a global address book, not personal.
1665 * abook_num -- This is the addrbook number which should come
1666 * right before the new addrbook we're adding, if
1667 * that makes sense. If this is -1, append to end
1669 * If we are editing instead of adding, this is
1670 * the abook number of the entry we are editing.
1671 * def_serv -- Default server.
1672 * def_fold -- Default folder.
1673 * def_nick -- Default nickname.
1675 * Returns: addrbook number of new addrbook, or
1676 * -1, no addrbook added
1679 ab_modify_abook_list(int edit
, int global
, int abook_num
, char *def_serv
, char *def_fold
, char *def_nick
)
1681 struct headerentry
*he
;
1684 int editor_result
, i
, how_many_in_list
= 0, new_abook_num
, num_in_list
;
1686 char *server
, *folder
, *nickname
;
1687 char *new_item
= NULL
;
1689 AccessType remember_access_result
;
1692 char **list
, **new_list
= NULL
;
1693 char tmp
[1000+MAXFOLDER
];
1695 char servpmt
[100], foldpmt
[100], nickpmt
[100];
1696 struct variable
*vars
= ps_global
->vars
;
1698 dprint((2, "- ab_modify_abook_list -\n"));
1700 if(ps_global
->readonly_pinerc
){
1702 /* TRANSLATORS: Change was the name of the command the user was
1703 trying to perform. It is what was cancelled. */
1704 q_status_message(SM_ORDER
, 0, 3, _("Change cancelled: config file not changeable"));
1706 /* TRANSLATORS: Add was the command that is being cancelled. */
1707 q_status_message(SM_ORDER
, 0, 3, _("Add cancelled: config file not changeable"));
1714 if((global
&& vars
[V_GLOB_ADDRBOOK
].is_fixed
) ||
1715 (!global
&& vars
[V_ADDRESSBOOK
].is_fixed
)){
1717 /* TRANSLATORS: Operation was cancelled because the system management
1718 does not allow the changing of global address books */
1719 q_status_message(SM_ORDER
, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing global address books"));
1721 q_status_message(SM_ORDER
, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing address books"));
1726 init_ab_if_needed();
1730 (abook_num
< 0 || abook_num
>= as
.how_many_personals
)) ||
1732 (abook_num
< as
.how_many_personals
||
1733 abook_num
>= as
.n_addrbk
))){
1734 dprint((1, "Programming botch in ab_modify_abook_list: global=%d abook_num=%d n_addrbk=%d\n", global
, abook_num
, as
.n_addrbk
));
1735 q_status_message(SM_ORDER
, 0, 3, "Programming botch, bad abook_num");
1739 the_one_were_editing
= abook_num
;
1742 the_one_were_editing
= -1;
1744 standard_picobuf_setup(&pbf
);
1745 pbf
.exittest
= pico_sendexit_for_adrbk
;
1746 pbf
.canceltest
= pico_cancel_for_adrbk_edit
;
1748 /* TRANSLATORS: screen title */
1749 strncpy(titlebar
, _("CHANGE ADDRESS BOOK"), sizeof(titlebar
));
1751 /* TRANSLATORS: screen title */
1752 strncpy(titlebar
, _("ADD ADDRESS BOOK"), sizeof(titlebar
));
1754 titlebar
[sizeof(titlebar
)-1] = '\0';
1755 pbf
.pine_anchor
= set_titlebar(titlebar
,
1756 ps_global
->mail_stream
,
1757 ps_global
->context_current
,
1758 ps_global
->cur_folder
,ps_global
->msgmap
,
1759 0, FolderName
, 0, 0, NULL
);
1760 pbf
.pine_flags
|= P_NOBODY
;
1762 /* An informational message */
1763 if((msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1766 /* TRANSLATORS: The next few lines go together to explain how to add
1767 the address book entry the user is working on. */
1768 _(" To add a local address book that will be accessed *only* by Alpine running\n on this machine, leave the server field blank.");
1770 _(" To add an address book that will be accessed by IMAP, fill in the\n server name.");
1772 _(" (NOTE: An address book cannot be accessed by IMAP from\n one Alpine and as a local file from another. It is either always accessed\n by IMAP or it is a local address book which is never accessed by IMAP.)");
1774 _(" In the Folder field, type the remote folder name or local file name.");
1776 _(" In the Nickname field, give the address book a nickname or leave it blank.");
1778 _(" To get help specific to an item, press ^G.");
1780 _(" To exit and save the configuration, press ^X. To cancel, press ^C.");
1782 pbf
.msgtext
= (void *)so_text(msgso
);
1784 * It's nice if we can make it so these lines make sense even if
1785 * they don't all make it on the screen, because the user can't
1786 * scroll down to see them.
1788 * The 3 is the number of fields to be defined, the 1 is for a
1789 * single blank line after the field definitions.
1791 lines_avail
= ps_global
->ttyo
->screen_rows
- HEADER_ROWS(ps_global
) -
1792 FOOTER_ROWS(ps_global
) - 3 - 1;
1794 if(lines_avail
>= 15){ /* extra blank line */
1795 so_puts(msgso
, "\n");
1799 if(lines_avail
>= 2){
1804 if(lines_avail
>= 5){
1805 so_puts(msgso
, "\n\n");
1810 else if(lines_avail
>= 3){
1811 so_puts(msgso
, "\n\n");
1816 if(lines_avail
>= 2){
1817 so_puts(msgso
, "\n\n");
1822 if(lines_avail
>= 2){
1823 so_puts(msgso
, "\n\n");
1828 if(lines_avail
>= 3){
1829 so_puts(msgso
, "\n\n");
1831 so_puts(msgso
, "\n");
1834 else if(lines_avail
>= 2){
1835 so_puts(msgso
, "\n\n");
1840 he
= (struct headerentry
*)fs_get((NN_END
+1) * sizeof(struct headerentry
));
1841 memset((void *)he
, 0, (NN_END
+1) * sizeof(struct headerentry
));
1844 abook_indent
= utf8_width(_("Server Name")) + 2;
1846 /* make a copy of each field */
1847 server
= cpystr(def_serv
? def_serv
: "");
1848 he
[NN_SERVER
] = headents_for_add
[NN_SERVER
];
1849 he
[NN_SERVER
].realaddr
= &server
;
1850 utf8_snprintf(servpmt
, sizeof(servpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Server Name"));
1851 he
[NN_SERVER
].prompt
= servpmt
;
1852 he
[NN_SERVER
].prwid
= abook_indent
+2;
1854 folder
= cpystr(def_fold
? def_fold
: "");
1855 he
[NN_FOLDER
] = headents_for_add
[NN_FOLDER
];
1856 he
[NN_FOLDER
].realaddr
= &folder
;
1857 utf8_snprintf(foldpmt
, sizeof(foldpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Folder Name"));
1858 he
[NN_FOLDER
].prompt
= foldpmt
;
1859 he
[NN_FOLDER
].prwid
= abook_indent
+2;
1861 nickname
= cpystr(def_nick
? def_nick
: "");
1862 he
[NN_NICK
] = headents_for_add
[NN_NICK
];
1863 he
[NN_NICK
].realaddr
= &nickname
;
1864 utf8_snprintf(nickpmt
, sizeof(nickpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
1865 he
[NN_NICK
].prompt
= nickpmt
;
1866 he
[NN_NICK
].prwid
= abook_indent
+2;
1868 he
[NN_END
] = headents_for_add
[NN_END
];
1870 /* pass to pico and let user change them */
1871 editor_result
= pico(&pbf
);
1872 standard_picobuf_teardown(&pbf
);
1874 if(editor_result
& COMP_GOTHUP
){
1879 fix_windsize(ps_global
);
1883 if(editor_result
& COMP_CANCEL
){
1886 q_status_message(SM_ORDER
, 0, 3, _("Address book change is cancelled"));
1888 q_status_message(SM_ORDER
, 0, 3, _("Address book add is cancelled"));
1890 else if(editor_result
& COMP_EXIT
){
1892 !strcmp(server
, def_serv
? def_serv
: "") &&
1893 !strcmp(folder
, def_fold
? def_fold
: "") &&
1894 !strcmp(nickname
, def_nick
? def_nick
: "")){
1897 q_status_message(SM_ORDER
, 0, 3, _("No change: Address book change is cancelled"));
1899 q_status_message(SM_ORDER
, 0, 3, _("No change: Address book add is cancelled"));
1903 list
= VAR_GLOB_ADDRBOOK
;
1904 how_many_in_list
= as
.n_addrbk
- as
.how_many_personals
;
1906 new_abook_num
= abook_num
;
1907 else if(abook_num
< 0)
1908 new_abook_num
= as
.n_addrbk
;
1910 new_abook_num
= MAX(MIN(abook_num
+ 1, as
.n_addrbk
),
1911 as
.how_many_personals
);
1913 num_in_list
= new_abook_num
- as
.how_many_personals
;
1916 list
= VAR_ADDRESSBOOK
;
1917 how_many_in_list
= as
.how_many_personals
;
1918 new_abook_num
= abook_num
;
1920 new_abook_num
= abook_num
;
1921 else if(abook_num
< 0)
1922 new_abook_num
= as
.how_many_personals
;
1924 new_abook_num
= MIN(abook_num
+ 1, as
.how_many_personals
);
1926 num_in_list
= new_abook_num
;
1930 how_many_in_list
++; /* for new abook */
1932 removing_leading_and_trailing_white_space(server
);
1933 removing_leading_and_trailing_white_space(folder
);
1934 removing_leading_and_trailing_white_space(nickname
);
1936 /* convert nickname to UTF-8 */
1940 conv
= convert_to_utf8(nickname
, NULL
, 0);
1942 fs_give((void **) &nickname
);
1947 /* eliminate surrounding brackets */
1948 if(server
[0] == '{' && server
[strlen(server
)-1] == '}'){
1951 server
[strlen(server
)-1] = '\0';
1952 for(p
= server
; *p
; p
++)
1956 snprintf(tmp
, sizeof(tmp
), "%s%s%s%.*s",
1958 *server
? server
: "",
1961 tmp
[sizeof(tmp
)-1] = '\0';
1963 new_item
= put_pair(nickname
, tmp
);
1965 if(!new_item
|| *new_item
== '\0'){
1967 q_status_message(SM_ORDER
, 0, 3, _("Address book change is cancelled"));
1969 q_status_message(SM_ORDER
, 0, 3, _("Address book add is cancelled"));
1975 /* allocate for new list */
1976 new_list
= (char **)fs_get((how_many_in_list
+ 1) * sizeof(char *));
1978 /* copy old list up to where we will insert new entry */
1979 for(i
= 0; i
< num_in_list
; i
++)
1980 new_list
[i
] = cpystr(list
[i
]);
1982 /* insert the new entry */
1983 new_list
[i
++] = cpystr(new_item
);
1985 /* copy rest of old list, skip current if editing */
1986 for(; i
< how_many_in_list
; i
++)
1987 new_list
[i
] = cpystr(list
[edit
? i
: (i
-1)]);
1991 /* this frees old variable contents for us */
1992 if(set_variable_list(global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
,
1993 new_list
, TRUE
, ew
)){
1995 q_status_message(SM_ORDER
, 0, 3, _("Change cancelled: couldn't save configuration file"));
1997 q_status_message(SM_ORDER
, 0, 3, _("Add cancelled: couldn't save configuration file"));
1999 set_current_val(&vars
[global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
],
2005 ret
= new_abook_num
;
2006 set_current_val(&vars
[global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
],
2010 init_ab_if_needed();
2013 * Test to see if this definition is going to work.
2014 * Error messages are a good side effect.
2016 pab
= &as
.adrbks
[num_in_list
];
2017 init_abook(pab
, NoDisplay
);
2018 remember_access_result
= pab
->access
;
2020 init_ab_if_needed();
2021 /* if we had trouble, give a clue to user (other than error msg) */
2022 if(remember_access_result
== NoAccess
){
2023 pab
= &as
.adrbks
[num_in_list
];
2024 pab
->access
= remember_access_result
;
2035 free_list_array(&new_list
);
2038 fs_give((void **)&new_item
);
2044 fs_give((void **)&server
);
2046 fs_give((void **)&folder
);
2048 fs_give((void **)&nickname
);
2055 any_addrbooks_to_convert(struct pine
*ps
)
2060 init_ab_if_needed();
2062 for(i
= 0; i
< as
.n_addrbk
; i
++){
2063 pab
= &as
.adrbks
[i
];
2064 if(pab
&& !(pab
->type
& REMOTE_VIA_IMAP
) && !(pab
->type
& GLOBAL
))
2073 convert_addrbooks_to_remote(struct pine
*ps
, char *rem_folder_prefix
, size_t len
)
2076 int i
, count
= 0, ret
= 0;
2078 init_ab_if_needed();
2080 for(i
= 0; i
< as
.n_addrbk
; i
++){
2081 pab
= &as
.adrbks
[i
];
2082 if(pab
&& !(pab
->type
& REMOTE_VIA_IMAP
) && !(pab
->type
& GLOBAL
))
2086 for(i
= 0; ret
!= -1 && i
< as
.n_addrbk
; i
++){
2087 pab
= &as
.adrbks
[i
];
2088 if(pab
&& !(pab
->type
& REMOTE_VIA_IMAP
) && !(pab
->type
& GLOBAL
))
2089 ret
= convert_abook_to_remote(ps
, pab
, rem_folder_prefix
, len
, count
);
2097 * Returns -1 if cancelled, -2 on error, 0 otherwise.
2100 convert_abook_to_remote(struct pine
*ps
, PerAddrBook
*pab
, char *rem_folder_prefix
, size_t len
, int count
)
2102 #define DEF_ABOOK_NAME "remote_addrbook"
2103 char local_file
[MAILTMPLEN
];
2104 char rem_abook
[MAILTMPLEN
+3], prompt
[MAILTMPLEN
], old_nick
[MAILTMPLEN
];
2105 char *p
= NULL
, *err_msg
= NULL
, *q
;
2106 char *serv
= NULL
, *nick
= NULL
, *file
= NULL
, *folder
= NULL
;
2107 int ans
, rc
, offset
, i
, abook_num
= -1, flags
= OE_APPEND_CURRENT
;
2110 snprintf(old_nick
, sizeof(old_nick
), "%s%s%s",
2111 count
> 1 ? " \"" : "",
2112 count
> 1 ? pab
->abnick
: "",
2113 count
> 1 ? "\"" : "");
2114 old_nick
[sizeof(old_nick
)-1] = '\0';
2116 snprintf(prompt
, sizeof(prompt
), _("Convert addressbook%s to a remote addrbook "), old_nick
);
2117 prompt
[sizeof(prompt
)-1] = '\0';
2118 if((ans
=want_to(prompt
, 'y', 'x', h_convert_abook
, WT_NORM
)) != 'y')
2119 return(ans
== 'n' ? 0 : -1);
2121 /* make sure the addrbook has been opened before, so that the file exists */
2122 if(pab
->ostatus
== Closed
|| pab
->ostatus
== HalfOpen
){
2123 (void)init_addrbooks(NoDisplay
, 0, 0, 0);
2124 (void)init_addrbooks(Closed
, 0, 0, 0);
2128 strncpy(local_file
, pab
->filename
, sizeof(local_file
)-1);
2129 local_file
[sizeof(local_file
)-1] = '\0';
2131 p
= strrindex(pab
->filename
, '\\');
2133 p
= strrindex(pab
->filename
, '/');
2137 strncpy(rem_abook
, rem_folder_prefix
, sizeof(rem_abook
)-3);
2139 /* TRANSLATORS: The user is defining an address book which will be
2140 stored on another server. This is called a remote addrbook and
2141 this is a question asking for the name of the server. */
2142 snprintf(prompt
, sizeof(prompt
), _("Name of server to contain remote addrbook : "));
2143 prompt
[sizeof(prompt
)-1] = '\0';
2146 rc
= optionally_enter(rem_abook
, -FOOTER_ROWS(ps
), 0,
2147 sizeof(rem_abook
), prompt
, NULL
,
2149 removing_leading_and_trailing_white_space(rem_abook
);
2151 help
= help
== NO_HELP
? h_convert_pinerc_server
: NO_HELP
;
2154 cmd_cancelled(NULL
);
2160 offset
= strlen(rem_abook
);
2161 for(i
= offset
; i
>= 0; i
--)
2162 rem_abook
[i
+1] = rem_abook
[i
];
2165 rem_abook
[++offset
] = '}';
2166 rem_abook
[++offset
] = '\0';
2175 strncat(rem_abook
, p
+1,
2176 sizeof(rem_abook
)-1-strlen(rem_abook
));
2178 strncat(rem_abook
, DEF_ABOOK_NAME
,
2179 sizeof(rem_abook
)-1-strlen(rem_abook
));
2183 file
= cpystr(rem_abook
);
2185 int len
= MAX(strlen(pab
->abnick
),strlen("Address Book"))+8;
2186 nick
= (char *)fs_get(len
* sizeof(char));
2187 snprintf(nick
, len
, "Remote %s",
2188 (pab
->abnick
&& !strcmp(pab
->abnick
, DF_ADDRESSBOOK
))
2189 ? "Address Book" : pab
->abnick
);
2193 nick
= cpystr("Remote Address Book");
2195 if(file
&& *file
== '{'){
2197 if((p
= strindex(file
, '}'))){
2203 fs_give((void **)&file
);
2209 q_status_message(SM_ORDER
, 3, 5,
2210 _("You now have a chance to change the name of the remote addrbook..."));
2211 abook_num
= ab_modify_abook_list(0, 0, -1, serv
, folder
, nick
);
2213 /* extract folder name of new abook so we can copy to it */
2216 EditWhich ew
= Main
;
2218 lval
= LVAL(&ps
->vars
[V_ADDRESSBOOK
], ew
);
2219 get_pair(lval
[abook_num
], &nick
, &file
, 0, 0);
2221 fs_give((void **)&nick
);
2224 strncpy(rem_abook
, file
, sizeof(rem_abook
)-1);
2225 rem_abook
[sizeof(rem_abook
)-1] = '\0';
2226 fs_give((void **)&file
);
2230 /* copy the abook */
2231 if(abook_num
>= 0 && copy_abook(local_file
, rem_abook
, &err_msg
)){
2233 q_status_message(SM_ORDER
| SM_DING
, 7, 10, err_msg
);
2234 fs_give((void **)&err_msg
);
2239 else if(abook_num
>= 0){ /* give user some info */
2245 * Save the hostname in rem_folder_prefix so we can use it again
2246 * for other conversions if needed.
2248 if((beg
= rem_abook
)
2249 && (*beg
== '{' || (*beg
== '*' && *++beg
== '{'))
2250 && (end
= strindex(rem_abook
, '}'))){
2251 rem_folder_prefix
[0] = '{';
2252 strncpy(rem_folder_prefix
+1, beg
+1, MIN(end
-beg
,len
-2));
2253 rem_folder_prefix
[MIN(end
-beg
,len
-2)] = '}';
2254 rem_folder_prefix
[MIN(end
-beg
+1,len
-1)] = '\0';
2257 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2258 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2259 _("Error allocating space for message."));
2263 /* TRANSLATORS: Several lines in a row here that go together. */
2264 snprintf(prompt
, sizeof(prompt
), _("\nYour addressbook%s has been copied to the"), old_nick
);
2265 prompt
[sizeof(prompt
)-1] = '\0';
2266 so_puts(store
, prompt
);
2267 so_puts(store
, _("\nremote folder \""));
2268 so_puts(store
, rem_abook
);
2269 so_puts(store
, "\".");
2270 so_puts(store
, _("\nA definition for this remote address book has been added to your list"));
2271 so_puts(store
, _("\nof address books. The definition for the address book it was copied"));
2272 so_puts(store
, _("\nfrom is also still there. You may want to remove that after you"));
2273 so_puts(store
, _("\nare confident that the new address book is complete and working."));
2274 so_puts(store
, _("\nUse the Setup/AddressBooks command to do that.\n"));
2276 memset(&sargs
, 0, sizeof(SCROLL_S
));
2277 sargs
.text
.text
= so_text(store
);
2278 sargs
.text
.src
= CharStar
;
2279 sargs
.text
.desc
= _("Remote Address Book Information");
2280 /* TRANSLATORS: a screen title */
2281 sargs
.bar
.title
= _("ABOUT REMOTE ABOOK");
2282 sargs
.help
.text
= NO_HELP
;
2283 sargs
.help
.title
= NULL
;
2287 so_give(&store
); /* free resources associated with store */
2288 ps
->mangled_screen
= 1;
2296 any_sigs_to_convert(struct pine
*ps
)
2298 char *sigfile
, *litsig
;
2302 PAT_LINE_S
*patline
;
2304 /* first check main signature file */
2305 sigfile
= ps
->VAR_SIGNATURE_FILE
;
2306 litsig
= ps
->VAR_LITERAL_SIG
;
2308 if(sigfile
&& *sigfile
&& !litsig
&& sigfile
[strlen(sigfile
)-1] != '|' &&
2309 !IS_REMOTE(sigfile
))
2312 rflags
= (ROLE_DO_ROLES
| PAT_USE_MAIN
);
2313 if(any_patterns(rflags
, &pstate
)){
2314 set_pathandle(rflags
);
2315 for(patline
= *cur_pat_h
? (*cur_pat_h
)->patlinehead
: NULL
;
2316 patline
; patline
= patline
->next
){
2317 for(pat
= patline
->first
; pat
; pat
= pat
->next
){
2320 * See detoken() for when a sig file is used with a role.
2322 sigfile
= pat
->action
? pat
->action
->sig
: NULL
;
2323 litsig
= pat
->action
? pat
->action
->litsig
: NULL
;
2325 if(sigfile
&& *sigfile
&& !litsig
&&
2326 sigfile
[strlen(sigfile
)-1] != '|' &&
2327 !IS_REMOTE(sigfile
))
2338 any_rule_files_to_warn_about(struct pine
*ps
)
2344 rflags
= (ROLE_DO_ROLES
| ROLE_DO_INCOLS
| ROLE_DO_SCORES
|
2345 ROLE_DO_FILTER
| ROLE_DO_OTHER
| ROLE_DO_SRCH
| PAT_USE_MAIN
);
2346 if(any_patterns(rflags
, &pstate
)){
2347 for(pat
= first_pattern(&pstate
);
2349 pat
= next_pattern(&pstate
)){
2350 if(pat
->patline
&& pat
->patline
->type
== File
)
2363 convert_sigs_to_literal(struct pine
*ps
, int interactive
)
2365 EditWhich ew
= Main
;
2366 char *sigfile
, *litsig
, *cstring_version
, *nick
, *src
= NULL
;
2367 char prompt
[MAILTMPLEN
];
2374 PAT_LINE_S
*patline
;
2376 /* first check main signature file */
2377 sigfile
= ps
->VAR_SIGNATURE_FILE
;
2378 litsig
= ps
->VAR_LITERAL_SIG
;
2380 if(sigfile
&& *sigfile
&& !litsig
&& sigfile
[strlen(sigfile
)-1] != '|' &&
2381 !IS_REMOTE(sigfile
)){
2383 snprintf(prompt
,sizeof(prompt
),
2384 /* TRANSLATORS: A literal sig is a way to store a signature
2385 in alpine. It isn't a very descriptive name. Instead of
2386 storing it in its own file it is stored in the configuration
2388 _("Convert signature file \"%s\" to a literal sig "),
2390 prompt
[sizeof(prompt
)-1] = '\0';
2392 ps
->mangled_body
= 1;
2393 if((ans
=want_to(prompt
, 'y', 'x', h_convert_sig
, WT_NORM
)) == 'x'){
2394 cmd_cancelled(NULL
);
2401 if(ans
== 'y' && (src
= get_signature_file(sigfile
, 0, 0, 0)) != NULL
){
2402 cstring_version
= string_to_cstring(src
);
2403 set_variable(V_LITERAL_SIG
, cstring_version
, 0, 0, ew
);
2406 fs_give((void **)&cstring_version
);
2408 fs_give((void **)&src
);
2411 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2412 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2413 _("Error allocating space for message."));
2417 snprintf(prompt
, sizeof(prompt
),
2418 /* TRANSLATORS: following lines go together */
2419 _("\nYour signature file \"%s\" has been converted"), sigfile
);
2420 prompt
[sizeof(prompt
)-1] = '\0';
2421 so_puts(store
, prompt
);
2423 _("\nto a literal signature, which means it is contained in your"));
2425 _("\nAlpine configuration instead of being in a file of its own."));
2427 _("\nIf that configuration is copied to a remote folder then the"));
2429 _("\nsignature will be available remotely also."));
2431 _("\nChanges to the signature file itself will no longer have any"));
2433 _("\neffect on Alpine but you may still edit the signature with the"));
2435 _("\nSetup/Signature command.\n"));
2437 memset(&sargs
, 0, sizeof(SCROLL_S
));
2438 sargs
.text
.text
= so_text(store
);
2439 sargs
.text
.src
= CharStar
;
2440 sargs
.text
.desc
= _("Literal Signature Information");
2441 /* TRANSLATORS: screen title */
2442 sargs
.bar
.title
= _("ABOUT LITERAL SIG");
2443 sargs
.help
.text
= NO_HELP
;
2444 sargs
.help
.title
= NULL
;
2449 ps
->mangled_screen
= 1;
2454 rflags
= (ROLE_DO_ROLES
| PAT_USE_MAIN
);
2455 if(any_patterns(rflags
, &pstate
)){
2456 set_pathandle(rflags
);
2457 for(patline
= *cur_pat_h
? (*cur_pat_h
)->patlinehead
: NULL
;
2458 patline
; patline
= patline
->next
){
2459 for(pat
= patline
->first
; pat
; pat
= pat
->next
){
2462 * See detoken() for when a sig file is used with a role.
2464 sigfile
= pat
->action
? pat
->action
->sig
: NULL
;
2465 litsig
= pat
->action
? pat
->action
->litsig
: NULL
;
2466 nick
= (pat
->action
&& pat
->action
->nick
&& pat
->action
->nick
[0]) ? pat
->action
->nick
: NULL
;
2468 if(sigfile
&& *sigfile
&& !litsig
&&
2469 sigfile
[strlen(sigfile
)-1] != '|' &&
2470 !IS_REMOTE(sigfile
)){
2472 snprintf(prompt
,sizeof(prompt
),
2473 /* TRANSLATORS: asking whether a signature file should be converted to what
2474 we call a literal signature, which is one contained in the regular
2475 configuration file. Think of the set of 4 %s arguments as a
2476 single argument which is the name of the signature file. */
2477 _("Convert signature file \"%s\"%s%s%s to a literal sig "),
2479 nick
? " in role \"" : "",
2482 prompt
[sizeof(prompt
)-1] = '\0';
2484 ps
->mangled_body
= 1;
2485 if((ans
=want_to(prompt
, 'y', 'x',
2486 h_convert_sig
, WT_NORM
)) == 'x'){
2487 cmd_cancelled(NULL
);
2495 (src
= get_signature_file(sigfile
,0,0,0)) != NULL
){
2497 cstring_version
= string_to_cstring(src
);
2499 if(pat
->action
->litsig
)
2500 fs_give((void **)&pat
->action
->litsig
);
2502 pat
->action
->litsig
= cstring_version
;
2503 fs_give((void **)&src
);
2505 set_pathandle(rflags
);
2506 if(patline
->type
== Literal
)
2507 (*cur_pat_h
)->dirtypinerc
= 1;
2511 if(write_patterns(rflags
) == 0){
2514 * Flush out current_vals of anything we've
2517 close_patterns(ROLE_DO_ROLES
| PAT_USE_CURRENT
);
2519 if(!(store
=so_get(CharStar
,NULL
,EDIT_ACCESS
))){
2520 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2521 _("Error allocating space for message."));
2525 snprintf(prompt
, sizeof(prompt
),
2526 /* TRANSLATORS: Keep the %s's together, they are sort of
2527 the name of the file. */
2528 _("Your signature file \"%s\"%s%s%s has been converted"),
2530 nick
? " in role \"" : "",
2533 prompt
[sizeof(prompt
)-1] = '\0';
2534 so_puts(store
, prompt
);
2536 /* TRANSLATORS: several lines that go together */
2537 _("\nto a literal signature, which means it is contained in your"));
2539 _("\nAlpine configuration instead of being in a file of its own."));
2541 _("\nIf that configuration is copied to a remote folder then the"));
2543 _("\nsignature will be available remotely also."));
2545 _("\nChanges to the signature file itself will no longer have any"));
2547 _("\neffect on Alpine. You may edit the signature with the"));
2549 _("\nSetup/Rules/Roles command.\n"));
2551 memset(&sargs
, 0, sizeof(SCROLL_S
));
2552 sargs
.text
.text
= so_text(store
);
2553 sargs
.text
.src
= CharStar
;
2555 _("Literal Signature Information");
2556 /* TRANSLATORS: a screen title */
2557 sargs
.bar
.title
= _("ABOUT LITERAL SIG");
2558 sargs
.help
.text
= NO_HELP
;
2559 sargs
.help
.title
= NULL
;
2564 ps
->mangled_screen
= 1;
2567 else if(interactive
){
2568 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2569 /* TRANSLATORS: config is an abbreviation for configuration */
2570 _("Error writing rules config."));
2573 /* TRANSLATORS: sig is signature */
2574 fprintf(stderr
, _("Error converting role sig\n"));
2588 warn_about_rule_files(struct pine
*ps
)
2593 if(any_rule_files_to_warn_about(ps
)){
2594 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2595 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2596 _("Error allocating space for message."));
2600 /* TRANSLATORS: several lines that go together */
2601 so_puts(store
, _("\nSome of your Rules are contained in Rule files instead of being directly"));
2602 so_puts(store
, _("\ncontained in your Alpine configuration file. To make those rules"));
2603 so_puts(store
, _("\navailable remotely you will need to move them out of the files."));
2604 so_puts(store
, _("\nThat can be done using the Shuffle command in the appropriate"));
2605 so_puts(store
, _("\nSetup/Rules subcommands.\n"));
2607 memset(&sargs
, 0, sizeof(SCROLL_S
));
2608 sargs
.text
.text
= so_text(store
);
2609 sargs
.text
.src
= CharStar
;
2610 sargs
.text
.desc
= _("Rule Files Information");
2611 /* TRANSLATORS: a screen title */
2612 sargs
.bar
.title
= _("ABOUT RULE FILES");
2613 sargs
.help
.text
= NO_HELP
;
2614 sargs
.help
.title
= NULL
;
2619 ps
->mangled_screen
= 1;
2625 convert_to_remote_config(struct pine
*ps
, int edit_exceptions
)
2627 char rem_pinerc_prefix
[MAILTMPLEN
];
2630 int abooks
= 0, sigs
= 0;
2632 if(edit_exceptions
){
2633 /* TRANSLATORS: The exceptions command (X) was typed but it doesn't make sense */
2634 q_status_message(SM_ORDER
, 3, 5,
2635 _("eXceptions does not make sense with this command"));
2640 alpine_panic("NULL prc in convert_to_remote_config");
2642 dprint((2, "convert_to_remote_config\n"));
2644 if(ps
->prc
->type
== RemImap
){ /* pinerc is already remote */
2645 char prompt
[MAILTMPLEN
];
2648 * Check to see if there is anything at all to do. If there are
2649 * address books to convert, sigfiles to convert, or rule files
2650 * to comment on, we have something to do. Otherwise, just bail.
2652 abooks
= any_addrbooks_to_convert(ps
);
2653 sigs
= any_sigs_to_convert(ps
);
2657 /* TRANSLATORS: AddressBooks is Address Books */
2658 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert AddressBooks and signature files "));
2660 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert AddressBooks "));
2662 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert signature files "));
2664 prompt
[sizeof(prompt
)-1] = '\0';
2665 if(want_to(prompt
, 'y', 'x',
2666 (abooks
&& sigs
) ? h_convert_abooks_and_sigs
:
2667 abooks
? h_convert_abooks
:
2668 sigs
? h_convert_sigs
: NO_HELP
,
2670 cmd_cancelled(NULL
);
2677 * Figure out a good default for where to put the remote config.
2678 * If the default collection is remote we'll take the hostname and
2679 * and modifiers from there. If not, we'll try to get the hostname from
2680 * the inbox-path. In either case, we use the home directory on the
2681 * server, not the directory where the folder collection is (if different).
2682 * If we don't have a clue, we'll ask user.
2684 if((context
= default_save_context(ps
->context_list
)) != NULL
&&
2685 IS_REMOTE(context_apply(rem_pinerc_prefix
, context
, "",
2686 sizeof(rem_pinerc_prefix
)))){
2687 /* just use the host from the default collection, not the whole path */
2688 if((end
= strrindex(rem_pinerc_prefix
, '}')) != NULL
)
2692 /* use host from inbox path */
2693 rem_pinerc_prefix
[0] = '\0';
2694 if((beg
= ps
->VAR_INBOX_PATH
)
2695 && (*beg
== '{' || (*beg
== '*' && *++beg
== '{'))
2696 && (end
= strindex(ps
->VAR_INBOX_PATH
, '}'))){
2697 rem_pinerc_prefix
[0] = '{';
2698 strncpy(rem_pinerc_prefix
+1, beg
+1,
2699 MIN(end
-beg
, sizeof(rem_pinerc_prefix
)-2));
2700 rem_pinerc_prefix
[MIN(end
-beg
, sizeof(rem_pinerc_prefix
)-2)] = '}';
2701 rem_pinerc_prefix
[MIN(end
-beg
+1,sizeof(rem_pinerc_prefix
)-1)]='\0';
2705 /* ask about converting addrbooks to remote abooks */
2706 if(ps
->prc
->type
!= RemImap
|| abooks
)
2707 if(convert_addrbooks_to_remote(ps
, rem_pinerc_prefix
,
2708 sizeof(rem_pinerc_prefix
)) == -1){
2709 cmd_cancelled(NULL
);
2713 /* ask about converting sigfiles to literal sigs */
2714 if(ps
->prc
->type
!= RemImap
|| sigs
)
2715 if(convert_sigs_to_literal(ps
, 1) == -1){
2716 cmd_cancelled(NULL
);
2720 warn_about_rule_files(ps
);
2722 /* finally, copy the config file */
2723 if(ps
->prc
->type
== Loc
)
2724 convert_pinerc_to_remote(ps
, rem_pinerc_prefix
);
2725 else if(!(abooks
|| sigs
))
2726 q_status_message(SM_ORDER
, 3, 5,
2727 _("Cannot copy config file since it is already remote."));
2732 convert_pinerc_to_remote(struct pine
*ps
, char *rem_pinerc_prefix
)
2734 #define DEF_FOLDER_NAME "remote_pinerc"
2735 char prompt
[MAILTMPLEN
], rem_pinerc
[MAILTMPLEN
];
2736 char *err_msg
= NULL
;
2739 int flags
= OE_APPEND_CURRENT
;
2742 ps
->mangled_body
= 1;
2743 strncpy(rem_pinerc
, rem_pinerc_prefix
, sizeof(rem_pinerc
)-1);
2744 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2746 if(*rem_pinerc
== '\0'){
2747 snprintf(prompt
, sizeof(prompt
), _("Name of server to contain remote Alpine config : "));
2748 prompt
[sizeof(prompt
)-1] = '\0';
2751 rc
= optionally_enter(rem_pinerc
, -FOOTER_ROWS(ps
), 0,
2752 sizeof(rem_pinerc
), prompt
, NULL
,
2754 removing_leading_and_trailing_white_space(rem_pinerc
);
2756 help
= help
== NO_HELP
? h_convert_pinerc_server
: NO_HELP
;
2759 cmd_cancelled(NULL
);
2765 offset
= strlen(rem_pinerc
);
2766 for(i
= offset
; i
>= 0; i
--)
2767 if(i
+1 < sizeof(rem_pinerc
))
2768 rem_pinerc
[i
+1] = rem_pinerc
[i
];
2770 rem_pinerc
[0] = '{';
2771 if(offset
+2 < sizeof(rem_pinerc
)){
2772 rem_pinerc
[++offset
] = '}';
2773 rem_pinerc
[++offset
] = '\0';
2782 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2785 * Add a default folder name.
2789 * Add /user= to modify hostname so that user won't be asked who they
2790 * are each time they login.
2792 if(!strstr(rem_pinerc
, "/user=") && ps
->VAR_USER_ID
&&
2793 ps
->VAR_USER_ID
[0]){
2796 p
= rem_pinerc
+ strlen(rem_pinerc
) - 1;
2797 if(*p
== '}') /* this should be the case */
2798 snprintf(p
, sizeof(rem_pinerc
)-(p
-rem_pinerc
), "/user=\"%s\"}", ps
->VAR_USER_ID
);
2800 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2803 strncat(rem_pinerc
, DEF_FOLDER_NAME
,
2804 sizeof(rem_pinerc
) - strlen(rem_pinerc
) - 1);
2805 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2808 /* ask user about folder name for remote config */
2809 snprintf(prompt
, sizeof(prompt
), _("Folder to contain remote config : "));
2810 prompt
[sizeof(prompt
)-1] = '\0';
2813 rc
= optionally_enter(rem_pinerc
, -FOOTER_ROWS(ps
), 0,
2814 sizeof(rem_pinerc
), prompt
, NULL
, help
, &flags
);
2815 removing_leading_and_trailing_white_space(rem_pinerc
);
2816 if(rc
== 0 && *rem_pinerc
){
2821 help
= (help
== NO_HELP
) ? h_convert_pinerc_folder
: NO_HELP
;
2823 else if(rc
== 1 || rem_pinerc
[0] == '\0'){
2824 cmd_cancelled(NULL
);
2831 * If we are on a Unix system, writing to a remote config, we want the
2832 * remote config to work smoothly from a PC, too. If we don't have a
2833 * user-id on the PC then we will be asked for our password.
2834 * So add user-id to the pinerc before we copy it.
2836 if(!ps
->vars
[V_USER_ID
].main_user_val
.p
&& ps
->VAR_USER_ID
)
2837 ps
->vars
[V_USER_ID
].main_user_val
.p
= cpystr(ps
->VAR_USER_ID
);
2839 ps
->vars
[V_USER_ID
].is_used
= 1; /* so it will write to pinerc */
2840 ps
->prc
->outstanding_pinerc_changes
= 1;
2843 if(ps
->prc
->outstanding_pinerc_changes
)
2844 write_pinerc(ps
, Main
, WRP_NONE
);
2847 ps
->vars
[V_USER_ID
].is_used
= 0;
2850 /* copy the pinerc */
2851 if(copy_pinerc(ps
->prc
->name
, rem_pinerc
, &err_msg
)){
2853 q_status_message(SM_ORDER
| SM_DING
, 7, 10, err_msg
);
2854 fs_give((void **)&err_msg
);
2860 /* tell user about command line flags */
2861 if(ps
->prc
->type
!= RemImap
){
2865 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2866 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2867 _("Error allocating space for message."));
2871 /* TRANSLATORS: several lines that go together */
2872 so_puts(store
, _("\nYou may want to save a copy of this information!"));
2873 so_puts(store
, _("\n\nYour Alpine configuration data has been copied to"));
2874 so_puts(store
, "\n\n ");
2875 so_puts(store
, rem_pinerc
);
2876 so_puts(store
, "\n");
2877 so_puts(store
, _("\nTo use that remote configuration from this computer you will"));
2878 so_puts(store
, _("\nhave to change the way you start Alpine by using the command line option"));
2879 so_puts(store
, _("\n\"alpine -p <remote_folder>\". The command should probably be"));
2881 so_puts(store
, "\n\n ");
2882 so_puts(store
, "alpine -p ");
2883 so_puts(store
, rem_pinerc
);
2884 so_puts(store
, "\n");
2885 so_puts(store
, _("\nWith PC-Alpine, you may want to create a shortcut which"));
2886 so_puts(store
, _("\nhas the required arguments."));
2888 so_puts(store
, "\n\n ");
2889 so_puts(store
, "alpine -p \"");
2890 so_puts(store
, rem_pinerc
);
2891 so_puts(store
, "\"\n");
2892 so_puts(store
, _("\nThe quotes are there around the last argument to protect the special"));
2893 so_puts(store
, _("\ncharacters in the folder name (like braces) from the command shell"));
2894 so_puts(store
, _("\nyou use. If you are not running Alpine from a command shell which knows"));
2895 so_puts(store
, _("\nabout quoting, it is possible you will have to remove those quotes"));
2896 so_puts(store
, _("\nfrom the command. For example, if you also use PC-Alpine you will probably"));
2897 so_puts(store
, _("\nwant to create a shortcut, and you would not need the quotes there."));
2898 so_puts(store
, _("\nWithout the quotes, the command might look like"));
2899 so_puts(store
, "\n\n ");
2900 so_puts(store
, "alpine -p ");
2901 so_puts(store
, rem_pinerc
);
2902 so_puts(store
, "\n");
2903 so_puts(store
, _("\nConsider creating an alias or shell script to execute this command to make"));
2904 so_puts(store
, _("\nit more convenient."));
2906 so_puts(store
, _("\n\nIf you want to use your new remote configuration for this session, quit"));
2907 so_puts(store
, _("\nAlpine now and restart with the changed command line options mentioned above.\n"));
2909 memset(&sargs
, 0, sizeof(SCROLL_S
));
2910 sargs
.text
.text
= so_text(store
);
2911 sargs
.text
.src
= CharStar
;
2912 sargs
.text
.desc
= _("Remote Config Information");
2913 /* TRANSLATORS: a screen title */
2914 sargs
.bar
.title
= _("ABOUT REMOTE CONFIG");
2915 sargs
.help
.text
= NO_HELP
;
2916 sargs
.help
.title
= NULL
;
2920 so_give(&store
); /* free resources associated with store */
2921 ps
->mangled_screen
= 1;
2927 verify_folder_name(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2931 tmp
= cpystr(given
? given
: "");
2932 removing_leading_and_trailing_white_space(tmp
);
2937 fs_give((void **)&tmp
);
2940 *error
= cpystr("");
2947 verify_server_name(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2951 tmp
= cpystr(given
? given
: "");
2952 removing_leading_and_trailing_white_space(tmp
);
2956 * could try to verify the hostname here
2963 fs_give((void **)&tmp
);
2966 *error
= cpystr("");
2973 verify_abook_nick(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2978 tmp
= cpystr(given
? given
: "");
2979 removing_leading_and_trailing_white_space(tmp
);
2981 if(strindex(tmp
, '"')){
2982 fs_give((void **)&tmp
);
2984 /* TRANSLATORS: Double quote refers to the " character */
2985 *error
= cpystr(_("Double quote not allowed in nickname"));
2990 for(i
= 0; i
< as
.n_addrbk
; i
++)
2991 if(i
!= the_one_were_editing
&& !strcmp(tmp
, as
.adrbks
[i
].abnick
))
2994 if(i
< as
.n_addrbk
){
2995 fs_give((void **)&tmp
);
2998 *error
= cpystr(_("Nickname is already being used"));
3006 fs_give((void **)&tmp
);
3009 *error
= cpystr("");
3016 * Delete an addressbook.
3018 * Args: cur_line -- The current line position (in global display list)
3020 * command_line -- The screen line on which to prompt
3021 * err -- Points to error message
3023 * Returns -- 0, deleted addrbook
3024 * -1, addrbook not deleted
3027 ab_del_abook(long int cur_line
, int command_line
, char **err
)
3029 int abook_num
, varnum
, delete_data
= 0,
3030 num_in_list
, how_many_in_list
= 0, i
, cnt
, warn_about_revert
= 0;
3031 char **list
, **new_list
, **t
, **lval
;
3034 struct variable
*vars
= ps_global
->vars
;
3040 DontChange
} modify_config
;
3042 /* restrict address book config to normal config file */
3045 if(ps_global
->readonly_pinerc
){
3047 *err
= _("Delete cancelled: config file not changeable");
3052 abook_num
= adrbk_num_from_lineno(cur_line
);
3054 pab
= &as
.adrbks
[abook_num
];
3056 dprint((2, "- ab_del_abook(%s) -\n",
3057 pab
->abnick
? pab
->abnick
: "?"));
3059 varnum
= (pab
->type
& GLOBAL
) ? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
;
3061 if(vars
[varnum
].is_fixed
){
3063 if(pab
->type
& GLOBAL
)
3065 _("Cancelled: Sys. Mgmt. does not allow changing global address book config");
3068 _("Cancelled: Sys. Mgmt. does not allow changing address book config");
3075 * Deal with reverting to default values of the address book
3076 * variables, or with user deleting a default value.
3078 modify_config
= NotSet
;
3080 /* First count how many address books are in the user's config. */
3082 lval
= LVAL(&vars
[varnum
], ew
);
3084 for(t
= lval
; *t
!= NULL
; t
++)
3088 * Easy case, we can just delete one from the user's list.
3091 modify_config
= Modify
;
3094 * Also easy. We'll revert to the default if it exists, and warn
3095 * the user about that.
3098 modify_config
= RevertToDefault
;
3099 /* see if there's a default to revert to */
3101 if(vars
[varnum
].global_val
.l
&& vars
[varnum
].global_val
.l
[0])
3102 for(t
= vars
[varnum
].global_val
.l
; *t
!= NULL
; t
++)
3105 warn_about_revert
= cnt
;
3108 * User is already using the default. Split it into two cases. If there
3109 * is one address book in default ask user if they want to delete that
3110 * default from their config. If there is more than one, ask them if
3111 * they want to ignore all the defaults or just delete this one.
3114 /* count how many in default */
3116 if(vars
[varnum
].global_val
.l
&& vars
[varnum
].global_val
.l
[0])
3117 for(t
= vars
[varnum
].global_val
.l
; *t
!= NULL
; t
++)
3121 static ESCKEY_S opts
[] = {
3122 /* TRANSLATORS: Ignore All means ignore all of the default values,
3123 and Remove One means just remove this one default value. */
3124 {'i', 'i', "I", N_("Ignore All")},
3125 {'r', 'r', "R", N_("Remove One")},
3126 {-1, 0, NULL
, NULL
}};
3128 snprintf(tmp
, sizeof(tmp
),
3129 /* TRANSLATORS: %s is an adjective modifying address books */
3130 _("Ignore all default %s address books or just remove this one ? "),
3131 /* TRANSLATORS: global or personal address books */
3132 pab
->type
& GLOBAL
? _("global") : _("personal"));
3133 tmp
[sizeof(tmp
)-1] = '\0';
3134 switch(radio_buttons(tmp
, command_line
, opts
, 'i', 'x',
3135 h_ab_del_ignore
, RB_NORM
)){
3137 modify_config
= OverRideDefault
;
3141 modify_config
= Modify
;
3146 *err
= _("Delete cancelled");
3152 /* TRANSLATORS: a question */
3153 switch(want_to(_("Delete this default address book from config "),
3154 'n', 'x', h_ab_del_default
, WT_NORM
)){
3158 *err
= _("Delete cancelled");
3163 modify_config
= OverRideDefault
;
3170 * ReadWrite means it exists and MaybeRorW means it is remote and we
3171 * haven't selected it yet to know our access permissions. The remote
3172 * folder should have been created, though, unless we didn't even have
3173 * permissions for that, in which case we got some error messages earlier.
3175 if(pab
->access
== ReadWrite
|| pab
->access
== MaybeRorW
){
3176 static ESCKEY_S o
[] = {
3177 /* TRANSLATORS: user is asked whether to remove just data files, just configuration,
3178 or both for an address book. */
3179 {'d', 'd', "D", N_("Data")},
3180 {'c', 'c', "C", N_("Config")},
3181 {'b', 'b', "B", N_("Both")},
3182 {-1, 0, NULL
, NULL
}};
3184 switch(radio_buttons(_("Delete data, config, or both ? "),
3185 command_line
, o
, 'c', 'x',
3186 (modify_config
== RevertToDefault
)
3187 ? h_ab_del_data_revert
3188 : h_ab_del_data_modify
,
3190 case 'b': /* Delete Both */
3194 case 'd': /* Delete only Data */
3195 modify_config
= DontChange
;
3199 case 'c': /* Delete only Config */
3202 case 'x': /* Cancel */
3205 *err
= _("Delete cancelled");
3212 * Deleting config for address book which doesn't yet exist (hasn't
3213 * ever been opened).
3215 /* TRANSLATORS: a question */
3216 switch(want_to(_("Delete configuration for highlighted addressbook "),
3218 (modify_config
== RevertToDefault
)
3219 ? h_ab_del_config_revert
3220 : h_ab_del_config_modify
,
3226 *err
= _("Delete cancelled");
3238 dprint((5, "deleting addrbook data\n"));
3242 * In order to delete the address book it is easiest if we open
3243 * it first. That fills in the filenames we want to delete.
3245 if(pab
->address_book
== NULL
){
3246 warning
[300] = '\0';
3247 pab
->address_book
= adrbk_open(pab
, ps_global
->home_dir
,
3248 &warning
[300], sizeof(warning
)-300,
3251 * Couldn't get it open.
3253 if(pab
->address_book
== NULL
){
3255 /* TRANSLATORS: %s is an error message */
3256 snprintf(warning
, 300, _("Can't delete data: %s"), &warning
[300]);
3258 strncpy(warning
, _("Can't delete address book data"), 100);
3263 * If we have it open, set the delete bits and close to get the
3264 * local copies. Delete the remote folder by hand.
3266 if(pab
->address_book
){
3267 char *file
, *origfile
= NULL
;
3271 * We're about to destroy addrbook data, better ask again.
3273 if(pab
->address_book
->count
> 0){
3276 /* TRANSLATORS: a question */
3277 snprintf(prompt
, sizeof(prompt
),
3278 _("About to delete the contents of address book (%ld entries), really delete "), (long) adrbk_count(pab
->address_book
));
3279 prompt
[sizeof(prompt
)-1] = '\0';
3281 switch(want_to(prompt
, 'n', 'n', h_ab_really_delete
, WT_NORM
)){
3288 *err
= _("Delete cancelled");
3294 pab
->address_book
->flags
|= DEL_FILE
;
3295 file
= cpystr(pab
->address_book
->filename
);
3296 if(pab
->type
& REMOTE_VIA_IMAP
)
3297 origfile
= cpystr(pab
->address_book
->orig_filename
);
3300 * In order to avoid locking problems when we delete the
3301 * remote folder, we need to actually close the remote stream
3302 * instead of just putting it back in the stream pool.
3303 * So we will remove this stream from the re-usable portion
3304 * of the stream pool by clearing the SP_USEPOOL flag.
3305 * Init_abook(pab, TotallyClosed) via rd_close_remdata is
3306 * going to pine_mail_close it.
3308 if(pab
->type
& REMOTE_VIA_IMAP
3309 && pab
->address_book
3310 && pab
->address_book
->type
== Imap
3311 && pab
->address_book
->rd
3312 && rd_stream_exists(pab
->address_book
->rd
)){
3314 sp_unflag(pab
->address_book
->rd
->t
.i
.stream
, SP_USEPOOL
);
3317 /* This deletes the files because of DEL_ bits we set above. */
3318 init_abook(pab
, TotallyClosed
);
3321 * Delete the remote folder.
3323 if(pab
->type
& REMOTE_VIA_IMAP
){
3327 ps_global
->c_client_error
[0] = '\0';
3328 if(!pine_mail_delete(NULL
, origfile
) &&
3329 ps_global
->c_client_error
[0] != '\0'){
3330 dprint((1, "%s: %s\n", origfile
? origfile
: "?",
3331 ps_global
->c_client_error
));
3334 /* delete line from metadata */
3335 rd
= rd_new_remdata(RemImap
, origfile
, NULL
);
3336 rd_write_metadata(rd
, 1);
3337 rd_close_remdata(&rd
);
3339 /* Check to see if it's still there */
3340 if((exists
=folder_exists(NULL
, origfile
)) &&
3341 (exists
!= FEX_ERROR
)){
3343 dprint((1, "Trouble deleting %s\n",
3344 origfile
? origfile
: "?"));
3348 if(can_access(file
, ACCESS_EXISTS
) == 0){
3350 dprint((1, "Trouble deleting %s\n",
3351 file
? file
: "?"));
3355 snprintf(warning
, sizeof(warning
), _("Trouble deleting data %s%s%s%s"),
3357 (f
&& o
) ? (o
? ", " : " and ") : "",
3360 warning
[sizeof(warning
)-1] = '\0';
3363 fs_give((void **) &file
);
3365 fs_give((void **) &origfile
);
3369 q_status_message(SM_ORDER
, 3, 3, warning
);
3370 dprint((1, "%s\n", warning
));
3371 display_message(NO_OP_COMMAND
);
3373 else if(modify_config
== DontChange
)
3374 q_status_message(SM_ORDER
, 0, 1, _("Addressbook data deleted"));
3377 if(modify_config
== DontChange
){
3379 * We return -1 to indicate that the addrbook wasn't deleted (as far
3380 * as we're concerned) but we don't fill in err so that no error
3381 * message will be printed.
3382 * Since the addrbook is still an addrbook we need to reinitialize it.
3384 pab
->access
= adrbk_access(pab
);
3385 if(pab
->type
& GLOBAL
&& pab
->access
!= NoAccess
)
3386 pab
->access
= ReadOnly
;
3388 init_abook(pab
, HalfOpen
);
3391 else if(modify_config
== Modify
){
3392 list
= vars
[varnum
].current_val
.l
;
3393 if(pab
->type
& GLOBAL
){
3394 how_many_in_list
= as
.n_addrbk
- as
.how_many_personals
- 1;
3395 num_in_list
= abook_num
- as
.how_many_personals
;
3398 how_many_in_list
= as
.how_many_personals
- 1;
3399 num_in_list
= abook_num
;
3402 else if(modify_config
== OverRideDefault
)
3403 how_many_in_list
= 1;
3404 else if(modify_config
== RevertToDefault
)
3405 how_many_in_list
= 0;
3407 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_del_abook");
3409 /* allocate for new list */
3410 if(how_many_in_list
)
3411 new_list
= (char **)fs_get((how_many_in_list
+ 1) * sizeof(char *));
3416 * This case is both for modifying the users user_val and for the
3417 * case where the user wants to modify the global_val default and
3418 * use the modified version for his or her new user_val. We just
3419 * copy from the existing global_val, deleting the one addrbook
3420 * and put the result in user_val.
3422 if(modify_config
== Modify
){
3423 /* copy old list up to where we will delete entry */
3424 for(i
= 0; i
< num_in_list
; i
++)
3425 new_list
[i
] = cpystr(list
[i
]);
3427 /* copy rest of old list */
3428 for(; i
< how_many_in_list
; i
++)
3429 new_list
[i
] = cpystr(list
[i
+1]);
3433 else if(modify_config
== OverRideDefault
){
3434 new_list
[0] = cpystr("");
3438 /* this also frees old variable contents for us */
3439 if(set_variable_list(varnum
, new_list
, TRUE
, ew
)){
3441 *err
= _("Delete cancelled: couldn't save pine configuration file");
3443 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3444 free_list_array(&new_list
);
3449 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3451 if(warn_about_revert
){
3452 /* TRANSLATORS: the %s may be "global " or nothing */
3453 snprintf(tmp
, sizeof(tmp
), _("Reverting to default %saddress books"),
3454 pab
->type
& GLOBAL
? _("global ") : "");
3455 tmp
[sizeof(tmp
)-1] = '\0';
3456 q_status_message(SM_ORDER
, 3, 4, tmp
);
3459 free_list_array(&new_list
);
3466 * Shuffle addrbooks.
3468 * Args: pab -- Pab from current addrbook.
3469 * slide -- return value, tells how far to slide the cursor. If slide
3470 * is negative, slide it up, if positive, slide it down.
3471 * command_line -- The screen line on which to prompt
3472 * msg -- Points to returned message, if any. Should be freed by
3475 * Result: Two address books are swapped in the display order. If the shuffle
3476 * crosses the Personal/Global boundary, then instead of swapping
3477 * two address books the highlighted abook is moved from one section
3480 * < 0 on failure, no changes.
3481 * > 0 If the return value is greater than zero it means that we've
3482 * reverted one of the variables to its default value. That
3483 * means we've added at least one new addrbook, so the caller
3484 * should reset. The value returned is the number of the
3485 * moved addrbook + 1 (+1 so it won't be confused with zero).
3486 * = 0 If the return value is zero we've just moved addrbooks around.
3487 * No reset need be done.
3490 ab_shuffle(PerAddrBook
*pab
, int *slide
, int command_line
, char **msg
)
3494 int i
, deefault
, rv
, target
= 0;
3495 int up_into_empty
= 0, down_into_empty
= 0;
3497 struct variable
*vars
= ps_global
->vars
;
3499 dprint((2, "- ab_shuffle() -\n"));
3503 if(ps_global
->readonly_pinerc
){
3505 *msg
= cpystr(_("Shuffle cancelled: config file not changeable"));
3510 /* Move it up or down? */
3515 /* TRANSLATORS: shuffle something Up or Down in a list */
3516 opts
[i
++].label
= N_("Up");
3521 opts
[i
++].label
= N_("Down");
3526 if(pab
->type
& GLOBAL
){
3527 if(vars
[V_GLOB_ADDRBOOK
].is_fixed
){
3529 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3543 if(as
.cur
== as
.n_addrbk
- 1) /* no down */
3547 if(vars
[V_ADDRESSBOOK
].is_fixed
){
3549 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book config"));
3554 if(as
.cur
== 0){ /* no up */
3559 if(as
.cur
== as
.n_addrbk
- 1){
3563 opts
[1].ch
= -2; /* no down */
3567 snprintf(tmp
, sizeof(tmp
), _("Shuffle \"%s\" %s%s%s ? "),
3569 (opts
[0].ch
!= -2) ? _("UP") : "",
3570 (opts
[0].ch
!= -2 && opts
[1].ch
!= -2) ? " or " : "",
3571 (opts
[1].ch
!= -2) ? _("DOWN") : "");
3572 tmp
[sizeof(tmp
)-1] = '\0';
3573 help
= (opts
[0].ch
== -2) ? h_ab_shuf_down
3574 : (opts
[1].ch
== -2) ? h_ab_shuf_up
3577 rv
= radio_buttons(tmp
, command_line
, opts
, deefault
, 'x',
3580 ps_global
->mangled_footer
= 1;
3582 if((rv
== 'u' && up_into_empty
) || (rv
== 'd' && down_into_empty
))
3585 target
= as
.cur
+ (rv
== 'u' ? -1 : 1);
3589 *msg
= cpystr(_("Shuffle cancelled"));
3594 return(do_the_shuffle(slide
, as
.cur
, target
, msg
));
3599 * Actually shuffle the config variables and address books structures around.
3601 * Args: anum1, anum2 -- The numbers of the address books
3602 * msg -- Points to returned message, if any.
3604 * Returns: >= 0 on success.
3605 * < 0 on failure, no changes.
3606 * > 0 If the return value is greater than zero it means that we've
3607 * reverted one of the variables to its default value. That
3608 * means we've added at least one new addrbook, so the caller
3609 * should reset. The value returned is the number of the
3610 * moved addrbook + 1 (+1 so it won't be confused with zero).
3611 * = 0 If the return value is zero we've just moved addrbooks around.
3612 * No reset need be done.
3614 * Anum1 is the one that we want to move, anum2 is the one that it will be
3615 * swapped with. When anum1 and anum2 are on the opposite sides of the
3616 * Personal/Global boundary then instead of swapping we just move anum1 to
3617 * the other side of the boundary.
3619 * Anum2 of -1 means it is a swap into the other type of address book, which
3620 * is currently empty.
3623 do_the_shuffle(int *slide
, int anum1
, int anum2
, char **msg
)
3626 enum {NotSet
, Pers
, Glob
, Empty
} type1
, type2
;
3627 int i
, j
, retval
= -1;
3628 struct variable
*vars
= ps_global
->vars
;
3631 char *cancel_msg
= _("Shuffle cancelled: couldn't save configuration file");
3633 dprint((5, "- do_the_shuffle(%d, %d) -\n", anum1
, anum2
));
3635 /* restrict address book config to normal config file */
3641 pab
= &as
.adrbks
[anum1
];
3642 type1
= (pab
->type
& GLOBAL
) ? Glob
: Pers
;
3648 cpystr(_("Shuffle cancelled: highlight entry you wish to shuffle"));
3656 pab
= &as
.adrbks
[anum2
];
3657 type2
= (pab
->type
& GLOBAL
) ? Glob
: Pers
;
3661 type2
= (type1
== Pers
) ? Glob
: Pers
;
3663 if((type1
== Pers
|| type2
== Pers
) && vars
[V_ADDRESSBOOK
].is_fixed
){
3665 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book configuration"));
3670 if((type1
== Glob
|| type2
== Glob
) && vars
[V_GLOB_ADDRBOOK
].is_fixed
){
3672 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3678 * There are two cases. If the shuffle is two address books within the
3679 * same variable, then they just swap places. If it is a shuffle of an
3680 * addrbook from one side of the boundary to the other, just that one
3683 if((type1
== Glob
&& type2
== Glob
) ||
3684 (type1
== Pers
&& type2
== Pers
)){
3685 int how_many_in_list
, varnum
;
3686 int anum1_rel
, anum2_rel
; /* position in specific list */
3687 char **list
, **new_list
;
3690 *slide
= (anum1
< anum2
) ? LINES_PER_ABOOK
: -1 * LINES_PER_ABOOK
;
3693 how_many_in_list
= as
.how_many_personals
;
3694 list
= VAR_ADDRESSBOOK
;
3695 varnum
= V_ADDRESSBOOK
;
3700 how_many_in_list
= as
.n_addrbk
- as
.how_many_personals
;
3701 list
= VAR_GLOB_ADDRBOOK
;
3702 varnum
= V_GLOB_ADDRBOOK
;
3703 anum1_rel
= anum1
- as
.how_many_personals
;
3704 anum2_rel
= anum2
- as
.how_many_personals
;
3707 /* allocate for new list, same size as old list */
3708 new_list
= (char **)fs_get((how_many_in_list
+ 1) * sizeof(char *));
3710 /* fill in new_list */
3711 for(i
= 0; i
< how_many_in_list
; i
++){
3712 /* swap anum1 and anum2 */
3715 else if(i
== anum2_rel
)
3720 new_list
[i
] = cpystr(list
[j
]);
3725 if(set_variable_list(varnum
, new_list
, TRUE
, ew
)){
3727 *msg
= cpystr(cancel_msg
);
3729 /* restore old values */
3730 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3731 free_list_array(&new_list
);
3736 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3737 free_list_array(&new_list
);
3739 /* Swap PerAddrBook structs */
3740 tmppab
= as
.adrbks
[anum1
];
3741 as
.adrbks
[anum1
] = as
.adrbks
[anum2
];
3742 as
.adrbks
[anum2
] = tmppab
;
3744 else if((type1
== Pers
&& type2
== Glob
) ||
3745 (type1
== Glob
&& type2
== Pers
)){
3746 int how_many_in_srclist
, how_many_in_dstlist
;
3747 int srcvarnum
, dstvarnum
, srcanum
;
3748 int cnt
, warn_about_revert
= 0;
3750 char **new_src
, **new_dst
, **srclist
, **dstlist
;
3752 enum {NotSet
, Modify
, RevertToDefault
, OverRideDefault
} modify_config
;
3755 * how_many_in_srclist = # in orig src list (Pers or Glob list).
3756 * how_many_in_dstlist = # in orig dst list
3757 * srcanum = # of highlighted addrbook that is being shuffled
3760 how_many_in_srclist
= as
.how_many_personals
;
3761 how_many_in_dstlist
= as
.n_addrbk
- as
.how_many_personals
;
3762 srclist
= VAR_ADDRESSBOOK
;
3763 dstlist
= VAR_GLOB_ADDRBOOK
;
3764 srcvarnum
= V_ADDRESSBOOK
;
3765 dstvarnum
= V_GLOB_ADDRBOOK
;
3766 srcanum
= as
.how_many_personals
- 1;
3767 *slide
= (how_many_in_srclist
== 1)
3768 ? (LINES_PER_ADD_LINE
+ XTRA_LINES_BETWEEN
)
3769 : XTRA_LINES_BETWEEN
;
3772 how_many_in_srclist
= as
.n_addrbk
- as
.how_many_personals
;
3773 how_many_in_dstlist
= as
.how_many_personals
;
3774 srclist
= VAR_GLOB_ADDRBOOK
;
3775 dstlist
= VAR_ADDRESSBOOK
;
3776 srcvarnum
= V_GLOB_ADDRBOOK
;
3777 dstvarnum
= V_ADDRESSBOOK
;
3778 srcanum
= as
.how_many_personals
;
3779 *slide
= (how_many_in_dstlist
== 0)
3780 ? (LINES_PER_ADD_LINE
+ XTRA_LINES_BETWEEN
)
3781 : XTRA_LINES_BETWEEN
;
3782 *slide
= -1 * (*slide
);
3786 modify_config
= Modify
;
3787 if(how_many_in_srclist
== 1){
3789 * Deal with reverting to default values of the address book
3790 * variables, or with user deleting a default value.
3792 modify_config
= NotSet
;
3795 * Count how many address books are in the user's config.
3796 * This has to be one or zero, because how_many_in_srclist == 1.
3799 lval
= LVAL(&vars
[srcvarnum
], ew
);
3801 for(t
= lval
; *t
!= NULL
; t
++)
3805 * We'll revert to the default if it exists, and warn
3806 * the user about that.
3809 modify_config
= RevertToDefault
;
3810 /* see if there's a default to revert to */
3812 if(vars
[srcvarnum
].global_val
.l
&&
3813 vars
[srcvarnum
].global_val
.l
[0])
3814 for(t
= vars
[srcvarnum
].global_val
.l
; *t
!= NULL
; t
++)
3817 warn_about_revert
= cnt
;
3818 if(warn_about_revert
> 1 && type1
== Pers
)
3819 *slide
= LINES_PER_ABOOK
* warn_about_revert
+
3823 * User is already using the default.
3826 modify_config
= OverRideDefault
;
3831 * We're adding one to the dstlist, so need how_many + 1 + 1.
3833 new_dst
= (char **)fs_get((how_many_in_dstlist
+ 2) * sizeof(char *));
3837 * Because the Personal list comes before the Global list, when
3838 * we move to Global we're inserting a new first element into
3839 * the global list (the dstlist).
3841 * When we move from Global to Personal, we're appending a new
3842 * last element onto the personal list (the dstlist).
3845 new_dst
[j
++] = cpystr(srclist
[how_many_in_srclist
-1]);
3847 for(i
= 0; i
< how_many_in_dstlist
; i
++)
3848 new_dst
[j
++] = cpystr(dstlist
[i
]);
3851 new_dst
[j
++] = cpystr(srclist
[0]);
3856 * The srclist is complicated by the reverting to default
3859 if(modify_config
== Modify
){
3861 * In this case we're just removing one from the srclist
3862 * so the new_src is of size how_many -1 +1.
3864 new_src
= (char **)fs_get((how_many_in_srclist
) * sizeof(char *));
3867 for(i
= 0; i
< how_many_in_srclist
-1; i
++)
3868 new_src
[j
++] = cpystr(srclist
[i
+ ((type1
== Glob
) ? 1 : 0)]);
3872 else if(modify_config
== OverRideDefault
){
3874 * We were using default and will now revert to nothing.
3876 new_src
= (char **)fs_get(2 * sizeof(char *));
3877 new_src
[0] = cpystr("");
3880 else if(modify_config
== RevertToDefault
){
3882 * We are moving our last user variable out and reverting
3883 * to the default value for this variable.
3888 if(set_variable_list(dstvarnum
, new_dst
, TRUE
, ew
) ||
3889 set_variable_list(srcvarnum
, new_src
, TRUE
, ew
)){
3891 *msg
= cpystr(cancel_msg
);
3893 /* restore old values */
3894 set_current_val(&vars
[dstvarnum
], TRUE
, FALSE
);
3895 set_current_val(&vars
[srcvarnum
], TRUE
, FALSE
);
3896 free_list_array(&new_dst
);
3897 free_list_array(&new_src
);
3901 set_current_val(&vars
[dstvarnum
], TRUE
, FALSE
);
3902 set_current_val(&vars
[srcvarnum
], TRUE
, FALSE
);
3903 free_list_array(&new_dst
);
3904 free_list_array(&new_src
);
3906 retval
= (type1
== Pers
&& warn_about_revert
)
3907 ? (warn_about_revert
+ 1) : (srcanum
+ 1);
3910 * This is a tough case. We're adding one or more new address books
3911 * in this case so we need to reset the addrbooks and start over.
3912 * We return the number of the address book we just moved after the
3913 * reset so that the caller can focus attention on the moved one.
3914 * Actually, we return 1+the number so that we can tell it apart
3915 * from a return of zero, which just means everything is ok.
3917 if(warn_about_revert
){
3918 snprintf(tmp
, sizeof(tmp
),
3919 "This address book now %s, reverting to default %s address %s",
3920 (type1
== Glob
) ? "Personal" : "Global",
3921 (type1
== Glob
) ? "Global" : "Personal",
3922 warn_about_revert
> 1 ? "books" : "book");
3923 tmp
[sizeof(tmp
)-1] = '\0';
3929 * Modify PerAddrBook struct and adjust boundary.
3930 * In this case we aren't swapping two addrbooks, but just modifying
3931 * one from being global to personal or the reverse. It will
3932 * still be the same element in the as.adrbks array.
3934 pab
= &as
.adrbks
[srcanum
];
3936 as
.how_many_personals
--;
3937 pab
->type
|= GLOBAL
;
3938 if(pab
->access
!= NoAccess
)
3939 pab
->access
= ReadOnly
;
3942 as
.how_many_personals
++;
3943 pab
->type
&= ~GLOBAL
;
3944 if(pab
->access
!= NoAccess
&& pab
->access
!= MaybeRorW
)
3945 pab
->access
= ReadWrite
;
3948 snprintf(tmp
, sizeof(tmp
),
3949 "This address book now %s",
3950 (type1
== Glob
) ? "Personal" : "Global");
3951 tmp
[sizeof(tmp
)-1] = '\0';
3962 ab_compose_to_addr(long int cur_line
, int agg
, int allow_role
)
3969 dprint((2, "- ab_compose_to_addr -\n"));
3974 bldto
.arg
.str
= NULL
;
3978 size_t incr
= 100, avail
, alloced
;
3981 to
= (char *)fs_get(incr
);
3987 * Run through all of the selected entries
3988 * in all of the address books.
3989 * Put the nicknames together into one long
3990 * string with comma separators.
3992 for(i
= 0; i
< as
.n_addrbk
; i
++){
3995 EXPANDED_S
*next_one
;
3997 pab
= &as
.adrbks
[i
];
3998 if(pab
->address_book
)
3999 next_one
= pab
->address_book
->selects
;
4003 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
4005 AddrScrn_Disp fake_dl
;
4007 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4010 * Since we're picking up address book entries
4011 * directly from the address books and have
4012 * no knowledge of the display lines they came
4013 * from, we don't know the dl's that go with
4014 * them. We need to pass a dl to abe_to_nick
4015 * but it really is only going to use the
4016 * type in this case.
4019 dl
->type
= (abe
->tag
== Single
) ? Simple
: ListHead
;
4020 a_string
= abe_to_nick_or_addr_string(abe
, dl
, i
);
4022 while(abe
&& avail
< (size_t)strlen(a_string
)+1){
4025 fs_resize((void **)&to
, alloced
);
4029 strncpy(to
, a_string
, alloced
);
4030 to
[alloced
-1] = '\0';
4033 strncat(to
, ",", alloced
-strlen(to
)-1);
4034 to
[alloced
-1] = '\0';
4035 strncat(to
, a_string
, alloced
-strlen(to
)-1);
4036 to
[alloced
-1] = '\0';
4039 avail
-= (strlen(a_string
) + 1);
4040 fs_give((void **)&a_string
);
4048 if(is_addr(cur_line
)){
4050 dl
= dlist(cur_line
);
4053 if(dl
->type
== ListEnt
){
4055 bldto
.arg
.str
= cpystr(listmem(cur_line
));
4059 bldto
.arg
.abe
= abe
;
4064 if(bldto
.type
== Str
&& bldto
.arg
.str
== NULL
)
4065 bldto
.arg
.str
= cpystr("");
4067 ab_compose_internal(bldto
, allow_role
);
4069 restore_state(&state
);
4071 if(bldto
.type
== Str
&& bldto
.arg
.str
)
4072 fs_give((void **)&bldto
.arg
.str
);
4075 * Window size may have changed in composer.
4076 * Pine_send will have reset the window size correctly,
4077 * but we still have to reset our address book data structures.
4080 ps_global
->mangled_screen
= 1;
4086 * Used by the two compose routines.
4089 ab_compose_internal(BuildTo bldto
, int allow_role
)
4092 char *addr
, *fcc
, *error
= NULL
;
4093 ACTION_S
*role
= NULL
;
4094 void (*prev_screen
)(struct pine
*) = ps_global
->prev_screen
,
4095 (*redraw
)(void) = ps_global
->redrawer
;
4098 ps_global
->redrawer
= NULL
;
4100 ps_global
->next_screen
= SCREEN_FUN_NULL
;
4105 good_addr
= (our_build_address(bldto
, &addr
, &error
, &fcc
, NULL
) >= 0);
4108 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4109 fs_give((void **)&error
);
4112 if(!good_addr
&& addr
&& *addr
)
4113 fs_give((void **)&addr
); /* relying on fs_give setting addr to NULL */
4117 if(role_select_screen(ps_global
, &role
, MC_COMPOSE
) < 0){
4118 cmd_cancelled("Composition");
4119 ps_global
->next_screen
= prev_screen
;
4120 ps_global
->redrawer
= redraw
;
4125 * If default role was selected (NULL) we need to make up a role which
4126 * won't do anything, but will cause compose_mail to think there's
4127 * already a role so that it won't try to confirm the default.
4130 role
= copy_action(role
);
4132 role
= (ACTION_S
*)fs_get(sizeof(*role
));
4133 memset((void *)role
, 0, sizeof(*role
));
4134 role
->nick
= cpystr("Default Role");
4138 compose_mail(addr
, fcc
, role
, NULL
, NULL
);
4141 fs_give((void **)&addr
);
4144 fs_give((void **)&fcc
);
4149 * Export addresses into a file.
4151 * Args: cur_line -- The current line position (in global display list)
4153 * command_line -- The screen line on which to prompt
4155 * Returns -- 1 if the export is done
4159 ab_export(struct pine
*ps
, long int cur_line
, int command_line
, int agg
)
4161 int ret
= 0, i
, retflags
= GER_NONE
;
4162 int r
, orig_errno
= 0, failure
= 0;
4163 struct variable
*vars
= ps
->vars
;
4164 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1];
4167 long start_of_append
;
4168 char *addr
= NULL
, *error
= NULL
;
4171 int good_addr
, plur
, vcard
= 0, tab
= 0;
4172 static HISTORY_S
*history
= NULL
;
4174 VCARD_INFO_S
*vinfo
;
4175 static ESCKEY_S ab_export_opts
[] = {
4176 {ctrl('T'), 10, "^T", N_("To Files")},
4177 {-1, 0, NULL
, NULL
},
4178 {-1, 0, NULL
, NULL
}};
4179 static ESCKEY_S vcard_or_addresses
[] = {
4180 {'a', 'a', "A", N_("Address List")},
4181 {'v', 'v', "V", N_("VCard")},
4182 /* TRANSLATORS: TabSep is a command key label meaning Tab Separated List */
4183 {'t', 't', "T", N_("TabSep")},
4184 {-1, 0, NULL
, NULL
}};
4187 dprint((2, "- ab_export -\n"));
4190 q_status_message(SM_ORDER
, 0, 3,
4191 "Alpine demo can't export addresses to files");
4196 i
= radio_buttons(_("Export list of addresses, vCard format, or Tab Separated ? "),
4197 command_line
, vcard_or_addresses
, 'a', 'x',
4198 NO_HELP
, RB_NORM
|RB_RET_HELP
);
4200 /* TRANSLATORS: a screen title */
4201 helper(h_ab_export_vcard
, _("HELP FOR EXPORT FORMAT"),
4203 ps_global
->mangled_screen
= 1;
4211 q_status_message(SM_INFO
, 0, 2, _("Address book export cancelled"));
4226 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_export");
4234 plur
= (abe
&& abe
->tag
== List
);
4240 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
4241 ab_export_opts
[++r
].ch
= ctrl('I');
4242 ab_export_opts
[r
].rval
= 11;
4243 ab_export_opts
[r
].name
= "TAB";
4244 ab_export_opts
[r
].label
= N_("Complete");
4247 ab_export_opts
[++r
].ch
= -1;
4249 r
= get_export_filename(ps
, filename
, NULL
, full_filename
, sizeof(filename
),
4250 plur
? _("addresses") : _("address"),
4251 _("EXPORT"), ab_export_opts
,
4252 &retflags
, command_line
, GE_IS_EXPORT
, &history
);
4257 q_status_message(SM_INFO
, 0, 2, _("Address book export cancelled"));
4261 q_status_message1(SM_ORDER
, 0, 2,
4262 _("Can't export to file outside of %s"), VAR_OPER_DIR
);
4269 dprint((5, "Opening file \"%s\" for export\n",
4270 full_filename
? full_filename
: "?"));
4272 if(!(store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
))){
4273 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4274 _("Error opening file \"%s\" for address export: %s"),
4275 full_filename
, error_description(errno
));
4280 * The write_single_vcard_entry function wants a pc.
4283 gf_set_so_writec(&pc
, store
);
4285 start_of_append
= so_tell(store
);
4288 for(i
= 0; !failure
&& i
< as
.n_addrbk
; i
++){
4291 EXPANDED_S
*next_one
;
4293 pab
= &as
.adrbks
[i
];
4294 if(pab
->address_book
)
4295 next_one
= pab
->address_book
->selects
;
4299 while(!failure
&& (num
= entry_get_next(&next_one
)) != NO_NEXT
){
4301 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4302 if((vcard
|| tab
) && abe
){
4304 * There is no place to store the charset information
4305 * so we don't ask for it.
4307 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, 1)))
4311 write_single_vcard_entry(ps
, pc
, vinfo
);
4313 write_single_tab_entry(pc
, vinfo
);
4315 free_vcard_info(&vinfo
);
4320 bldto
.arg
.abe
= abe
;
4323 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4326 q_status_message1(SM_ORDER
, 0, 4, "%s", error
);
4327 fs_give((void **)&error
);
4330 /* rfc1522_decode the addr */
4334 len
= 4*strlen(addr
)+1;
4335 p
= (char *)fs_get(len
* sizeof(char));
4336 if(rfc1522_decode_to_utf8((unsigned char *)p
,len
,addr
) == (unsigned char *)p
){
4337 fs_give((void **)&addr
);
4341 fs_give((void **)&p
);
4348 * Change the unquoted commas into newlines.
4349 * Not worth it to do complicated quoting,
4350 * just consider double quotes.
4352 for(p
= addr
; *p
; p
++){
4355 else if(!quoted
&& *p
== ','){
4357 removing_leading_white_space(p
);
4362 if(!so_puts(store
, addr
) || !so_puts(store
, NEWLINE
)){
4369 fs_give((void **)&addr
);
4377 dl
= dlist(cur_line
);
4379 if((vcard
|| tab
) && abe
){
4380 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, 1)))
4384 write_single_vcard_entry(ps
, pc
, vinfo
);
4386 write_single_tab_entry(pc
, vinfo
);
4388 free_vcard_info(&vinfo
);
4393 if(dl
->type
== ListHead
&& listmem_count_from_abe(abe
) == 0){
4394 error
= _("List is empty, nothing to export!");
4397 else if(dl
->type
== ListEnt
){
4399 bldto
.arg
.str
= listmem(cur_line
);
4400 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4404 bldto
.arg
.abe
= abe
;
4405 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4409 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4410 fs_give((void **)&error
);
4413 /* Have to rfc1522_decode the addr */
4416 len
= 4*strlen(addr
)+1;
4417 p
= (char *)fs_get(len
* sizeof(char));
4418 if(rfc1522_decode_to_utf8((unsigned char *)p
,len
,addr
) == (unsigned char *)p
){
4419 fs_give((void **)&addr
);
4423 fs_give((void **)&p
);
4430 * Change the unquoted commas into newlines.
4431 * Not worth it to do complicated quoting,
4432 * just consider double quotes.
4434 for(p
= addr
; *p
; p
++){
4437 else if(!quoted
&& *p
== ','){
4439 removing_leading_white_space(p
);
4444 if(!so_puts(store
, addr
) || !so_puts(store
, NEWLINE
)){
4451 fs_give((void **)&addr
);
4456 gf_clear_so_writec(store
);
4458 if(so_give(&store
)) /* release storage */
4462 our_truncate(full_filename
, (off_t
)start_of_append
);
4463 dprint((1, "FAILED Export: file \"%s\" : %s\n",
4464 full_filename
? full_filename
: "?",
4465 error_description(orig_errno
)));
4466 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4467 _("Error exporting to \"%s\" : %s"),
4468 filename
, error_description(orig_errno
));
4472 q_status_message3(SM_ORDER
,0,3,
4473 "%s %s to file \"%s\"",
4474 (vcard
|| tab
) ? (agg
? "Entries" : "Entry")
4475 : (plur
? "Addresses" : "Address"),
4478 : retflags
& GER_APPEND
? "appended" : "exported",
4483 ps
->mangled_footer
= 1;
4489 * Forward an address book entry or entries via email attachment.
4491 * We use the vCard standard to send entries. We group multiple entries
4492 * using the BEGIN/END construct of vCard, not with multiple MIME parts.
4493 * A limitation of vCard is that there can be only one charset for the
4494 * whole group we send, so we might lose that information.
4496 * Args: cur_line -- The current line position (in global display list)
4498 * command_line -- The screen line on which to prompt
4501 ab_forward(struct pine
*ps
, long int cur_line
, int agg
)
4504 AdrBk_Entry
*abe
= NULL
;
4505 ENVELOPE
*outgoing
= NULL
;
4506 BODY
*pb
, *body
= NULL
;
4511 VCARD_INFO_S
*vinfo
;
4512 ACTION_S
*role
= NULL
;
4514 dprint((2, "- ab_forward -\n"));
4517 dl
= dlist(cur_line
);
4518 if(dl
->type
!= ListHead
&& dl
->type
!= Simple
)
4523 q_status_message(SM_ORDER
, 3, 3, _("Trouble accessing current entry"));
4528 outgoing
= mail_newenvelope();
4529 if(agg
&& as
.selections
> 1)
4530 outgoing
->subject
= cpystr("Forwarded address book entries from Alpine");
4532 outgoing
->subject
= cpystr("Forwarded address book entry from Alpine");
4534 body
= mail_newbody();
4535 body
->type
= TYPEMULTIPART
;
4536 /*---- The TEXT part/body ----*/
4537 body
->nested
.part
= mail_newbody_part();
4538 body
->nested
.part
->body
.type
= TYPETEXT
;
4539 /*--- Allocate an object for the body ---*/
4540 if((body
->nested
.part
->body
.contents
.text
.data
=
4541 (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
4543 long rflags
= ROLE_COMPOSE
;
4546 pp
= &(body
->nested
.part
->next
);
4548 if(nonempty_patterns(rflags
, &dummy
)){
4550 * This is really more like Compose, even though it
4551 * is called Forward.
4553 if(confirm_role(rflags
, &role
))
4554 role
= combine_inherited_role(role
);
4557 cmd_cancelled("Composition");
4563 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
4566 if((sig
= detoken(role
, NULL
, 2, 0, 1, NULL
, NULL
)) != NULL
){
4568 so_puts((STORE_S
*)body
->nested
.part
->body
.contents
.text
.data
,
4573 fs_give((void **)&sig
);
4576 /* so we don't have an empty part */
4578 so_puts((STORE_S
*)body
->nested
.part
->body
.contents
.text
.data
, "\n");
4581 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4582 _("Problem creating space for message text"));
4586 outgoing
->message_id
= generate_message_id(role
);
4588 /*---- create the attachment, and write abook entry into it ----*/
4589 *pp
= mail_newbody_part();
4590 pb
= &((*pp
)->body
);
4591 pb
->type
= TYPETEXT
;
4592 pb
->encoding
= ENCOTHER
; /* let data decide */
4593 pb
->id
= generate_message_id(role
);
4594 pb
->subtype
= cpystr("DIRECTORY");
4595 if(agg
&& as
.selections
> 1)
4596 pb
->description
= cpystr("Alpine addressbook entries");
4598 pb
->description
= cpystr("Alpine addressbook entry");
4600 pb
->parameter
= NULL
;
4601 set_parameter(&pb
->parameter
, "profile", "vCard");
4603 if((pb
->contents
.text
.data
= (void *)so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
4604 int are_some_unqualified
= 0, expand_nicks
= 0;
4607 EXPANDED_S
*next_one
;
4609 gf_set_so_writec(&pc
, (STORE_S
*) pb
->contents
.text
.data
);
4612 for(i
= 0; i
< as
.n_addrbk
&& !are_some_unqualified
; i
++){
4614 pab
= &as
.adrbks
[i
];
4615 if(pab
->address_book
)
4616 next_one
= pab
->address_book
->selects
;
4620 while((num
= entry_get_next(&next_one
)) != NO_NEXT
&&
4621 !are_some_unqualified
){
4623 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4624 if(abe
->tag
== Single
){
4625 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
4626 && !strindex(abe
->addr
.addr
, '@'))
4627 are_some_unqualified
++;
4632 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4633 if(!strindex(*ll
, '@')){
4634 are_some_unqualified
++;
4644 * Search through the addresses to see if there are any
4645 * that are unqualified, and so would be different if
4648 if(abe
->tag
== Single
){
4649 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
4650 && !strindex(abe
->addr
.addr
, '@'))
4651 are_some_unqualified
++;
4656 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4657 if(!strindex(*ll
, '@')){
4658 are_some_unqualified
++;
4665 if(are_some_unqualified
){
4666 switch(want_to(_("Expand nicknames"), 'y', 'x', h_ab_forward
,WT_NORM
)){
4668 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4669 q_status_message(SM_INFO
, 0, 2, _("Address book forward cancelled"));
4681 ps
->mangled_footer
= 1;
4685 for(i
= 0; i
< as
.n_addrbk
; i
++){
4687 pab
= &as
.adrbks
[i
];
4688 if(pab
->address_book
)
4689 next_one
= pab
->address_book
->selects
;
4693 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
4695 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4696 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, expand_nicks
))){
4697 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4701 write_single_vcard_entry(ps
, pc
, vinfo
);
4702 free_vcard_info(&vinfo
);
4708 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, expand_nicks
))){
4709 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4713 write_single_vcard_entry(ps
, pc
, vinfo
);
4714 free_vcard_info(&vinfo
);
4718 /* This sets parameter charset, if necessary, and encoding */
4719 set_mime_type_by_grope(pb
);
4720 set_charset_possibly_to_ascii(pb
, "UTF-8");
4722 strlen((char *)so_text((STORE_S
*)pb
->contents
.text
.data
));
4725 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4726 _("Problem creating space for message text"));
4730 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4732 pine_send(outgoing
, &body
, _("FORWARDING ADDRESS BOOK ENTRY"), role
, NULL
,
4733 NULL
, NULL
, NULL
, NULL
, 0);
4735 ps
->mangled_screen
= 1;
4740 mail_free_envelope(&outgoing
);
4743 pine_free_body(&body
);
4751 * Given an Adrbk_Entry fill in some of the fields in a VCARD_INFO_S
4752 * for use by write_single_vcard_entry. The returned structure is freed
4756 prepare_abe_for_vcard(struct pine
*ps
, AdrBk_Entry
*abe
, int expand_nicks
)
4758 VCARD_INFO_S
*vinfo
= NULL
;
4759 char *init_addr
= NULL
, *addr
= NULL
, *astring
;
4761 ADDRESS
*adrlist
= NULL
;
4766 vinfo
= (VCARD_INFO_S
*) fs_get(sizeof(*vinfo
));
4767 memset((void *) vinfo
, 0, sizeof(*vinfo
));
4769 if(abe
->nickname
&& abe
->nickname
[0]){
4770 vinfo
->nickname
= (char **) fs_get((1+1) * sizeof(char *));
4771 vinfo
->nickname
[0] = cpystr(abe
->nickname
);
4772 vinfo
->nickname
[1] = NULL
;
4775 if(abe
->fcc
&& abe
->fcc
[0]){
4776 vinfo
->fcc
= (char **) fs_get((1+1) * sizeof(char *));
4777 vinfo
->fcc
[0] = cpystr(abe
->fcc
);
4778 vinfo
->fcc
[1] = NULL
;
4781 if(abe
->extra
&& abe
->extra
[0]){
4782 vinfo
->note
= (char **) fs_get((1+1) * sizeof(char *));
4783 vinfo
->note
[0] = cpystr(abe
->extra
);
4784 vinfo
->note
[1] = NULL
;
4787 if(abe
->fullname
&& abe
->fullname
[0]){
4788 char *fn
, *last
= NULL
, *middle
= NULL
, *first
= NULL
;
4790 fn
= adrbk_formatname(abe
->fullname
, &first
, &last
);
4793 vinfo
->fullname
= (char **)fs_get((1+1) * sizeof(char *));
4794 vinfo
->fullname
[0] = fn
;
4795 vinfo
->fullname
[1] = NULL
;
4798 fs_give((void **)&fn
);
4802 if(first
&& (middle
=strindex(first
, ' '))){
4804 middle
= skip_white_space(middle
);
4808 vinfo
->first
= first
;
4809 vinfo
->middle
= middle
? cpystr(middle
) : NULL
;
4815 fs_give((void **)&last
);
4817 fs_give((void **)&first
);
4820 /* expand nicknames and fully-qualify unqualified names */
4826 if(abe
->tag
== Single
)
4827 init_addr
= cpystr(abe
->addr
.addr
);
4833 /* figure out how large a string we need to allocate */
4835 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
4836 length
+= (strlen(*ll
) + 2);
4841 init_addr
= (char *)fs_get((size_t)(length
+1L) * sizeof(char));
4844 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4845 sstrncpy(&p
, *ll
, length
-(p
-init_addr
));
4847 sstrncpy(&p
, ", ", length
-(p
-init_addr
));
4850 init_addr
[length
] = '\0';
4854 bldto
.arg
.str
= init_addr
;
4855 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
4857 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4858 fs_give((void **)&error
);
4859 free_vcard_info(&vinfo
);
4864 rfc822_parse_adrlist(&adrlist
, addr
, ps
->maildomain
);
4866 for(cnt
= 0, a
= adrlist
; a
; a
= a
->next
)
4869 vinfo
->email
= (char **)fs_get((cnt
+1) * sizeof(char *));
4871 for(cnt
= 0, a
= adrlist
; a
; a
= a
->next
){
4876 next_addr
= a
->next
;
4879 bufp
= (char *) fs_get(len
* sizeof(char));
4880 astring
= addr_string(a
, bufp
, len
);
4881 a
->next
= next_addr
;
4882 vinfo
->email
[cnt
++] = cpystr(astring
? astring
: "");
4883 fs_give((void **)&bufp
);
4886 vinfo
->email
[cnt
] = NULL
;
4888 else{ /* don't expand or qualify */
4889 if(abe
->tag
== Single
){
4891 (abe
->addr
.addr
&& abe
->addr
.addr
[0]) ? abe
->addr
.addr
: "";
4892 vinfo
->email
= (char **)fs_get((1+1) * sizeof(char *));
4893 vinfo
->email
[0] = cpystr(astring
);
4894 vinfo
->email
[1] = NULL
;
4899 for(cnt
= 0, ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
4902 vinfo
->email
= (char **)fs_get((cnt
+1) * sizeof(char *));
4903 for(cnt
= 0, ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
4904 vinfo
->email
[cnt
++] = cpystr(*ll
);
4906 vinfo
->email
[cnt
] = NULL
;
4915 free_vcard_info(VCARD_INFO_S
**vinfo
)
4917 if(vinfo
&& *vinfo
){
4918 if((*vinfo
)->nickname
)
4919 free_list_array(&(*vinfo
)->nickname
);
4920 if((*vinfo
)->fullname
)
4921 free_list_array(&(*vinfo
)->fullname
);
4923 free_list_array(&(*vinfo
)->fcc
);
4925 free_list_array(&(*vinfo
)->note
);
4927 free_list_array(&(*vinfo
)->title
);
4929 free_list_array(&(*vinfo
)->tel
);
4931 free_list_array(&(*vinfo
)->email
);
4934 fs_give((void **)&(*vinfo
)->first
);
4935 if((*vinfo
)->middle
)
4936 fs_give((void **)&(*vinfo
)->middle
);
4938 fs_give((void **)&(*vinfo
)->last
);
4940 fs_give((void **)vinfo
);
4949 write_single_vcard_entry(struct pine
*ps
, gf_o_t pc
, VCARD_INFO_S
*vinfo
)
4951 char *decoded
, *tmp2
, *tmp
= NULL
, *hdr
;
4953 int i
, did_fn
= 0, did_n
= 0;
4961 #if defined(DOS) || defined(OS2)
4968 strncpy(eol
, "\r\n", sizeof(eol
));
4970 strncpy(eol
, "\n", sizeof(eol
));
4972 eol
[sizeof(eol
)-1] = '\0';
4974 gf_puts("BEGIN:VCARD", pc
);
4976 gf_puts("VERSION:3.0", pc
);
4979 for(i
= 0; i
< 7; i
++){
4982 ll
= vinfo
->nickname
;
4987 ll
= vinfo
->fullname
;
5017 alpine_panic("can't happen in write_single_vcard_entry");
5020 for(; ll
&& *ll
; ll
++){
5021 decoded
= (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
), SIZEOF_20KBUF
, *ll
);
5023 tmp
= vcard_escape(decoded
);
5025 if((tmp2
= fold(tmp
, FOLD_BY
, FOLD_BY
, hdr
, " ", FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5027 fs_give((void **)&tmp2
);
5032 fs_give((void **)&tmp
);
5037 if(vinfo
->last
&& vinfo
->last
[0]){
5040 pl
= vcard_escape(vinfo
->last
);
5041 pf
= (vinfo
->first
&& *vinfo
->first
) ? vcard_escape(vinfo
->first
)
5043 pm
= (vinfo
->middle
&& *vinfo
->middle
) ? vcard_escape(vinfo
->middle
)
5045 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s%s%s",
5046 (pl
&& *pl
) ? pl
: "",
5047 ((pf
&& *pf
) || (pm
&& *pm
)) ? ";" : "",
5048 (pf
&& *pf
) ? pf
: "",
5049 (pm
&& *pm
) ? ";" : "",
5050 (pm
&& *pm
) ? pm
: "");
5052 if((tmp2
= fold(tmp_20k_buf
, FOLD_BY
, FOLD_BY
, "N:", " ",
5053 FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5055 fs_give((void **)&tmp2
);
5060 fs_give((void **)&pl
);
5062 fs_give((void **)&pf
);
5064 fs_give((void **)&pm
);
5068 * These two types are required in draft-ietf-asid-mime-vcard-06, which
5069 * is April 98 and is in last call.
5071 if(!did_fn
|| !did_n
){
5073 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s%s%s",
5074 (vinfo
->first
&& *vinfo
->first
) ? vinfo
->first
: "",
5075 (vinfo
->first
&& *vinfo
->first
&&
5076 vinfo
->middle
&& *vinfo
->middle
) ? " " : "",
5077 (vinfo
->middle
&& *vinfo
->middle
) ? vinfo
->middle
: "",
5078 (((vinfo
->first
&& *vinfo
->first
) ||
5079 (vinfo
->middle
&& *vinfo
->middle
)) &&
5080 vinfo
->last
&& *vinfo
->last
) ? " " : "",
5081 (vinfo
->last
&& *vinfo
->last
) ? vinfo
->last
: "");
5083 tmp
= vcard_escape(tmp_20k_buf
);
5085 if((tmp2
= fold(tmp
, FOLD_BY
, FOLD_BY
, "FN:", " ",
5086 FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5088 fs_give((void **)&tmp2
);
5092 fs_give((void **)&tmp
);
5097 gf_puts("FN:<Unknown>", pc
);
5101 gf_puts("N:<Unknown>", pc
);
5106 gf_puts("END:VCARD", pc
);
5115 write_single_tab_entry(gf_o_t pc
, VCARD_INFO_S
*vinfo
)
5117 char *decoded
, *tmp
= NULL
;
5125 #if defined(DOS) || defined(OS2)
5131 for(i
= 0; i
< 4; i
++){
5134 ll
= vinfo
->nickname
;
5138 ll
= vinfo
->fullname
;
5150 alpine_panic("can't happen in write_single_tab_entry");
5156 for(first
= 1; ll
&& *ll
; ll
++){
5158 decoded
= (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
), SIZEOF_20KBUF
, *ll
);
5159 tmp
= vcard_escape(decoded
);
5161 if(i
== 2 && !first
)
5167 fs_give((void **)&tmp
);
5177 * for ab_save percent done
5179 static int total_to_copy
;
5180 static int copied_so_far
;
5182 percent_done_copying(void)
5184 return((copied_so_far
* 100) / total_to_copy
);
5188 cmp_action_list(const qsort_t
*a1
, const qsort_t
*a2
)
5190 ACTION_LIST_S
*x
= (ACTION_LIST_S
*)a1
;
5191 ACTION_LIST_S
*y
= (ACTION_LIST_S
*)a2
;
5193 if(x
->pab
!= y
->pab
)
5194 return((x
->pab
> y
->pab
) ? 1 : -1); /* order doesn't matter */
5197 * The only one that matters is when both x and y have dup lit.
5198 * For the others, just need to be consistent so sort will terminate.
5202 return((x
->num_in_dst
> y
->num_in_dst
) ? -1
5203 : (x
->num_in_dst
== y
->num_in_dst
) ? 0 : 1);
5210 return((x
->num
> y
->num
) ? -1 : (x
->num
== y
->num
) ? 0 : 1);
5215 * Copy a bunch of address book entries to a particular address book.
5217 * Args abook -- the current addrbook handle
5218 * cur_line -- the current line the cursor is on
5219 * command_line -- the line to prompt on
5220 * agg -- 1 if this is an aggregate copy
5222 * Returns 1 if successful, 0 if not
5225 ab_save(struct pine
*ps
, AdrBk
*abook
, long int cur_line
, int command_line
, int agg
)
5227 PerAddrBook
*pab_dst
, *pab
;
5228 SAVE_STATE_S state
; /* For saving state of addrbooks temporarily */
5230 int how_many_dups
= 0, how_many_to_copy
= 0, skip_dups
= 0;
5231 int how_many_no_action
= 0, ret
= 1;
5232 int err
= 0, need_write
= 0, we_cancel
= 0;
5233 int act_list_size
, special_case
= 0;
5234 adrbk_cntr_t num
, new_entry_num
;
5235 char warn
[2][MAX_NICKNAME
+1];
5236 char warning
[MAX_NICKNAME
+1];
5237 char tmp
[MAX(200,2*MAX_NICKNAME
+80)];
5238 ACTION_LIST_S
*action_list
= NULL
, *al
;
5239 static ESCKEY_S save_or_export
[] = {
5240 {'s', 's', "S", N_("Save")},
5241 {'e', 'e', "E", N_("Export")},
5242 {-1, 0, NULL
, NULL
}};
5245 snprintf(tmp
, sizeof(tmp
), _("Save highlighted entry to address book or Export to filesystem ? "));
5246 else if(as
.selections
> 1)
5247 snprintf(tmp
, sizeof(tmp
), _("Save selected entries to address book or Export to filesystem ? "));
5248 else if(as
.selections
== 1)
5249 snprintf(tmp
, sizeof(tmp
), _("Save selected entry to address book or Export to filesystem ? "));
5251 snprintf(tmp
, sizeof(tmp
), _("Save to address book or Export to filesystem ? "));
5253 i
= radio_buttons(tmp
, -FOOTER_ROWS(ps
), save_or_export
, 's', 'x',
5254 h_ab_save_exp
, RB_NORM
);
5257 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
5261 return(ab_export(ps
, cur_line
, command_line
, agg
));
5267 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_save");
5271 pab_dst
= setup_for_addrbook_add(&state
, command_line
, _("Save"));
5275 pab
= &as
.adrbks
[as
.cur
];
5277 dprint((2, "- ab_save: %s -> %s (agg=%d)-\n",
5278 pab
->abnick
? pab
->abnick
: "?",
5279 pab_dst
->abnick
? pab_dst
->abnick
: "?", agg
));
5282 act_list_size
= as
.selections
;
5286 action_list
= (ACTION_LIST_S
*)fs_get((act_list_size
+1) *
5287 sizeof(ACTION_LIST_S
));
5288 memset((void *)action_list
, 0, (act_list_size
+1) * sizeof(ACTION_LIST_S
));
5293 for(i
= 0; i
< as
.n_addrbk
; i
++){
5294 EXPANDED_S
*next_one
;
5296 pab
= &as
.adrbks
[i
];
5297 if(pab
->address_book
)
5298 next_one
= pab
->address_book
->selects
;
5302 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
5303 if(pab
!= pab_dst
&&
5304 pab
->ostatus
!= Open
&&
5305 pab
->ostatus
!= NoDisplay
)
5306 init_abook(pab
, NoDisplay
);
5308 if(pab
->ostatus
!= Open
&& pab
->ostatus
!= NoDisplay
){
5309 q_status_message1(SM_ORDER
, 0, 4,
5310 _("Can't re-open address book %s to save from"),
5316 set_act_list_member(al
, (a_c_arg_t
)num
, pab_dst
, pab
, warning
);
5318 how_many_no_action
++;
5321 if(how_many_dups
< 2 && warning
[0]){
5322 strncpy(warn
[how_many_dups
], warning
, MAX_NICKNAME
);
5323 warn
[how_many_dups
][MAX_NICKNAME
] = '\0';
5337 if(is_addr(cur_line
)){
5340 dl
= dlist(cur_line
);
5342 if(dl
->type
== ListEnt
)
5347 set_act_list_member(al
, (a_c_arg_t
)num
, pab_dst
, pab
, warning
);
5353 how_many_no_action
++;
5356 if(how_many_dups
< 2 && warning
[0]){
5357 strncpy(warn
[how_many_dups
], warning
, MAX_NICKNAME
);
5358 warn
[how_many_dups
][MAX_NICKNAME
] = '\0';
5368 q_status_message(SM_ORDER
, 0, 4, _("No current entry to save"));
5373 if(how_many_to_copy
== 0 && how_many_no_action
== 1 && act_list_size
== 1)
5377 TA_STATE_S tas
, *tasp
;
5379 /* Not going to use the action_list now */
5381 fs_give((void **)&action_list
);
5386 take_this_one_entry(ps
, &tasp
, abook
, cur_line
);
5389 * If take_this_one_entry or its children didn't do this for
5390 * us, we do it here.
5393 restore_state(&(tas
.state
));
5396 * We don't have enough information to know what to return.
5401 /* nothing to do (except for special Take case below) */
5402 if(how_many_to_copy
== 0){
5403 if(how_many_no_action
== 0){
5408 restore_state(&state
);
5410 if(how_many_no_action
> 1)
5411 snprintf(tmp
, sizeof(tmp
), _("Saved %d entries to %s"), how_many_no_action
, pab_dst
->abnick
);
5413 snprintf(tmp
, sizeof(tmp
), _("Saved %d entry to %s"), how_many_no_action
, pab_dst
->abnick
);
5415 tmp
[sizeof(tmp
)-1] = '\0';
5416 q_status_message(SM_ORDER
, 0, 4, tmp
);
5418 fs_give((void **)&action_list
);
5425 * If there are some nicknames which already exist in the selected
5426 * abook, ask user what to do.
5428 if(how_many_dups
> 0){
5429 if(how_many_dups
== 1)
5430 snprintf(tmp
, sizeof(tmp
), _("Entry with nickname \"%.*s\" already exists, replace "),
5431 MAX_NICKNAME
, warn
[0]);
5432 else if(how_many_dups
== 2)
5433 snprintf(tmp
, sizeof(tmp
),
5434 _("Nicknames \"%.*s\" and \"%.*s\" already exist, replace "),
5435 MAX_NICKNAME
, warn
[0], MAX_NICKNAME
, warn
[1]);
5437 snprintf(tmp
, sizeof(tmp
), _("%d of the nicknames already exist, replace "),
5440 tmp
[sizeof(tmp
)-1] = '\0';
5442 switch(want_to(tmp
, 'n', 'x', h_ab_copy_dups
, WT_NORM
)){
5445 if(how_many_to_copy
== how_many_dups
){
5446 restore_state(&state
);
5448 fs_give((void **)&action_list
);
5450 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
5466 * Because the deletes happen immediately we have to delete from high
5467 * entry number towards lower entry numbers so that we are deleting
5468 * the correct entries. In order to do that we'll sort the action_list
5469 * to give us a safe order.
5471 if(!skip_dups
&& how_many_dups
> 1)
5472 qsort((qsort_t
*)action_list
, (size_t)as
.selections
, sizeof(*action_list
),
5476 * Set up the busy alarm percent counters.
5478 total_to_copy
= how_many_to_copy
- (skip_dups
? how_many_dups
: 0);
5480 we_cancel
= busy_cue(_("Saving entries"),
5481 (total_to_copy
> 4) ? percent_done_copying
: NULL
, 0);
5484 * Add the list of entries to the destination abook.
5486 for(al
= action_list
; al
&& al
->pab
; al
++){
5489 if(al
->skip
|| (skip_dups
&& al
->dup
))
5492 if(!(abe
= adrbk_get_ae(al
->pab
->address_book
, (a_c_arg_t
) al
->num
))){
5493 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
5494 _("Error saving entry: %s"),
5495 error_description(errno
));
5501 * Delete existing dups and replace them.
5505 /* delete the existing entry */
5507 if(adrbk_delete(pab_dst
->address_book
,
5508 (a_c_arg_t
)al
->num_in_dst
, 1, 0, 0, 0) == 0){
5512 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
5513 _("Error replacing entry in %s: %s"),
5515 error_description(errno
));
5522 * Now we have a clean slate to work with.
5523 * Add (sorted in correctly) or append abe to the destination
5526 if(total_to_copy
<= 1)
5527 rc
= adrbk_add(pab_dst
->address_book
,
5531 abe
->tag
== Single
? abe
->addr
.addr
: NULL
,
5541 rc
= adrbk_append(pab_dst
->address_book
,
5544 abe
->tag
== Single
? abe
->addr
.addr
: NULL
,
5554 * If the entry we copied is a list, we also have to add
5555 * the list members to the copy.
5557 if(rc
== 0 && abe
->tag
== List
){
5561 * We want it to copy the list in the exact order
5562 * without sorting it.
5564 save_sort_rule
= pab_dst
->address_book
->sort_rule
;
5565 pab_dst
->address_book
->sort_rule
= AB_SORT_RULE_NONE
;
5567 rc
= adrbk_nlistadd(pab_dst
->address_book
,
5568 (a_c_arg_t
)new_entry_num
, NULL
, NULL
,
5572 pab_dst
->address_book
->sort_rule
= save_sort_rule
;
5576 if(abe
&& abe
->nickname
)
5577 q_status_message2(SM_ORDER
| SM_DING
, 3, 5, _("Error saving %s: %s"), abe
->nickname
, error_description(errno
));
5579 q_status_message1(SM_ORDER
| SM_DING
, 3, 5, _("Error saving entry: %s"), error_description(errno
));
5588 int sort_happened
= 0;
5590 if(adrbk_write(pab_dst
->address_book
, 0, NULL
, &sort_happened
, 0, 1)){
5596 ps_global
->mangled_screen
= 1;
5603 restore_state(&state
);
5605 fs_give((void **)&action_list
);
5607 ps_global
->mangled_footer
= 1;
5612 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
5613 _("Save only partially completed"));
5615 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
5617 else if (how_many_to_copy
+ how_many_no_action
-
5618 (skip_dups
? how_many_dups
: 0) > 0){
5621 snprintf(tmp
, sizeof(tmp
), "Saved %d %s to %s",
5622 how_many_to_copy
+ how_many_no_action
-
5623 (skip_dups
? how_many_dups
: 0),
5624 ((how_many_to_copy
+ how_many_no_action
-
5625 (skip_dups
? how_many_dups
: 0)) > 1) ? "entries" : "entry",
5627 tmp
[sizeof(tmp
)-1] = '\0';
5628 q_status_message(SM_ORDER
, 0, 4, tmp
);
5636 * Warn should point to an array of size MAX_NICKNAME+1.
5639 set_act_list_member(ACTION_LIST_S
*al
, a_c_arg_t numarg
, PerAddrBook
*pab_dst
, PerAddrBook
*pab
, char *warn
)
5641 AdrBk_Entry
*abe1
, *abe2
;
5644 num
= (adrbk_cntr_t
)numarg
;
5649 /* skip if they're copying from and to same addrbook */
5653 abe1
= adrbk_get_ae(pab
->address_book
, numarg
);
5654 if(abe1
&& abe1
->nickname
&& abe1
->nickname
[0]){
5655 adrbk_cntr_t dst_enum
;
5657 abe2
= adrbk_lookup_by_nick(pab_dst
->address_book
,
5658 abe1
->nickname
, &dst_enum
);
5660 * This nickname already exists in the destn address book.
5663 /* If it isn't different, no problem. Check it out. */
5664 if(abes_are_equal(abe1
, abe2
))
5667 strncpy(warn
, abe1
->nickname
, MAX_NICKNAME
);
5668 warn
[MAX_NICKNAME
] = '\0';
5670 al
->num_in_dst
= dst_enum
;
5679 * Print out the display list.
5684 int do_entry
= 0, curopen
;
5687 dprint((2, "- ab_print -\n"));
5689 curopen
= cur_is_open();
5690 if(!agg
&& curopen
){
5691 static ESCKEY_S prt
[] = {
5692 {'a', 'a', "A", N_("AddressBook")},
5693 {'e', 'e', "E", N_("Entry")},
5694 {-1, 0, NULL
, NULL
}};
5696 prompt
= _("Print Address Book or just this Entry? ");
5697 switch(radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), prt
, 'a', 'x',
5700 q_status_message(SM_INFO
, 0, 2, _("Address book print cancelled"));
5701 ps_global
->mangled_footer
= 1;
5714 /* TRANSLATORS: This is input for
5715 Print something1 using something2. The thing we're
5716 defining here is something1. */
5718 prompt
= _("selected entries");
5721 prompt
= _("address book list");
5723 prompt
= _("entry");
5725 prompt
= _("address book");
5728 if(open_printer(prompt
) == 0){
5729 DL_CACHE_S dlc_buf
, *match_dlc
;
5735 char more_spaces
[100];
5739 save_line
= as
.top_ent
+ as
.cur_row
;
5740 match_dlc
= get_dlc(save_line
);
5741 dlc_buf
= *match_dlc
;
5742 match_dlc
= &dlc_buf
;
5744 if(do_entry
){ /* print an individual addrbook entry */
5746 abook_indent
= utf8_width(_("Nickname")) + 2;
5748 snprintf(spaces
, sizeof(spaces
), "%*.*s", abook_indent
+2, abook_indent
+2, "");
5749 snprintf(more_spaces
, sizeof(more_spaces
), "%*.*s",
5750 abook_indent
+4, abook_indent
+4, "");
5752 dl
= dlist(save_line
);
5753 abe
= ae(save_line
);
5756 int are_some_unqualified
= 0, expand_nicks
= 0;
5758 ADDRESS
*adrlist
= NULL
;
5761 * Search through the addresses to see if there are any
5762 * that are unqualified, and so would be different if
5765 if(abe
->tag
== Single
){
5766 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
5767 && !strindex(abe
->addr
.addr
, '@'))
5768 are_some_unqualified
++;
5773 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5774 if(!strindex(*ll
, '@')){
5775 are_some_unqualified
++;
5781 if(are_some_unqualified
){
5782 switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward
,
5785 q_status_message(SM_INFO
, 0, 2, _("Address book print cancelled"));
5786 ps_global
->mangled_footer
= 1;
5799 /* expand nicknames and fully-qualify unqualified names */
5803 char *init_addr
= NULL
;
5805 if(abe
->tag
== Single
)
5806 init_addr
= cpystr(abe
->addr
.addr
);
5812 /* figure out how large a string we need to allocate */
5814 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
5815 length
+= (strlen(*ll
) + 2);
5820 init_addr
= (char *)fs_get((size_t)(length
+1L) *
5824 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5825 sstrncpy(&p
, *ll
, length
-(p
-init_addr
));
5827 sstrncpy(&p
, ", ", length
-(p
-init_addr
));
5830 init_addr
[length
] = '\0';
5834 bldto
.arg
.str
= init_addr
;
5835 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
5837 fs_give((void **)&init_addr
);
5840 q_status_message1(SM_ORDER
, 0, 4, "%s", error
);
5841 fs_give((void **)&error
);
5843 ps_global
->mangled_footer
= 1;
5848 rfc822_parse_adrlist(&adrlist
, addr
,
5849 ps_global
->maildomain
);
5850 fs_give((void **)&addr
);
5853 /* Will use adrlist to do the printing below */
5856 tmp
= abe
->nickname
? abe
->nickname
: "";
5857 string
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, SIZEOF_20KBUF
, tmp
);
5858 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
5859 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5861 fs_give((void **)&tmp
);
5864 tmp
= abe
->fullname
? abe
->fullname
: "";
5865 string
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, SIZEOF_20KBUF
, tmp
);
5866 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fullname"));
5867 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5869 fs_give((void **)&tmp
);
5872 tmp
= abe
->fcc
? abe
->fcc
: "";
5873 string
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, SIZEOF_20KBUF
, tmp
);
5874 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fcc"));
5875 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5877 fs_give((void **)&tmp
);
5880 tmp
= abe
->extra
? abe
->extra
: "";
5881 {unsigned char *p
, *bb
= NULL
;
5883 if((n
= 4*strlen(tmp
)) > SIZEOF_20KBUF
-1){
5885 p
= bb
= (unsigned char *)fs_get(len
* sizeof(char));
5888 len
= SIZEOF_20KBUF
;
5889 p
= (unsigned char *)tmp_20k_buf
;
5892 string
= (char *)rfc1522_decode_to_utf8(p
, len
, tmp
);
5893 utf8_snprintf(b
, len
, "%-*.*w: ", abook_indent
, abook_indent
, _("Comment"));
5894 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5896 fs_give((void **)&tmp
);
5900 fs_give((void **)&bb
);
5910 for(a
= adrlist
; a
; a
= a
->next
){
5915 next_addr
= a
->next
;
5918 bufp
= (char *) fs_get(len
* sizeof(char));
5919 tmp
= addr_string(a
, bufp
, len
);
5920 a
->next
= next_addr
;
5921 string
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
+10000,
5922 SIZEOF_20KBUF
-10000, tmp
);
5923 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
5924 if((tmp
= fold(string
, 80, 80,
5925 (a
== adrlist
) ? b
: spaces
,
5926 more_spaces
, FLD_NONE
)) != NULL
){
5928 fs_give((void **)&tmp
);
5931 fs_give((void **)&bufp
);
5935 mail_free_address(&adrlist
);
5937 utf8_snprintf(b
, sizeof(b
), "%-*.*w:\n", abook_indent
, abook_indent
, _("Addresses"));
5941 else{ /* don't expand or qualify */
5942 if(abe
->tag
== Single
){
5943 tmp
= abe
->addr
.addr
? abe
->addr
.addr
: "";
5944 string
= (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf
+10000),
5945 SIZEOF_20KBUF
-10000, tmp
);
5946 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
5947 if((tmp
= fold(string
, 80, 80, b
,
5948 more_spaces
, FLD_NONE
)) != NULL
){
5950 fs_give((void **)&tmp
);
5956 if(!abe
->addr
.list
|| !abe
->addr
.list
[0]){
5957 utf8_snprintf(b
, sizeof(b
), "%-*.*w:\n", abook_indent
, abook_indent
, _("Addresses"));
5961 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5962 string
= (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf
+10000),
5963 SIZEOF_20KBUF
-10000, *ll
);
5964 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
5965 if((tmp
= fold(string
, 80, 80,
5966 (ll
== abe
->addr
.list
)
5968 more_spaces
, FLD_NONE
)) != NULL
){
5970 fs_give((void **)&tmp
);
5979 char lbuf
[6*MAX_SCREEN_COLS
+ 1];
5981 int i
, savecur
, savezoomed
;
5982 OpenStatus savestatus
;
5985 if(agg
){ /* print all selected entries */
5986 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
5987 savezoomed
= as
.zoomed
;
5989 * Fool display code into thinking display is zoomed, so
5990 * we'll skip the unselected entries. Since this feature
5991 * causes all abooks to be displayed we don't have to
5992 * step through each addrbook.
5995 warp_to_beginning();
5997 for(dl
= dlist(lineno
);
5999 dl
= dlist(++lineno
)){
6016 p
= get_abook_display_line(lineno
, 0, NULL
, NULL
,
6017 NULL
, lbuf
, sizeof(lbuf
));
6018 print_text1("%s\n", p
);
6021 as
.zoomed
= savezoomed
;
6023 else{ /* print all selected entries */
6025 savezoomed
= as
.zoomed
;
6028 for(i
= 0; i
< as
.n_addrbk
; i
++){
6029 pab
= &as
.adrbks
[i
];
6030 if(!(pab
->address_book
&&
6031 any_selected(pab
->address_book
->selects
)))
6035 * Print selected entries from addrbook i.
6036 * We have to put addrbook i into Open state so
6037 * that the display code will work right.
6040 savestatus
= pab
->ostatus
;
6041 init_abook(pab
, Open
);
6042 init_disp_form(pab
, ps_global
->VAR_ABOOK_FORMATS
, i
);
6043 (void)calculate_field_widths();
6044 warp_to_beginning();
6047 for(dl
= dlist(lineno
);
6049 dl
= dlist(++lineno
)){
6060 p
= get_abook_display_line(lineno
, 0, NULL
, NULL
,
6061 NULL
, lbuf
,sizeof(lbuf
));
6062 print_text1("%s\n", p
);
6065 init_abook(pab
, savestatus
);
6069 as
.zoomed
= savezoomed
;
6070 /* restore the display for the current addrbook */
6071 init_disp_form(&as
.adrbks
[as
.cur
],
6072 ps_global
->VAR_ABOOK_FORMATS
, as
.cur
);
6075 else{ /* print either the abook list or a single abook */
6079 savezoomed
= as
.zoomed
;
6082 if(curopen
){ /* print a single address book */
6083 anum
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
6084 warp_to_top_of_abook(anum
);
6085 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
))
6086 lineno
= 0L - XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP
;
6089 print_text(_("ADDRESS BOOK"));
6090 print_text1(" %s\n\n", as
.adrbks
[as
.cur
].abnick
);
6094 else{ /* print the list of address books */
6095 warp_to_beginning();
6099 for(dl
= dlist(lineno
);
6100 dl
->type
!= End
&& (!curopen
||
6101 (anum
==adrbk_num_from_lineno(lineno
) &&
6103 ((dlc
=get_dlc(lineno
)) &&
6104 dlc
->type
!= DlcDirDelim1
))));
6105 dl
= dlist(++lineno
)){
6116 p
= get_abook_display_line(lineno
, 0, NULL
, NULL
, NULL
,
6117 lbuf
, sizeof(lbuf
));
6118 print_text1("%s\n", p
);
6121 as
.zoomed
= savezoomed
;
6128 * jump cache back to where we started so that the next
6129 * request won't cause us to page through the whole thing
6132 warp_to_dlc(match_dlc
, save_line
);
6134 ps_global
->mangled_screen
= 1;
6137 ps_global
->mangled_footer
= 1;
6143 * Delete address book entries.
6146 ab_agg_delete(struct pine
*ps
, int agg
)
6148 int ret
= 0, i
, ch
, rc
= 0;
6150 adrbk_cntr_t num
, ab_count
;
6153 dprint((2, "- ab_agg_delete -\n"));
6156 snprintf(prompt
, sizeof(prompt
), _("Really delete %d selected entries"), as
.selections
);
6157 prompt
[sizeof(prompt
)-1] = '\0';
6158 ch
= want_to(prompt
, 'n', 'n', NO_HELP
, WT_NORM
);
6160 adrbk_cntr_t newelnum
= NO_NEXT
, flushelnum
= NO_NEXT
;
6161 DL_CACHE_S dlc_save
, dlc_restart
, *dlc
;
6163 int top_level_display
;
6166 * We want to try to put the cursor in a reasonable position
6167 * on the screen when we're done. If we are in the top-level
6168 * display, then we can leave it the same. If we have an
6169 * addrbook opened, then we want to see if we can get back to
6170 * the same entry we are currently on.
6172 if(!(top_level_display
= !any_ab_open())){
6173 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
6175 newelnum
= dlc_save
.dlcelnum
;
6178 we_cancel
= busy_cue(NULL
, NULL
, 1);
6180 for(i
= 0; i
< as
.n_addrbk
&& rc
!= -5; i
++){
6181 int orig_selected
, selected
;
6183 pab
= &as
.adrbks
[i
];
6184 if(!pab
->address_book
)
6187 ab_count
= adrbk_count(pab
->address_book
);
6189 selected
= howmany_selected(pab
->address_book
->selects
);
6190 orig_selected
= selected
;
6192 * Because deleting an entry causes the addrbook to be
6193 * immediately updated, we need to delete from higher entry
6194 * numbers to lower numbers. That way, entry number n is still
6195 * entry number n in the updated address book because we've
6196 * only deleted entries higher than n.
6198 for(num
= ab_count
-1; selected
> 0 && rc
== 0; num
--){
6199 if(entry_is_selected(pab
->address_book
->selects
,
6201 rc
= adrbk_delete(pab
->address_book
, (a_c_arg_t
)num
,
6207 * This is just here to help us reposition the cursor.
6209 if(!top_level_display
&& as
.cur
== i
&& rc
== 0){
6218 if(rc
== 0 && orig_selected
> 0){
6219 int sort_happened
= 0;
6221 rc
= adrbk_write(pab
->address_book
, 0, NULL
, &sort_happened
, 1, 0);
6223 ps_global
->mangled_screen
= 1;
6227 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
6228 "Error updating %s: %s",
6229 (as
.n_addrbk
> 1) ? pab
->abnick
6231 error_description(errno
));
6232 dprint((1, "Error updating %s: %s\n",
6233 pab
->filename
? pab
->filename
: "?",
6234 error_description(errno
)));
6239 cancel_busy_cue(-1);
6242 q_status_message(SM_ORDER
, 0, 2, _("Deletions completed"));
6247 if(!top_level_display
){
6251 if(flushelnum
!= dlc_save
.dlcelnum
){
6253 * We didn't delete current so restart there. The elnum
6254 * may have changed if we deleted entries above it.
6256 dlc_restart
= dlc_save
;
6257 dlc_restart
.dlcelnum
= newelnum
;
6261 * Current was deleted.
6263 dlc_restart
.adrbk_num
= as
.cur
;
6264 pab
= &as
.adrbks
[as
.cur
];
6265 ab_count
= adrbk_count(pab
->address_book
);
6267 dlc_restart
.type
= DlcEmpty
;
6271 dlc_restart
.dlcelnum
= MIN(newelnum
, ab_count
-1);
6272 abe
= adrbk_get_ae(pab
->address_book
,
6273 (a_c_arg_t
) dlc_restart
.dlcelnum
);
6274 if(abe
&& abe
->tag
== Single
)
6275 dlc_restart
.type
= DlcSimple
;
6276 else if(abe
&& abe
->tag
== List
)
6277 dlc_restart
.type
= DlcListHead
;
6284 warp_to_top_of_abook(as
.cur
);
6286 new_ent
= first_selectable_line(0L);
6287 if(new_ent
== NO_LINE
)
6290 as
.cur_row
= new_ent
;
6292 /* if it is off screen */
6293 if(as
.cur_row
>= as
.l_p_page
){
6294 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
6295 as
.cur_row
= (as
.l_p_page
- 1);
6298 else if(dlc_restart
.type
!= DlcEmpty
&&
6299 dlc_restart
.dlcelnum
== dlc_save
.dlcelnum
&&
6300 (F_OFF(F_CMBND_ABOOK_DISP
,ps_global
) || as
.cur
== 0)){
6302 * Didn't delete any before this line.
6303 * Leave screen about the same. (May have deleted current.)
6305 warp_to_dlc(&dlc_restart
, as
.cur_row
+as
.top_ent
);
6308 warp_to_dlc(&dlc_restart
, 0L);
6309 /* put in middle of screen */
6310 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
6311 as
.cur_row
= 0L - as
.top_ent
;
6314 ps
->mangled_body
= 1;
6318 cmd_cancelled("Apply Delete command");
6326 * Delete an entry from the address book
6328 * Args: abook -- The addrbook handle into access library
6329 * command_line -- The screen line on which to prompt
6330 * cur_line -- The entry number in the display list
6331 * warped -- We warped to a new part of the addrbook
6333 * Result: returns 1 if an entry was deleted, 0 if not.
6335 * The main routine above knows what to repaint because it's always the
6336 * current entry that's deleted. Here confirmation is asked of the user
6337 * and the appropriate adrbklib functions are called.
6340 single_entry_delete(AdrBk
*abook
, long int cur_line
, int *warped
)
6342 char ch
, *cmd
= NULL
, *dname
= NULL
;
6345 register AddrScrn_Disp
*dl
;
6347 DL_CACHE_S
*dlc_to_flush
;
6349 dprint((2, "- single_entry_delete -\n"));
6354 dl
= dlist(cur_line
);
6355 abe
= adrbk_get_ae(abook
, (a_c_arg_t
) dl
->elnum
);
6359 dname
= (abe
->fullname
&& abe
->fullname
[0])
6360 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6361 SIZEOF_20KBUF
, abe
->fullname
)
6362 : abe
->nickname
? abe
->nickname
: "";
6363 cmd
= _("Really delete \"%s\"");
6367 dname
= (abe
->fullname
&& abe
->fullname
[0])
6368 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6369 SIZEOF_20KBUF
, abe
->fullname
)
6370 : abe
->nickname
? abe
->nickname
: "";
6371 cmd
= _("Really delete ENTIRE list \"%s\"");
6375 dname
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6376 SIZEOF_20KBUF
, listmem_from_dl(abook
, dl
));
6377 cmd
= _("Really delete \"%s\" from list");
6384 dname
= dname
? dname
: "";
6385 cmd
= cmd
? cmd
: "";
6387 snprintf(prompt
, sizeof(prompt
), cmd
, dname
);
6388 prompt
[sizeof(prompt
)-1] = '\0';
6389 ch
= want_to(prompt
, 'n', 'n', NO_HELP
, WT_NORM
);
6391 dlc_to_flush
= get_dlc(cur_line
);
6392 if(dl
->type
== Simple
|| dl
->type
== ListHead
){
6393 /*--- Kill a single entry or an entire list ---*/
6394 rc
= adrbk_delete(abook
, (a_c_arg_t
)dl
->elnum
, 1, 1, 0, 1);
6396 else if(listmem_count_from_abe(abe
) > 2){
6397 /*---- Kill an entry out of a list ----*/
6398 rc
= adrbk_listdel(abook
, (a_c_arg_t
)dl
->elnum
,
6399 listmem_from_dl(abook
, dl
));
6402 char *nick
, *full
, *addr
, *fcc
, *comment
;
6403 adrbk_cntr_t new_entry_num
= NO_NEXT
;
6405 /*---- Convert a List to a Single entry ----*/
6407 /* Save old info to be transferred */
6408 nick
= cpystr(abe
->nickname
);
6409 full
= cpystr(abe
->fullname
);
6410 fcc
= cpystr(abe
->fcc
);
6411 comment
= cpystr(abe
->extra
);
6412 if(listmem_count_from_abe(abe
) == 2)
6413 addr
= cpystr(abe
->addr
.list
[1 - dl
->l_offset
]);
6417 rc
= adrbk_delete(abook
, (a_c_arg_t
)dl
->elnum
, 0, 1, 0, 0);
6433 fs_give((void **)&nick
);
6434 fs_give((void **)&full
);
6435 fs_give((void **)&fcc
);
6436 fs_give((void **)&comment
);
6437 fs_give((void **)&addr
);
6440 DL_CACHE_S dlc_restart
;
6442 dlc_restart
.adrbk_num
= as
.cur
;
6443 dlc_restart
.dlcelnum
= new_entry_num
;
6444 dlc_restart
.type
= DlcSimple
;
6445 warp_to_dlc(&dlc_restart
, 0L);
6452 q_status_message(SM_ORDER
, 0, 3,
6453 _("Entry deleted, address book updated"));
6454 dprint((5, "abook: Entry %s\n",
6455 (dl
->type
== Simple
|| dl
->type
== ListHead
) ? "deleted"
6458 * Remove deleted line and everything after it from
6459 * the dlc cache. Next time we try to access those lines they
6460 * will get filled in with the right info.
6462 flush_dlc_from_cache(dlc_to_flush
);
6469 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
6470 _("Error updating address book: %s"),
6471 error_description(errno
));
6472 pab
= &as
.adrbks
[as
.cur
];
6473 dprint((1, "Error deleting entry from %s (%s): %s\n",
6474 pab
->abnick
? pab
->abnick
: "?",
6475 pab
->filename
? pab
->filename
: "?",
6476 error_description(errno
)));
6482 q_status_message(SM_INFO
, 0, 2, _("Entry not deleted"));
6489 free_headents(struct headerentry
**head
)
6491 struct headerentry
*he
;
6495 for(he
= *head
; he
->name
; he
++)
6496 if(he
->bldr_private
){
6497 pt
= (PrivateTop
*)he
->bldr_private
;
6498 free_privatetop(&pt
);
6501 fs_give((void **)head
);
6508 prompt::name::help::prwid::maxlen::realaddr::
6509 builder::affected_entry::next_affected::selector::key_label::fileedit::
6510 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
6511 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
6513 static struct headerentry headents_for_query
[]={
6514 {"Normal Search : ", "NormalSearch", h_composer_qserv_qq
, 16, 0, NULL
,
6515 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6516 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6517 {"Name : ", "Name", h_composer_qserv_cn
, 16, 0, NULL
,
6518 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6519 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6520 {"Surname : ", "SurName", h_composer_qserv_sn
, 16, 0, NULL
,
6521 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6522 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6523 {"Given Name : ", "GivenName", h_composer_qserv_gn
, 16, 0, NULL
,
6524 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6525 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6526 {"Email Address : ", "EmailAddress", h_composer_qserv_mail
, 16, 0, NULL
,
6527 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6528 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6529 {"Organization : ", "Organization", h_composer_qserv_org
, 16, 0, NULL
,
6530 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6531 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6532 {"Org Unit : ", "OrganizationalUnit", h_composer_qserv_unit
, 16, 0, NULL
,
6533 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6534 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6535 {"Country : ", "Country", h_composer_qserv_country
, 16, 0, NULL
,
6536 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6537 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6538 {"State : ", "State", h_composer_qserv_state
, 16, 0, NULL
,
6539 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6540 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6541 {"Locality : ", "Locality", h_composer_qserv_locality
, 16, 0, NULL
,
6542 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6543 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6544 {" ", "BlankLine", NO_HELP
, 1, 0, NULL
,
6545 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6546 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_NONE
},
6547 {"Custom Filter : ", "CustomFilter", h_composer_qserv_custom
, 16, 0, NULL
,
6548 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6549 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6550 {NULL
, NULL
, NO_HELP
, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6551 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
6560 #define QQ_COUNTRY 7
6562 #define QQ_LOCALITY 9
6564 #define QQ_CUSTOM 11
6567 static SAVED_QUERY_S
*saved_params
;
6571 * Formulate a query for LDAP servers and send it and view it.
6572 * This is called from the Address Book screen, either from ^T from the
6573 * composer (selecting) or just when browsing in the address book screen
6576 * Args ps -- Pine struct
6577 * selecting -- This is set if we're just selecting an address, as opposed
6578 * to browsing on an LDAP server
6579 * who -- Tells us which server to query
6580 * error -- An error message allocated here and freed by caller.
6582 * Returns -- Null if not selecting, possibly an address if selecting.
6583 * The address is 1522 decoded and should be freed by the caller.
6586 query_server(struct pine
*ps
, int selecting
, int *exit
, int who
, char **error
)
6588 struct headerentry
*he
= NULL
;
6590 STORE_S
*msgso
= NULL
;
6591 int i
, lret
, editor_result
;
6593 HelpType help
= NO_HELP
;
6594 #define FILTSIZE 1000
6595 char fbuf
[FILTSIZE
+1];
6597 LDAP_CHOOSE_S
*winning_e
= NULL
;
6598 LDAP_SERV_RES_S
*free_when_done
= NULL
;
6599 SAVED_QUERY_S
*sq
= NULL
;
6600 static ESCKEY_S ekey
[] = {
6601 /* TRANSLATORS: go to more complex search screen */
6602 {ctrl('T'), 10, "^T", N_("To complex search")},
6606 dprint((2, "- query_server(%s) -\n", selecting
?"Selecting":""));
6608 if(!(ps
->VAR_LDAP_SERVERS
&& ps
->VAR_LDAP_SERVERS
[0] &&
6609 ps
->VAR_LDAP_SERVERS
[0][0])){
6611 *error
= cpystr(_("No LDAP server available for lookup"));
6617 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
6618 while(r
== 4 || r
== 3){
6619 /* TRANSLATORS: we're asking for a character string to search for */
6620 r
= optionally_enter(fbuf
, -FOOTER_ROWS(ps
), 0, sizeof(fbuf
),
6621 _("String to search for : "),
6622 ekey
, help
, &flags
);
6624 help
= help
== NO_HELP
? h_dir_comp_search
: NO_HELP
;
6627 /* strip quotes that user typed by mistake */
6628 (void)removing_double_quotes(fbuf
);
6630 if(r
== 1 || (r
!= 10 && fbuf
[0] == '\0')){
6631 ps
->mangled_footer
= 1;
6633 *error
= cpystr(_("Cancelled"));
6638 editor_result
= COMP_EXIT
; /* just to get right logic below */
6640 memset((void *)&pbf
, 0, sizeof(pbf
));
6643 standard_picobuf_setup(&pbf
);
6644 pbf
.exittest
= pico_simpleexit
;
6645 pbf
.exit_label
= _("Search");
6646 pbf
.canceltest
= pico_simplecancel
;
6648 pbf
.expander
= restore_query_parameters
;
6649 pbf
.ctrlr_label
= _("Restore");
6652 pbf
.pine_anchor
= set_titlebar(_("SEARCH DIRECTORY SERVER"),
6653 ps_global
->mail_stream
,
6654 ps_global
->context_current
,
6655 ps_global
->cur_folder
,
6657 0, FolderName
, 0, 0, NULL
);
6658 pbf
.pine_flags
|= P_NOBODY
;
6660 /* An informational message */
6661 if((msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
6662 pbf
.msgtext
= (void *)so_text(msgso
);
6664 * It's nice if we can make it so these lines make sense even if
6665 * they don't all make it on the screen, because the user can't
6666 * scroll down to see them. So just make each line a whole sentence
6667 * that doesn't need the others below it to make sense.
6670 _("\n Fill in some of the fields above to create a query."));
6672 _("\n The match will be for the exact string unless you include wildcards (*)."));
6674 _("\n All filled-in fields must match in order to be counted as a match."));
6676 _("\n Press \"^R\" to restore previous query values (if you've queried previously)."));
6678 _("\n \"^G\" for help specific to each item. \"^X\" to make the query, or \"^C\" to cancel."));
6681 he
= (struct headerentry
*)fs_get((QQ_END
+1) *
6682 sizeof(struct headerentry
));
6683 memset((void *)he
, 0, (QQ_END
+1) * sizeof(struct headerentry
));
6684 for(i
= QQ_QQ
; i
<= QQ_END
; i
++)
6685 he
[i
] = headents_for_query
[i
];
6689 sq
= copy_query_parameters(NULL
);
6690 he
[QQ_QQ
].realaddr
= &sq
->qq
;
6691 he
[QQ_CN
].realaddr
= &sq
->cn
;
6692 he
[QQ_SN
].realaddr
= &sq
->sn
;
6693 he
[QQ_GN
].realaddr
= &sq
->gn
;
6694 he
[QQ_MAIL
].realaddr
= &sq
->mail
;
6695 he
[QQ_ORG
].realaddr
= &sq
->org
;
6696 he
[QQ_UNIT
].realaddr
= &sq
->unit
;
6697 he
[QQ_COUNTRY
].realaddr
= &sq
->country
;
6698 he
[QQ_STATE
].realaddr
= &sq
->state
;
6699 he
[QQ_LOCALITY
].realaddr
= &sq
->locality
;
6700 he
[QQ_CUSTOM
].realaddr
= &sq
->custom
;
6702 /* pass to pico and let user set them */
6703 editor_result
= pico(&pbf
);
6704 ps
->mangled_screen
= 1;
6705 standard_picobuf_teardown(&pbf
);
6707 if(editor_result
& COMP_GOTHUP
)
6710 fix_windsize(ps_global
);
6715 if(editor_result
& COMP_EXIT
&&
6716 ((r
== 0 && *fbuf
) ||
6718 (*sq
->qq
|| *sq
->cn
|| *sq
->sn
|| *sq
->gn
|| *sq
->mail
||
6719 *sq
->org
|| *sq
->unit
|| *sq
->country
|| *sq
->state
||
6720 *sq
->locality
|| *sq
->custom
)))){
6721 LDAPLookupStyle style
;
6723 int need_and
, mangled
;
6725 CUSTOM_FILT_S
*filter
;
6728 s
= copy_query_parameters(sq
);
6729 save_query_parameters(s
);
6738 categories
= ((*sq
->cn
!= '\0') ? 1 : 0) +
6739 ((*sq
->sn
!= '\0') ? 1 : 0) +
6740 ((*sq
->gn
!= '\0') ? 1 : 0) +
6741 ((*sq
->mail
!= '\0') ? 1 : 0) +
6742 ((*sq
->org
!= '\0') ? 1 : 0) +
6743 ((*sq
->unit
!= '\0') ? 1 : 0) +
6744 ((*sq
->country
!= '\0') ? 1 : 0) +
6745 ((*sq
->state
!= '\0') ? 1 : 0) +
6746 ((*sq
->locality
!= '\0') ? 1 : 0);
6747 need_and
= (categories
> 1);
6749 if(((sq
->cn
? strlen(sq
->cn
) : 0) +
6750 (sq
->sn
? strlen(sq
->sn
) : 0) +
6751 (sq
->gn
? strlen(sq
->gn
) : 0) +
6752 (sq
->mail
? strlen(sq
->mail
) : 0) +
6753 (sq
->org
? strlen(sq
->org
) : 0) +
6754 (sq
->unit
? strlen(sq
->unit
) : 0) +
6755 (sq
->country
? strlen(sq
->country
) : 0) +
6756 (sq
->state
? strlen(sq
->state
) : 0) +
6757 (sq
->locality
? strlen(sq
->locality
) : 0)) > FILTSIZE
- 100){
6759 *error
= cpystr(_("Search strings too long"));
6766 snprintf(fbuf
, sizeof(fbuf
),
6767 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
6768 need_and
? "(&" : "",
6769 *sq
->cn
? "(cn=" : "",
6770 *sq
->cn
? sq
->cn
: "",
6772 *sq
->sn
? "(sn=" : "",
6773 *sq
->sn
? sq
->sn
: "",
6775 *sq
->gn
? "(givenname=" : "",
6776 *sq
->gn
? sq
->gn
: "",
6778 *sq
->mail
? "(mail=" : "",
6779 *sq
->mail
? sq
->mail
: "",
6780 *sq
->mail
? ")" : "",
6781 *sq
->org
? "(o=" : "",
6782 *sq
->org
? sq
->org
: "",
6783 *sq
->org
? ")" : "",
6784 *sq
->unit
? "(ou=" : "",
6785 *sq
->unit
? sq
->unit
: "",
6786 *sq
->unit
? ")" : "",
6787 *sq
->country
? "(c=" : "",
6788 *sq
->country
? sq
->country
: "",
6789 *sq
->country
? ")" : "",
6790 *sq
->state
? "(st=" : "",
6791 *sq
->state
? sq
->state
: "",
6792 *sq
->state
? ")" : "",
6793 *sq
->locality
? "(l=" : "",
6794 *sq
->locality
? sq
->locality
: "",
6795 *sq
->locality
? ")" : "",
6796 need_and
? ")" : "");
6797 fbuf
[sizeof(fbuf
)-1] = '\0';
6800 if(categories
> 0 || *sq
->custom
)
6801 filter
= (CUSTOM_FILT_S
*)fs_get(sizeof(CUSTOM_FILT_S
));
6803 /* combine the configured filters with this filter */
6806 filter
->filt
= sq
->custom
;
6807 filter
->combine
= 0;
6809 else if(*sq
->qq
&& categories
> 0){
6811 filter
->filt
= fbuf
;
6812 filter
->combine
= 1;
6814 else if(categories
> 0){
6816 filter
->filt
= fbuf
;
6817 filter
->combine
= 0;
6826 memset(&wp_err
, 0, sizeof(wp_err
));
6827 wp_err
.mangled
= &mangled
;
6828 style
= selecting
? AlwaysDisplayAndMailRequired
: AlwaysDisplay
;
6830 /* maybe coming from composer */
6831 fix_windsize(ps_global
);
6835 lret
= ldap_lookup_all(string
, who
, 0, style
, filter
, &winning_e
,
6836 &wp_err
, &free_when_done
);
6839 fs_give((void **)&filter
);
6842 ps
->mangled_screen
= 1;
6845 if(status_message_remaining() && error
)
6846 *error
= wp_err
.error
;
6848 fs_give((void **)&wp_err
.error
);
6851 if(lret
== 0 && winning_e
&& selecting
){
6854 addr
= address_from_ldap(winning_e
);
6857 addr
->host
= cpystr("missing-hostname");
6860 fs_give((void **)error
);
6862 *error
= cpystr(_("Missing hostname in LDAP address"));
6866 ret
= addr_list_string(addr
, NULL
, 1);
6867 if(!ret
|| !ret
[0]){
6869 fs_give((void **)&ret
);
6874 if(error
&& !*error
){
6877 snprintf(buf
, sizeof(buf
), _("No email address available for \"%s\""),
6878 (addr
->personal
&& *addr
->personal
)
6880 : "selected entry");
6881 buf
[sizeof(buf
)-1] = '\0';
6882 *error
= cpystr(buf
);
6886 mail_free_address(&addr
);
6889 else if(lret
== -1 && exit
)
6898 free_ldap_result_list(&free_when_done
);
6901 fs_give((void **)&winning_e
);
6907 free_query_parameters(&sq
);
6914 * View all fields of an LDAP entry while browsing.
6916 * Args ps -- Pine struct
6917 * winning_e -- The struct containing the information about the entry
6921 view_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
)
6923 STORE_S
*srcstore
= NULL
;
6924 SourceType srctype
= CharStar
;
6926 HANDLE_S
*handles
= NULL
;
6928 dprint((9, "- view_ldap_entry -\n"));
6930 if((srcstore
=prep_ldap_for_viewing(ps
,winning_e
,srctype
,&handles
)) != NULL
){
6931 memset(&sargs
, 0, sizeof(SCROLL_S
));
6932 sargs
.text
.text
= so_text(srcstore
);
6933 sargs
.text
.src
= srctype
;
6934 sargs
.text
.desc
= _("expanded entry");
6935 sargs
.text
.handles
= handles
;
6936 sargs
.bar
.title
= _("DIRECTORY ENTRY");
6937 sargs
.proc
.tool
= process_ldap_cmd
;
6938 sargs
.proc
.data
.p
= (void *) winning_e
;
6939 sargs
.help
.text
= h_ldap_view
;
6940 sargs
.help
.title
= _("HELP FOR DIRECTORY VIEW");
6941 sargs
.keys
.menu
= &ldap_view_keymenu
;
6942 setbitmap(sargs
.keys
.bitmap
);
6945 sargs
.keys
.menu
->how_many
= 2;
6947 sargs
.keys
.menu
->how_many
= 1;
6948 clrbitn(OTHER_KEY
, sargs
.keys
.bitmap
);
6953 ps
->mangled_screen
= 1;
6955 free_handles(&handles
);
6958 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
6963 * Compose a message to the email addresses contained in an LDAP entry.
6965 * Args ps -- Pine struct
6966 * winning_e -- The struct containing the information about the entry
6970 compose_to_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*e
, int allow_role
)
6972 struct berval
**elecmail
= NULL
,
6980 dprint((9, "- compose_to_ldap_entry -\n"));
6986 for(a
= ldap_first_attribute(e
->ld
, e
->selected_entry
, &ber
);
6988 a
= ldap_next_attribute(e
->ld
, e
->selected_entry
, ber
)){
6990 if(strcmp(a
, e
->info_used
->mailattr
) == 0){
6992 mail
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
6994 else if(strcmp(a
, "electronicmail") == 0){
6996 elecmail
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
6998 else if(strcmp(a
, e
->info_used
->cnattr
) == 0){
7000 cn
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7002 else if(strcmp(a
, e
->info_used
->gnattr
) == 0){
7004 givenname
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7006 else if(strcmp(a
, e
->info_used
->snattr
) == 0){
7008 sn
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7011 our_ldap_memfree(a
);
7016 if(ALPINE_LDAP_can_use(elecmail
) && !mail
)
7019 ldap_value_free_len(elecmail
);
7024 for(num
= 0; ALPINE_LDAP_usable(mail
, num
); num
++)
7025 len
+= strlen(mail
[num
]->bv_val
) + 1;
7028 char *p
, *address
, *fn
= NULL
;
7032 address
= (char *)fs_get(len
* sizeof(char));
7035 while(ALPINE_LDAP_usable(mail
, num
)){
7036 sstrncpy(&p
, mail
[num
]->bv_val
, len
-(p
-address
));
7039 sstrncpy(&p
, ",", len
-(p
-address
));
7042 address
[len
-1] = '\0';
7045 * If we have a fullname and there is only a single address and
7046 * the address doesn't seem to have a fullname with it, add it.
7048 if(ALPINE_LDAP_can_use(mail
) && !mail
[1]){
7049 if(ALPINE_LDAP_can_use(cn
))
7050 fn
= cpystr(cn
[0]->bv_val
);
7051 else if(ALPINE_LDAP_can_use(sn
) && ALPINE_LDAP_can_use(givenname
)){
7054 l
= strlen(givenname
[0]->bv_val
) + strlen(sn
[0]->bv_val
) + 1;
7055 fn
= (char *) fs_get((l
+1) * sizeof(char));
7056 snprintf(fn
, l
+1, "%s %s", givenname
[0]->bv_val
, sn
[0]->bv_val
);
7061 if(ALPINE_LDAP_can_use(mail
) && !mail
[1] && fn
){
7062 ADDRESS
*adrlist
= NULL
;
7065 tmp_a_string
= cpystr(address
);
7066 rfc822_parse_adrlist(&adrlist
, tmp_a_string
, fakedomain
);
7067 fs_give((void **)&tmp_a_string
);
7068 if(adrlist
&& !adrlist
->next
&& !adrlist
->personal
){
7073 adrlist
->personal
= cpystr(fn
);
7074 len
= est_size(adrlist
);
7075 new_address
= (char *) fs_get(len
* sizeof(char));
7076 new_address
[0] ='\0';
7077 rbuf
.f
= dummy_soutr
;
7079 rbuf
.beg
= new_address
;
7080 rbuf
.cur
= new_address
;
7081 rbuf
.end
= new_address
+len
-1;
7082 /* this will quote it if it needs quoting */
7083 rfc822_output_address_list(&rbuf
, adrlist
, 0L, NULL
);
7085 fs_give((void **)&address
);
7086 address
= new_address
;
7090 mail_free_address(&adrlist
);
7094 bldto
.arg
.str
= address
;
7097 ab_compose_internal(bldto
, allow_role
);
7098 restore_state(&state
);
7099 fs_give((void **)&address
);
7101 fs_give((void **)&fn
);
7104 * Window size may have changed in composer.
7105 * Pine_send will have reset the window size correctly,
7106 * but we still have to reset our address book data structures.
7111 ps_global
->mangled_screen
= 1;
7114 q_status_message(SM_ORDER
, 0, 4, _("No address to compose to"));
7117 ldap_value_free_len(mail
);
7119 ldap_value_free_len(cn
);
7121 ldap_value_free_len(sn
);
7123 ldap_value_free_len(givenname
);
7128 * Forward the text of an LDAP entry via email (not a vcard attachment)
7130 * Args ps -- Pine struct
7131 * winning_e -- The struct containing the information about the entry
7135 forward_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
)
7137 STORE_S
*srcstore
= NULL
;
7138 SourceType srctype
= CharStar
;
7140 dprint((9, "- forward_ldap_entry -\n"));
7142 if((srcstore
= prep_ldap_for_viewing(ps
,winning_e
,srctype
,NULL
)) != NULL
){
7143 forward_text(ps
, so_text(srcstore
), srctype
);
7144 ps
->mangled_screen
= 1;
7148 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
7153 prep_ldap_for_viewing(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
, SourceType srctype
, HANDLE_S
**handlesp
)
7155 STORE_S
*store
= NULL
;
7160 #define INDENTHERE (22)
7162 char hdr
[6*INDENTHERE
+1], hdr2
[6*INDENTHERE
+1];
7163 struct berval
**cn
= NULL
;
7164 int indent
= INDENTHERE
;
7166 if(!(store
= so_get(srctype
, NULL
, EDIT_ACCESS
)))
7169 /* for mailto handles so user can select individual email addrs */
7171 init_handles(handlesp
);
7173 width
= MAX(ps
->ttyo
->screen_cols
, 25);
7175 snprintf(hdr2
, sizeof(hdr2
), "%-*.*s: ", indent
-2,indent
-2, "");
7176 hdr2
[sizeof(hdr2
)-1] = '\0';
7178 if(sizeof(obuf
) > ps
->ttyo
->screen_cols
+1){
7179 memset((void *)obuf
, '-', ps
->ttyo
->screen_cols
* sizeof(char));
7180 obuf
[ps
->ttyo
->screen_cols
] = '\n';
7181 obuf
[ps
->ttyo
->screen_cols
+1] = '\0';
7184 a
= ldap_get_dn(winning_e
->ld
, winning_e
->selected_entry
);
7186 so_puts(store
, obuf
);
7187 if((tmp
= fold(a
, width
, width
, "", " ", FLD_NONE
)) != NULL
){
7188 so_puts(store
, tmp
);
7189 fs_give((void **)&tmp
);
7192 so_puts(store
, obuf
);
7193 so_puts(store
, "\n");
7195 our_ldap_dn_memfree(a
);
7197 for(a
= ldap_first_attribute(winning_e
->ld
, winning_e
->selected_entry
, &ber
);
7199 a
= ldap_next_attribute(winning_e
->ld
, winning_e
->selected_entry
, ber
)){
7202 struct berval
**vals
;
7205 vals
= ldap_get_values_len(winning_e
->ld
, winning_e
->selected_entry
, a
);
7207 /* save this for mailto */
7208 if(handlesp
&& !cn
&& !strcmp(a
, winning_e
->info_used
->cnattr
))
7209 cn
= ldap_get_values_len(winning_e
->ld
, winning_e
->selected_entry
, a
);
7214 do_mailto
= (handlesp
&&
7215 !strcmp(a
, winning_e
->info_used
->mailattr
));
7217 utf8_snprintf(hdr
, sizeof(hdr
), "%-*.*w: ", indent
-2,indent
-2,
7218 ldap_translate(a
, winning_e
->info_used
));
7219 hdr
[sizeof(hdr
)-1] = '\0';
7220 for(i
= 0; ALPINE_LDAP_usable(vals
, i
); i
++){
7226 char *addr
, *new_addr
, *enc_addr
;
7229 addr
= cpystr(vals
[i
]->bv_val
);
7230 if(ALPINE_LDAP_can_use(cn
))
7231 fn
= cpystr(cn
[0]->bv_val
);
7234 tmp_a_string
= cpystr(addr
);
7235 rfc822_parse_adrlist(&ad
, tmp_a_string
, "@");
7236 fs_give((void **)&tmp_a_string
);
7237 if(ad
&& !ad
->next
&& !ad
->personal
){
7241 ad
->personal
= cpystr(fn
);
7243 new_addr
= (char *) fs_get(len
* sizeof(char));
7245 /* this will quote it if it needs quoting */
7246 rbuf
.f
= dummy_soutr
;
7248 rbuf
.beg
= new_addr
;
7249 rbuf
.cur
= new_addr
;
7250 rbuf
.end
= new_addr
+len
-1;
7251 rfc822_output_address_list(&rbuf
, ad
, 0L, NULL
);
7253 fs_give((void **) &addr
);
7258 mail_free_address(&ad
);
7260 fs_give((void **)&fn
);
7263 if((enc_addr
= rfc1738_encode_mailto(addr
)) != NULL
){
7266 l
= strlen(enc_addr
) + 7;
7267 path
= (char *) fs_get((l
+1) * sizeof(char));
7268 snprintf(path
, l
+1, "mailto:%s", enc_addr
);
7270 fs_give((void **)&enc_addr
);
7273 fs_give((void **)&addr
);
7276 h
= new_handle(handlesp
);
7278 h
->h
.url
.path
= path
;
7279 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
7280 buf
[sizeof(buf
)-1] = '\0';
7283 * Don't try to fold this address. Just put it on
7284 * one line and let scrolltool worry about
7285 * cutting it off before it goes past the
7286 * right hand edge. Otherwise, we have to figure
7287 * out how to handle the wrapped handle.
7289 snprintf(obuf
, sizeof(obuf
), "%c%c%c%s%s%c%c%c%c",
7290 TAG_EMBED
, TAG_HANDLE
,
7291 (int) strlen(buf
), buf
, vals
[i
]->bv_val
,
7292 TAG_EMBED
, TAG_BOLDOFF
,
7293 TAG_EMBED
, TAG_INVOFF
);
7294 obuf
[sizeof(obuf
)-1] = '\0';
7296 so_puts(store
, (i
==0) ? hdr
: hdr2
);
7297 so_puts(store
, obuf
);
7298 so_puts(store
, "\n");
7301 snprintf(obuf
, sizeof(obuf
), "%s", vals
[i
]->bv_val
);
7302 obuf
[sizeof(obuf
)-1] = '\0';
7304 if((tmp
= fold(obuf
, width
, width
,
7305 (i
==0) ? hdr
: hdr2
,
7306 repeat_char(indent
+2, SPACE
),
7307 FLD_NONE
)) != NULL
){
7308 so_puts(store
, tmp
);
7309 fs_give((void **)&tmp
);
7317 for(tmp
= strchr(a
, ';'); tmp
!= NULL
; tmp
= tmp2
){
7318 tmp2
= strchr(++tmp
, ';');
7321 is_binary
= !strucmp(tmp
, "binary");
7328 snprintf(obuf
, sizeof(obuf
), "%s", (is_binary
&&
7329 vals
[i
]->bv_val
&& vals
[i
]->bv_val
[0] != '\0') ?
7330 _("[ Binary Data ]") : vals
[i
]->bv_val
);
7331 obuf
[sizeof(obuf
)-1] = '\0';
7333 if((tmp
= fold(obuf
, width
, width
,
7334 (i
==0) ? hdr
: hdr2
,
7335 repeat_char(indent
+2, SPACE
),
7336 FLD_NONE
)) != NULL
){
7337 so_puts(store
, tmp
);
7338 fs_give((void **)&tmp
);
7343 ldap_value_free_len(vals
);
7346 utf8_snprintf(obuf
, sizeof(obuf
), "%-*.*w\n", indent
-1,indent
-1,
7347 ldap_translate(a
, winning_e
->info_used
));
7348 obuf
[sizeof(obuf
)-1] = '\0';
7349 so_puts(store
, obuf
);
7353 our_ldap_memfree(a
);
7357 ldap_value_free_len(cn
);
7364 process_ldap_cmd(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
7368 ps_global
->next_screen
= SCREEN_FUN_NULL
;
7372 save_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 0);
7377 compose_to_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 0);
7382 compose_to_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 1);
7387 alpine_panic("Unexpected command in process_ldap_cmd");
7396 pico_simpleexit(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
7406 pico_simplecancel(void (*redraw_pico
)(void))
7408 return("Cancelled");
7413 * Store query parameters so that they can be recalled by user later with ^R.
7416 save_query_parameters(SAVED_QUERY_S
*params
)
7418 free_saved_query_parameters();
7419 saved_params
= params
;
7424 copy_query_parameters(SAVED_QUERY_S
*params
)
7428 sq
= (SAVED_QUERY_S
*)fs_get(sizeof(SAVED_QUERY_S
));
7429 memset((void *)sq
, 0, sizeof(SAVED_QUERY_S
));
7431 if(params
&& params
->qq
)
7432 sq
->qq
= cpystr(params
->qq
);
7434 sq
->qq
= cpystr("");
7436 if(params
&& params
->cn
)
7437 sq
->cn
= cpystr(params
->cn
);
7439 sq
->cn
= cpystr("");
7441 if(params
&& params
->sn
)
7442 sq
->sn
= cpystr(params
->sn
);
7444 sq
->sn
= cpystr("");
7446 if(params
&& params
->gn
)
7447 sq
->gn
= cpystr(params
->gn
);
7449 sq
->gn
= cpystr("");
7451 if(params
&& params
->mail
)
7452 sq
->mail
= cpystr(params
->mail
);
7454 sq
->mail
= cpystr("");
7456 if(params
&& params
->org
)
7457 sq
->org
= cpystr(params
->org
);
7459 sq
->org
= cpystr("");
7461 if(params
&& params
->unit
)
7462 sq
->unit
= cpystr(params
->unit
);
7464 sq
->unit
= cpystr("");
7466 if(params
&& params
->country
)
7467 sq
->country
= cpystr(params
->country
);
7469 sq
->country
= cpystr("");
7471 if(params
&& params
->state
)
7472 sq
->state
= cpystr(params
->state
);
7474 sq
->state
= cpystr("");
7476 if(params
&& params
->locality
)
7477 sq
->locality
= cpystr(params
->locality
);
7479 sq
->locality
= cpystr("");
7481 if(params
&& params
->custom
)
7482 sq
->custom
= cpystr(params
->custom
);
7484 sq
->custom
= cpystr("");
7491 free_saved_query_parameters(void)
7494 free_query_parameters(&saved_params
);
7499 free_query_parameters(SAVED_QUERY_S
**parm
)
7504 fs_give((void **)&(*parm
)->qq
);
7506 fs_give((void **)&(*parm
)->cn
);
7508 fs_give((void **)&(*parm
)->sn
);
7510 fs_give((void **)&(*parm
)->gn
);
7512 fs_give((void **)&(*parm
)->mail
);
7514 fs_give((void **)&(*parm
)->org
);
7516 fs_give((void **)&(*parm
)->unit
);
7517 if((*parm
)->country
)
7518 fs_give((void **)&(*parm
)->country
);
7520 fs_give((void **)&(*parm
)->state
);
7521 if((*parm
)->locality
)
7522 fs_give((void **)&(*parm
)->locality
);
7524 fs_give((void **)&(*parm
)->custom
);
7526 fs_give((void **)parm
);
7533 * A callback from pico to restore the saved query parameters.
7535 * Args he -- Unused.
7536 * s -- The place to return the allocated array of values.
7538 * Returns -- 1 if there are parameters to return, 0 otherwise.
7541 restore_query_parameters(struct headerentry
*he
, char ***s
)
7543 int retval
= 0, i
= 0;
7548 if(saved_params
&& s
){
7549 *s
= (char **)fs_get((QQ_END
+ 1) * sizeof(char *));
7550 (*s
)[i
++] = cpystr(saved_params
->qq
? saved_params
->qq
: "");
7551 (*s
)[i
++] = cpystr(saved_params
->cn
? saved_params
->cn
: "");
7552 (*s
)[i
++] = cpystr(saved_params
->sn
? saved_params
->sn
: "");
7553 (*s
)[i
++] = cpystr(saved_params
->gn
? saved_params
->gn
: "");
7554 (*s
)[i
++] = cpystr(saved_params
->mail
? saved_params
->mail
: "");
7555 (*s
)[i
++] = cpystr(saved_params
->org
? saved_params
->org
: "");
7556 (*s
)[i
++] = cpystr(saved_params
->unit
? saved_params
->unit
: "");
7557 (*s
)[i
++] = cpystr(saved_params
->country
? saved_params
->country
: "");
7558 (*s
)[i
++] = cpystr(saved_params
->state
? saved_params
->state
: "");
7559 (*s
)[i
++] = cpystr(saved_params
->locality
? saved_params
->locality
:"");
7560 (*s
)[i
++] = cpystr(saved_params
->custom
? saved_params
->custom
:"");
7570 * Internal handler for viewing an LDAP url.
7573 url_local_ldap(char *url
)
7577 int ld_err
, mangled
= 0, we_cancel
, retval
= 0, proto
= 3;
7578 int we_turned_on
= 0;
7580 LDAPMessage
*result
;
7582 LDAP_SERV_RES_S
*serv_res
= NULL
;
7583 LDAPURLDesc
*ldapurl
= NULL
;
7586 dprint((2, "url_local_ldap(%s)\n", url
? url
: "?"));
7588 ld_err
= ldap_url_parse(url
, &ldapurl
);
7589 if(ld_err
|| !ldapurl
){
7590 snprintf(ebuf
, sizeof(ebuf
), "URL parse failed for %s", url
);
7591 ebuf
[sizeof(ebuf
)-1] = '\0';
7592 q_status_message(SM_ORDER
, 3, 5, ebuf
);
7596 if(!ldapurl
->lud_host
){
7597 /* TRNASLATORS: No host in <url> */
7598 snprintf(ebuf
, sizeof(ebuf
), _("No host in %s"), url
);
7599 ebuf
[sizeof(ebuf
)-1] = '\0';
7600 q_status_message(SM_ORDER
, 3, 5, ebuf
);
7601 ldap_free_urldesc(ldapurl
);
7605 we_turned_on
= intr_handling_on();
7606 we_cancel
= busy_cue(_("Searching for LDAP url"), NULL
, 0);
7607 ps_global
->mangled_footer
= 1;
7610 if((ld
= ldap_init(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7614 if((ld
= ldap_init(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7616 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "ldap://%s:%d", ldapurl
->lud_host
, ldapurl
->lud_port
);
7617 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
7618 if(ldap_initialize(&ld
, tmp_20k_buf
) != LDAP_SUCCESS
)
7619 #endif /* _WINDOWS */
7621 if((ld
= ldap_open(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7622 #endif /* LDAPAPI >= 11 */
7623 #endif /* _SOLARIS_SDK */
7626 cancel_busy_cue(-1);
7630 q_status_message(SM_ORDER
,3,5, _("LDAP search failed: can't initialize"));
7632 else if(!ps_global
->intr_pending
){
7633 if(ldap_v3_is_supported(ld
) &&
7634 our_ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &proto
) == 0){
7635 dprint((5, "ldap: using version 3 protocol\n"));
7639 * If we don't set RESTART then the select() waiting for the answer
7640 * in libldap will be interrupted and stopped by our busy_cue.
7642 our_ldap_set_option(ld
, LDAP_OPT_RESTART
, LDAP_OPT_ON
);
7644 t
.tv_sec
= 30; t
.tv_usec
= 0;
7646 ld_err
= ldap_search_st(ld
, ldapurl
->lud_dn
, ldapurl
->lud_scope
,
7647 ldapurl
->lud_filter
, ldapurl
->lud_attrs
,
7650 ld_err
= ldap_search_ext_s(ld
, ldapurl
->lud_dn
, ldapurl
->lud_scope
,
7651 ldapurl
->lud_filter
, ldapurl
->lud_attrs
, 0,
7653 &t
, DEF_LDAP_SIZE
, &result
);
7655 if(ld_err
!= LDAP_SUCCESS
){
7657 cancel_busy_cue(-1);
7661 snprintf(ebuf
, sizeof(ebuf
), _("LDAP search failed: %s"), ldap_err2string(ld_err
));
7662 ebuf
[sizeof(ebuf
)-1] = '\0';
7663 q_status_message(SM_ORDER
, 3, 5, ebuf
);
7667 ldap_unbind_ext(ld
, NULL
, NULL
);
7668 #endif /* _WINDOWS */
7670 else if(!ps_global
->intr_pending
){
7672 cancel_busy_cue(-1);
7677 intr_handling_off();
7681 if(ldap_count_entries(ld
, result
) == 0){
7682 q_status_message(SM_ORDER
, 3, 5, _("No matches found for url"));
7686 ldap_unbind_ext(ld
, NULL
, NULL
);
7687 #endif /* _WINDOWS */
7689 ldap_msgfree(result
);
7692 serv_res
= (LDAP_SERV_RES_S
*)fs_get(sizeof(LDAP_SERV_RES_S
));
7693 memset((void *)serv_res
, 0, sizeof(*serv_res
));
7695 serv_res
->res
= result
;
7696 info
= (LDAP_SERV_S
*)fs_get(sizeof(LDAP_SERV_S
));
7697 memset((void *)info
, 0, sizeof(*info
));
7698 info
->mailattr
= cpystr(DEF_LDAP_MAILATTR
);
7699 info
->snattr
= cpystr(DEF_LDAP_SNATTR
);
7700 info
->gnattr
= cpystr(DEF_LDAP_GNATTR
);
7701 info
->cnattr
= cpystr(DEF_LDAP_CNATTR
);
7702 serv_res
->info_used
= info
;
7703 memset(&wp_err
, 0, sizeof(wp_err
));
7704 wp_err
.mangled
= &mangled
;
7706 ask_user_which_entry(serv_res
, NULL
, NULL
, &wp_err
, DisplayForURL
);
7708 q_status_message(SM_ORDER
, 3, 5, wp_err
.error
);
7709 fs_give((void **)&wp_err
.error
);
7713 ps_global
->mangled_screen
= 1;
7715 free_ldap_result_list(&serv_res
);
7722 cancel_busy_cue(-1);
7725 intr_handling_off();
7728 ldap_free_urldesc(ldapurl
);
7732 #endif /* ENABLE_LDAP */