diff options
Diffstat (limited to 'pith/ablookup.c')
-rw-r--r-- | pith/ablookup.c | 1629 |
1 files changed, 1629 insertions, 0 deletions
diff --git a/pith/ablookup.c b/pith/ablookup.c new file mode 100644 index 00000000..63ff9133 --- /dev/null +++ b/pith/ablookup.c @@ -0,0 +1,1629 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: ablookup.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2009 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 + * + * ======================================================================== + */ + +#include "../pith/headers.h" /* for os-dep and pith defs/includes */ +#include "../pith/debug.h" +#include "../pith/util.h" +#include "../pith/adrbklib.h" +#include "../pith/copyaddr.h" +#include "../pith/status.h" +#include "../pith/conf.h" +#include "../pith/search.h" +#include "../pith/abdlc.h" +#include "../pith/addrstring.h" +#include "../pith/ablookup.h" +#include "../pith/options.h" +#include "../pith/takeaddr.h" +#ifdef ENABLE_LDAP +#include "../pith/ldap.h" +#endif /* ENABLE_LDAP */ + + +/* + * Internal prototypes + */ +int addr_is_in_addrbook(PerAddrBook *, ADDRESS *); +ABOOK_ENTRY_S *adrbk_list_of_possible_trie_completions(AdrBk_Trie *, AdrBk *, char *, unsigned); +void gather_abook_entry_list(AdrBk *, AdrBk_Trie *, char *, ABOOK_ENTRY_S **, unsigned); +void add_addr_to_return_list(ADDRESS *, unsigned, char *, int, COMPLETE_S **); + + +/* + * Given an address, try to find the first nickname that goes with it. + * Copies that nickname into the passed in buffer, which is assumed to + * be at least MAX_NICKNAME+1 in length. Returns NULL if it can't be found, + * else it returns a pointer to the buffer. + */ +char * +get_nickname_from_addr(struct mail_address *adr, char *buffer, size_t buflen) +{ + AdrBk_Entry *abe; + char *ret = NULL; + SAVE_STATE_S state; + jmp_buf save_jmp_buf; + int *save_nesting_level; + ADDRESS *copied_adr; + + state.savep = NULL; + state.stp = NULL; + state.dlc_to_warp_to = NULL; + copied_adr = copyaddr(adr); + + if(ps_global->remote_abook_validity > 0) + (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0); + + save_nesting_level = cpyint(ab_nesting_level); + memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf)); + if(setjmp(addrbook_changed_unexpectedly)){ + ret = NULL; + if(state.savep) + fs_give((void **)&(state.savep)); + if(state.stp) + fs_give((void **)&(state.stp)); + if(state.dlc_to_warp_to) + fs_give((void **)&(state.dlc_to_warp_to)); + + q_status_message(SM_ORDER, 3, 5, _("Resetting address book...")); + dprint((1, + "RESETTING address book... get_nickname_from_addr()!\n")); + addrbook_reset(); + ab_nesting_level = *save_nesting_level; + } + + ab_nesting_level++; + init_ab_if_needed(); + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_SAVE, &state); + + abe = address_to_abe(copied_adr); + + if(copied_adr) + mail_free_address(&copied_adr); + + if(abe && abe->nickname && abe->nickname[0]){ + strncpy(buffer, abe->nickname, buflen-1); + buffer[buflen-1] = '\0'; + ret = buffer; + } + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_RESTORE, &state); + + memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf)); + ab_nesting_level--; + + if(save_nesting_level) + fs_give((void **)&save_nesting_level); + + return(ret); +} + + +/* + * Given an address, try to find the first fcc that goes with it. + * Copies that fcc into the passed in buffer. + * Returns NULL if it can't be found, else it returns a pointer to the buffer. + */ +char * +get_fcc_from_addr(struct mail_address *adr, char *buffer, size_t buflen) +{ + AdrBk_Entry *abe; + char *ret = NULL; + SAVE_STATE_S state; + jmp_buf save_jmp_buf; + int *save_nesting_level; + ADDRESS *copied_adr; + + state.savep = NULL; + state.stp = NULL; + state.dlc_to_warp_to = NULL; + copied_adr = copyaddr(adr); + + if(ps_global->remote_abook_validity > 0) + (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0); + + save_nesting_level = cpyint(ab_nesting_level); + memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf)); + if(setjmp(addrbook_changed_unexpectedly)){ + ret = NULL; + if(state.savep) + fs_give((void **)&(state.savep)); + if(state.stp) + fs_give((void **)&(state.stp)); + if(state.dlc_to_warp_to) + fs_give((void **)&(state.dlc_to_warp_to)); + + q_status_message(SM_ORDER, 3, 5, _("Resetting address book...")); + dprint((1, + "RESETTING address book... get_fcc_from_addr()!\n")); + addrbook_reset(); + ab_nesting_level = *save_nesting_level; + } + + ab_nesting_level++; + init_ab_if_needed(); + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_SAVE, &state); + + abe = address_to_abe(copied_adr); + + if(copied_adr) + mail_free_address(&copied_adr); + + if(abe && abe->fcc && abe->fcc[0]){ + if(!strcmp(abe->fcc, "\"\"")) + buffer[0] = '\0'; + else{ + strncpy(buffer, abe->fcc, buflen-1); + buffer[buflen-1] = '\0'; + } + + ret = buffer; + } + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_RESTORE, &state); + + memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf)); + ab_nesting_level--; + + if(save_nesting_level) + fs_give((void **)&save_nesting_level); + + return(ret); +} + + +/* + * Given an address, try to find the first address book entry that + * matches it and return all the other fields in the passed in pointers. + * Caller needs to free the four fields. + * Returns -1 if it can't be found, 0 if it is found. + */ +int +get_contactinfo_from_addr(struct mail_address *adr, char **nick, char **full, char **fcc, char **comment) +{ + AdrBk_Entry *abe; + int ret = -1; + SAVE_STATE_S state; + jmp_buf save_jmp_buf; + int *save_nesting_level; + ADDRESS *copied_adr; + + state.savep = NULL; + state.stp = NULL; + state.dlc_to_warp_to = NULL; + copied_adr = copyaddr(adr); + + if(ps_global->remote_abook_validity > 0) + (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0); + + save_nesting_level = cpyint(ab_nesting_level); + memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf)); + if(setjmp(addrbook_changed_unexpectedly)){ + ret = -1; + if(state.savep) + fs_give((void **)&(state.savep)); + if(state.stp) + fs_give((void **)&(state.stp)); + if(state.dlc_to_warp_to) + fs_give((void **)&(state.dlc_to_warp_to)); + + q_status_message(SM_ORDER, 3, 5, _("Resetting address book...")); + dprint((1, + "RESETTING address book... get_contactinfo_from_addr()!\n")); + addrbook_reset(); + ab_nesting_level = *save_nesting_level; + } + + ab_nesting_level++; + init_ab_if_needed(); + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_SAVE, &state); + + abe = address_to_abe(copied_adr); + + if(copied_adr) + mail_free_address(&copied_adr); + + if(abe){ + if(nick && abe->nickname && abe->nickname[0]) + *nick = cpystr(abe->nickname); + + if(full && abe->fullname && abe->fullname[0]) + *full = cpystr(abe->fullname); + + if(fcc && abe->fcc && abe->fcc[0]) + *fcc = cpystr(abe->fcc); + + if(comment && abe->extra && abe->extra[0]) + *comment = cpystr(abe->extra); + + ret = 0; + } + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_RESTORE, &state); + + memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf)); + ab_nesting_level--; + + if(save_nesting_level) + fs_give((void **)&save_nesting_level); + + return(ret); +} + + +/* + * This is a very special-purpose routine. + * It implements the From or Reply-To address is in the Address Book + * part of Pattern matching. + */ +void +address_in_abook(MAILSTREAM *stream, SEARCHSET *searchset, + int inabook, PATTERN_S *abooks) +{ + char *savebits; + MESSAGECACHE *mc; + long i, count = 0L; + SEARCHSET *s, *ss; + ADDRESS *from, *reply_to, *sender, *to, *cc; + int is_there, adrbknum, *abooklist = NULL, positive_match; + PATTERN_S *pat; + PerAddrBook *pab; + ENVELOPE *e; + SAVE_STATE_S state; + jmp_buf save_jmp_buf; + int *save_nesting_level; + + if(!stream) + return; + + /* everything that matches remains a match */ + if(inabook == IAB_EITHER) + return; + + state.savep = NULL; + state.stp = NULL; + state.dlc_to_warp_to = NULL; + + /* + * This may call build_header_line recursively because we may be in + * build_header_line now. So we have to preserve and restore the + * sequence bits since we want to use them here. + */ + savebits = (char *) fs_get((stream->nmsgs+1) * sizeof(char)); + + for(i = 1L; i <= stream->nmsgs; i++){ + if((mc = mail_elt(stream, i)) != NULL){ + savebits[i] = mc->sequence; + mc->sequence = 0; + } + } + + /* + * Build a searchset so we can look at all the envelopes + * we need to look at but only those we need to look at. + * Everything with the searched bit set is still a + * possibility, so restrict to that set. + */ + + for(s = searchset; s; s = s->next) + for(i = s->first; i <= s->last; i++) + if(i > 0L && i <= stream->nmsgs + && (mc=mail_elt(stream, i)) && mc->searched){ + mc->sequence = 1; + count++; + } + + ss = build_searchset(stream); + + /* + * We save the address book state here so we don't have to do it + * each time through the loop below. + */ + if(ss){ + if(ps_global->remote_abook_validity > 0) + (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0); + + save_nesting_level = cpyint(ab_nesting_level); + memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf)); + if(setjmp(addrbook_changed_unexpectedly)){ + if(state.savep) + fs_give((void **)&(state.savep)); + if(state.stp) + fs_give((void **)&(state.stp)); + if(state.dlc_to_warp_to) + fs_give((void **)&(state.dlc_to_warp_to)); + + q_status_message(SM_ORDER, 3, 5, _("Resetting address book...")); + dprint((1, + "RESETTING address book... address_in_abook()!\n")); + addrbook_reset(); + ab_nesting_level = *save_nesting_level; + } + + ab_nesting_level++; + init_ab_if_needed(); + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_SAVE, &state); + + if(as.n_addrbk > 0){ + abooklist = (int *) fs_get(as.n_addrbk * sizeof(*abooklist)); + memset((void *) abooklist, 0, as.n_addrbk * sizeof(*abooklist)); + } + + if(abooklist) + switch(inabook & IAB_TYPE_MASK){ + case IAB_YES: + case IAB_NO: + for(adrbknum = 0; adrbknum < as.n_addrbk; adrbknum++) + abooklist[adrbknum] = 1; + + break; + + case IAB_SPEC_YES: + case IAB_SPEC_NO: + /* figure out which address books we're going to look in */ + for(adrbknum = 0; adrbknum < as.n_addrbk; adrbknum++){ + pab = &as.adrbks[adrbknum]; + /* + * For each address book, check all of the address books + * in the pattern's list to see if they are it. + */ + for(pat = abooks; pat; pat = pat->next){ + if(!strcmp(pab->abnick, pat->substring) + || !strcmp(pab->filename, pat->substring)){ + abooklist[adrbknum] = 1; + break; + } + } + } + + break; + } + + switch(inabook & IAB_TYPE_MASK){ + case IAB_YES: + case IAB_SPEC_YES: + positive_match = 1; + break; + + case IAB_NO: + case IAB_SPEC_NO: + positive_match = 0; + break; + } + } + + if(count){ + SEARCHSET **sset; + + mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count); + + /* + * This causes the lookahead to fetch precisely + * the messages we want (in the searchset) instead + * of just fetching the next 20 sequential + * messages. If the searching so far has caused + * a sparse searchset in a large mailbox, the + * difference can be substantial. + * This resets automatically after the first fetch. + */ + sset = (SEARCHSET **) mail_parameters(stream, + GET_FETCHLOOKAHEAD, + (void *) stream); + if(sset) + *sset = ss; + } + + for(s = ss; s; s = s->next){ + for(i = s->first; i <= s->last; i++){ + if(i <= 0L || i > stream->nmsgs) + continue; + + e = pine_mail_fetchenvelope(stream, i); + + from = e ? e->from : NULL; + reply_to = e ? e->reply_to : NULL; + sender = e ? e->sender : NULL; + to = e ? e->to : NULL; + cc = e ? e->cc : NULL; + + is_there = 0; + for(adrbknum = 0; !is_there && adrbknum < as.n_addrbk; adrbknum++){ + if(!abooklist[adrbknum]) + continue; + + pab = &as.adrbks[adrbknum]; + is_there = ((inabook & IAB_FROM) && addr_is_in_addrbook(pab, from)) + || ((inabook & IAB_REPLYTO) && addr_is_in_addrbook(pab, reply_to)) + || ((inabook & IAB_SENDER) && addr_is_in_addrbook(pab, sender)) + || ((inabook & IAB_TO) && addr_is_in_addrbook(pab, to)) + || ((inabook & IAB_CC) && addr_is_in_addrbook(pab, cc)); + } + + if(positive_match){ + /* + * We matched up until now. If it isn't there, then it + * isn't a match. If it is there, leave the searched bit + * set. + */ + if(!is_there && i > 0L && i <= stream->nmsgs && (mc = mail_elt(stream, i))) + mc->searched = NIL; + } + else{ + if(is_there && i > 0L && i <= stream->nmsgs && (mc = mail_elt(stream, i))) + mc->searched = NIL; + } + } + } + + if(ss){ + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_RESTORE, &state); + + memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf)); + ab_nesting_level--; + + if(save_nesting_level) + fs_give((void **)&save_nesting_level); + } + + /* restore sequence bits */ + for(i = 1L; i <= stream->nmsgs; i++) + if((mc = mail_elt(stream, i)) != NULL) + mc->sequence = savebits[i]; + + fs_give((void **) &savebits); + + if(ss) + mail_free_searchset(&ss); + + if(abooklist) + fs_give((void **) &abooklist); +} + + +/* + * Given two addresses, check to see if either is in the address book. + * Returns 1 if yes, 0 if not found. + */ +int +addr_is_in_addrbook(PerAddrBook *pab, struct mail_address *addr) +{ + AdrBk_Entry *abe = NULL; + int ret = 0; + char abuf[MAX_ADDR_FIELD+1]; + + if(!(pab && addr && addr->mailbox)) + return(ret); + + if(addr){ + strncpy(abuf, addr->mailbox, sizeof(abuf)-1); + abuf[sizeof(abuf)-1] = '\0'; + if(addr->host && addr->host[0]){ + strncat(abuf, "@", sizeof(abuf)-strlen(abuf)-1); + strncat(abuf, addr->host, sizeof(abuf)-strlen(abuf)-1); + } + + if(pab->ostatus != Open && pab->ostatus != NoDisplay) + init_abook(pab, NoDisplay); + + abe = adrbk_lookup_by_addr(pab->address_book, abuf, + (adrbk_cntr_t *) NULL); + } + + ret = abe ? 1 : 0; + + return(ret); +} + + +/* + * Look through addrbooks for nickname, opening addrbooks first + * if necessary. It is assumed that the caller will restore the + * state of the addrbooks if desired. + * + * Args: nickname -- the nickname to lookup + * clearrefs -- clear reference bits before lookup + * which_addrbook -- If matched, addrbook number it was found in. + * not_here -- If non-negative, skip looking in this abook. + * + * Results: A pointer to an AdrBk_Entry is returned, or NULL if not found. + * Stop at first match (so order of addrbooks is important). + */ +AdrBk_Entry * +adrbk_lookup_with_opens_by_nick(char *nickname, int clearrefs, int *which_addrbook, int not_here) +{ + AdrBk_Entry *abe = (AdrBk_Entry *)NULL; + int i; + PerAddrBook *pab; + + dprint((5, "- adrbk_lookup_with_opens_by_nick(%s) -\n", + nickname ? nickname : "?")); + + for(i = 0; i < as.n_addrbk; i++){ + + if(i == not_here) + continue; + + pab = &as.adrbks[i]; + + if(pab->ostatus != Open && pab->ostatus != NoDisplay) + init_abook(pab, NoDisplay); + + if(clearrefs) + adrbk_clearrefs(pab->address_book); + + abe = adrbk_lookup_by_nick(pab->address_book, + nickname, + (adrbk_cntr_t *)NULL); + if(abe) + break; + } + + if(abe && which_addrbook) + *which_addrbook = i; + + return(abe); +} + + +/* + * Find the addressbook entry that matches the argument address. + * Searches through all addressbooks looking for the match. + * Opens addressbooks if necessary. It is assumed that the caller + * will restore the state of the addrbooks if desired. + * + * Args: addr -- the address we're trying to match + * + * Returns: NULL -- no match found + * abe -- a pointer to the addrbook entry that matches + */ +AdrBk_Entry * +address_to_abe(struct mail_address *addr) +{ + register PerAddrBook *pab; + int adrbk_number; + AdrBk_Entry *abe = NULL; + char *abuf = NULL; + + if(!(addr && addr->mailbox)) + return (AdrBk_Entry *)NULL; + + abuf = (char *)fs_get((MAX_ADDR_FIELD + 1) * sizeof(char)); + + strncpy(abuf, addr->mailbox, MAX_ADDR_FIELD); + abuf[MAX_ADDR_FIELD] = '\0'; + if(addr->host && addr->host[0]){ + strncat(abuf, "@", MAX_ADDR_FIELD+1-1-strlen(abuf)); + strncat(abuf, addr->host, MAX_ADDR_FIELD+1-1-strlen(abuf)); + } + + /* for each addressbook */ + for(adrbk_number = 0; adrbk_number < as.n_addrbk; adrbk_number++){ + + pab = &as.adrbks[adrbk_number]; + + if(pab->ostatus != Open && pab->ostatus != NoDisplay) + init_abook(pab, NoDisplay); + + abe = adrbk_lookup_by_addr(pab->address_book, + abuf, + (adrbk_cntr_t *)NULL); + if(abe) + break; + } + + if(abuf) + fs_give((void **)&abuf); + + return(abe); +} + + +/* + * Turn an AdrBk_Entry into an address list + * + * Args: abe -- the AdrBk_Entry + * dl -- the corresponding dl + * abook -- which addrbook the abe is in (only used for type ListEnt) + * how_many -- The number of addresses is returned here + * + * Result: allocated address list or NULL + */ +ADDRESS * +abe_to_address(AdrBk_Entry *abe, AddrScrn_Disp *dl, AdrBk *abook, int *how_many) +{ + char *fullname, *tmp_a_string; + char *list, *l1, **l2; + char *fakedomain = "@"; + ADDRESS *addr = NULL; + size_t length; + int count = 0; + + if(!dl || !abe) + return(NULL); + + fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL; + + switch(dl->type){ + case Simple: + /* rfc822_parse_adrlist feels free to destroy input so send copy */ + tmp_a_string = cpystr(abe->addr.addr); + rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain); + + if(tmp_a_string) + fs_give((void **)&tmp_a_string); + + if(addr && fullname){ +#ifdef ENABLE_LDAP + if(strncmp(addr->mailbox, RUN_LDAP, LEN_RL) != 0) +#endif /* ENABLE_LDAP */ + { + if(addr->personal) + fs_give((void **)&addr->personal); + + addr->personal = adrbk_formatname(fullname, NULL, NULL); + } + } + + if(addr) + count++; + + break; + + case ListEnt: + /* rfc822_parse_adrlist feels free to destroy input so send copy */ + tmp_a_string = cpystr(listmem_from_dl(abook, dl)); + rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain); + if(tmp_a_string) + fs_give((void **)&tmp_a_string); + + if(addr) + count++; + + break; + + case ListHead: + length = 0; + for(l2 = abe->addr.list; *l2; l2++) + length += (strlen(*l2) + 1); + + list = (char *)fs_get(length + 1); + l1 = list; + for(l2 = abe->addr.list; *l2; l2++){ + if(l1 != list && length-(l1-list) > 0) + *l1++ = ','; + + strncpy(l1, *l2, length-(l1-list)); + list[length] = '\0'; + l1 += strlen(l1); + count++; + } + + rfc822_parse_adrlist(&addr, list, fakedomain); + if(list) + fs_give((void **)&list); + + break; + + default: + dprint((1, "default case in abe_to_address, shouldn't happen\n")); + break; + } + + if(how_many) + *how_many = count; + + return(addr); +} + + +/* + * Turn an AdrBk_Entry into a nickname (if it has a nickname) or a + * formatted addr_string which has been rfc1522 decoded. + * + * Args: abe -- the AdrBk_Entry + * dl -- the corresponding dl + * abook -- which addrbook the abe is in (only used for type ListEnt) + * + * Result: allocated string is returned + */ +char * +abe_to_nick_or_addr_string(AdrBk_Entry *abe, AddrScrn_Disp *dl, int abook_num) +{ + ADDRESS *addr; + char *a_string; + + if(!dl || !abe) + return(cpystr("")); + + if((dl->type == Simple || dl->type == ListHead) + && abe->nickname && abe->nickname[0]){ + char *fname; + int which_addrbook; + + /* + * We prefer to pass back the nickname since that allows the + * caller to keep track of which entry the address came from. + * This is useful in build_address so that the fcc line can + * be kept correct. However, if the nickname is also present in + * another addressbook then we have to be careful. If that other + * addressbook comes before this one then passing back the nickname + * will cause the wrong entry to get used. So check for that + * and pass back the addr_string in that case. + */ + if((fname = addr_lookup(abe->nickname, &which_addrbook, abook_num)) != NULL){ + fs_give((void **)&fname); + if(which_addrbook >= abook_num) + return(cpystr(abe->nickname)); + } + else + return(cpystr(abe->nickname)); + } + + addr = abe_to_address(abe, dl, as.adrbks[abook_num].address_book, NULL); + a_string = addr_list_string(addr, NULL, 0); /* always returns a string */ + if(addr) + mail_free_address(&addr); + + return(a_string); +} + + +/* + * Check to see if address is that of the current user running pine + * + * Args: a -- Address to check + * ps -- The pine_state structure + * + * Result: returns 1 if it matches, 0 otherwise. + * + * The mailbox must match the user name and the hostname must match. + * In matching the hostname, matches occur if the hostname in the address + * is blank, or if it matches the local hostname, or the full hostname + * with qualifying domain, or the qualifying domain without a specific host. + * Note, there is a very small chance that we will err on the + * non-conservative side here. That is, we may decide two addresses are + * the same even though they are different (because we do case-insensitive + * compares on the mailbox). That might cause a reply not to be sent to + * somebody because they look like they are us. This should be very, + * very rare. + * + * It is also considered a match if any of the addresses in alt-addresses + * matches a. The check there is simpler. It parses each address in + * the list, adding maildomain if there wasn't a domain, and compares + * mailbox and host in the ADDRESS's for equality. + */ +int +address_is_us(struct mail_address *a, struct pine *ps) +{ + char **t; + int ret; + char addrstr[500]; + + if(!a || a->mailbox == NULL || !ps) + ret = 0; + + /* at least LHS must match, but case-independent */ + else if(strucmp(a->mailbox, ps->VAR_USER_ID) == 0 + + && /* and hostname matches */ + + /* hostname matches if it's not there, */ + (a->host == NULL || + /* or if hostname and userdomain (the one user sets) match exactly, */ + ((ps->userdomain && a->host && strucmp(a->host,ps->userdomain) == 0) || + + /* + * or if(userdomain is either not set or it is set to be + * the same as the localdomain or hostname) and (the hostname + * of the address matches either localdomain or hostname) + */ + ((ps->userdomain == NULL || + strucmp(ps->userdomain, ps->localdomain) == 0 || + strucmp(ps->userdomain, ps->hostname) == 0) && + (strucmp(a->host, ps->hostname) == 0 || + strucmp(a->host, ps->localdomain) == 0))))) + ret = 1; + + /* + * If no match yet, check to see if it matches any of the alternate + * addresses the user has specified. + */ + else if(!ps_global->VAR_ALT_ADDRS || + !ps_global->VAR_ALT_ADDRS[0] || + !ps_global->VAR_ALT_ADDRS[0][0]) + ret = 0; /* none defined */ + + else{ + ret = 0; + if(a && a->host && a->mailbox) + snprintf(addrstr, sizeof(addrstr), "%s@%s", a->mailbox, a->host); + + for(t = ps_global->VAR_ALT_ADDRS; !ret && t[0] && t[0][0]; t++){ + char *alt; + regex_t reg; + int err; + char ebuf[200]; + + alt = (*t); + + if(F_ON(F_DISABLE_REGEX, ps_global) || !contains_regex_special_chars(alt)){ + ADDRESS *alt_addr; + char *alt2; + + alt2 = cpystr(alt); + alt_addr = NULL; + rfc822_parse_adrlist(&alt_addr, alt2, ps_global->maildomain); + if(alt2) + fs_give((void **) &alt2); + + if(address_is_same(a, alt_addr)) + ret = 1; + + if(alt_addr) + mail_free_address(&alt_addr); + } + else{ + /* treat alt as a regular expression */ + ebuf[0] = '\0'; + if(!(err=regcomp(®, alt, REG_ICASE | REG_NOSUB | REG_EXTENDED))){ + err = regexec(®, addrstr, 0, NULL, 0); + if(err == 0) + ret = 1; + else if(err != REG_NOMATCH){ + regerror(err, ®, ebuf, sizeof(ebuf)); + if(ebuf[0]) + dprint((2, "- address_is_us regexec error: %s (%s)", ebuf, alt)); + } + + regfree(®); + } + else{ + regerror(err, ®, ebuf, sizeof(ebuf)); + if(ebuf[0]) + dprint((2, "- address_is_us regcomp error: %s (%s)", ebuf, alt)); + } + } + } + } + + return(ret); +} + + +/* + * In an ad hoc way try to decide if str is meant to be a regular + * expression or not. Dot doesn't count * as regex stuff because + * we're worried about addresses. + * + * Returns 0 or 1 + */ +int +contains_regex_special_chars(char *str) +{ + char special_chars[] = {'*', '|', '+', '?', '{', '[', '^', '$', '\\', '\0'}; + char *c; + + if(!str) + return 0; + + /* + * If any of special_chars are in str consider it a regex expression. + */ + for(c = special_chars; *c; c++) + if(strindex(str, *c)) + return 1; + + return 0; +} + + +/* + * Compare the two addresses, and return true if they're the same, + * false otherwise + * + * Args: a -- First address for comparison + * b -- Second address for comparison + * + * Result: returns 1 if it matches, 0 otherwise. + */ +int +address_is_same(struct mail_address *a, struct mail_address *b) +{ + return(a && b && a->mailbox && b->mailbox && a->host && b->host + && strucmp(a->mailbox, b->mailbox) == 0 + && strucmp(a->host, b->host) == 0); +} + + +/* + * Returns nonzero if the two address book entries are equal. + * Returns zero if not equal. + */ +int +abes_are_equal(AdrBk_Entry *a, AdrBk_Entry *b) +{ + int result = 0; + char **alist, **blist; + char *addra, *addrb; + + if(a && b && + a->tag == b->tag && + ((!a->nickname && !b->nickname) || + (a->nickname && b->nickname && strcmp(a->nickname,b->nickname) == 0)) && + ((!a->fullname && !b->fullname) || + (a->fullname && b->fullname && strcmp(a->fullname,b->fullname) == 0)) && + ((!a->fcc && !b->fcc) || + (a->fcc && b->fcc && strcmp(a->fcc,b->fcc) == 0)) && + ((!a->extra && !b->extra) || + (a->extra && b->extra && strcmp(a->extra,b->extra) == 0))){ + + /* If we made it in here, they still might be equal. */ + if(a->tag == Single) + result = strcmp(a->addr.addr,b->addr.addr) == 0; + else{ + alist = a->addr.list; + blist = b->addr.list; + if(!alist && !blist) + result = 1; + else{ + /* compare the whole lists */ + addra = *alist; + addrb = *blist; + while(addra && addrb && strcmp(addra,addrb) == 0){ + alist++; + blist++; + addra = *alist; + addrb = *blist; + } + + if(!addra && !addrb) + result = 1; + } + } + } + + return(result); +} + + +/* + * Interface to address book lookups for callers outside or inside this file. + * + * Args: nickname -- The nickname to look up + * which_addrbook -- If matched, addrbook number it was found in. + * not_here -- If non-negative, skip looking in this abook. + * + * Result: returns NULL or the corresponding fullname. The fullname is + * allocated here so the caller must free it. + * + * This opens the address books if they haven't been opened and restores + * them to the state they were in upon entry. + */ +char * +addr_lookup(char *nickname, int *which_addrbook, int not_here) +{ + AdrBk_Entry *abe; + SAVE_STATE_S state; + char *fullname; + + dprint((9, "- addr_lookup(%s) -\n",nickname ? nickname : "nil")); + + init_ab_if_needed(); + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_SAVE, &state); + + abe = adrbk_lookup_with_opens_by_nick(nickname,0,which_addrbook,not_here); + + fullname = (abe && abe->fullname) ? cpystr(abe->fullname) : NULL; + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_RESTORE, &state); + + return(fullname); +} + + +/* + * Look in all of the address books for all of the possible entries + * that match the query string. The matches can be for the nickname, + * for the fullname, or for the address@host part of the address. + * All of the matches are at the starts of the strings, not a general + * substring match. This is not true anymore. Fullname matches can be + * at the start of the fullname or starting after a space in the fullname. + * If flags has ALC_INCLUDE_LDAP defined then LDAP + * entries are added to the end of the list. The LDAP queries are done + * only for those servers that have the 'impl' feature turned on, which + * means that lookups should be done implicitly. This feature also + * controls whether or not lookups should be done when typing carriage + * return (instead of this which is TAB). + * + * Args query -- What the user has typed so far + * + * Returns a list of possibilities for the given query string. + * + * Caller needs to free the answer. + */ +COMPLETE_S * +adrbk_list_of_completions(char *query, MAILSTREAM *stream, imapuid_t uid, int flags) +{ + int i; + SAVE_STATE_S state; + PerAddrBook *pab; + ABOOK_ENTRY_S *list, *list2, *biglist = NULL; + COMPLETE_S *return_list = NULL, *last_one_added = NULL, *new, *cp, *dp, *dprev; + BuildTo toaddr; + ADDRESS *addr; + char buf[1000]; + char *newaddr = NULL, *simple_addr = NULL, *fcc = NULL; + ENVELOPE *env = NULL; + BODY *body = NULL; + + init_ab_if_needed(); + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_SAVE, &state); + + for(i = 0; i < as.n_addrbk; i++){ + + pab = &as.adrbks[i]; + + if(pab->ostatus != Open && pab->ostatus != NoDisplay) + init_abook(pab, NoDisplay); + + list = adrbk_list_of_possible_completions(pab ? pab->address_book : NULL, query); + combine_abook_entry_lists(&biglist, list); + } + + /* + * Eliminate duplicates by NO_NEXTing the entrynums. + */ + for(list = biglist; list; list = list->next) + /* eliminate any dups further along in the list */ + if(list->entrynum != NO_NEXT) + for(list2 = list->next; list2; list2 = list2->next) + if(list2->entrynum == list->entrynum){ + list2->entrynum = NO_NEXT; + list->matches_bitmap |= list2->matches_bitmap; + } + + /* build the return list */ + for(list = biglist; list; list = list->next) + if(list->entrynum != NO_NEXT){ + fcc = NULL; + toaddr.type = Abe; + toaddr.arg.abe = adrbk_get_ae(list->ab, list->entrynum); + if(our_build_address(toaddr, &newaddr, NULL, &fcc, NULL) == 0){ + char *reverse_fullname = NULL; + + /* + * ALC_FULL is a regular FullName match and that will be + * captured in the full_address field. If there was also + * an ALC_REVFULL match that means that the user has the + * FullName entered in their addrbook as Last, First and + * that is where the match was. We want to put that in + * the completions structure in the rev_fullname field. + */ + if(list->matches_bitmap & ALC_REVFULL + && toaddr.arg.abe + && toaddr.arg.abe->fullname && toaddr.arg.abe->fullname[0] + && toaddr.arg.abe->fullname[0] != '"' + && strindex(toaddr.arg.abe->fullname, ',') != NULL){ + + reverse_fullname = toaddr.arg.abe->fullname; + } + + if(flags & ALC_INCLUDE_ADDRS){ + if(toaddr.arg.abe && toaddr.arg.abe->tag == Single + && toaddr.arg.abe->addr.addr && toaddr.arg.abe->addr.addr[0]){ + char *tmp_a_string; + char *fakedomain = "@"; + + tmp_a_string = cpystr(toaddr.arg.abe->addr.addr); + addr = NULL; + rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain); + if(tmp_a_string) + fs_give((void **) &tmp_a_string); + + if(addr){ + if(addr->mailbox && addr->host + && !(addr->host[0] == '@' && addr->host[1] == '\0')) + simple_addr = simple_addr_string(addr, buf, sizeof(buf)); + + mail_free_address(&addr); + } + } + } + + new = new_complete_s(toaddr.arg.abe ? toaddr.arg.abe->nickname : NULL, + newaddr, simple_addr, reverse_fullname, fcc, + list->matches_bitmap | ALC_ABOOK); + + /* add to end of list */ + if(return_list == NULL){ + return_list = new; + last_one_added = new; + } + else{ + last_one_added->next = new; + last_one_added = new; + } + } + + if(newaddr) + fs_give((void **) &newaddr); + } + + if(pith_opt_save_and_restore) + (*pith_opt_save_and_restore)(SAR_RESTORE, &state); + + free_abook_entry_s(&biglist); + +#ifdef ENABLE_LDAP + if(flags & ALC_INCLUDE_LDAP){ + LDAP_SERV_RES_S *head_of_result_list = NULL, *res; + LDAP_CHOOSE_S cs; + WP_ERR_S wp_err; + LDAPMessage *e; + + memset(&wp_err, 0, sizeof(wp_err)); + + /* + * This lookup covers all servers with the impl bit set. + * It uses the regular LDAP search parameters that the + * user has set, not necessarily just a prefix match + * like the rest of the address completion above. + */ + head_of_result_list = ldap_lookup_all_work(query, as.n_serv, 0, NULL, &wp_err); + for(res = head_of_result_list; res; res = res->next){ + for(e = ldap_first_entry(res->ld, res->res); + e != NULL; + e = ldap_next_entry(res->ld, e)){ + simple_addr = newaddr = NULL; + cs.ld = res->ld; + cs.selected_entry = e; + cs.info_used = res->info_used; + cs.serv = res->serv; + addr = address_from_ldap(&cs); + if(addr){ + add_addr_to_return_list(addr, ALC_LDAP, query, flags, &return_list); + mail_free_address(&addr); + } + } + } + + if(wp_err.error) + fs_give((void **) &wp_err.error); + + if(head_of_result_list) + free_ldap_result_list(&head_of_result_list); + } +#endif /* ENABLE_LDAP */ + + /* add from current message */ + if(uid > 0 && stream) + env = pine_mail_fetch_structure(stream, uid, &body, FT_UID); + + /* from the envelope addresses */ + if(env){ + for(addr = env->from; addr; addr = addr->next) + add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list); + + for(addr = env->reply_to; addr; addr = addr->next) + add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list); + + for(addr = env->sender; addr; addr = addr->next) + add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list); + + for(addr = env->to; addr; addr = addr->next) + add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list); + + for(addr = env->cc; addr; addr = addr->next) + add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list); + + /* + * May as well search the body for addresses. + * Use this function written for TakeAddr. + */ + if(body){ + TA_S *talist = NULL, *tp; + + if(grab_addrs_from_body(stream, mail_msgno(stream,uid), body, &talist) > 0){ + if(talist){ + + /* rewind to start */ + while(talist->prev) + talist = talist->prev; + + for(tp = talist; tp; tp = tp->next){ + addr = tp->addr; + if(addr) + add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list); + } + + free_talines(&talist); + } + } + } + } + + + /* + * Check for and eliminate some duplicates. + * The criteria for deciding what is a duplicate is + * kind of ad hoc. + */ + for(cp = return_list; cp; cp = cp->next) + for(dprev = cp, dp = cp->next; dp; ){ + if(cp->full_address && dp->full_address + && !strucmp(dp->full_address, cp->full_address) + && (((cp->matches_bitmap & ALC_ABOOK) + && (dp->matches_bitmap & ALC_ABOOK) + && (!(dp->matches_bitmap & ALC_NICK && dp->nickname && dp->nickname[0]) + || ((!cp->addr && !dp->addr) || (cp->addr && dp->addr && !strucmp(cp->addr, dp->addr))))) + || + (dp->matches_bitmap & ALC_CURR) + || + (dp->matches_bitmap & ALC_LDAP + && dp->matches_bitmap & (ALC_FULL | ALC_ADDR)) + || + (cp->matches_bitmap == dp->matches_bitmap + && (!(dp->matches_bitmap & ALC_NICK && dp->nickname && dp->nickname[0]) + || (dp->nickname && cp->nickname + && !strucmp(cp->nickname, dp->nickname)))))){ + /* + * dp is equivalent to cp so eliminate dp + */ + dprev->next = dp->next; + dp->next = NULL; + free_complete_s(&dp); + dp = dprev; + } + + dprev = dp; + dp = dp->next; + } + + return(return_list); +} + + +void +add_addr_to_return_list(ADDRESS *addr, unsigned bitmap, char *query, + int flags, COMPLETE_S **return_list) +{ + char buf[1000]; + char *newaddr = NULL; + char *simple_addr = NULL; + COMPLETE_S *new = NULL, *cp; + ADDRESS *savenext; + + if(return_list && query && addr && addr->mailbox && addr->host){ + + savenext = addr->next; + addr->next = NULL; + newaddr = addr_list_string(addr, NULL, 0); + addr->next = savenext; + + /* + * If the start of the full_address actually matches the query + * string then mark this as ALC_FULL. This might be helpful + * when deciding on the longest unambiguous match. + */ + if(newaddr && newaddr[0] && !struncmp(newaddr, query, strlen(query))) + bitmap |= ALC_FULL; + + if(newaddr && newaddr[0] && flags & ALC_INCLUDE_ADDRS){ + if(addr->mailbox && addr->host + && !(addr->host[0] == '@' && addr->host[1] == '\0')) + simple_addr = simple_addr_string(addr, buf, sizeof(buf)); + + if(simple_addr && !simple_addr[0]) + simple_addr = NULL; + + if(simple_addr && !struncmp(simple_addr, query, strlen(query))) + bitmap |= ALC_ADDR; + } + + /* + * We used to require && bitmap & (ALC_FULL | ALC_ADDR) before + * we would add a match but we think that we should match + * other stuff that matches (like middle names), too, unless we + * are adding an address from the current message. + */ + if((newaddr && newaddr[0]) + && (!(bitmap & ALC_CURR) || bitmap & (ALC_FULL | ALC_ADDR))){ + new = new_complete_s(NULL, newaddr, simple_addr, NULL, NULL, bitmap); + + /* add to end of list */ + if(*return_list == NULL){ + *return_list = new; + } + else{ + for(cp = *return_list; cp->next; cp = cp->next) + ; + + cp->next = new; + } + } + + if(newaddr) + fs_give((void **) &newaddr); + } +} + + +/* + * nick = nickname + * full = whole thing, like Some Body <someb@there.org> + * addr = address part, like someb@there.org + */ +COMPLETE_S * +new_complete_s(char *nick, char *full, char *addr, + char *rev_fullname, char *fcc, unsigned matches_bitmap) +{ + COMPLETE_S *new = NULL; + + new = (COMPLETE_S *) fs_get(sizeof(*new)); + memset((void *) new, 0, sizeof(*new)); + new->nickname = nick ? cpystr(nick) : NULL; + new->full_address = full ? cpystr(full) : NULL; + new->addr = addr ? cpystr(addr) : NULL; + new->rev_fullname = rev_fullname ? cpystr(rev_fullname) : NULL; + new->fcc = fcc ? cpystr(fcc) : NULL; + new->matches_bitmap = matches_bitmap; + + return(new); +} + + +void +free_complete_s(COMPLETE_S **compptr) +{ + if(compptr && *compptr){ + if((*compptr)->next) + free_complete_s(&(*compptr)->next); + + if((*compptr)->nickname) + fs_give((void **) &(*compptr)->nickname); + + if((*compptr)->full_address) + fs_give((void **) &(*compptr)->full_address); + + if((*compptr)->addr) + fs_give((void **) &(*compptr)->addr); + + if((*compptr)->rev_fullname) + fs_give((void **) &(*compptr)->rev_fullname); + + if((*compptr)->fcc) + fs_give((void **) &(*compptr)->fcc); + + fs_give((void **) compptr); + } +} + + +ABOOK_ENTRY_S * +new_abook_entry_s(AdrBk *ab, a_c_arg_t numarg, unsigned bit) +{ + ABOOK_ENTRY_S *new = NULL; + adrbk_cntr_t entrynum; + + entrynum = (adrbk_cntr_t) numarg; + + new = (ABOOK_ENTRY_S *) fs_get(sizeof(*new)); + memset((void *) new, 0, sizeof(*new)); + new->ab = ab; + new->entrynum = entrynum; + new->matches_bitmap = bit; + + return(new); +} + + +void +free_abook_entry_s(ABOOK_ENTRY_S **aep) +{ + if(aep && *aep){ + if((*aep)->next) + free_abook_entry_s(&(*aep)->next); + + fs_give((void **) aep); + } +} + + +/* + * Add the second list to the end of the first. + */ +void +combine_abook_entry_lists(ABOOK_ENTRY_S **first, ABOOK_ENTRY_S *second) +{ + ABOOK_ENTRY_S *sl; + + if(!second) + return; + + if(first){ + if(*first){ + for(sl = *first; sl->next; sl = sl->next) + ; + + sl->next = second; + } + else + *first = second; + } +} + + +ABOOK_ENTRY_S * +adrbk_list_of_possible_completions(AdrBk *ab, char *prefix) +{ + ABOOK_ENTRY_S *list = NULL, *biglist = NULL; + + if(!ab || !prefix) + return(biglist); + + if(ab->nick_trie){ + list = adrbk_list_of_possible_trie_completions(ab->nick_trie, ab, prefix, ALC_NICK); + combine_abook_entry_lists(&biglist, list); + } + + if(ab->full_trie){ + list = adrbk_list_of_possible_trie_completions(ab->full_trie, ab, prefix, ALC_FULL); + combine_abook_entry_lists(&biglist, list); + } + + if(ab->addr_trie){ + list = adrbk_list_of_possible_trie_completions(ab->addr_trie, ab, prefix, ALC_ADDR); + combine_abook_entry_lists(&biglist, list); + } + + if(ab->revfull_trie){ + list = adrbk_list_of_possible_trie_completions(ab->revfull_trie, ab, prefix, ALC_REVFULL); + combine_abook_entry_lists(&biglist, list); + } + + return(biglist); +} + + +/* + * Look in this address book for all nicknames, addresses, or fullnames + * which begin with the prefix prefix, and return an allocated + * list of them. + */ +ABOOK_ENTRY_S * +adrbk_list_of_possible_trie_completions(AdrBk_Trie *trie, AdrBk *ab, char *prefix, + unsigned bit) +{ + AdrBk_Trie *t; + char *p, *lookthisup; + char buf[1000]; + ABOOK_ENTRY_S *list = NULL; + + if(!ab || !prefix || !trie) + return(list); + + t = trie; + + /* make lookup case independent */ + + for(p = prefix; *p && !(*p & 0x80) && islower((unsigned char) *p); p++) + ; + + if(*p){ + strncpy(buf, prefix, sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + for(p = buf; *p; p++) + if(!(*p & 0x80) && isupper((unsigned char) *p)) + *p = tolower(*p); + + lookthisup = buf; + } + else + lookthisup = prefix; + + p = lookthisup; + + while(*p){ + /* search for character at this level */ + while(t->value != *p){ + if(t->right == NULL) + return(list); /* no match */ + + t = t->right; + } + + if(*++p == '\0') /* matched through end of prefix */ + break; + + /* need to go down to match next character */ + if(t->down == NULL) /* no match */ + return(list); + + t = t->down; + } + + /* + * If we get here that means we found at least + * one entry that matches up through prefix. + * Gather_abook_list recursively adds the nicknames starting at + * this node. + */ + if(t->entrynum != NO_NEXT){ + /* + * Add it to the list. + */ + list = new_abook_entry_s(ab, t->entrynum, bit); + } + + gather_abook_entry_list(ab, t->down, prefix, &list, bit); + + return(list); +} + + +void +gather_abook_entry_list(AdrBk *ab, AdrBk_Trie *node, char *prefix, ABOOK_ENTRY_S **list, unsigned bit) +{ + char *next_prefix = NULL; + size_t l; + ABOOK_ENTRY_S *newlist = NULL; + + if(node){ + if(node->entrynum != NO_NEXT || node->down || node->right){ + l = strlen(prefix ? prefix : ""); + if(node->entrynum != NO_NEXT){ + /* + * Add it to the list. + */ + newlist = new_abook_entry_s(ab, node->entrynum, bit); + combine_abook_entry_lists(list, newlist); + } + + /* same prefix for node->right */ + if(node->right) + gather_abook_entry_list(ab, node->right, prefix, list, bit); + + /* prefix is one longer for node->down */ + if(node->down){ + next_prefix = (char *) fs_get((l+2) * sizeof(char)); + strncpy(next_prefix, prefix ? prefix : "", l+2); + next_prefix[l] = node->value; + next_prefix[l+1] = '\0'; + gather_abook_entry_list(ab, node->down, next_prefix, list, bit); + + if(next_prefix) + fs_give((void **) &next_prefix); + } + } + } +} |