* Implement a different way to delete a password from the cache.
[alpine.git] / alpine / takeaddr.c
blob96057aed57f258f311ed2beb3bf8ec3344ca26cc
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 takeaddr.c
17 Mostly support for Take Address command.
18 ====*/
21 #include "headers.h"
22 #include "takeaddr.h"
23 #include "addrbook.h"
24 #include "adrbkcmd.h"
25 #include "status.h"
26 #include "confscroll.h"
27 #include "keymenu.h"
28 #include "radio.h"
29 #include "titlebar.h"
30 #include "alpine.h"
31 #include "help.h"
32 #include "mailcmd.h"
33 #include "mailpart.h"
34 #include "roleconf.h"
35 #include "../pith/state.h"
36 #include "../pith/msgno.h"
37 #include "../pith/adrbklib.h"
38 #include "../pith/bldaddr.h"
39 #include "../pith/bitmap.h"
40 #include "../pith/util.h"
41 #include "../pith/addrstring.h"
42 #include "../pith/remote.h"
43 #include "../pith/newmail.h"
44 #include "../pith/list.h"
45 #include "../pith/abdlc.h"
46 #include "../pith/ablookup.h"
47 #include "../pith/stream.h"
48 #include "../pith/mailcmd.h"
49 #include "../pith/busy.h"
52 typedef struct takeaddress_screen {
53 ScreenMode mode;
54 TA_S *current,
55 *top_line;
56 } TA_SCREEN_S;
58 static TA_SCREEN_S *ta_screen;
59 static char *fakedomain = "@";
61 static ESCKEY_S save_or_export[] = {
62 {'s', 's', "S", N_("Save")},
63 {'e', 'e', "E", N_("Export")},
64 {-1, 0, NULL, NULL}};
67 /* internal prototypes */
68 int edit_nickname(AdrBk *, AddrScrn_Disp *, int, char *, char *, HelpType, int, int);
69 void add_abook_entry(TA_S *, char *, char *, char *, char *, int, TA_STATE_S **, char *);
70 void take_to_addrbooks_frontend(char **, char *, char *, char *, char *,
71 char *, int, TA_STATE_S **, char *);
72 void take_to_addrbooks(char **, char *, char *, char *, char *,
73 char *, int, TA_STATE_S **, char *);
74 PerAddrBook *use_this_addrbook(int, char *);
75 PerAddrBook *check_for_addrbook(char *);
76 int takeaddr_screen(struct pine *, TA_S *, int, ScreenMode, TA_STATE_S **, char *);
77 void takeaddr_bypass(struct pine *, TA_S *, TA_STATE_S **);
78 int ta_do_take(TA_S *, int, int, TA_STATE_S **, char *);
79 TA_S *whereis_taline(TA_S *);
80 int ta_take_marked_addrs(int, TA_S *, int, TA_STATE_S **, char *);
81 int ta_take_single_addr(TA_S *, int, TA_STATE_S **, char *);
82 int update_takeaddr_screen(struct pine *, TA_S *, TA_SCREEN_S *, Pos *);
83 void takeaddr_screen_redrawer_list(void);
84 void takeaddr_screen_redrawer_single(void);
85 int attached_addr_handler(TA_S *, int);
86 int take_without_edit(TA_S *, int, int, TA_STATE_S **, char *);
87 void export_vcard_att(struct pine *, int, long, ATTACH_S *);
88 int take_export_tool(struct pine *, int, CONF_S **, unsigned);
90 #ifdef _WINDOWS
91 int ta_scroll_up(long);
92 int ta_scroll_down(long);
93 int ta_scroll_to_pos(long);
94 int ta_scroll_callback(int, long);
95 #endif
99 * Edit a nickname field.
101 * Args: abook -- the addressbook handle
102 * dl -- display list line (NULL if new entry)
103 * command_line -- line to prompt on
104 * orig -- nickname to edit
105 * prompt -- prompt
106 * this_help -- help
107 * return_existing -- changes the behavior when a user types in a nickname
108 * which already exists in this abook. If not set, it
109 * will just keep looping until the user changes; if set,
110 * it will return -8 to the caller and orig will be set
111 * to the matching nickname.
113 * Returns: -10 to cancel
114 * -9 no change
115 * -7 only case of nickname changed (only happens if dl set)
116 * -8 existing nickname chosen (only happens if return_existing set)
117 * 0 new value copied into orig
120 edit_nickname(AdrBk *abook, AddrScrn_Disp *dl, int command_line, char *orig,
121 char *prompt, HelpType this_help, int return_existing, int takeaddr)
123 char edit_buf[MAX_NICKNAME + 1];
124 HelpType help;
125 int i, flags, lastrc, rc;
126 AdrBk_Entry *check, *passed_in_ae;
127 ESCKEY_S ekey[3];
128 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
129 char *error = NULL;
131 ekey[i = 0].ch = ctrl('T');
132 ekey[i].rval = 2;
133 ekey[i].name = "^T";
134 ekey[i++].label = N_("To AddrBk");
136 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
137 ekey[i].ch = ctrl('I');
138 ekey[i].rval = 11;
139 ekey[i].name = "TAB";
140 ekey[i++].label = N_("Complete");
143 ekey[i].ch = -1;
145 strncpy(edit_buf, orig, sizeof(edit_buf)-1);
146 edit_buf[sizeof(edit_buf)-1] = '\0';
147 if(dl)
148 passed_in_ae = adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
149 else
150 passed_in_ae = (AdrBk_Entry *)NULL;
152 help = NO_HELP;
153 rc = 0;
154 check = NULL;
156 if(error){
157 q_status_message(SM_ORDER, 3, 4, error);
158 fs_give((void **)&error);
161 /* display a message because adrbk_lookup_by_nick returned positive */
162 if(check){
163 if(return_existing){
164 strncpy(orig, edit_buf, sizeof(edit_buf)-1);
165 orig[sizeof(edit_buf)-1] = '\0';
166 if(passed_in_ae)
167 (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
168 return -8;
171 q_status_message1(SM_ORDER, 0, 4,
172 _("Already an entry with nickname \"%s\""), edit_buf);
175 lastrc = rc;
176 if(rc == 3)
177 help = (help == NO_HELP ? this_help : NO_HELP);
179 flags = OE_APPEND_CURRENT;
180 rc = optionally_enter(edit_buf, command_line, 0, sizeof(edit_buf),
181 prompt, ekey, help, &flags);
183 if(rc == 1) /* ^C */
184 break;
186 if(rc == 2){ /* ^T */
187 void (*redraw) (void) = ps_global->redrawer;
188 char *returned_nickname;
190 push_titlebar_state();
191 save_state(&state);
192 if(takeaddr)
193 returned_nickname = addr_book_takeaddr();
194 else
195 returned_nickname = addr_book_selnick();
197 restore_state(&state);
198 if(returned_nickname){
199 strncpy(edit_buf, returned_nickname, sizeof(edit_buf)-1);
200 edit_buf[sizeof(edit_buf)-1] = '\0';
201 fs_give((void **)&returned_nickname);
204 ClearScreen();
205 pop_titlebar_state();
206 redraw_titlebar();
207 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
208 (*ps_global->redrawer)();
210 else if(rc == 11){ /* TAB */
211 if(edit_buf[0]){
212 char *new_nickname = NULL;
213 int ambiguity;
215 ambiguity = abook_nickname_complete(edit_buf, &new_nickname,
216 (lastrc==rc && !(flags & OE_USER_MODIFIED)), 0);
217 if(new_nickname){
218 if(*new_nickname){
219 strncpy(edit_buf, new_nickname, sizeof(edit_buf));
220 edit_buf[sizeof(edit_buf)-1] = '\0';
223 fs_give((void **) &new_nickname);
226 if(ambiguity != 2)
227 Writechar(BELL, 0);
231 }while(rc == 2 ||
232 rc == 3 ||
233 rc == 4 ||
234 rc == 11||
235 nickname_check(edit_buf, &error) ||
236 ((check =
237 adrbk_lookup_by_nick(abook, edit_buf, (adrbk_cntr_t *)NULL)) &&
238 check != passed_in_ae));
240 if(rc != 0){
241 if(passed_in_ae)
242 (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
244 return -10;
247 /* only the case of nickname changed */
248 if(passed_in_ae && check == passed_in_ae && strcmp(edit_buf, orig)){
249 (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
250 strncpy(orig, edit_buf, sizeof(edit_buf)-1);
251 orig[sizeof(edit_buf)-1] = '\0';
252 return -7;
255 if(passed_in_ae)
256 (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
258 if(strcmp(edit_buf, orig) == 0) /* no change */
259 return -9;
261 strncpy(orig, edit_buf, sizeof(edit_buf)-1);
262 orig[sizeof(edit_buf)-1] = '\0';
263 return 0;
268 * Add an entry to address book.
269 * It is for capturing addresses off incoming mail.
270 * This is a front end for take_to_addrbooks.
271 * It is also used for replacing an existing entry and for adding a single
272 * new address to an existing list.
274 * The reason this is here is so that when Taking a single address, we can
275 * rearrange the fullname to be Last, First instead of First Last.
277 * Args: ta_entry -- the entry from the take screen
278 * command_line -- line to prompt on
280 * Result: item is added to one of the address books,
281 * an error message is queued if appropriate.
283 void
284 add_abook_entry(TA_S *ta_entry, char *nick, char *fullname, char *fcc,
285 char *comment, int command_line, TA_STATE_S **tas, char *cmd)
287 ADDRESS *addr;
288 char new_fullname[6*MAX_FULLNAME + 1], new_address[6*MAX_ADDRESS + 1];
289 char **new_list;
291 dprint((5, "-- add_abook_entry --\n"));
293 /*-- rearrange full name (Last, First) ---*/
294 new_fullname[0] = '\0';
295 addr = ta_entry->addr;
296 if(!fullname && addr->personal != NULL){
297 if(F_ON(F_DISABLE_TAKE_LASTFIRST, ps_global)){
298 strncpy(new_fullname, addr->personal, sizeof(new_fullname)-1);
299 new_fullname[sizeof(new_fullname)-1] = '\0';
301 else{
302 char old_fullname[6*MAX_FULLNAME + 1];
304 snprintf(old_fullname, sizeof(old_fullname), "%s", addr->personal);
305 old_fullname[sizeof(old_fullname)-1] = '\0';
306 switch_to_last_comma_first(old_fullname, new_fullname, sizeof(new_fullname));
310 /* initial value for new address */
311 new_address[0] = '\0';
312 if(addr->mailbox && addr->mailbox[0]){
313 char *scratch, *p, *t, *u;
314 size_t es;
315 unsigned long l;
316 RFC822BUFFER rbuf;
318 es = est_size(addr);
319 scratch = (char *) fs_get(es);
320 scratch[0] = '\0';
321 rbuf.f = dummy_soutr;
322 rbuf.s = NULL;
323 rbuf.beg = scratch;
324 rbuf.cur = scratch;
325 rbuf.end = scratch+es-1;
326 rfc822_output_address_list(&rbuf, addr, 0L, NULL);
327 *rbuf.cur = '\0';
328 if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){
329 for(t = p; ; t--)
330 if(*t == '&'){ /* find "leading" token */
331 *t++ = ' '; /* replace token */
332 *p = '\0'; /* tie off string */
333 u = (char *)rfc822_base64((unsigned char *)t,
334 (unsigned long)strlen(t), &l);
335 *p = '@'; /* restore 'p' */
336 rplstr(p, es-(p-scratch), 12, ""); /* clear special token */
337 rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */
338 if(u)
339 fs_give((void **)&u);
341 else if(t == scratch)
342 break;
345 strncpy(new_address, scratch, sizeof(new_address)-1);
346 new_address[sizeof(new_address)-1] = '\0';
348 if(scratch)
349 fs_give((void **)&scratch);
352 if(ta_entry->frwrded){
353 ADDRESS *a;
354 int i, j;
356 for(i = 0, a = addr; a; i++, a = a->next)
357 ;/* just counting for alloc below */
359 /* catch special case where empty addr was set in vcard_to_ta */
360 if(i == 1 && !addr->host && !addr->mailbox && !addr->personal)
361 i = 0;
363 new_list = (char **) fs_get((i+1) * sizeof(char *));
364 for(j = 0, a = addr; i && a; j++, a = a->next){
365 ADDRESS *next_addr;
366 char *bufp;
367 size_t len;
369 next_addr = a->next;
370 a->next = NULL;
371 len = est_size(a);
372 bufp = (char *) fs_get(len * sizeof(char));
373 new_list[j] = cpystr(addr_string(a, bufp, len));
374 a->next = next_addr;
375 fs_give((void **) &bufp);
378 new_list[j] = NULL;
380 else{
381 int i = 0, j;
383 j = (ta_entry->strvalue && ta_entry->strvalue[0]) ? 2 : 1;
385 new_list = (char **) fs_get(j * sizeof(char *));
387 if(j == 2)
388 new_list[i++] = cpystr(ta_entry->strvalue);
390 new_list[i] = NULL;
393 take_to_addrbooks_frontend(new_list, nick,
394 fullname ? fullname : new_fullname,
395 new_address, fcc, comment, command_line,
396 tas, cmd);
397 free_list_array(&new_list);
401 void
402 take_to_addrbooks_frontend(char **new_entries, char *nick, char *fullname,
403 char *addr, char *fcc, char *comment, int cmdline,
404 TA_STATE_S **tas, char *cmd)
406 jmp_buf save_jmp_buf;
407 int *save_nesting_level;
409 dprint((5, "-- take_to_addrbooks_frontend --\n"));
411 if(ps_global->remote_abook_validity > 0 &&
412 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0))
413 ps_global->mangled_footer = 1;
415 save_nesting_level = cpyint(ab_nesting_level);
416 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
417 if(setjmp(addrbook_changed_unexpectedly)){
418 q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
419 dprint((1,
420 "RESETTING address book... take_to_addrbooks_frontend!\n"));
421 addrbook_reset();
422 ab_nesting_level = *save_nesting_level;
425 ab_nesting_level++;
426 take_to_addrbooks(new_entries, nick, fullname, addr, fcc, comment, cmdline,
427 tas, cmd);
428 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
429 ab_nesting_level--;
430 if(save_nesting_level)
431 fs_give((void **)&save_nesting_level);
436 * Add to address book, called from take screen.
437 * It is also used for adding to an existing list or replacing an existing
438 * entry.
440 * Args: new_entries -- a list of addresses to add to a list or to form
441 * a new list with
442 * nick -- if adding new entry, suggest this for nickname
443 * fullname -- if adding new entry, use this for fullname
444 * addr -- if only one new_entry, this is its addr
445 * fcc -- if adding new entry, use this for fcc
446 * comment -- if adding new entry, use this for comment
447 * command_line -- line to prompt on
449 * Result: item is added to one of the address books,
450 * an error message is queued if appropriate.
452 void
453 take_to_addrbooks(char **new_entries, char *nick, char *fullname, char *addr,
454 char *fcc, char *comment, int command_line, TA_STATE_S **tas, char *cmd)
456 char new_nickname[6*MAX_NICKNAME + 1], exist_nick[6*MAX_NICKNAME + 1];
457 char prompt[200], **p;
458 int rc, listadd = 0, ans, i;
459 AdrBk *abook;
460 SAVE_STATE_S state;
461 PerAddrBook *pab;
462 AdrBk_Entry *abe = (AdrBk_Entry *)NULL, *abe_copy;
463 adrbk_cntr_t entry_num = NO_NEXT;
464 size_t tot_size, new_size, old_size;
465 Tag old_tag;
466 char *tmp_a_string;
467 char *simple_a = NULL;
468 ADDRESS *a = NULL;
471 dprint((5, "-- take_to_addrbooks --\n"));
473 if(tas && *tas)
474 pab = (*tas)->pab;
475 else
476 pab = setup_for_addrbook_add(&state, command_line, cmd);
478 /* check we got it opened ok */
479 if(pab == NULL || pab->address_book == NULL)
480 goto take_to_addrbooks_cancel;
482 adrbk_check_validity(pab->address_book, 1L);
483 if(pab->address_book->flags & FILE_OUTOFDATE ||
484 (pab->address_book->rd &&
485 pab->address_book->rd->flags & REM_OUTOFDATE)){
486 q_status_message3(SM_ORDER, 0, 4,
487 "Address book%s%s has changed: %stry again",
488 (as.n_addrbk > 1 && pab->abnick) ? " " : "",
489 (as.n_addrbk > 1 && pab->abnick) ? pab->abnick : "",
490 (ps_global->remote_abook_validity == -1) ? "resynchronize and " : "");
491 if(tas && *tas){
492 restore_state(&((*tas)->state));
493 (*tas)->pab = NULL;
495 else
496 restore_state(&state);
498 return;
501 abook = pab->address_book;
502 new_nickname[0] = '\0';
503 exist_nick[0] = '\0';
505 if(addr){
506 simple_a = NULL;
507 a = NULL;
508 /* rfc822_parse_adrlist feels free to destroy input so send copy */
509 tmp_a_string = cpystr(addr);
510 rfc822_parse_adrlist(&a, tmp_a_string, fakedomain);
511 if(tmp_a_string)
512 fs_give((void **)&tmp_a_string);
514 if(a){
515 simple_a = simple_addr_string(a, tmp_20k_buf, SIZEOF_20KBUF);
516 mail_free_address(&a);
519 if(simple_a && *simple_a)
520 abe = adrbk_lookup_by_addr(abook, simple_a, NULL);
522 if(abe){
523 snprintf(prompt, sizeof(prompt), _("Warning: address exists with %s%s, continue "),
524 (abe->nickname && abe->nickname[0]) ? "nickname "
525 : (abe->fullname && abe->fullname[0]) ? "fullname "
526 : "no nickname",
527 (abe->nickname && abe->nickname[0]) ? abe->nickname
528 : (abe->fullname && abe->fullname[0]) ? abe->fullname
529 : "");
530 prompt[sizeof(prompt)-1] = '\0';
531 switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
532 case 'y':
533 if(abe->nickname && abe->nickname[0]){
534 strncpy(new_nickname, abe->nickname, sizeof(new_nickname));
535 new_nickname[sizeof(new_nickname)-1] = '\0';
536 strncpy(exist_nick, new_nickname, sizeof(exist_nick));
537 exist_nick[sizeof(exist_nick)-1] = '\0';
540 break;
542 default:
543 goto take_to_addrbooks_cancel;
548 get_nick:
549 abe = NULL;
550 old_tag = NotSet;
551 entry_num = NO_NEXT;
553 /*----- nickname ------*/
554 snprintf(prompt, sizeof(prompt),
555 _("Enter new or existing nickname (one word and easy to remember): "));
556 prompt[sizeof(prompt)-1] = '\0';
557 if(!new_nickname[0] && nick){
558 strncpy(new_nickname, nick, sizeof(new_nickname));
559 new_nickname[sizeof(new_nickname)-1] = '\0';
562 rc = edit_nickname(abook, (AddrScrn_Disp *)NULL, command_line,
563 new_nickname, prompt, h_oe_takenick, 1, 1);
564 if(rc == -8){ /* this means an existing nickname was entered */
565 abe = adrbk_lookup_by_nick(abook, new_nickname, &entry_num);
566 if(!abe){ /* this shouldn't happen */
567 q_status_message1(SM_ORDER, 0, 4,
568 _("Already an entry %s in address book!"), new_nickname);
569 goto take_to_addrbooks_cancel;
572 old_tag = abe->tag;
574 if(abe->tag == Single && !strcmp(new_nickname, exist_nick)){
575 static ESCKEY_S choices[] = {
576 {'r', 'r', "R", N_("Replace")},
577 {'n', 'n', "N", N_("No")},
578 {-1, 0, NULL, NULL}};
580 snprintf(prompt, sizeof(prompt), _("Entry %s (%s) exists, replace ? "),
581 new_nickname,
582 (abe->fullname && abe->fullname[0])
583 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
584 SIZEOF_20KBUF, abe->fullname)
585 : "<no long name>");
586 prompt[sizeof(prompt)-1] = '\0';
587 ans = radio_buttons(prompt,
588 command_line,
589 choices,
590 'r',
591 'x',
592 h_oe_take_replace,
593 RB_NORM);
595 else{
596 static ESCKEY_S choices[] = {
597 {'r', 'r', "R", N_("Replace")},
598 {'a', 'a', "A", N_("Add")},
599 {'n', 'n', "N", N_("No")},
600 {-1, 0, NULL, NULL}};
602 snprintf(prompt, sizeof(prompt),
603 _("%s %s (%s) exists, replace or add addresses to it ? "),
604 abe->tag == List ? "List" : "Entry",
605 new_nickname,
606 (abe->fullname && abe->fullname[0])
607 ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
608 SIZEOF_20KBUF, abe->fullname)
609 : "<no long name>");
610 prompt[sizeof(prompt)-1] = '\0';
612 ans = radio_buttons(prompt,
613 command_line,
614 choices,
615 'a',
616 'x',
617 h_oe_take_replace_or_add,
618 RB_NORM);
621 switch(ans){
622 case 'y':
623 case 'r':
624 break;
626 case 'n':
627 goto get_nick;
628 break;
630 case 'a':
631 listadd++;
632 break;
634 default:
635 goto take_to_addrbooks_cancel;
638 else if(rc != 0 && rc != -9) /* -9 means a null nickname */
639 goto take_to_addrbooks_cancel;
641 if((long)abook->count > MAX_ADRBK_SIZE ||
642 (old_tag == NotSet && (long)abook->count >= MAX_ADRBK_SIZE)){
643 q_status_message(SM_ORDER, 3, 5,
644 _("Address book is at maximum size, cancelled."));
645 dprint((2, "Addrbook at Max size, TakeAddr cancelled\n"));
646 goto take_to_addrbooks_cancel;
649 if(listadd){
650 /* count up size of existing list */
651 if(abe->tag == List){
652 for(p = abe->addr.list; p != NULL && *p != NULL; p++)
653 ;/* do nothing */
655 old_size = p - abe->addr.list;
657 /* or size of existing single address */
658 else if(abe->addr.addr && abe->addr.addr[0])
659 old_size = 1;
660 else
661 old_size = 0;
663 else /* don't care about old size, they will be tossed in edit_entry */
664 old_size = 0;
666 /* make up an abe to pass to edit_entry */
667 abe_copy = adrbk_newentry();
668 abe_copy->nickname = cpystr(new_nickname);
669 abe_copy->tag = List;
670 abe_copy->addr.list = NULL;
672 if(listadd){
673 abe_copy->fullname = cpystr((abe->fullname && abe->fullname[0])
674 ? abe->fullname : "");
675 abe_copy->fcc = cpystr((abe->fcc && abe->fcc[0]) ? abe->fcc : "");
676 abe_copy->extra = cpystr((abe->extra&&abe->extra[0]) ? abe->extra : "");
678 else{
680 * use passed in info if available
682 abe_copy->fullname = cpystr((fullname && fullname[0])
683 ? fullname
684 : (abe && abe->fullname)
685 ? abe->fullname
686 : "");
687 abe_copy->fcc = cpystr((fcc && fcc[0])
688 ? fcc
689 : (abe && abe->fcc)
690 ? abe->fcc
691 : "");
692 abe_copy->extra = cpystr((comment && comment[0])
693 ? comment
694 : (abe && abe->extra)
695 ? abe->extra
696 : "");
699 /* get rid of duplicates */
700 if(listadd){
701 if(abe->tag == List){
702 int elim_dup;
703 char **q, **r;
704 ADDRESS *newadr, *oldadr;
706 for(q = new_entries; q != NULL && *q != NULL;){
708 tmp_a_string = cpystr(*q);
709 newadr = NULL;
710 rfc822_parse_adrlist(&newadr, tmp_a_string, fakedomain);
711 fs_give((void **) &tmp_a_string);
713 elim_dup = (newadr == NULL);
714 for(p = abe->addr.list;
715 !elim_dup && p != NULL && *p != NULL;
716 p++){
717 tmp_a_string = cpystr(*p);
718 oldadr = NULL;
719 rfc822_parse_adrlist(&oldadr, tmp_a_string, fakedomain);
720 fs_give((void **) &tmp_a_string);
722 if(address_is_same(newadr, oldadr))
723 elim_dup++;
725 if(oldadr)
726 mail_free_address(&oldadr);
729 /* slide the addresses down one to eliminate newadr */
730 if(elim_dup){
731 char *f;
733 f = *q;
734 for(r = q; r != NULL && *r != NULL; r++)
735 *r = *(r+1);
737 if(f)
738 fs_give((void **) &f);
740 else
741 q++;
743 if(newadr)
744 mail_free_address(&newadr);
747 else{
748 char **q, **r;
749 ADDRESS *newadr, *oldadr;
751 tmp_a_string = cpystr(abe->addr.addr ? abe->addr.addr : "");
752 oldadr = NULL;
753 rfc822_parse_adrlist(&oldadr, tmp_a_string, fakedomain);
754 fs_give((void **) &tmp_a_string);
756 for(q = new_entries; q != NULL && *q != NULL;){
758 tmp_a_string = cpystr(*q);
759 newadr = NULL;
760 rfc822_parse_adrlist(&newadr, tmp_a_string, fakedomain);
761 fs_give((void **) &tmp_a_string);
763 /* slide the addresses down one to eliminate newadr */
764 if(address_is_same(newadr, oldadr)){
765 char *f;
767 f = *q;
768 for(r = q; r != NULL && *r != NULL; r++)
769 *r = *(r+1);
771 if(f)
772 fs_give((void **) &f);
774 else
775 q++;
777 if(newadr)
778 mail_free_address(&newadr);
781 if(oldadr)
782 mail_free_address(&oldadr);
785 if(!new_entries || !*new_entries){
786 q_status_message1(SM_ORDER, 0, 4,
787 _("All of the addresses are already included in \"%s\""),
788 new_nickname);
789 free_ae(&abe_copy);
790 if(tas && *tas){
791 restore_state(&((*tas)->state));
792 (*tas)->pab = NULL;
794 else
795 restore_state(&state);
797 return;
801 /* count up size of new list */
802 for(p = new_entries; p != NULL && *p != NULL; p++)
803 ;/* do nothing */
805 new_size = p - new_entries;
806 tot_size = old_size + new_size;
807 abe_copy->addr.list = (char **) fs_get((tot_size+1) * sizeof(char *));
808 memset((void *) abe_copy->addr.list, 0, (tot_size+1) * sizeof(char *));
809 if(old_size > 0){
810 if(abe->tag == List){
811 for(i = 0; i < old_size; i++)
812 abe_copy->addr.list[i] = cpystr(abe->addr.list[i]);
814 else
815 abe_copy->addr.list[0] = cpystr(abe->addr.addr);
818 /* add new addresses to list */
819 if(tot_size == 1 && addr)
820 abe_copy->addr.list[0] = cpystr(addr);
821 else
822 for(i = 0; i < new_size; i++)
823 abe_copy->addr.list[old_size + i] = cpystr(new_entries[i]);
825 abe_copy->addr.list[tot_size] = NULL;
827 if(F_ON(F_DISABLE_TAKE_FULLNAMES, ps_global)){
828 for(i = 0; abe_copy->addr.list[i]; i++){
829 simple_a = NULL;
830 a = NULL;
831 tmp_a_string = cpystr(abe_copy->addr.list[i]);
832 rfc822_parse_adrlist(&a, tmp_a_string, fakedomain);
833 if(tmp_a_string)
834 fs_give((void **) &tmp_a_string);
836 if(a){
837 simple_a = simple_addr_string(a, tmp_20k_buf, SIZEOF_20KBUF);
838 mail_free_address(&a);
841 /* replace the old addr string with one with no full name */
842 if(simple_a && *simple_a){
843 if(abe_copy->addr.list[i])
844 fs_give((void **) &abe_copy->addr.list[i]);
846 abe_copy->addr.list[i] = cpystr(simple_a);
851 edit_entry(abook, abe_copy, (a_c_arg_t) entry_num, old_tag, 0, NULL, cmd);
853 /* free copy */
854 free_ae(&abe_copy);
855 if(tas && *tas){
856 restore_state(&((*tas)->state));
857 (*tas)->pab = NULL;
859 else
860 restore_state(&state);
862 return;
864 take_to_addrbooks_cancel:
865 q_status_message(SM_INFO, 0, 2, _("Address book addition cancelled"));
866 if(tas && *tas){
867 restore_state(&((*tas)->state));
868 (*tas)->pab = NULL;
870 else
871 restore_state(&state);
876 * Prep addrbook for TakeAddr add operation.
878 * Arg: savep -- Address of a pointer to save addrbook state in.
879 * stp -- Address of a pointer to save addrbook state in.
881 * Returns: a PerAddrBook pointer, or NULL.
883 PerAddrBook *
884 setup_for_addrbook_add(SAVE_STATE_S *state, int command_line, char *cmd)
886 PerAddrBook *pab;
887 int save_rem_abook_valid = 0;
889 init_ab_if_needed();
890 save_state(state);
892 if(as.n_addrbk == 0){
893 q_status_message(SM_ORDER, 3, 4, _("No address book configured!"));
894 return NULL;
896 else
897 pab = use_this_addrbook(command_line, cmd);
899 if(!pab)
900 return NULL;
902 if((pab->type & REMOTE_VIA_IMAP) && ps_global->remote_abook_validity == -1){
903 save_rem_abook_valid = -1;
904 ps_global->remote_abook_validity = 0;
907 /* initialize addrbook so we can add to it */
908 init_abook(pab, Open);
910 if(save_rem_abook_valid)
911 ps_global->remote_abook_validity = save_rem_abook_valid;
913 if(pab->ostatus != Open){
914 q_status_message(SM_ORDER, 3, 4, _("Can't open address book!"));
915 return NULL;
918 if(pab->access != ReadWrite){
919 if(pab->access == ReadOnly)
920 q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
921 else if(pab->access == NoAccess)
922 q_status_message(SM_ORDER, 3, 4,
923 _("AddressBook not accessible, permission denied"));
925 return NULL;
928 return(pab);
933 * Interact with user to figure out which address book they want to add a
934 * new entry (TakeAddr) to.
936 * Args: command_line -- just the line to prompt on
938 * Results: returns a pab pointing to the selected addrbook, or NULL.
940 PerAddrBook *
941 use_this_addrbook(int command_line, char *cmd)
943 HelpType help;
944 int rc = 0;
945 PerAddrBook *pab, *the_only_pab;
946 #define MAX_ABOOK 2000
947 int i, abook_num, count_read_write;
948 char addrbook[MAX_ABOOK + 1],
949 prompt[MAX_ABOOK + 81];
950 static ESCKEY_S ekey[] = {
951 {-2, 0, NULL, NULL},
952 {ctrl('P'), 10, "^P", N_("Prev AddrBook")},
953 {ctrl('N'), 11, "^N", N_("Next AddrBook")},
954 {KEY_UP, 10, "", ""},
955 {KEY_DOWN, 11, "", ""},
956 {-1, 0, NULL, NULL}};
958 dprint((9, "- use_this_addrbook -\n"));
960 /* check for only one ReadWrite addrbook */
961 count_read_write = 0;
962 for(i = 0; i < as.n_addrbk; i++){
963 pab = &as.adrbks[i];
965 * NoExists is counted, too, so the user can add to an empty
966 * addrbook the first time.
968 if(pab->access == ReadWrite ||
969 pab->access == NoExists ||
970 pab->access == MaybeRorW){
971 count_read_write++;
972 the_only_pab = &as.adrbks[i];
976 /* only one usable addrbook, use it */
977 if(count_read_write == 1)
978 return(the_only_pab);
980 /* no addrbook to write to */
981 if(count_read_write == 0){
982 q_status_message2(SM_ORDER | SM_DING, 3, 4,
983 "No %sAddressbook to %s to!",
984 (as.n_addrbk > 0) ? "writable " : "", cmd);
985 return NULL;
988 /* start with the first addrbook */
989 abook_num = 0;
990 pab = &as.adrbks[abook_num];
991 strncpy(addrbook, pab->abnick, sizeof(addrbook)-1);
992 addrbook[sizeof(addrbook)-1] = '\0';
993 snprintf(prompt, sizeof(prompt), "%c%s to which addrbook : %s",
994 islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd) : *cmd,
995 cmd+1,
996 (pab->access == ReadOnly || pab->access == NoAccess) ?
997 "[ReadOnly] " : "");
998 prompt[sizeof(prompt)-1] = '\0';
999 help = NO_HELP;
1000 ps_global->mangled_footer = 1;
1002 int flags;
1004 if(!pab)
1005 q_status_message1(SM_ORDER, 3, 4, _("No addressbook \"%s\""),
1006 addrbook);
1008 if(rc == 3)
1009 help = (help == NO_HELP ? h_oe_chooseabook : NO_HELP);
1011 flags = OE_APPEND_CURRENT;
1012 rc = optionally_enter(addrbook, command_line, 0, sizeof(addrbook),
1013 prompt, ekey, help, &flags);
1015 if(rc == 1){ /* ^C */
1016 char capcmd[50];
1018 snprintf(capcmd, sizeof(capcmd),
1019 "%c%s",
1020 islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd)
1021 : *cmd,
1022 cmd+1);
1023 capcmd[sizeof(capcmd)-1] = '\0';
1024 cmd_cancelled(capcmd);
1025 break;
1028 if(rc == 10){ /* Previous addrbook */
1029 if(--abook_num < 0)
1030 abook_num = as.n_addrbk - 1;
1032 pab = &as.adrbks[abook_num];
1033 strncpy(addrbook, pab->abnick, sizeof(addrbook)-1);
1034 addrbook[sizeof(addrbook)-1] = '\0';
1035 snprintf(prompt, sizeof(prompt), "%s to which addrbook : %s", cmd,
1036 (pab->access == ReadOnly || pab->access == NoAccess) ?
1037 "[ReadOnly] " : "");
1038 prompt[sizeof(prompt)-1] = '\0';
1040 else if(rc == 11){ /* Next addrbook */
1041 if(++abook_num > as.n_addrbk - 1)
1042 abook_num = 0;
1044 pab = &as.adrbks[abook_num];
1045 strncpy(addrbook, pab->abnick, sizeof(addrbook)-1);
1046 addrbook[sizeof(addrbook)-1] = '\0';
1047 snprintf(prompt, sizeof(prompt), "%s to which addrbook : %s", cmd,
1048 (pab->access == ReadOnly || pab->access == NoAccess) ?
1049 "[ReadOnly] " : "");
1050 prompt[sizeof(prompt)-1] = '\0';
1053 }while(rc == 2 || rc == 3 || rc == 4 || rc == 10 || rc == 11 || rc == 12 ||
1054 !(pab = check_for_addrbook(addrbook)));
1056 ps_global->mangled_footer = 1;
1058 if(rc != 0)
1059 return NULL;
1061 return(pab);
1066 * Return a pab pointer to the addrbook which corresponds to the argument.
1068 * Args: addrbook -- the string representing the addrbook.
1070 * Results: returns a PerAddrBook pointer for the referenced addrbook, NULL
1071 * if none. First the nicknames are checked and then the filenames.
1072 * This must be one of the existing addrbooks.
1074 PerAddrBook *
1075 check_for_addrbook(char *addrbook)
1077 register int i;
1078 register PerAddrBook *pab;
1080 for(i = 0; i < as.n_addrbk; i++){
1081 pab = &as.adrbks[i];
1082 if(strcmp(pab->abnick, addrbook) == 0)
1083 break;
1086 if(i < as.n_addrbk)
1087 return(pab);
1089 for(i = 0; i < as.n_addrbk; i++){
1090 pab = &as.adrbks[i];
1091 if(strcmp(pab->filename, addrbook) == 0)
1092 break;
1095 if(i < as.n_addrbk)
1096 return(pab);
1098 return NULL;
1103 * Screen for selecting which addresses to Take to address book.
1105 * Args: ps -- Pine state
1106 * ta_list -- Screen is formed from this list of addresses
1107 * how_many_selected -- how many checked initially in ListMode
1108 * mode -- which mode to start in
1110 * Result: an address book may be updated
1111 * Returns -- 0 normally
1112 * 1 if it returns before redrawing screen
1115 takeaddr_screen(struct pine *ps, TA_S *ta_list, int how_many_selected,
1116 ScreenMode mode, TA_STATE_S **tas, char *command)
1118 UCS ch = 'x';
1119 int cmd, dline, give_warn_message, command_line;
1120 int km_popped = 0,
1121 directly_to_take = 0,
1122 ret = 0,
1123 done = 0;
1124 TA_S *current = NULL,
1125 *ctmp = NULL;
1126 TA_SCREEN_S screen;
1127 Pos cursor_pos;
1128 char *utf8str;
1129 struct key_menu *km = NULL;
1131 dprint((2, "- takeaddr_screen -\n"));
1133 command_line = -FOOTER_ROWS(ps); /* third line from the bottom */
1135 screen.current = screen.top_line = NULL;
1136 screen.mode = mode;
1138 if(ta_list == NULL){
1139 /* TRANSLATORS: something like
1140 No addresses to save, cancelled */
1141 q_status_message1(SM_INFO, 0, 2, "No addresses to %s, cancelled",
1142 command);
1143 return 1;
1146 current = first_sel_taline(ta_list);
1147 ps->mangled_screen = 1;
1148 ta_screen = &screen;
1150 if(is_talist_of_one(current)){
1151 directly_to_take++;
1152 screen.mode = SingleMode;
1154 else if(screen.mode == ListMode)
1155 q_status_message(SM_INFO, 0, 1,
1156 _("List mode: Use \"X\" to mark addresses to be included in list"));
1157 else
1158 q_status_message(SM_INFO, 0, 1,
1159 _("Single mode: Use \"P\" or \"N\" to select desired address"));
1161 while(!done){
1162 if(km_popped){
1163 km_popped--;
1164 if(km_popped == 0){
1165 clearfooter(ps);
1166 ps->mangled_body = 1;
1170 if(screen.mode == ListMode)
1171 ps->redrawer = takeaddr_screen_redrawer_list;
1172 else
1173 ps->redrawer = takeaddr_screen_redrawer_single;
1175 if(ps->mangled_screen){
1176 ps->mangled_header = 1;
1177 ps->mangled_footer = 1;
1178 ps->mangled_body = 1;
1179 ps->mangled_screen = 0;
1182 /*----------- Check for new mail -----------*/
1183 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
1184 ps->mangled_header = 1;
1186 #ifdef _WINDOWS
1187 mswin_beginupdate();
1188 #endif
1189 if(ps->mangled_header){
1190 char tbuf[40];
1192 snprintf(tbuf, sizeof(tbuf), "TAKE ADDRESS SCREEN (%s Mode)",
1193 (screen.mode == ListMode) ? "List"
1194 : "Single");
1195 tbuf[sizeof(tbuf)-1] = '\0';
1196 set_titlebar(tbuf, ps->mail_stream, ps->context_current,
1197 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0,
1198 NULL);
1199 ps->mangled_header = 0;
1202 dline = update_takeaddr_screen(ps, current, &screen, &cursor_pos);
1203 if(F_OFF(F_SHOW_CURSOR, ps)){
1204 cursor_pos.row = ps->ttyo->screen_rows - FOOTER_ROWS(ps);
1205 cursor_pos.col = 0;
1208 /*---- This displays new mail notification, or errors ---*/
1209 if(km_popped){
1210 FOOTER_ROWS(ps_global) = 3;
1211 mark_status_unknown();
1214 display_message(ch);
1215 if(km_popped){
1216 FOOTER_ROWS(ps_global) = 1;
1217 mark_status_unknown();
1220 /*---- Redraw footer ----*/
1221 if(ps->mangled_footer){
1222 bitmap_t bitmap;
1224 if(km_popped){
1225 FOOTER_ROWS(ps) = 3;
1226 clearfooter(ps);
1229 setbitmap(bitmap);
1230 ps->mangled_footer = 0;
1232 km = (screen.mode == ListMode) ? &ta_keymenu_lm : &ta_keymenu_sm;
1234 menu_clear_binding(km, KEY_LEFT);
1235 menu_clear_binding(km, KEY_RIGHT);
1236 if(F_ON(F_ARROW_NAV, ps_global)){
1237 int cmd;
1239 if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
1240 menu_add_binding(km, '<', cmd);
1241 menu_add_binding(km, KEY_LEFT, cmd);
1244 if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
1245 menu_add_binding(km, '>', cmd);
1246 menu_add_binding(km, KEY_RIGHT, cmd);
1250 draw_keymenu(km, bitmap, ps->ttyo->screen_cols,
1251 1 - FOOTER_ROWS(ps_global), 0, FirstMenu);
1253 if(km_popped){
1254 FOOTER_ROWS(ps) = 1;
1255 mark_keymenu_dirty();
1259 #ifdef _WINDOWS
1260 mswin_endupdate();
1261 #endif
1262 /*------ Read the command from the keyboard ----*/
1263 MoveCursor(cursor_pos.row, cursor_pos.col);
1265 if(directly_to_take){ /* bypass this screen */
1266 cmd = MC_TAKE;
1267 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
1269 else {
1270 #ifdef MOUSE
1271 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1272 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
1273 ps_global->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
1274 ps_global->ttyo->screen_cols);
1275 #endif
1277 #ifdef _WINDOWS
1278 mswin_setscrollcallback(ta_scroll_callback);
1279 #endif
1280 ch = READ_COMMAND(&utf8str);
1281 #ifdef MOUSE
1282 clear_mfunc(mouse_in_content);
1283 #endif
1285 #ifdef _WINDOWS
1286 mswin_setscrollcallback(NULL);
1287 #endif
1288 cmd = menu_command(ch, km);
1289 if (ta_screen->current)
1290 current = ta_screen->current;
1292 if(km_popped)
1293 switch(cmd){
1294 case MC_NONE :
1295 case MC_OTHER :
1296 case MC_RESIZE :
1297 case MC_REPAINT :
1298 km_popped++;
1299 break;
1301 default:
1302 clearfooter(ps);
1303 break;
1307 switch(cmd){
1308 case MC_HELP : /* help! */
1309 if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
1310 km_popped = 2;
1311 ps_global->mangled_footer = 1;
1312 break;
1315 helper(h_takeaddr_screen, _("HELP FOR TAKE ADDRESS SCREEN"),
1316 HLPD_SIMPLE);
1317 ps->mangled_screen = 1;
1318 break;
1320 case MC_EXIT: /* exit takeaddr screen */
1321 q_status_message(SM_INFO, 0, 2, _("Address book addition cancelled"));
1322 ret = 1;
1323 done++;
1324 break;
1326 case MC_TAKE:
1327 if(ta_do_take(current, how_many_selected, command_line, tas,
1328 command))
1329 done++;
1330 else
1331 directly_to_take = 0;
1333 break;
1335 case MC_CHARDOWN : /* next list element */
1336 if((ctmp = next_sel_taline(current)) != NULL)
1337 current = ctmp;
1338 else
1339 q_status_message(SM_INFO, 0, 1, _("Already on last line."));
1341 break;
1343 case MC_CHARUP: /* previous list element */
1344 if((ctmp = pre_sel_taline(current)) != NULL)
1345 current = ctmp;
1346 else
1347 q_status_message(SM_INFO, 0, 1, _("Already on first line."));
1349 break;
1351 case MC_PAGEDN : /* page forward */
1352 give_warn_message = 1;
1353 while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){
1354 if((ctmp = next_sel_taline(current)) != NULL){
1355 current = ctmp;
1356 give_warn_message = 0;
1358 else
1359 break;
1362 if(give_warn_message)
1363 q_status_message(SM_INFO, 0, 1, _("Already on last page."));
1365 break;
1367 case MC_PAGEUP : /* page backward */
1368 /* move to top of screen */
1369 give_warn_message = 1;
1370 while(dline-- > HEADER_ROWS(ps_global)){
1371 if((ctmp = pre_sel_taline(current)) != NULL){
1372 current = ctmp;
1373 give_warn_message = 0;
1375 else
1376 break;
1379 /* page back one screenful */
1380 while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){
1381 if((ctmp = pre_sel_taline(current)) != NULL){
1382 current = ctmp;
1383 give_warn_message = 0;
1385 else
1386 break;
1389 if(give_warn_message)
1390 q_status_message(SM_INFO, 0, 1, _("Already on first page."));
1392 break;
1394 case MC_WHEREIS : /* whereis */
1395 if((ctmp = whereis_taline(current)) != NULL)
1396 current = ctmp;
1398 ps->mangled_footer = 1;
1399 break;
1401 case KEY_SCRLTO:
1402 /* no op for now */
1403 break;
1405 #ifdef MOUSE
1406 case MC_MOUSE:
1408 MOUSEPRESS mp;
1410 mouse_get_last(NULL, &mp);
1411 mp.row -= HEADER_ROWS(ps_global);
1412 ctmp = screen.top_line;
1413 if(mp.doubleclick){
1414 if(screen.mode == SingleMode){
1415 if(ta_do_take(current, how_many_selected, command_line,
1416 tas, command))
1417 done++;
1418 else
1419 directly_to_take = 0;
1421 else{
1422 current->checked = !current->checked; /* flip it */
1423 how_many_selected += (current->checked ? 1 : -1);
1426 else{
1427 while(mp.row && ctmp != NULL){
1428 --mp.row;
1429 do ctmp = ctmp->next;
1430 while(ctmp != NULL && ctmp->skip_it && !ctmp->print);
1433 if(ctmp != NULL && !ctmp->skip_it)
1434 current = ctmp;
1437 break;
1438 #endif
1440 case MC_REPAINT :
1441 case MC_RESIZE :
1442 ClearScreen();
1443 ps->mangled_screen = 1;
1444 break;
1446 case MC_CHOICE : /* [UN]select this addr */
1447 current->checked = !current->checked; /* flip it */
1448 how_many_selected += (current->checked ? 1 : -1);
1449 break;
1451 case MC_SELALL : /* select all */
1452 how_many_selected = ta_mark_all(first_sel_taline(current));
1453 ps->mangled_body = 1;
1454 break;
1456 case MC_UNSELALL: /* unselect all */
1457 how_many_selected = ta_unmark_all(first_sel_taline(current));
1458 ps->mangled_body = 1;
1459 break;
1461 case MC_LISTMODE: /* switch to SingleMode */
1462 if(screen.mode == ListMode){
1463 screen.mode = SingleMode;
1464 q_status_message(SM_INFO, 0, 1,
1465 _("Single mode: Use \"P\" or \"N\" to select desired address"));
1467 else{
1468 screen.mode = ListMode;
1469 q_status_message(SM_INFO, 0, 1,
1470 _("List mode: Use \"X\" to mark addresses to be included in list"));
1472 if(how_many_selected <= 1){
1473 how_many_selected =
1474 ta_unmark_all(first_sel_taline(current));
1475 current->checked = 1;
1476 how_many_selected++;
1480 ps->mangled_screen = 1;
1481 break;
1484 case MC_NONE : /* simple timeout */
1485 break;
1488 /* Unbound (or not dealt with) keystroke */
1489 case MC_CHARRIGHT :
1490 case MC_CHARLEFT :
1491 case MC_GOTOBOL :
1492 case MC_GOTOEOL :
1493 case MC_UNKNOWN :
1494 default:
1495 bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
1496 break;
1498 case MC_UTF8:
1499 bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
1500 break;
1504 ps->mangled_screen = 1;
1506 return(ret);
1511 * Do what takeaddr_screen does except bypass the takeaddr_screen and
1512 * go directly to do_take.
1514 void
1515 takeaddr_bypass(struct pine *ps, TA_S *current, TA_STATE_S **tasp)
1517 TA_SCREEN_S screen; /* We have to fake out ta_do_take because */
1518 /* we're bypassing takeaddr_screen. */
1519 ta_screen = &screen;
1520 ta_screen->mode = SingleMode;
1521 current = first_sel_taline(current);
1522 (void) ta_do_take(current, 1, -FOOTER_ROWS(ps_global), tasp, _("save"));
1523 ps->mangled_screen = 1;
1531 ta_do_take(TA_S *current, int how_many_selected, int command_line,
1532 TA_STATE_S **tas, char *cmd)
1534 return((ta_screen->mode == ListMode)
1535 ? ta_take_marked_addrs(how_many_selected,
1536 first_sel_taline(current),
1537 command_line, tas, cmd)
1538 : ta_take_single_addr(current, command_line, tas, cmd));
1543 * WhereIs for TakeAddr screen.
1545 * Returns the line match is found in or NULL.
1547 TA_S *
1548 whereis_taline(TA_S *current)
1550 TA_S *p;
1551 int rc, found = 0, wrapped = 0, flags;
1552 char *result = NULL, buf[MAX_SEARCH+1], tmp[MAX_SEARCH+20];
1553 static char last[MAX_SEARCH+1];
1554 HelpType help;
1555 static ESCKEY_S ekey[] = {
1556 {0, 0, "", ""},
1557 {ctrl('Y'), 10, "^Y", N_("Top")},
1558 {ctrl('V'), 11, "^V", N_("Bottom")},
1559 {-1, 0, NULL, NULL}};
1561 if(!current)
1562 return NULL;
1564 /*--- get string ---*/
1565 buf[0] = '\0';
1566 snprintf(tmp, sizeof(tmp), _("Word to find %s%.*s%s: "),
1567 (last[0]) ? "[" : "",
1568 MAX_SEARCH, (last[0]) ? last : "", /* MAX_SEARCH == sizeof(tmp) - 20 */
1569 (last[0]) ? "]" : "");
1570 tmp[sizeof(tmp)-1] = '\0';
1571 help = NO_HELP;
1572 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
1573 while(1){
1574 rc = optionally_enter(buf,-FOOTER_ROWS(ps_global),0,sizeof(buf),
1575 tmp,ekey,help,&flags);
1576 if(rc == 3)
1577 help = help == NO_HELP ? h_config_whereis : NO_HELP;
1578 else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){
1579 if(rc == 0 && !buf[0] && last[0]){
1580 strncpy(buf, last, sizeof(buf)-1);
1581 buf[sizeof(buf)-1] = '\0';
1584 break;
1588 if(rc == 0 && buf[0]){
1589 p = current;
1590 while((p = next_taline(p)) != NULL)
1591 if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
1592 SIZEOF_20KBUF, p->strvalue),
1593 buf)){
1594 found++;
1595 break;
1598 if(!found){
1599 p = first_taline(current);
1601 while(p != current)
1602 if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
1603 SIZEOF_20KBUF, p->strvalue),
1604 buf)){
1605 found++;
1606 wrapped++;
1607 break;
1609 else
1610 p = next_taline(p);
1613 else if(rc == 10){
1614 current = first_sel_taline(current);
1615 result = _("Searched to top");
1617 else if(rc == 11){
1618 current = last_sel_taline(current);
1619 result = _("Searched to bottom");
1621 else{
1622 current = NULL;
1623 result = _("WhereIs cancelled");
1626 if(found){
1627 current = p;
1628 result = wrapped ? _("Search wrapped to beginning") : _("Word found");
1629 strncpy(last, buf, sizeof(last)-1);
1630 last[sizeof(last)-1] = '\0';
1633 q_status_message(SM_ORDER,0,3,result ? result : _("Word not found"));
1634 return(current);
1639 * Call the addrbook functions which add the checked addresses.
1641 * Args: how_many_selected -- how many addresses are checked
1642 * f_line -- the first ta line
1644 * Returns: 1 -- we're done, caller should return
1645 * 0 -- we're not done
1648 ta_take_marked_addrs(int how_many_selected, TA_S *f_line, int command_line,
1649 TA_STATE_S **tas, char *cmd)
1651 char **new_list;
1652 TA_S *p;
1654 if(how_many_selected == 0){
1655 q_status_message(SM_ORDER, 0, 4,
1656 _("No addresses marked for taking. Use ExitTake to leave TakeAddr screen"));
1657 return 0;
1660 if(how_many_selected == 1){
1661 for(p = f_line; p; p = next_sel_taline(p))
1662 if(p->checked && !p->skip_it)
1663 break;
1665 if(p)
1666 add_abook_entry(p,
1667 (p->nickname && p->nickname[0]) ? p->nickname : NULL,
1668 (p->fullname && p->fullname[0]) ? p->fullname : NULL,
1669 (p->fcc && p->fcc[0]) ? p->fcc : NULL,
1670 (p->comment && p->comment[0]) ? p->comment : NULL,
1671 command_line, tas, cmd);
1673 else{
1674 new_list = list_of_checked(f_line);
1675 for(p = f_line; p; p = next_sel_taline(p))
1676 if(p->checked && !p->skip_it)
1677 break;
1679 take_to_addrbooks_frontend(new_list, p ? p->nickname : NULL,
1680 p ? p->fullname : NULL, NULL, p ? p->fcc : NULL,
1681 p ? p->comment : NULL, command_line, tas, cmd);
1682 free_list_array(&new_list);
1685 return 1;
1690 ta_take_single_addr(TA_S *cur, int command_line, TA_STATE_S **tas, char *cmd)
1692 add_abook_entry(cur,
1693 (cur->nickname && cur->nickname[0]) ? cur->nickname : NULL,
1694 (cur->fullname && cur->fullname[0]) ? cur->fullname : NULL,
1695 (cur->fcc && cur->fcc[0]) ? cur->fcc : NULL,
1696 (cur->comment && cur->comment[0]) ? cur->comment : NULL,
1697 command_line, tas, cmd);
1699 return 1;
1704 * Manage display of the Take Address screen.
1706 * Args: ps -- pine state
1707 * current -- the current TA line
1708 * screen -- the TA screen
1709 * cursor_pos -- return good cursor position here
1712 update_takeaddr_screen(struct pine *ps, TA_S *current, TA_SCREEN_S *screen, Pos *cursor_pos)
1714 int dline;
1715 TA_S *top_line,
1716 *ctmp;
1717 int longest, i, j;
1718 char buf1[6*MAX_SCREEN_COLS + 30];
1719 char buf2[6*MAX_SCREEN_COLS + 30 + 10];
1720 char *p, *q;
1721 int screen_width = ps->ttyo->screen_cols;
1722 Pos cpos;
1724 cpos.row = HEADER_ROWS(ps); /* default return value */
1726 /* calculate top line of display */
1727 dline = 0;
1728 top_line = 0;
1730 if (ta_screen->top_line){
1731 for(dline = 0, ctmp = ta_screen->top_line;
1732 ctmp && ctmp != current; ctmp = next_taline(ctmp))
1733 dline++;
1735 if (ctmp && (dline < ps->ttyo->screen_rows - HEADER_ROWS(ps)
1736 - FOOTER_ROWS(ps)))
1737 top_line = ta_screen->top_line;
1740 if (!top_line){
1741 dline = 0;
1742 ctmp = top_line = first_taline(current);
1744 if(((dline++) % (ps->ttyo->screen_rows - HEADER_ROWS(ps)
1745 - FOOTER_ROWS(ps))) == 0)
1746 top_line = ctmp;
1747 while(ctmp != current && (ctmp = next_taline(ctmp)));
1750 #ifdef _WINDOWS
1752 * Figure out how far down the top line is from the top and how many
1753 * total lines there are. Dumb to loop every time thru, but
1754 * there aren't that many lines, and it's cheaper than rewriting things
1755 * to maintain a line count in each structure...
1757 for(dline = 0, ctmp = pre_taline(top_line); ctmp; ctmp = pre_taline(ctmp))
1758 dline++;
1760 scroll_setpos(dline);
1762 for(ctmp = next_taline(top_line); ctmp ; ctmp = next_taline(ctmp))
1763 dline++;
1765 scroll_setrange(ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps),
1766 dline);
1767 #endif
1770 /* mangled body or new page, force redraw */
1771 if(ps->mangled_body || screen->top_line != top_line)
1772 screen->current = NULL;
1774 /* find width of longest line for nicer formatting */
1775 longest = 0;
1776 for(ctmp = first_taline(top_line); ctmp; ctmp = next_taline(ctmp)){
1777 int width;
1779 if(ctmp
1780 && !ctmp->print
1781 && ctmp->strvalue
1782 && longest < (width = utf8_width((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
1783 SIZEOF_20KBUF, ctmp->strvalue))))
1784 longest = width;
1787 #define LENGTH_OF_THAT_STRING 5 /* "[X] " */
1788 longest = MIN(longest, ps->ttyo->screen_cols);
1790 /* loop thru painting what's needed */
1791 for(dline = 0, ctmp = top_line;
1792 dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
1793 dline++, ctmp = next_taline(ctmp)){
1796 * only fall thru painting if something needs painting...
1798 if(!ctmp || !screen->current || ctmp == screen->current ||
1799 ctmp == top_line || ctmp == current){
1800 ClearLine(dline + HEADER_ROWS(ps));
1801 if(!ctmp || !ctmp->strvalue)
1802 continue;
1805 p = buf1;
1806 if(ctmp == current){
1807 cpos.row = dline + HEADER_ROWS(ps); /* col set below */
1808 StartInverse();
1811 if(ctmp->print)
1812 j = 0;
1813 else
1814 j = LENGTH_OF_THAT_STRING;
1817 * Copy the value to a temp buffer expanding tabs, and
1818 * making sure not to write beyond screen right...
1820 q = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
1821 SIZEOF_20KBUF, ctmp->strvalue);
1823 for(i = 0; q[i] && j < ps->ttyo->screen_cols && p-buf1 < sizeof(buf1); i++){
1824 if(q[i] == ctrl('I')){
1826 *p++ = SPACE;
1827 while(j < ps->ttyo->screen_cols && ((++j)&0x07) && p-buf1 < sizeof(buf1));
1830 else{
1831 *p++ = q[i];
1832 j++;
1836 if(p-buf1 < sizeof(buf1))
1837 *p = '\0';
1839 if(utf8_width(buf1) < longest){
1840 (void) utf8_pad_to_width(buf2, buf1, sizeof(buf2), longest, 1);
1841 /* it's expected to be in buf1 below */
1842 strncpy(buf1, buf2, sizeof(buf1));
1843 buf1[sizeof(buf1)-1] = '\0';
1846 /* mark lines which have check marks */
1847 if(ctmp == current){
1848 if(screen->mode == ListMode){
1849 snprintf(buf2, sizeof(buf2), "[%c] %s", ctmp->checked ? 'X' : SPACE, buf1);
1850 buf2[sizeof(buf2)-1] = '\0';
1851 cpos.col = 1; /* position on the X */
1853 else{
1854 snprintf(buf2, sizeof(buf2), " %s", buf1);
1855 buf2[sizeof(buf2)-1] = '\0';
1856 cpos.col = 5; /* 5 spaces before text */
1859 else{
1860 if(ctmp->print){
1861 int width, actual_width;
1862 size_t len;
1865 * In buf2 make ------string--------
1866 * which reaches all the way across the screen. String will
1867 * already have a leading and trailing space.
1869 width = utf8_width(buf1);
1871 if(width > screen_width){
1872 actual_width = utf8_truncate(buf1, screen_width);
1873 /* it might be 1 less */
1874 if(actual_width < screen_width && (len=strlen(buf1))+1 < sizeof(buf1)){
1875 buf1[len] = SPACE;
1876 buf1[len+1] = '\0';
1879 else{
1880 snprintf(buf2, sizeof(buf2), "%s%s",
1881 repeat_char((screen_width-width)/2, '-'), buf1);
1882 buf2[sizeof(buf2)-1] = '\0';
1883 len = strlen(buf2);
1884 width = utf8_width(buf2);
1885 snprintf(buf2+len, sizeof(buf2)-len, "%s",
1886 repeat_char(screen_width-width, '-'));
1887 buf2[sizeof(buf2)-1] = '\0';
1890 else{
1891 if(screen->mode == ListMode) /* 6*MAX_SCREEN_COLS + 24 = sizeof(buf2)-6 */
1892 snprintf(buf2, sizeof(buf2), "[%c] %.*s", ctmp->checked ? 'X' : SPACE,
1893 6*MAX_SCREEN_COLS + 24, buf1);
1894 else
1895 snprintf(buf2, sizeof(buf2), " %.*s", 6*MAX_SCREEN_COLS+24, buf1);
1897 buf2[sizeof(buf2)-1] = '\0';
1901 PutLine0(dline + HEADER_ROWS(ps), 0, buf2);
1903 if(ctmp == current)
1904 EndInverse();
1907 ps->mangled_body = 0;
1908 screen->top_line = top_line;
1909 screen->current = current;
1910 if(cursor_pos)
1911 *cursor_pos = cpos;
1913 return(cpos.row);
1917 void
1918 takeaddr_screen_redrawer_list(void)
1920 ps_global->mangled_body = 1;
1921 (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen,
1922 (Pos *)NULL);
1926 void
1927 takeaddr_screen_redrawer_single(void)
1929 ps_global->mangled_body = 1;
1930 (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen,
1931 (Pos *)NULL);
1935 /* jpf work in progress
1936 * Execute command to take addresses out of message and put in the address book
1938 * Args: ps -- pine state
1939 * msgmap -- the MessageMap
1940 * agg -- this is aggregate operation if set
1942 * Result: The entry is added to an address book.
1945 cmd_take_addr(struct pine *ps, MSGNO_S *msgmap, int agg)
1947 TA_S *ta_list = NULL;
1948 int how_many_selected = 0, rtype;
1950 /* Ask user what kind of Take they want to do */
1951 if(!agg && F_ON(F_ENABLE_ROLE_TAKE, ps)){
1952 rtype = rule_setup_type(ps,
1953 RS_RULES |
1954 ((mn_get_total(msgmap) > 0)
1955 ? (F_ON(F_ENABLE_TAKE_EXPORT, ps)
1956 ? (RS_INCADDR | RS_INCEXP)
1957 : RS_INCADDR)
1958 : RS_NONE),
1959 "Take to : ");
1961 else if(F_ON(F_ENABLE_TAKE_EXPORT, ps) && mn_get_total(msgmap) > 0)
1962 rtype = rule_setup_type(ps, RS_INCADDR | RS_INCEXP, "Take to : ");
1963 else
1964 rtype = 'a';
1966 if(rtype == 'x' || rtype == 'Z'){
1967 if(rtype == 'x')
1968 cmd_cancelled(NULL);
1969 else if(rtype == 'Z')
1970 q_status_message(SM_ORDER | SM_DING, 3, 5,
1971 "Try turning on color with the Setup/Kolor command.");
1972 return -1;
1975 ps->mangled_footer = 1;
1977 if(rtype > 0)
1978 switch(rtype){
1979 case 'e':
1981 LINES_TO_TAKE *lines_to_take = NULL;
1983 rtype = set_up_takeaddr('e', ps, msgmap, &ta_list, &how_many_selected,
1984 agg ? TA_AGG : 0, NULL);
1986 if(rtype >= 0){
1987 if(convert_ta_to_lines(ta_list, &lines_to_take)){
1988 while(lines_to_take && lines_to_take->prev)
1989 lines_to_take = lines_to_take->prev;
1991 take_to_export(ps, lines_to_take);
1993 free_ltlines(&lines_to_take);
1995 else
1996 q_status_message(SM_ORDER, 3, 4, _("Can't find anything to export"));
2000 break;
2002 case 'r':
2003 case 's':
2004 case 'i':
2005 case 'f':
2006 case 'o':
2007 case 'c':
2008 case 'x':
2009 role_take(ps, msgmap, rtype);
2010 break;
2012 case 'a':
2013 rtype = set_up_takeaddr('a', ps, msgmap, &ta_list, &how_many_selected,
2014 agg ? TA_AGG : 0, attached_addr_handler);
2015 if(rtype >= 0){
2016 (void) takeaddr_screen(ps, ta_list, how_many_selected,
2017 agg ? ListMode : SingleMode,
2018 NULL, _("take"));
2021 break;
2023 default:
2024 break;
2027 /* clean up */
2028 free_talines(&ta_list);
2029 env_for_pico_callback = NULL;
2030 body_for_pico_callback = NULL;
2032 return(rtype >= 0 ? 1 : 0);
2037 attached_addr_handler(TA_S *current, int added)
2039 char prompt[200];
2040 int command_line = -FOOTER_ROWS(ps_global);
2042 snprintf(prompt, sizeof(prompt),
2043 "Take %d entries from attachment to addrbook all at once ",
2044 added);
2045 switch(want_to(prompt, 'n', 'x', NO_HELP, WT_NORM)){
2046 case 'y':
2047 if(take_without_edit(current, added, command_line, NULL, "take") >= 0)
2048 return(0); /* all taken care of */
2049 else
2050 return(-1); /* problem */
2052 case 'x':
2053 cmd_cancelled("Take");
2054 return(-1); /* problem */
2056 default:
2057 return(1); /* proceed */
2063 take_without_edit(TA_S *ta_list, int num_in_list, int command_line, TA_STATE_S **tas, char *cmd)
2065 #define OURTMPBUFLEN 200
2066 PerAddrBook *pab_dst;
2067 SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
2068 int rc, total_to_copy = 0;
2069 int how_many_dups = 0, how_many_to_copy = 0, skip_dups = 0;
2070 int ret = 0;
2071 int err = 0, need_write = 0, we_cancel = 0;
2072 adrbk_cntr_t new_entry_num;
2073 char warn[2][MAX_NICKNAME+1];
2074 char tmp[OURTMPBUFLEN];
2075 TA_S *current;
2076 SWOOP_S *swoop_list = NULL, *sw;
2078 dprint((2, "\n - take_without_edit(%d) - \n",
2079 num_in_list));
2081 /* move to beginning of the list */
2082 if(ta_list)
2083 while(ta_list->prev)
2084 ta_list = ta_list->prev;
2086 pab_dst = setup_for_addrbook_add(&state, command_line, cmd);
2087 if(!pab_dst)
2088 goto get_out;
2090 swoop_list = (SWOOP_S *)fs_get((num_in_list+1) * sizeof(SWOOP_S));
2091 memset((void *)swoop_list, 0, (num_in_list+1) * sizeof(SWOOP_S));
2092 sw = swoop_list;
2095 * Look through all the vcards for those with nicknames already
2096 * existing in the destination abook (dups) and build a list of
2097 * entries to be acted on.
2099 for(current = ta_list; current; current = current->next){
2100 adrbk_cntr_t dst_enum;
2102 if(current->skip_it)
2103 continue;
2105 /* check to see if this nickname already exists in the dest abook */
2106 if(current->nickname && current->nickname[0]){
2107 AdrBk_Entry *abe;
2109 current->checked = 0;
2110 abe = adrbk_lookup_by_nick(pab_dst->address_book,
2111 current->nickname, &dst_enum);
2113 * This nickname already exists.
2115 if(abe){
2116 sw->dup = 1;
2117 sw->dst_enum = dst_enum;
2118 if(how_many_dups < 2){
2119 strncpy(warn[how_many_dups], current->nickname, MAX_NICKNAME);
2120 warn[how_many_dups][MAX_NICKNAME] = '\0';
2123 how_many_dups++;
2127 sw->ta = current;
2128 sw++;
2129 how_many_to_copy++;
2133 * If there are some nicknames which already exist in the selected
2134 * abook, ask user what to do.
2136 if(how_many_dups > 0){
2137 if(how_many_dups == 1){
2138 if(how_many_to_copy == 1 && num_in_list == 1){
2139 ret = 'T'; /* use Take */
2140 if(tas && *tas){
2141 (*tas)->state = state;
2142 (*tas)->pab = pab_dst;
2145 goto get_out;
2147 else{
2148 snprintf(tmp, sizeof(tmp),
2149 "Entry with nickname \"%.*s\" already exists, replace ",
2150 OURTMPBUFLEN-50, warn[0]);
2153 else if(how_many_dups == 2)
2154 snprintf(tmp, sizeof(tmp),
2155 "Nicknames \"%.*s\" and \"%.*s\" already exist, replace ",
2156 (OURTMPBUFLEN-50)/2, warn[0], (OURTMPBUFLEN-50)/2, warn[1]);
2157 else
2158 snprintf(tmp, sizeof(tmp), "%d of the nicknames already exist, replace ",
2159 how_many_dups);
2161 switch(want_to(tmp, 'n', 'x', h_ab_copy_dups, WT_NORM)){
2162 case 'n':
2163 skip_dups++;
2164 break;
2166 case 'y':
2167 break;
2169 case 'x':
2170 err++;
2171 goto get_out;
2176 * Because the deletes happen immediately we have to delete from high
2177 * entry number towards lower entry numbers so that we are deleting
2178 * the correct entries. In order to do that we'll sort the swoop_list
2179 * to give us a safe order.
2181 if(!skip_dups && how_many_dups > 1)
2182 qsort((qsort_t *)swoop_list, (size_t)num_in_list, sizeof(*swoop_list),
2183 cmp_swoop_list);
2185 we_cancel = busy_cue("Saving addrbook entries", NULL, 0);
2186 total_to_copy = how_many_to_copy - (skip_dups ? how_many_dups : 0);
2189 * Add the list of entries to the destination abook.
2191 for(sw = swoop_list; sw && sw->ta; sw++){
2192 Tag tag;
2193 char abuf[MAX_ADDRESS + 1];
2194 int count_of_addrs;
2196 if(skip_dups && sw->dup)
2197 continue;
2200 * Delete existing dups and replace them.
2202 if(sw->dup){
2204 /* delete the existing entry */
2205 rc = 0;
2206 if(adrbk_delete(pab_dst->address_book,
2207 (a_c_arg_t)sw->dst_enum, 1, 0, 0, 0) == 0){
2208 need_write++;
2210 else{
2211 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2212 "Error replacing entry in %.200s: %.200s",
2213 pab_dst->abnick,
2214 error_description(errno));
2215 err++;
2216 goto get_out;
2221 * We need to count the number of addresses in this entry in order
2222 * to tell the adrbk routines if it is a List or a Single, and in
2223 * order to pass the right stuff to be added.
2225 count_of_addrs = count_addrs(sw->ta->addr);
2226 tag = (count_of_addrs > 1) ? List : Single;
2227 if(tag == Single){
2228 if(sw->ta->addr->mailbox && sw->ta->addr->mailbox[0]){
2229 char *scratch, *p, *t, *u;
2230 unsigned long l;
2231 RFC822BUFFER rbuf;
2232 size_t es;
2234 es = est_size(sw->ta->addr);
2235 scratch = (char *) fs_get(es * sizeof(char));
2236 scratch[0] = '\0';
2237 rbuf.f = dummy_soutr;
2238 rbuf.s = NULL;
2239 rbuf.beg = scratch;
2240 rbuf.cur = scratch;
2241 rbuf.end = scratch+es-1;
2242 rfc822_output_address_list(&rbuf, sw->ta->addr, 0L, NULL);
2243 *rbuf.cur = '\0';
2244 if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){
2245 for(t = p; ; t--)
2246 if(*t == '&'){ /* find "leading" token */
2247 *t++ = ' '; /* replace token */
2248 *p = '\0'; /* tie off string */
2249 u = (char *)rfc822_base64((unsigned char *)t,
2250 (unsigned long)strlen(t), &l);
2251 *p = '@'; /* restore 'p' */
2252 rplstr(p, es-(p-scratch), 12, ""); /* clear special token */
2253 rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */
2254 if(u)
2255 fs_give((void **)&u);
2257 else if(t == scratch)
2258 break;
2261 strncpy(abuf, scratch, sizeof(abuf)-1);
2262 abuf[sizeof(abuf)-1] = '\0';
2264 if(scratch)
2265 fs_give((void **)&scratch);
2267 else
2268 abuf[0] = '\0';
2273 * Now we have a clean slate to work with.
2275 if(total_to_copy <= 1)
2276 rc = adrbk_add(pab_dst->address_book,
2277 NO_NEXT,
2278 sw->ta->nickname,
2279 sw->ta->fullname,
2280 tag == Single ? abuf : NULL,
2281 sw->ta->fcc,
2282 sw->ta->comment,
2283 tag,
2284 &new_entry_num,
2285 (int *)NULL,
2289 else
2290 rc = adrbk_append(pab_dst->address_book,
2291 sw->ta->nickname,
2292 sw->ta->fullname,
2293 tag == Single ? abuf : NULL,
2294 sw->ta->fcc,
2295 sw->ta->comment,
2296 tag,
2297 &new_entry_num);
2299 if(rc == 0)
2300 need_write++;
2303 * If the entry we copied is a list, we also have to add
2304 * the list members to the copy.
2306 if(rc == 0 && tag == List){
2307 int i, save_sort_rule;
2308 ADDRESS *a, *save_next;
2309 char **list;
2311 list = (char **)fs_get((count_of_addrs + 1) * sizeof(char *));
2312 memset((void *)list, 0, (count_of_addrs+1) * sizeof(char *));
2313 i = 0;
2314 for(a = sw->ta->addr; a; a = a->next){
2315 save_next = a->next;
2316 a->next = NULL;
2318 if(a->mailbox && a->mailbox[0]){
2319 char *scratch, *p, *t, *u;
2320 unsigned long l;
2321 RFC822BUFFER rbuf;
2322 size_t es;
2324 es = est_size(a);
2325 scratch = (char *) fs_get(es * sizeof(char));
2326 scratch[0] = '\0';
2327 rbuf.f = dummy_soutr;
2328 rbuf.s = NULL;
2329 rbuf.beg = scratch;
2330 rbuf.cur = scratch;
2331 rbuf.end = scratch+es-1;
2332 rfc822_output_address_list(&rbuf, a, 0L, NULL);
2333 *rbuf.cur = '\0';
2334 if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){
2335 for(t = p; ; t--)
2336 if(*t == '&'){ /* find "leading" token */
2337 *t++ = ' '; /* replace token */
2338 *p = '\0'; /* tie off string */
2339 u = (char *)rfc822_base64((unsigned char *)t,
2340 (unsigned long)strlen(t), &l);
2341 *p = '@'; /* restore 'p' */
2342 rplstr(p, es-(p-scratch), 12, ""); /* clear special token */
2343 rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */
2344 if(u)
2345 fs_give((void **)&u);
2347 else if(t == scratch)
2348 break;
2351 strncpy(abuf, scratch, sizeof(abuf)-1);
2352 abuf[sizeof(abuf)-1] = '\0';
2354 if(scratch)
2355 fs_give((void **)&scratch);
2357 else
2358 abuf[0] = '\0';
2360 list[i++] = cpystr(abuf);
2361 a->next = save_next;
2365 * We want it to copy the list in the exact order
2366 * without sorting it.
2368 save_sort_rule = pab_dst->address_book->sort_rule;
2369 pab_dst->address_book->sort_rule = AB_SORT_RULE_NONE;
2371 rc = adrbk_nlistadd(pab_dst->address_book,
2372 (a_c_arg_t)new_entry_num, NULL, NULL,
2373 list, 0, 0, 0);
2375 pab_dst->address_book->sort_rule = save_sort_rule;
2376 free_list_array(&list);
2379 if(rc != 0){
2380 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2381 "Error saving: %.200s",
2382 error_description(errno));
2383 err++;
2384 goto get_out;
2388 if(need_write){
2389 int sort_happened = 0;
2391 if(adrbk_write(pab_dst->address_book, 0, NULL, &sort_happened, 0, 1)){
2392 err++;
2393 goto get_out;
2396 if(sort_happened)
2397 ps_global->mangled_screen = 1;
2400 get_out:
2401 if(we_cancel)
2402 cancel_busy_cue(1);
2404 if(!ret)
2405 restore_state(&state);
2407 if(swoop_list)
2408 fs_give((void **)&swoop_list);
2410 ps_global->mangled_footer = 1;
2412 if(err){
2413 #define CAPCMDLEN 50
2414 char capcmd[CAPCMDLEN];
2416 ret = -1;
2417 snprintf(capcmd, sizeof(capcmd),
2418 "%c%.*s",
2419 islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd)
2420 : *cmd,
2421 CAPCMDLEN-2, cmd+1);
2422 if(need_write)
2423 q_status_message1(SM_ORDER | SM_DING, 3, 4,
2424 "%.200s only partially completed", capcmd);
2425 else
2426 cmd_cancelled(capcmd);
2428 else if(ret != 'T' && total_to_copy > 0){
2430 ret = 1;
2431 snprintf(tmp, sizeof(tmp), "Saved %d %s to \"%.*s\"",
2432 total_to_copy,
2433 (total_to_copy > 1) ? "entries" : "entry",
2434 OURTMPBUFLEN-30, pab_dst->abnick);
2435 q_status_message(SM_ORDER, 4, 4, tmp);
2438 return(ret);
2443 * Special case interface to allow a more interactive Save in the case where
2444 * the user seems to be wanting to save an exact copy of an existing entry.
2445 * For example, they might be trying to save a copy of a list with the intention
2446 * of changing it a little bit. The regular save doesn't allow this, since no
2447 * editing takes place, but this version plops them into the address book
2448 * editor.
2450 void
2451 take_this_one_entry(struct pine *ps, TA_STATE_S **tasp, AdrBk *abook, long int cur_line)
2453 AdrBk_Entry *abe;
2454 AddrScrn_Disp *dl;
2455 char *fcc = NULL, *comment = NULL, *fullname = NULL,
2456 *nickname = NULL;
2457 ADDRESS *addr;
2458 int how_many_selected;
2459 TA_S *current = NULL;
2461 dl = dlist(cur_line);
2462 abe = ae(cur_line);
2463 if(!abe){
2464 q_status_message(SM_ORDER, 0, 4, _("Nothing to save, cancelled"));
2465 return;
2468 if(dl->type == ListHead || dl->type == Simple){
2469 fcc = (abe->fcc && abe->fcc[0]) ? abe->fcc : NULL;
2470 comment = (abe->extra && abe->extra[0]) ? abe->extra : NULL;
2471 fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL;
2472 nickname = (abe->nickname && abe->nickname[0]) ? abe->nickname : NULL;
2475 addr = abe_to_address(abe, dl, abook, &how_many_selected);
2476 if(!addr){
2477 addr = mail_newaddr();
2478 addr->host = cpystr("");
2479 addr->mailbox = cpystr("");
2482 switch(abe->tag){
2483 case Single:
2484 #ifdef ENABLE_LDAP
2486 * Special case. When user is saving an entry with a runtime
2487 * ldap lookup address, they may be doing it because the lookup
2488 * has become stale. Give them a way to get the old address out
2489 * of the lookup entry so they can save that, instead.
2491 if(!addr->personal && !strncmp(addr->mailbox, RUN_LDAP, LEN_RL)){
2492 LDAP_SERV_S *info = NULL;
2493 int i = 'l';
2494 static ESCKEY_S backup_or_ldap[] = {
2495 {'b', 'b', "B", N_("Backup")},
2496 {'l', 'l', "L", N_("LDAP")},
2497 {-1, 0, NULL, NULL}};
2499 info = break_up_ldap_server(addr->mailbox + LEN_RL);
2500 if(info && info->mail && *info->mail)
2501 i = radio_buttons(_("Copy backup address or retain LDAP search criteria ? "),
2502 -FOOTER_ROWS(ps_global), backup_or_ldap,
2503 'b', 'x',
2504 h_ab_backup_or_ldap, RB_NORM);
2506 if(i == 'b'){
2507 ADDRESS *a = NULL;
2509 rfc822_parse_adrlist(&a, info->mail, fakedomain);
2511 if(a){
2512 if(addr->mailbox)
2513 fs_give((void **)&addr->mailbox);
2514 if(addr->host)
2515 fs_give((void **)&addr->host);
2517 addr->mailbox = a->mailbox;
2518 a->mailbox = NULL;
2519 addr->host = a->host;
2520 a->host = NULL;
2521 mail_free_address(&a);
2525 if(info)
2526 free_ldap_server_info(&info);
2528 #endif /* ENABLE_LDAP */
2529 current = fill_in_ta(&current, addr, 0, (char *)NULL);
2530 break;
2532 case List:
2534 * The empty string for the last argument is significant. Fill_in_ta
2535 * copies the whole adrlist into a single TA if there is a print
2536 * string.
2538 if(dl->type == ListHead)
2539 current = fill_in_ta(&current, addr, 1, "");
2540 else
2541 current = fill_in_ta(&current, addr, 0, (char *)NULL);
2543 break;
2545 default:
2546 dprint((1,
2547 "default case in take_this_one_entry, shouldn't happen\n"));
2548 return;
2551 if(current->strvalue && !strcmp(current->strvalue, "@")){
2552 fs_give((void **)&current->strvalue);
2553 if(fullname && fullname[0])
2554 current->strvalue = cpystr(fullname);
2555 else if(nickname && nickname[0])
2556 current->strvalue = cpystr(nickname);
2557 else
2558 current->strvalue = cpystr("?");
2560 convert_possibly_encoded_str_to_utf8(&current->strvalue);
2563 if(addr)
2564 mail_free_address(&addr);
2566 if(current){
2567 current = first_sel_taline(current);
2568 if(fullname && *fullname){
2569 current->fullname = cpystr(fullname);
2570 convert_possibly_encoded_str_to_utf8(&current->fullname);
2573 if(fcc && *fcc){
2574 current->fcc = cpystr(fcc);
2575 convert_possibly_encoded_str_to_utf8(&current->fcc);
2578 if(comment && *comment){
2579 current->comment = cpystr(comment);
2580 convert_possibly_encoded_str_to_utf8(&current->comment);
2583 if(nickname && *nickname){
2584 current->nickname = cpystr(nickname);
2585 convert_possibly_encoded_str_to_utf8(&current->nickname);
2588 takeaddr_bypass(ps, current, tasp);
2590 else
2591 q_status_message(SM_ORDER, 0, 4, _("Nothing to save, cancelled"));
2593 free_talines(&current);
2598 * Execute command to save addresses out of vcard attachment.
2600 void
2601 save_vcard_att(struct pine *ps, int qline, long int msgno, ATTACH_S *a)
2603 int how_many_selected, j;
2604 TA_S *current = NULL;
2605 TA_STATE_S tas, *tasp;
2608 dprint((2, "\n - saving vcard attachment - \n"));
2610 j = radio_buttons(_("Save to address book or Export to filesystem ? "),
2611 qline, save_or_export, 's', 'x',
2612 h_ab_save_exp, RB_NORM|RB_SEQ_SENSITIVE);
2614 switch(j){
2615 case 'x':
2616 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
2617 return;
2619 case 'e':
2620 export_vcard_att(ps, qline, msgno, a);
2621 return;
2623 case 's':
2624 break;
2626 default:
2627 q_status_message(SM_ORDER, 3, 3, "can't happen in save_vcard_att");
2628 return;
2631 dprint((2, "\n - saving attachment into address book - \n"));
2632 ps->mangled_footer = 1;
2633 current = NULL;
2634 how_many_selected = process_vcard_atts(ps->mail_stream, msgno, NULL,
2635 a->body, a->number, &current);
2636 if(how_many_selected > 0){
2637 tas.pab = NULL;
2638 tasp = &tas;
2639 if(how_many_selected == 1){
2640 takeaddr_bypass(ps, current, NULL);
2642 else if(take_without_edit(current, how_many_selected, qline,
2643 &tasp, "save") == 'T'){
2645 * Eliminate dups.
2647 how_many_selected -=
2648 eliminate_dups_but_not_us(first_sel_taline(current));
2650 (void)takeaddr_screen(ps, current, how_many_selected, SingleMode,
2651 &tasp, _("save"));
2654 * If takeaddr_screen or its children didn't do this for us,
2655 * we do it here.
2657 if(tas.pab)
2658 restore_state(&(tas.state));
2661 else if(how_many_selected == 0)
2662 q_status_message(SM_ORDER, 0, 3,
2663 _("Save cancelled: no entries in attachment"));
2665 free_talines(&current);
2670 * Execute command to export vcard attachment.
2672 void
2673 export_vcard_att(struct pine *ps, int qline, long int msgno, ATTACH_S *a)
2675 int how_many_selected, i;
2676 TA_S *current;
2677 STORE_S *srcstore = NULL;
2678 SourceType srctype;
2679 static ESCKEY_S vcard_or_addresses[] = {
2680 {'a', 'a', "A", N_("Address List")},
2681 {'v', 'v', "V", N_("VCard")},
2682 {-1, 0, NULL, NULL}};
2684 if(ps->restricted){
2685 q_status_message(SM_ORDER, 0, 3,
2686 "Alpine demo can't export addresses to files");
2687 return;
2690 dprint((2, "\n - exporting vcard attachment - \n"));
2692 i = radio_buttons(_("Export list of addresses or vCard text ? "),
2693 qline, vcard_or_addresses, 'a', 'x',
2694 h_ab_export_vcard, RB_NORM|RB_SEQ_SENSITIVE);
2696 switch(i){
2697 case 'x':
2698 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
2699 return;
2701 case 'a':
2702 break;
2704 case 'v':
2705 write_attachment(qline, msgno, a, "EXPORT");
2706 return;
2708 default:
2709 q_status_message(SM_ORDER, 3, 3, _("can't happen in export_vcard_att"));
2710 return;
2713 ps->mangled_footer = 1;
2714 current = NULL;
2715 how_many_selected = process_vcard_atts(ps->mail_stream, msgno, NULL,
2716 a->body, a->number, &current);
2718 * Run through all of the list and run through
2719 * the addresses in each ta->addr, writing them into a storage object.
2720 * Then export to filesystem.
2722 srctype = CharStar;
2723 if(how_many_selected > 0 &&
2724 (srcstore = so_get(srctype, NULL, EDIT_ACCESS)) != NULL){
2725 ADDRESS *aa, *bb;
2726 int are_some = 0;
2728 for(current = first_taline(current);
2729 current;
2730 current = next_taline(current)){
2732 for(aa = current->addr; aa; aa = aa->next){
2733 bb = aa->next;
2734 aa->next = NULL;
2735 so_puts(srcstore, addr_list_string(aa, NULL, 0));
2736 are_some++;
2737 so_puts(srcstore, "\n");
2738 aa->next = bb;
2742 if(are_some)
2743 simple_export(ps, so_text(srcstore), srctype, "addresses", NULL);
2744 else
2745 q_status_message(SM_ORDER, 0, 3, _("No addresses to export"));
2747 so_give(&srcstore);
2749 else{
2750 if(how_many_selected == 0)
2751 q_status_message(SM_ORDER, 0, 3, _("Nothing to export"));
2752 else
2753 q_status_message(SM_ORDER,0,2, _("Error allocating space"));
2758 void
2759 take_to_export(struct pine *ps, LINES_TO_TAKE *lines_to_take)
2761 CONF_S *ctmp = NULL, *first_line = NULL;
2762 OPT_SCREEN_S screen;
2763 LINES_TO_TAKE *li;
2764 char *help_title = _("HELP FOR TAKE EXPORT SCREEN");
2765 char *p;
2766 ScreenMode listmode = SingleMode;
2768 for(li = lines_to_take; li; li = li->next){
2770 new_confline(&ctmp);
2771 ctmp->flags |= CF_STARTITEM;
2772 if(li->flags & LT_NOSELECT)
2773 ctmp->flags |= CF_NOSELECT;
2774 else if(!first_line)
2775 first_line = ctmp;
2777 p = li->printval ? li->printval : "";
2779 if(ctmp->flags & CF_NOSELECT)
2780 ctmp->value = cpystr(p);
2781 else{
2782 size_t l;
2784 /* 5 is for "[X] " */
2785 l = strlen(p)+5;
2786 ctmp->value = (char *)fs_get((l+1) * sizeof(char));
2787 snprintf(ctmp->value, l+1, " %s", p);
2788 ctmp->value[l] = '\0';
2791 /* this points to data, it doesn't have its own copy */
2792 ctmp->d.t.exportval = li->exportval ? li->exportval : NULL;
2793 ctmp->d.t.selected = 0;
2794 ctmp->d.t.listmode = &listmode;
2796 ctmp->tool = take_export_tool;
2797 ctmp->help_title = help_title;
2798 ctmp->help = h_takeexport_screen;
2799 ctmp->keymenu = &take_export_keymenu_sm;
2802 if(!first_line)
2803 q_status_message(SM_ORDER, 3, 3, _("No lines to export"));
2804 else{
2805 memset(&screen, 0, sizeof(screen));
2806 conf_scroll_screen(ps, &screen, first_line, _("Take Export"), NULL, 0, NULL);
2812 take_export_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
2814 CONF_S *ctmp;
2815 int retval = 0;
2816 int some_selected = 0, something_to_export = 0;
2817 SourceType srctype;
2818 STORE_S *srcstore = NULL;
2819 char *prompt_msg;
2821 switch(cmd){
2822 case MC_TAKE :
2823 srctype = CharStar;
2824 if((srcstore = so_get(srctype, NULL, EDIT_ACCESS)) != NULL){
2825 if(*(*cl)->d.t.listmode == SingleMode){
2826 some_selected++;
2827 if((*cl)->d.t.exportval && (*cl)->d.t.exportval[0]){
2828 so_puts(srcstore, (*cl)->d.t.exportval);
2829 so_puts(srcstore, "\n");
2830 something_to_export++;
2831 prompt_msg = "selection";
2834 else{
2835 /* go to first line */
2836 for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
2839 for(; ctmp; ctmp = next_confline(ctmp))
2840 if(!(ctmp->flags & CF_NOSELECT) && ctmp->d.t.selected){
2841 some_selected++;
2842 if(ctmp->d.t.exportval && ctmp->d.t.exportval[0]){
2843 so_puts(srcstore, ctmp->d.t.exportval);
2844 so_puts(srcstore, "\n");
2845 something_to_export++;
2846 prompt_msg = "selections";
2852 if(!srcstore)
2853 q_status_message(SM_ORDER, 0, 3, _("Error allocating space"));
2854 else if(something_to_export)
2855 simple_export(ps, so_text(srcstore), srctype, prompt_msg, NULL);
2856 else if(!some_selected && *(*cl)->d.t.listmode == ListMode)
2857 q_status_message(SM_ORDER, 0, 3, _("Use \"X\" to mark selections"));
2858 else
2859 q_status_message(SM_ORDER, 0, 3, _("Nothing to export"));
2861 if(srcstore)
2862 so_give(&srcstore);
2864 break;
2866 case MC_LISTMODE :
2867 if(*(*cl)->d.t.listmode == SingleMode){
2869 * UnHide the checkboxes
2872 *(*cl)->d.t.listmode = ListMode;
2874 /* go to first line */
2875 for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
2878 for(; ctmp; ctmp = next_confline(ctmp))
2879 if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
2880 ctmp->value[0] = '[';
2881 ctmp->value[1] = ctmp->d.t.selected ? 'X' : SPACE;
2882 ctmp->value[2] = ']';
2883 ctmp->keymenu = &take_export_keymenu_lm;
2886 else{
2888 * Hide the checkboxes
2891 *(*cl)->d.t.listmode = SingleMode;
2893 /* go to first line */
2894 for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
2897 for(; ctmp; ctmp = next_confline(ctmp))
2898 if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
2899 ctmp->value[0] = ctmp->value[1] = ctmp->value[2] = SPACE;
2900 ctmp->keymenu = &take_export_keymenu_sm;
2904 ps->mangled_body = ps->mangled_footer = 1;
2905 break;
2907 case MC_CHOICE :
2908 if((*cl)->value[1] == 'X'){
2909 (*cl)->d.t.selected = 0;
2910 (*cl)->value[1] = SPACE;
2912 else{
2913 (*cl)->d.t.selected = 1;
2914 (*cl)->value[1] = 'X';
2917 ps->mangled_body = 1;
2918 break;
2920 case MC_SELALL :
2921 /* go to first line */
2922 for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
2925 for(; ctmp; ctmp = next_confline(ctmp)){
2926 if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
2927 ctmp->d.t.selected = 1;
2928 if(ctmp->value)
2929 ctmp->value[1] = 'X';
2933 ps->mangled_body = 1;
2934 break;
2936 case MC_UNSELALL :
2937 /* go to first line */
2938 for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
2941 for(; ctmp; ctmp = next_confline(ctmp)){
2942 if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
2943 ctmp->d.t.selected = 0;
2944 if(ctmp->value)
2945 ctmp->value[1] = SPACE;
2949 ps->mangled_body = 1;
2950 break;
2952 case MC_EXIT :
2953 retval = simple_exit_cmd(flags);
2954 break;
2956 default:
2957 retval = -1;
2958 break;
2961 return(retval);
2964 #ifdef ENABLE_LDAP
2966 * Save an LDAP directory entry somewhere
2968 * Args: ps -- pine struct
2969 * e -- the entry to save
2970 * save -- If this is set, then bypass the question about whether
2971 * to save or export and just do the save.
2973 void
2974 save_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *e, int save)
2976 char *fullname = NULL,
2977 *address = NULL,
2978 *first = NULL,
2979 *last = NULL,
2980 *comment = NULL;
2981 struct berval **cn = NULL,
2982 **mail = NULL,
2983 **sn = NULL,
2984 **givenname = NULL,
2985 **title = NULL,
2986 **telephone = NULL,
2987 **elecmail = NULL,
2988 **note = NULL;
2989 int j, num,
2990 export = 0;
2993 dprint((2, "\n - save_ldap_entry - \n"));
2995 if(e){
2996 char *a;
2997 BerElement *ber;
2999 for(a = ldap_first_attribute(e->ld, e->selected_entry, &ber);
3000 a != NULL;
3001 a = ldap_next_attribute(e->ld, e->selected_entry, ber)){
3003 dprint((9, " %s", a ? a : "?"));
3004 if(strcmp(a, e->info_used->cnattr) == 0){
3005 if(!cn)
3006 cn = ldap_get_values_len(e->ld, e->selected_entry, a);
3008 else if(strcmp(a, e->info_used->mailattr) == 0){
3009 if(!mail)
3010 mail = ldap_get_values_len(e->ld, e->selected_entry, a);
3012 else if(strcmp(a, "electronicmail") == 0){
3013 if(!elecmail)
3014 elecmail = ldap_get_values_len(e->ld, e->selected_entry, a);
3016 else if(strcmp(a, "comment") == 0){
3017 if(!note)
3018 note = ldap_get_values_len(e->ld, e->selected_entry, a);
3020 else if(strcmp(a, e->info_used->snattr) == 0){
3021 if(!sn)
3022 sn = ldap_get_values_len(e->ld, e->selected_entry, a);
3024 else if(strcmp(a, e->info_used->gnattr) == 0){
3025 if(!givenname)
3026 givenname = ldap_get_values_len(e->ld, e->selected_entry, a);
3028 else if(strcmp(a, "telephonenumber") == 0){
3029 if(!telephone)
3030 telephone = ldap_get_values_len(e->ld, e->selected_entry, a);
3032 else if(strcmp(a, "title") == 0){
3033 if(!title)
3034 title = ldap_get_values_len(e->ld, e->selected_entry, a);
3038 if(!save){
3039 j = radio_buttons(_("Save to address book or Export to filesystem ? "),
3040 -FOOTER_ROWS(ps), save_or_export, 's', 'x',
3041 h_ab_save_exp, RB_NORM);
3043 switch(j){
3044 case 'x':
3045 q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
3046 break;
3048 case 'e':
3049 export++;
3050 break;
3052 case 's':
3053 save++;
3054 break;
3056 default:
3057 q_status_message(SM_ORDER, 3, 3,
3058 "can't happen in save_ldap_ent");
3059 break;
3064 if(elecmail){
3065 if(ALPINE_LDAP_can_use(elecmail) && !mail)
3066 mail = elecmail;
3067 else
3068 ldap_value_free_len(elecmail);
3070 elecmail = NULL;
3073 if(save){ /* save into the address book */
3074 ADDRESS *addr;
3075 char *d,
3076 *fakedomain = "@";
3077 size_t len;
3078 struct berval **cm;
3079 int how_many_selected = 0;
3082 if(ALPINE_LDAP_can_use(cn))
3083 fullname = cpystr(cn[0]->bv_val);
3084 if(ALPINE_LDAP_can_use(sn))
3085 last = cpystr(sn[0]->bv_val);
3086 if(ALPINE_LDAP_can_use(givenname))
3087 first = cpystr(givenname[0]->bv_val);
3089 if(ALPINE_LDAP_can_use(note))
3090 cm = note;
3091 else
3092 cm = title;
3094 for(len = 0, num = 0; ALPINE_LDAP_usable(cm, num); num++)
3095 len += strlen(cm[num]->bv_val) + 2;
3097 if(len){
3098 comment = (char *)fs_get(len * sizeof(char));
3099 d = comment;
3100 num = 0;
3101 while(ALPINE_LDAP_usable(cm, num)){
3102 sstrncpy(&d, cm[num]->bv_val, len-(d-comment));
3103 num++;
3104 if(ALPINE_LDAP_usable(cm, num))
3105 sstrncpy(&d, "; ", len-(d-comment));
3108 comment[len-1] = '\0';
3111 for(len = 0, num = 0; ALPINE_LDAP_usable(mail, num); num++)
3112 len += strlen(mail[num]->bv_val) + 2;
3114 /* paste the email addresses together */
3115 if(len){
3116 address = (char *)fs_get(len * sizeof(char));
3117 d = address;
3118 num = 0;
3119 while(ALPINE_LDAP_usable(mail, num)){
3120 sstrncpy(&d, mail[num]->bv_val, len-(d-address));
3121 num++;
3122 if(ALPINE_LDAP_usable(mail, num))
3123 sstrncpy(&d, ", ", len-(d-address));
3126 address[len-1] = '\0';
3129 addr = NULL;
3130 if(address)
3131 rfc822_parse_adrlist(&addr, address, fakedomain);
3133 if(addr && fullname && !(first && last)){
3134 if(addr->personal)
3135 fs_give((void **)&addr->personal);
3137 addr->personal = cpystr(fullname);
3140 if(addr && e->serv && *e->serv){ /* save by reference */
3141 char *dn, *edn = NULL;
3143 dn = ldap_get_dn(e->ld, e->selected_entry);
3144 if(dn){
3145 edn = add_backslash_escapes(dn);
3146 our_ldap_dn_memfree(dn);
3149 if(e->serv && *e->serv && edn && *edn){
3150 char buf[MAILTMPLEN+1];
3151 char *backup_mail = NULL;
3153 how_many_selected++;
3155 if(addr && addr->mailbox && addr->host){
3156 strncpy(buf, addr->mailbox, sizeof(buf)-2),
3157 buf[sizeof(buf)-2] = '\0';
3158 strncat(buf, "@", sizeof(buf)-1-strlen(buf));
3159 strncat(buf, addr->host, sizeof(buf)-1-strlen(buf));
3160 buf[sizeof(buf)-1] = '\0';
3161 backup_mail = cpystr(buf);
3165 * We only need one addr which we will use to hold the
3166 * pointer to the query.
3168 if(addr->next)
3169 mail_free_address(&addr->next);
3171 if(addr->mailbox)
3172 fs_give((void **)&addr->mailbox);
3173 if(addr->host)
3174 fs_give((void **)&addr->host);
3175 if(addr->personal)
3176 fs_give((void **)&addr->personal);
3178 snprintf(buf, sizeof(buf),
3179 "%s%s /base=%s/scope=base/cust=(objectclass=*)%s%s",
3180 RUN_LDAP,
3181 e->serv,
3182 edn,
3183 backup_mail ? "/mail=" : "",
3184 backup_mail ? backup_mail : "");
3185 buf[sizeof(buf)-1] = '\0';
3187 if(backup_mail)
3188 fs_give((void **)&backup_mail);
3191 * Put the search parameters in mailbox and put @ in
3192 * host so that expand_address accepts it as an unqualified
3193 * address and doesn't try to add localdomain.
3195 addr->mailbox = cpystr(buf);
3196 addr->host = cpystr("@");
3199 if(edn)
3200 fs_give((void **)&edn);
3202 else{ /* save by value */
3203 how_many_selected++;
3204 if(!addr)
3205 addr = mail_newaddr();
3208 if(how_many_selected > 0){
3209 TA_S *current = NULL;
3212 * The empty string for the last argument is significant.
3213 * Fill_in_ta copies the whole adrlist into a single TA if
3214 * there is a print string.
3216 current = fill_in_ta(&current, addr, 1, "");
3217 current = first_sel_taline(current);
3219 if(first && last && current){
3220 char *p;
3221 size_t l;
3223 l = strlen(last) + 2 + strlen(first);
3224 p = (char *)fs_get((l+1) * sizeof(char));
3225 snprintf(p, l, "%s, %s", last, first);
3226 p[l] = '\0';
3227 current->fullname = p;
3228 convert_possibly_encoded_str_to_utf8(&current->fullname);
3231 if(comment && current){
3232 current->comment = cpystr(comment);
3233 convert_possibly_encoded_str_to_utf8(&current->comment);
3237 * We don't want the personal name to make it into the address
3238 * field in an LDAP: query sort of address, so move it
3239 * out of the addr.
3241 if(e->serv && *e->serv && current && fullname){
3242 if(current->fullname)
3243 fs_give((void **)&current->fullname);
3245 current->fullname = fullname;
3246 fullname = NULL;
3247 convert_possibly_encoded_str_to_utf8(&current->fullname);
3250 mail_free_address(&addr);
3252 if(current)
3253 takeaddr_bypass(ps, current, NULL);
3254 else
3255 q_status_message(SM_ORDER, 0, 4, "Nothing to save");
3257 free_talines(&current);
3259 else
3260 q_status_message(SM_ORDER, 0, 4, "Nothing to save");
3263 else if(export){ /* export to filesystem */
3264 STORE_S *srcstore = NULL;
3265 SourceType srctype;
3266 static ESCKEY_S text_or_vcard[] = {
3267 {'t', 't', "T", N_("Text")},
3268 {'a', 'a', "A", N_("Addresses")},
3269 {'v', 'v', "V", N_("VCard")},
3270 {-1, 0, NULL, NULL}};
3272 j = radio_buttons(
3273 _("Export text of entry, address, or VCard format ? "),
3274 -FOOTER_ROWS(ps), text_or_vcard, 't', 'x',
3275 h_ldap_text_or_vcard, RB_NORM);
3277 switch(j){
3278 case 'x':
3279 q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
3280 break;
3282 case 't':
3283 srctype = CharStar;
3284 if(!(srcstore = prep_ldap_for_viewing(ps, e, srctype, NULL)))
3285 q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
3286 else{
3287 (void)simple_export(ps_global, so_text(srcstore), srctype,
3288 "text", NULL);
3289 so_give(&srcstore);
3292 break;
3294 case 'a':
3295 if(ALPINE_LDAP_can_use(mail)){
3297 srctype = CharStar;
3298 if(!(srcstore = so_get(srctype, NULL, EDIT_ACCESS)))
3299 q_status_message(SM_ORDER,0,2, _("Error allocating space"));
3300 else{
3301 for(num = 0; ALPINE_LDAP_usable(mail, num); num++){
3302 so_puts(srcstore, mail[num]->bv_val);
3303 so_puts(srcstore, "\n");
3306 (void)simple_export(ps_global, so_text(srcstore),
3307 srctype, "addresses", NULL);
3308 so_give(&srcstore);
3312 break;
3314 case 'v':
3315 srctype = CharStar;
3316 if(!(srcstore = so_get(srctype, NULL, EDIT_ACCESS)))
3317 q_status_message(SM_ORDER,0,2, _("Error allocating space"));
3318 else{
3319 gf_o_t pc;
3320 VCARD_INFO_S *vinfo;
3322 vinfo = (VCARD_INFO_S *)fs_get(sizeof(VCARD_INFO_S));
3323 memset((void *)vinfo, 0, sizeof(VCARD_INFO_S));
3325 if(ALPINE_LDAP_can_use(cn))
3326 vinfo->fullname = berval_to_array(cn);
3328 if(ALPINE_LDAP_can_use(note))
3329 vinfo->note = berval_to_array(note);
3331 if(ALPINE_LDAP_can_use(title))
3332 vinfo->title = berval_to_array(title);
3334 if(ALPINE_LDAP_can_use(telephone))
3335 vinfo->tel = berval_to_array(telephone);
3337 if(ALPINE_LDAP_can_use(mail))
3338 vinfo->email = berval_to_array(mail);
3340 if(ALPINE_LDAP_can_use(sn))
3341 vinfo->last = cpystr(sn[0]->bv_val);
3343 if(ALPINE_LDAP_can_use(givenname))
3344 vinfo->first = cpystr(givenname[0]->bv_val);
3346 gf_set_so_writec(&pc, srcstore);
3348 write_single_vcard_entry(ps_global, pc, vinfo);
3350 free_vcard_info(&vinfo);
3352 (void)simple_export(ps_global, so_text(srcstore),
3353 srctype, "vcard text", NULL);
3354 so_give(&srcstore);
3357 break;
3359 default:
3360 q_status_message(SM_ORDER, 3, 3, "can't happen in text_or_vcard");
3361 break;
3365 if(cn)
3366 ldap_value_free_len(cn);
3367 if(mail)
3368 ldap_value_free_len(mail);
3369 if(elecmail)
3370 ldap_value_free_len(elecmail);
3371 if(note)
3372 ldap_value_free_len(note);
3373 if(sn)
3374 ldap_value_free_len(sn);
3375 if(givenname)
3376 ldap_value_free_len(givenname);
3377 if(telephone)
3378 ldap_value_free_len(telephone);
3379 if(title)
3380 ldap_value_free_len(title);
3381 if(fullname)
3382 fs_give((void **)&fullname);
3383 if(address)
3384 fs_give((void **)&address);
3385 if(first)
3386 fs_give((void **)&first);
3387 if(last)
3388 fs_give((void **)&last);
3389 if(comment)
3390 fs_give((void **)&comment);
3392 #endif /* ENABLE_LDAP */
3395 #ifdef _WINDOWS
3398 ta_scroll_up(count)
3399 long count;
3401 if(count<0)
3402 return(ta_scroll_down(-count));
3403 else if (count){
3404 long i=count;
3405 TA_S *next_sel;
3407 while(i && ta_screen->top_line->next){
3408 if(ta_screen->top_line == ta_screen->current){
3409 if(next_sel = next_sel_taline(ta_screen->current)){
3410 ta_screen->current = next_sel;
3411 ta_screen->top_line = next_taline(ta_screen->top_line);
3412 i--;
3414 else i = 0;
3416 else{
3417 ta_screen->top_line = next_taline(ta_screen->top_line);
3418 i--;
3422 return(TRUE);
3426 ta_scroll_down(count)
3427 long count;
3429 if(count < 0)
3430 return(ta_scroll_up(-count));
3431 else if (count){
3432 long i,dline;
3433 long page_size = ps_global->ttyo->screen_rows -
3434 FOOTER_ROWS(ps_global) - HEADER_ROWS(ps_global);
3435 TA_S *ctmp;
3436 TA_S *first = first_taline(ta_screen->top_line);
3438 i=count;
3439 dline=0;
3440 for(ctmp = ta_screen->top_line;
3441 ctmp != ta_screen->current; ctmp = next_taline(ctmp))
3442 dline++;
3443 while(i && ta_screen->top_line != first){
3444 ta_screen->top_line = pre_taline(ta_screen->top_line);
3445 i--;
3446 dline++;
3447 if(dline >= page_size){
3448 ctmp = pre_sel_taline(ta_screen->current);
3449 if(ctmp == NULL){
3450 i = 0;
3451 ta_screen->top_line = next_taline(ta_screen->top_line);
3453 else {
3454 for(; ctmp != ta_screen->current;
3455 ta_screen->current = pre_taline(ta_screen->current))
3456 dline--;
3461 return(TRUE);
3464 int ta_scroll_to_pos(line)
3465 long line;
3467 TA_S *ctmp;
3468 int dline;
3470 for(dline = 0, ctmp = first_taline(ta_screen->top_line);
3471 ctmp != ta_screen->top_line; ctmp = next_taline(ctmp))
3472 dline++;
3474 if (!ctmp)
3475 dline = 1;
3477 return(ta_scroll_up(line - dline));
3481 ta_scroll_callback (cmd, scroll_pos)
3482 int cmd;
3483 long scroll_pos;
3485 int paint = TRUE;
3486 long page_size = ps_global->ttyo->screen_rows - HEADER_ROWS(ps_global)
3487 - FOOTER_ROWS(ps_global);
3489 switch (cmd) {
3490 case MSWIN_KEY_SCROLLUPLINE:
3491 paint = ta_scroll_down (scroll_pos);
3492 break;
3494 case MSWIN_KEY_SCROLLDOWNLINE:
3495 paint = ta_scroll_up (scroll_pos);
3496 break;
3498 case MSWIN_KEY_SCROLLUPPAGE:
3499 paint = ta_scroll_down (page_size);
3500 break;
3502 case MSWIN_KEY_SCROLLDOWNPAGE:
3503 paint = ta_scroll_up (page_size);
3504 break;
3506 case MSWIN_KEY_SCROLLTO:
3507 paint = ta_scroll_to_pos (scroll_pos);
3508 break;
3511 if(paint)
3512 update_takeaddr_screen(ps_global, ta_screen->current, ta_screen, (Pos *)NULL);
3514 return(paint);
3517 #endif /* _WINDOWS */