1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: adrbkcmd.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 Commands called from the addrbook screens.
42 #include "../pith/adrbklib.h"
43 #include "../pith/addrbook.h"
44 #include "../pith/abdlc.h"
45 #include "../pith/ablookup.h"
46 #include "../pith/bldaddr.h"
47 #include "../pith/ldap.h"
48 #include "../pith/state.h"
49 #include "../pith/filter.h"
50 #include "../pith/conf.h"
51 #include "../pith/msgno.h"
52 #include "../pith/addrstring.h"
53 #include "../pith/remote.h"
54 #include "../pith/url.h"
55 #include "../pith/util.h"
56 #include "../pith/detoken.h"
57 #include "../pith/stream.h"
58 #include "../pith/send.h"
59 #include "../pith/list.h"
60 #include "../pith/busy.h"
61 #include "../pith/icache.h"
62 #include "../pith/osdep/color.h"
65 /* internal prototypes */
66 int url_hilite_abook(long, char *, LT_INS_S
**, void *);
67 int process_abook_view_cmd(int, MSGNO_S
*, SCROLL_S
*);
68 int expand_addrs_for_pico(struct headerentry
*, char ***);
69 char *view_message_for_pico(char **);
70 int verify_nick(char *, char **, char **, BUILDER_ARG
*, int *);
71 int verify_addr(char *, char **, char **, BUILDER_ARG
*, int *);
72 int pico_sendexit_for_adrbk(struct headerentry
*, void(*)(void), int, char **);
73 char *pico_cancelexit_for_adrbk(char *, void (*)(void));
74 char *pico_cancel_for_adrbk_take(void (*)(void));
75 char *pico_cancel_for_adrbk_edit(void (*)(void));
76 int ab_modify_abook_list(int, int, int, char *, char *, char *);
77 int convert_abook_to_remote(struct pine
*, PerAddrBook
*, char *, size_t, int);
78 int any_rule_files_to_warn_about(struct pine
*);
79 int verify_folder_name(char *,char **,char **,BUILDER_ARG
*, int *);
80 int verify_server_name(char *,char **,char **,BUILDER_ARG
*, int *);
81 int verify_abook_nick(char *, char **,char **,BUILDER_ARG
*, int *);
82 int do_the_shuffle(int *, int, int, char **);
83 void ab_compose_internal(BuildTo
, int);
84 int ab_export(struct pine
*, long, int, int);
85 VCARD_INFO_S
*prepare_abe_for_vcard(struct pine
*, AdrBk_Entry
*, int);
86 void write_single_tab_entry(gf_io_t
, VCARD_INFO_S
*);
87 int percent_done_copying(void);
88 int cmp_action_list(const qsort_t
*, const qsort_t
*);
89 void set_act_list_member(ACTION_LIST_S
*, a_c_arg_t
, PerAddrBook
*, PerAddrBook
*, char *);
90 void convert_pinerc_to_remote(struct pine
*, char *);
93 typedef struct _saved_query
{
107 int process_ldap_cmd(int, MSGNO_S
*, SCROLL_S
*);
108 int pico_simpleexit(struct headerentry
*, void (*)(void), int, char **);
109 char *pico_simplecancel(void (*)(void));
110 void save_query_parameters(SAVED_QUERY_S
*);
111 SAVED_QUERY_S
*copy_query_parameters(SAVED_QUERY_S
*);
112 void free_query_parameters(SAVED_QUERY_S
**);
113 int restore_query_parameters(struct headerentry
*, char ***);
115 static char *expander_address
;
116 #endif /* ENABLE_LDAP */
118 static char *fakedomain
= "@";
121 #define VIEW_ABOOK_NONE 0
122 #define VIEW_ABOOK_EDITED 1
123 #define VIEW_ABOOK_WARPED 2
126 * View an addrbook entry.
127 * Call scrolltool to do the work.
130 view_abook_entry(struct pine
*ps
, long int cur_line
)
133 STORE_S
*in_store
, *out_store
;
134 char *string
, *errstr
;
136 HANDLE_S
*handles
= NULL
;
139 int cmd
, abook_indent
;
143 dprint((5, "- view_abook_entry -\n"));
145 if(is_addr(cur_line
)){
148 q_status_message(SM_ORDER
, 0, 3, _("Error reading entry"));
153 q_status_message(SM_ORDER
, 0, 3, _("Nothing to view"));
157 if(!(in_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
158 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
162 abook_indent
= utf8_width(_("Nickname")) + 2;
164 /* TRANSLATORS: Nickname is a shorthand name for something */
165 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
166 so_puts(in_store
, b
);
168 so_puts(in_store
, abe
->nickname
);
170 so_puts(in_store
, "\015\012");
172 /* TRANSLATORS: Full name is the name that goes with an email address.
174 Fred Flintstone <fred@bedrock.org>
175 Fred Flintstone is the Full Name. */
176 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fullname"));
177 so_puts(in_store
, b
);
179 so_puts(in_store
, abe
->fullname
);
181 so_puts(in_store
, "\015\012");
183 /* TRANSLATORS: Fcc is an abbreviation for File carbon copy. It is like
184 a cc which is a copy of a message that goes to somebody other than the
185 main recipient, only this is a copy of a message which is put into a
186 file on the user's computer. */
187 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fcc"));
188 so_puts(in_store
, b
);
190 so_puts(in_store
, abe
->fcc
);
192 so_puts(in_store
, "\015\012");
194 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, AB_COMMENT_STR
);
195 so_puts(in_store
, b
);
197 so_puts(in_store
, abe
->extra
);
199 so_puts(in_store
, "\015\012");
201 /* TRANSLATORS: Addresses refers to email Addresses */
202 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
203 so_puts(in_store
, b
);
204 if(abe
->tag
== Single
){
205 char *tmp
= abe
->addr
.addr
? abe
->addr
.addr
: "";
207 if(!strncmp(tmp
, QRUN_LDAP
, LEN_QRL
))
213 so_puts(in_store
, string
);
218 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
219 if(ll
!= abe
->addr
.list
){
220 so_puts(in_store
, "\015\012");
221 so_puts(in_store
, repeat_char(abook_indent
+2, SPACE
));
225 if(!strncmp(*ll
, QRUN_LDAP
, LEN_QRL
))
231 so_puts(in_store
, string
);
233 if(*(ll
+1)) /* not the last one */
234 so_puts(in_store
, ",");
238 so_puts(in_store
, "\015\012");
241 if(!(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
243 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
244 _("Error allocating space."));
248 so_seek(in_store
, 0L, 0);
250 init_handles(&handles
);
253 if(F_ON(F_VIEW_SEL_URL
, ps_global
)
254 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
255 || F_ON(F_SCAN_ADDR
, ps_global
))
256 gf_link_filter(gf_line_test
,
257 gf_line_test_opt(url_hilite_abook
,
258 gf_url_hilite_opt(&uh
,&handles
,0)));
260 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(ps
->ttyo
->screen_cols
- 4,
261 ps
->ttyo
->screen_cols
,
262 NULL
, abook_indent
+2,
264 gf_link_filter(gf_nvtnl_local
, NULL
);
266 gf_set_so_readc(&gc
, in_store
);
267 gf_set_so_writec(&pc
, out_store
);
269 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
272 free_handles(&handles
);
273 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
274 /* TRANSLATORS: %s is the error message */
275 _("Can't format entry: %s"), errstr
);
279 gf_clear_so_writec(out_store
);
280 gf_clear_so_readc(in_store
);
282 memset(&sargs
, 0, sizeof(SCROLL_S
));
283 sargs
.text
.text
= so_text(out_store
);
284 sargs
.text
.src
= CharStar
;
285 sargs
.text
.desc
= _("expanded entry");
286 sargs
.text
.handles
= handles
;
288 if(offset
){ /* resize? preserve paging! */
289 sargs
.start
.on
= Offset
;
290 sargs
.start
.loc
.offset
= offset
;
294 /* TRANSLATORS: a screen title. We are viewing an address book */
295 sargs
.bar
.title
= _("ADDRESS BOOK (View)");
296 sargs
.bar
.style
= TextPercent
;
297 sargs
.proc
.tool
= process_abook_view_cmd
;
298 sargs
.proc
.data
.i
= VIEW_ABOOK_NONE
;
299 sargs
.resize_exit
= 1;
300 sargs
.help
.text
= h_abook_view
;
301 /* TRANSLATORS: help screen title */
302 sargs
.help
.title
= _("HELP FOR ADDRESS BOOK VIEW");
303 sargs
.keys
.menu
= &abook_view_keymenu
;
304 setbitmap(sargs
.keys
.bitmap
);
307 sargs
.keys
.menu
->how_many
= 2;
309 sargs
.keys
.menu
->how_many
= 1;
310 clrbitn(OTHER_KEY
, sargs
.keys
.bitmap
);
313 if((cmd
= scrolltool(&sargs
)) == MC_RESIZE
)
314 offset
= sargs
.start
.loc
.offset
;
317 free_handles(&handles
);
319 while(cmd
== MC_RESIZE
);
323 if(sargs
.proc
.data
.i
!= VIEW_ABOOK_NONE
){
324 long old_l_p_p
, old_top_ent
, old_cur_row
;
326 if(sargs
.proc
.data
.i
== VIEW_ABOOK_WARPED
){
328 * Warped means we got plopped down somewhere in the display
329 * list so that we don't know where we are relative to where
330 * we were before we warped. The current line number will
331 * be zero, since that is what the warp would have set.
333 as
.top_ent
= first_line(0L - as
.l_p_page
/2L);
334 as
.cur_row
= 0L - as
.top_ent
;
336 else if(sargs
.proc
.data
.i
== VIEW_ABOOK_EDITED
){
337 old_l_p_p
= as
.l_p_page
;
338 old_top_ent
= as
.top_ent
;
339 old_cur_row
= as
.cur_row
;
342 /* Window size may have changed while in pico. */
345 /* fix up what ab_resize messed up */
346 if(sargs
.proc
.data
.i
!= VIEW_ABOOK_WARPED
&& old_l_p_p
== as
.l_p_page
){
347 as
.top_ent
= old_top_ent
;
348 as
.cur_row
= old_cur_row
;
349 as
.old_cur_row
= old_cur_row
;
352 cur_line
= as
.top_ent
+as
.cur_row
;
355 ps
->mangled_screen
= 1;
360 url_hilite_abook(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
364 if((lp
= strchr(line
, ':')) &&
365 !strncmp(line
, AB_COMMENT_STR
, strlen(AB_COMMENT_STR
)))
366 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
373 process_abook_view_cmd(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
378 static ESCKEY_S text_or_vcard
[] = {
379 /* TRANSLATORS: Text refers to plain old text, probably the text of
381 {'t', 't', "T", N_("Text")},
382 /* TRANSLATORS: A VCard is kind of like an electronic business card. It is not
383 something specific to alpine, it is universal. */
384 {'v', 'v', "V", N_("VCard")},
385 {-1, 0, NULL
, NULL
}};
387 ps_global
->next_screen
= SCREEN_FUN_NULL
;
392 * MC_EDIT works a little differently from the other cmds here.
393 * The others return 0 to scrolltool so that we are still in
394 * the view screen. This one is different because we may have
395 * changed what we're viewing. We handle that by returning 1
396 * to scrolltool and setting the sparms opt union's gint
397 * to the value below.
399 * (Late breaking news. Now we're going to return 1 from all these
400 * commands and then in the caller we're going to bounce back out
401 * to the index view instead of the view of the individual entry.
402 * So there is some dead code around for now.)
404 * Then, in the view_abook_entry function we check the value
405 * of this field on scrolltool's return and if it is one of
406 * the two special values below view_abook_entry resets the
407 * current line if necessary, flushes the display cache (in
408 * ab_resize) and loops back and starts over, effectively
409 * putting us back in the view screen but with updated
410 * contents. A side effect is that the screen above that (the
411 * abook index) will also have been flushed and corrected by
414 pab
= &as
.adrbks
[cur_addr_book()];
415 if(pab
&& pab
->access
== ReadOnly
){
416 /* TRANSLATORS: Address book can be viewed but not changed */
417 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
422 if(adrbk_check_all_validity_now()){
423 if(resync_screen(pab
, AddrBookScreen
, 0)){
424 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
425 /* TRANSLATORS: The address book was changed by some other
426 process. The user is being told that their change (their
427 Update) has been canceled and they should try again. */
428 _("Address book changed. Update cancelled. Try again."));
429 ps_global
->mangled_screen
= 1;
434 if(pab
&& pab
->access
== ReadOnly
){
435 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
441 * Arguments all come from globals, not from the arguments to
442 * process_abook_view_cmd. It would be a little cleaner if the
443 * information was contained in att, I suppose.
445 if(is_addr(as
.cur_row
+as
.top_ent
)){
446 AdrBk_Entry
*abe
, *abe_copy
;
450 dl
= dlist(as
.top_ent
+as
.cur_row
);
452 abe
= adrbk_get_ae(pab
->address_book
, entry
);
453 abe_copy
= copy_ae(abe
);
454 dprint((9,"Calling edit_entry to edit from view\n"));
455 /* TRANSLATORS: update as in update an address book entry */
456 edit_entry(pab
->address_book
, abe_copy
, entry
,
457 abe
->tag
, 0, &warped
, _("update"));
459 * The ABOOK_EDITED case doesn't mean that we necessarily
460 * changed something, just that we might have but we know
461 * we didn't change the sort order (causing the warp).
463 sparms
->proc
.data
.i
= warped
464 ? VIEW_ABOOK_WARPED
: VIEW_ABOOK_EDITED
;
467 rv
= 1; /* causes scrolltool to return */
470 q_status_message(SM_ORDER
, 0, 4,
471 "Something wrong, entry not updateable");
479 pab
= &as
.adrbks
[cur_addr_book()];
481 * Arguments all come from globals, not from the arguments to
482 * process_abook_view_cmd. It would be a little cleaner if the
483 * information was contained in att, I suppose.
485 (void)ab_save(ps_global
, pab
? pab
->address_book
: NULL
,
486 as
.top_ent
+as
.cur_row
, -FOOTER_ROWS(ps_global
), 0);
491 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 0);
496 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 1);
502 /* TRANSLATORS: A question with two choices for the answer. Forward
503 as text means to include the text of the message being forwarded
504 in the new message. Forward as a Vcard attachment means to
505 attach it to the message in a special format so it is recognizable
507 i
= radio_buttons(_("Forward as text or forward as Vcard attachment ? "),
508 -FOOTER_ROWS(ps_global
), text_or_vcard
, 't', 'x',
509 h_ab_text_or_vcard
, RB_NORM
);
512 q_status_message(SM_INFO
, 0, 2, _("Address book forward cancelled"));
517 forward_text(ps_global
, sparms
->text
.text
, sparms
->text
.src
);
521 (void)ab_forward(ps_global
, as
.top_ent
+as
.cur_row
, 0);
525 q_status_message(SM_ORDER
, 3, 3,
526 "can't happen in process_abook_view_cmd");
533 alpine_panic("Unexpected command in process_abook_view_cmd");
542 * Give expanded view of this address entry.
543 * Call scrolltool to do the work.
545 * Args: headents -- The headerentry array from pico.
548 * Returns -- Always 0.
551 expand_addrs_for_pico(struct headerentry
*headents
, char ***s
)
555 char *error
= NULL
, *addr
= NULL
, *fullname
= NULL
, *address
= NULL
;
557 ADDRESS
*adrlist
= NULL
, *a
;
558 int j
, address_index
= -1, fullname_index
= -1, no_a_fld
= 0;
559 void (*redraw
)(void) = ps_global
->redrawer
;
561 char *tmp
, *tmp2
, *tmp3
;
564 char fakeaddrpmt
[500];
567 dprint((5, "- expand_addrs_for_pico -\n"));
569 abook_indent
= utf8_width(_("Nickname")) + 2;
570 utf8_snprintf(fakeaddrpmt
, sizeof(fakeaddrpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Address"));
575 ps_global
->redrawer
= NULL
;
576 fix_windsize(ps_global
);
582 headents
[j
].name
!= NULL
&& (address_index
< 0 || fullname_index
< 0);
584 if(!strncmp(headents
[j
].name
, "Address", 7) || !strncmp(headents
[j
].name
, _("Address"), strlen(_("Address"))))
586 else if(!strncmp(headents
[j
].name
, "Fullname", 8) || !strncmp(headents
[j
].name
, _("Fullname"), strlen(_("Fullname"))))
590 if(address_index
>= 0)
591 address
= *headents
[address_index
].realaddr
;
593 address_index
= 1000; /* a big number */
597 if(fullname_index
>= 0)
598 fullname
= adrbk_formatname(*headents
[fullname_index
].realaddr
,
601 memset(&abe
, 0, sizeof(abe
));
603 abe
.fullname
= cpystr(fullname
);
608 tmp_a_string
= cpystr(address
);
609 rfc822_parse_adrlist(&adrlist
, tmp_a_string
, fakedomain
);
610 fs_give((void **)&tmp_a_string
);
611 if(adrlist
&& adrlist
->next
){
615 for(a
= adrlist
; a
; a
= a
->next
)
618 abe
.addr
.list
= (char **)fs_get((cnt
+1) * sizeof(char *));
620 for(a
= adrlist
; a
; a
= a
->next
){
621 if(a
->host
&& a
->host
[0] == '@')
622 abe
.addr
.list
[cnt
++] = cpystr(a
->mailbox
);
623 else if(a
->host
&& a
->host
[0] && a
->mailbox
&& a
->mailbox
[0])
624 abe
.addr
.list
[cnt
++] =
625 cpystr(simple_addr_string(a
, tmp_20k_buf
,
629 abe
.addr
.list
[cnt
] = NULL
;
633 abe
.addr
.addr
= address
;
637 mail_free_address(&adrlist
);
640 bldto
.arg
.abe
= &abe
;
641 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
643 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
644 fs_give((void **)&error
);
648 tmp_a_string
= cpystr(addr
);
649 rfc822_parse_adrlist(&adrlist
, tmp_a_string
, ps_global
->maildomain
);
650 fs_give((void **)&tmp_a_string
);
654 else if(no_a_fld
&& expander_address
){
657 memset(&wp_err
, 0, sizeof(wp_err
));
658 adrlist
= wp_lookups(expander_address
, &wp_err
, 0);
659 if(fullname
&& *fullname
){
661 if(adrlist
->personal
)
662 fs_give((void **)&adrlist
->personal
);
664 adrlist
->personal
= cpystr(fullname
);
669 q_status_message1(SM_ORDER
, 3, 4, "%s", wp_err
.error
);
670 fs_give((void **)&wp_err
.error
);
675 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
676 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
677 restore_state(&state
);
681 for(j
= 0; j
< address_index
&& headents
[j
].name
!= NULL
; j
++){
682 if((tmp
= fold(*headents
[j
].realaddr
,
683 ps_global
->ttyo
->screen_cols
,
684 ps_global
->ttyo
->screen_cols
,
686 repeat_char(headents
[j
].prwid
, SPACE
), FLD_NONE
)) != NULL
){
688 fs_give((void **)&tmp
);
693 * There is an assumption that Addresses is the last field.
695 tmp3
= cpystr(repeat_char(headents
[0].prwid
+ 2, SPACE
));
697 for(a
= adrlist
; a
; a
= a
->next
){
705 bufp
= (char *) fs_get(len
* sizeof(char));
706 (void) addr_string(a
, bufp
, len
);
710 * Another assumption, all the prwids are the same.
713 ps_global
->ttyo
->screen_cols
,
714 ps_global
->ttyo
->screen_cols
,
715 (a
== adrlist
) ? (no_a_fld
717 : headents
[address_index
].prompt
)
719 tmp3
, FLD_NONE
)) != NULL
){
721 fs_give((void **) &tmp
);
724 fs_give((void **) &bufp
);
729 if((tmp
= fold(tmp2
=cpystr("<none>"),
730 ps_global
->ttyo
->screen_cols
,
731 ps_global
->ttyo
->screen_cols
,
732 no_a_fld
? fakeaddrpmt
733 : headents
[address_index
].prompt
,
734 tmp3
, FLD_NONE
)) != NULL
){
736 fs_give((void **) &tmp
);
740 fs_give((void **)&tmp2
);
744 fs_give((void **)&tmp3
);
746 memset(&sargs
, 0, sizeof(SCROLL_S
));
747 sargs
.text
.text
= so_text(store
);
748 sargs
.text
.src
= CharStar
;
749 sargs
.text
.desc
= _("expanded entry");
750 /* TRANSLATORS: screen title for viewing an address book entry in Rich
751 view mode. Rich just means it is expanded to include everything. */
752 sargs
.bar
.title
= _("ADDRESS BOOK (Rich View)");
753 sargs
.bar
.style
= TextPercent
;
754 sargs
.keys
.menu
= &abook_text_km
;
755 setbitmap(sargs
.keys
.bitmap
);
760 restore_state(&state
);
764 fs_give((void **)&addr
);
767 mail_free_address(&adrlist
);
770 fs_give((void **)&fullname
);
773 fs_give((void **)&abe
.fullname
);
775 if(abe
.tag
== List
&& abe
.addr
.list
)
776 free_list_array(&(abe
.addr
.list
));
778 ps_global
->redrawer
= redraw
;
785 * Callback from TakeAddr editing screen to see message that was being
786 * viewed. Call scrolltool to do the work.
789 view_message_for_pico(char **error
)
793 void (*redraw
)(void) = ps_global
->redrawer
;
794 SourceType src
= CharStar
;
797 dprint((5, "- view_message_for_pico -\n"));
799 ps_global
->redrawer
= NULL
;
800 fix_windsize(ps_global
);
806 if(!(store
= so_get(src
, NULL
, EDIT_ACCESS
))){
807 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Error allocating space."));
811 gf_set_so_writec(&pc
, store
);
813 format_message(msgno_for_pico_callback
, env_for_pico_callback
,
814 body_for_pico_callback
, NULL
, FM_NEW_MESS
| FM_DISPLAY
, pc
);
816 gf_clear_so_writec(store
);
818 memset(&sargs
, 0, sizeof(SCROLL_S
));
819 sargs
.text
.text
= so_text(store
);
820 sargs
.text
.src
= src
;
821 sargs
.text
.desc
= _("expanded entry");
822 /* TRANSLATORS: this is a screen title */
823 sargs
.bar
.title
= _("MESSAGE TEXT");
824 sargs
.bar
.style
= TextPercent
;
825 sargs
.keys
.menu
= &abook_text_km
;
826 setbitmap(sargs
.keys
.bitmap
);
832 ps_global
->redrawer
= redraw
;
839 prompt::name::help::prwid::maxlen::realaddr::
840 builder::affected_entry::next_affected::selector::key_label::fileedit::nickcmpl
841 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
842 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
844 static struct headerentry headents_for_edit
[]={
845 {"Nickname : ", N_("Nickname"), h_composer_abook_nick
, 12, 0, NULL
,
846 /* TRANSLATORS: To AddrBk is a command that takes the user to
847 the address book screen to select an entry from there. */
848 verify_nick
, NULL
, NULL
, addr_book_nick_for_edit
, N_("To AddrBk"), NULL
, abook_nickname_complete
,
849 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
850 {"Fullname : ", N_("Fullname"), h_composer_abook_full
, 12, 0, NULL
,
851 NULL
, NULL
, NULL
, view_message_for_pico
, N_("To Message"), NULL
, NULL
,
852 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
853 /* TRANSLATORS: a File Copy is a copy of a sent message saved in a regular
854 file on the computer's disk */
855 {"Fcc : ", N_("FileCopy"), h_composer_abook_fcc
, 12, 0, NULL
,
856 /* TRANSLATORS: To Folders */
857 NULL
, NULL
, NULL
, folders_for_fcc
, N_("To Fldrs"), NULL
, NULL
,
858 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
859 {"Comment : ", N_("Comment"), h_composer_abook_comment
, 12, 0, NULL
,
860 NULL
, NULL
, NULL
, view_message_for_pico
, N_("To Message"), NULL
, NULL
,
861 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
862 {"Addresses : ", N_("Addresses"), h_composer_abook_addrs
, 12, 0, NULL
,
863 verify_addr
, NULL
, NULL
, addr_book_change_list
, N_("To AddrBk"), NULL
, abook_nickname_complete
,
864 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
865 {NULL
, NULL
, NO_HELP
, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
866 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
871 #define NNN_COMMENT 3
875 static char *nick_saved_for_pico_check
;
876 static AdrBk
*abook_saved_for_pico_check
;
879 * Args: abook -- Address book handle
880 * abe -- AdrBk_Entry of old entry to work on. If NULL, this will
881 * be a new entry. This has to be a pointer to a copy of
882 * an abe that won't go away until we finish this function.
883 * In other words, don't pass in a pointer to an abe in
884 * the cache, copy it first. The tag on this abe is only
885 * used to decide whether to read abe->addr.list or
886 * abe->addr.addr, not to determine what the final result
887 * will be. That's determined solely by how many addresses
888 * there are after the user edits.
889 * entry -- The entry number of the old entry that we will be changing.
890 * old_tag -- If we're changing an old entry, then this is the tag of
892 * readonly -- Call pico with readonly flag
893 * warped -- We warped to a new part of the addrbook
894 * (We also overload warped in a couple places and use it's
895 * being set as an indicator of whether we are Taking or
896 * not. It will be NULL if we are Taking.)
899 edit_entry(AdrBk
*abook
, AdrBk_Entry
*abe
, a_c_arg_t entry
, Tag old_tag
, int readonly
, int *warped
, char *cmd
)
901 AdrBk_Entry local_abe
;
902 struct headerentry
*he
;
905 adrbk_cntr_t old_entry_num
, new_entry_num
= NO_NEXT
;
906 int rc
= 0, resort_happened
= 0, list_changed
= 0, which_addrbook
;
907 int editor_result
, i
= 0, add
, n_end
, ldap
= 0;
908 char *nick
, *full
, *fcc
, *comment
, *fname
, *pp
;
909 char **orig_addrarray
= NULL
;
910 char **new_addrarray
= NULL
;
911 char *comma_sep_addr
= NULL
;
915 char nickpmt
[100], fullpmt
[100], fccpmt
[100], cmtpmt
[100], addrpmt
[100];
918 SAVE_STATE_S state
; /* For saving state of addrbooks temporarily */
920 dprint((2, "- edit_entry -\n"));
922 old_entry_num
= (adrbk_cntr_t
) entry
;
924 abook_saved_for_pico_check
= abook
;
926 add
= (abe
== NULL
); /* doing add or change? */
928 local_abe
.nickname
= "";
929 local_abe
.fullname
= "";
931 local_abe
.extra
= "";
932 local_abe
.addr
.addr
= "";
933 local_abe
.tag
= NotSet
;
935 old_entry_num
= NO_NEXT
;
941 expander_address
= NULL
;
942 if(abe
->tag
== Single
&&
944 !strncmp(abe
->addr
.addr
,QRUN_LDAP
,LEN_QRL
)){
946 expander_address
= cpystr(abe
->addr
.addr
);
947 removing_double_quotes(expander_address
);
949 else if(abe
->tag
== List
&&
952 !abe
->addr
.list
[1] &&
953 !strncmp(abe
->addr
.list
[0],QRUN_LDAP
,LEN_QRL
)){
955 expander_address
= cpystr(abe
->addr
.list
[0]);
956 removing_double_quotes(expander_address
);
960 standard_picobuf_setup(&pbf
);
961 pbf
.exittest
= pico_sendexit_for_adrbk
;
962 pbf
.canceltest
= warped
? pico_cancel_for_adrbk_edit
963 : pico_cancel_for_adrbk_take
;
964 pbf
.expander
= expand_addrs_for_pico
;
965 pbf
.ctrlr_label
= _("RichView");
966 /* xgettext: c-format */
968 /* TRANSLATORS: screen titles */
969 snprintf(titlebar
, sizeof(titlebar
), _("ADDRESS BOOK (View)"));
971 snprintf(titlebar
, sizeof(titlebar
), _("ADDRESS BOOK (%c%s)"),
972 islower((unsigned char)(*cmd
))
973 ? toupper((unsigned char)*cmd
)
976 pbf
.pine_anchor
= set_titlebar(titlebar
,
977 ps_global
->mail_stream
,
978 ps_global
->context_current
,
979 ps_global
->cur_folder
,ps_global
->msgmap
,
980 0, FolderName
, 0, 0, NULL
);
981 pbf
.pine_flags
|= P_NOBODY
;
983 pbf
.pine_flags
|= P_VIEW
;
985 /* An informational message */
986 if((msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
987 pbf
.msgtext
= (void *)so_text(msgso
);
989 * It's nice if we can make it so these lines make sense even if
990 * they don't all make it on the screen, because the user can't
991 * scroll down to see them. So just make each line a whole sentence
992 * that doesn't need the others below it to make sense.
997 * TRANSLATORS: The following lines go together to form a screen of
998 * explanation about how to edit an address book entry.
1000 _("\n Fill in the fields. It is ok to leave fields blank."));
1002 _("\n To form a list, just enter multiple comma-separated addresses."));
1006 /* TRANSLATORS: Same here, but a different version of the screen. */
1007 _("\n Change any of the fields. It is ok to leave fields blank."));
1010 _("\n Since this entry does a directory lookup you may not edit the address field."));
1013 _("\n Additional comma-separated addresses may be entered in the address field."));
1017 _("\n Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."));
1019 _("\n If you want to use quotation marks inside the Fullname field, it is best"));
1021 _("\n to use single quotation marks; for example: George 'Husky' Washington."));
1024 he
= (struct headerentry
*) fs_get((NNN_END
+1) * sizeof(struct headerentry
));
1025 memset((void *)he
, 0, (NNN_END
+1) * sizeof(struct headerentry
));
1028 abook_indent
= utf8_width(_("Nickname")) + 2;
1030 /* make a copy of each field */
1031 nick
= cpystr(abe
->nickname
? abe
->nickname
: "");
1032 removing_leading_and_trailing_white_space(nick
);
1033 nick_saved_for_pico_check
= cpystr(nick
);
1034 he
[NNN_NICK
] = headents_for_edit
[NNN_NICK
];
1035 he
[NNN_NICK
].realaddr
= &nick
;
1036 utf8_snprintf(nickpmt
, sizeof(nickpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
1037 he
[NNN_NICK
].prompt
= nickpmt
;
1038 he
[NNN_NICK
].prwid
= abook_indent
+2;
1039 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
1040 he
[NNN_NICK
].nickcmpl
= NULL
;
1042 full
= cpystr(abe
->fullname
? abe
->fullname
: "");
1043 removing_leading_and_trailing_white_space(full
);
1044 he
[NNN_FULL
] = headents_for_edit
[NNN_FULL
];
1045 he
[NNN_FULL
].realaddr
= &full
;
1046 utf8_snprintf(fullpmt
, sizeof(fullpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fullname"));
1047 he
[NNN_FULL
].prompt
= fullpmt
;
1048 he
[NNN_FULL
].prwid
= abook_indent
+2;
1050 fcc
= cpystr(abe
->fcc
? abe
->fcc
: "");
1051 removing_leading_and_trailing_white_space(fcc
);
1052 he
[NNN_FCC
] = headents_for_edit
[NNN_FCC
];
1053 he
[NNN_FCC
].realaddr
= &fcc
;
1054 utf8_snprintf(fccpmt
, sizeof(fccpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fcc"));
1055 he
[NNN_FCC
].prompt
= fccpmt
;
1056 he
[NNN_FCC
].prwid
= abook_indent
+2;
1058 comment
= cpystr(abe
->extra
? abe
->extra
: "");
1059 removing_leading_and_trailing_white_space(comment
);
1060 he
[NNN_COMMENT
] = headents_for_edit
[NNN_COMMENT
];
1061 he
[NNN_COMMENT
].realaddr
= &comment
;
1062 utf8_snprintf(cmtpmt
, sizeof(cmtpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Comment"));
1063 he
[NNN_COMMENT
].prompt
= cmtpmt
;
1064 he
[NNN_COMMENT
].prwid
= abook_indent
+2;
1071 he
[NNN_ADDR
] = headents_for_edit
[NNN_ADDR
];
1072 he
[NNN_ADDR
].realaddr
= &comma_sep_addr
;
1073 utf8_snprintf(addrpmt
, sizeof(addrpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
1074 he
[NNN_ADDR
].prompt
= addrpmt
;
1075 he
[NNN_ADDR
].prwid
= abook_indent
+2;
1076 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
1077 he
[NNN_NICK
].nickcmpl
= NULL
;
1079 if(abe
->tag
== Single
){
1081 orig_addrarray
= (char **) fs_get(2 * sizeof(char *));
1082 orig_addrarray
[0] = cpystr(abe
->addr
.addr
);
1083 orig_addrarray
[1] = NULL
;
1086 else if(abe
->tag
== List
){
1087 if(listmem_count_from_abe(abe
) > 0){
1088 orig_addrarray
= (char **) fs_get(
1089 (size_t)(listmem_count_from_abe(abe
) + 1)
1091 for(q
= orig_addrarray
, p
= abe
->addr
.list
; p
&& *p
; p
++, q
++)
1098 /* figure out how large a string we need to allocate */
1100 for(p
= orig_addrarray
; p
&& *p
; p
++)
1101 length
+= (strlen(*p
) + 2);
1106 pp
= comma_sep_addr
= (char *) fs_get((size_t)(length
+1L) * sizeof(char));
1108 for(p
= orig_addrarray
; p
&& *p
; p
++){
1109 sstrncpy(&pp
, *p
, length
-(pp
-comma_sep_addr
));
1111 sstrncpy(&pp
, ", ", length
-(pp
-comma_sep_addr
));
1114 comma_sep_addr
[length
] = '\0';
1116 if(verify_addr(comma_sep_addr
, NULL
, NULL
, NULL
, NULL
) < 0)
1117 he
[NNN_ADDR
].start_here
= 1;
1120 he
[n_end
] = headents_for_edit
[NNN_END
];
1121 for(i
= 0; i
< n_end
; i
++){
1122 /* no callbacks in some cases */
1123 if(readonly
|| ((i
== NNN_FULL
|| i
== NNN_COMMENT
) && !env_for_pico_callback
)){
1124 he
[i
].selector
= NULL
;
1125 he
[i
].key_label
= NULL
;
1128 /* no builders for readonly */
1130 he
[i
].builder
= NULL
;
1133 /* pass to pico and let user change them */
1134 editor_result
= pico(&pbf
);
1135 ps_global
->mangled_screen
= 1;
1136 standard_picobuf_teardown(&pbf
);
1138 if(editor_result
& COMP_GOTHUP
)
1141 fix_windsize(ps_global
);
1145 if(editor_result
& COMP_CANCEL
){
1147 /* TRANSLATOR: Something like
1148 Address book save cancelled */
1149 q_status_message1(SM_INFO
, 0, 2, _("Address book %s cancelled"), cmd
);
1151 else if(editor_result
& COMP_EXIT
){
1152 if(pico_usingcolor())
1153 clear_index_cache(ps_global
->mail_stream
, 0);
1154 removing_leading_and_trailing_white_space(nick
);
1155 removing_leading_and_trailing_white_space(full
);
1156 removing_leading_and_trailing_white_space(fcc
);
1157 removing_leading_and_trailing_white_space(comment
);
1158 removing_leading_and_trailing_white_space(comma_sep_addr
);
1160 /* not needed if pico is returning UTF-8 */
1161 convert_possibly_encoded_str_to_utf8(&nick
);
1162 convert_possibly_encoded_str_to_utf8(&full
);
1163 convert_possibly_encoded_str_to_utf8(&fcc
);
1164 convert_possibly_encoded_str_to_utf8(&comment
);
1165 convert_possibly_encoded_str_to_utf8(&comma_sep_addr
);
1167 /* don't allow adding null entry */
1168 if(add
&& !*nick
&& !*full
&& !*fcc
&& !*comment
&& !*comma_sep_addr
)
1172 * comma_sep_addr is now the string which has been edited
1175 new_addrarray
= parse_addrlist(comma_sep_addr
);
1177 if(!ldap
&& (!new_addrarray
|| !new_addrarray
[0]))
1178 q_status_message(SM_ORDER
, 3, 5, _("Warning: entry has no addresses"));
1180 if(!new_addrarray
|| !new_addrarray
[0] || !new_addrarray
[1])
1181 new_tag
= Single
; /* one or zero addresses means its a Single */
1183 new_tag
= List
; /* more than one addresses means its a List */
1185 if(new_tag
== List
&& old_tag
== List
){
1187 * If Taking, make sure we write it even if user didn't edit
1192 else if(he
[NNN_ADDR
].dirty
)
1193 for(q
= orig_addrarray
, p
= new_addrarray
; p
&& *p
&& q
&& *q
; p
++, q
++)
1194 if(strcmp(*p
, *q
) != 0){
1199 if(!list_changed
&& he
[NNN_ADDR
].dirty
1200 && ((!(p
&& *p
) && (q
&& *q
)) || ((p
&& *p
) && !(q
&& *q
))))
1205 * need to delete old list members and add new members below
1207 rc
= adrbk_listdel_all(abook
, (a_c_arg_t
) old_entry_num
);
1210 /* don't need new_addrarray */
1211 free_list_array(&new_addrarray
);
1215 fs_give((void **) &comma_sep_addr
);
1217 else if((new_tag
== List
&& old_tag
== Single
)
1218 || (new_tag
== Single
&& old_tag
== List
)){
1219 /* delete old entry */
1220 rc
= adrbk_delete(abook
, (a_c_arg_t
) old_entry_num
, 0, 0, 0, 0);
1221 old_entry_num
= NO_NEXT
;
1222 if(comma_sep_addr
&& new_tag
== List
)
1223 fs_give((void **) &comma_sep_addr
);
1227 * This will be an edit in the cases where the tag didn't change
1228 * and an add in the cases where it did.
1231 rc
= adrbk_add(abook
,
1232 (a_c_arg_t
)old_entry_num
,
1234 he
[NNN_FULL
].dirty
? full
: abe
->fullname
,
1235 new_tag
== Single
? (ldap
== 1 ? abe
->addr
.addr
:
1236 ldap
== 2 ? abe
->addr
.list
[0] :
1240 he
[NNN_COMMENT
].dirty
? comment
: abe
->extra
,
1246 (new_tag
!= List
|| !new_addrarray
));
1249 if(rc
== 0 && new_tag
== List
&& new_addrarray
)
1250 rc
= adrbk_nlistadd(abook
, (a_c_arg_t
) new_entry_num
, &new_entry_num
,
1251 &resort_happened
, new_addrarray
, 1, 0, 1);
1253 restore_state(&state
);
1255 if(rc
== -2 || rc
== -3){
1256 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1257 _("Error updating address book: %s"),
1258 rc
== -2 ? error_description(errno
) : "Alpine bug");
1261 && strucmp(nick
, nick_saved_for_pico_check
) != 0
1262 && (editor_result
& COMP_EXIT
)){
1265 for(added_to
= 0; added_to
< as
.n_addrbk
; added_to
++)
1266 if(abook_saved_for_pico_check
== as
.adrbks
[added_to
].address_book
)
1269 if(added_to
>= as
.n_addrbk
)
1272 fname
= addr_lookup(nick
, &which_addrbook
, added_to
);
1274 q_status_message4(SM_ORDER
, 5, 9,
1275 /* TRANSLATORS: The first %s is the nickname the user is
1276 trying to use that exists in another address book.
1277 The second %s is the name of the other address book.
1278 The third %s is " as " because it will say something
1279 like also exists in <name> as <description>. */
1280 _("Warning! Nickname %s also exists in \"%s\"%s%s"),
1281 nick
, as
.adrbks
[which_addrbook
].abnick
,
1282 (fname
&& *fname
) ? _(" as ") : "",
1283 (fname
&& *fname
) ? fname
: "");
1284 fs_give((void **)&fname
);
1288 if(resort_happened
|| list_changed
){
1289 DL_CACHE_S dlc_restart
;
1291 dlc_restart
.adrbk_num
= as
.cur
;
1292 dlc_restart
.dlcelnum
= new_entry_num
;
1295 dlc_restart
.type
= DlcSimple
;
1299 dlc_restart
.type
= DlcListHead
;
1306 warp_to_dlc(&dlc_restart
, 0L);
1319 fs_give((void **)&nick
);
1321 fs_give((void **)&full
);
1323 fs_give((void **)&fcc
);
1325 fs_give((void **)&comment
);
1328 fs_give((void **)&comma_sep_addr
);
1329 if(nick_saved_for_pico_check
)
1330 fs_give((void **)&nick_saved_for_pico_check
);
1332 free_list_array(&orig_addrarray
);
1333 free_list_array(&new_addrarray
);
1335 if(expander_address
)
1336 fs_give((void **) &expander_address
);
1343 verify_nick(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
1347 dprint((7, "- verify_nick - (%s)\n", given
? given
: "nul"));
1349 tmp
= cpystr(given
);
1350 removing_leading_and_trailing_white_space(tmp
);
1352 if(nickname_check(tmp
, error
)){
1353 fs_give((void **)&tmp
);
1355 if(ps_global
->mangled_screen
)
1356 *mangled
|= BUILDER_SCREEN_MANGLED
;
1357 else if(ps_global
->mangled_footer
)
1358 *mangled
|= BUILDER_FOOTER_MANGLED
;
1364 if(ps_global
->remote_abook_validity
> 0 &&
1365 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
1366 *mangled
|= BUILDER_SCREEN_MANGLED
;
1369 if(strucmp(tmp
, nick_saved_for_pico_check
) != 0
1370 && adrbk_lookup_by_nick(abook_saved_for_pico_check
,
1371 tmp
, (adrbk_cntr_t
*)NULL
)){
1373 char buf
[MAX_NICKNAME
+ 80];
1375 /* TRANSLATORS: The %s is the nickname of an entry that is already
1376 in the address book */
1377 snprintf(buf
, sizeof(buf
), _("\"%s\" already in address book."), tmp
);
1378 buf
[sizeof(buf
)-1] = '\0';
1379 *error
= cpystr(buf
);
1383 fs_give((void **)&tmp
);
1385 if(ps_global
->mangled_screen
)
1386 *mangled
|= BUILDER_SCREEN_MANGLED
;
1387 else if(ps_global
->mangled_footer
)
1388 *mangled
|= BUILDER_FOOTER_MANGLED
;
1398 fs_give((void **)&tmp
);
1400 /* This is so pico will erase any old message */
1402 *error
= cpystr("");
1405 if(ps_global
->mangled_screen
)
1406 *mangled
|= BUILDER_SCREEN_MANGLED
;
1407 else if(ps_global
->mangled_footer
)
1408 *mangled
|= BUILDER_FOOTER_MANGLED
;
1416 * Args: to -- the passed in line to parse
1417 * full_to -- Address of a pointer to return the full address in.
1418 * This will be allocated here and freed by the caller.
1419 * However, this function is just going to copy "to".
1420 * (special case for the route-addr-hack in build_address_int)
1421 * We're just looking for the error messages.
1422 * error -- Address of a pointer to return an error message in.
1423 * This will be allocated here and freed by the caller.
1424 * fcc -- This should be passed in NULL.
1425 * This builder doesn't support affected_entry's.
1427 * Result: 0 is returned if address was OK,
1428 * -2 if address wasn't OK.
1430 * Side effect: Can flush addrbook entry cache entries so they need to be
1431 * re-fetched afterwords.
1434 verify_addr(char *to
, char **full_to
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
1439 jmp_buf save_jmp_buf
;
1440 int *save_nesting_level
;
1442 dprint((7, "- verify_addr - (%s)\n", to
? to
: "nul"));
1444 /* check to see if to string is empty to avoid work */
1445 for(p
= to
; p
&& *p
&& isspace((unsigned char)(*p
)); p
++)
1450 *full_to
= cpystr(to
? to
: ""); /* because pico does a strcmp() */
1456 *full_to
= (char *)NULL
;
1459 *error
= (char *)NULL
;
1462 * If we end up jumping back here because somebody else changed one of
1463 * our addrbooks out from underneath us, we may well leak some memory.
1464 * That's probably ok since this will be very rare.
1466 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1467 save_nesting_level
= cpyint(ab_nesting_level
);
1468 if(setjmp(addrbook_changed_unexpectedly
)){
1469 if(full_to
&& *full_to
)
1470 fs_give((void **)full_to
);
1472 /* TRANSLATORS: This is sort of an error, something unexpected has
1473 happened and alpine is re-initializing the address book. */
1474 q_status_message(SM_ORDER
, 3, 5, _("Resetting address book..."));
1476 "RESETTING address book... verify_addr(%s)!\n", to
? to
: "?"));
1478 ab_nesting_level
= *save_nesting_level
;
1484 if(ps_global
->remote_abook_validity
> 0 &&
1485 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
1486 *mangled
|= BUILDER_SCREEN_MANGLED
;
1490 ret_val
= build_address_internal(bldto
, full_to
, error
, NULL
, NULL
, NULL
,
1491 save_and_restore
, 1, mangled
);
1494 if(save_nesting_level
)
1495 fs_give((void **)&save_nesting_level
);
1497 if(full_to
&& *full_to
&& ret_val
>= 0)
1498 removing_leading_and_trailing_white_space(*full_to
);
1500 /* This is so pico will erase the old message */
1501 if(error
!= NULL
&& *error
== NULL
)
1502 *error
= cpystr("");
1505 ret_val
= -2; /* cause pico to stay on same header line */
1507 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1513 * Call back for pico to prompt the user for exit confirmation
1515 * Returns: either NULL if the user accepts exit, or string containing
1516 * reason why the user declined.
1519 pico_sendexit_for_adrbk(struct headerentry
*he
, void (*redraw_pico
)(void),
1520 int allow_flowed
, char **result
)
1523 void (*redraw
)(void) = ps_global
->redrawer
;
1525 ps_global
->redrawer
= redraw_pico
;
1526 fix_windsize(ps_global
);
1528 /* TRANSLATORS: A question */
1529 switch(want_to(_("Exit and save changes "), 'y', 0, NO_HELP
, WT_NORM
)){
1534 rstr
= _("Use ^C to abandon changes you've made");
1541 ps_global
->redrawer
= redraw
;
1542 return((rstr
== NULL
) ? 0 : 1);
1547 * Call back for pico to prompt the user for exit confirmation
1549 * Returns: either NULL if the user accepts exit, or string containing
1550 * reason why the user declined.
1553 pico_cancelexit_for_adrbk(char *word
, void (*redraw_pico
)(void))
1557 void (*redraw
)(void) = ps_global
->redrawer
;
1559 /* TRANSLATORS: A question. The %s is a noun describing what is being cancelled. */
1560 snprintf(prompt
, sizeof(prompt
), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), word
);
1561 ps_global
->redrawer
= redraw_pico
;
1562 fix_windsize(ps_global
);
1564 switch(want_to(prompt
, 'y', 'x', NO_HELP
, WT_NORM
)){
1574 ps_global
->redrawer
= redraw
;
1580 pico_cancel_for_adrbk_take(void (*redraw_pico
)(void))
1582 return(pico_cancelexit_for_adrbk(_("take"), redraw_pico
));
1587 pico_cancel_for_adrbk_edit(void (*redraw_pico
)(void))
1589 return(pico_cancelexit_for_adrbk(_("changes"), redraw_pico
));
1594 prompt::name::help::prwid::maxlen::realaddr::
1595 builder::affected_entry::next_affected::selector::key_label::fileedit::
1596 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
1597 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
1599 static struct headerentry headents_for_add
[]={
1600 {"Server Name : ", N_("Server"), h_composer_abook_add_server
, 14, 0, NULL
,
1601 verify_server_name
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1602 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
1603 {"Folder Name : ", N_("Folder"), h_composer_abook_add_folder
, 14, 0, NULL
,
1604 verify_folder_name
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1605 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
1606 {"NickName : ", N_("Nickname"), h_composer_abook_add_nick
, 14, 0, NULL
,
1607 verify_abook_nick
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1608 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
1609 {NULL
, NULL
, NO_HELP
, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1610 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
1618 * Args: global -- Add a global address book, not personal.
1619 * add_after_this -- This is the addrbook number which should come
1620 * right before the new addrbook we're adding, if
1621 * that makes sense. If this is -1, append to end
1624 * Returns: addrbook number of new addrbook, or
1625 * -1, no addrbook added
1628 ab_add_abook(int global
, int add_after_this
)
1632 dprint((2, "- ab_add_abook -\n"));
1634 ret
= ab_modify_abook_list(0, global
, add_after_this
, NULL
, NULL
, NULL
);
1637 q_status_message(SM_ORDER
, 0, 3,
1638 _("New address book added. Use \"$\" to adjust order"));
1645 * Args: global -- Add a global address book, not personal.
1646 * abook_num -- Abook num of the entry we are editing.
1647 * serv -- Default server.
1648 * folder -- Default folder.
1649 * nick -- Default nickname.
1651 * Returns: abook_num if successful,
1655 ab_edit_abook(int global
, int abook_num
, char *serv
, char *folder
, char *nick
)
1657 dprint((2, "- ab_edit_abook -\n"));
1659 return(ab_modify_abook_list(1, global
, abook_num
, serv
, folder
, nick
));
1662 static int the_one_were_editing
;
1666 * Args: edit -- Edit existing entry
1667 * global -- Add a global address book, not personal.
1668 * abook_num -- This is the addrbook number which should come
1669 * right before the new addrbook we're adding, if
1670 * that makes sense. If this is -1, append to end
1672 * If we are editing instead of adding, this is
1673 * the abook number of the entry we are editing.
1674 * def_serv -- Default server.
1675 * def_fold -- Default folder.
1676 * def_nick -- Default nickname.
1678 * Returns: addrbook number of new addrbook, or
1679 * -1, no addrbook added
1682 ab_modify_abook_list(int edit
, int global
, int abook_num
, char *def_serv
, char *def_fold
, char *def_nick
)
1684 struct headerentry
*he
;
1687 int editor_result
, i
, how_many_in_list
, new_abook_num
, num_in_list
;
1689 char *server
, *folder
, *nickname
;
1690 char *new_item
= NULL
;
1692 AccessType remember_access_result
;
1695 char **list
, **new_list
= NULL
;
1696 char tmp
[1000+MAXFOLDER
];
1698 char servpmt
[100], foldpmt
[100], nickpmt
[100];
1699 struct variable
*vars
= ps_global
->vars
;
1701 dprint((2, "- ab_modify_abook_list -\n"));
1703 if(ps_global
->readonly_pinerc
){
1705 /* TRANSLATORS: Change was the name of the command the user was
1706 trying to perform. It is what was cancelled. */
1707 q_status_message(SM_ORDER
, 0, 3, _("Change cancelled: config file not changeable"));
1709 /* TRANSLATORS: Add was the command that is being cancelled. */
1710 q_status_message(SM_ORDER
, 0, 3, _("Add cancelled: config file not changeable"));
1717 if((global
&& vars
[V_GLOB_ADDRBOOK
].is_fixed
) ||
1718 (!global
&& vars
[V_ADDRESSBOOK
].is_fixed
)){
1720 /* TRANSLATORS: Operation was cancelled because the system management
1721 does not allow the changing of global address books */
1722 q_status_message(SM_ORDER
, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing global address books"));
1724 q_status_message(SM_ORDER
, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing address books"));
1729 init_ab_if_needed();
1733 (abook_num
< 0 || abook_num
>= as
.how_many_personals
)) ||
1735 (abook_num
< as
.how_many_personals
||
1736 abook_num
>= as
.n_addrbk
))){
1737 dprint((1, "Programming botch in ab_modify_abook_list: global=%d abook_num=%d n_addrbk=%d\n", global
, abook_num
, as
.n_addrbk
));
1738 q_status_message(SM_ORDER
, 0, 3, "Programming botch, bad abook_num");
1742 the_one_were_editing
= abook_num
;
1745 the_one_were_editing
= -1;
1747 standard_picobuf_setup(&pbf
);
1748 pbf
.exittest
= pico_sendexit_for_adrbk
;
1749 pbf
.canceltest
= pico_cancel_for_adrbk_edit
;
1751 /* TRANSLATORS: screen title */
1752 strncpy(titlebar
, _("CHANGE ADDRESS BOOK"), sizeof(titlebar
));
1754 /* TRANSLATORS: screen title */
1755 strncpy(titlebar
, _("ADD ADDRESS BOOK"), sizeof(titlebar
));
1757 titlebar
[sizeof(titlebar
)-1] = '\0';
1758 pbf
.pine_anchor
= set_titlebar(titlebar
,
1759 ps_global
->mail_stream
,
1760 ps_global
->context_current
,
1761 ps_global
->cur_folder
,ps_global
->msgmap
,
1762 0, FolderName
, 0, 0, NULL
);
1763 pbf
.pine_flags
|= P_NOBODY
;
1765 /* An informational message */
1766 if((msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1769 /* TRANSLATORS: The next few lines go together to explain how to add
1770 the address book entry the user is working on. */
1771 _(" To add a local address book that will be accessed *only* by Alpine running\n on this machine, leave the server field blank.");
1773 _(" To add an address book that will be accessed by IMAP, fill in the\n server name.");
1775 _(" (NOTE: An address book cannot be accessed by IMAP from\n one Alpine and as a local file from another. It is either always accessed\n by IMAP or it is a local address book which is never accessed by IMAP.)");
1777 _(" In the Folder field, type the remote folder name or local file name.");
1779 _(" In the Nickname field, give the address book a nickname or leave it blank.");
1781 _(" To get help specific to an item, press ^G.");
1783 _(" To exit and save the configuration, press ^X. To cancel, press ^C.");
1785 pbf
.msgtext
= (void *)so_text(msgso
);
1787 * It's nice if we can make it so these lines make sense even if
1788 * they don't all make it on the screen, because the user can't
1789 * scroll down to see them.
1791 * The 3 is the number of fields to be defined, the 1 is for a
1792 * single blank line after the field definitions.
1794 lines_avail
= ps_global
->ttyo
->screen_rows
- HEADER_ROWS(ps_global
) -
1795 FOOTER_ROWS(ps_global
) - 3 - 1;
1797 if(lines_avail
>= 15){ /* extra blank line */
1798 so_puts(msgso
, "\n");
1802 if(lines_avail
>= 2){
1807 if(lines_avail
>= 5){
1808 so_puts(msgso
, "\n\n");
1813 else if(lines_avail
>= 3){
1814 so_puts(msgso
, "\n\n");
1819 if(lines_avail
>= 2){
1820 so_puts(msgso
, "\n\n");
1825 if(lines_avail
>= 2){
1826 so_puts(msgso
, "\n\n");
1831 if(lines_avail
>= 3){
1832 so_puts(msgso
, "\n\n");
1834 so_puts(msgso
, "\n");
1837 else if(lines_avail
>= 2){
1838 so_puts(msgso
, "\n\n");
1843 he
= (struct headerentry
*)fs_get((NN_END
+1) * sizeof(struct headerentry
));
1844 memset((void *)he
, 0, (NN_END
+1) * sizeof(struct headerentry
));
1847 abook_indent
= utf8_width(_("Server Name")) + 2;
1849 /* make a copy of each field */
1850 server
= cpystr(def_serv
? def_serv
: "");
1851 he
[NN_SERVER
] = headents_for_add
[NN_SERVER
];
1852 he
[NN_SERVER
].realaddr
= &server
;
1853 utf8_snprintf(servpmt
, sizeof(servpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Server Name"));
1854 he
[NN_SERVER
].prompt
= servpmt
;
1855 he
[NN_SERVER
].prwid
= abook_indent
+2;
1857 folder
= cpystr(def_fold
? def_fold
: "");
1858 he
[NN_FOLDER
] = headents_for_add
[NN_FOLDER
];
1859 he
[NN_FOLDER
].realaddr
= &folder
;
1860 utf8_snprintf(foldpmt
, sizeof(foldpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Folder Name"));
1861 he
[NN_FOLDER
].prompt
= foldpmt
;
1862 he
[NN_FOLDER
].prwid
= abook_indent
+2;
1864 nickname
= cpystr(def_nick
? def_nick
: "");
1865 he
[NN_NICK
] = headents_for_add
[NN_NICK
];
1866 he
[NN_NICK
].realaddr
= &nickname
;
1867 utf8_snprintf(nickpmt
, sizeof(nickpmt
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
1868 he
[NN_NICK
].prompt
= nickpmt
;
1869 he
[NN_NICK
].prwid
= abook_indent
+2;
1871 he
[NN_END
] = headents_for_add
[NN_END
];
1873 /* pass to pico and let user change them */
1874 editor_result
= pico(&pbf
);
1875 standard_picobuf_teardown(&pbf
);
1877 if(editor_result
& COMP_GOTHUP
){
1882 fix_windsize(ps_global
);
1886 if(editor_result
& COMP_CANCEL
){
1889 q_status_message(SM_ORDER
, 0, 3, _("Address book change is cancelled"));
1891 q_status_message(SM_ORDER
, 0, 3, _("Address book add is cancelled"));
1893 else if(editor_result
& COMP_EXIT
){
1895 !strcmp(server
, def_serv
? def_serv
: "") &&
1896 !strcmp(folder
, def_fold
? def_fold
: "") &&
1897 !strcmp(nickname
, def_nick
? def_nick
: "")){
1900 q_status_message(SM_ORDER
, 0, 3, _("No change: Address book change is cancelled"));
1902 q_status_message(SM_ORDER
, 0, 3, _("No change: Address book add is cancelled"));
1906 list
= VAR_GLOB_ADDRBOOK
;
1907 how_many_in_list
= as
.n_addrbk
- as
.how_many_personals
;
1909 new_abook_num
= abook_num
;
1910 else if(abook_num
< 0)
1911 new_abook_num
= as
.n_addrbk
;
1913 new_abook_num
= MAX(MIN(abook_num
+ 1, as
.n_addrbk
),
1914 as
.how_many_personals
);
1916 num_in_list
= new_abook_num
- as
.how_many_personals
;
1919 list
= VAR_ADDRESSBOOK
;
1920 how_many_in_list
= as
.how_many_personals
;
1921 new_abook_num
= abook_num
;
1923 new_abook_num
= abook_num
;
1924 else if(abook_num
< 0)
1925 new_abook_num
= as
.how_many_personals
;
1927 new_abook_num
= MIN(abook_num
+ 1, as
.how_many_personals
);
1929 num_in_list
= new_abook_num
;
1933 how_many_in_list
++; /* for new abook */
1935 removing_leading_and_trailing_white_space(server
);
1936 removing_leading_and_trailing_white_space(folder
);
1937 removing_leading_and_trailing_white_space(nickname
);
1939 /* convert nickname to UTF-8 */
1943 conv
= convert_to_utf8(nickname
, NULL
, 0);
1945 fs_give((void **) &nickname
);
1950 /* eliminate surrounding brackets */
1951 if(server
[0] == '{' && server
[strlen(server
)-1] == '}'){
1954 server
[strlen(server
)-1] = '\0';
1955 for(p
= server
; *p
; p
++)
1959 snprintf(tmp
, sizeof(tmp
), "%s%s%s%.*s",
1961 *server
? server
: "",
1964 tmp
[sizeof(tmp
)-1] = '\0';
1966 new_item
= put_pair(nickname
, tmp
);
1968 if(!new_item
|| *new_item
== '\0'){
1970 q_status_message(SM_ORDER
, 0, 3, _("Address book change is cancelled"));
1972 q_status_message(SM_ORDER
, 0, 3, _("Address book add is cancelled"));
1978 /* allocate for new list */
1979 new_list
= (char **)fs_get((how_many_in_list
+ 1) * sizeof(char *));
1981 /* copy old list up to where we will insert new entry */
1982 for(i
= 0; i
< num_in_list
; i
++)
1983 new_list
[i
] = cpystr(list
[i
]);
1985 /* insert the new entry */
1986 new_list
[i
++] = cpystr(new_item
);
1988 /* copy rest of old list, skip current if editing */
1989 for(; i
< how_many_in_list
; i
++)
1990 new_list
[i
] = cpystr(list
[edit
? i
: (i
-1)]);
1994 /* this frees old variable contents for us */
1995 if(set_variable_list(global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
,
1996 new_list
, TRUE
, ew
)){
1998 q_status_message(SM_ORDER
, 0, 3, _("Change cancelled: couldn't save configuration file"));
2000 q_status_message(SM_ORDER
, 0, 3, _("Add cancelled: couldn't save configuration file"));
2002 set_current_val(&vars
[global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
],
2008 ret
= new_abook_num
;
2009 set_current_val(&vars
[global
? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
],
2013 init_ab_if_needed();
2016 * Test to see if this definition is going to work.
2017 * Error messages are a good side effect.
2019 pab
= &as
.adrbks
[num_in_list
];
2020 init_abook(pab
, NoDisplay
);
2021 remember_access_result
= pab
->access
;
2023 init_ab_if_needed();
2024 /* if we had trouble, give a clue to user (other than error msg) */
2025 if(remember_access_result
== NoAccess
){
2026 pab
= &as
.adrbks
[num_in_list
];
2027 pab
->access
= remember_access_result
;
2038 free_list_array(&new_list
);
2041 fs_give((void **)&new_item
);
2047 fs_give((void **)&server
);
2049 fs_give((void **)&folder
);
2051 fs_give((void **)&nickname
);
2058 any_addrbooks_to_convert(struct pine
*ps
)
2063 init_ab_if_needed();
2065 for(i
= 0; i
< as
.n_addrbk
; i
++){
2066 pab
= &as
.adrbks
[i
];
2067 if(pab
&& !(pab
->type
& REMOTE_VIA_IMAP
) && !(pab
->type
& GLOBAL
))
2076 convert_addrbooks_to_remote(struct pine
*ps
, char *rem_folder_prefix
, size_t len
)
2079 int i
, count
= 0, ret
= 0;
2081 init_ab_if_needed();
2083 for(i
= 0; i
< as
.n_addrbk
; i
++){
2084 pab
= &as
.adrbks
[i
];
2085 if(pab
&& !(pab
->type
& REMOTE_VIA_IMAP
) && !(pab
->type
& GLOBAL
))
2089 for(i
= 0; ret
!= -1 && i
< as
.n_addrbk
; i
++){
2090 pab
= &as
.adrbks
[i
];
2091 if(pab
&& !(pab
->type
& REMOTE_VIA_IMAP
) && !(pab
->type
& GLOBAL
))
2092 ret
= convert_abook_to_remote(ps
, pab
, rem_folder_prefix
, len
, count
);
2100 * Returns -1 if cancelled, -2 on error, 0 otherwise.
2103 convert_abook_to_remote(struct pine
*ps
, PerAddrBook
*pab
, char *rem_folder_prefix
, size_t len
, int count
)
2105 #define DEF_ABOOK_NAME "remote_addrbook"
2106 char local_file
[MAILTMPLEN
];
2107 char rem_abook
[MAILTMPLEN
+3], prompt
[MAILTMPLEN
], old_nick
[MAILTMPLEN
];
2108 char *p
= NULL
, *err_msg
= NULL
, *q
;
2109 char *serv
= NULL
, *nick
= NULL
, *file
= NULL
, *folder
= NULL
;
2110 int ans
, rc
, offset
, i
, abook_num
= -1, flags
= OE_APPEND_CURRENT
;
2113 snprintf(old_nick
, sizeof(old_nick
), "%s%s%s",
2114 count
> 1 ? " \"" : "",
2115 count
> 1 ? pab
->abnick
: "",
2116 count
> 1 ? "\"" : "");
2117 old_nick
[sizeof(old_nick
)-1] = '\0';
2119 snprintf(prompt
, sizeof(prompt
), _("Convert addressbook%s to a remote addrbook "), old_nick
);
2120 prompt
[sizeof(prompt
)-1] = '\0';
2121 if((ans
=want_to(prompt
, 'y', 'x', h_convert_abook
, WT_NORM
)) != 'y')
2122 return(ans
== 'n' ? 0 : -1);
2124 /* make sure the addrbook has been opened before, so that the file exists */
2125 if(pab
->ostatus
== Closed
|| pab
->ostatus
== HalfOpen
){
2126 (void)init_addrbooks(NoDisplay
, 0, 0, 0);
2127 (void)init_addrbooks(Closed
, 0, 0, 0);
2131 strncpy(local_file
, pab
->filename
, sizeof(local_file
)-1);
2132 local_file
[sizeof(local_file
)-1] = '\0';
2134 p
= strrindex(pab
->filename
, '\\');
2136 p
= strrindex(pab
->filename
, '/');
2140 strncpy(rem_abook
, rem_folder_prefix
, sizeof(rem_abook
)-3);
2142 /* TRANSLATORS: The user is defining an address book which will be
2143 stored on another server. This is called a remote addrbook and
2144 this is a question asking for the name of the server. */
2145 snprintf(prompt
, sizeof(prompt
), _("Name of server to contain remote addrbook : "));
2146 prompt
[sizeof(prompt
)-1] = '\0';
2149 rc
= optionally_enter(rem_abook
, -FOOTER_ROWS(ps
), 0,
2150 sizeof(rem_abook
), prompt
, NULL
,
2152 removing_leading_and_trailing_white_space(rem_abook
);
2154 help
= help
== NO_HELP
? h_convert_pinerc_server
: NO_HELP
;
2157 cmd_cancelled(NULL
);
2163 offset
= strlen(rem_abook
);
2164 for(i
= offset
; i
>= 0; i
--)
2165 rem_abook
[i
+1] = rem_abook
[i
];
2168 rem_abook
[++offset
] = '}';
2169 rem_abook
[++offset
] = '\0';
2178 strncat(rem_abook
, p
+1,
2179 sizeof(rem_abook
)-1-strlen(rem_abook
));
2181 strncat(rem_abook
, DEF_ABOOK_NAME
,
2182 sizeof(rem_abook
)-1-strlen(rem_abook
));
2186 file
= cpystr(rem_abook
);
2188 nick
= (char *)fs_get((MAX(strlen(pab
->abnick
),strlen("Address Book"))+8) * sizeof(char));
2189 snprintf(nick
, sizeof(nick
), "Remote %s",
2190 (pab
->abnick
&& !strcmp(pab
->abnick
, DF_ADDRESSBOOK
))
2191 ? "Address Book" : pab
->abnick
);
2192 nick
[sizeof(nick
)-1] = '\0';
2195 nick
= cpystr("Remote Address Book");
2197 if(file
&& *file
== '{'){
2199 if((p
= strindex(file
, '}'))){
2205 fs_give((void **)&file
);
2211 q_status_message(SM_ORDER
, 3, 5,
2212 _("You now have a chance to change the name of the remote addrbook..."));
2213 abook_num
= ab_modify_abook_list(0, 0, -1, serv
, folder
, nick
);
2215 /* extract folder name of new abook so we can copy to it */
2218 EditWhich ew
= Main
;
2220 lval
= LVAL(&ps
->vars
[V_ADDRESSBOOK
], ew
);
2221 get_pair(lval
[abook_num
], &nick
, &file
, 0, 0);
2223 fs_give((void **)&nick
);
2226 strncpy(rem_abook
, file
, sizeof(rem_abook
)-1);
2227 rem_abook
[sizeof(rem_abook
)-1] = '\0';
2228 fs_give((void **)&file
);
2232 /* copy the abook */
2233 if(abook_num
>= 0 && copy_abook(local_file
, rem_abook
, &err_msg
)){
2235 q_status_message(SM_ORDER
| SM_DING
, 7, 10, err_msg
);
2236 fs_give((void **)&err_msg
);
2241 else if(abook_num
>= 0){ /* give user some info */
2247 * Save the hostname in rem_folder_prefix so we can use it again
2248 * for other conversions if needed.
2250 if((beg
= rem_abook
)
2251 && (*beg
== '{' || (*beg
== '*' && *++beg
== '{'))
2252 && (end
= strindex(rem_abook
, '}'))){
2253 rem_folder_prefix
[0] = '{';
2254 strncpy(rem_folder_prefix
+1, beg
+1, MIN(end
-beg
,len
-2));
2255 rem_folder_prefix
[MIN(end
-beg
,len
-2)] = '}';
2256 rem_folder_prefix
[MIN(end
-beg
+1,len
-1)] = '\0';
2259 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2260 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2261 _("Error allocating space for message."));
2265 /* TRANSLATORS: Several lines in a row here that go together. */
2266 snprintf(prompt
, sizeof(prompt
), _("\nYour addressbook%s has been copied to the"), old_nick
);
2267 prompt
[sizeof(prompt
)-1] = '\0';
2268 so_puts(store
, prompt
);
2269 so_puts(store
, _("\nremote folder \""));
2270 so_puts(store
, rem_abook
);
2271 so_puts(store
, "\".");
2272 so_puts(store
, _("\nA definition for this remote address book has been added to your list"));
2273 so_puts(store
, _("\nof address books. The definition for the address book it was copied"));
2274 so_puts(store
, _("\nfrom is also still there. You may want to remove that after you"));
2275 so_puts(store
, _("\nare confident that the new address book is complete and working."));
2276 so_puts(store
, _("\nUse the Setup/AddressBooks command to do that.\n"));
2278 memset(&sargs
, 0, sizeof(SCROLL_S
));
2279 sargs
.text
.text
= so_text(store
);
2280 sargs
.text
.src
= CharStar
;
2281 sargs
.text
.desc
= _("Remote Address Book Information");
2282 /* TRANSLATORS: a screen title */
2283 sargs
.bar
.title
= _("ABOUT REMOTE ABOOK");
2284 sargs
.help
.text
= NO_HELP
;
2285 sargs
.help
.title
= NULL
;
2289 so_give(&store
); /* free resources associated with store */
2290 ps
->mangled_screen
= 1;
2298 any_sigs_to_convert(struct pine
*ps
)
2300 char *sigfile
, *litsig
;
2304 PAT_LINE_S
*patline
;
2306 /* first check main signature file */
2307 sigfile
= ps
->VAR_SIGNATURE_FILE
;
2308 litsig
= ps
->VAR_LITERAL_SIG
;
2310 if(sigfile
&& *sigfile
&& !litsig
&& sigfile
[strlen(sigfile
)-1] != '|' &&
2311 !IS_REMOTE(sigfile
))
2314 rflags
= (ROLE_DO_ROLES
| PAT_USE_MAIN
);
2315 if(any_patterns(rflags
, &pstate
)){
2316 set_pathandle(rflags
);
2317 for(patline
= *cur_pat_h
? (*cur_pat_h
)->patlinehead
: NULL
;
2318 patline
; patline
= patline
->next
){
2319 for(pat
= patline
->first
; pat
; pat
= pat
->next
){
2322 * See detoken() for when a sig file is used with a role.
2324 sigfile
= pat
->action
? pat
->action
->sig
: NULL
;
2325 litsig
= pat
->action
? pat
->action
->litsig
: NULL
;
2327 if(sigfile
&& *sigfile
&& !litsig
&&
2328 sigfile
[strlen(sigfile
)-1] != '|' &&
2329 !IS_REMOTE(sigfile
))
2340 any_rule_files_to_warn_about(struct pine
*ps
)
2346 rflags
= (ROLE_DO_ROLES
| ROLE_DO_INCOLS
| ROLE_DO_SCORES
|
2347 ROLE_DO_FILTER
| ROLE_DO_OTHER
| ROLE_DO_SRCH
| PAT_USE_MAIN
);
2348 if(any_patterns(rflags
, &pstate
)){
2349 for(pat
= first_pattern(&pstate
);
2351 pat
= next_pattern(&pstate
)){
2352 if(pat
->patline
&& pat
->patline
->type
== File
)
2365 convert_sigs_to_literal(struct pine
*ps
, int interactive
)
2367 EditWhich ew
= Main
;
2368 char *sigfile
, *litsig
, *cstring_version
, *nick
, *src
= NULL
;
2369 char prompt
[MAILTMPLEN
];
2376 PAT_LINE_S
*patline
;
2378 /* first check main signature file */
2379 sigfile
= ps
->VAR_SIGNATURE_FILE
;
2380 litsig
= ps
->VAR_LITERAL_SIG
;
2382 if(sigfile
&& *sigfile
&& !litsig
&& sigfile
[strlen(sigfile
)-1] != '|' &&
2383 !IS_REMOTE(sigfile
)){
2385 snprintf(prompt
,sizeof(prompt
),
2386 /* TRANSLATORS: A literal sig is a way to store a signature
2387 in alpine. It isn't a very descriptive name. Instead of
2388 storing it in its own file it is stored in the configuration
2390 _("Convert signature file \"%s\" to a literal sig "),
2392 prompt
[sizeof(prompt
)-1] = '\0';
2394 ps
->mangled_body
= 1;
2395 if((ans
=want_to(prompt
, 'y', 'x', h_convert_sig
, WT_NORM
)) == 'x'){
2396 cmd_cancelled(NULL
);
2403 if(ans
== 'y' && (src
= get_signature_file(sigfile
, 0, 0, 0)) != NULL
){
2404 cstring_version
= string_to_cstring(src
);
2405 set_variable(V_LITERAL_SIG
, cstring_version
, 0, 0, ew
);
2408 fs_give((void **)&cstring_version
);
2410 fs_give((void **)&src
);
2413 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2414 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2415 _("Error allocating space for message."));
2419 snprintf(prompt
, sizeof(prompt
),
2420 /* TRANSLATORS: following lines go together */
2421 _("\nYour signature file \"%s\" has been converted"), sigfile
);
2422 prompt
[sizeof(prompt
)-1] = '\0';
2423 so_puts(store
, prompt
);
2425 _("\nto a literal signature, which means it is contained in your"));
2427 _("\nAlpine configuration instead of being in a file of its own."));
2429 _("\nIf that configuration is copied to a remote folder then the"));
2431 _("\nsignature will be available remotely also."));
2433 _("\nChanges to the signature file itself will no longer have any"));
2435 _("\neffect on Alpine but you may still edit the signature with the"));
2437 _("\nSetup/Signature command.\n"));
2439 memset(&sargs
, 0, sizeof(SCROLL_S
));
2440 sargs
.text
.text
= so_text(store
);
2441 sargs
.text
.src
= CharStar
;
2442 sargs
.text
.desc
= _("Literal Signature Information");
2443 /* TRANSLATORS: screen title */
2444 sargs
.bar
.title
= _("ABOUT LITERAL SIG");
2445 sargs
.help
.text
= NO_HELP
;
2446 sargs
.help
.title
= NULL
;
2451 ps
->mangled_screen
= 1;
2456 rflags
= (ROLE_DO_ROLES
| PAT_USE_MAIN
);
2457 if(any_patterns(rflags
, &pstate
)){
2458 set_pathandle(rflags
);
2459 for(patline
= *cur_pat_h
? (*cur_pat_h
)->patlinehead
: NULL
;
2460 patline
; patline
= patline
->next
){
2461 for(pat
= patline
->first
; pat
; pat
= pat
->next
){
2464 * See detoken() for when a sig file is used with a role.
2466 sigfile
= pat
->action
? pat
->action
->sig
: NULL
;
2467 litsig
= pat
->action
? pat
->action
->litsig
: NULL
;
2468 nick
= (pat
->action
&& pat
->action
->nick
&& pat
->action
->nick
[0]) ? pat
->action
->nick
: NULL
;
2470 if(sigfile
&& *sigfile
&& !litsig
&&
2471 sigfile
[strlen(sigfile
)-1] != '|' &&
2472 !IS_REMOTE(sigfile
)){
2474 snprintf(prompt
,sizeof(prompt
),
2475 /* TRANSLATORS: asking whether a signature file should be converted to what
2476 we call a literal signature, which is one contained in the regular
2477 configuration file. Think of the set of 4 %s arguments as a
2478 single argument which is the name of the signature file. */
2479 _("Convert signature file \"%s\"%s%s%s to a literal sig "),
2481 nick
? " in role \"" : "",
2484 prompt
[sizeof(prompt
)-1] = '\0';
2486 ps
->mangled_body
= 1;
2487 if((ans
=want_to(prompt
, 'y', 'x',
2488 h_convert_sig
, WT_NORM
)) == 'x'){
2489 cmd_cancelled(NULL
);
2497 (src
= get_signature_file(sigfile
,0,0,0)) != NULL
){
2499 cstring_version
= string_to_cstring(src
);
2501 if(pat
->action
->litsig
)
2502 fs_give((void **)&pat
->action
->litsig
);
2504 pat
->action
->litsig
= cstring_version
;
2505 fs_give((void **)&src
);
2507 set_pathandle(rflags
);
2508 if(patline
->type
== Literal
)
2509 (*cur_pat_h
)->dirtypinerc
= 1;
2513 if(write_patterns(rflags
) == 0){
2516 * Flush out current_vals of anything we've
2519 close_patterns(ROLE_DO_ROLES
| PAT_USE_CURRENT
);
2521 if(!(store
=so_get(CharStar
,NULL
,EDIT_ACCESS
))){
2522 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2523 _("Error allocating space for message."));
2527 snprintf(prompt
, sizeof(prompt
),
2528 /* TRANSLATORS: Keep the %s's together, they are sort of
2529 the name of the file. */
2530 _("Your signature file \"%s\"%s%s%s has been converted"),
2532 nick
? " in role \"" : "",
2535 prompt
[sizeof(prompt
)-1] = '\0';
2536 so_puts(store
, prompt
);
2538 /* TRANSLATORS: several lines that go together */
2539 _("\nto a literal signature, which means it is contained in your"));
2541 _("\nAlpine configuration instead of being in a file of its own."));
2543 _("\nIf that configuration is copied to a remote folder then the"));
2545 _("\nsignature will be available remotely also."));
2547 _("\nChanges to the signature file itself will no longer have any"));
2549 _("\neffect on Alpine. You may edit the signature with the"));
2551 _("\nSetup/Rules/Roles command.\n"));
2553 memset(&sargs
, 0, sizeof(SCROLL_S
));
2554 sargs
.text
.text
= so_text(store
);
2555 sargs
.text
.src
= CharStar
;
2557 _("Literal Signature Information");
2558 /* TRANSLATORS: a screen title */
2559 sargs
.bar
.title
= _("ABOUT LITERAL SIG");
2560 sargs
.help
.text
= NO_HELP
;
2561 sargs
.help
.title
= NULL
;
2566 ps
->mangled_screen
= 1;
2569 else if(interactive
){
2570 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2571 /* TRANSLATORS: config is an abbreviation for configuration */
2572 _("Error writing rules config."));
2575 /* TRANSLATORS: sig is signature */
2576 fprintf(stderr
, _("Error converting role sig\n"));
2590 warn_about_rule_files(struct pine
*ps
)
2595 if(any_rule_files_to_warn_about(ps
)){
2596 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2597 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2598 _("Error allocating space for message."));
2602 /* TRANSLATORS: several lines that go together */
2603 so_puts(store
, _("\nSome of your Rules are contained in Rule files instead of being directly"));
2604 so_puts(store
, _("\ncontained in your Alpine configuration file. To make those rules"));
2605 so_puts(store
, _("\navailable remotely you will need to move them out of the files."));
2606 so_puts(store
, _("\nThat can be done using the Shuffle command in the appropriate"));
2607 so_puts(store
, _("\nSetup/Rules subcommands.\n"));
2609 memset(&sargs
, 0, sizeof(SCROLL_S
));
2610 sargs
.text
.text
= so_text(store
);
2611 sargs
.text
.src
= CharStar
;
2612 sargs
.text
.desc
= _("Rule Files Information");
2613 /* TRANSLATORS: a screen title */
2614 sargs
.bar
.title
= _("ABOUT RULE FILES");
2615 sargs
.help
.text
= NO_HELP
;
2616 sargs
.help
.title
= NULL
;
2621 ps
->mangled_screen
= 1;
2627 convert_to_remote_config(struct pine
*ps
, int edit_exceptions
)
2629 char rem_pinerc_prefix
[MAILTMPLEN
];
2634 if(edit_exceptions
){
2635 /* TRANSLATORS: The exceptions command (X) was typed but it doesn't make sense */
2636 q_status_message(SM_ORDER
, 3, 5,
2637 _("eXceptions does not make sense with this command"));
2642 alpine_panic("NULL prc in convert_to_remote_config");
2644 dprint((2, "convert_to_remote_config\n"));
2646 if(ps
->prc
->type
== RemImap
){ /* pinerc is already remote */
2647 char prompt
[MAILTMPLEN
];
2650 * Check to see if there is anything at all to do. If there are
2651 * address books to convert, sigfiles to convert, or rule files
2652 * to comment on, we have something to do. Otherwise, just bail.
2654 abooks
= any_addrbooks_to_convert(ps
);
2655 sigs
= any_sigs_to_convert(ps
);
2659 /* TRANSLATORS: AddressBooks is Address Books */
2660 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert AddressBooks and signature files "));
2662 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert AddressBooks "));
2664 snprintf(prompt
, sizeof(prompt
), _("Config is already remote, convert signature files "));
2666 prompt
[sizeof(prompt
)-1] = '\0';
2667 if(want_to(prompt
, 'y', 'x',
2668 (abooks
&& sigs
) ? h_convert_abooks_and_sigs
:
2669 abooks
? h_convert_abooks
:
2670 sigs
? h_convert_sigs
: NO_HELP
,
2672 cmd_cancelled(NULL
);
2679 * Figure out a good default for where to put the remote config.
2680 * If the default collection is remote we'll take the hostname and
2681 * and modifiers from there. If not, we'll try to get the hostname from
2682 * the inbox-path. In either case, we use the home directory on the
2683 * server, not the directory where the folder collection is (if different).
2684 * If we don't have a clue, we'll ask user.
2686 if((context
= default_save_context(ps
->context_list
)) != NULL
&&
2687 IS_REMOTE(context_apply(rem_pinerc_prefix
, context
, "",
2688 sizeof(rem_pinerc_prefix
)))){
2689 /* just use the host from the default collection, not the whole path */
2690 if((end
= strrindex(rem_pinerc_prefix
, '}')) != NULL
)
2694 /* use host from inbox path */
2695 rem_pinerc_prefix
[0] = '\0';
2696 if((beg
= ps
->VAR_INBOX_PATH
)
2697 && (*beg
== '{' || (*beg
== '*' && *++beg
== '{'))
2698 && (end
= strindex(ps
->VAR_INBOX_PATH
, '}'))){
2699 rem_pinerc_prefix
[0] = '{';
2700 strncpy(rem_pinerc_prefix
+1, beg
+1,
2701 MIN(end
-beg
, sizeof(rem_pinerc_prefix
)-2));
2702 rem_pinerc_prefix
[MIN(end
-beg
, sizeof(rem_pinerc_prefix
)-2)] = '}';
2703 rem_pinerc_prefix
[MIN(end
-beg
+1,sizeof(rem_pinerc_prefix
)-1)]='\0';
2707 /* ask about converting addrbooks to remote abooks */
2708 if(ps
->prc
->type
!= RemImap
|| abooks
)
2709 if(convert_addrbooks_to_remote(ps
, rem_pinerc_prefix
,
2710 sizeof(rem_pinerc_prefix
)) == -1){
2711 cmd_cancelled(NULL
);
2715 /* ask about converting sigfiles to literal sigs */
2716 if(ps
->prc
->type
!= RemImap
|| sigs
)
2717 if(convert_sigs_to_literal(ps
, 1) == -1){
2718 cmd_cancelled(NULL
);
2722 warn_about_rule_files(ps
);
2724 /* finally, copy the config file */
2725 if(ps
->prc
->type
== Loc
)
2726 convert_pinerc_to_remote(ps
, rem_pinerc_prefix
);
2727 else if(!(abooks
|| sigs
))
2728 q_status_message(SM_ORDER
, 3, 5,
2729 _("Cannot copy config file since it is already remote."));
2734 convert_pinerc_to_remote(struct pine
*ps
, char *rem_pinerc_prefix
)
2736 #define DEF_FOLDER_NAME "remote_pinerc"
2737 char prompt
[MAILTMPLEN
], rem_pinerc
[MAILTMPLEN
];
2738 char *err_msg
= NULL
;
2741 int flags
= OE_APPEND_CURRENT
;
2744 ps
->mangled_body
= 1;
2745 strncpy(rem_pinerc
, rem_pinerc_prefix
, sizeof(rem_pinerc
)-1);
2746 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2748 if(*rem_pinerc
== '\0'){
2749 snprintf(prompt
, sizeof(prompt
), _("Name of server to contain remote Alpine config : "));
2750 prompt
[sizeof(prompt
)-1] = '\0';
2753 rc
= optionally_enter(rem_pinerc
, -FOOTER_ROWS(ps
), 0,
2754 sizeof(rem_pinerc
), prompt
, NULL
,
2756 removing_leading_and_trailing_white_space(rem_pinerc
);
2758 help
= help
== NO_HELP
? h_convert_pinerc_server
: NO_HELP
;
2761 cmd_cancelled(NULL
);
2767 offset
= strlen(rem_pinerc
);
2768 for(i
= offset
; i
>= 0; i
--)
2769 if(i
+1 < sizeof(rem_pinerc
))
2770 rem_pinerc
[i
+1] = rem_pinerc
[i
];
2772 rem_pinerc
[0] = '{';
2773 if(offset
+2 < sizeof(rem_pinerc
)){
2774 rem_pinerc
[++offset
] = '}';
2775 rem_pinerc
[++offset
] = '\0';
2784 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2787 * Add a default folder name.
2791 * Add /user= to modify hostname so that user won't be asked who they
2792 * are each time they login.
2794 if(!strstr(rem_pinerc
, "/user=") && ps
->VAR_USER_ID
&&
2795 ps
->VAR_USER_ID
[0]){
2798 p
= rem_pinerc
+ strlen(rem_pinerc
) - 1;
2799 if(*p
== '}') /* this should be the case */
2800 snprintf(p
, sizeof(rem_pinerc
)-(p
-rem_pinerc
), "/user=\"%s\"}", ps
->VAR_USER_ID
);
2802 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2805 strncat(rem_pinerc
, DEF_FOLDER_NAME
,
2806 sizeof(rem_pinerc
) - strlen(rem_pinerc
) - 1);
2807 rem_pinerc
[sizeof(rem_pinerc
)-1] = '\0';
2810 /* ask user about folder name for remote config */
2811 snprintf(prompt
, sizeof(prompt
), _("Folder to contain remote config : "));
2812 prompt
[sizeof(prompt
)-1] = '\0';
2815 rc
= optionally_enter(rem_pinerc
, -FOOTER_ROWS(ps
), 0,
2816 sizeof(rem_pinerc
), prompt
, NULL
, help
, &flags
);
2817 removing_leading_and_trailing_white_space(rem_pinerc
);
2818 if(rc
== 0 && *rem_pinerc
){
2823 help
= (help
== NO_HELP
) ? h_convert_pinerc_folder
: NO_HELP
;
2825 else if(rc
== 1 || rem_pinerc
[0] == '\0'){
2826 cmd_cancelled(NULL
);
2833 * If we are on a Unix system, writing to a remote config, we want the
2834 * remote config to work smoothly from a PC, too. If we don't have a
2835 * user-id on the PC then we will be asked for our password.
2836 * So add user-id to the pinerc before we copy it.
2838 if(!ps
->vars
[V_USER_ID
].main_user_val
.p
&& ps
->VAR_USER_ID
)
2839 ps
->vars
[V_USER_ID
].main_user_val
.p
= cpystr(ps
->VAR_USER_ID
);
2841 ps
->vars
[V_USER_ID
].is_used
= 1; /* so it will write to pinerc */
2842 ps
->prc
->outstanding_pinerc_changes
= 1;
2845 if(ps
->prc
->outstanding_pinerc_changes
)
2846 write_pinerc(ps
, Main
, WRP_NONE
);
2849 ps
->vars
[V_USER_ID
].is_used
= 0;
2852 /* copy the pinerc */
2853 if(copy_pinerc(ps
->prc
->name
, rem_pinerc
, &err_msg
)){
2855 q_status_message(SM_ORDER
| SM_DING
, 7, 10, err_msg
);
2856 fs_give((void **)&err_msg
);
2862 /* tell user about command line flags */
2863 if(ps
->prc
->type
!= RemImap
){
2867 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2868 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
2869 _("Error allocating space for message."));
2873 /* TRANSLATORS: several lines that go together */
2874 so_puts(store
, _("\nYou may want to save a copy of this information!"));
2875 so_puts(store
, _("\n\nYour Alpine configuration data has been copied to"));
2876 so_puts(store
, "\n\n ");
2877 so_puts(store
, rem_pinerc
);
2878 so_puts(store
, "\n");
2879 so_puts(store
, _("\nTo use that remote configuration from this computer you will"));
2880 so_puts(store
, _("\nhave to change the way you start Alpine by using the command line option"));
2881 so_puts(store
, _("\n\"alpine -p <remote_folder>\". The command should probably be"));
2883 so_puts(store
, "\n\n ");
2884 so_puts(store
, "alpine -p ");
2885 so_puts(store
, rem_pinerc
);
2886 so_puts(store
, "\n");
2887 so_puts(store
, _("\nWith PC-Alpine, you may want to create a shortcut which"));
2888 so_puts(store
, _("\nhas the required arguments."));
2890 so_puts(store
, "\n\n ");
2891 so_puts(store
, "alpine -p \"");
2892 so_puts(store
, rem_pinerc
);
2893 so_puts(store
, "\"\n");
2894 so_puts(store
, _("\nThe quotes are there around the last argument to protect the special"));
2895 so_puts(store
, _("\ncharacters in the folder name (like braces) from the command shell"));
2896 so_puts(store
, _("\nyou use. If you are not running Alpine from a command shell which knows"));
2897 so_puts(store
, _("\nabout quoting, it is possible you will have to remove those quotes"));
2898 so_puts(store
, _("\nfrom the command. For example, if you also use PC-Alpine you will probably"));
2899 so_puts(store
, _("\nwant to create a shortcut, and you would not need the quotes there."));
2900 so_puts(store
, _("\nWithout the quotes, the command might look like"));
2901 so_puts(store
, "\n\n ");
2902 so_puts(store
, "alpine -p ");
2903 so_puts(store
, rem_pinerc
);
2904 so_puts(store
, "\n");
2905 so_puts(store
, _("\nConsider creating an alias or shell script to execute this command to make"));
2906 so_puts(store
, _("\nit more convenient."));
2908 so_puts(store
, _("\n\nIf you want to use your new remote configuration for this session, quit"));
2909 so_puts(store
, _("\nAlpine now and restart with the changed command line options mentioned above.\n"));
2911 memset(&sargs
, 0, sizeof(SCROLL_S
));
2912 sargs
.text
.text
= so_text(store
);
2913 sargs
.text
.src
= CharStar
;
2914 sargs
.text
.desc
= _("Remote Config Information");
2915 /* TRANSLATORS: a screen title */
2916 sargs
.bar
.title
= _("ABOUT REMOTE CONFIG");
2917 sargs
.help
.text
= NO_HELP
;
2918 sargs
.help
.title
= NULL
;
2922 so_give(&store
); /* free resources associated with store */
2923 ps
->mangled_screen
= 1;
2929 verify_folder_name(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2933 tmp
= cpystr(given
? given
: "");
2934 removing_leading_and_trailing_white_space(tmp
);
2939 fs_give((void **)&tmp
);
2942 *error
= cpystr("");
2949 verify_server_name(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2953 tmp
= cpystr(given
? given
: "");
2954 removing_leading_and_trailing_white_space(tmp
);
2958 * could try to verify the hostname here
2965 fs_give((void **)&tmp
);
2968 *error
= cpystr("");
2975 verify_abook_nick(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
2980 tmp
= cpystr(given
? given
: "");
2981 removing_leading_and_trailing_white_space(tmp
);
2983 if(strindex(tmp
, '"')){
2984 fs_give((void **)&tmp
);
2986 /* TRANSLATORS: Double quote refers to the " character */
2987 *error
= cpystr(_("Double quote not allowed in nickname"));
2992 for(i
= 0; i
< as
.n_addrbk
; i
++)
2993 if(i
!= the_one_were_editing
&& !strcmp(tmp
, as
.adrbks
[i
].abnick
))
2996 if(i
< as
.n_addrbk
){
2997 fs_give((void **)&tmp
);
3000 *error
= cpystr(_("Nickname is already being used"));
3008 fs_give((void **)&tmp
);
3011 *error
= cpystr("");
3018 * Delete an addressbook.
3020 * Args: cur_line -- The current line position (in global display list)
3022 * command_line -- The screen line on which to prompt
3023 * err -- Points to error message
3025 * Returns -- 0, deleted addrbook
3026 * -1, addrbook not deleted
3029 ab_del_abook(long int cur_line
, int command_line
, char **err
)
3031 int abook_num
, varnum
, delete_data
= 0,
3032 num_in_list
, how_many_in_list
, i
, cnt
, warn_about_revert
= 0;
3033 char **list
, **new_list
, **t
, **lval
;
3036 struct variable
*vars
= ps_global
->vars
;
3042 DontChange
} modify_config
;
3044 /* restrict address book config to normal config file */
3047 if(ps_global
->readonly_pinerc
){
3049 *err
= _("Delete cancelled: config file not changeable");
3054 abook_num
= adrbk_num_from_lineno(cur_line
);
3056 pab
= &as
.adrbks
[abook_num
];
3058 dprint((2, "- ab_del_abook(%s) -\n",
3059 pab
->abnick
? pab
->abnick
: "?"));
3061 varnum
= (pab
->type
& GLOBAL
) ? V_GLOB_ADDRBOOK
: V_ADDRESSBOOK
;
3063 if(vars
[varnum
].is_fixed
){
3065 if(pab
->type
& GLOBAL
)
3067 _("Cancelled: Sys. Mgmt. does not allow changing global address book config");
3070 _("Cancelled: Sys. Mgmt. does not allow changing address book config");
3077 * Deal with reverting to default values of the address book
3078 * variables, or with user deleting a default value.
3080 modify_config
= NotSet
;
3082 /* First count how many address books are in the user's config. */
3084 lval
= LVAL(&vars
[varnum
], ew
);
3086 for(t
= lval
; *t
!= NULL
; t
++)
3090 * Easy case, we can just delete one from the user's list.
3093 modify_config
= Modify
;
3096 * Also easy. We'll revert to the default if it exists, and warn
3097 * the user about that.
3100 modify_config
= RevertToDefault
;
3101 /* see if there's a default to revert to */
3103 if(vars
[varnum
].global_val
.l
&& vars
[varnum
].global_val
.l
[0])
3104 for(t
= vars
[varnum
].global_val
.l
; *t
!= NULL
; t
++)
3107 warn_about_revert
= cnt
;
3110 * User is already using the default. Split it into two cases. If there
3111 * is one address book in default ask user if they want to delete that
3112 * default from their config. If there is more than one, ask them if
3113 * they want to ignore all the defaults or just delete this one.
3116 /* count how many in default */
3118 if(vars
[varnum
].global_val
.l
&& vars
[varnum
].global_val
.l
[0])
3119 for(t
= vars
[varnum
].global_val
.l
; *t
!= NULL
; t
++)
3123 static ESCKEY_S opts
[] = {
3124 /* TRANSLATORS: Ignore All means ignore all of the default values,
3125 and Remove One means just remove this one default value. */
3126 {'i', 'i', "I", N_("Ignore All")},
3127 {'r', 'r', "R", N_("Remove One")},
3128 {-1, 0, NULL
, NULL
}};
3130 snprintf(tmp
, sizeof(tmp
),
3131 /* TRANSLATORS: %s is an adjective modifying address books */
3132 _("Ignore all default %s address books or just remove this one ? "),
3133 /* TRANSLATORS: global or personal address books */
3134 pab
->type
& GLOBAL
? _("global") : _("personal"));
3135 tmp
[sizeof(tmp
)-1] = '\0';
3136 switch(radio_buttons(tmp
, command_line
, opts
, 'i', 'x',
3137 h_ab_del_ignore
, RB_NORM
)){
3139 modify_config
= OverRideDefault
;
3143 modify_config
= Modify
;
3148 *err
= _("Delete cancelled");
3154 /* TRANSLATORS: a question */
3155 switch(want_to(_("Delete this default address book from config "),
3156 'n', 'x', h_ab_del_default
, WT_NORM
)){
3160 *err
= _("Delete cancelled");
3165 modify_config
= OverRideDefault
;
3172 * ReadWrite means it exists and MaybeRorW means it is remote and we
3173 * haven't selected it yet to know our access permissions. The remote
3174 * folder should have been created, though, unless we didn't even have
3175 * permissions for that, in which case we got some error messages earlier.
3177 if(pab
->access
== ReadWrite
|| pab
->access
== MaybeRorW
){
3178 static ESCKEY_S o
[] = {
3179 /* TRANSLATORS: user is asked whether to remove just data files, just configuration,
3180 or both for an address book. */
3181 {'d', 'd', "D", N_("Data")},
3182 {'c', 'c', "C", N_("Config")},
3183 {'b', 'b', "B", N_("Both")},
3184 {-1, 0, NULL
, NULL
}};
3186 switch(radio_buttons(_("Delete data, config, or both ? "),
3187 command_line
, o
, 'c', 'x',
3188 (modify_config
== RevertToDefault
)
3189 ? h_ab_del_data_revert
3190 : h_ab_del_data_modify
,
3192 case 'b': /* Delete Both */
3196 case 'd': /* Delete only Data */
3197 modify_config
= DontChange
;
3201 case 'c': /* Delete only Config */
3204 case 'x': /* Cancel */
3207 *err
= _("Delete cancelled");
3214 * Deleting config for address book which doesn't yet exist (hasn't
3215 * ever been opened).
3217 /* TRANSLATORS: a question */
3218 switch(want_to(_("Delete configuration for highlighted addressbook "),
3220 (modify_config
== RevertToDefault
)
3221 ? h_ab_del_config_revert
3222 : h_ab_del_config_modify
,
3228 *err
= _("Delete cancelled");
3240 dprint((5, "deleting addrbook data\n"));
3244 * In order to delete the address book it is easiest if we open
3245 * it first. That fills in the filenames we want to delete.
3247 if(pab
->address_book
== NULL
){
3248 warning
[300] = '\0';
3249 pab
->address_book
= adrbk_open(pab
, ps_global
->home_dir
,
3250 &warning
[300], sizeof(warning
)-300,
3253 * Couldn't get it open.
3255 if(pab
->address_book
== NULL
){
3257 /* TRANSLATORS: %s is an error message */
3258 snprintf(warning
, 300, _("Can't delete data: %s"), &warning
[300]);
3260 strncpy(warning
, _("Can't delete address book data"), 100);
3265 * If we have it open, set the delete bits and close to get the
3266 * local copies. Delete the remote folder by hand.
3268 if(pab
->address_book
){
3269 char *file
, *origfile
= NULL
;
3273 * We're about to destroy addrbook data, better ask again.
3275 if(pab
->address_book
->count
> 0){
3278 /* TRANSLATORS: a question */
3279 snprintf(prompt
, sizeof(prompt
),
3280 _("About to delete the contents of address book (%ld entries), really delete "), (long) adrbk_count(pab
->address_book
));
3281 prompt
[sizeof(prompt
)-1] = '\0';
3283 switch(want_to(prompt
, 'n', 'n', h_ab_really_delete
, WT_NORM
)){
3290 *err
= _("Delete cancelled");
3296 pab
->address_book
->flags
|= DEL_FILE
;
3297 file
= cpystr(pab
->address_book
->filename
);
3298 if(pab
->type
& REMOTE_VIA_IMAP
)
3299 origfile
= cpystr(pab
->address_book
->orig_filename
);
3302 * In order to avoid locking problems when we delete the
3303 * remote folder, we need to actually close the remote stream
3304 * instead of just putting it back in the stream pool.
3305 * So we will remove this stream from the re-usable portion
3306 * of the stream pool by clearing the SP_USEPOOL flag.
3307 * Init_abook(pab, TotallyClosed) via rd_close_remdata is
3308 * going to pine_mail_close it.
3310 if(pab
->type
& REMOTE_VIA_IMAP
3311 && pab
->address_book
3312 && pab
->address_book
->type
== Imap
3313 && pab
->address_book
->rd
3314 && rd_stream_exists(pab
->address_book
->rd
)){
3316 sp_unflag(pab
->address_book
->rd
->t
.i
.stream
, SP_USEPOOL
);
3319 /* This deletes the files because of DEL_ bits we set above. */
3320 init_abook(pab
, TotallyClosed
);
3323 * Delete the remote folder.
3325 if(pab
->type
& REMOTE_VIA_IMAP
){
3329 ps_global
->c_client_error
[0] = '\0';
3330 if(!pine_mail_delete(NULL
, origfile
) &&
3331 ps_global
->c_client_error
[0] != '\0'){
3332 dprint((1, "%s: %s\n", origfile
? origfile
: "?",
3333 ps_global
->c_client_error
));
3336 /* delete line from metadata */
3337 rd
= rd_new_remdata(RemImap
, origfile
, NULL
);
3338 rd_write_metadata(rd
, 1);
3339 rd_close_remdata(&rd
);
3341 /* Check to see if it's still there */
3342 if((exists
=folder_exists(NULL
, origfile
)) &&
3343 (exists
!= FEX_ERROR
)){
3345 dprint((1, "Trouble deleting %s\n",
3346 origfile
? origfile
: "?"));
3350 if(can_access(file
, ACCESS_EXISTS
) == 0){
3352 dprint((1, "Trouble deleting %s\n",
3353 file
? file
: "?"));
3357 snprintf(warning
, sizeof(warning
), _("Trouble deleting data %s%s%s%s"),
3359 (f
&& o
) ? (o
? ", " : " and ") : "",
3362 warning
[sizeof(warning
)-1] = '\0';
3365 fs_give((void **) &file
);
3367 fs_give((void **) &origfile
);
3371 q_status_message(SM_ORDER
, 3, 3, warning
);
3372 dprint((1, "%s\n", warning
));
3373 display_message(NO_OP_COMMAND
);
3375 else if(modify_config
== DontChange
)
3376 q_status_message(SM_ORDER
, 0, 1, _("Addressbook data deleted"));
3379 if(modify_config
== DontChange
){
3381 * We return -1 to indicate that the addrbook wasn't deleted (as far
3382 * as we're concerned) but we don't fill in err so that no error
3383 * message will be printed.
3384 * Since the addrbook is still an addrbook we need to reinitialize it.
3386 pab
->access
= adrbk_access(pab
);
3387 if(pab
->type
& GLOBAL
&& pab
->access
!= NoAccess
)
3388 pab
->access
= ReadOnly
;
3390 init_abook(pab
, HalfOpen
);
3393 else if(modify_config
== Modify
){
3394 list
= vars
[varnum
].current_val
.l
;
3395 if(pab
->type
& GLOBAL
){
3396 how_many_in_list
= as
.n_addrbk
- as
.how_many_personals
- 1;
3397 num_in_list
= abook_num
- as
.how_many_personals
;
3400 how_many_in_list
= as
.how_many_personals
- 1;
3401 num_in_list
= abook_num
;
3404 else if(modify_config
== OverRideDefault
)
3405 how_many_in_list
= 1;
3406 else if(modify_config
== RevertToDefault
)
3407 how_many_in_list
= 0;
3409 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_del_abook");
3411 /* allocate for new list */
3412 if(how_many_in_list
)
3413 new_list
= (char **)fs_get((how_many_in_list
+ 1) * sizeof(char *));
3418 * This case is both for modifying the users user_val and for the
3419 * case where the user wants to modify the global_val default and
3420 * use the modified version for his or her new user_val. We just
3421 * copy from the existing global_val, deleting the one addrbook
3422 * and put the result in user_val.
3424 if(modify_config
== Modify
){
3425 /* copy old list up to where we will delete entry */
3426 for(i
= 0; i
< num_in_list
; i
++)
3427 new_list
[i
] = cpystr(list
[i
]);
3429 /* copy rest of old list */
3430 for(; i
< how_many_in_list
; i
++)
3431 new_list
[i
] = cpystr(list
[i
+1]);
3435 else if(modify_config
== OverRideDefault
){
3436 new_list
[0] = cpystr("");
3440 /* this also frees old variable contents for us */
3441 if(set_variable_list(varnum
, new_list
, TRUE
, ew
)){
3443 *err
= _("Delete cancelled: couldn't save pine configuration file");
3445 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3446 free_list_array(&new_list
);
3451 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3453 if(warn_about_revert
){
3454 /* TRANSLATORS: the %s may be "global " or nothing */
3455 snprintf(tmp
, sizeof(tmp
), _("Reverting to default %saddress books"),
3456 pab
->type
& GLOBAL
? _("global ") : "");
3457 tmp
[sizeof(tmp
)-1] = '\0';
3458 q_status_message(SM_ORDER
, 3, 4, tmp
);
3461 free_list_array(&new_list
);
3468 * Shuffle addrbooks.
3470 * Args: pab -- Pab from current addrbook.
3471 * slide -- return value, tells how far to slide the cursor. If slide
3472 * is negative, slide it up, if positive, slide it down.
3473 * command_line -- The screen line on which to prompt
3474 * msg -- Points to returned message, if any. Should be freed by
3477 * Result: Two address books are swapped in the display order. If the shuffle
3478 * crosses the Personal/Global boundary, then instead of swapping
3479 * two address books the highlighted abook is moved from one section
3482 * < 0 on failure, no changes.
3483 * > 0 If the return value is greater than zero it means that we've
3484 * reverted one of the variables to its default value. That
3485 * means we've added at least one new addrbook, so the caller
3486 * should reset. The value returned is the number of the
3487 * moved addrbook + 1 (+1 so it won't be confused with zero).
3488 * = 0 If the return value is zero we've just moved addrbooks around.
3489 * No reset need be done.
3492 ab_shuffle(PerAddrBook
*pab
, int *slide
, int command_line
, char **msg
)
3496 int i
, deefault
, rv
, target
= 0;
3497 int up_into_empty
= 0, down_into_empty
= 0;
3499 struct variable
*vars
= ps_global
->vars
;
3501 dprint((2, "- ab_shuffle() -\n"));
3505 if(ps_global
->readonly_pinerc
){
3507 *msg
= cpystr(_("Shuffle cancelled: config file not changeable"));
3512 /* Move it up or down? */
3517 /* TRANSLATORS: shuffle something Up or Down in a list */
3518 opts
[i
++].label
= N_("Up");
3523 opts
[i
++].label
= N_("Down");
3528 if(pab
->type
& GLOBAL
){
3529 if(vars
[V_GLOB_ADDRBOOK
].is_fixed
){
3531 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3545 if(as
.cur
== as
.n_addrbk
- 1) /* no down */
3549 if(vars
[V_ADDRESSBOOK
].is_fixed
){
3551 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book config"));
3556 if(as
.cur
== 0){ /* no up */
3561 if(as
.cur
== as
.n_addrbk
- 1){
3565 opts
[1].ch
= -2; /* no down */
3569 snprintf(tmp
, sizeof(tmp
), _("Shuffle \"%s\" %s%s%s ? "),
3571 (opts
[0].ch
!= -2) ? _("UP") : "",
3572 (opts
[0].ch
!= -2 && opts
[1].ch
!= -2) ? " or " : "",
3573 (opts
[1].ch
!= -2) ? _("DOWN") : "");
3574 tmp
[sizeof(tmp
)-1] = '\0';
3575 help
= (opts
[0].ch
== -2) ? h_ab_shuf_down
3576 : (opts
[1].ch
== -2) ? h_ab_shuf_up
3579 rv
= radio_buttons(tmp
, command_line
, opts
, deefault
, 'x',
3582 ps_global
->mangled_footer
= 1;
3584 if((rv
== 'u' && up_into_empty
) || (rv
== 'd' && down_into_empty
))
3587 target
= as
.cur
+ (rv
== 'u' ? -1 : 1);
3591 *msg
= cpystr(_("Shuffle cancelled"));
3596 return(do_the_shuffle(slide
, as
.cur
, target
, msg
));
3601 * Actually shuffle the config variables and address books structures around.
3603 * Args: anum1, anum2 -- The numbers of the address books
3604 * msg -- Points to returned message, if any.
3606 * Returns: >= 0 on success.
3607 * < 0 on failure, no changes.
3608 * > 0 If the return value is greater than zero it means that we've
3609 * reverted one of the variables to its default value. That
3610 * means we've added at least one new addrbook, so the caller
3611 * should reset. The value returned is the number of the
3612 * moved addrbook + 1 (+1 so it won't be confused with zero).
3613 * = 0 If the return value is zero we've just moved addrbooks around.
3614 * No reset need be done.
3616 * Anum1 is the one that we want to move, anum2 is the one that it will be
3617 * swapped with. When anum1 and anum2 are on the opposite sides of the
3618 * Personal/Global boundary then instead of swapping we just move anum1 to
3619 * the other side of the boundary.
3621 * Anum2 of -1 means it is a swap into the other type of address book, which
3622 * is currently empty.
3625 do_the_shuffle(int *slide
, int anum1
, int anum2
, char **msg
)
3628 enum {NotSet
, Pers
, Glob
, Empty
} type1
, type2
;
3629 int i
, j
, retval
= -1;
3630 struct variable
*vars
= ps_global
->vars
;
3633 char *cancel_msg
= _("Shuffle cancelled: couldn't save configuration file");
3635 dprint((5, "- do_the_shuffle(%d, %d) -\n", anum1
, anum2
));
3637 /* restrict address book config to normal config file */
3643 pab
= &as
.adrbks
[anum1
];
3644 type1
= (pab
->type
& GLOBAL
) ? Glob
: Pers
;
3650 cpystr(_("Shuffle cancelled: highlight entry you wish to shuffle"));
3658 pab
= &as
.adrbks
[anum2
];
3659 type2
= (pab
->type
& GLOBAL
) ? Glob
: Pers
;
3663 type2
= (type1
== Pers
) ? Glob
: Pers
;
3665 if((type1
== Pers
|| type2
== Pers
) && vars
[V_ADDRESSBOOK
].is_fixed
){
3667 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book configuration"));
3672 if((type1
== Glob
|| type2
== Glob
) && vars
[V_GLOB_ADDRBOOK
].is_fixed
){
3674 *msg
= cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
3680 * There are two cases. If the shuffle is two address books within the
3681 * same variable, then they just swap places. If it is a shuffle of an
3682 * addrbook from one side of the boundary to the other, just that one
3685 if((type1
== Glob
&& type2
== Glob
) ||
3686 (type1
== Pers
&& type2
== Pers
)){
3687 int how_many_in_list
, varnum
;
3688 int anum1_rel
, anum2_rel
; /* position in specific list */
3689 char **list
, **new_list
;
3692 *slide
= (anum1
< anum2
) ? LINES_PER_ABOOK
: -1 * LINES_PER_ABOOK
;
3695 how_many_in_list
= as
.how_many_personals
;
3696 list
= VAR_ADDRESSBOOK
;
3697 varnum
= V_ADDRESSBOOK
;
3702 how_many_in_list
= as
.n_addrbk
- as
.how_many_personals
;
3703 list
= VAR_GLOB_ADDRBOOK
;
3704 varnum
= V_GLOB_ADDRBOOK
;
3705 anum1_rel
= anum1
- as
.how_many_personals
;
3706 anum2_rel
= anum2
- as
.how_many_personals
;
3709 /* allocate for new list, same size as old list */
3710 new_list
= (char **)fs_get((how_many_in_list
+ 1) * sizeof(char *));
3712 /* fill in new_list */
3713 for(i
= 0; i
< how_many_in_list
; i
++){
3714 /* swap anum1 and anum2 */
3717 else if(i
== anum2_rel
)
3722 new_list
[i
] = cpystr(list
[j
]);
3727 if(set_variable_list(varnum
, new_list
, TRUE
, ew
)){
3729 *msg
= cpystr(cancel_msg
);
3731 /* restore old values */
3732 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3733 free_list_array(&new_list
);
3738 set_current_val(&vars
[varnum
], TRUE
, FALSE
);
3739 free_list_array(&new_list
);
3741 /* Swap PerAddrBook structs */
3742 tmppab
= as
.adrbks
[anum1
];
3743 as
.adrbks
[anum1
] = as
.adrbks
[anum2
];
3744 as
.adrbks
[anum2
] = tmppab
;
3746 else if((type1
== Pers
&& type2
== Glob
) ||
3747 (type1
== Glob
&& type2
== Pers
)){
3748 int how_many_in_srclist
, how_many_in_dstlist
;
3749 int srcvarnum
, dstvarnum
, srcanum
;
3750 int cnt
, warn_about_revert
= 0;
3752 char **new_src
, **new_dst
, **srclist
, **dstlist
;
3754 enum {NotSet
, Modify
, RevertToDefault
, OverRideDefault
} modify_config
;
3757 * how_many_in_srclist = # in orig src list (Pers or Glob list).
3758 * how_many_in_dstlist = # in orig dst list
3759 * srcanum = # of highlighted addrbook that is being shuffled
3762 how_many_in_srclist
= as
.how_many_personals
;
3763 how_many_in_dstlist
= as
.n_addrbk
- as
.how_many_personals
;
3764 srclist
= VAR_ADDRESSBOOK
;
3765 dstlist
= VAR_GLOB_ADDRBOOK
;
3766 srcvarnum
= V_ADDRESSBOOK
;
3767 dstvarnum
= V_GLOB_ADDRBOOK
;
3768 srcanum
= as
.how_many_personals
- 1;
3769 *slide
= (how_many_in_srclist
== 1)
3770 ? (LINES_PER_ADD_LINE
+ XTRA_LINES_BETWEEN
)
3771 : XTRA_LINES_BETWEEN
;
3774 how_many_in_srclist
= as
.n_addrbk
- as
.how_many_personals
;
3775 how_many_in_dstlist
= as
.how_many_personals
;
3776 srclist
= VAR_GLOB_ADDRBOOK
;
3777 dstlist
= VAR_ADDRESSBOOK
;
3778 srcvarnum
= V_GLOB_ADDRBOOK
;
3779 dstvarnum
= V_ADDRESSBOOK
;
3780 srcanum
= as
.how_many_personals
;
3781 *slide
= (how_many_in_dstlist
== 0)
3782 ? (LINES_PER_ADD_LINE
+ XTRA_LINES_BETWEEN
)
3783 : XTRA_LINES_BETWEEN
;
3784 *slide
= -1 * (*slide
);
3788 modify_config
= Modify
;
3789 if(how_many_in_srclist
== 1){
3791 * Deal with reverting to default values of the address book
3792 * variables, or with user deleting a default value.
3794 modify_config
= NotSet
;
3797 * Count how many address books are in the user's config.
3798 * This has to be one or zero, because how_many_in_srclist == 1.
3801 lval
= LVAL(&vars
[srcvarnum
], ew
);
3803 for(t
= lval
; *t
!= NULL
; t
++)
3807 * We'll revert to the default if it exists, and warn
3808 * the user about that.
3811 modify_config
= RevertToDefault
;
3812 /* see if there's a default to revert to */
3814 if(vars
[srcvarnum
].global_val
.l
&&
3815 vars
[srcvarnum
].global_val
.l
[0])
3816 for(t
= vars
[srcvarnum
].global_val
.l
; *t
!= NULL
; t
++)
3819 warn_about_revert
= cnt
;
3820 if(warn_about_revert
> 1 && type1
== Pers
)
3821 *slide
= LINES_PER_ABOOK
* warn_about_revert
+
3825 * User is already using the default.
3828 modify_config
= OverRideDefault
;
3833 * We're adding one to the dstlist, so need how_many + 1 + 1.
3835 new_dst
= (char **)fs_get((how_many_in_dstlist
+ 2) * sizeof(char *));
3839 * Because the Personal list comes before the Global list, when
3840 * we move to Global we're inserting a new first element into
3841 * the global list (the dstlist).
3843 * When we move from Global to Personal, we're appending a new
3844 * last element onto the personal list (the dstlist).
3847 new_dst
[j
++] = cpystr(srclist
[how_many_in_srclist
-1]);
3849 for(i
= 0; i
< how_many_in_dstlist
; i
++)
3850 new_dst
[j
++] = cpystr(dstlist
[i
]);
3853 new_dst
[j
++] = cpystr(srclist
[0]);
3858 * The srclist is complicated by the reverting to default
3861 if(modify_config
== Modify
){
3863 * In this case we're just removing one from the srclist
3864 * so the new_src is of size how_many -1 +1.
3866 new_src
= (char **)fs_get((how_many_in_srclist
) * sizeof(char *));
3869 for(i
= 0; i
< how_many_in_srclist
-1; i
++)
3870 new_src
[j
++] = cpystr(srclist
[i
+ ((type1
== Glob
) ? 1 : 0)]);
3874 else if(modify_config
== OverRideDefault
){
3876 * We were using default and will now revert to nothing.
3878 new_src
= (char **)fs_get(2 * sizeof(char *));
3879 new_src
[0] = cpystr("");
3882 else if(modify_config
== RevertToDefault
){
3884 * We are moving our last user variable out and reverting
3885 * to the default value for this variable.
3890 if(set_variable_list(dstvarnum
, new_dst
, TRUE
, ew
) ||
3891 set_variable_list(srcvarnum
, new_src
, TRUE
, ew
)){
3893 *msg
= cpystr(cancel_msg
);
3895 /* restore old values */
3896 set_current_val(&vars
[dstvarnum
], TRUE
, FALSE
);
3897 set_current_val(&vars
[srcvarnum
], TRUE
, FALSE
);
3898 free_list_array(&new_dst
);
3899 free_list_array(&new_src
);
3903 set_current_val(&vars
[dstvarnum
], TRUE
, FALSE
);
3904 set_current_val(&vars
[srcvarnum
], TRUE
, FALSE
);
3905 free_list_array(&new_dst
);
3906 free_list_array(&new_src
);
3908 retval
= (type1
== Pers
&& warn_about_revert
)
3909 ? (warn_about_revert
+ 1) : (srcanum
+ 1);
3912 * This is a tough case. We're adding one or more new address books
3913 * in this case so we need to reset the addrbooks and start over.
3914 * We return the number of the address book we just moved after the
3915 * reset so that the caller can focus attention on the moved one.
3916 * Actually, we return 1+the number so that we can tell it apart
3917 * from a return of zero, which just means everything is ok.
3919 if(warn_about_revert
){
3920 snprintf(tmp
, sizeof(tmp
),
3921 "This address book now %s, reverting to default %s address %s",
3922 (type1
== Glob
) ? "Personal" : "Global",
3923 (type1
== Glob
) ? "Global" : "Personal",
3924 warn_about_revert
> 1 ? "books" : "book");
3925 tmp
[sizeof(tmp
)-1] = '\0';
3931 * Modify PerAddrBook struct and adjust boundary.
3932 * In this case we aren't swapping two addrbooks, but just modifying
3933 * one from being global to personal or the reverse. It will
3934 * still be the same element in the as.adrbks array.
3936 pab
= &as
.adrbks
[srcanum
];
3938 as
.how_many_personals
--;
3939 pab
->type
|= GLOBAL
;
3940 if(pab
->access
!= NoAccess
)
3941 pab
->access
= ReadOnly
;
3944 as
.how_many_personals
++;
3945 pab
->type
&= ~GLOBAL
;
3946 if(pab
->access
!= NoAccess
&& pab
->access
!= MaybeRorW
)
3947 pab
->access
= ReadWrite
;
3950 snprintf(tmp
, sizeof(tmp
),
3951 "This address book now %s",
3952 (type1
== Glob
) ? "Personal" : "Global");
3953 tmp
[sizeof(tmp
)-1] = '\0';
3964 ab_compose_to_addr(long int cur_line
, int agg
, int allow_role
)
3971 dprint((2, "- ab_compose_to_addr -\n"));
3976 bldto
.arg
.str
= NULL
;
3980 size_t incr
= 100, avail
, alloced
;
3983 to
= (char *)fs_get(incr
);
3989 * Run through all of the selected entries
3990 * in all of the address books.
3991 * Put the nicknames together into one long
3992 * string with comma separators.
3994 for(i
= 0; i
< as
.n_addrbk
; i
++){
3997 EXPANDED_S
*next_one
;
3999 pab
= &as
.adrbks
[i
];
4000 if(pab
->address_book
)
4001 next_one
= pab
->address_book
->selects
;
4005 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
4007 AddrScrn_Disp fake_dl
;
4009 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4012 * Since we're picking up address book entries
4013 * directly from the address books and have
4014 * no knowledge of the display lines they came
4015 * from, we don't know the dl's that go with
4016 * them. We need to pass a dl to abe_to_nick
4017 * but it really is only going to use the
4018 * type in this case.
4021 dl
->type
= (abe
->tag
== Single
) ? Simple
: ListHead
;
4022 a_string
= abe_to_nick_or_addr_string(abe
, dl
, i
);
4024 while(abe
&& avail
< (size_t)strlen(a_string
)+1){
4027 fs_resize((void **)&to
, alloced
);
4031 strncpy(to
, a_string
, alloced
);
4032 to
[alloced
-1] = '\0';
4035 strncat(to
, ",", alloced
-strlen(to
)-1);
4036 to
[alloced
-1] = '\0';
4037 strncat(to
, a_string
, alloced
-strlen(to
)-1);
4038 to
[alloced
-1] = '\0';
4041 avail
-= (strlen(a_string
) + 1);
4042 fs_give((void **)&a_string
);
4050 if(is_addr(cur_line
)){
4052 dl
= dlist(cur_line
);
4055 if(dl
->type
== ListEnt
){
4057 bldto
.arg
.str
= cpystr(listmem(cur_line
));
4061 bldto
.arg
.abe
= abe
;
4066 if(bldto
.type
== Str
&& bldto
.arg
.str
== NULL
)
4067 bldto
.arg
.str
= cpystr("");
4069 ab_compose_internal(bldto
, allow_role
);
4071 restore_state(&state
);
4073 if(bldto
.type
== Str
&& bldto
.arg
.str
)
4074 fs_give((void **)&bldto
.arg
.str
);
4077 * Window size may have changed in composer.
4078 * Pine_send will have reset the window size correctly,
4079 * but we still have to reset our address book data structures.
4082 ps_global
->mangled_screen
= 1;
4088 * Used by the two compose routines.
4091 ab_compose_internal(BuildTo bldto
, int allow_role
)
4094 char *addr
, *fcc
, *error
= NULL
;
4095 ACTION_S
*role
= NULL
;
4096 void (*prev_screen
)(struct pine
*) = ps_global
->prev_screen
,
4097 (*redraw
)(void) = ps_global
->redrawer
;
4100 ps_global
->redrawer
= NULL
;
4102 ps_global
->next_screen
= SCREEN_FUN_NULL
;
4107 good_addr
= (our_build_address(bldto
, &addr
, &error
, &fcc
, NULL
) >= 0);
4110 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4111 fs_give((void **)&error
);
4114 if(!good_addr
&& addr
&& *addr
)
4115 fs_give((void **)&addr
); /* relying on fs_give setting addr to NULL */
4119 if(role_select_screen(ps_global
, &role
, MC_COMPOSE
) < 0){
4120 cmd_cancelled("Composition");
4121 ps_global
->next_screen
= prev_screen
;
4122 ps_global
->redrawer
= redraw
;
4127 * If default role was selected (NULL) we need to make up a role which
4128 * won't do anything, but will cause compose_mail to think there's
4129 * already a role so that it won't try to confirm the default.
4132 role
= copy_action(role
);
4134 role
= (ACTION_S
*)fs_get(sizeof(*role
));
4135 memset((void *)role
, 0, sizeof(*role
));
4136 role
->nick
= cpystr("Default Role");
4140 compose_mail(addr
, fcc
, role
, NULL
, NULL
);
4143 fs_give((void **)&addr
);
4146 fs_give((void **)&fcc
);
4151 * Export addresses into a file.
4153 * Args: cur_line -- The current line position (in global display list)
4155 * command_line -- The screen line on which to prompt
4157 * Returns -- 1 if the export is done
4161 ab_export(struct pine
*ps
, long int cur_line
, int command_line
, int agg
)
4163 int ret
= 0, i
, retflags
= GER_NONE
;
4164 int r
, orig_errno
, failure
= 0;
4165 struct variable
*vars
= ps
->vars
;
4166 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1];
4169 long start_of_append
;
4170 char *addr
= NULL
, *error
= NULL
;
4173 int good_addr
, plur
, vcard
= 0, tab
= 0;
4174 static HISTORY_S
*history
= NULL
;
4176 VCARD_INFO_S
*vinfo
;
4177 static ESCKEY_S ab_export_opts
[] = {
4178 {ctrl('T'), 10, "^T", N_("To Files")},
4179 {-1, 0, NULL
, NULL
},
4180 {-1, 0, NULL
, NULL
}};
4181 static ESCKEY_S vcard_or_addresses
[] = {
4182 {'a', 'a', "A", N_("Address List")},
4183 {'v', 'v', "V", N_("VCard")},
4184 /* TRANSLATORS: TabSep is a command key label meaning Tab Separated List */
4185 {'t', 't', "T", N_("TabSep")},
4186 {-1, 0, NULL
, NULL
}};
4189 dprint((2, "- ab_export -\n"));
4192 q_status_message(SM_ORDER
, 0, 3,
4193 "Alpine demo can't export addresses to files");
4198 i
= radio_buttons(_("Export list of addresses, vCard format, or Tab Separated ? "),
4199 command_line
, vcard_or_addresses
, 'a', 'x',
4200 NO_HELP
, RB_NORM
|RB_RET_HELP
);
4202 /* TRANSLATORS: a screen title */
4203 helper(h_ab_export_vcard
, _("HELP FOR EXPORT FORMAT"),
4205 ps_global
->mangled_screen
= 1;
4213 q_status_message(SM_INFO
, 0, 2, _("Address book export cancelled"));
4228 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_export");
4236 plur
= (abe
&& abe
->tag
== List
);
4242 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
4243 ab_export_opts
[++r
].ch
= ctrl('I');
4244 ab_export_opts
[r
].rval
= 11;
4245 ab_export_opts
[r
].name
= "TAB";
4246 ab_export_opts
[r
].label
= N_("Complete");
4249 ab_export_opts
[++r
].ch
= -1;
4251 r
= get_export_filename(ps
, filename
, NULL
, full_filename
, sizeof(filename
),
4252 plur
? _("addresses") : _("address"),
4253 _("EXPORT"), ab_export_opts
,
4254 &retflags
, command_line
, GE_IS_EXPORT
, &history
);
4259 q_status_message(SM_INFO
, 0, 2, _("Address book export cancelled"));
4263 q_status_message1(SM_ORDER
, 0, 2,
4264 _("Can't export to file outside of %s"), VAR_OPER_DIR
);
4271 dprint((5, "Opening file \"%s\" for export\n",
4272 full_filename
? full_filename
: "?"));
4274 if(!(store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
))){
4275 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4276 _("Error opening file \"%s\" for address export: %s"),
4277 full_filename
, error_description(errno
));
4282 * The write_single_vcard_entry function wants a pc.
4285 gf_set_so_writec(&pc
, store
);
4287 start_of_append
= so_tell(store
);
4290 for(i
= 0; !failure
&& i
< as
.n_addrbk
; i
++){
4293 EXPANDED_S
*next_one
;
4295 pab
= &as
.adrbks
[i
];
4296 if(pab
->address_book
)
4297 next_one
= pab
->address_book
->selects
;
4301 while(!failure
&& (num
= entry_get_next(&next_one
)) != NO_NEXT
){
4303 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4304 if((vcard
|| tab
) && abe
){
4306 * There is no place to store the charset information
4307 * so we don't ask for it.
4309 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, 1)))
4313 write_single_vcard_entry(ps
, pc
, vinfo
);
4315 write_single_tab_entry(pc
, vinfo
);
4317 free_vcard_info(&vinfo
);
4322 bldto
.arg
.abe
= abe
;
4325 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4328 q_status_message1(SM_ORDER
, 0, 4, "%s", error
);
4329 fs_give((void **)&error
);
4332 /* rfc1522_decode the addr */
4336 len
= 4*strlen(addr
)+1;
4337 p
= (char *)fs_get(len
* sizeof(char));
4338 if(rfc1522_decode_to_utf8((unsigned char *)p
,len
,addr
) == (unsigned char *)p
){
4339 fs_give((void **)&addr
);
4343 fs_give((void **)&p
);
4350 * Change the unquoted commas into newlines.
4351 * Not worth it to do complicated quoting,
4352 * just consider double quotes.
4354 for(p
= addr
; *p
; p
++){
4357 else if(!quoted
&& *p
== ','){
4359 removing_leading_white_space(p
);
4364 if(!so_puts(store
, addr
) || !so_puts(store
, NEWLINE
)){
4371 fs_give((void **)&addr
);
4379 dl
= dlist(cur_line
);
4381 if((vcard
|| tab
) && abe
){
4382 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, 1)))
4386 write_single_vcard_entry(ps
, pc
, vinfo
);
4388 write_single_tab_entry(pc
, vinfo
);
4390 free_vcard_info(&vinfo
);
4395 if(dl
->type
== ListHead
&& listmem_count_from_abe(abe
) == 0){
4396 error
= _("List is empty, nothing to export!");
4399 else if(dl
->type
== ListEnt
){
4401 bldto
.arg
.str
= listmem(cur_line
);
4402 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4406 bldto
.arg
.abe
= abe
;
4407 good_addr
= (our_build_address(bldto
,&addr
,&error
,NULL
,NULL
) >= 0);
4411 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4412 fs_give((void **)&error
);
4415 /* Have to rfc1522_decode the addr */
4418 len
= 4*strlen(addr
)+1;
4419 p
= (char *)fs_get(len
* sizeof(char));
4420 if(rfc1522_decode_to_utf8((unsigned char *)p
,len
,addr
) == (unsigned char *)p
){
4421 fs_give((void **)&addr
);
4425 fs_give((void **)&p
);
4432 * Change the unquoted commas into newlines.
4433 * Not worth it to do complicated quoting,
4434 * just consider double quotes.
4436 for(p
= addr
; *p
; p
++){
4439 else if(!quoted
&& *p
== ','){
4441 removing_leading_white_space(p
);
4446 if(!so_puts(store
, addr
) || !so_puts(store
, NEWLINE
)){
4453 fs_give((void **)&addr
);
4458 gf_clear_so_writec(store
);
4460 if(so_give(&store
)) /* release storage */
4464 our_truncate(full_filename
, (off_t
)start_of_append
);
4465 dprint((1, "FAILED Export: file \"%s\" : %s\n",
4466 full_filename
? full_filename
: "?",
4467 error_description(orig_errno
)));
4468 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4469 _("Error exporting to \"%s\" : %s"),
4470 filename
, error_description(orig_errno
));
4474 q_status_message3(SM_ORDER
,0,3,
4475 "%s %s to file \"%s\"",
4476 (vcard
|| tab
) ? (agg
? "Entries" : "Entry")
4477 : (plur
? "Addresses" : "Address"),
4480 : retflags
& GER_APPEND
? "appended" : "exported",
4485 ps
->mangled_footer
= 1;
4491 * Forward an address book entry or entries via email attachment.
4493 * We use the vCard standard to send entries. We group multiple entries
4494 * using the BEGIN/END construct of vCard, not with multiple MIME parts.
4495 * A limitation of vCard is that there can be only one charset for the
4496 * whole group we send, so we might lose that information.
4498 * Args: cur_line -- The current line position (in global display list)
4500 * command_line -- The screen line on which to prompt
4503 ab_forward(struct pine
*ps
, long int cur_line
, int agg
)
4507 ENVELOPE
*outgoing
= NULL
;
4508 BODY
*pb
, *body
= NULL
;
4513 VCARD_INFO_S
*vinfo
;
4514 ACTION_S
*role
= NULL
;
4516 dprint((2, "- ab_forward -\n"));
4519 dl
= dlist(cur_line
);
4520 if(dl
->type
!= ListHead
&& dl
->type
!= Simple
)
4525 q_status_message(SM_ORDER
, 3, 3, _("Trouble accessing current entry"));
4530 outgoing
= mail_newenvelope();
4531 if(agg
&& as
.selections
> 1)
4532 outgoing
->subject
= cpystr("Forwarded address book entries from Alpine");
4534 outgoing
->subject
= cpystr("Forwarded address book entry from Alpine");
4536 body
= mail_newbody();
4537 body
->type
= TYPEMULTIPART
;
4538 /*---- The TEXT part/body ----*/
4539 body
->nested
.part
= mail_newbody_part();
4540 body
->nested
.part
->body
.type
= TYPETEXT
;
4541 /*--- Allocate an object for the body ---*/
4542 if((body
->nested
.part
->body
.contents
.text
.data
=
4543 (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
4545 long rflags
= ROLE_COMPOSE
;
4548 pp
= &(body
->nested
.part
->next
);
4550 if(nonempty_patterns(rflags
, &dummy
)){
4552 * This is really more like Compose, even though it
4553 * is called Forward.
4555 if(confirm_role(rflags
, &role
))
4556 role
= combine_inherited_role(role
);
4559 cmd_cancelled("Composition");
4565 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
4568 if((sig
= detoken(role
, NULL
, 2, 0, 1, NULL
, NULL
)) != NULL
){
4570 so_puts((STORE_S
*)body
->nested
.part
->body
.contents
.text
.data
,
4575 fs_give((void **)&sig
);
4578 /* so we don't have an empty part */
4580 so_puts((STORE_S
*)body
->nested
.part
->body
.contents
.text
.data
, "\n");
4583 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4584 _("Problem creating space for message text"));
4588 outgoing
->message_id
= generate_message_id(role
);
4590 /*---- create the attachment, and write abook entry into it ----*/
4591 *pp
= mail_newbody_part();
4592 pb
= &((*pp
)->body
);
4593 pb
->type
= TYPETEXT
;
4594 pb
->encoding
= ENCOTHER
; /* let data decide */
4595 pb
->id
= generate_message_id(role
);
4596 pb
->subtype
= cpystr("DIRECTORY");
4597 if(agg
&& as
.selections
> 1)
4598 pb
->description
= cpystr("Alpine addressbook entries");
4600 pb
->description
= cpystr("Alpine addressbook entry");
4602 pb
->parameter
= NULL
;
4603 set_parameter(&pb
->parameter
, "profile", "vCard");
4605 if((pb
->contents
.text
.data
= (void *)so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
4606 int are_some_unqualified
= 0, expand_nicks
= 0;
4609 EXPANDED_S
*next_one
;
4611 gf_set_so_writec(&pc
, (STORE_S
*) pb
->contents
.text
.data
);
4614 for(i
= 0; i
< as
.n_addrbk
&& !are_some_unqualified
; i
++){
4616 pab
= &as
.adrbks
[i
];
4617 if(pab
->address_book
)
4618 next_one
= pab
->address_book
->selects
;
4622 while((num
= entry_get_next(&next_one
)) != NO_NEXT
&&
4623 !are_some_unqualified
){
4625 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4626 if(abe
->tag
== Single
){
4627 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
4628 && !strindex(abe
->addr
.addr
, '@'))
4629 are_some_unqualified
++;
4634 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4635 if(!strindex(*ll
, '@')){
4636 are_some_unqualified
++;
4646 * Search through the addresses to see if there are any
4647 * that are unqualified, and so would be different if
4650 if(abe
->tag
== Single
){
4651 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
4652 && !strindex(abe
->addr
.addr
, '@'))
4653 are_some_unqualified
++;
4658 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4659 if(!strindex(*ll
, '@')){
4660 are_some_unqualified
++;
4667 if(are_some_unqualified
){
4668 switch(want_to(_("Expand nicknames"), 'y', 'x', h_ab_forward
,WT_NORM
)){
4670 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4671 q_status_message(SM_INFO
, 0, 2, _("Address book forward cancelled"));
4683 ps
->mangled_footer
= 1;
4687 for(i
= 0; i
< as
.n_addrbk
; i
++){
4689 pab
= &as
.adrbks
[i
];
4690 if(pab
->address_book
)
4691 next_one
= pab
->address_book
->selects
;
4695 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
4697 abe
= adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) num
);
4698 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, expand_nicks
))){
4699 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4703 write_single_vcard_entry(ps
, pc
, vinfo
);
4704 free_vcard_info(&vinfo
);
4710 if(!(vinfo
=prepare_abe_for_vcard(ps
, abe
, expand_nicks
))){
4711 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4715 write_single_vcard_entry(ps
, pc
, vinfo
);
4716 free_vcard_info(&vinfo
);
4720 /* This sets parameter charset, if necessary, and encoding */
4721 set_mime_type_by_grope(pb
);
4722 set_charset_possibly_to_ascii(pb
, "UTF-8");
4724 strlen((char *)so_text((STORE_S
*)pb
->contents
.text
.data
));
4727 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4728 _("Problem creating space for message text"));
4732 gf_clear_so_writec((STORE_S
*) pb
->contents
.text
.data
);
4734 pine_send(outgoing
, &body
, _("FORWARDING ADDRESS BOOK ENTRY"), role
, NULL
,
4735 NULL
, NULL
, NULL
, NULL
, 0);
4737 ps
->mangled_screen
= 1;
4742 mail_free_envelope(&outgoing
);
4745 pine_free_body(&body
);
4753 * Given an Adrbk_Entry fill in some of the fields in a VCARD_INFO_S
4754 * for use by write_single_vcard_entry. The returned structure is freed
4758 prepare_abe_for_vcard(struct pine
*ps
, AdrBk_Entry
*abe
, int expand_nicks
)
4760 VCARD_INFO_S
*vinfo
= NULL
;
4761 char *init_addr
= NULL
, *addr
= NULL
, *astring
;
4763 ADDRESS
*adrlist
= NULL
;
4768 vinfo
= (VCARD_INFO_S
*) fs_get(sizeof(*vinfo
));
4769 memset((void *) vinfo
, 0, sizeof(*vinfo
));
4771 if(abe
->nickname
&& abe
->nickname
[0]){
4772 vinfo
->nickname
= (char **) fs_get((1+1) * sizeof(char *));
4773 vinfo
->nickname
[0] = cpystr(abe
->nickname
);
4774 vinfo
->nickname
[1] = NULL
;
4777 if(abe
->fcc
&& abe
->fcc
[0]){
4778 vinfo
->fcc
= (char **) fs_get((1+1) * sizeof(char *));
4779 vinfo
->fcc
[0] = cpystr(abe
->fcc
);
4780 vinfo
->fcc
[1] = NULL
;
4783 if(abe
->extra
&& abe
->extra
[0]){
4784 vinfo
->note
= (char **) fs_get((1+1) * sizeof(char *));
4785 vinfo
->note
[0] = cpystr(abe
->extra
);
4786 vinfo
->note
[1] = NULL
;
4789 if(abe
->fullname
&& abe
->fullname
[0]){
4790 char *fn
, *last
= NULL
, *middle
= NULL
, *first
= NULL
;
4792 fn
= adrbk_formatname(abe
->fullname
, &first
, &last
);
4795 vinfo
->fullname
= (char **)fs_get((1+1) * sizeof(char *));
4796 vinfo
->fullname
[0] = fn
;
4797 vinfo
->fullname
[1] = NULL
;
4800 fs_give((void **)&fn
);
4804 if(first
&& (middle
=strindex(first
, ' '))){
4806 middle
= skip_white_space(middle
);
4810 vinfo
->first
= first
;
4811 vinfo
->middle
= middle
? cpystr(middle
) : NULL
;
4817 fs_give((void **)&last
);
4819 fs_give((void **)&first
);
4822 /* expand nicknames and fully-qualify unqualified names */
4828 if(abe
->tag
== Single
)
4829 init_addr
= cpystr(abe
->addr
.addr
);
4835 /* figure out how large a string we need to allocate */
4837 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
4838 length
+= (strlen(*ll
) + 2);
4843 init_addr
= (char *)fs_get((size_t)(length
+1L) * sizeof(char));
4846 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
4847 sstrncpy(&p
, *ll
, length
-(p
-init_addr
));
4849 sstrncpy(&p
, ", ", length
-(p
-init_addr
));
4852 init_addr
[length
] = '\0';
4856 bldto
.arg
.str
= init_addr
;
4857 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
4859 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
4860 fs_give((void **)&error
);
4861 free_vcard_info(&vinfo
);
4866 rfc822_parse_adrlist(&adrlist
, addr
, ps
->maildomain
);
4868 for(cnt
= 0, a
= adrlist
; a
; a
= a
->next
)
4871 vinfo
->email
= (char **)fs_get((cnt
+1) * sizeof(char *));
4873 for(cnt
= 0, a
= adrlist
; a
; a
= a
->next
){
4878 next_addr
= a
->next
;
4881 bufp
= (char *) fs_get(len
* sizeof(char));
4882 astring
= addr_string(a
, bufp
, len
);
4883 a
->next
= next_addr
;
4884 vinfo
->email
[cnt
++] = cpystr(astring
? astring
: "");
4885 fs_give((void **)&bufp
);
4888 vinfo
->email
[cnt
] = NULL
;
4890 else{ /* don't expand or qualify */
4891 if(abe
->tag
== Single
){
4893 (abe
->addr
.addr
&& abe
->addr
.addr
[0]) ? abe
->addr
.addr
: "";
4894 vinfo
->email
= (char **)fs_get((1+1) * sizeof(char *));
4895 vinfo
->email
[0] = cpystr(astring
);
4896 vinfo
->email
[1] = NULL
;
4901 for(cnt
= 0, ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
4904 vinfo
->email
= (char **)fs_get((cnt
+1) * sizeof(char *));
4905 for(cnt
= 0, ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
4906 vinfo
->email
[cnt
++] = cpystr(*ll
);
4908 vinfo
->email
[cnt
] = NULL
;
4917 free_vcard_info(VCARD_INFO_S
**vinfo
)
4919 if(vinfo
&& *vinfo
){
4920 if((*vinfo
)->nickname
)
4921 free_list_array(&(*vinfo
)->nickname
);
4922 if((*vinfo
)->fullname
)
4923 free_list_array(&(*vinfo
)->fullname
);
4925 free_list_array(&(*vinfo
)->fcc
);
4927 free_list_array(&(*vinfo
)->note
);
4929 free_list_array(&(*vinfo
)->title
);
4931 free_list_array(&(*vinfo
)->tel
);
4933 free_list_array(&(*vinfo
)->email
);
4936 fs_give((void **)&(*vinfo
)->first
);
4937 if((*vinfo
)->middle
)
4938 fs_give((void **)&(*vinfo
)->middle
);
4940 fs_give((void **)&(*vinfo
)->last
);
4942 fs_give((void **)vinfo
);
4951 write_single_vcard_entry(struct pine
*ps
, gf_io_t pc
, VCARD_INFO_S
*vinfo
)
4953 char *decoded
, *tmp2
, *tmp
= NULL
, *hdr
;
4955 int i
, did_fn
= 0, did_n
= 0;
4963 #if defined(DOS) || defined(OS2)
4970 strncpy(eol
, "\r\n", sizeof(eol
));
4972 strncpy(eol
, "\n", sizeof(eol
));
4974 eol
[sizeof(eol
)-1] = '\0';
4976 gf_puts("BEGIN:VCARD", pc
);
4978 gf_puts("VERSION:3.0", pc
);
4981 for(i
= 0; i
< 7; i
++){
4984 ll
= vinfo
->nickname
;
4989 ll
= vinfo
->fullname
;
5019 alpine_panic("can't happen in write_single_vcard_entry");
5022 for(; ll
&& *ll
; ll
++){
5023 decoded
= (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
), SIZEOF_20KBUF
, *ll
);
5025 tmp
= vcard_escape(decoded
);
5027 if((tmp2
= fold(tmp
, FOLD_BY
, FOLD_BY
, hdr
, " ", FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5029 fs_give((void **)&tmp2
);
5034 fs_give((void **)&tmp
);
5039 if(vinfo
->last
&& vinfo
->last
[0]){
5042 pl
= vcard_escape(vinfo
->last
);
5043 pf
= (vinfo
->first
&& *vinfo
->first
) ? vcard_escape(vinfo
->first
)
5045 pm
= (vinfo
->middle
&& *vinfo
->middle
) ? vcard_escape(vinfo
->middle
)
5047 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s%s%s",
5048 (pl
&& *pl
) ? pl
: "",
5049 ((pf
&& *pf
) || (pm
&& *pm
)) ? ";" : "",
5050 (pf
&& *pf
) ? pf
: "",
5051 (pm
&& *pm
) ? ";" : "",
5052 (pm
&& *pm
) ? pm
: "");
5054 if((tmp2
= fold(tmp_20k_buf
, FOLD_BY
, FOLD_BY
, "N:", " ",
5055 FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5057 fs_give((void **)&tmp2
);
5062 fs_give((void **)&pl
);
5064 fs_give((void **)&pf
);
5066 fs_give((void **)&pm
);
5070 * These two types are required in draft-ietf-asid-mime-vcard-06, which
5071 * is April 98 and is in last call.
5073 if(!did_fn
|| !did_n
){
5075 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s%s%s",
5076 (vinfo
->first
&& *vinfo
->first
) ? vinfo
->first
: "",
5077 (vinfo
->first
&& *vinfo
->first
&&
5078 vinfo
->middle
&& *vinfo
->middle
) ? " " : "",
5079 (vinfo
->middle
&& *vinfo
->middle
) ? vinfo
->middle
: "",
5080 (((vinfo
->first
&& *vinfo
->first
) ||
5081 (vinfo
->middle
&& *vinfo
->middle
)) &&
5082 vinfo
->last
&& *vinfo
->last
) ? " " : "",
5083 (vinfo
->last
&& *vinfo
->last
) ? vinfo
->last
: "");
5085 tmp
= vcard_escape(tmp_20k_buf
);
5087 if((tmp2
= fold(tmp
, FOLD_BY
, FOLD_BY
, "FN:", " ",
5088 FLD_PWS
| (cr
? FLD_CRLF
: 0))) != NULL
){
5090 fs_give((void **)&tmp2
);
5094 fs_give((void **)&tmp
);
5099 gf_puts("FN:<Unknown>", pc
);
5103 gf_puts("N:<Unknown>", pc
);
5108 gf_puts("END:VCARD", pc
);
5117 write_single_tab_entry(gf_io_t pc
, VCARD_INFO_S
*vinfo
)
5119 char *decoded
, *tmp
= NULL
;
5127 #if defined(DOS) || defined(OS2)
5133 for(i
= 0; i
< 4; i
++){
5136 ll
= vinfo
->nickname
;
5140 ll
= vinfo
->fullname
;
5152 alpine_panic("can't happen in write_single_tab_entry");
5158 for(first
= 1; ll
&& *ll
; ll
++){
5160 decoded
= (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
), SIZEOF_20KBUF
, *ll
);
5161 tmp
= vcard_escape(decoded
);
5163 if(i
== 2 && !first
)
5169 fs_give((void **)&tmp
);
5179 * for ab_save percent done
5181 static int total_to_copy
;
5182 static int copied_so_far
;
5184 percent_done_copying(void)
5186 return((copied_so_far
* 100) / total_to_copy
);
5190 cmp_action_list(const qsort_t
*a1
, const qsort_t
*a2
)
5192 ACTION_LIST_S
*x
= (ACTION_LIST_S
*)a1
;
5193 ACTION_LIST_S
*y
= (ACTION_LIST_S
*)a2
;
5195 if(x
->pab
!= y
->pab
)
5196 return((x
->pab
> y
->pab
) ? 1 : -1); /* order doesn't matter */
5199 * The only one that matters is when both x and y have dup lit.
5200 * For the others, just need to be consistent so sort will terminate.
5204 return((x
->num_in_dst
> y
->num_in_dst
) ? -1
5205 : (x
->num_in_dst
== y
->num_in_dst
) ? 0 : 1);
5212 return((x
->num
> y
->num
) ? -1 : (x
->num
== y
->num
) ? 0 : 1);
5217 * Copy a bunch of address book entries to a particular address book.
5219 * Args abook -- the current addrbook handle
5220 * cur_line -- the current line the cursor is on
5221 * command_line -- the line to prompt on
5222 * agg -- 1 if this is an aggregate copy
5224 * Returns 1 if successful, 0 if not
5227 ab_save(struct pine
*ps
, AdrBk
*abook
, long int cur_line
, int command_line
, int agg
)
5229 PerAddrBook
*pab_dst
, *pab
;
5230 SAVE_STATE_S state
; /* For saving state of addrbooks temporarily */
5232 int how_many_dups
= 0, how_many_to_copy
= 0, skip_dups
= 0;
5233 int how_many_no_action
= 0, ret
= 1;
5234 int err
= 0, need_write
= 0, we_cancel
= 0;
5235 int act_list_size
, special_case
= 0;
5236 adrbk_cntr_t num
, new_entry_num
;
5237 char warn
[2][MAX_NICKNAME
+1];
5238 char warning
[MAX_NICKNAME
+1];
5239 char tmp
[MAX(200,2*MAX_NICKNAME
+80)];
5240 ACTION_LIST_S
*action_list
= NULL
, *al
;
5241 static ESCKEY_S save_or_export
[] = {
5242 {'s', 's', "S", N_("Save")},
5243 {'e', 'e', "E", N_("Export")},
5244 {-1, 0, NULL
, NULL
}};
5247 snprintf(tmp
, sizeof(tmp
), _("Save highlighted entry to address book or Export to filesystem ? "));
5248 else if(as
.selections
> 1)
5249 snprintf(tmp
, sizeof(tmp
), _("Save selected entries to address book or Export to filesystem ? "));
5250 else if(as
.selections
== 1)
5251 snprintf(tmp
, sizeof(tmp
), _("Save selected entry to address book or Export to filesystem ? "));
5253 snprintf(tmp
, sizeof(tmp
), _("Save to address book or Export to filesystem ? "));
5255 i
= radio_buttons(tmp
, -FOOTER_ROWS(ps
), save_or_export
, 's', 'x',
5256 h_ab_save_exp
, RB_NORM
);
5259 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
5263 return(ab_export(ps
, cur_line
, command_line
, agg
));
5269 q_status_message(SM_ORDER
, 3, 3, "can't happen in ab_save");
5273 pab_dst
= setup_for_addrbook_add(&state
, command_line
, _("Save"));
5277 pab
= &as
.adrbks
[as
.cur
];
5279 dprint((2, "- ab_save: %s -> %s (agg=%d)-\n",
5280 pab
->abnick
? pab
->abnick
: "?",
5281 pab_dst
->abnick
? pab_dst
->abnick
: "?", agg
));
5284 act_list_size
= as
.selections
;
5288 action_list
= (ACTION_LIST_S
*)fs_get((act_list_size
+1) *
5289 sizeof(ACTION_LIST_S
));
5290 memset((void *)action_list
, 0, (act_list_size
+1) * sizeof(ACTION_LIST_S
));
5295 for(i
= 0; i
< as
.n_addrbk
; i
++){
5296 EXPANDED_S
*next_one
;
5298 pab
= &as
.adrbks
[i
];
5299 if(pab
->address_book
)
5300 next_one
= pab
->address_book
->selects
;
5304 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
5305 if(pab
!= pab_dst
&&
5306 pab
->ostatus
!= Open
&&
5307 pab
->ostatus
!= NoDisplay
)
5308 init_abook(pab
, NoDisplay
);
5310 if(pab
->ostatus
!= Open
&& pab
->ostatus
!= NoDisplay
){
5311 q_status_message1(SM_ORDER
, 0, 4,
5312 _("Can't re-open address book %s to save from"),
5318 set_act_list_member(al
, (a_c_arg_t
)num
, pab_dst
, pab
, warning
);
5320 how_many_no_action
++;
5323 if(how_many_dups
< 2 && warning
[0]){
5324 strncpy(warn
[how_many_dups
], warning
, MAX_NICKNAME
);
5325 warn
[how_many_dups
][MAX_NICKNAME
] = '\0';
5339 if(is_addr(cur_line
)){
5342 dl
= dlist(cur_line
);
5344 if(dl
->type
== ListEnt
)
5349 set_act_list_member(al
, (a_c_arg_t
)num
, pab_dst
, pab
, warning
);
5355 how_many_no_action
++;
5358 if(how_many_dups
< 2 && warning
[0]){
5359 strncpy(warn
[how_many_dups
], warning
, MAX_NICKNAME
);
5360 warn
[how_many_dups
][MAX_NICKNAME
] = '\0';
5370 q_status_message(SM_ORDER
, 0, 4, _("No current entry to save"));
5375 if(how_many_to_copy
== 0 && how_many_no_action
== 1 && act_list_size
== 1)
5379 TA_STATE_S tas
, *tasp
;
5381 /* Not going to use the action_list now */
5383 fs_give((void **)&action_list
);
5388 take_this_one_entry(ps
, &tasp
, abook
, cur_line
);
5391 * If take_this_one_entry or its children didn't do this for
5392 * us, we do it here.
5395 restore_state(&(tas
.state
));
5398 * We don't have enough information to know what to return.
5403 /* nothing to do (except for special Take case below) */
5404 if(how_many_to_copy
== 0){
5405 if(how_many_no_action
== 0){
5410 restore_state(&state
);
5412 if(how_many_no_action
> 1)
5413 snprintf(tmp
, sizeof(tmp
), _("Saved %d entries to %s"), how_many_no_action
, pab_dst
->abnick
);
5415 snprintf(tmp
, sizeof(tmp
), _("Saved %d entry to %s"), how_many_no_action
, pab_dst
->abnick
);
5417 tmp
[sizeof(tmp
)-1] = '\0';
5418 q_status_message(SM_ORDER
, 0, 4, tmp
);
5420 fs_give((void **)&action_list
);
5427 * If there are some nicknames which already exist in the selected
5428 * abook, ask user what to do.
5430 if(how_many_dups
> 0){
5431 if(how_many_dups
== 1)
5432 snprintf(tmp
, sizeof(tmp
), _("Entry with nickname \"%.*s\" already exists, replace "),
5433 MAX_NICKNAME
, warn
[0]);
5434 else if(how_many_dups
== 2)
5435 snprintf(tmp
, sizeof(tmp
),
5436 _("Nicknames \"%.*s\" and \"%.*s\" already exist, replace "),
5437 MAX_NICKNAME
, warn
[0], MAX_NICKNAME
, warn
[1]);
5439 snprintf(tmp
, sizeof(tmp
), _("%d of the nicknames already exist, replace "),
5442 tmp
[sizeof(tmp
)-1] = '\0';
5444 switch(want_to(tmp
, 'n', 'x', h_ab_copy_dups
, WT_NORM
)){
5447 if(how_many_to_copy
== how_many_dups
){
5448 restore_state(&state
);
5450 fs_give((void **)&action_list
);
5452 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
5468 * Because the deletes happen immediately we have to delete from high
5469 * entry number towards lower entry numbers so that we are deleting
5470 * the correct entries. In order to do that we'll sort the action_list
5471 * to give us a safe order.
5473 if(!skip_dups
&& how_many_dups
> 1)
5474 qsort((qsort_t
*)action_list
, (size_t)as
.selections
, sizeof(*action_list
),
5478 * Set up the busy alarm percent counters.
5480 total_to_copy
= how_many_to_copy
- (skip_dups
? how_many_dups
: 0);
5482 we_cancel
= busy_cue(_("Saving entries"),
5483 (total_to_copy
> 4) ? percent_done_copying
: NULL
, 0);
5486 * Add the list of entries to the destination abook.
5488 for(al
= action_list
; al
&& al
->pab
; al
++){
5491 if(al
->skip
|| (skip_dups
&& al
->dup
))
5494 if(!(abe
= adrbk_get_ae(al
->pab
->address_book
, (a_c_arg_t
) al
->num
))){
5495 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
5496 _("Error saving entry: %s"),
5497 error_description(errno
));
5503 * Delete existing dups and replace them.
5507 /* delete the existing entry */
5509 if(adrbk_delete(pab_dst
->address_book
,
5510 (a_c_arg_t
)al
->num_in_dst
, 1, 0, 0, 0) == 0){
5514 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
5515 _("Error replacing entry in %s: %s"),
5517 error_description(errno
));
5524 * Now we have a clean slate to work with.
5525 * Add (sorted in correctly) or append abe to the destination
5528 if(total_to_copy
<= 1)
5529 rc
= adrbk_add(pab_dst
->address_book
,
5533 abe
->tag
== Single
? abe
->addr
.addr
: NULL
,
5543 rc
= adrbk_append(pab_dst
->address_book
,
5546 abe
->tag
== Single
? abe
->addr
.addr
: NULL
,
5556 * If the entry we copied is a list, we also have to add
5557 * the list members to the copy.
5559 if(rc
== 0 && abe
->tag
== List
){
5563 * We want it to copy the list in the exact order
5564 * without sorting it.
5566 save_sort_rule
= pab_dst
->address_book
->sort_rule
;
5567 pab_dst
->address_book
->sort_rule
= AB_SORT_RULE_NONE
;
5569 rc
= adrbk_nlistadd(pab_dst
->address_book
,
5570 (a_c_arg_t
)new_entry_num
, NULL
, NULL
,
5574 pab_dst
->address_book
->sort_rule
= save_sort_rule
;
5578 if(abe
&& abe
->nickname
)
5579 q_status_message2(SM_ORDER
| SM_DING
, 3, 5, _("Error saving %s: %s"), abe
->nickname
, error_description(errno
));
5581 q_status_message1(SM_ORDER
| SM_DING
, 3, 5, _("Error saving entry: %s"), error_description(errno
));
5590 int sort_happened
= 0;
5592 if(adrbk_write(pab_dst
->address_book
, 0, NULL
, &sort_happened
, 0, 1)){
5598 ps_global
->mangled_screen
= 1;
5605 restore_state(&state
);
5607 fs_give((void **)&action_list
);
5609 ps_global
->mangled_footer
= 1;
5614 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
5615 _("Save only partially completed"));
5617 q_status_message(SM_INFO
, 0, 2, _("Address book save cancelled"));
5619 else if (how_many_to_copy
+ how_many_no_action
-
5620 (skip_dups
? how_many_dups
: 0) > 0){
5623 snprintf(tmp
, sizeof(tmp
), "Saved %d %s to %s",
5624 how_many_to_copy
+ how_many_no_action
-
5625 (skip_dups
? how_many_dups
: 0),
5626 ((how_many_to_copy
+ how_many_no_action
-
5627 (skip_dups
? how_many_dups
: 0)) > 1) ? "entries" : "entry",
5629 tmp
[sizeof(tmp
)-1] = '\0';
5630 q_status_message(SM_ORDER
, 0, 4, tmp
);
5638 * Warn should point to an array of size MAX_NICKNAME+1.
5641 set_act_list_member(ACTION_LIST_S
*al
, a_c_arg_t numarg
, PerAddrBook
*pab_dst
, PerAddrBook
*pab
, char *warn
)
5643 AdrBk_Entry
*abe1
, *abe2
;
5646 num
= (adrbk_cntr_t
)numarg
;
5651 /* skip if they're copying from and to same addrbook */
5655 abe1
= adrbk_get_ae(pab
->address_book
, numarg
);
5656 if(abe1
&& abe1
->nickname
&& abe1
->nickname
[0]){
5657 adrbk_cntr_t dst_enum
;
5659 abe2
= adrbk_lookup_by_nick(pab_dst
->address_book
,
5660 abe1
->nickname
, &dst_enum
);
5662 * This nickname already exists in the destn address book.
5665 /* If it isn't different, no problem. Check it out. */
5666 if(abes_are_equal(abe1
, abe2
))
5669 strncpy(warn
, abe1
->nickname
, MAX_NICKNAME
);
5670 warn
[MAX_NICKNAME
] = '\0';
5672 al
->num_in_dst
= dst_enum
;
5681 * Print out the display list.
5686 int do_entry
= 0, curopen
;
5689 dprint((2, "- ab_print -\n"));
5691 curopen
= cur_is_open();
5692 if(!agg
&& curopen
){
5693 static ESCKEY_S prt
[] = {
5694 {'a', 'a', "A", N_("AddressBook")},
5695 {'e', 'e', "E", N_("Entry")},
5696 {-1, 0, NULL
, NULL
}};
5698 prompt
= _("Print Address Book or just this Entry? ");
5699 switch(radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), prt
, 'a', 'x',
5702 q_status_message(SM_INFO
, 0, 2, _("Address book print cancelled"));
5703 ps_global
->mangled_footer
= 1;
5716 /* TRANSLATORS: This is input for
5717 Print something1 using something2. The thing we're
5718 defining here is something1. */
5720 prompt
= _("selected entries");
5723 prompt
= _("address book list");
5725 prompt
= _("entry");
5727 prompt
= _("address book");
5730 if(open_printer(prompt
) == 0){
5731 DL_CACHE_S dlc_buf
, *match_dlc
;
5737 char more_spaces
[100];
5741 save_line
= as
.top_ent
+ as
.cur_row
;
5742 match_dlc
= get_dlc(save_line
);
5743 dlc_buf
= *match_dlc
;
5744 match_dlc
= &dlc_buf
;
5746 if(do_entry
){ /* print an individual addrbook entry */
5748 abook_indent
= utf8_width(_("Nickname")) + 2;
5750 snprintf(spaces
, sizeof(spaces
), "%*.*s", abook_indent
+2, abook_indent
+2, "");
5751 snprintf(more_spaces
, sizeof(more_spaces
), "%*.*s",
5752 abook_indent
+4, abook_indent
+4, "");
5754 dl
= dlist(save_line
);
5755 abe
= ae(save_line
);
5758 int are_some_unqualified
= 0, expand_nicks
= 0;
5760 ADDRESS
*adrlist
= NULL
;
5763 * Search through the addresses to see if there are any
5764 * that are unqualified, and so would be different if
5767 if(abe
->tag
== Single
){
5768 if(abe
->addr
.addr
&& abe
->addr
.addr
[0]
5769 && !strindex(abe
->addr
.addr
, '@'))
5770 are_some_unqualified
++;
5775 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5776 if(!strindex(*ll
, '@')){
5777 are_some_unqualified
++;
5783 if(are_some_unqualified
){
5784 switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward
,
5787 q_status_message(SM_INFO
, 0, 2, _("Address book print cancelled"));
5788 ps_global
->mangled_footer
= 1;
5801 /* expand nicknames and fully-qualify unqualified names */
5805 char *init_addr
= NULL
;
5807 if(abe
->tag
== Single
)
5808 init_addr
= cpystr(abe
->addr
.addr
);
5814 /* figure out how large a string we need to allocate */
5816 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++)
5817 length
+= (strlen(*ll
) + 2);
5822 init_addr
= (char *)fs_get((size_t)(length
+1L) *
5826 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5827 sstrncpy(&p
, *ll
, length
-(p
-init_addr
));
5829 sstrncpy(&p
, ", ", length
-(p
-init_addr
));
5832 init_addr
[length
] = '\0';
5836 bldto
.arg
.str
= init_addr
;
5837 our_build_address(bldto
, &addr
, &error
, NULL
, NULL
);
5839 fs_give((void **)&init_addr
);
5842 q_status_message1(SM_ORDER
, 0, 4, "%s", error
);
5843 fs_give((void **)&error
);
5845 ps_global
->mangled_footer
= 1;
5850 rfc822_parse_adrlist(&adrlist
, addr
,
5851 ps_global
->maildomain
);
5852 fs_give((void **)&addr
);
5855 /* Will use adrlist to do the printing below */
5858 tmp
= abe
->nickname
? abe
->nickname
: "";
5859 string
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, SIZEOF_20KBUF
, tmp
);
5860 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Nickname"));
5861 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5863 fs_give((void **)&tmp
);
5866 tmp
= abe
->fullname
? abe
->fullname
: "";
5867 string
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, SIZEOF_20KBUF
, tmp
);
5868 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fullname"));
5869 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5871 fs_give((void **)&tmp
);
5874 tmp
= abe
->fcc
? abe
->fcc
: "";
5875 string
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, SIZEOF_20KBUF
, tmp
);
5876 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Fcc"));
5877 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5879 fs_give((void **)&tmp
);
5882 tmp
= abe
->extra
? abe
->extra
: "";
5883 {unsigned char *p
, *bb
= NULL
;
5885 if((n
= 4*strlen(tmp
)) > SIZEOF_20KBUF
-1){
5887 p
= bb
= (unsigned char *)fs_get(len
* sizeof(char));
5890 len
= SIZEOF_20KBUF
;
5891 p
= (unsigned char *)tmp_20k_buf
;
5894 string
= (char *)rfc1522_decode_to_utf8(p
, len
, tmp
);
5895 utf8_snprintf(b
, len
, "%-*.*w: ", abook_indent
, abook_indent
, _("Comment"));
5896 if((tmp
= fold(string
, 80, 80, b
, spaces
, FLD_NONE
)) != NULL
){
5898 fs_give((void **)&tmp
);
5902 fs_give((void **)&bb
);
5912 for(a
= adrlist
; a
; a
= a
->next
){
5917 next_addr
= a
->next
;
5920 bufp
= (char *) fs_get(len
* sizeof(char));
5921 tmp
= addr_string(a
, bufp
, len
);
5922 a
->next
= next_addr
;
5923 string
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
+10000,
5924 SIZEOF_20KBUF
-10000, tmp
);
5925 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
5926 if((tmp
= fold(string
, 80, 80,
5927 (a
== adrlist
) ? b
: spaces
,
5928 more_spaces
, FLD_NONE
)) != NULL
){
5930 fs_give((void **)&tmp
);
5933 fs_give((void **)&bufp
);
5937 mail_free_address(&adrlist
);
5939 utf8_snprintf(b
, sizeof(b
), "%-*.*w:\n", abook_indent
, abook_indent
, _("Addresses"));
5943 else{ /* don't expand or qualify */
5944 if(abe
->tag
== Single
){
5945 tmp
= abe
->addr
.addr
? abe
->addr
.addr
: "";
5946 string
= (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf
+10000),
5947 SIZEOF_20KBUF
-10000, tmp
);
5948 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
5949 if((tmp
= fold(string
, 80, 80, b
,
5950 more_spaces
, FLD_NONE
)) != NULL
){
5952 fs_give((void **)&tmp
);
5958 if(!abe
->addr
.list
|| !abe
->addr
.list
[0]){
5959 utf8_snprintf(b
, sizeof(b
), "%-*.*w:\n", abook_indent
, abook_indent
, _("Addresses"));
5963 for(ll
= abe
->addr
.list
; ll
&& *ll
; ll
++){
5964 string
= (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf
+10000),
5965 SIZEOF_20KBUF
-10000, *ll
);
5966 utf8_snprintf(b
, sizeof(b
), "%-*.*w: ", abook_indent
, abook_indent
, _("Addresses"));
5967 if((tmp
= fold(string
, 80, 80,
5968 (ll
== abe
->addr
.list
)
5970 more_spaces
, FLD_NONE
)) != NULL
){
5972 fs_give((void **)&tmp
);
5981 char lbuf
[6*MAX_SCREEN_COLS
+ 1];
5983 int i
, savecur
, savezoomed
;
5984 OpenStatus savestatus
;
5987 if(agg
){ /* print all selected entries */
5988 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
5989 savezoomed
= as
.zoomed
;
5991 * Fool display code into thinking display is zoomed, so
5992 * we'll skip the unselected entries. Since this feature
5993 * causes all abooks to be displayed we don't have to
5994 * step through each addrbook.
5997 warp_to_beginning();
5999 for(dl
= dlist(lineno
);
6001 dl
= dlist(++lineno
)){
6018 p
= get_abook_display_line(lineno
, 0, NULL
, NULL
,
6019 NULL
, lbuf
, sizeof(lbuf
));
6020 print_text1("%s\n", p
);
6023 as
.zoomed
= savezoomed
;
6025 else{ /* print all selected entries */
6027 savezoomed
= as
.zoomed
;
6030 for(i
= 0; i
< as
.n_addrbk
; i
++){
6031 pab
= &as
.adrbks
[i
];
6032 if(!(pab
->address_book
&&
6033 any_selected(pab
->address_book
->selects
)))
6037 * Print selected entries from addrbook i.
6038 * We have to put addrbook i into Open state so
6039 * that the display code will work right.
6042 savestatus
= pab
->ostatus
;
6043 init_abook(pab
, Open
);
6044 init_disp_form(pab
, ps_global
->VAR_ABOOK_FORMATS
, i
);
6045 (void)calculate_field_widths();
6046 warp_to_beginning();
6049 for(dl
= dlist(lineno
);
6051 dl
= dlist(++lineno
)){
6062 p
= get_abook_display_line(lineno
, 0, NULL
, NULL
,
6063 NULL
, lbuf
,sizeof(lbuf
));
6064 print_text1("%s\n", p
);
6067 init_abook(pab
, savestatus
);
6071 as
.zoomed
= savezoomed
;
6072 /* restore the display for the current addrbook */
6073 init_disp_form(&as
.adrbks
[as
.cur
],
6074 ps_global
->VAR_ABOOK_FORMATS
, as
.cur
);
6077 else{ /* print either the abook list or a single abook */
6081 savezoomed
= as
.zoomed
;
6084 if(curopen
){ /* print a single address book */
6085 anum
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
6086 warp_to_top_of_abook(anum
);
6087 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
))
6088 lineno
= 0L - XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP
;
6091 print_text(_("ADDRESS BOOK"));
6092 print_text1(" %s\n\n", as
.adrbks
[as
.cur
].abnick
);
6096 else{ /* print the list of address books */
6097 warp_to_beginning();
6101 for(dl
= dlist(lineno
);
6102 dl
->type
!= End
&& (!curopen
||
6103 (anum
==adrbk_num_from_lineno(lineno
) &&
6105 ((dlc
=get_dlc(lineno
)) &&
6106 dlc
->type
!= DlcDirDelim1
))));
6107 dl
= dlist(++lineno
)){
6118 p
= get_abook_display_line(lineno
, 0, NULL
, NULL
, NULL
,
6119 lbuf
, sizeof(lbuf
));
6120 print_text1("%s\n", p
);
6123 as
.zoomed
= savezoomed
;
6130 * jump cache back to where we started so that the next
6131 * request won't cause us to page through the whole thing
6134 warp_to_dlc(match_dlc
, save_line
);
6136 ps_global
->mangled_screen
= 1;
6139 ps_global
->mangled_footer
= 1;
6145 * Delete address book entries.
6148 ab_agg_delete(struct pine
*ps
, int agg
)
6150 int ret
= 0, i
, ch
, rc
= 0;
6152 adrbk_cntr_t num
, ab_count
;
6155 dprint((2, "- ab_agg_delete -\n"));
6158 snprintf(prompt
, sizeof(prompt
), _("Really delete %d selected entries"), as
.selections
);
6159 prompt
[sizeof(prompt
)-1] = '\0';
6160 ch
= want_to(prompt
, 'n', 'n', NO_HELP
, WT_NORM
);
6162 adrbk_cntr_t newelnum
, flushelnum
= NO_NEXT
;
6163 DL_CACHE_S dlc_save
, dlc_restart
, *dlc
;
6165 int top_level_display
;
6168 * We want to try to put the cursor in a reasonable position
6169 * on the screen when we're done. If we are in the top-level
6170 * display, then we can leave it the same. If we have an
6171 * addrbook opened, then we want to see if we can get back to
6172 * the same entry we are currently on.
6174 if(!(top_level_display
= !any_ab_open())){
6175 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
6177 newelnum
= dlc_save
.dlcelnum
;
6180 we_cancel
= busy_cue(NULL
, NULL
, 1);
6182 for(i
= 0; i
< as
.n_addrbk
&& rc
!= -5; i
++){
6183 int orig_selected
, selected
;
6185 pab
= &as
.adrbks
[i
];
6186 if(!pab
->address_book
)
6189 ab_count
= adrbk_count(pab
->address_book
);
6191 selected
= howmany_selected(pab
->address_book
->selects
);
6192 orig_selected
= selected
;
6194 * Because deleting an entry causes the addrbook to be
6195 * immediately updated, we need to delete from higher entry
6196 * numbers to lower numbers. That way, entry number n is still
6197 * entry number n in the updated address book because we've
6198 * only deleted entries higher than n.
6200 for(num
= ab_count
-1; selected
> 0 && rc
== 0; num
--){
6201 if(entry_is_selected(pab
->address_book
->selects
,
6203 rc
= adrbk_delete(pab
->address_book
, (a_c_arg_t
)num
,
6209 * This is just here to help us reposition the cursor.
6211 if(!top_level_display
&& as
.cur
== i
&& rc
== 0){
6220 if(rc
== 0 && orig_selected
> 0){
6221 int sort_happened
= 0;
6223 rc
= adrbk_write(pab
->address_book
, 0, NULL
, &sort_happened
, 1, 0);
6225 ps_global
->mangled_screen
= 1;
6229 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
6230 "Error updating %s: %s",
6231 (as
.n_addrbk
> 1) ? pab
->abnick
6233 error_description(errno
));
6234 dprint((1, "Error updating %s: %s\n",
6235 pab
->filename
? pab
->filename
: "?",
6236 error_description(errno
)));
6241 cancel_busy_cue(-1);
6244 q_status_message(SM_ORDER
, 0, 2, _("Deletions completed"));
6249 if(!top_level_display
){
6253 if(flushelnum
!= dlc_save
.dlcelnum
){
6255 * We didn't delete current so restart there. The elnum
6256 * may have changed if we deleted entries above it.
6258 dlc_restart
= dlc_save
;
6259 dlc_restart
.dlcelnum
= newelnum
;
6263 * Current was deleted.
6265 dlc_restart
.adrbk_num
= as
.cur
;
6266 pab
= &as
.adrbks
[as
.cur
];
6267 ab_count
= adrbk_count(pab
->address_book
);
6269 dlc_restart
.type
= DlcEmpty
;
6273 dlc_restart
.dlcelnum
= MIN(newelnum
, ab_count
-1);
6274 abe
= adrbk_get_ae(pab
->address_book
,
6275 (a_c_arg_t
) dlc_restart
.dlcelnum
);
6276 if(abe
&& abe
->tag
== Single
)
6277 dlc_restart
.type
= DlcSimple
;
6278 else if(abe
&& abe
->tag
== List
)
6279 dlc_restart
.type
= DlcListHead
;
6286 warp_to_top_of_abook(as
.cur
);
6288 new_ent
= first_selectable_line(0L);
6289 if(new_ent
== NO_LINE
)
6292 as
.cur_row
= new_ent
;
6294 /* if it is off screen */
6295 if(as
.cur_row
>= as
.l_p_page
){
6296 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
6297 as
.cur_row
= (as
.l_p_page
- 1);
6300 else if(dlc_restart
.type
!= DlcEmpty
&&
6301 dlc_restart
.dlcelnum
== dlc_save
.dlcelnum
&&
6302 (F_OFF(F_CMBND_ABOOK_DISP
,ps_global
) || as
.cur
== 0)){
6304 * Didn't delete any before this line.
6305 * Leave screen about the same. (May have deleted current.)
6307 warp_to_dlc(&dlc_restart
, as
.cur_row
+as
.top_ent
);
6310 warp_to_dlc(&dlc_restart
, 0L);
6311 /* put in middle of screen */
6312 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
6313 as
.cur_row
= 0L - as
.top_ent
;
6316 ps
->mangled_body
= 1;
6320 cmd_cancelled("Apply Delete command");
6328 * Delete an entry from the address book
6330 * Args: abook -- The addrbook handle into access library
6331 * command_line -- The screen line on which to prompt
6332 * cur_line -- The entry number in the display list
6333 * warped -- We warped to a new part of the addrbook
6335 * Result: returns 1 if an entry was deleted, 0 if not.
6337 * The main routine above knows what to repaint because it's always the
6338 * current entry that's deleted. Here confirmation is asked of the user
6339 * and the appropriate adrbklib functions are called.
6342 single_entry_delete(AdrBk
*abook
, long int cur_line
, int *warped
)
6344 char ch
, *cmd
, *dname
;
6347 register AddrScrn_Disp
*dl
;
6349 DL_CACHE_S
*dlc_to_flush
;
6351 dprint((2, "- single_entry_delete -\n"));
6356 dl
= dlist(cur_line
);
6357 abe
= adrbk_get_ae(abook
, (a_c_arg_t
) dl
->elnum
);
6361 dname
= (abe
->fullname
&& abe
->fullname
[0])
6362 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6363 SIZEOF_20KBUF
, abe
->fullname
)
6364 : abe
->nickname
? abe
->nickname
: "";
6365 cmd
= _("Really delete \"%s\"");
6369 dname
= (abe
->fullname
&& abe
->fullname
[0])
6370 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6371 SIZEOF_20KBUF
, abe
->fullname
)
6372 : abe
->nickname
? abe
->nickname
: "";
6373 cmd
= _("Really delete ENTIRE list \"%s\"");
6377 dname
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6378 SIZEOF_20KBUF
, listmem_from_dl(abook
, dl
));
6379 cmd
= _("Really delete \"%s\" from list");
6386 dname
= dname
? dname
: "";
6387 cmd
= cmd
? cmd
: "";
6389 snprintf(prompt
, sizeof(prompt
), cmd
, dname
);
6390 prompt
[sizeof(prompt
)-1] = '\0';
6391 ch
= want_to(prompt
, 'n', 'n', NO_HELP
, WT_NORM
);
6393 dlc_to_flush
= get_dlc(cur_line
);
6394 if(dl
->type
== Simple
|| dl
->type
== ListHead
){
6395 /*--- Kill a single entry or an entire list ---*/
6396 rc
= adrbk_delete(abook
, (a_c_arg_t
)dl
->elnum
, 1, 1, 0, 1);
6398 else if(listmem_count_from_abe(abe
) > 2){
6399 /*---- Kill an entry out of a list ----*/
6400 rc
= adrbk_listdel(abook
, (a_c_arg_t
)dl
->elnum
,
6401 listmem_from_dl(abook
, dl
));
6404 char *nick
, *full
, *addr
, *fcc
, *comment
;
6405 adrbk_cntr_t new_entry_num
= NO_NEXT
;
6407 /*---- Convert a List to a Single entry ----*/
6409 /* Save old info to be transferred */
6410 nick
= cpystr(abe
->nickname
);
6411 full
= cpystr(abe
->fullname
);
6412 fcc
= cpystr(abe
->fcc
);
6413 comment
= cpystr(abe
->extra
);
6414 if(listmem_count_from_abe(abe
) == 2)
6415 addr
= cpystr(abe
->addr
.list
[1 - dl
->l_offset
]);
6419 rc
= adrbk_delete(abook
, (a_c_arg_t
)dl
->elnum
, 0, 1, 0, 0);
6435 fs_give((void **)&nick
);
6436 fs_give((void **)&full
);
6437 fs_give((void **)&fcc
);
6438 fs_give((void **)&comment
);
6439 fs_give((void **)&addr
);
6442 DL_CACHE_S dlc_restart
;
6444 dlc_restart
.adrbk_num
= as
.cur
;
6445 dlc_restart
.dlcelnum
= new_entry_num
;
6446 dlc_restart
.type
= DlcSimple
;
6447 warp_to_dlc(&dlc_restart
, 0L);
6454 q_status_message(SM_ORDER
, 0, 3,
6455 _("Entry deleted, address book updated"));
6456 dprint((5, "abook: Entry %s\n",
6457 (dl
->type
== Simple
|| dl
->type
== ListHead
) ? "deleted"
6460 * Remove deleted line and everything after it from
6461 * the dlc cache. Next time we try to access those lines they
6462 * will get filled in with the right info.
6464 flush_dlc_from_cache(dlc_to_flush
);
6471 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
6472 _("Error updating address book: %s"),
6473 error_description(errno
));
6474 pab
= &as
.adrbks
[as
.cur
];
6475 dprint((1, "Error deleting entry from %s (%s): %s\n",
6476 pab
->abnick
? pab
->abnick
: "?",
6477 pab
->filename
? pab
->filename
: "?",
6478 error_description(errno
)));
6484 q_status_message(SM_INFO
, 0, 2, _("Entry not deleted"));
6491 free_headents(struct headerentry
**head
)
6493 struct headerentry
*he
;
6497 for(he
= *head
; he
->name
; he
++)
6498 if(he
->bldr_private
){
6499 pt
= (PrivateTop
*)he
->bldr_private
;
6500 free_privatetop(&pt
);
6503 fs_give((void **)head
);
6510 prompt::name::help::prwid::maxlen::realaddr::
6511 builder::affected_entry::next_affected::selector::key_label::fileedit::
6512 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
6513 single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
6515 static struct headerentry headents_for_query
[]={
6516 {"Normal Search : ", "NormalSearch", h_composer_qserv_qq
, 16, 0, NULL
,
6517 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6518 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6519 {"Name : ", "Name", h_composer_qserv_cn
, 16, 0, NULL
,
6520 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6521 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6522 {"Surname : ", "SurName", h_composer_qserv_sn
, 16, 0, NULL
,
6523 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6524 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6525 {"Given Name : ", "GivenName", h_composer_qserv_gn
, 16, 0, NULL
,
6526 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6527 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6528 {"Email Address : ", "EmailAddress", h_composer_qserv_mail
, 16, 0, NULL
,
6529 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6530 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6531 {"Organization : ", "Organization", h_composer_qserv_org
, 16, 0, NULL
,
6532 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6533 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6534 {"Org Unit : ", "OrganizationalUnit", h_composer_qserv_unit
, 16, 0, NULL
,
6535 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6536 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6537 {"Country : ", "Country", h_composer_qserv_country
, 16, 0, NULL
,
6538 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6539 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6540 {"State : ", "State", h_composer_qserv_state
, 16, 0, NULL
,
6541 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6542 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6543 {"Locality : ", "Locality", h_composer_qserv_locality
, 16, 0, NULL
,
6544 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6545 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6546 {" ", "BlankLine", NO_HELP
, 1, 0, NULL
,
6547 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6548 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_NONE
},
6549 {"Custom Filter : ", "CustomFilter", h_composer_qserv_custom
, 16, 0, NULL
,
6550 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6551 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
6552 {NULL
, NULL
, NO_HELP
, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
6553 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
6562 #define QQ_COUNTRY 7
6564 #define QQ_LOCALITY 9
6566 #define QQ_CUSTOM 11
6569 static SAVED_QUERY_S
*saved_params
;
6573 * Formulate a query for LDAP servers and send it and view it.
6574 * This is called from the Address Book screen, either from ^T from the
6575 * composer (selecting) or just when browsing in the address book screen
6578 * Args ps -- Pine struct
6579 * selecting -- This is set if we're just selecting an address, as opposed
6580 * to browsing on an LDAP server
6581 * who -- Tells us which server to query
6582 * error -- An error message allocated here and freed by caller.
6584 * Returns -- Null if not selecting, possibly an address if selecting.
6585 * The address is 1522 decoded and should be freed by the caller.
6588 query_server(struct pine
*ps
, int selecting
, int *exit
, int who
, char **error
)
6590 struct headerentry
*he
= NULL
;
6592 STORE_S
*msgso
= NULL
;
6593 int i
, lret
, editor_result
;
6595 HelpType help
= NO_HELP
;
6596 #define FILTSIZE 1000
6597 char fbuf
[FILTSIZE
+1];
6599 LDAP_CHOOSE_S
*winning_e
= NULL
;
6600 LDAP_SERV_RES_S
*free_when_done
= NULL
;
6601 SAVED_QUERY_S
*sq
= NULL
;
6602 static ESCKEY_S ekey
[] = {
6603 /* TRANSLATORS: go to more complex search screen */
6604 {ctrl('T'), 10, "^T", N_("To complex search")},
6608 dprint((2, "- query_server(%s) -\n", selecting
?"Selecting":""));
6610 if(!(ps
->VAR_LDAP_SERVERS
&& ps
->VAR_LDAP_SERVERS
[0] &&
6611 ps
->VAR_LDAP_SERVERS
[0][0])){
6613 *error
= cpystr(_("No LDAP server available for lookup"));
6619 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
6620 while(r
== 4 || r
== 3){
6621 /* TRANSLATORS: we're asking for a character string to search for */
6622 r
= optionally_enter(fbuf
, -FOOTER_ROWS(ps
), 0, sizeof(fbuf
),
6623 _("String to search for : "),
6624 ekey
, help
, &flags
);
6626 help
= help
== NO_HELP
? h_dir_comp_search
: NO_HELP
;
6629 /* strip quotes that user typed by mistake */
6630 (void)removing_double_quotes(fbuf
);
6632 if(r
== 1 || (r
!= 10 && fbuf
[0] == '\0')){
6633 ps
->mangled_footer
= 1;
6635 *error
= cpystr(_("Cancelled"));
6640 editor_result
= COMP_EXIT
; /* just to get right logic below */
6642 memset((void *)&pbf
, 0, sizeof(pbf
));
6645 standard_picobuf_setup(&pbf
);
6646 pbf
.exittest
= pico_simpleexit
;
6647 pbf
.exit_label
= _("Search");
6648 pbf
.canceltest
= pico_simplecancel
;
6650 pbf
.expander
= restore_query_parameters
;
6651 pbf
.ctrlr_label
= _("Restore");
6654 pbf
.pine_anchor
= set_titlebar(_("SEARCH DIRECTORY SERVER"),
6655 ps_global
->mail_stream
,
6656 ps_global
->context_current
,
6657 ps_global
->cur_folder
,
6659 0, FolderName
, 0, 0, NULL
);
6660 pbf
.pine_flags
|= P_NOBODY
;
6662 /* An informational message */
6663 if((msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
6664 pbf
.msgtext
= (void *)so_text(msgso
);
6666 * It's nice if we can make it so these lines make sense even if
6667 * they don't all make it on the screen, because the user can't
6668 * scroll down to see them. So just make each line a whole sentence
6669 * that doesn't need the others below it to make sense.
6672 _("\n Fill in some of the fields above to create a query."));
6674 _("\n The match will be for the exact string unless you include wildcards (*)."));
6676 _("\n All filled-in fields must match in order to be counted as a match."));
6678 _("\n Press \"^R\" to restore previous query values (if you've queried previously)."));
6680 _("\n \"^G\" for help specific to each item. \"^X\" to make the query, or \"^C\" to cancel."));
6683 he
= (struct headerentry
*)fs_get((QQ_END
+1) *
6684 sizeof(struct headerentry
));
6685 memset((void *)he
, 0, (QQ_END
+1) * sizeof(struct headerentry
));
6686 for(i
= QQ_QQ
; i
<= QQ_END
; i
++)
6687 he
[i
] = headents_for_query
[i
];
6691 sq
= copy_query_parameters(NULL
);
6692 he
[QQ_QQ
].realaddr
= &sq
->qq
;
6693 he
[QQ_CN
].realaddr
= &sq
->cn
;
6694 he
[QQ_SN
].realaddr
= &sq
->sn
;
6695 he
[QQ_GN
].realaddr
= &sq
->gn
;
6696 he
[QQ_MAIL
].realaddr
= &sq
->mail
;
6697 he
[QQ_ORG
].realaddr
= &sq
->org
;
6698 he
[QQ_UNIT
].realaddr
= &sq
->unit
;
6699 he
[QQ_COUNTRY
].realaddr
= &sq
->country
;
6700 he
[QQ_STATE
].realaddr
= &sq
->state
;
6701 he
[QQ_LOCALITY
].realaddr
= &sq
->locality
;
6702 he
[QQ_CUSTOM
].realaddr
= &sq
->custom
;
6704 /* pass to pico and let user set them */
6705 editor_result
= pico(&pbf
);
6706 ps
->mangled_screen
= 1;
6707 standard_picobuf_teardown(&pbf
);
6709 if(editor_result
& COMP_GOTHUP
)
6712 fix_windsize(ps_global
);
6717 if(editor_result
& COMP_EXIT
&&
6718 ((r
== 0 && *fbuf
) ||
6720 (*sq
->qq
|| *sq
->cn
|| *sq
->sn
|| *sq
->gn
|| *sq
->mail
||
6721 *sq
->org
|| *sq
->unit
|| *sq
->country
|| *sq
->state
||
6722 *sq
->locality
|| *sq
->custom
)))){
6723 LDAPLookupStyle style
;
6725 int need_and
, mangled
;
6727 CUSTOM_FILT_S
*filter
;
6730 s
= copy_query_parameters(sq
);
6731 save_query_parameters(s
);
6740 categories
= ((*sq
->cn
!= '\0') ? 1 : 0) +
6741 ((*sq
->sn
!= '\0') ? 1 : 0) +
6742 ((*sq
->gn
!= '\0') ? 1 : 0) +
6743 ((*sq
->mail
!= '\0') ? 1 : 0) +
6744 ((*sq
->org
!= '\0') ? 1 : 0) +
6745 ((*sq
->unit
!= '\0') ? 1 : 0) +
6746 ((*sq
->country
!= '\0') ? 1 : 0) +
6747 ((*sq
->state
!= '\0') ? 1 : 0) +
6748 ((*sq
->locality
!= '\0') ? 1 : 0);
6749 need_and
= (categories
> 1);
6751 if(((sq
->cn
? strlen(sq
->cn
) : 0) +
6752 (sq
->sn
? strlen(sq
->sn
) : 0) +
6753 (sq
->gn
? strlen(sq
->gn
) : 0) +
6754 (sq
->mail
? strlen(sq
->mail
) : 0) +
6755 (sq
->org
? strlen(sq
->org
) : 0) +
6756 (sq
->unit
? strlen(sq
->unit
) : 0) +
6757 (sq
->country
? strlen(sq
->country
) : 0) +
6758 (sq
->state
? strlen(sq
->state
) : 0) +
6759 (sq
->locality
? strlen(sq
->locality
) : 0)) > FILTSIZE
- 100){
6761 *error
= cpystr(_("Search strings too long"));
6768 snprintf(fbuf
, sizeof(fbuf
),
6769 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
6770 need_and
? "(&" : "",
6771 *sq
->cn
? "(cn=" : "",
6772 *sq
->cn
? sq
->cn
: "",
6774 *sq
->sn
? "(sn=" : "",
6775 *sq
->sn
? sq
->sn
: "",
6777 *sq
->gn
? "(givenname=" : "",
6778 *sq
->gn
? sq
->gn
: "",
6780 *sq
->mail
? "(mail=" : "",
6781 *sq
->mail
? sq
->mail
: "",
6782 *sq
->mail
? ")" : "",
6783 *sq
->org
? "(o=" : "",
6784 *sq
->org
? sq
->org
: "",
6785 *sq
->org
? ")" : "",
6786 *sq
->unit
? "(ou=" : "",
6787 *sq
->unit
? sq
->unit
: "",
6788 *sq
->unit
? ")" : "",
6789 *sq
->country
? "(c=" : "",
6790 *sq
->country
? sq
->country
: "",
6791 *sq
->country
? ")" : "",
6792 *sq
->state
? "(st=" : "",
6793 *sq
->state
? sq
->state
: "",
6794 *sq
->state
? ")" : "",
6795 *sq
->locality
? "(l=" : "",
6796 *sq
->locality
? sq
->locality
: "",
6797 *sq
->locality
? ")" : "",
6798 need_and
? ")" : "");
6799 fbuf
[sizeof(fbuf
)-1] = '\0';
6802 if(categories
> 0 || *sq
->custom
)
6803 filter
= (CUSTOM_FILT_S
*)fs_get(sizeof(CUSTOM_FILT_S
));
6805 /* combine the configured filters with this filter */
6808 filter
->filt
= sq
->custom
;
6809 filter
->combine
= 0;
6811 else if(*sq
->qq
&& categories
> 0){
6813 filter
->filt
= fbuf
;
6814 filter
->combine
= 1;
6816 else if(categories
> 0){
6818 filter
->filt
= fbuf
;
6819 filter
->combine
= 0;
6828 memset(&wp_err
, 0, sizeof(wp_err
));
6829 wp_err
.mangled
= &mangled
;
6830 style
= selecting
? AlwaysDisplayAndMailRequired
: AlwaysDisplay
;
6832 /* maybe coming from composer */
6833 fix_windsize(ps_global
);
6837 lret
= ldap_lookup_all(string
, who
, 0, style
, filter
, &winning_e
,
6838 &wp_err
, &free_when_done
);
6841 fs_give((void **)&filter
);
6844 ps
->mangled_screen
= 1;
6847 if(status_message_remaining() && error
)
6848 *error
= wp_err
.error
;
6850 fs_give((void **)&wp_err
.error
);
6853 if(lret
== 0 && winning_e
&& selecting
){
6856 addr
= address_from_ldap(winning_e
);
6859 addr
->host
= cpystr("missing-hostname");
6862 fs_give((void **)error
);
6864 *error
= cpystr(_("Missing hostname in LDAP address"));
6868 ret
= addr_list_string(addr
, NULL
, 1);
6869 if(!ret
|| !ret
[0]){
6871 fs_give((void **)&ret
);
6876 if(error
&& !*error
){
6879 snprintf(buf
, sizeof(buf
), _("No email address available for \"%s\""),
6880 (addr
->personal
&& *addr
->personal
)
6882 : "selected entry");
6883 buf
[sizeof(buf
)-1] = '\0';
6884 *error
= cpystr(buf
);
6888 mail_free_address(&addr
);
6891 else if(lret
== -1 && exit
)
6900 free_ldap_result_list(&free_when_done
);
6903 fs_give((void **)&winning_e
);
6909 free_query_parameters(&sq
);
6916 * View all fields of an LDAP entry while browsing.
6918 * Args ps -- Pine struct
6919 * winning_e -- The struct containing the information about the entry
6923 view_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
)
6925 STORE_S
*srcstore
= NULL
;
6926 SourceType srctype
= CharStar
;
6928 HANDLE_S
*handles
= NULL
;
6930 dprint((9, "- view_ldap_entry -\n"));
6932 if((srcstore
=prep_ldap_for_viewing(ps
,winning_e
,srctype
,&handles
)) != NULL
){
6933 memset(&sargs
, 0, sizeof(SCROLL_S
));
6934 sargs
.text
.text
= so_text(srcstore
);
6935 sargs
.text
.src
= srctype
;
6936 sargs
.text
.desc
= _("expanded entry");
6937 sargs
.text
.handles
= handles
;
6938 sargs
.bar
.title
= _("DIRECTORY ENTRY");
6939 sargs
.proc
.tool
= process_ldap_cmd
;
6940 sargs
.proc
.data
.p
= (void *) winning_e
;
6941 sargs
.help
.text
= h_ldap_view
;
6942 sargs
.help
.title
= _("HELP FOR DIRECTORY VIEW");
6943 sargs
.keys
.menu
= &ldap_view_keymenu
;
6944 setbitmap(sargs
.keys
.bitmap
);
6947 sargs
.keys
.menu
->how_many
= 2;
6949 sargs
.keys
.menu
->how_many
= 1;
6950 clrbitn(OTHER_KEY
, sargs
.keys
.bitmap
);
6955 ps
->mangled_screen
= 1;
6957 free_handles(&handles
);
6960 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
6965 * Compose a message to the email addresses contained in an LDAP entry.
6967 * Args ps -- Pine struct
6968 * winning_e -- The struct containing the information about the entry
6972 compose_to_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*e
, int allow_role
)
6974 struct berval
**elecmail
= NULL
,
6982 dprint((9, "- compose_to_ldap_entry -\n"));
6988 for(a
= ldap_first_attribute(e
->ld
, e
->selected_entry
, &ber
);
6990 a
= ldap_next_attribute(e
->ld
, e
->selected_entry
, ber
)){
6992 if(strcmp(a
, e
->info_used
->mailattr
) == 0){
6994 mail
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
6996 else if(strcmp(a
, "electronicmail") == 0){
6998 elecmail
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7000 else if(strcmp(a
, e
->info_used
->cnattr
) == 0){
7002 cn
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7004 else if(strcmp(a
, e
->info_used
->gnattr
) == 0){
7006 givenname
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7008 else if(strcmp(a
, e
->info_used
->snattr
) == 0){
7010 sn
= ldap_get_values_len(e
->ld
, e
->selected_entry
, a
);
7013 our_ldap_memfree(a
);
7018 if(ALPINE_LDAP_can_use(elecmail
) && !mail
)
7021 ldap_value_free_len(elecmail
);
7026 for(num
= 0; ALPINE_LDAP_usable(mail
, num
); num
++)
7027 len
+= strlen(mail
[num
]->bv_val
) + 1;
7030 char *p
, *address
, *fn
= NULL
;
7034 address
= (char *)fs_get(len
* sizeof(char));
7037 while(ALPINE_LDAP_usable(mail
, num
)){
7038 sstrncpy(&p
, mail
[num
]->bv_val
, len
-(p
-address
));
7041 sstrncpy(&p
, ",", len
-(p
-address
));
7044 address
[len
-1] = '\0';
7047 * If we have a fullname and there is only a single address and
7048 * the address doesn't seem to have a fullname with it, add it.
7050 if(ALPINE_LDAP_can_use(mail
) && !mail
[1]){
7051 if(ALPINE_LDAP_can_use(cn
))
7052 fn
= cpystr(cn
[0]->bv_val
);
7053 else if(ALPINE_LDAP_can_use(sn
) && ALPINE_LDAP_can_use(givenname
)){
7056 l
= strlen(givenname
[0]->bv_val
) + strlen(sn
[0]->bv_val
) + 1;
7057 fn
= (char *) fs_get((l
+1) * sizeof(char));
7058 snprintf(fn
, l
+1, "%s %s", givenname
[0]->bv_val
, sn
[0]->bv_val
);
7063 if(ALPINE_LDAP_can_use(mail
) && !mail
[1] && fn
){
7064 ADDRESS
*adrlist
= NULL
;
7067 tmp_a_string
= cpystr(address
);
7068 rfc822_parse_adrlist(&adrlist
, tmp_a_string
, fakedomain
);
7069 fs_give((void **)&tmp_a_string
);
7070 if(adrlist
&& !adrlist
->next
&& !adrlist
->personal
){
7075 adrlist
->personal
= cpystr(fn
);
7076 len
= est_size(adrlist
);
7077 new_address
= (char *) fs_get(len
* sizeof(char));
7078 new_address
[0] ='\0';
7079 rbuf
.f
= dummy_soutr
;
7081 rbuf
.beg
= new_address
;
7082 rbuf
.cur
= new_address
;
7083 rbuf
.end
= new_address
+len
-1;
7084 /* this will quote it if it needs quoting */
7085 rfc822_output_address_list(&rbuf
, adrlist
, 0L, NULL
);
7087 fs_give((void **)&address
);
7088 address
= new_address
;
7092 mail_free_address(&adrlist
);
7096 bldto
.arg
.str
= address
;
7099 ab_compose_internal(bldto
, allow_role
);
7100 restore_state(&state
);
7101 fs_give((void **)&address
);
7103 fs_give((void **)&fn
);
7106 * Window size may have changed in composer.
7107 * Pine_send will have reset the window size correctly,
7108 * but we still have to reset our address book data structures.
7113 ps_global
->mangled_screen
= 1;
7116 q_status_message(SM_ORDER
, 0, 4, _("No address to compose to"));
7119 ldap_value_free_len(mail
);
7121 ldap_value_free_len(cn
);
7123 ldap_value_free_len(sn
);
7125 ldap_value_free_len(givenname
);
7130 * Forward the text of an LDAP entry via email (not a vcard attachment)
7132 * Args ps -- Pine struct
7133 * winning_e -- The struct containing the information about the entry
7137 forward_ldap_entry(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
)
7139 STORE_S
*srcstore
= NULL
;
7140 SourceType srctype
= CharStar
;
7142 dprint((9, "- forward_ldap_entry -\n"));
7144 if((srcstore
= prep_ldap_for_viewing(ps
,winning_e
,srctype
,NULL
)) != NULL
){
7145 forward_text(ps
, so_text(srcstore
), srctype
);
7146 ps
->mangled_screen
= 1;
7150 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
7155 prep_ldap_for_viewing(struct pine
*ps
, LDAP_CHOOSE_S
*winning_e
, SourceType srctype
, HANDLE_S
**handlesp
)
7157 STORE_S
*store
= NULL
;
7162 #define INDENTHERE (22)
7164 char hdr
[6*INDENTHERE
+1], hdr2
[6*INDENTHERE
+1];
7165 struct berval
**cn
= NULL
;
7166 int indent
= INDENTHERE
;
7168 if(!(store
= so_get(srctype
, NULL
, EDIT_ACCESS
)))
7171 /* for mailto handles so user can select individual email addrs */
7173 init_handles(handlesp
);
7175 width
= MAX(ps
->ttyo
->screen_cols
, 25);
7177 snprintf(hdr2
, sizeof(hdr2
), "%-*.*s: ", indent
-2,indent
-2, "");
7178 hdr2
[sizeof(hdr2
)-1] = '\0';
7180 if(sizeof(obuf
) > ps
->ttyo
->screen_cols
+1){
7181 memset((void *)obuf
, '-', ps
->ttyo
->screen_cols
* sizeof(char));
7182 obuf
[ps
->ttyo
->screen_cols
] = '\n';
7183 obuf
[ps
->ttyo
->screen_cols
+1] = '\0';
7186 a
= ldap_get_dn(winning_e
->ld
, winning_e
->selected_entry
);
7188 so_puts(store
, obuf
);
7189 if((tmp
= fold(a
, width
, width
, "", " ", FLD_NONE
)) != NULL
){
7190 so_puts(store
, tmp
);
7191 fs_give((void **)&tmp
);
7194 so_puts(store
, obuf
);
7195 so_puts(store
, "\n");
7197 our_ldap_dn_memfree(a
);
7199 for(a
= ldap_first_attribute(winning_e
->ld
, winning_e
->selected_entry
, &ber
);
7201 a
= ldap_next_attribute(winning_e
->ld
, winning_e
->selected_entry
, ber
)){
7204 struct berval
**vals
;
7207 vals
= ldap_get_values_len(winning_e
->ld
, winning_e
->selected_entry
, a
);
7209 /* save this for mailto */
7210 if(handlesp
&& !cn
&& !strcmp(a
, winning_e
->info_used
->cnattr
))
7211 cn
= ldap_get_values_len(winning_e
->ld
, winning_e
->selected_entry
, a
);
7216 do_mailto
= (handlesp
&&
7217 !strcmp(a
, winning_e
->info_used
->mailattr
));
7219 utf8_snprintf(hdr
, sizeof(hdr
), "%-*.*w: ", indent
-2,indent
-2,
7220 ldap_translate(a
, winning_e
->info_used
));
7221 hdr
[sizeof(hdr
)-1] = '\0';
7222 for(i
= 0; ALPINE_LDAP_usable(vals
, i
); i
++){
7228 char *addr
, *new_addr
, *enc_addr
;
7231 addr
= cpystr(vals
[i
]->bv_val
);
7232 if(ALPINE_LDAP_can_use(cn
))
7233 fn
= cpystr(cn
[0]->bv_val
);
7236 tmp_a_string
= cpystr(addr
);
7237 rfc822_parse_adrlist(&ad
, tmp_a_string
, "@");
7238 fs_give((void **)&tmp_a_string
);
7239 if(ad
&& !ad
->next
&& !ad
->personal
){
7243 ad
->personal
= cpystr(fn
);
7245 new_addr
= (char *) fs_get(len
* sizeof(char));
7247 /* this will quote it if it needs quoting */
7248 rbuf
.f
= dummy_soutr
;
7250 rbuf
.beg
= new_addr
;
7251 rbuf
.cur
= new_addr
;
7252 rbuf
.end
= new_addr
+len
-1;
7253 rfc822_output_address_list(&rbuf
, ad
, 0L, NULL
);
7255 fs_give((void **) &addr
);
7260 mail_free_address(&ad
);
7262 fs_give((void **)&fn
);
7265 if((enc_addr
= rfc1738_encode_mailto(addr
)) != NULL
){
7268 l
= strlen(enc_addr
) + 7;
7269 path
= (char *) fs_get((l
+1) * sizeof(char));
7270 snprintf(path
, l
+1, "mailto:%s", enc_addr
);
7272 fs_give((void **)&enc_addr
);
7275 fs_give((void **)&addr
);
7278 h
= new_handle(handlesp
);
7280 h
->h
.url
.path
= path
;
7281 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
7282 buf
[sizeof(buf
)-1] = '\0';
7285 * Don't try to fold this address. Just put it on
7286 * one line and let scrolltool worry about
7287 * cutting it off before it goes past the
7288 * right hand edge. Otherwise, we have to figure
7289 * out how to handle the wrapped handle.
7291 snprintf(obuf
, sizeof(obuf
), "%c%c%c%s%s%c%c%c%c",
7292 TAG_EMBED
, TAG_HANDLE
,
7293 (int) strlen(buf
), buf
, vals
[i
]->bv_val
,
7294 TAG_EMBED
, TAG_BOLDOFF
,
7295 TAG_EMBED
, TAG_INVOFF
);
7296 obuf
[sizeof(obuf
)-1] = '\0';
7298 so_puts(store
, (i
==0) ? hdr
: hdr2
);
7299 so_puts(store
, obuf
);
7300 so_puts(store
, "\n");
7303 snprintf(obuf
, sizeof(obuf
), "%s", vals
[i
]->bv_val
);
7304 obuf
[sizeof(obuf
)-1] = '\0';
7306 if((tmp
= fold(obuf
, width
, width
,
7307 (i
==0) ? hdr
: hdr2
,
7308 repeat_char(indent
+2, SPACE
),
7309 FLD_NONE
)) != NULL
){
7310 so_puts(store
, tmp
);
7311 fs_give((void **)&tmp
);
7316 snprintf(obuf
, sizeof(obuf
), "%s", vals
[i
]->bv_val
);
7317 obuf
[sizeof(obuf
)-1] = '\0';
7319 if((tmp
= fold(obuf
, width
, width
,
7320 (i
==0) ? hdr
: hdr2
,
7321 repeat_char(indent
+2, SPACE
),
7322 FLD_NONE
)) != NULL
){
7323 so_puts(store
, tmp
);
7324 fs_give((void **)&tmp
);
7329 ldap_value_free_len(vals
);
7332 utf8_snprintf(obuf
, sizeof(obuf
), "%-*.*w\n", indent
-1,indent
-1,
7333 ldap_translate(a
, winning_e
->info_used
));
7334 obuf
[sizeof(obuf
)-1] = '\0';
7335 so_puts(store
, obuf
);
7339 our_ldap_memfree(a
);
7343 ldap_value_free_len(cn
);
7350 process_ldap_cmd(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
7354 ps_global
->next_screen
= SCREEN_FUN_NULL
;
7358 save_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 0);
7363 compose_to_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 0);
7368 compose_to_ldap_entry(ps_global
, sparms
->proc
.data
.p
, 1);
7373 alpine_panic("Unexpected command in process_ldap_cmd");
7382 pico_simpleexit(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
7392 pico_simplecancel(void (*redraw_pico
)(void))
7394 return("Cancelled");
7399 * Store query parameters so that they can be recalled by user later with ^R.
7402 save_query_parameters(SAVED_QUERY_S
*params
)
7404 free_saved_query_parameters();
7405 saved_params
= params
;
7410 copy_query_parameters(SAVED_QUERY_S
*params
)
7414 sq
= (SAVED_QUERY_S
*)fs_get(sizeof(SAVED_QUERY_S
));
7415 memset((void *)sq
, 0, sizeof(SAVED_QUERY_S
));
7417 if(params
&& params
->qq
)
7418 sq
->qq
= cpystr(params
->qq
);
7420 sq
->qq
= cpystr("");
7422 if(params
&& params
->cn
)
7423 sq
->cn
= cpystr(params
->cn
);
7425 sq
->cn
= cpystr("");
7427 if(params
&& params
->sn
)
7428 sq
->sn
= cpystr(params
->sn
);
7430 sq
->sn
= cpystr("");
7432 if(params
&& params
->gn
)
7433 sq
->gn
= cpystr(params
->gn
);
7435 sq
->gn
= cpystr("");
7437 if(params
&& params
->mail
)
7438 sq
->mail
= cpystr(params
->mail
);
7440 sq
->mail
= cpystr("");
7442 if(params
&& params
->org
)
7443 sq
->org
= cpystr(params
->org
);
7445 sq
->org
= cpystr("");
7447 if(params
&& params
->unit
)
7448 sq
->unit
= cpystr(params
->unit
);
7450 sq
->unit
= cpystr("");
7452 if(params
&& params
->country
)
7453 sq
->country
= cpystr(params
->country
);
7455 sq
->country
= cpystr("");
7457 if(params
&& params
->state
)
7458 sq
->state
= cpystr(params
->state
);
7460 sq
->state
= cpystr("");
7462 if(params
&& params
->locality
)
7463 sq
->locality
= cpystr(params
->locality
);
7465 sq
->locality
= cpystr("");
7467 if(params
&& params
->custom
)
7468 sq
->custom
= cpystr(params
->custom
);
7470 sq
->custom
= cpystr("");
7477 free_saved_query_parameters(void)
7480 free_query_parameters(&saved_params
);
7485 free_query_parameters(SAVED_QUERY_S
**parm
)
7490 fs_give((void **)&(*parm
)->qq
);
7492 fs_give((void **)&(*parm
)->cn
);
7494 fs_give((void **)&(*parm
)->sn
);
7496 fs_give((void **)&(*parm
)->gn
);
7498 fs_give((void **)&(*parm
)->mail
);
7500 fs_give((void **)&(*parm
)->org
);
7502 fs_give((void **)&(*parm
)->unit
);
7503 if((*parm
)->country
)
7504 fs_give((void **)&(*parm
)->country
);
7506 fs_give((void **)&(*parm
)->state
);
7507 if((*parm
)->locality
)
7508 fs_give((void **)&(*parm
)->locality
);
7510 fs_give((void **)&(*parm
)->custom
);
7512 fs_give((void **)parm
);
7519 * A callback from pico to restore the saved query parameters.
7521 * Args he -- Unused.
7522 * s -- The place to return the allocated array of values.
7524 * Returns -- 1 if there are parameters to return, 0 otherwise.
7527 restore_query_parameters(struct headerentry
*he
, char ***s
)
7529 int retval
= 0, i
= 0;
7534 if(saved_params
&& s
){
7535 *s
= (char **)fs_get((QQ_END
+ 1) * sizeof(char *));
7536 (*s
)[i
++] = cpystr(saved_params
->qq
? saved_params
->qq
: "");
7537 (*s
)[i
++] = cpystr(saved_params
->cn
? saved_params
->cn
: "");
7538 (*s
)[i
++] = cpystr(saved_params
->sn
? saved_params
->sn
: "");
7539 (*s
)[i
++] = cpystr(saved_params
->gn
? saved_params
->gn
: "");
7540 (*s
)[i
++] = cpystr(saved_params
->mail
? saved_params
->mail
: "");
7541 (*s
)[i
++] = cpystr(saved_params
->org
? saved_params
->org
: "");
7542 (*s
)[i
++] = cpystr(saved_params
->unit
? saved_params
->unit
: "");
7543 (*s
)[i
++] = cpystr(saved_params
->country
? saved_params
->country
: "");
7544 (*s
)[i
++] = cpystr(saved_params
->state
? saved_params
->state
: "");
7545 (*s
)[i
++] = cpystr(saved_params
->locality
? saved_params
->locality
:"");
7546 (*s
)[i
++] = cpystr(saved_params
->custom
? saved_params
->custom
:"");
7556 * Internal handler for viewing an LDAP url.
7559 url_local_ldap(char *url
)
7563 int ld_err
, mangled
= 0, we_cancel
, retval
= 0, proto
= 3;
7564 int we_turned_on
= 0;
7566 LDAPMessage
*result
;
7568 LDAP_SERV_RES_S
*serv_res
= NULL
;
7569 LDAPURLDesc
*ldapurl
= NULL
;
7572 dprint((2, "url_local_ldap(%s)\n", url
? url
: "?"));
7574 ld_err
= ldap_url_parse(url
, &ldapurl
);
7575 if(ld_err
|| !ldapurl
){
7576 snprintf(ebuf
, sizeof(ebuf
), "URL parse failed for %s", url
);
7577 ebuf
[sizeof(ebuf
)-1] = '\0';
7578 q_status_message(SM_ORDER
, 3, 5, ebuf
);
7582 if(!ldapurl
->lud_host
){
7583 /* TRNASLATORS: No host in <url> */
7584 snprintf(ebuf
, sizeof(ebuf
), _("No host in %s"), url
);
7585 ebuf
[sizeof(ebuf
)-1] = '\0';
7586 q_status_message(SM_ORDER
, 3, 5, ebuf
);
7587 ldap_free_urldesc(ldapurl
);
7591 we_turned_on
= intr_handling_on();
7592 we_cancel
= busy_cue(_("Searching for LDAP url"), NULL
, 0);
7593 ps_global
->mangled_footer
= 1;
7596 if((ld
= ldap_init(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7600 if((ld
= ldap_init(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7602 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "ldap://%s:%d", ldapurl
->lud_host
, ldapurl
->lud_port
);
7603 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
7604 if(ldap_initialize(&ld
, tmp_20k_buf
) != LDAP_SUCCESS
)
7605 #endif /* _WINDOWS */
7607 if((ld
= ldap_open(ldapurl
->lud_host
, ldapurl
->lud_port
)) == NULL
)
7608 #endif /* LDAPAPI >= 11 */
7609 #endif /* _SOLARIS_SDK */
7612 cancel_busy_cue(-1);
7616 q_status_message(SM_ORDER
,3,5, _("LDAP search failed: can't initialize"));
7618 else if(!ps_global
->intr_pending
){
7619 if(ldap_v3_is_supported(ld
) &&
7620 our_ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &proto
) == 0){
7621 dprint((5, "ldap: using version 3 protocol\n"));
7625 * If we don't set RESTART then the select() waiting for the answer
7626 * in libldap will be interrupted and stopped by our busy_cue.
7628 our_ldap_set_option(ld
, LDAP_OPT_RESTART
, LDAP_OPT_ON
);
7630 t
.tv_sec
= 30; t
.tv_usec
= 0;
7632 ld_err
= ldap_search_st(ld
, ldapurl
->lud_dn
, ldapurl
->lud_scope
,
7633 ldapurl
->lud_filter
, ldapurl
->lud_attrs
,
7636 ld_err
= ldap_search_ext_s(ld
, ldapurl
->lud_dn
, ldapurl
->lud_scope
,
7637 ldapurl
->lud_filter
, ldapurl
->lud_attrs
, 0,
7639 &t
, DEF_LDAP_SIZE
, &result
);
7641 if(ld_err
!= LDAP_SUCCESS
){
7643 cancel_busy_cue(-1);
7647 snprintf(ebuf
, sizeof(ebuf
), _("LDAP search failed: %s"), ldap_err2string(ld_err
));
7648 ebuf
[sizeof(ebuf
)-1] = '\0';
7649 q_status_message(SM_ORDER
, 3, 5, ebuf
);
7653 ldap_unbind_ext(ld
, NULL
, NULL
);
7654 #endif /* _WINDOWS */
7656 else if(!ps_global
->intr_pending
){
7658 cancel_busy_cue(-1);
7663 intr_handling_off();
7667 if(ldap_count_entries(ld
, result
) == 0){
7668 q_status_message(SM_ORDER
, 3, 5, _("No matches found for url"));
7672 ldap_unbind_ext(ld
, NULL
, NULL
);
7673 #endif /* _WINDOWS */
7675 ldap_msgfree(result
);
7678 serv_res
= (LDAP_SERV_RES_S
*)fs_get(sizeof(LDAP_SERV_RES_S
));
7679 memset((void *)serv_res
, 0, sizeof(*serv_res
));
7681 serv_res
->res
= result
;
7682 info
= (LDAP_SERV_S
*)fs_get(sizeof(LDAP_SERV_S
));
7683 memset((void *)info
, 0, sizeof(*info
));
7684 info
->mailattr
= cpystr(DEF_LDAP_MAILATTR
);
7685 info
->snattr
= cpystr(DEF_LDAP_SNATTR
);
7686 info
->gnattr
= cpystr(DEF_LDAP_GNATTR
);
7687 info
->cnattr
= cpystr(DEF_LDAP_CNATTR
);
7688 serv_res
->info_used
= info
;
7689 memset(&wp_err
, 0, sizeof(wp_err
));
7690 wp_err
.mangled
= &mangled
;
7692 ask_user_which_entry(serv_res
, NULL
, NULL
, &wp_err
, DisplayForURL
);
7694 q_status_message(SM_ORDER
, 3, 5, wp_err
.error
);
7695 fs_give((void **)&wp_err
.error
);
7699 ps_global
->mangled_screen
= 1;
7701 free_ldap_result_list(&serv_res
);
7708 cancel_busy_cue(-1);
7711 intr_handling_off();
7714 ldap_free_urldesc(ldapurl
);
7718 #endif /* ENABLE_LDAP */