diff options
author | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
---|---|---|
committer | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
commit | 094ca96844842928810f14844413109fc6cdd890 (patch) | |
tree | e60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pith/bldaddr.c | |
download | alpine-094ca96844842928810f14844413109fc6cdd890.tar.xz |
Initial Alpine Version
Diffstat (limited to 'pith/bldaddr.c')
-rw-r--r-- | pith/bldaddr.c | 1263 |
1 files changed, 1263 insertions, 0 deletions
diff --git a/pith/bldaddr.c b/pith/bldaddr.c new file mode 100644 index 00000000..9feeae38 --- /dev/null +++ b/pith/bldaddr.c @@ -0,0 +1,1263 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: bldaddr.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2007 University of Washington + * Copyright 2013 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 + * + * ======================================================================== + */ + +/*====================================================================== + buildaddr.c + Support for build_address function and low-level display cache. + ====*/ + + +#include "../pith/headers.h" +#include "../pith/bldaddr.h" +#include "../pith/ldap.h" +#include "../pith/conf.h" +#include "../pith/adrbklib.h" +#include "../pith/copyaddr.h" +#include "../pith/remote.h" +#include "../pith/status.h" +#include "../pith/search.h" +#include "../pith/addrstring.h" +#include "../pith/util.h" +#include "../pith/ablookup.h" +#include "../pith/news.h" +#include "../pith/stream.h" +#include "../pith/mailcmd.h" +#include "../pith/osdep/pw_stuff.h" + + +/* + * Jump back to this location if we discover that one of the open addrbooks + * has been changed by some other process. + */ +jmp_buf addrbook_changed_unexpectedly; + + +/* + * Sometimes the address book code calls something outside of the address + * book and that calls back to the address book. For example, you could be + * in the address book mgmt screen, call ab_compose, and then a ^T could + * call back to an address book select screen. Or moving out of a line + * calls back to build_address. This keeps track of the nesting level so + * that we can know when it is safe to update an out of date address book. + * For example, if we know an address book is open and displayed, it isn't + * safe to update it because that would pull the cache out from under the + * displayed lines. If we don't have it displayed, then it is ok to close + * it and re-open it (adrbk_check_and_fix()). + */ +int ab_nesting_level = 0; + + + +/* + * This is like build_address() only it doesn't close + * everything down when it is done, and it doesn't open addrbooks that + * are already open. Other than that, it has the same functionality. + * It opens addrbooks that haven't been opened and saves and restores the + * addrbooks open states (if save_and_restore is set). + * + * Args: to -- the address to attempt expanding (see the + * description in expand_address) + * full_to -- a pointer to result + * This will be allocated here and freed by the caller. + * error -- a pointer to an error message, if non-null + * fcc -- a pointer to returned fcc, if non-null + * This will be allocated here and freed by the caller. + * *fcc should be null on entry. + * save_and_restore -- restore addrbook states when finished + * + * Results: 0 -- address is ok + * -1 -- address is not ok + * full_to contains the expanded address on success, or a copy of to + * on failure + * *error will point to an error message on failure it it is non-null + * + * Side effect: Can flush addrbook entry cache entries so they need to be + * re-fetched afterwords. + */ +int +our_build_address(BuildTo to, char **full_to, char **error, char **fcc, + void (*save_and_restore_f)(int, SAVE_STATE_S *)) +{ + int ret; + + dprint((7, "- our_build_address - (%s)\n", + (to.type == Str) ? (to.arg.str ? to.arg.str : "nul") + : (to.arg.abe->nickname ? to.arg.abe->nickname + : "no nick"))); + + if((to.type == Str && !to.arg.str) || (to.type == Abe && !to.arg.abe)){ + if(full_to) + *full_to = cpystr(""); + ret = 0; + } + else + ret = build_address_internal(to, full_to, error, fcc, NULL, NULL, + save_and_restore_f, 0, NULL); + + dprint((8, " our_build_address says %s address\n", + ret ? "BAD" : "GOOD")); + + return(ret); +} + + + +#define FCC_SET 1 /* Fcc was set */ +#define LCC_SET 2 /* Lcc was set */ +#define FCC_NOREPO 4 /* Fcc was set in a non-reproducible way */ +#define LCC_NOREPO 8 /* Lcc was set in a non-reproducible way */ +/* + * Given an address, expand it based on address books, local domain, etc. + * This will open addrbooks if needed before checking (actually one of + * its children will open them). + * + * Args: to -- The given address to expand (see the description + * in expand_address) + * full_to -- Returned value after parsing to. + * error -- This gets pointed at error message, if any + * fcc -- Returned value of fcc for first addr in to + * no_repo -- Returned value, set to 1 if the fcc or lcc we're + * returning is not reproducible from the expanded + * address. That is, if we were to run + * build_address_internal again on the resulting full_to, + * we wouldn't get back the fcc again. For example, + * if we expand a list and use the list fcc from the + * addrbook, the full_to no longer contains the + * information that this was originally list foo. + * save_and_restore -- restore addrbook state when done + * + * Result: 0 is returned if address was OK, + * -1 if address wasn't OK. + * The address is expanded, fully-qualified, and personal name added. + * + * Input may have more than one address separated by commas. + * + * Side effect: Can flush addrbook entry cache entries so they need to be + * re-fetched afterwords. + */ +int +build_address_internal(BuildTo to, char **full_to, char **error, char **fcc, + int *no_repo, char **lcc, + void (*save_and_restore_f)(int, SAVE_STATE_S *), + int simple_verify, int *mangled) +{ + ADDRESS *a; + int loop, i; + int tried_route_addr_hack = 0; + int did_set = 0; + char *tmp = NULL; + SAVE_STATE_S state; + PerAddrBook *pab; + + dprint((8, "- build_address_internal - (%s)\n", + (to.type == Str) ? (to.arg.str ? to.arg.str : "nul") + : (to.arg.abe->nickname ? to.arg.abe->nickname + : "no nick"))); + + init_ab_if_needed(); + if(save_and_restore_f) + (*save_and_restore_f)(SAR_SAVE, &state); + +start: + loop = 0; + ps_global->c_client_error[0] = '\0'; + wp_exit = wp_nobail = 0; + + a = expand_address(to, ps_global->maildomain, + F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global) + ? ps_global->maildomain : NULL, + &loop, fcc, &did_set, lcc, error, + 0, simple_verify, mangled); + + /* + * If the address is a route-addr, expand_address() will have rejected + * it unless it was enclosed in brackets, since route-addrs can't stand + * alone. Try it again with brackets. We should really be checking + * each address in the list of addresses instead of assuming there is + * only one address, but we don't want to have this function know + * all about parsing rfc822 addrs. + */ + if(!tried_route_addr_hack && + ps_global->c_client_error[0] != '\0' && + ((to.type == Str && to.arg.str && to.arg.str[0] == '@') || + (to.type == Abe && to.arg.abe->tag == Single && + to.arg.abe->addr.addr[0] == '@'))){ + BuildTo bldto; + + tried_route_addr_hack++; + + tmp = (char *)fs_get((size_t)(MAX_ADDR_FIELD + 3)); + + /* add brackets to whole thing */ + strncpy(tmp, "<", MAX_ADDR_FIELD+3); + tmp[MAX_ADDR_FIELD+3-1] = '\0'; + if(to.type == Str) + strncat(tmp, to.arg.str, MAX_ADDR_FIELD+3-strlen(tmp)-1); + else + strncat(tmp, to.arg.abe->addr.addr, MAX_ADDR_FIELD+3-strlen(tmp)-1); + + tmp[MAX_ADDR_FIELD+3-1] = '\0'; + strncat(tmp, ">", MAX_ADDR_FIELD+3-strlen(tmp)-1); + tmp[MAX_ADDR_FIELD+3-1] = '\0'; + + loop = 0; + ps_global->c_client_error[0] = '\0'; + + bldto.type = Str; + bldto.arg.str = tmp; + + if(a) + mail_free_address(&a); + + /* try it */ + a = expand_address(bldto, ps_global->maildomain, + F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global) + ? ps_global->maildomain : NULL, + &loop, fcc, &did_set, lcc, error, + 0, simple_verify, mangled); + + /* if no error this time, use it */ + if(ps_global->c_client_error[0] == '\0'){ + if(save_and_restore_f) + (*save_and_restore_f)(SAR_RESTORE, &state); + + /* + * Clear references so that Addrbook Entry caching in adrbklib.c + * is allowed to throw them out of cache. + */ + for(i = 0; i < as.n_addrbk; i++){ + pab = &as.adrbks[i]; + if(pab->ostatus == Open || pab->ostatus == NoDisplay) + adrbk_clearrefs(pab->address_book); + } + + goto ok; + } + else{ /* go back and use what we had before, so we get the error */ + if(tmp) + fs_give((void **)&tmp); + + tmp = NULL; + goto start; + } + } + + if(save_and_restore_f) + (*save_and_restore_f)(SAR_RESTORE, &state); + + /* + * Clear references so that Addrbook Entry caching in adrbklib.c + * is allowed to throw them out of cache. + */ + for(i = 0; i < as.n_addrbk; i++){ + pab = &as.adrbks[i]; + if(pab->ostatus == Open || pab->ostatus == NoDisplay) + adrbk_clearrefs(pab->address_book); + } + + if(ps_global->c_client_error[0] != '\0'){ + /* Parse error. Return string as is and error message */ + if(full_to){ + if(to.type == Str) + *full_to = cpystr(to.arg.str); + else{ + if(to.arg.abe->nickname && to.arg.abe->nickname[0]) + *full_to = cpystr(to.arg.abe->nickname); + else if(to.arg.abe->tag == Single) + *full_to = cpystr(to.arg.abe->addr.addr); + else + *full_to = cpystr(""); + } + } + + if(error != NULL){ + /* display previous error and add new one */ + if(*error){ + q_status_message(SM_ORDER, 3, 5, *error); + display_message('x'); + fs_give((void **)error); + } + + *error = cpystr(ps_global->c_client_error); + } + + dprint((2, + "build_address_internal returning parse error: %s\n", + ps_global->c_client_error ? ps_global->c_client_error : "?")); + if(a) + mail_free_address(&a); + + if(tmp) + fs_give((void **)&tmp); + + if(mangled){ + if(ps_global->mangled_screen) + *mangled |= BUILDER_SCREEN_MANGLED; + else if(ps_global->mangled_footer) + *mangled |= BUILDER_FOOTER_MANGLED; + } + + return -1; + } + + if(mangled){ + if(ps_global->mangled_screen) + *mangled |= BUILDER_SCREEN_MANGLED; + else if(ps_global->mangled_footer) + *mangled |= BUILDER_FOOTER_MANGLED; + } + + /* + * If there's a loop in the addressbook, we modify the address and + * send an error back, but we still return 0. + */ +ok: + if(loop && error != NULL){ + /* display previous error and add new one */ + if(*error){ + q_status_message(SM_ORDER, 3, 5, *error); + display_message('x'); + fs_give((void **)error); + } + + *error = cpystr(_("Loop or Duplicate detected in addressbook!")); + } + + + if(full_to){ + if(simple_verify){ + if(tmp){ + *full_to = tmp; /* add the brackets to route addr */ + tmp = NULL; + } + else{ + /* try to return what they sent us */ + if(to.type == Str) + *full_to = cpystr(to.arg.str); + else{ + if(to.arg.abe->nickname && to.arg.abe->nickname[0]) + *full_to = cpystr(to.arg.abe->nickname); + else if(to.arg.abe->tag == Single) + *full_to = cpystr(to.arg.abe->addr.addr); + else + *full_to = cpystr(""); + } + } + } + else{ + RFC822BUFFER rbuf; + size_t len; + + len = est_size(a); + *full_to = (char *) fs_get(len * sizeof(char)); + (*full_to)[0] = '\0'; + /* + * Assume that quotes surrounding the whole personal name are + * not meant to be literal quotes. That is, the name + * "Joe College, PhD." is quoted so that we won't do the + * switcheroo of Last, First, not so that the quotes will be + * literal. Rfc822_write_address will put the quotes back if they + * are needed, so Joe College would end up looking like + * "Joe College, PhD." <joe@somewhere.edu> but not like + * "\"Joe College, PhD.\"" <joe@somewhere.edu>. + */ + strip_personal_quotes(a); + rbuf.f = dummy_soutr; + rbuf.s = NULL; + rbuf.beg = *full_to; + rbuf.cur = *full_to; + rbuf.end = (*full_to)+len-1; + rfc822_output_address_list(&rbuf, a, 0L, NULL); + *rbuf.cur = '\0'; + } + } + + if(no_repo && (did_set & FCC_NOREPO || did_set & LCC_NOREPO)) + *no_repo = 1; + + /* + * The condition in the leading if means that addressbook fcc's + * override the fcc-rule (because did_set will be set). + */ + if(fcc && !(did_set & FCC_SET)){ + char *fcc_got = NULL; + + if((ps_global->fcc_rule == FCC_RULE_LAST + || ps_global->fcc_rule == FCC_RULE_CURRENT) + && strcmp(fcc_got = get_fcc(NULL), ps_global->VAR_DEFAULT_FCC)){ + if(*fcc) + fs_give((void **)fcc); + + *fcc = cpystr(fcc_got); + } + else if(a && a->host){ /* not group syntax */ + if(*fcc) + fs_give((void **)fcc); + + if(!tmp) + tmp = (char *)fs_get((size_t)200); + + if((ps_global->fcc_rule == FCC_RULE_RECIP || + ps_global->fcc_rule == FCC_RULE_NICK_RECIP) && + get_uname(a ? a->mailbox : NULL, tmp, 200)) + *fcc = cpystr(tmp); + else + *fcc = cpystr(ps_global->VAR_DEFAULT_FCC); + } + else{ /* first addr is group syntax */ + if(!*fcc) + *fcc = cpystr(ps_global->VAR_DEFAULT_FCC); + /* else, leave it alone */ + } + + if(fcc_got) + fs_give((void **)&fcc_got); + } + + if(a) + mail_free_address(&a); + + if(tmp) + fs_give((void **)&tmp); + + if(wp_exit) + return -1; + + return 0; +} + + +/* + * Expand an address string against the address books, local names, and domain. + * + * Args: to -- this is either an address string to parse (one or more + * address strings separated by commas) or it is an + * AdrBk_Entry, in which case it refers to a single addrbook + * entry. If it is an abe, then it is treated the same as + * if the nickname of this entry was passed in and we + * looked it up in the addrbook, except that it doesn't + * actually have to have a non-null nickname. + * userdomain -- domain the user is in + * localdomain -- domain of the password file (usually same as userdomain) + * loop_detected -- pointer to an int we set if we detect a loop in the + * address books (or a duplicate in a list) + * fcc -- Returned value of fcc for first addr in a_string + * did_set -- expand_address set the fcc (need this in case somebody + * sets fcc explicitly to a value equal to default-fcc) + * simple_verify -- don't add list full names or expand 2nd level lists + * + * Result: An adrlist of expanded addresses is returned + * + * If the localdomain is NULL, then no lookup against the password file will + * be done. + */ +ADDRESS * +expand_address(BuildTo to, char *userdomain, char *localdomain, int *loop_detected, + char **fcc, int *did_set, char **lcc, char **error, int recursing, + int simple_verify, int *mangled) +{ + size_t domain_length, length; + ADDRESS *adr, *a, *a_tail, *adr2, *a2, *a_temp, *wp_a; + AdrBk_Entry *abe, *abe2; + char *list, *l1, **l2; + char *tmp_a_string, *q; + BuildTo bldto; + static char *fakedomain; + + dprint((9, "- expand_address - (%s)\n", + (to.type == Str) ? (to.arg.str ? to.arg.str : "nul") + : (to.arg.abe->nickname ? to.arg.abe->nickname + : "no nick"))); + /* + * We use the domain "@" to detect an unqualified address. If it comes + * back from rfc822_parse_adrlist with the host part set to "@", then + * we know it must have been unqualified (so we should look it up in the + * addressbook). Later, we also use a c-client hack. If an ADDRESS has + * a host part that begins with @ then rfc822_write_address() + * will write only the local part and leave off the @domain part. + * + * We also malloc enough space here so that we can strcpy over host below. + */ + domain_length = MAX(localdomain!=NULL ? strlen(localdomain) : (size_t)0, + userdomain!=NULL ? strlen(userdomain) : (size_t)0); + if(!recursing){ + fakedomain = (char *)fs_get(domain_length + 1); + memset((void *)fakedomain, '@', domain_length); + fakedomain[domain_length] = '\0'; + } + + adr = NULL; + + if(to.type == Str){ + /* rfc822_parse_adrlist feels free to destroy input so send copy */ + tmp_a_string = cpystr(to.arg.str); + /* remove trailing comma */ + for(q = tmp_a_string + strlen(tmp_a_string) - 1; + q >= tmp_a_string && (*q == SPACE || *q == ','); + q--) + *q = '\0'; + + if(as.n_impl) + mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr); + + rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain); + + if(as.n_impl) + mail_parameters(NIL, SET_PARSEPHRASE, NULL); + + /* + * Short circuit the process if there was a parsing error. + */ + if(!recursing && ps_global->c_client_error[0] != '\0') + mail_free_address(&adr); + + fs_give((void **)&tmp_a_string); + } + else{ + if(!to.arg.abe || + (to.arg.abe->tag == Single && + (!to.arg.abe->addr.addr || to.arg.abe->addr.addr[0] == '\0')) || + (to.arg.abe->tag == List && + (!to.arg.abe->addr.list || !to.arg.abe->addr.list[0] || + to.arg.abe->addr.list[0][0] == '\0'))){ + adr = NULL; + } + else{ + /* if we've already looked it up, fake an adr */ + adr = mail_newaddr(); + adr->mailbox = cpystr(to.arg.abe->nickname); + adr->host = cpystr(fakedomain); + } + } + + for(a = adr, a_tail = adr; a;){ + + /* start or end of c-client group syntax */ + if(!a->host){ + a_tail = a; + a = a->next; + continue; + } + else if(a->host[0] != '@'){ + /* Already fully qualified hostname */ + a_tail = a; + a = a->next; + } + else{ + /* + * Hostname is "@" indicating name wasn't qualified. + * Need to look up in address book, and the password file. + * If no match then fill in the local domain for host. + */ + if(to.type == Str) + abe = adrbk_lookup_with_opens_by_nick(a->mailbox, + !recursing, + (int *)NULL, -1); + else + abe = to.arg.abe; + + if(simple_verify && abe == NULL){ + /*--- Move to next address in list -----*/ + a_tail = a; + a = a->next; + } + else if(abe == NULL){ + WP_ERR_S wp_err; + + if(F_OFF(F_COMPOSE_REJECTS_UNQUAL, ps_global)){ + if(localdomain != NULL && a->personal == NULL){ + /* lookup in passwd file for local full name */ + a->personal = local_name_lookup(a->mailbox); + /* we know a->host is long enough for localdomain */ + if(a->personal){ + /* we know a->host is long enough for userdomain */ + strncpy(a->host, localdomain, domain_length+1); + a->host[domain_length] = '\0'; + } + } + } + + /* + * Didn't find it in address book or password + * file, try white pages. + */ + memset(&wp_err, 0, sizeof(wp_err)); + wp_err.mangled = mangled; + if(!wp_exit && a->personal == NULL && + (wp_a = wp_lookups(a->mailbox, &wp_err, recursing))){ + if(wp_a->mailbox && wp_a->mailbox[0] && + wp_a->host && wp_a->host[0]){ + a->personal = wp_a->personal; + if(a->adl)fs_give((void **)&a->adl); + a->adl = wp_a->adl; + if(a->mailbox)fs_give((void **)&a->mailbox); + a->mailbox = wp_a->mailbox; + if(a->host)fs_give((void **)&a->host); + a->host = wp_a->host; + } + + fs_give((void **)&wp_a); + } + + if(wp_err.error){ + /* + * If wp_err has already been displayed long enough + * just get rid of it. Otherwise, try to fit it in + * with any other error messages we have had. + * In that case we may display error messages in a + * weird order. We'll have to see if this is a problem + * in real life. + */ + if(status_message_remaining() && error && !wp_exit){ + if(*error){ + q_status_message(SM_ORDER, 3, 5, *error); + display_message('x'); + fs_give((void **)error); + } + + *error = wp_err.error; + } + else + fs_give((void **)&wp_err.error); + } + + /* still haven't found it */ + if(a->host[0] == '@' && !wp_nobail){ + int space_phrase; + + /* + * Figure out if there is a space in the mailbox so + * that user probably meant it to resolve on the + * directory server. + */ + space_phrase = (a->mailbox && strindex(a->mailbox, SPACE)); + + if(!wp_nobail && (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global) || + (space_phrase && as.n_impl))){ + char ebuf[200]; + + /* TRANSLATORS: The first %s is a mailbox, the second is either + directory or addressbook */ + snprintf(ebuf, sizeof(ebuf), _("Address for \"%s\" not in %s"), + a->mailbox, + (space_phrase && as.n_impl) ? _("directory") + : _("addressbook")); + + if(error){ + /* display previous error and add new one */ + if(*error){ + if(!wp_exit){ + q_status_message(SM_ORDER, 3, 5, *error); + display_message('x'); + } + + fs_give((void **)error); + } + + if(!wp_exit) + *error = cpystr(ebuf); + } + + if(!wp_exit) + strncpy(ps_global->c_client_error, ebuf, 200); + + if(!recursing) + fs_give((void **)&fakedomain); + + if(adr) + mail_free_address(&adr); + + return(adr); + } + else if(wp_err.wp_err_occurred){ + if(!recursing){ + if(error && *error && !wp_exit) + strncpy(ps_global->c_client_error, *error, sizeof(ps_global->c_client_error)); + else + strncpy(ps_global->c_client_error, " ", sizeof(ps_global->c_client_error)); + + ps_global->c_client_error[sizeof(ps_global->c_client_error)-1] = '\0'; + + fs_give((void **)&fakedomain); + if(adr) + mail_free_address(&adr); + + return(adr); + } + } + else{ + /* we know a->host is long enough for userdomain */ + strncpy(a->host, userdomain, domain_length+1); + a->host[domain_length] = '\0'; + } + } + + /*--- Move to next address in list -----*/ + a_tail = a; + a = a->next; + + } + /* expand first list, but not others if simple_verify */ + else if(abe->tag == List && simple_verify && recursing){ + /*--- Move to next address in list -----*/ + a_tail = a; + a = a->next; + } + else{ + /* + * There was a match in the address book. We have to do a lot + * here because the item from the address book might be a + * distribution list. Treat the string just like an address + * passed in to parse and recurse on it. Then combine + * the personal names from address book. Lastly splice + * result into address list being processed + */ + + /* first addr in list and fcc needs to be filled in */ + if(!recursing && a == adr && fcc && !(*did_set & FCC_SET)){ + /* + * Easy case for fcc. This is a nickname that has + * an fcc associated with it. + */ + if(abe->fcc && abe->fcc[0]){ + if(*fcc) + fs_give((void **)fcc); + + if(!strcmp(abe->fcc, "\"\"")) + *fcc = cpystr(""); + else + *fcc = cpystr(abe->fcc); + + /* + * After we expand the list, we no longer remember + * that it came from this address book entry, so + * we wouldn't be able to set the fcc again based + * on the result. This tells our caller to remember + * that for us. + */ + *did_set |= (FCC_SET | FCC_NOREPO); + } + /* + * else if fcc-rule=fcc-by-nickname, use that + */ + else if(abe->nickname && abe->nickname[0] && + (ps_global->fcc_rule == FCC_RULE_NICK || + ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){ + if(*fcc) + fs_give((void **)fcc); + + *fcc = cpystr(abe->nickname); + /* + * After we expand the list, we no longer remember + * that it came from this address book entry, so + * we wouldn't be able to set the fcc again based + * on the result. This tells our caller to remember + * that for us. + */ + *did_set |= (FCC_SET | FCC_NOREPO); + } + } + + /* lcc needs to be filled in */ + if(a == adr && + lcc && + (!*lcc || !**lcc)){ + ADDRESS *atmp; + char *tmp; + + /* return fullname for To line */ + if(abe->fullname && *abe->fullname){ + RFC822BUFFER rbuf; + size_t l, len; + + if(*lcc) + fs_give((void **)lcc); + + atmp = mail_newaddr(); + atmp->mailbox = cpystr(abe->fullname); + len = est_size(atmp); + tmp = (char *) fs_get(len * sizeof(char)); + tmp[0] = '\0'; + /* write the phrase with quoting */ + rbuf.f = dummy_soutr; + rbuf.s = NULL; + rbuf.beg = tmp; + rbuf.cur = tmp; + rbuf.end = tmp+len-1; + rfc822_output_address_list(&rbuf, atmp, 0L, NULL); + *rbuf.cur = '\0'; + l = strlen(tmp)+1; + *lcc = (char *) fs_get((l+1) * sizeof(char)); + strncpy(*lcc, tmp, l); + (*lcc)[l] = '\0'; + strncat(*lcc, ";", l+1-strlen(*lcc)-1); + (*lcc)[l] = '\0'; + mail_free_address(&atmp); + fs_give((void **)&tmp); + *did_set |= (LCC_SET | LCC_NOREPO); + } + } + + if(recursing && abe->referenced){ + /*---- caught an address loop! ----*/ + fs_give(((void **)&a->host)); + (*loop_detected)++; + a->host = cpystr("***address-loop-in-addressbooks***"); + continue; + } + + abe->referenced++; /* For address loop detection */ + if(abe->tag == List){ + length = 0; + for(l2 = abe->addr.list; *l2; l2++) + length += (strlen(*l2) + 1); + + list = (char *)fs_get(length + 1); + list[0] = '\0'; + l1 = list; + for(l2 = abe->addr.list; *l2; l2++){ + if(l1 != list && length+1-(l1-list) > 0) + *l1++ = ','; + + strncpy(l1, *l2, length+1-(l1-list)); + if(l1 > &list[length]) + l1 = &list[length]; + + list[length] = '\0'; + + l1 += strlen(l1); + } + + bldto.type = Str; + bldto.arg.str = list; + adr2 = expand_address(bldto, userdomain, localdomain, + loop_detected, fcc, did_set, + lcc, error, 1, simple_verify, + mangled); + fs_give((void **)&list); + } + else if(abe->tag == Single){ + if(strucmp(abe->addr.addr, a->mailbox)){ + bldto.type = Str; + bldto.arg.str = abe->addr.addr; + adr2 = expand_address(bldto, userdomain, + localdomain, loop_detected, + fcc, did_set, lcc, + error, 1, simple_verify, + mangled); + } + else{ + /* + * A loop within plain single entry is ignored. + * Set up so later code thinks we expanded. + */ + adr2 = mail_newaddr(); + adr2->mailbox = cpystr(abe->addr.addr); + adr2->host = cpystr(userdomain); + adr2->adl = cpystr(a->adl); + } + } + + abe->referenced--; /* Janet Jackson <janet@dialix.oz.au> */ + if(adr2 == NULL){ + /* expanded to nothing, hack out of list */ + a_temp = a; + if(a == adr){ + adr = a->next; + a = adr; + a_tail = adr; + } + else{ + a_tail->next = a->next; + a = a->next; + } + + a_temp->next = NULL; /* So free won't do whole list */ + mail_free_address(&a_temp); + continue; + } + + /* + * Personal names: If the expanded address has a personal + * name and the address book entry is a list with a fullname, + * tack the full name from the address book on in front. + * This mainly occurs with a distribution list where the + * list has a full name, and the first person in the list also + * has a full name. + * + * This algorithm doesn't work very well if lists are + * included within lists, but it's not clear what would + * be better. + */ + if(abe->fullname && abe->fullname[0]){ + if(adr2->personal && adr2->personal[0]){ + if(abe->tag == List){ + /* combine list name and existing name */ + char *name; + + if(!simple_verify){ + size_t l; + + l = strlen(adr2->personal) + strlen(abe->fullname) + 4; + name = (char *) fs_get((l+1) * sizeof(char)); + snprintf(name, l+1, "%s -- %s", abe->fullname, + adr2->personal); + fs_give((void **)&adr2->personal); + adr2->personal = name; + } + } + else{ + /* replace with nickname fullname */ + fs_give((void **)&adr2->personal); + adr2->personal = adrbk_formatname(abe->fullname, + NULL, NULL); + } + } + else{ + if(abe->tag != List || !simple_verify){ + if(adr2->personal) + fs_give((void **)&adr2->personal); + + adr2->personal = adrbk_formatname(abe->fullname, + NULL, NULL); + } + } + } + + /* splice new list into old list and remove replaced addr */ + for(a2 = adr2; a2->next != NULL; a2 = a2->next) + ;/* do nothing */ + + a2->next = a->next; + if(a == adr) + adr = adr2; + else + a_tail->next = adr2; + + /* advance to next item, and free replaced ADDRESS */ + a_tail = a2; + a_temp = a; + a = a->next; + a_temp->next = NULL; /* So free won't do whole list */ + mail_free_address(&a_temp); + } + } + + if((a_tail == adr && fcc && !(*did_set & FCC_SET)) + || !a_tail->personal) + /* + * This looks for the addressbook entry that matches the given + * address. It looks through all the addressbooks + * looking for an exact match and then returns that entry. + */ + abe2 = address_to_abe(a_tail); + else + abe2 = NULL; + + /* + * If there is no personal name yet but we found the address in + * an address book, then we take the fullname from that address + * book entry and use it. One consequence of this is that if I + * have an address book entry with address hubert@cac.washington.edu + * and a fullname of Steve Hubert, then there is no way I can + * send mail to hubert@cac.washington.edu without having the + * personal name filled in for me. + */ + if(!a_tail->personal && abe2 && abe2->fullname && abe2->fullname[0]) + a_tail->personal = adrbk_formatname(abe2->fullname, NULL, NULL); + + /* if it's first addr in list and fcc hasn't been set yet */ + if(!recursing && a_tail == adr && fcc && !(*did_set & FCC_SET)){ + if(abe2 && abe2->fcc && abe2->fcc[0]){ + if(*fcc) + fs_give((void **)fcc); + + if(!strcmp(abe2->fcc, "\"\"")) + *fcc = cpystr(""); + else + *fcc = cpystr(abe2->fcc); + + *did_set |= FCC_SET; + } + else if(abe2 && abe2->nickname && abe2->nickname[0] && + (ps_global->fcc_rule == FCC_RULE_NICK || + ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){ + if(*fcc) + fs_give((void **)fcc); + + *fcc = cpystr(abe2->nickname); + *did_set |= FCC_SET; + } + } + + /* + * Lcc needs to be filled in. + * Bug: if ^T select was used to put the list in the lcc field, then + * the list will have been expanded already and the fullname for + * the list will be mixed with the initial fullname in the list, + * and we don't have anyway to tell them apart. We could look for + * the --. We could change expand_address so it doesn't combine + * those two addresses. + */ + if(adr && + a_tail == adr && + lcc && + (!*lcc || !**lcc)){ + if(adr->personal){ + ADDRESS *atmp; + char *tmp; + RFC822BUFFER rbuf; + size_t l, len; + + if(*lcc) + fs_give((void **)lcc); + + atmp = mail_newaddr(); + atmp->mailbox = cpystr(adr->personal); + len = est_size(atmp); + tmp = (char *) fs_get(len * sizeof(char)); + tmp[0] = '\0'; + /* write the phrase with quoting */ + rbuf.f = dummy_soutr; + rbuf.s = NULL; + rbuf.beg = tmp; + rbuf.cur = tmp; + rbuf.end = tmp+len-1; + rfc822_output_address_list(&rbuf, atmp, 0L, NULL); + *rbuf.cur = '\0'; + l = strlen(tmp)+1; + *lcc = (char *) fs_get((l+1) * sizeof(char)); + strncpy(*lcc, tmp, l); + (*lcc)[l] = '\0'; + strncat(*lcc, ";", l+1-strlen(*lcc)-1); + (*lcc)[l] = '\0'; + mail_free_address(&atmp); + fs_give((void **)&tmp); + *did_set |= (LCC_SET | LCC_NOREPO); + } + } + } + + if(!recursing) + fs_give((void **)&fakedomain); + + return(adr); +} + + +/* + * This is a call back from rfc822_parse_adrlist. If we return NULL then + * it does its regular parsing. However, if we return and ADDRESS then it + * returns that address to us. + * + * We want a phrase with no address to be parsed just like a nickname would + * be in expand_address. That is, the phrase gets put in the mailbox name + * and the host is set to the fakedomain. Then expand_address will try to + * look that up using wp_lookup(). + * + * Args phrase -- The start of the phrase of the address + * end -- The first character after the phrase + * host -- The defaulthost that was passed to rfc822_parse_adrlist + * + * Returns An allocated address, or NULL. + */ +ADDRESS * +massage_phrase_addr(char *phrase, char *end, char *host) +{ + ADDRESS *adr = NULL; + size_t size; + + if((size = end - phrase) > 0){ + char *mycopy; + + mycopy = (char *)fs_get((size+1) * sizeof(char)); + strncpy(mycopy, phrase, size); + mycopy[size] = '\0'; + removing_trailing_white_space(mycopy); + + /* + * If it is quoted we want to leave it alone. It will be treated + * like an atom in parse_adrlist anyway, which is what we want to + * have happen, and we'd have to remove the quotes ourselves here + * if we did it. The problem then is that we don't know if we + * removed the quotes here or they just weren't there in the + * first place. + */ + if(*mycopy == '"' && mycopy[strlen(mycopy)-1] == '"') + fs_give((void **)&mycopy); + else{ + adr = mail_newaddr(); + adr->mailbox = mycopy; + adr->host = cpystr(host); + } + } + + return(adr); +} + + +/* + * Run through the adrlist "adr" and strip off any enclosing quotes + * around personal names. That is, change "Joe L. Normal" to + * Joe L. Normal. + */ +void +strip_personal_quotes(struct mail_address *adr) +{ + int len; + register char *p, *q; + + while(adr){ + if(adr->personal){ + len = strlen(adr->personal); + if(len > 1 + && adr->personal[0] == '"' + && adr->personal[len-1] == '"'){ + adr->personal[len-1] = '\0'; + p = adr->personal; + q = p + 1; + while((*p++ = *q++) != '\0') + ; + } + } + + adr = adr->next; + } +} + + +static char *last_fcc_used; +/* + * Returns alloc'd fcc. + */ +char * +get_fcc(char *fcc_arg) +{ + char *fcc; + + /* + * Use passed in arg unless it is the same as default (and then + * may use that anyway below). + */ + if(fcc_arg && strcmp(fcc_arg, ps_global->VAR_DEFAULT_FCC)) + fcc = cpystr(fcc_arg); + else{ + if(ps_global->fcc_rule == FCC_RULE_LAST && last_fcc_used) + fcc = cpystr(last_fcc_used); + else if(ps_global->fcc_rule == FCC_RULE_CURRENT + && ps_global->mail_stream + && !sp_flagged(ps_global->mail_stream, SP_INBOX) + && !IS_NEWS(ps_global->mail_stream) + && ps_global->cur_folder + && ps_global->cur_folder[0]){ + CONTEXT_S *cntxt = ps_global->context_current; + char *rs = NULL; + + if(((cntxt->use) & CNTXT_SAVEDFLT)) + rs = ps_global->cur_folder; + else + rs = ps_global->mail_stream->mailbox; + + fcc = cpystr((rs&&*rs) ? rs : ps_global->VAR_DEFAULT_FCC); + } + else + fcc = cpystr(ps_global->VAR_DEFAULT_FCC); + } + + return(fcc); +} + + +/* + * Save the fcc for use with next composition. + */ +void +set_last_fcc(char *fcc) +{ + size_t l; + + if(fcc){ + if(!last_fcc_used) + last_fcc_used = cpystr(fcc); + else if(strcmp(last_fcc_used, fcc)){ + if((l=strlen(last_fcc_used)) >= strlen(fcc)){ + strncpy(last_fcc_used, fcc, l+1); + last_fcc_used[l] = '\0'; + } + else{ + fs_give((void **)&last_fcc_used); + last_fcc_used = cpystr(fcc); + } + } + } +} + + +/* + * Figure out what the fcc is based on the given to address. + * + * Returns an allocated copy of the fcc, to be freed by the caller, or NULL. + */ +char * +get_fcc_based_on_to(struct mail_address *to) +{ + ADDRESS *next_addr; + char *bufp, *fcc; + BuildTo bldto; + size_t len; + + if(!to || !to->host || to->host[0] == '.') + return(NULL); + + /* pick off first address */ + next_addr = to->next; + to->next = NULL; + len = est_size(to); + bufp = (char *) fs_get(len * sizeof(char)); + bufp[0] = '\0'; + + bldto.type = Str; + bldto.arg.str = cpystr(addr_string(to, bufp, len)); + + fs_give((void **)&bufp); + to->next = next_addr; + + fcc = NULL; + + (void) build_address_internal(bldto, NULL, NULL, &fcc, NULL, NULL, NULL, 0, NULL); + + if(bldto.arg.str) + fs_give((void **) &bldto.arg.str); + + return(fcc); +} + + +/* + * Free storage in headerentry.bldr_private. + */ +void +free_privatetop(PrivateTop **pt) +{ + if(pt && *pt){ + if((*pt)->affector) + fs_give((void **)&(*pt)->affector); + + fs_give((void **)pt); + } +} |