#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: takeaddr.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2006-2008 University of Washington * Copyright 2013-2021 Eduardo Chappa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * ======================================================================== */ /*====================================================================== takeaddr.c Mostly support for Take Address command. ====*/ #include "headers.h" #include "takeaddr.h" #include "addrbook.h" #include "adrbkcmd.h" #include "status.h" #include "confscroll.h" #include "keymenu.h" #include "radio.h" #include "titlebar.h" #include "alpine.h" #include "help.h" #include "mailcmd.h" #include "mailpart.h" #include "roleconf.h" #include "../pith/state.h" #include "../pith/msgno.h" #include "../pith/adrbklib.h" #include "../pith/bldaddr.h" #include "../pith/bitmap.h" #include "../pith/util.h" #include "../pith/addrstring.h" #include "../pith/remote.h" #include "../pith/newmail.h" #include "../pith/list.h" #include "../pith/abdlc.h" #include "../pith/ablookup.h" #include "../pith/stream.h" #include "../pith/mailcmd.h" #include "../pith/busy.h" typedef struct takeaddress_screen { ScreenMode mode; TA_S *current, *top_line; } TA_SCREEN_S; static TA_SCREEN_S *ta_screen; static char *fakedomain = "@"; static ESCKEY_S save_or_export[] = { {'s', 's', "S", N_("Save")}, {'e', 'e', "E", N_("Export")}, {-1, 0, NULL, NULL}}; /* internal prototypes */ int edit_nickname(AdrBk *, AddrScrn_Disp *, int, char *, char *, HelpType, int, int); void add_abook_entry(TA_S *, char *, char *, char *, char *, int, TA_STATE_S **, char *); void take_to_addrbooks_frontend(char **, char *, char *, char *, char *, char *, int, TA_STATE_S **, char *); void take_to_addrbooks(char **, char *, char *, char *, char *, char *, int, TA_STATE_S **, char *); PerAddrBook *use_this_addrbook(int, char *); PerAddrBook *check_for_addrbook(char *); int takeaddr_screen(struct pine *, TA_S *, int, ScreenMode, TA_STATE_S **, char *); void takeaddr_bypass(struct pine *, TA_S *, TA_STATE_S **); int ta_do_take(TA_S *, int, int, TA_STATE_S **, char *); TA_S *whereis_taline(TA_S *); int ta_take_marked_addrs(int, TA_S *, int, TA_STATE_S **, char *); int ta_take_single_addr(TA_S *, int, TA_STATE_S **, char *); int update_takeaddr_screen(struct pine *, TA_S *, TA_SCREEN_S *, Pos *); void takeaddr_screen_redrawer_list(void); void takeaddr_screen_redrawer_single(void); int attached_addr_handler(TA_S *, int); int take_without_edit(TA_S *, int, int, TA_STATE_S **, char *); void export_vcard_att(struct pine *, int, long, ATTACH_S *); int take_export_tool(struct pine *, int, CONF_S **, unsigned); #ifdef _WINDOWS int ta_scroll_up(long); int ta_scroll_down(long); int ta_scroll_to_pos(long); int ta_scroll_callback(int, long); #endif /* * Edit a nickname field. * * Args: abook -- the addressbook handle * dl -- display list line (NULL if new entry) * command_line -- line to prompt on * orig -- nickname to edit * prompt -- prompt * this_help -- help * return_existing -- changes the behavior when a user types in a nickname * which already exists in this abook. If not set, it * will just keep looping until the user changes; if set, * it will return -8 to the caller and orig will be set * to the matching nickname. * * Returns: -10 to cancel * -9 no change * -7 only case of nickname changed (only happens if dl set) * -8 existing nickname chosen (only happens if return_existing set) * 0 new value copied into orig */ int edit_nickname(AdrBk *abook, AddrScrn_Disp *dl, int command_line, char *orig, char *prompt, HelpType this_help, int return_existing, int takeaddr) { char edit_buf[MAX_NICKNAME + 1]; HelpType help; int i, flags, lastrc, rc; AdrBk_Entry *check, *passed_in_ae; ESCKEY_S ekey[3]; SAVE_STATE_S state; /* For saving state of addrbooks temporarily */ char *error = NULL; ekey[i = 0].ch = ctrl('T'); ekey[i].rval = 2; ekey[i].name = "^T"; ekey[i++].label = N_("To AddrBk"); if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){ ekey[i].ch = ctrl('I'); ekey[i].rval = 11; ekey[i].name = "TAB"; ekey[i++].label = N_("Complete"); } ekey[i].ch = -1; strncpy(edit_buf, orig, sizeof(edit_buf)-1); edit_buf[sizeof(edit_buf)-1] = '\0'; if(dl) passed_in_ae = adrbk_get_ae(abook, (a_c_arg_t) dl->elnum); else passed_in_ae = (AdrBk_Entry *)NULL; help = NO_HELP; rc = 0; check = NULL; do{ if(error){ q_status_message(SM_ORDER, 3, 4, error); fs_give((void **)&error); } /* display a message because adrbk_lookup_by_nick returned positive */ if(check){ if(return_existing){ strncpy(orig, edit_buf, sizeof(edit_buf)-1); orig[sizeof(edit_buf)-1] = '\0'; if(passed_in_ae) (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum); return -8; } q_status_message1(SM_ORDER, 0, 4, _("Already an entry with nickname \"%s\""), edit_buf); } lastrc = rc; if(rc == 3) help = (help == NO_HELP ? this_help : NO_HELP); flags = OE_APPEND_CURRENT; rc = optionally_enter(edit_buf, command_line, 0, sizeof(edit_buf), prompt, ekey, help, &flags); if(rc == 1) /* ^C */ break; if(rc == 2){ /* ^T */ void (*redraw) (void) = ps_global->redrawer; char *returned_nickname; push_titlebar_state(); save_state(&state); if(takeaddr) returned_nickname = addr_book_takeaddr(); else returned_nickname = addr_book_selnick(); restore_state(&state); if(returned_nickname){ strncpy(edit_buf, returned_nickname, sizeof(edit_buf)-1); edit_buf[sizeof(edit_buf)-1] = '\0'; fs_give((void **)&returned_nickname); } ClearScreen(); pop_titlebar_state(); redraw_titlebar(); if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */ (*ps_global->redrawer)(); } else if(rc == 11){ /* TAB */ if(edit_buf[0]){ char *new_nickname = NULL; int ambiguity; ambiguity = abook_nickname_complete(edit_buf, &new_nickname, (lastrc==rc && !(flags & OE_USER_MODIFIED)), 0); if(new_nickname){ if(*new_nickname){ strncpy(edit_buf, new_nickname, sizeof(edit_buf)); edit_buf[sizeof(edit_buf)-1] = '\0'; } fs_give((void **) &new_nickname); } if(ambiguity != 2) Writechar(BELL, 0); } } }while(rc == 2 || rc == 3 || rc == 4 || rc == 11|| nickname_check(edit_buf, &error) || ((check = adrbk_lookup_by_nick(abook, edit_buf, (adrbk_cntr_t *)NULL)) && check != passed_in_ae)); if(rc != 0){ if(passed_in_ae) (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum); return -10; } /* only the case of nickname changed */ if(passed_in_ae && check == passed_in_ae && strcmp(edit_buf, orig)){ (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum); strncpy(orig, edit_buf, sizeof(edit_buf)-1); orig[sizeof(edit_buf)-1] = '\0'; return -7; } if(passed_in_ae) (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum); if(strcmp(edit_buf, orig) == 0) /* no change */ return -9; strncpy(orig, edit_buf, sizeof(edit_buf)-1); orig[sizeof(edit_buf)-1] = '\0'; return 0; } /* * Add an entry to address book. * It is for capturing addresses off incoming mail. * This is a front end for take_to_addrbooks. * It is also used for replacing an existing entry and for adding a single * new address to an existing list. * * The reason this is here is so that when Taking a single address, we can * rearrange the fullname to be Last, First instead of First Last. * * Args: ta_entry -- the entry from the take screen * command_line -- line to prompt on * * Result: item is added to one of the address books, * an error message is queued if appropriate. */ void add_abook_entry(TA_S *ta_entry, char *nick, char *fullname, char *fcc, char *comment, int command_line, TA_STATE_S **tas, char *cmd) { ADDRESS *addr; char new_fullname[6*MAX_FULLNAME + 1], new_address[6*MAX_ADDRESS + 1]; char **new_list; dprint((5, "-- add_abook_entry --\n")); /*-- rearrange full name (Last, First) ---*/ new_fullname[0] = '\0'; addr = ta_entry->addr; if(!fullname && addr->personal != NULL){ if(F_ON(F_DISABLE_TAKE_LASTFIRST, ps_global)){ strncpy(new_fullname, addr->personal, sizeof(new_fullname)-1); new_fullname[sizeof(new_fullname)-1] = '\0'; } else{ char old_fullname[6*MAX_FULLNAME + 1]; snprintf(old_fullname, sizeof(old_fullname), "%s", addr->personal); old_fullname[sizeof(old_fullname)-1] = '\0'; switch_to_last_comma_first(old_fullname, new_fullname, sizeof(new_fullname)); } } /* initial value for new address */ new_address[0] = '\0'; if(addr->mailbox && addr->mailbox[0]){ char *scratch, *p, *t, *u; size_t es; unsigned long l; RFC822BUFFER rbuf; es = est_size(addr); scratch = (char *) fs_get(es); scratch[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = scratch; rbuf.cur = scratch; rbuf.end = scratch+es-1; rfc822_output_address_list(&rbuf, addr, 0L, NULL); *rbuf.cur = '\0'; if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){ for(t = p; ; t--) if(*t == '&'){ /* find "leading" token */ *t++ = ' '; /* replace token */ *p = '\0'; /* tie off string */ u = (char *)rfc822_base64((unsigned char *)t, (unsigned long)strlen(t), &l); *p = '@'; /* restore 'p' */ rplstr(p, es-(p-scratch), 12, ""); /* clear special token */ rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */ if(u) fs_give((void **)&u); } else if(t == scratch) break; } strncpy(new_address, scratch, sizeof(new_address)-1); new_address[sizeof(new_address)-1] = '\0'; if(scratch) fs_give((void **)&scratch); } if(ta_entry->frwrded){ ADDRESS *a; int i, j; for(i = 0, a = addr; a; i++, a = a->next) ;/* just counting for alloc below */ /* catch special case where empty addr was set in vcard_to_ta */ if(i == 1 && !addr->host && !addr->mailbox && !addr->personal) i = 0; new_list = (char **) fs_get((i+1) * sizeof(char *)); for(j = 0, a = addr; i && a; j++, a = a->next){ ADDRESS *next_addr; char *bufp; size_t len; next_addr = a->next; a->next = NULL; len = est_size(a); bufp = (char *) fs_get(len * sizeof(char)); new_list[j] = cpystr(addr_string(a, bufp, len)); a->next = next_addr; fs_give((void **) &bufp); } new_list[j] = NULL; } else{ int i = 0, j; j = (ta_entry->strvalue && ta_entry->strvalue[0]) ? 2 : 1; new_list = (char **) fs_get(j * sizeof(char *)); if(j == 2) new_list[i++] = cpystr(ta_entry->strvalue); new_list[i] = NULL; } take_to_addrbooks_frontend(new_list, nick, fullname ? fullname : new_fullname, new_address, fcc, comment, command_line, tas, cmd); free_list_array(&new_list); } void take_to_addrbooks_frontend(char **new_entries, char *nick, char *fullname, char *addr, char *fcc, char *comment, int cmdline, TA_STATE_S **tas, char *cmd) { jmp_buf save_jmp_buf; int *save_nesting_level; dprint((5, "-- take_to_addrbooks_frontend --\n")); if(ps_global->remote_abook_validity > 0 && adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0)) ps_global->mangled_footer = 1; save_nesting_level = cpyint(ab_nesting_level); memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf)); if(setjmp(addrbook_changed_unexpectedly)){ q_status_message(SM_ORDER, 5, 10, _("Resetting address book...")); dprint((1, "RESETTING address book... take_to_addrbooks_frontend!\n")); addrbook_reset(); ab_nesting_level = *save_nesting_level; } ab_nesting_level++; take_to_addrbooks(new_entries, nick, fullname, addr, fcc, comment, cmdline, tas, cmd); memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf)); ab_nesting_level--; if(save_nesting_level) fs_give((void **)&save_nesting_level); } /* * Add to address book, called from take screen. * It is also used for adding to an existing list or replacing an existing * entry. * * Args: new_entries -- a list of addresses to add to a list or to form * a new list with * nick -- if adding new entry, suggest this for nickname * fullname -- if adding new entry, use this for fullname * addr -- if only one new_entry, this is its addr * fcc -- if adding new entry, use this for fcc * comment -- if adding new entry, use this for comment * command_line -- line to prompt on * * Result: item is added to one of the address books, * an error message is queued if appropriate. */ void take_to_addrbooks(char **new_entries, char *nick, char *fullname, char *addr, char *fcc, char *comment, int command_line, TA_STATE_S **tas, char *cmd) { char new_nickname[6*MAX_NICKNAME + 1], exist_nick[6*MAX_NICKNAME + 1]; char prompt[200], **p; int rc, listadd = 0, ans, i; AdrBk *abook; SAVE_STATE_S state; PerAddrBook *pab; AdrBk_Entry *abe = (AdrBk_Entry *)NULL, *abe_copy; adrbk_cntr_t entry_num = NO_NEXT; size_t tot_size, new_size, old_size; Tag old_tag; char *tmp_a_string; char *simple_a = NULL; ADDRESS *a = NULL; dprint((5, "-- take_to_addrbooks --\n")); if(tas && *tas) pab = (*tas)->pab; else pab = setup_for_addrbook_add(&state, command_line, cmd); /* check we got it opened ok */ if(pab == NULL || pab->address_book == NULL) goto take_to_addrbooks_cancel; adrbk_check_validity(pab->address_book, 1L); if(pab->address_book->flags & FILE_OUTOFDATE || (pab->address_book->rd && pab->address_book->rd->flags & REM_OUTOFDATE)){ q_status_message3(SM_ORDER, 0, 4, "Address book%s%s has changed: %stry again", (as.n_addrbk > 1 && pab->abnick) ? " " : "", (as.n_addrbk > 1 && pab->abnick) ? pab->abnick : "", (ps_global->remote_abook_validity == -1) ? "resynchronize and " : ""); if(tas && *tas){ restore_state(&((*tas)->state)); (*tas)->pab = NULL; } else restore_state(&state); return; } abook = pab->address_book; new_nickname[0] = '\0'; exist_nick[0] = '\0'; if(addr){ simple_a = NULL; a = NULL; /* rfc822_parse_adrlist feels free to destroy input so send copy */ tmp_a_string = cpystr(addr); rfc822_parse_adrlist(&a, tmp_a_string, fakedomain); if(tmp_a_string) fs_give((void **)&tmp_a_string); if(a){ simple_a = simple_addr_string(a, tmp_20k_buf, SIZEOF_20KBUF); mail_free_address(&a); } if(simple_a && *simple_a) abe = adrbk_lookup_by_addr(abook, simple_a, NULL); if(abe){ snprintf(prompt, sizeof(prompt), _("Warning: address exists with %s%s, continue "), (abe->nickname && abe->nickname[0]) ? "nickname " : (abe->fullname && abe->fullname[0]) ? "fullname " : "no nickname", (abe->nickname && abe->nickname[0]) ? abe->nickname : (abe->fullname && abe->fullname[0]) ? abe->fullname : ""); prompt[sizeof(prompt)-1] = '\0'; switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){ case 'y': if(abe->nickname && abe->nickname[0]){ strncpy(new_nickname, abe->nickname, sizeof(new_nickname)); new_nickname[sizeof(new_nickname)-1] = '\0'; strncpy(exist_nick, new_nickname, sizeof(exist_nick)); exist_nick[sizeof(exist_nick)-1] = '\0'; } break; default: goto take_to_addrbooks_cancel; } } } get_nick: abe = NULL; old_tag = NotSet; entry_num = NO_NEXT; /*----- nickname ------*/ snprintf(prompt, sizeof(prompt), _("Enter new or existing nickname (one word and easy to remember): ")); prompt[sizeof(prompt)-1] = '\0'; if(!new_nickname[0] && nick){ strncpy(new_nickname, nick, sizeof(new_nickname)); new_nickname[sizeof(new_nickname)-1] = '\0'; } rc = edit_nickname(abook, (AddrScrn_Disp *)NULL, command_line, new_nickname, prompt, h_oe_takenick, 1, 1); if(rc == -8){ /* this means an existing nickname was entered */ abe = adrbk_lookup_by_nick(abook, new_nickname, &entry_num); if(!abe){ /* this shouldn't happen */ q_status_message1(SM_ORDER, 0, 4, _("Already an entry %s in address book!"), new_nickname); goto take_to_addrbooks_cancel; } old_tag = abe->tag; if(abe->tag == Single && !strcmp(new_nickname, exist_nick)){ static ESCKEY_S choices[] = { {'r', 'r', "R", N_("Replace")}, {'n', 'n', "N", N_("No")}, {-1, 0, NULL, NULL}}; snprintf(prompt, sizeof(prompt), _("Entry %s (%s) exists, replace ? "), new_nickname, (abe->fullname && abe->fullname[0]) ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, abe->fullname) : ""); prompt[sizeof(prompt)-1] = '\0'; ans = radio_buttons(prompt, command_line, choices, 'r', 'x', h_oe_take_replace, RB_NORM); } else{ static ESCKEY_S choices[] = { {'r', 'r', "R", N_("Replace")}, {'a', 'a', "A", N_("Add")}, {'n', 'n', "N", N_("No")}, {-1, 0, NULL, NULL}}; snprintf(prompt, sizeof(prompt), _("%s %s (%s) exists, replace or add addresses to it ? "), abe->tag == List ? "List" : "Entry", new_nickname, (abe->fullname && abe->fullname[0]) ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, abe->fullname) : ""); prompt[sizeof(prompt)-1] = '\0'; ans = radio_buttons(prompt, command_line, choices, 'a', 'x', h_oe_take_replace_or_add, RB_NORM); } switch(ans){ case 'y': case 'r': break; case 'n': goto get_nick; break; case 'a': listadd++; break; default: goto take_to_addrbooks_cancel; } } else if(rc != 0 && rc != -9) /* -9 means a null nickname */ goto take_to_addrbooks_cancel; if((long)abook->count > MAX_ADRBK_SIZE || (old_tag == NotSet && (long)abook->count >= MAX_ADRBK_SIZE)){ q_status_message(SM_ORDER, 3, 5, _("Address book is at maximum size, cancelled.")); dprint((2, "Addrbook at Max size, TakeAddr cancelled\n")); goto take_to_addrbooks_cancel; } if(listadd){ /* count up size of existing list */ if(abe->tag == List){ for(p = abe->addr.list; p != NULL && *p != NULL; p++) ;/* do nothing */ old_size = p - abe->addr.list; } /* or size of existing single address */ else if(abe->addr.addr && abe->addr.addr[0]) old_size = 1; else old_size = 0; } else /* don't care about old size, they will be tossed in edit_entry */ old_size = 0; /* make up an abe to pass to edit_entry */ abe_copy = adrbk_newentry(); abe_copy->nickname = cpystr(new_nickname); abe_copy->tag = List; abe_copy->addr.list = NULL; if(listadd){ abe_copy->fullname = cpystr((abe->fullname && abe->fullname[0]) ? abe->fullname : ""); abe_copy->fcc = cpystr((abe->fcc && abe->fcc[0]) ? abe->fcc : ""); abe_copy->extra = cpystr((abe->extra&&abe->extra[0]) ? abe->extra : ""); } else{ /* * use passed in info if available */ abe_copy->fullname = cpystr((fullname && fullname[0]) ? fullname : (abe && abe->fullname) ? abe->fullname : ""); abe_copy->fcc = cpystr((fcc && fcc[0]) ? fcc : (abe && abe->fcc) ? abe->fcc : ""); abe_copy->extra = cpystr((comment && comment[0]) ? comment : (abe && abe->extra) ? abe->extra : ""); } /* get rid of duplicates */ if(listadd){ if(abe->tag == List){ int elim_dup; char **q, **r; ADDRESS *newadr, *oldadr; for(q = new_entries; q != NULL && *q != NULL;){ tmp_a_string = cpystr(*q); newadr = NULL; rfc822_parse_adrlist(&newadr, tmp_a_string, fakedomain); fs_give((void **) &tmp_a_string); elim_dup = (newadr == NULL); for(p = abe->addr.list; !elim_dup && p != NULL && *p != NULL; p++){ tmp_a_string = cpystr(*p); oldadr = NULL; rfc822_parse_adrlist(&oldadr, tmp_a_string, fakedomain); fs_give((void **) &tmp_a_string); if(address_is_same(newadr, oldadr)) elim_dup++; if(oldadr) mail_free_address(&oldadr); } /* slide the addresses down one to eliminate newadr */ if(elim_dup){ char *f; f = *q; for(r = q; r != NULL && *r != NULL; r++) *r = *(r+1); if(f) fs_give((void **) &f); } else q++; if(newadr) mail_free_address(&newadr); } } else{ char **q, **r; ADDRESS *newadr, *oldadr; tmp_a_string = cpystr(abe->addr.addr ? abe->addr.addr : ""); oldadr = NULL; rfc822_parse_adrlist(&oldadr, tmp_a_string, fakedomain); fs_give((void **) &tmp_a_string); for(q = new_entries; q != NULL && *q != NULL;){ tmp_a_string = cpystr(*q); newadr = NULL; rfc822_parse_adrlist(&newadr, tmp_a_string, fakedomain); fs_give((void **) &tmp_a_string); /* slide the addresses down one to eliminate newadr */ if(address_is_same(newadr, oldadr)){ char *f; f = *q; for(r = q; r != NULL && *r != NULL; r++) *r = *(r+1); if(f) fs_give((void **) &f); } else q++; if(newadr) mail_free_address(&newadr); } if(oldadr) mail_free_address(&oldadr); } if(!new_entries || !*new_entries){ q_status_message1(SM_ORDER, 0, 4, _("All of the addresses are already included in \"%s\""), new_nickname); free_ae(&abe_copy); if(tas && *tas){ restore_state(&((*tas)->state)); (*tas)->pab = NULL; } else restore_state(&state); return; } } /* count up size of new list */ for(p = new_entries; p != NULL && *p != NULL; p++) ;/* do nothing */ new_size = p - new_entries; tot_size = old_size + new_size; abe_copy->addr.list = (char **) fs_get((tot_size+1) * sizeof(char *)); memset((void *) abe_copy->addr.list, 0, (tot_size+1) * sizeof(char *)); if(old_size > 0){ if(abe->tag == List){ for(i = 0; i < old_size; i++) abe_copy->addr.list[i] = cpystr(abe->addr.list[i]); } else abe_copy->addr.list[0] = cpystr(abe->addr.addr); } /* add new addresses to list */ if(tot_size == 1 && addr) abe_copy->addr.list[0] = cpystr(addr); else for(i = 0; i < new_size; i++) abe_copy->addr.list[old_size + i] = cpystr(new_entries[i]); abe_copy->addr.list[tot_size] = NULL; if(F_ON(F_DISABLE_TAKE_FULLNAMES, ps_global)){ for(i = 0; abe_copy->addr.list[i]; i++){ simple_a = NULL; a = NULL; tmp_a_string = cpystr(abe_copy->addr.list[i]); rfc822_parse_adrlist(&a, tmp_a_string, fakedomain); if(tmp_a_string) fs_give((void **) &tmp_a_string); if(a){ simple_a = simple_addr_string(a, tmp_20k_buf, SIZEOF_20KBUF); mail_free_address(&a); } /* replace the old addr string with one with no full name */ if(simple_a && *simple_a){ if(abe_copy->addr.list[i]) fs_give((void **) &abe_copy->addr.list[i]); abe_copy->addr.list[i] = cpystr(simple_a); } } } edit_entry(abook, abe_copy, (a_c_arg_t) entry_num, old_tag, 0, NULL, cmd); /* free copy */ free_ae(&abe_copy); if(tas && *tas){ restore_state(&((*tas)->state)); (*tas)->pab = NULL; } else restore_state(&state); return; take_to_addrbooks_cancel: q_status_message(SM_INFO, 0, 2, _("Address book addition cancelled")); if(tas && *tas){ restore_state(&((*tas)->state)); (*tas)->pab = NULL; } else restore_state(&state); } /* * Prep addrbook for TakeAddr add operation. * * Arg: savep -- Address of a pointer to save addrbook state in. * stp -- Address of a pointer to save addrbook state in. * * Returns: a PerAddrBook pointer, or NULL. */ PerAddrBook * setup_for_addrbook_add(SAVE_STATE_S *state, int command_line, char *cmd) { PerAddrBook *pab; int save_rem_abook_valid = 0; init_ab_if_needed(); save_state(state); if(as.n_addrbk == 0){ q_status_message(SM_ORDER, 3, 4, _("No address book configured!")); return NULL; } else pab = use_this_addrbook(command_line, cmd); if(!pab) return NULL; if((pab->type & REMOTE_VIA_IMAP) && ps_global->remote_abook_validity == -1){ save_rem_abook_valid = -1; ps_global->remote_abook_validity = 0; } /* initialize addrbook so we can add to it */ init_abook(pab, Open); if(save_rem_abook_valid) ps_global->remote_abook_validity = save_rem_abook_valid; if(pab->ostatus != Open){ q_status_message(SM_ORDER, 3, 4, _("Can't open address book!")); return NULL; } if(pab->access != ReadWrite){ if(pab->access == ReadOnly) q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only")); else if(pab->access == NoAccess) q_status_message(SM_ORDER, 3, 4, _("AddressBook not accessible, permission denied")); return NULL; } return(pab); } /* * Interact with user to figure out which address book they want to add a * new entry (TakeAddr) to. * * Args: command_line -- just the line to prompt on * * Results: returns a pab pointing to the selected addrbook, or NULL. */ PerAddrBook * use_this_addrbook(int command_line, char *cmd) { HelpType help; int rc = 0; PerAddrBook *pab, *the_only_pab; #define MAX_ABOOK 2000 int i, abook_num, count_read_write; char addrbook[MAX_ABOOK + 1], prompt[MAX_ABOOK + 81]; static ESCKEY_S ekey[] = { {-2, 0, NULL, NULL}, {ctrl('P'), 10, "^P", N_("Prev AddrBook")}, {ctrl('N'), 11, "^N", N_("Next AddrBook")}, {KEY_UP, 10, "", ""}, {KEY_DOWN, 11, "", ""}, {-1, 0, NULL, NULL}}; dprint((9, "- use_this_addrbook -\n")); /* check for only one ReadWrite addrbook */ count_read_write = 0; for(i = 0; i < as.n_addrbk; i++){ pab = &as.adrbks[i]; /* * NoExists is counted, too, so the user can add to an empty * addrbook the first time. */ if(pab->access == ReadWrite || pab->access == NoExists || pab->access == MaybeRorW){ count_read_write++; the_only_pab = &as.adrbks[i]; } } /* only one usable addrbook, use it */ if(count_read_write == 1) return(the_only_pab); /* no addrbook to write to */ if(count_read_write == 0){ q_status_message2(SM_ORDER | SM_DING, 3, 4, "No %sAddressbook to %s to!", (as.n_addrbk > 0) ? "writable " : "", cmd); return NULL; } /* start with the first addrbook */ abook_num = 0; pab = &as.adrbks[abook_num]; strncpy(addrbook, pab->abnick, sizeof(addrbook)-1); addrbook[sizeof(addrbook)-1] = '\0'; snprintf(prompt, sizeof(prompt), "%c%s to which addrbook : %s", islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd) : *cmd, cmd+1, (pab->access == ReadOnly || pab->access == NoAccess) ? "[ReadOnly] " : ""); prompt[sizeof(prompt)-1] = '\0'; help = NO_HELP; ps_global->mangled_footer = 1; do{ int flags; if(!pab) q_status_message1(SM_ORDER, 3, 4, _("No addressbook \"%s\""), addrbook); if(rc == 3) help = (help == NO_HELP ? h_oe_chooseabook : NO_HELP); flags = OE_APPEND_CURRENT; rc = optionally_enter(addrbook, command_line, 0, sizeof(addrbook), prompt, ekey, help, &flags); if(rc == 1){ /* ^C */ char capcmd[50]; snprintf(capcmd, sizeof(capcmd), "%c%s", islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd) : *cmd, cmd+1); capcmd[sizeof(capcmd)-1] = '\0'; cmd_cancelled(capcmd); break; } if(rc == 10){ /* Previous addrbook */ if(--abook_num < 0) abook_num = as.n_addrbk - 1; pab = &as.adrbks[abook_num]; strncpy(addrbook, pab->abnick, sizeof(addrbook)-1); addrbook[sizeof(addrbook)-1] = '\0'; snprintf(prompt, sizeof(prompt), "%s to which addrbook : %s", cmd, (pab->access == ReadOnly || pab->access == NoAccess) ? "[ReadOnly] " : ""); prompt[sizeof(prompt)-1] = '\0'; } else if(rc == 11){ /* Next addrbook */ if(++abook_num > as.n_addrbk - 1) abook_num = 0; pab = &as.adrbks[abook_num]; strncpy(addrbook, pab->abnick, sizeof(addrbook)-1); addrbook[sizeof(addrbook)-1] = '\0'; snprintf(prompt, sizeof(prompt), "%s to which addrbook : %s", cmd, (pab->access == ReadOnly || pab->access == NoAccess) ? "[ReadOnly] " : ""); prompt[sizeof(prompt)-1] = '\0'; } }while(rc == 2 || rc == 3 || rc == 4 || rc == 10 || rc == 11 || rc == 12 || !(pab = check_for_addrbook(addrbook))); ps_global->mangled_footer = 1; if(rc != 0) return NULL; return(pab); } /* * Return a pab pointer to the addrbook which corresponds to the argument. * * Args: addrbook -- the string representing the addrbook. * * Results: returns a PerAddrBook pointer for the referenced addrbook, NULL * if none. First the nicknames are checked and then the filenames. * This must be one of the existing addrbooks. */ PerAddrBook * check_for_addrbook(char *addrbook) { register int i; register PerAddrBook *pab; for(i = 0; i < as.n_addrbk; i++){ pab = &as.adrbks[i]; if(strcmp(pab->abnick, addrbook) == 0) break; } if(i < as.n_addrbk) return(pab); for(i = 0; i < as.n_addrbk; i++){ pab = &as.adrbks[i]; if(strcmp(pab->filename, addrbook) == 0) break; } if(i < as.n_addrbk) return(pab); return NULL; } /* * Screen for selecting which addresses to Take to address book. * * Args: ps -- Pine state * ta_list -- Screen is formed from this list of addresses * how_many_selected -- how many checked initially in ListMode * mode -- which mode to start in * * Result: an address book may be updated * Returns -- 0 normally * 1 if it returns before redrawing screen */ int takeaddr_screen(struct pine *ps, TA_S *ta_list, int how_many_selected, ScreenMode mode, TA_STATE_S **tas, char *command) { UCS ch = 'x'; int cmd, dline, give_warn_message, command_line; int km_popped = 0, directly_to_take = 0, ret = 0, done = 0; TA_S *current = NULL, *ctmp = NULL; TA_SCREEN_S screen; Pos cursor_pos; char *utf8str; struct key_menu *km; dprint((2, "- takeaddr_screen -\n")); command_line = -FOOTER_ROWS(ps); /* third line from the bottom */ screen.current = screen.top_line = NULL; screen.mode = mode; if(ta_list == NULL){ /* TRANSLATORS: something like No addresses to save, cancelled */ q_status_message1(SM_INFO, 0, 2, "No addresses to %s, cancelled", command); return 1; } current = first_sel_taline(ta_list); ps->mangled_screen = 1; ta_screen = &screen; if(is_talist_of_one(current)){ directly_to_take++; screen.mode = SingleMode; } else if(screen.mode == ListMode) q_status_message(SM_INFO, 0, 1, _("List mode: Use \"X\" to mark addresses to be included in list")); else q_status_message(SM_INFO, 0, 1, _("Single mode: Use \"P\" or \"N\" to select desired address")); while(!done){ if(km_popped){ km_popped--; if(km_popped == 0){ clearfooter(ps); ps->mangled_body = 1; } } if(screen.mode == ListMode) ps->redrawer = takeaddr_screen_redrawer_list; else ps->redrawer = takeaddr_screen_redrawer_single; if(ps->mangled_screen){ ps->mangled_header = 1; ps->mangled_footer = 1; ps->mangled_body = 1; ps->mangled_screen = 0; } /*----------- Check for new mail -----------*/ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0) ps->mangled_header = 1; #ifdef _WINDOWS mswin_beginupdate(); #endif if(ps->mangled_header){ char tbuf[40]; snprintf(tbuf, sizeof(tbuf), "TAKE ADDRESS SCREEN (%s Mode)", (screen.mode == ListMode) ? "List" : "Single"); tbuf[sizeof(tbuf)-1] = '\0'; set_titlebar(tbuf, ps->mail_stream, ps->context_current, ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL); ps->mangled_header = 0; } dline = update_takeaddr_screen(ps, current, &screen, &cursor_pos); if(F_OFF(F_SHOW_CURSOR, ps)){ cursor_pos.row = ps->ttyo->screen_rows - FOOTER_ROWS(ps); cursor_pos.col = 0; } /*---- This displays new mail notification, or errors ---*/ if(km_popped){ FOOTER_ROWS(ps_global) = 3; mark_status_unknown(); } display_message(ch); if(km_popped){ FOOTER_ROWS(ps_global) = 1; mark_status_unknown(); } /*---- Redraw footer ----*/ if(ps->mangled_footer){ bitmap_t bitmap; if(km_popped){ FOOTER_ROWS(ps) = 3; clearfooter(ps); } setbitmap(bitmap); ps->mangled_footer = 0; km = (screen.mode == ListMode) ? &ta_keymenu_lm : &ta_keymenu_sm; menu_clear_binding(km, KEY_LEFT); menu_clear_binding(km, KEY_RIGHT); if(F_ON(F_ARROW_NAV, ps_global)){ int cmd; if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){ menu_add_binding(km, '<', cmd); menu_add_binding(km, KEY_LEFT, cmd); } if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){ menu_add_binding(km, '>', cmd); menu_add_binding(km, KEY_RIGHT, cmd); } } draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1 - FOOTER_ROWS(ps_global), 0, FirstMenu); if(km_popped){ FOOTER_ROWS(ps) = 1; mark_keymenu_dirty(); } } #ifdef _WINDOWS mswin_endupdate(); #endif /*------ Read the command from the keyboard ----*/ MoveCursor(cursor_pos.row, cursor_pos.col); if(directly_to_take){ /* bypass this screen */ cmd = MC_TAKE; blank_keymenu(ps_global->ttyo->screen_rows - 2, 0); } else { #ifdef MOUSE mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0, ps_global->ttyo->screen_rows - (FOOTER_ROWS(ps)+1), ps_global->ttyo->screen_cols); #endif #ifdef _WINDOWS mswin_setscrollcallback(ta_scroll_callback); #endif ch = READ_COMMAND(&utf8str); #ifdef MOUSE clear_mfunc(mouse_in_content); #endif #ifdef _WINDOWS mswin_setscrollcallback(NULL); #endif cmd = menu_command(ch, km); if (ta_screen->current) current = ta_screen->current; if(km_popped) switch(cmd){ case MC_NONE : case MC_OTHER : case MC_RESIZE : case MC_REPAINT : km_popped++; break; default: clearfooter(ps); break; } } switch(cmd){ case MC_HELP : /* help! */ if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){ km_popped = 2; ps_global->mangled_footer = 1; break; } helper(h_takeaddr_screen, _("HELP FOR TAKE ADDRESS SCREEN"), HLPD_SIMPLE); ps->mangled_screen = 1; break; case MC_EXIT: /* exit takeaddr screen */ q_status_message(SM_INFO, 0, 2, _("Address book addition cancelled")); ret = 1; done++; break; case MC_TAKE: if(ta_do_take(current, how_many_selected, command_line, tas, command)) done++; else directly_to_take = 0; break; case MC_CHARDOWN : /* next list element */ if((ctmp = next_sel_taline(current)) != NULL) current = ctmp; else q_status_message(SM_INFO, 0, 1, _("Already on last line.")); break; case MC_CHARUP: /* previous list element */ if((ctmp = pre_sel_taline(current)) != NULL) current = ctmp; else q_status_message(SM_INFO, 0, 1, _("Already on first line.")); break; case MC_PAGEDN : /* page forward */ give_warn_message = 1; while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){ if((ctmp = next_sel_taline(current)) != NULL){ current = ctmp; give_warn_message = 0; } else break; } if(give_warn_message) q_status_message(SM_INFO, 0, 1, _("Already on last page.")); break; case MC_PAGEUP : /* page backward */ /* move to top of screen */ give_warn_message = 1; while(dline-- > HEADER_ROWS(ps_global)){ if((ctmp = pre_sel_taline(current)) != NULL){ current = ctmp; give_warn_message = 0; } else break; } /* page back one screenful */ while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){ if((ctmp = pre_sel_taline(current)) != NULL){ current = ctmp; give_warn_message = 0; } else break; } if(give_warn_message) q_status_message(SM_INFO, 0, 1, _("Already on first page.")); break; case MC_WHEREIS : /* whereis */ if((ctmp = whereis_taline(current)) != NULL) current = ctmp; ps->mangled_footer = 1; break; case KEY_SCRLTO: /* no op for now */ break; #ifdef MOUSE case MC_MOUSE: { MOUSEPRESS mp; mouse_get_last(NULL, &mp); mp.row -= HEADER_ROWS(ps_global); ctmp = screen.top_line; if(mp.doubleclick){ if(screen.mode == SingleMode){ if(ta_do_take(current, how_many_selected, command_line, tas, command)) done++; else directly_to_take = 0; } else{ current->checked = !current->checked; /* flip it */ how_many_selected += (current->checked ? 1 : -1); } } else{ while(mp.row && ctmp != NULL){ --mp.row; do ctmp = ctmp->next; while(ctmp != NULL && ctmp->skip_it && !ctmp->print); } if(ctmp != NULL && !ctmp->skip_it) current = ctmp; } } break; #endif case MC_REPAINT : case MC_RESIZE : ClearScreen(); ps->mangled_screen = 1; break; case MC_CHOICE : /* [UN]select this addr */ current->checked = !current->checked; /* flip it */ how_many_selected += (current->checked ? 1 : -1); break; case MC_SELALL : /* select all */ how_many_selected = ta_mark_all(first_sel_taline(current)); ps->mangled_body = 1; break; case MC_UNSELALL: /* unselect all */ how_many_selected = ta_unmark_all(first_sel_taline(current)); ps->mangled_body = 1; break; case MC_LISTMODE: /* switch to SingleMode */ if(screen.mode == ListMode){ screen.mode = SingleMode; q_status_message(SM_INFO, 0, 1, _("Single mode: Use \"P\" or \"N\" to select desired address")); } else{ screen.mode = ListMode; q_status_message(SM_INFO, 0, 1, _("List mode: Use \"X\" to mark addresses to be included in list")); if(how_many_selected <= 1){ how_many_selected = ta_unmark_all(first_sel_taline(current)); current->checked = 1; how_many_selected++; } } ps->mangled_screen = 1; break; case MC_NONE : /* simple timeout */ break; /* Unbound (or not dealt with) keystroke */ case MC_CHARRIGHT : case MC_CHARLEFT : case MC_GOTOBOL : case MC_GOTOEOL : case MC_UNKNOWN : default: bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?"); break; case MC_UTF8: bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?"); break; } } ps->mangled_screen = 1; return(ret); } /* * Do what takeaddr_screen does except bypass the takeaddr_screen and * go directly to do_take. */ void takeaddr_bypass(struct pine *ps, TA_S *current, TA_STATE_S **tasp) { TA_SCREEN_S screen; /* We have to fake out ta_do_take because */ /* we're bypassing takeaddr_screen. */ ta_screen = &screen; ta_screen->mode = SingleMode; current = first_sel_taline(current); (void) ta_do_take(current, 1, -FOOTER_ROWS(ps_global), tasp, _("save")); ps->mangled_screen = 1; } /* * */ int ta_do_take(TA_S *current, int how_many_selected, int command_line, TA_STATE_S **tas, char *cmd) { return((ta_screen->mode == ListMode) ? ta_take_marked_addrs(how_many_selected, first_sel_taline(current), command_line, tas, cmd) : ta_take_single_addr(current, command_line, tas, cmd)); } /* * WhereIs for TakeAddr screen. * * Returns the line match is found in or NULL. */ TA_S * whereis_taline(TA_S *current) { TA_S *p; int rc, found = 0, wrapped = 0, flags; char *result = NULL, buf[MAX_SEARCH+1], tmp[MAX_SEARCH+20]; static char last[MAX_SEARCH+1]; HelpType help; static ESCKEY_S ekey[] = { {0, 0, "", ""}, {ctrl('Y'), 10, "^Y", N_("Top")}, {ctrl('V'), 11, "^V", N_("Bottom")}, {-1, 0, NULL, NULL}}; if(!current) return NULL; /*--- get string ---*/ buf[0] = '\0'; snprintf(tmp, sizeof(tmp), _("Word to find %s%.*s%s: "), (last[0]) ? "[" : "", MAX_SEARCH, (last[0]) ? last : "", /* MAX_SEARCH == sizeof(tmp) - 20 */ (last[0]) ? "]" : ""); tmp[sizeof(tmp)-1] = '\0'; help = NO_HELP; flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE; while(1){ rc = optionally_enter(buf,-FOOTER_ROWS(ps_global),0,sizeof(buf), tmp,ekey,help,&flags); if(rc == 3) help = help == NO_HELP ? h_config_whereis : NO_HELP; else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){ if(rc == 0 && !buf[0] && last[0]){ strncpy(buf, last, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; } break; } } if(rc == 0 && buf[0]){ p = current; while((p = next_taline(p)) != NULL) if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, p->strvalue), buf)){ found++; break; } if(!found){ p = first_taline(current); while(p != current) if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, p->strvalue), buf)){ found++; wrapped++; break; } else p = next_taline(p); } } else if(rc == 10){ current = first_sel_taline(current); result = _("Searched to top"); } else if(rc == 11){ current = last_sel_taline(current); result = _("Searched to bottom"); } else{ current = NULL; result = _("WhereIs cancelled"); } if(found){ current = p; result = wrapped ? _("Search wrapped to beginning") : _("Word found"); strncpy(last, buf, sizeof(last)-1); last[sizeof(last)-1] = '\0'; } q_status_message(SM_ORDER,0,3,result ? result : _("Word not found")); return(current); } /* * Call the addrbook functions which add the checked addresses. * * Args: how_many_selected -- how many addresses are checked * f_line -- the first ta line * * Returns: 1 -- we're done, caller should return * 0 -- we're not done */ int ta_take_marked_addrs(int how_many_selected, TA_S *f_line, int command_line, TA_STATE_S **tas, char *cmd) { char **new_list; TA_S *p; if(how_many_selected == 0){ q_status_message(SM_ORDER, 0, 4, _("No addresses marked for taking. Use ExitTake to leave TakeAddr screen")); return 0; } if(how_many_selected == 1){ for(p = f_line; p; p = next_sel_taline(p)) if(p->checked && !p->skip_it) break; if(p) add_abook_entry(p, (p->nickname && p->nickname[0]) ? p->nickname : NULL, (p->fullname && p->fullname[0]) ? p->fullname : NULL, (p->fcc && p->fcc[0]) ? p->fcc : NULL, (p->comment && p->comment[0]) ? p->comment : NULL, command_line, tas, cmd); } else{ new_list = list_of_checked(f_line); for(p = f_line; p; p = next_sel_taline(p)) if(p->checked && !p->skip_it) break; take_to_addrbooks_frontend(new_list, p ? p->nickname : NULL, p ? p->fullname : NULL, NULL, p ? p->fcc : NULL, p ? p->comment : NULL, command_line, tas, cmd); free_list_array(&new_list); } return 1; } int ta_take_single_addr(TA_S *cur, int command_line, TA_STATE_S **tas, char *cmd) { add_abook_entry(cur, (cur->nickname && cur->nickname[0]) ? cur->nickname : NULL, (cur->fullname && cur->fullname[0]) ? cur->fullname : NULL, (cur->fcc && cur->fcc[0]) ? cur->fcc : NULL, (cur->comment && cur->comment[0]) ? cur->comment : NULL, command_line, tas, cmd); return 1; } /* * Manage display of the Take Address screen. * * Args: ps -- pine state * current -- the current TA line * screen -- the TA screen * cursor_pos -- return good cursor position here */ int update_takeaddr_screen(struct pine *ps, TA_S *current, TA_SCREEN_S *screen, Pos *cursor_pos) { int dline; TA_S *top_line, *ctmp; int longest, i, j; char buf1[6*MAX_SCREEN_COLS + 30]; char buf2[6*MAX_SCREEN_COLS + 30]; char *p, *q; int screen_width = ps->ttyo->screen_cols; Pos cpos; cpos.row = HEADER_ROWS(ps); /* default return value */ /* calculate top line of display */ dline = 0; top_line = 0; if (ta_screen->top_line){ for(dline = 0, ctmp = ta_screen->top_line; ctmp && ctmp != current; ctmp = next_taline(ctmp)) dline++; if (ctmp && (dline < ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps))) top_line = ta_screen->top_line; } if (!top_line){ dline = 0; ctmp = top_line = first_taline(current); do if(((dline++) % (ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps))) == 0) top_line = ctmp; while(ctmp != current && (ctmp = next_taline(ctmp))); } #ifdef _WINDOWS /* * Figure out how far down the top line is from the top and how many * total lines there are. Dumb to loop every time thru, but * there aren't that many lines, and it's cheaper than rewriting things * to maintain a line count in each structure... */ for(dline = 0, ctmp = pre_taline(top_line); ctmp; ctmp = pre_taline(ctmp)) dline++; scroll_setpos(dline); for(ctmp = next_taline(top_line); ctmp ; ctmp = next_taline(ctmp)) dline++; scroll_setrange(ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps), dline); #endif /* mangled body or new page, force redraw */ if(ps->mangled_body || screen->top_line != top_line) screen->current = NULL; /* find width of longest line for nicer formatting */ longest = 0; for(ctmp = first_taline(top_line); ctmp; ctmp = next_taline(ctmp)){ int width; if(ctmp && !ctmp->print && ctmp->strvalue && longest < (width = utf8_width((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, ctmp->strvalue)))) longest = width; } #define LENGTH_OF_THAT_STRING 5 /* "[X] " */ longest = MIN(longest, ps->ttyo->screen_cols); /* loop thru painting what's needed */ for(dline = 0, ctmp = top_line; dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps); dline++, ctmp = next_taline(ctmp)){ /* * only fall thru painting if something needs painting... */ if(!ctmp || !screen->current || ctmp == screen->current || ctmp == top_line || ctmp == current){ ClearLine(dline + HEADER_ROWS(ps)); if(!ctmp || !ctmp->strvalue) continue; } p = buf1; if(ctmp == current){ cpos.row = dline + HEADER_ROWS(ps); /* col set below */ StartInverse(); } if(ctmp->print) j = 0; else j = LENGTH_OF_THAT_STRING; /* * Copy the value to a temp buffer expanding tabs, and * making sure not to write beyond screen right... */ q = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, ctmp->strvalue); for(i = 0; q[i] && j < ps->ttyo->screen_cols && p-buf1 < sizeof(buf1); i++){ if(q[i] == ctrl('I')){ do *p++ = SPACE; while(j < ps->ttyo->screen_cols && ((++j)&0x07) && p-buf1 < sizeof(buf1)); } else{ *p++ = q[i]; j++; } } if(p-buf1 < sizeof(buf1)) *p = '\0'; if(utf8_width(buf1) < longest){ (void) utf8_pad_to_width(buf2, buf1, sizeof(buf2), longest, 1); /* it's expected to be in buf1 below */ strncpy(buf1, buf2, sizeof(buf1)); buf1[sizeof(buf1)-1] = '\0'; } /* mark lines which have check marks */ if(ctmp == current){ if(screen->mode == ListMode){ snprintf(buf2, sizeof(buf2), "[%c] %s", ctmp->checked ? 'X' : SPACE, buf1); buf2[sizeof(buf2)-1] = '\0'; cpos.col = 1; /* position on the X */ } else{ snprintf(buf2, sizeof(buf2), " %s", buf1); buf2[sizeof(buf2)-1] = '\0'; cpos.col = 5; /* 5 spaces before text */ } } else{ if(ctmp->print){ int width, actual_width; size_t len; /* * In buf2 make ------string-------- * which reaches all the way across the screen. String will * already have a leading and trailing space. */ width = utf8_width(buf1); if(width > screen_width){ actual_width = utf8_truncate(buf1, screen_width); /* it might be 1 less */ if(actual_width < screen_width && (len=strlen(buf1))+1 < sizeof(buf1)){ buf1[len] = SPACE; buf1[len+1] = '\0'; } } else{ snprintf(buf2, sizeof(buf2), "%s%s", repeat_char((screen_width-width)/2, '-'), buf1); buf2[sizeof(buf2)-1] = '\0'; len = strlen(buf2); width = utf8_width(buf2); snprintf(buf2+len, sizeof(buf2)-len, "%s", repeat_char(screen_width-width, '-')); buf2[sizeof(buf2)-1] = '\0'; } } else{ if(screen->mode == ListMode) /* 6*MAX_SCREEN_COLS + 24 = sizeof(buf2)-6 */ snprintf(buf2, sizeof(buf2), "[%c] %.*s", ctmp->checked ? 'X' : SPACE, 6*MAX_SCREEN_COLS + 24, buf1); else snprintf(buf2, sizeof(buf2), " %.*s", 6*MAX_SCREEN_COLS+24, buf1); buf2[sizeof(buf2)-1] = '\0'; } } PutLine0(dline + HEADER_ROWS(ps), 0, buf2); if(ctmp == current) EndInverse(); } ps->mangled_body = 0; screen->top_line = top_line; screen->current = current; if(cursor_pos) *cursor_pos = cpos; return(cpos.row); } void takeaddr_screen_redrawer_list(void) { ps_global->mangled_body = 1; (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen, (Pos *)NULL); } void takeaddr_screen_redrawer_single(void) { ps_global->mangled_body = 1; (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen, (Pos *)NULL); } /* jpf work in progress * Execute command to take addresses out of message and put in the address book * * Args: ps -- pine state * msgmap -- the MessageMap * agg -- this is aggregate operation if set * * Result: The entry is added to an address book. */ int cmd_take_addr(struct pine *ps, MSGNO_S *msgmap, int agg) { TA_S *ta_list = NULL; int how_many_selected = 0, rtype; /* Ask user what kind of Take they want to do */ if(!agg && F_ON(F_ENABLE_ROLE_TAKE, ps)){ rtype = rule_setup_type(ps, RS_RULES | ((mn_get_total(msgmap) > 0) ? (F_ON(F_ENABLE_TAKE_EXPORT, ps) ? (RS_INCADDR | RS_INCEXP) : RS_INCADDR) : RS_NONE), "Take to : "); } else if(F_ON(F_ENABLE_TAKE_EXPORT, ps) && mn_get_total(msgmap) > 0) rtype = rule_setup_type(ps, RS_INCADDR | RS_INCEXP, "Take to : "); else rtype = 'a'; if(rtype == 'x' || rtype == 'Z'){ if(rtype == 'x') cmd_cancelled(NULL); else if(rtype == 'Z') q_status_message(SM_ORDER | SM_DING, 3, 5, "Try turning on color with the Setup/Kolor command."); return -1; } ps->mangled_footer = 1; if(rtype > 0) switch(rtype){ case 'e': { LINES_TO_TAKE *lines_to_take = NULL; rtype = set_up_takeaddr('e', ps, msgmap, &ta_list, &how_many_selected, agg ? TA_AGG : 0, NULL); if(rtype >= 0){ if(convert_ta_to_lines(ta_list, &lines_to_take)){ while(lines_to_take && lines_to_take->prev) lines_to_take = lines_to_take->prev; take_to_export(ps, lines_to_take); free_ltlines(&lines_to_take); } else q_status_message(SM_ORDER, 3, 4, _("Can't find anything to export")); } } break; case 'r': case 's': case 'i': case 'f': case 'o': case 'c': case 'x': role_take(ps, msgmap, rtype); break; case 'a': rtype = set_up_takeaddr('a', ps, msgmap, &ta_list, &how_many_selected, agg ? TA_AGG : 0, attached_addr_handler); if(rtype >= 0){ (void) takeaddr_screen(ps, ta_list, how_many_selected, agg ? ListMode : SingleMode, NULL, _("take")); } break; default: break; } /* clean up */ free_talines(&ta_list); env_for_pico_callback = NULL; body_for_pico_callback = NULL; return(rtype >= 0 ? 1 : 0); } int attached_addr_handler(TA_S *current, int added) { char prompt[200]; int command_line = -FOOTER_ROWS(ps_global); snprintf(prompt, sizeof(prompt), "Take %d entries from attachment to addrbook all at once ", added); switch(want_to(prompt, 'n', 'x', NO_HELP, WT_NORM)){ case 'y': if(take_without_edit(current, added, command_line, NULL, "take") >= 0) return(0); /* all taken care of */ else return(-1); /* problem */ case 'x': cmd_cancelled("Take"); return(-1); /* problem */ default: return(1); /* proceed */ } } int take_without_edit(TA_S *ta_list, int num_in_list, int command_line, TA_STATE_S **tas, char *cmd) { #define OURTMPBUFLEN 200 PerAddrBook *pab_dst; SAVE_STATE_S state; /* For saving state of addrbooks temporarily */ int rc, total_to_copy; int how_many_dups = 0, how_many_to_copy = 0, skip_dups = 0; int ret = 0; int err = 0, need_write = 0, we_cancel = 0; adrbk_cntr_t new_entry_num; char warn[2][MAX_NICKNAME+1]; char tmp[OURTMPBUFLEN]; TA_S *current; SWOOP_S *swoop_list = NULL, *sw; dprint((2, "\n - take_without_edit(%d) - \n", num_in_list)); /* move to beginning of the list */ if(ta_list) while(ta_list->prev) ta_list = ta_list->prev; pab_dst = setup_for_addrbook_add(&state, command_line, cmd); if(!pab_dst) goto get_out; swoop_list = (SWOOP_S *)fs_get((num_in_list+1) * sizeof(SWOOP_S)); memset((void *)swoop_list, 0, (num_in_list+1) * sizeof(SWOOP_S)); sw = swoop_list; /* * Look through all the vcards for those with nicknames already * existing in the destination abook (dups) and build a list of * entries to be acted on. */ for(current = ta_list; current; current = current->next){ adrbk_cntr_t dst_enum; if(current->skip_it) continue; /* check to see if this nickname already exists in the dest abook */ if(current->nickname && current->nickname[0]){ AdrBk_Entry *abe; current->checked = 0; abe = adrbk_lookup_by_nick(pab_dst->address_book, current->nickname, &dst_enum); /* * This nickname already exists. */ if(abe){ sw->dup = 1; sw->dst_enum = dst_enum; if(how_many_dups < 2){ strncpy(warn[how_many_dups], current->nickname, MAX_NICKNAME); warn[how_many_dups][MAX_NICKNAME] = '\0'; } how_many_dups++; } } sw->ta = current; sw++; how_many_to_copy++; } /* * If there are some nicknames which already exist in the selected * abook, ask user what to do. */ if(how_many_dups > 0){ if(how_many_dups == 1){ if(how_many_to_copy == 1 && num_in_list == 1){ ret = 'T'; /* use Take */ if(tas && *tas){ (*tas)->state = state; (*tas)->pab = pab_dst; } goto get_out; } else{ snprintf(tmp, sizeof(tmp), "Entry with nickname \"%.*s\" already exists, replace ", OURTMPBUFLEN-50, warn[0]); } } else if(how_many_dups == 2) snprintf(tmp, sizeof(tmp), "Nicknames \"%.*s\" and \"%.*s\" already exist, replace ", (OURTMPBUFLEN-50)/2, warn[0], (OURTMPBUFLEN-50)/2, warn[1]); else snprintf(tmp, sizeof(tmp), "%d of the nicknames already exist, replace ", how_many_dups); switch(want_to(tmp, 'n', 'x', h_ab_copy_dups, WT_NORM)){ case 'n': skip_dups++; break; case 'y': break; case 'x': err++; goto get_out; } } /* * Because the deletes happen immediately we have to delete from high * entry number towards lower entry numbers so that we are deleting * the correct entries. In order to do that we'll sort the swoop_list * to give us a safe order. */ if(!skip_dups && how_many_dups > 1) qsort((qsort_t *)swoop_list, (size_t)num_in_list, sizeof(*swoop_list), cmp_swoop_list); we_cancel = busy_cue("Saving addrbook entries", NULL, 0); total_to_copy = how_many_to_copy - (skip_dups ? how_many_dups : 0); /* * Add the list of entries to the destination abook. */ for(sw = swoop_list; sw && sw->ta; sw++){ Tag tag; char abuf[MAX_ADDRESS + 1]; int count_of_addrs; if(skip_dups && sw->dup) continue; /* * Delete existing dups and replace them. */ if(sw->dup){ /* delete the existing entry */ rc = 0; if(adrbk_delete(pab_dst->address_book, (a_c_arg_t)sw->dst_enum, 1, 0, 0, 0) == 0){ need_write++; } else{ q_status_message2(SM_ORDER | SM_DING, 3, 5, "Error replacing entry in %.200s: %.200s", pab_dst->abnick, error_description(errno)); err++; goto get_out; } } /* * We need to count the number of addresses in this entry in order * to tell the adrbk routines if it is a List or a Single, and in * order to pass the right stuff to be added. */ count_of_addrs = count_addrs(sw->ta->addr); tag = (count_of_addrs > 1) ? List : Single; if(tag == Single){ if(sw->ta->addr->mailbox && sw->ta->addr->mailbox[0]){ char *scratch, *p, *t, *u; unsigned long l; RFC822BUFFER rbuf; size_t es; es = est_size(sw->ta->addr); scratch = (char *) fs_get(es * sizeof(char)); scratch[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = scratch; rbuf.cur = scratch; rbuf.end = scratch+es-1; rfc822_output_address_list(&rbuf, sw->ta->addr, 0L, NULL); *rbuf.cur = '\0'; if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){ for(t = p; ; t--) if(*t == '&'){ /* find "leading" token */ *t++ = ' '; /* replace token */ *p = '\0'; /* tie off string */ u = (char *)rfc822_base64((unsigned char *)t, (unsigned long)strlen(t), &l); *p = '@'; /* restore 'p' */ rplstr(p, es-(p-scratch), 12, ""); /* clear special token */ rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */ if(u) fs_give((void **)&u); } else if(t == scratch) break; } strncpy(abuf, scratch, sizeof(abuf)-1); abuf[sizeof(abuf)-1] = '\0'; if(scratch) fs_give((void **)&scratch); } else abuf[0] = '\0'; } /* * Now we have a clean slate to work with. */ if(total_to_copy <= 1) rc = adrbk_add(pab_dst->address_book, NO_NEXT, sw->ta->nickname, sw->ta->fullname, tag == Single ? abuf : NULL, sw->ta->fcc, sw->ta->comment, tag, &new_entry_num, (int *)NULL, 0, 0, 0); else rc = adrbk_append(pab_dst->address_book, sw->ta->nickname, sw->ta->fullname, tag == Single ? abuf : NULL, sw->ta->fcc, sw->ta->comment, tag, &new_entry_num); if(rc == 0) need_write++; /* * If the entry we copied is a list, we also have to add * the list members to the copy. */ if(rc == 0 && tag == List){ int i, save_sort_rule; ADDRESS *a, *save_next; char **list; list = (char **)fs_get((count_of_addrs + 1) * sizeof(char *)); memset((void *)list, 0, (count_of_addrs+1) * sizeof(char *)); i = 0; for(a = sw->ta->addr; a; a = a->next){ save_next = a->next; a->next = NULL; if(a->mailbox && a->mailbox[0]){ char *scratch, *p, *t, *u; unsigned long l; RFC822BUFFER rbuf; size_t es; es = est_size(a); scratch = (char *) fs_get(es * sizeof(char)); scratch[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = scratch; rbuf.cur = scratch; rbuf.end = scratch+es-1; rfc822_output_address_list(&rbuf, a, 0L, NULL); *rbuf.cur = '\0'; if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){ for(t = p; ; t--) if(*t == '&'){ /* find "leading" token */ *t++ = ' '; /* replace token */ *p = '\0'; /* tie off string */ u = (char *)rfc822_base64((unsigned char *)t, (unsigned long)strlen(t), &l); *p = '@'; /* restore 'p' */ rplstr(p, es-(p-scratch), 12, ""); /* clear special token */ rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */ if(u) fs_give((void **)&u); } else if(t == scratch) break; } strncpy(abuf, scratch, sizeof(abuf)-1); abuf[sizeof(abuf)-1] = '\0'; if(scratch) fs_give((void **)&scratch); } else abuf[0] = '\0'; list[i++] = cpystr(abuf); a->next = save_next; } /* * We want it to copy the list in the exact order * without sorting it. */ save_sort_rule = pab_dst->address_book->sort_rule; pab_dst->address_book->sort_rule = AB_SORT_RULE_NONE; rc = adrbk_nlistadd(pab_dst->address_book, (a_c_arg_t)new_entry_num, NULL, NULL, list, 0, 0, 0); pab_dst->address_book->sort_rule = save_sort_rule; free_list_array(&list); } if(rc != 0){ q_status_message1(SM_ORDER | SM_DING, 3, 5, "Error saving: %.200s", error_description(errno)); err++; goto get_out; } } if(need_write){ int sort_happened = 0; if(adrbk_write(pab_dst->address_book, 0, NULL, &sort_happened, 0, 1)){ err++; goto get_out; } if(sort_happened) ps_global->mangled_screen = 1; } get_out: if(we_cancel) cancel_busy_cue(1); if(!ret) restore_state(&state); if(swoop_list) fs_give((void **)&swoop_list); ps_global->mangled_footer = 1; if(err){ #define CAPCMDLEN 50 char capcmd[CAPCMDLEN]; ret = -1; snprintf(capcmd, sizeof(capcmd), "%c%.*s", islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd) : *cmd, CAPCMDLEN-2, cmd+1); if(need_write) q_status_message1(SM_ORDER | SM_DING, 3, 4, "%.200s only partially completed", capcmd); else cmd_cancelled(capcmd); } else if(ret != 'T' && total_to_copy > 0){ ret = 1; snprintf(tmp, sizeof(tmp), "Saved %d %s to \"%.*s\"", total_to_copy, (total_to_copy > 1) ? "entries" : "entry", OURTMPBUFLEN-30, pab_dst->abnick); q_status_message(SM_ORDER, 4, 4, tmp); } return(ret); } /* * Special case interface to allow a more interactive Save in the case where * the user seems to be wanting to save an exact copy of an existing entry. * For example, they might be trying to save a copy of a list with the intention * of changing it a little bit. The regular save doesn't allow this, since no * editing takes place, but this version plops them into the address book * editor. */ void take_this_one_entry(struct pine *ps, TA_STATE_S **tasp, AdrBk *abook, long int cur_line) { AdrBk_Entry *abe; AddrScrn_Disp *dl; char *fcc = NULL, *comment = NULL, *fullname = NULL, *nickname = NULL; ADDRESS *addr; int how_many_selected; TA_S *current = NULL; dl = dlist(cur_line); abe = ae(cur_line); if(!abe){ q_status_message(SM_ORDER, 0, 4, _("Nothing to save, cancelled")); return; } if(dl->type == ListHead || dl->type == Simple){ fcc = (abe->fcc && abe->fcc[0]) ? abe->fcc : NULL; comment = (abe->extra && abe->extra[0]) ? abe->extra : NULL; fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL; nickname = (abe->nickname && abe->nickname[0]) ? abe->nickname : NULL; } addr = abe_to_address(abe, dl, abook, &how_many_selected); if(!addr){ addr = mail_newaddr(); addr->host = cpystr(""); addr->mailbox = cpystr(""); } switch(abe->tag){ case Single: #ifdef ENABLE_LDAP /* * Special case. When user is saving an entry with a runtime * ldap lookup address, they may be doing it because the lookup * has become stale. Give them a way to get the old address out * of the lookup entry so they can save that, instead. */ if(!addr->personal && !strncmp(addr->mailbox, RUN_LDAP, LEN_RL)){ LDAP_SERV_S *info = NULL; int i = 'l'; static ESCKEY_S backup_or_ldap[] = { {'b', 'b', "B", N_("Backup")}, {'l', 'l', "L", N_("LDAP")}, {-1, 0, NULL, NULL}}; info = break_up_ldap_server(addr->mailbox + LEN_RL); if(info && info->mail && *info->mail) i = radio_buttons(_("Copy backup address or retain LDAP search criteria ? "), -FOOTER_ROWS(ps_global), backup_or_ldap, 'b', 'x', h_ab_backup_or_ldap, RB_NORM); if(i == 'b'){ ADDRESS *a = NULL; rfc822_parse_adrlist(&a, info->mail, fakedomain); if(a){ if(addr->mailbox) fs_give((void **)&addr->mailbox); if(addr->host) fs_give((void **)&addr->host); addr->mailbox = a->mailbox; a->mailbox = NULL; addr->host = a->host; a->host = NULL; mail_free_address(&a); } } if(info) free_ldap_server_info(&info); } #endif /* ENABLE_LDAP */ current = fill_in_ta(¤t, addr, 0, (char *)NULL); break; case List: /* * The empty string for the last argument is significant. Fill_in_ta * copies the whole adrlist into a single TA if there is a print * string. */ if(dl->type == ListHead) current = fill_in_ta(¤t, addr, 1, ""); else current = fill_in_ta(¤t, addr, 0, (char *)NULL); break; default: dprint((1, "default case in take_this_one_entry, shouldn't happen\n")); return; } if(current->strvalue && !strcmp(current->strvalue, "@")){ fs_give((void **)¤t->strvalue); if(fullname && fullname[0]) current->strvalue = cpystr(fullname); else if(nickname && nickname[0]) current->strvalue = cpystr(nickname); else current->strvalue = cpystr("?"); convert_possibly_encoded_str_to_utf8(¤t->strvalue); } if(addr) mail_free_address(&addr); if(current){ current = first_sel_taline(current); if(fullname && *fullname){ current->fullname = cpystr(fullname); convert_possibly_encoded_str_to_utf8(¤t->fullname); } if(fcc && *fcc){ current->fcc = cpystr(fcc); convert_possibly_encoded_str_to_utf8(¤t->fcc); } if(comment && *comment){ current->comment = cpystr(comment); convert_possibly_encoded_str_to_utf8(¤t->comment); } if(nickname && *nickname){ current->nickname = cpystr(nickname); convert_possibly_encoded_str_to_utf8(¤t->nickname); } takeaddr_bypass(ps, current, tasp); } else q_status_message(SM_ORDER, 0, 4, _("Nothing to save, cancelled")); free_talines(¤t); } /* * Execute command to save addresses out of vcard attachment. */ void save_vcard_att(struct pine *ps, int qline, long int msgno, ATTACH_S *a) { int how_many_selected, j; TA_S *current = NULL; TA_STATE_S tas, *tasp; dprint((2, "\n - saving vcard attachment - \n")); j = radio_buttons(_("Save to address book or Export to filesystem ? "), qline, save_or_export, 's', 'x', h_ab_save_exp, RB_NORM|RB_SEQ_SENSITIVE); switch(j){ case 'x': q_status_message(SM_INFO, 0, 2, _("Address book save cancelled")); return; case 'e': export_vcard_att(ps, qline, msgno, a); return; case 's': break; default: q_status_message(SM_ORDER, 3, 3, "can't happen in save_vcard_att"); return; } dprint((2, "\n - saving attachment into address book - \n")); ps->mangled_footer = 1; current = NULL; how_many_selected = process_vcard_atts(ps->mail_stream, msgno, NULL, a->body, a->number, ¤t); if(how_many_selected > 0){ tas.pab = NULL; tasp = &tas; if(how_many_selected == 1){ takeaddr_bypass(ps, current, NULL); } else if(take_without_edit(current, how_many_selected, qline, &tasp, "save") == 'T'){ /* * Eliminate dups. */ how_many_selected -= eliminate_dups_but_not_us(first_sel_taline(current)); (void)takeaddr_screen(ps, current, how_many_selected, SingleMode, &tasp, _("save")); /* * If takeaddr_screen or its children didn't do this for us, * we do it here. */ if(tas.pab) restore_state(&(tas.state)); } } else if(how_many_selected == 0) q_status_message(SM_ORDER, 0, 3, _("Save cancelled: no entries in attachment")); free_talines(¤t); } /* * Execute command to export vcard attachment. */ void export_vcard_att(struct pine *ps, int qline, long int msgno, ATTACH_S *a) { int how_many_selected, i; TA_S *current; STORE_S *srcstore = NULL; SourceType srctype; static ESCKEY_S vcard_or_addresses[] = { {'a', 'a', "A", N_("Address List")}, {'v', 'v', "V", N_("VCard")}, {-1, 0, NULL, NULL}}; if(ps->restricted){ q_status_message(SM_ORDER, 0, 3, "Alpine demo can't export addresses to files"); return; } dprint((2, "\n - exporting vcard attachment - \n")); i = radio_buttons(_("Export list of addresses or vCard text ? "), qline, vcard_or_addresses, 'a', 'x', h_ab_export_vcard, RB_NORM|RB_SEQ_SENSITIVE); switch(i){ case 'x': q_status_message(SM_INFO, 0, 2, _("Address book export cancelled")); return; case 'a': break; case 'v': write_attachment(qline, msgno, a, "EXPORT"); return; default: q_status_message(SM_ORDER, 3, 3, _("can't happen in export_vcard_att")); return; } ps->mangled_footer = 1; current = NULL; how_many_selected = process_vcard_atts(ps->mail_stream, msgno, NULL, a->body, a->number, ¤t); /* * Run through all of the list and run through * the addresses in each ta->addr, writing them into a storage object. * Then export to filesystem. */ srctype = CharStar; if(how_many_selected > 0 && (srcstore = so_get(srctype, NULL, EDIT_ACCESS)) != NULL){ ADDRESS *aa, *bb; int are_some = 0; for(current = first_taline(current); current; current = next_taline(current)){ for(aa = current->addr; aa; aa = aa->next){ bb = aa->next; aa->next = NULL; so_puts(srcstore, addr_list_string(aa, NULL, 0)); are_some++; so_puts(srcstore, "\n"); aa->next = bb; } } if(are_some) simple_export(ps, so_text(srcstore), srctype, "addresses", NULL); else q_status_message(SM_ORDER, 0, 3, _("No addresses to export")); so_give(&srcstore); } else{ if(how_many_selected == 0) q_status_message(SM_ORDER, 0, 3, _("Nothing to export")); else q_status_message(SM_ORDER,0,2, _("Error allocating space")); } } void take_to_export(struct pine *ps, LINES_TO_TAKE *lines_to_take) { CONF_S *ctmp = NULL, *first_line = NULL; OPT_SCREEN_S screen; LINES_TO_TAKE *li; char *help_title = _("HELP FOR TAKE EXPORT SCREEN"); char *p; ScreenMode listmode = SingleMode; for(li = lines_to_take; li; li = li->next){ new_confline(&ctmp); ctmp->flags |= CF_STARTITEM; if(li->flags & LT_NOSELECT) ctmp->flags |= CF_NOSELECT; else if(!first_line) first_line = ctmp; p = li->printval ? li->printval : ""; if(ctmp->flags & CF_NOSELECT) ctmp->value = cpystr(p); else{ size_t l; /* 5 is for "[X] " */ l = strlen(p)+5; ctmp->value = (char *)fs_get((l+1) * sizeof(char)); snprintf(ctmp->value, l+1, " %s", p); ctmp->value[l] = '\0'; } /* this points to data, it doesn't have its own copy */ ctmp->d.t.exportval = li->exportval ? li->exportval : NULL; ctmp->d.t.selected = 0; ctmp->d.t.listmode = &listmode; ctmp->tool = take_export_tool; ctmp->help_title = help_title; ctmp->help = h_takeexport_screen; ctmp->keymenu = &take_export_keymenu_sm; } if(!first_line) q_status_message(SM_ORDER, 3, 3, _("No lines to export")); else{ memset(&screen, 0, sizeof(screen)); conf_scroll_screen(ps, &screen, first_line, _("Take Export"), NULL, 0, NULL); } } int take_export_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags) { CONF_S *ctmp; int retval = 0; int some_selected = 0, something_to_export = 0; SourceType srctype; STORE_S *srcstore = NULL; char *prompt_msg; switch(cmd){ case MC_TAKE : srctype = CharStar; if((srcstore = so_get(srctype, NULL, EDIT_ACCESS)) != NULL){ if(*(*cl)->d.t.listmode == SingleMode){ some_selected++; if((*cl)->d.t.exportval && (*cl)->d.t.exportval[0]){ so_puts(srcstore, (*cl)->d.t.exportval); so_puts(srcstore, "\n"); something_to_export++; prompt_msg = "selection"; } } else{ /* go to first line */ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp)) ; for(; ctmp; ctmp = next_confline(ctmp)) if(!(ctmp->flags & CF_NOSELECT) && ctmp->d.t.selected){ some_selected++; if(ctmp->d.t.exportval && ctmp->d.t.exportval[0]){ so_puts(srcstore, ctmp->d.t.exportval); so_puts(srcstore, "\n"); something_to_export++; prompt_msg = "selections"; } } } } if(!srcstore) q_status_message(SM_ORDER, 0, 3, _("Error allocating space")); else if(something_to_export) simple_export(ps, so_text(srcstore), srctype, prompt_msg, NULL); else if(!some_selected && *(*cl)->d.t.listmode == ListMode) q_status_message(SM_ORDER, 0, 3, _("Use \"X\" to mark selections")); else q_status_message(SM_ORDER, 0, 3, _("Nothing to export")); if(srcstore) so_give(&srcstore); break; case MC_LISTMODE : if(*(*cl)->d.t.listmode == SingleMode){ /* * UnHide the checkboxes */ *(*cl)->d.t.listmode = ListMode; /* go to first line */ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp)) ; for(; ctmp; ctmp = next_confline(ctmp)) if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){ ctmp->value[0] = '['; ctmp->value[1] = ctmp->d.t.selected ? 'X' : SPACE; ctmp->value[2] = ']'; ctmp->keymenu = &take_export_keymenu_lm; } } else{ /* * Hide the checkboxes */ *(*cl)->d.t.listmode = SingleMode; /* go to first line */ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp)) ; for(; ctmp; ctmp = next_confline(ctmp)) if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){ ctmp->value[0] = ctmp->value[1] = ctmp->value[2] = SPACE; ctmp->keymenu = &take_export_keymenu_sm; } } ps->mangled_body = ps->mangled_footer = 1; break; case MC_CHOICE : if((*cl)->value[1] == 'X'){ (*cl)->d.t.selected = 0; (*cl)->value[1] = SPACE; } else{ (*cl)->d.t.selected = 1; (*cl)->value[1] = 'X'; } ps->mangled_body = 1; break; case MC_SELALL : /* go to first line */ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp)) ; for(; ctmp; ctmp = next_confline(ctmp)){ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){ ctmp->d.t.selected = 1; if(ctmp->value) ctmp->value[1] = 'X'; } } ps->mangled_body = 1; break; case MC_UNSELALL : /* go to first line */ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp)) ; for(; ctmp; ctmp = next_confline(ctmp)){ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){ ctmp->d.t.selected = 0; if(ctmp->value) ctmp->value[1] = SPACE; } } ps->mangled_body = 1; break; case MC_EXIT : retval = simple_exit_cmd(flags); break; default: retval = -1; break; } return(retval); } #ifdef ENABLE_LDAP /* * Save an LDAP directory entry somewhere * * Args: ps -- pine struct * e -- the entry to save * save -- If this is set, then bypass the question about whether * to save or export and just do the save. */ void save_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *e, int save) { char *fullname = NULL, *address = NULL, *first = NULL, *last = NULL, *comment = NULL; struct berval **cn = NULL, **mail = NULL, **sn = NULL, **givenname = NULL, **title = NULL, **telephone = NULL, **elecmail = NULL, **note = NULL; int j, num, export = 0; dprint((2, "\n - save_ldap_entry - \n")); if(e){ char *a; BerElement *ber; for(a = ldap_first_attribute(e->ld, e->selected_entry, &ber); a != NULL; a = ldap_next_attribute(e->ld, e->selected_entry, ber)){ dprint((9, " %s", a ? a : "?")); if(strcmp(a, e->info_used->cnattr) == 0){ if(!cn) cn = ldap_get_values_len(e->ld, e->selected_entry, a); } else if(strcmp(a, e->info_used->mailattr) == 0){ if(!mail) mail = ldap_get_values_len(e->ld, e->selected_entry, a); } else if(strcmp(a, "electronicmail") == 0){ if(!elecmail) elecmail = ldap_get_values_len(e->ld, e->selected_entry, a); } else if(strcmp(a, "comment") == 0){ if(!note) note = ldap_get_values_len(e->ld, e->selected_entry, a); } else if(strcmp(a, e->info_used->snattr) == 0){ if(!sn) sn = ldap_get_values_len(e->ld, e->selected_entry, a); } else if(strcmp(a, e->info_used->gnattr) == 0){ if(!givenname) givenname = ldap_get_values_len(e->ld, e->selected_entry, a); } else if(strcmp(a, "telephonenumber") == 0){ if(!telephone) telephone = ldap_get_values_len(e->ld, e->selected_entry, a); } else if(strcmp(a, "title") == 0){ if(!title) title = ldap_get_values_len(e->ld, e->selected_entry, a); } } if(!save){ j = radio_buttons(_("Save to address book or Export to filesystem ? "), -FOOTER_ROWS(ps), save_or_export, 's', 'x', h_ab_save_exp, RB_NORM); switch(j){ case 'x': q_status_message(SM_INFO, 0, 2, _("Address book save cancelled")); break; case 'e': export++; break; case 's': save++; break; default: q_status_message(SM_ORDER, 3, 3, "can't happen in save_ldap_ent"); break; } } } if(elecmail){ if(ALPINE_LDAP_can_use(elecmail) && !mail) mail = elecmail; else ldap_value_free_len(elecmail); elecmail = NULL; } if(save){ /* save into the address book */ ADDRESS *addr; char *d, *fakedomain = "@"; size_t len; struct berval **cm; int how_many_selected = 0; if(ALPINE_LDAP_can_use(cn)) fullname = cpystr(cn[0]->bv_val); if(ALPINE_LDAP_can_use(sn)) last = cpystr(sn[0]->bv_val); if(ALPINE_LDAP_can_use(givenname)) first = cpystr(givenname[0]->bv_val); if(ALPINE_LDAP_can_use(note)) cm = note; else cm = title; for(len = 0, num = 0; ALPINE_LDAP_usable(cm, num); num++) len += strlen(cm[num]->bv_val) + 2; if(len){ comment = (char *)fs_get(len * sizeof(char)); d = comment; num = 0; while(ALPINE_LDAP_usable(cm, num)){ sstrncpy(&d, cm[num]->bv_val, len-(d-comment)); num++; if(ALPINE_LDAP_usable(cm, num)) sstrncpy(&d, "; ", len-(d-comment)); } comment[len-1] = '\0'; } for(len = 0, num = 0; ALPINE_LDAP_usable(mail, num); num++) len += strlen(mail[num]->bv_val) + 2; /* paste the email addresses together */ if(len){ address = (char *)fs_get(len * sizeof(char)); d = address; num = 0; while(ALPINE_LDAP_usable(mail, num)){ sstrncpy(&d, mail[num]->bv_val, len-(d-address)); num++; if(ALPINE_LDAP_usable(mail, num)) sstrncpy(&d, ", ", len-(d-address)); } address[len-1] = '\0'; } addr = NULL; if(address) rfc822_parse_adrlist(&addr, address, fakedomain); if(addr && fullname && !(first && last)){ if(addr->personal) fs_give((void **)&addr->personal); addr->personal = cpystr(fullname); } if(addr && e->serv && *e->serv){ /* save by reference */ char *dn, *edn = NULL; dn = ldap_get_dn(e->ld, e->selected_entry); if(dn){ edn = add_backslash_escapes(dn); our_ldap_dn_memfree(dn); } if(e->serv && *e->serv && edn && *edn){ char buf[MAILTMPLEN+1]; char *backup_mail = NULL; how_many_selected++; if(addr && addr->mailbox && addr->host){ strncpy(buf, addr->mailbox, sizeof(buf)-2), buf[sizeof(buf)-2] = '\0'; strncat(buf, "@", sizeof(buf)-1-strlen(buf)); strncat(buf, addr->host, sizeof(buf)-1-strlen(buf)); buf[sizeof(buf)-1] = '\0'; backup_mail = cpystr(buf); } /* * We only need one addr which we will use to hold the * pointer to the query. */ if(addr->next) mail_free_address(&addr->next); if(addr->mailbox) fs_give((void **)&addr->mailbox); if(addr->host) fs_give((void **)&addr->host); if(addr->personal) fs_give((void **)&addr->personal); snprintf(buf, sizeof(buf), "%s%s /base=%s/scope=base/cust=(objectclass=*)%s%s", RUN_LDAP, e->serv, edn, backup_mail ? "/mail=" : "", backup_mail ? backup_mail : ""); buf[sizeof(buf)-1] = '\0'; if(backup_mail) fs_give((void **)&backup_mail); /* * Put the search parameters in mailbox and put @ in * host so that expand_address accepts it as an unqualified * address and doesn't try to add localdomain. */ addr->mailbox = cpystr(buf); addr->host = cpystr("@"); } if(edn) fs_give((void **)&edn); } else{ /* save by value */ how_many_selected++; if(!addr) addr = mail_newaddr(); } if(how_many_selected > 0){ TA_S *current = NULL; /* * The empty string for the last argument is significant. * Fill_in_ta copies the whole adrlist into a single TA if * there is a print string. */ current = fill_in_ta(¤t, addr, 1, ""); current = first_sel_taline(current); if(first && last && current){ char *p; size_t l; l = strlen(last) + 2 + strlen(first); p = (char *)fs_get((l+1) * sizeof(char)); snprintf(p, l, "%s, %s", last, first); p[l] = '\0'; current->fullname = p; convert_possibly_encoded_str_to_utf8(¤t->fullname); } if(comment && current){ current->comment = cpystr(comment); convert_possibly_encoded_str_to_utf8(¤t->comment); } /* * We don't want the personal name to make it into the address * field in an LDAP: query sort of address, so move it * out of the addr. */ if(e->serv && *e->serv && current && fullname){ if(current->fullname) fs_give((void **)¤t->fullname); current->fullname = fullname; fullname = NULL; convert_possibly_encoded_str_to_utf8(¤t->fullname); } mail_free_address(&addr); if(current) takeaddr_bypass(ps, current, NULL); else q_status_message(SM_ORDER, 0, 4, "Nothing to save"); free_talines(¤t); } else q_status_message(SM_ORDER, 0, 4, "Nothing to save"); } else if(export){ /* export to filesystem */ STORE_S *srcstore = NULL; SourceType srctype; static ESCKEY_S text_or_vcard[] = { {'t', 't', "T", N_("Text")}, {'a', 'a', "A", N_("Addresses")}, {'v', 'v', "V", N_("VCard")}, {-1, 0, NULL, NULL}}; j = radio_buttons( _("Export text of entry, address, or VCard format ? "), -FOOTER_ROWS(ps), text_or_vcard, 't', 'x', h_ldap_text_or_vcard, RB_NORM); switch(j){ case 'x': q_status_message(SM_INFO, 0, 2, _("Address book export cancelled")); break; case 't': srctype = CharStar; if(!(srcstore = prep_ldap_for_viewing(ps, e, srctype, NULL))) q_status_message(SM_ORDER, 0, 2, _("Error allocating space")); else{ (void)simple_export(ps_global, so_text(srcstore), srctype, "text", NULL); so_give(&srcstore); } break; case 'a': if(ALPINE_LDAP_can_use(mail)){ srctype = CharStar; if(!(srcstore = so_get(srctype, NULL, EDIT_ACCESS))) q_status_message(SM_ORDER,0,2, _("Error allocating space")); else{ for(num = 0; ALPINE_LDAP_usable(mail, num); num++){ so_puts(srcstore, mail[num]->bv_val); so_puts(srcstore, "\n"); } (void)simple_export(ps_global, so_text(srcstore), srctype, "addresses", NULL); so_give(&srcstore); } } break; case 'v': srctype = CharStar; if(!(srcstore = so_get(srctype, NULL, EDIT_ACCESS))) q_status_message(SM_ORDER,0,2, _("Error allocating space")); else{ gf_io_t pc; VCARD_INFO_S *vinfo; vinfo = (VCARD_INFO_S *)fs_get(sizeof(VCARD_INFO_S)); memset((void *)vinfo, 0, sizeof(VCARD_INFO_S)); if(ALPINE_LDAP_can_use(cn)) vinfo->fullname = berval_to_array(cn); if(ALPINE_LDAP_can_use(note)) vinfo->note = berval_to_array(note); if(ALPINE_LDAP_can_use(title)) vinfo->title = berval_to_array(title); if(ALPINE_LDAP_can_use(telephone)) vinfo->tel = berval_to_array(telephone); if(ALPINE_LDAP_can_use(mail)) vinfo->email = berval_to_array(mail); if(ALPINE_LDAP_can_use(sn)) vinfo->last = cpystr(sn[0]->bv_val); if(ALPINE_LDAP_can_use(givenname)) vinfo->first = cpystr(givenname[0]->bv_val); gf_set_so_writec(&pc, srcstore); write_single_vcard_entry(ps_global, pc, vinfo); free_vcard_info(&vinfo); (void)simple_export(ps_global, so_text(srcstore), srctype, "vcard text", NULL); so_give(&srcstore); } break; default: q_status_message(SM_ORDER, 3, 3, "can't happen in text_or_vcard"); break; } } if(cn) ldap_value_free_len(cn); if(mail) ldap_value_free_len(mail); if(elecmail) ldap_value_free_len(elecmail); if(note) ldap_value_free_len(note); if(sn) ldap_value_free_len(sn); if(givenname) ldap_value_free_len(givenname); if(telephone) ldap_value_free_len(telephone); if(title) ldap_value_free_len(title); if(fullname) fs_give((void **)&fullname); if(address) fs_give((void **)&address); if(first) fs_give((void **)&first); if(last) fs_give((void **)&last); if(comment) fs_give((void **)&comment); } #endif /* ENABLE_LDAP */ #ifdef _WINDOWS int ta_scroll_up(count) long count; { if(count<0) return(ta_scroll_down(-count)); else if (count){ long i=count; TA_S *next_sel; while(i && ta_screen->top_line->next){ if(ta_screen->top_line == ta_screen->current){ if(next_sel = next_sel_taline(ta_screen->current)){ ta_screen->current = next_sel; ta_screen->top_line = next_taline(ta_screen->top_line); i--; } else i = 0; } else{ ta_screen->top_line = next_taline(ta_screen->top_line); i--; } } } return(TRUE); } int ta_scroll_down(count) long count; { if(count < 0) return(ta_scroll_up(-count)); else if (count){ long i,dline; long page_size = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global) - HEADER_ROWS(ps_global); TA_S *ctmp; TA_S *first = first_taline(ta_screen->top_line); i=count; dline=0; for(ctmp = ta_screen->top_line; ctmp != ta_screen->current; ctmp = next_taline(ctmp)) dline++; while(i && ta_screen->top_line != first){ ta_screen->top_line = pre_taline(ta_screen->top_line); i--; dline++; if(dline >= page_size){ ctmp = pre_sel_taline(ta_screen->current); if(ctmp == NULL){ i = 0; ta_screen->top_line = next_taline(ta_screen->top_line); } else { for(; ctmp != ta_screen->current; ta_screen->current = pre_taline(ta_screen->current)) dline--; } } } } return(TRUE); } int ta_scroll_to_pos(line) long line; { TA_S *ctmp; int dline; for(dline = 0, ctmp = first_taline(ta_screen->top_line); ctmp != ta_screen->top_line; ctmp = next_taline(ctmp)) dline++; if (!ctmp) dline = 1; return(ta_scroll_up(line - dline)); } int ta_scroll_callback (cmd, scroll_pos) int cmd; long scroll_pos; { int paint = TRUE; long page_size = ps_global->ttyo->screen_rows - HEADER_ROWS(ps_global) - FOOTER_ROWS(ps_global); switch (cmd) { case MSWIN_KEY_SCROLLUPLINE: paint = ta_scroll_down (scroll_pos); break; case MSWIN_KEY_SCROLLDOWNLINE: paint = ta_scroll_up (scroll_pos); break; case MSWIN_KEY_SCROLLUPPAGE: paint = ta_scroll_down (page_size); break; case MSWIN_KEY_SCROLLDOWNPAGE: paint = ta_scroll_up (page_size); break; case MSWIN_KEY_SCROLLTO: paint = ta_scroll_to_pos (scroll_pos); break; } if(paint) update_takeaddr_screen(ps_global, ta_screen->current, ta_screen, (Pos *)NULL); return(paint); } #endif /* _WINDOWS */