* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / ablookup.c
blob6134af3df27ef4fe8dc4a67b559675e8689605d6
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2009 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h" /* for os-dep and pith defs/includes */
16 #include "../pith/debug.h"
17 #include "../pith/util.h"
18 #include "../pith/adrbklib.h"
19 #include "../pith/copyaddr.h"
20 #include "../pith/status.h"
21 #include "../pith/conf.h"
22 #include "../pith/search.h"
23 #include "../pith/abdlc.h"
24 #include "../pith/addrstring.h"
25 #include "../pith/ablookup.h"
26 #include "../pith/options.h"
27 #include "../pith/takeaddr.h"
28 #ifdef ENABLE_LDAP
29 #include "../pith/ldap.h"
30 #endif /* ENABLE_LDAP */
34 * Internal prototypes
36 int addr_is_in_addrbook(PerAddrBook *, ADDRESS *);
37 ABOOK_ENTRY_S *adrbk_list_of_possible_trie_completions(AdrBk_Trie *, AdrBk *, char *, unsigned);
38 void gather_abook_entry_list(AdrBk *, AdrBk_Trie *, char *, ABOOK_ENTRY_S **, unsigned);
39 void add_addr_to_return_list(ADDRESS *, unsigned, char *, int, COMPLETE_S **);
43 * Given an address, try to find the first nickname that goes with it.
44 * Copies that nickname into the passed in buffer, which is assumed to
45 * be at least MAX_NICKNAME+1 in length. Returns NULL if it can't be found,
46 * else it returns a pointer to the buffer.
48 char *
49 get_nickname_from_addr(struct mail_address *adr, char *buffer, size_t buflen)
51 AdrBk_Entry *abe;
52 char *ret = NULL;
53 SAVE_STATE_S state;
54 jmp_buf save_jmp_buf;
55 int *save_nesting_level;
56 ADDRESS *copied_adr;
58 state.savep = NULL;
59 state.stp = NULL;
60 state.dlc_to_warp_to = NULL;
61 copied_adr = copyaddr(adr);
63 if(ps_global->remote_abook_validity > 0)
64 (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
66 save_nesting_level = cpyint(ab_nesting_level);
67 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
68 if(setjmp(addrbook_changed_unexpectedly)){
69 ret = NULL;
70 if(state.savep)
71 fs_give((void **)&(state.savep));
72 if(state.stp)
73 fs_give((void **)&(state.stp));
74 if(state.dlc_to_warp_to)
75 fs_give((void **)&(state.dlc_to_warp_to));
77 q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
78 dprint((1,
79 "RESETTING address book... get_nickname_from_addr()!\n"));
80 addrbook_reset();
81 ab_nesting_level = *save_nesting_level;
84 ab_nesting_level++;
85 init_ab_if_needed();
87 if(pith_opt_save_and_restore)
88 (*pith_opt_save_and_restore)(SAR_SAVE, &state);
90 abe = address_to_abe(copied_adr);
92 if(copied_adr)
93 mail_free_address(&copied_adr);
95 if(abe && abe->nickname && abe->nickname[0]){
96 strncpy(buffer, abe->nickname, buflen-1);
97 buffer[buflen-1] = '\0';
98 ret = buffer;
101 if(pith_opt_save_and_restore)
102 (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
104 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
105 ab_nesting_level--;
107 if(save_nesting_level)
108 fs_give((void **)&save_nesting_level);
110 return(ret);
115 * Given an address, try to find the first fcc that goes with it.
116 * Copies that fcc into the passed in buffer.
117 * Returns NULL if it can't be found, else it returns a pointer to the buffer.
119 char *
120 get_fcc_from_addr(struct mail_address *adr, char *buffer, size_t buflen)
122 AdrBk_Entry *abe;
123 char *ret = NULL;
124 SAVE_STATE_S state;
125 jmp_buf save_jmp_buf;
126 int *save_nesting_level;
127 ADDRESS *copied_adr;
129 state.savep = NULL;
130 state.stp = NULL;
131 state.dlc_to_warp_to = NULL;
132 copied_adr = copyaddr(adr);
134 if(ps_global->remote_abook_validity > 0)
135 (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
137 save_nesting_level = cpyint(ab_nesting_level);
138 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
139 if(setjmp(addrbook_changed_unexpectedly)){
140 ret = NULL;
141 if(state.savep)
142 fs_give((void **)&(state.savep));
143 if(state.stp)
144 fs_give((void **)&(state.stp));
145 if(state.dlc_to_warp_to)
146 fs_give((void **)&(state.dlc_to_warp_to));
148 q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
149 dprint((1,
150 "RESETTING address book... get_fcc_from_addr()!\n"));
151 addrbook_reset();
152 ab_nesting_level = *save_nesting_level;
155 ab_nesting_level++;
156 init_ab_if_needed();
158 if(pith_opt_save_and_restore)
159 (*pith_opt_save_and_restore)(SAR_SAVE, &state);
161 abe = address_to_abe(copied_adr);
163 if(copied_adr)
164 mail_free_address(&copied_adr);
166 if(abe && abe->fcc && abe->fcc[0]){
167 if(!strcmp(abe->fcc, "\"\""))
168 buffer[0] = '\0';
169 else{
170 strncpy(buffer, abe->fcc, buflen-1);
171 buffer[buflen-1] = '\0';
174 ret = buffer;
177 if(pith_opt_save_and_restore)
178 (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
180 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
181 ab_nesting_level--;
183 if(save_nesting_level)
184 fs_give((void **)&save_nesting_level);
186 return(ret);
191 * Given an address, try to find the first address book entry that
192 * matches it and return all the other fields in the passed in pointers.
193 * Caller needs to free the four fields.
194 * Returns -1 if it can't be found, 0 if it is found.
197 get_contactinfo_from_addr(struct mail_address *adr, char **nick, char **full, char **fcc, char **comment)
199 AdrBk_Entry *abe;
200 int ret = -1;
201 SAVE_STATE_S state;
202 jmp_buf save_jmp_buf;
203 int *save_nesting_level;
204 ADDRESS *copied_adr;
206 state.savep = NULL;
207 state.stp = NULL;
208 state.dlc_to_warp_to = NULL;
209 copied_adr = copyaddr(adr);
211 if(ps_global->remote_abook_validity > 0)
212 (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
214 save_nesting_level = cpyint(ab_nesting_level);
215 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
216 if(setjmp(addrbook_changed_unexpectedly)){
217 ret = -1;
218 if(state.savep)
219 fs_give((void **)&(state.savep));
220 if(state.stp)
221 fs_give((void **)&(state.stp));
222 if(state.dlc_to_warp_to)
223 fs_give((void **)&(state.dlc_to_warp_to));
225 q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
226 dprint((1,
227 "RESETTING address book... get_contactinfo_from_addr()!\n"));
228 addrbook_reset();
229 ab_nesting_level = *save_nesting_level;
232 ab_nesting_level++;
233 init_ab_if_needed();
235 if(pith_opt_save_and_restore)
236 (*pith_opt_save_and_restore)(SAR_SAVE, &state);
238 abe = address_to_abe(copied_adr);
240 if(copied_adr)
241 mail_free_address(&copied_adr);
243 if(abe){
244 if(nick && abe->nickname && abe->nickname[0])
245 *nick = cpystr(abe->nickname);
247 if(full && abe->fullname && abe->fullname[0])
248 *full = cpystr(abe->fullname);
250 if(fcc && abe->fcc && abe->fcc[0])
251 *fcc = cpystr(abe->fcc);
253 if(comment && abe->extra && abe->extra[0])
254 *comment = cpystr(abe->extra);
256 ret = 0;
259 if(pith_opt_save_and_restore)
260 (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
262 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
263 ab_nesting_level--;
265 if(save_nesting_level)
266 fs_give((void **)&save_nesting_level);
268 return(ret);
273 * This is a very special-purpose routine.
274 * It implements the From or Reply-To address is in the Address Book
275 * part of Pattern matching.
277 void
278 address_in_abook(MAILSTREAM *stream, SEARCHSET *searchset,
279 int inabook, PATTERN_S *abooks)
281 char *savebits;
282 MESSAGECACHE *mc;
283 long i, count = 0L;
284 SEARCHSET *s, *ss;
285 ADDRESS *from, *reply_to, *sender, *to, *cc;
286 int is_there, adrbknum, *abooklist = NULL, positive_match;
287 PATTERN_S *pat;
288 PerAddrBook *pab;
289 ENVELOPE *e;
290 SAVE_STATE_S state;
291 jmp_buf save_jmp_buf;
292 int *save_nesting_level;
294 if(!stream)
295 return;
297 /* everything that matches remains a match */
298 if(inabook == IAB_EITHER)
299 return;
301 state.savep = NULL;
302 state.stp = NULL;
303 state.dlc_to_warp_to = NULL;
306 * This may call build_header_line recursively because we may be in
307 * build_header_line now. So we have to preserve and restore the
308 * sequence bits since we want to use them here.
310 savebits = (char *) fs_get((stream->nmsgs+1) * sizeof(char));
312 for(i = 1L; i <= stream->nmsgs; i++){
313 if((mc = mail_elt(stream, i)) != NULL){
314 savebits[i] = mc->sequence;
315 mc->sequence = 0;
320 * Build a searchset so we can look at all the envelopes
321 * we need to look at but only those we need to look at.
322 * Everything with the searched bit set is still a
323 * possibility, so restrict to that set.
326 for(s = searchset; s; s = s->next)
327 for(i = s->first; i <= s->last; i++)
328 if(i > 0L && i <= stream->nmsgs
329 && (mc=mail_elt(stream, i)) && mc->searched){
330 mc->sequence = 1;
331 count++;
334 ss = build_searchset(stream);
337 * We save the address book state here so we don't have to do it
338 * each time through the loop below.
340 if(ss){
341 if(ps_global->remote_abook_validity > 0)
342 (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
344 save_nesting_level = cpyint(ab_nesting_level);
345 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
346 if(setjmp(addrbook_changed_unexpectedly)){
347 if(state.savep)
348 fs_give((void **)&(state.savep));
349 if(state.stp)
350 fs_give((void **)&(state.stp));
351 if(state.dlc_to_warp_to)
352 fs_give((void **)&(state.dlc_to_warp_to));
354 q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
355 dprint((1,
356 "RESETTING address book... address_in_abook()!\n"));
357 addrbook_reset();
358 ab_nesting_level = *save_nesting_level;
361 ab_nesting_level++;
362 init_ab_if_needed();
364 if(pith_opt_save_and_restore)
365 (*pith_opt_save_and_restore)(SAR_SAVE, &state);
367 if(as.n_addrbk > 0){
368 abooklist = (int *) fs_get(as.n_addrbk * sizeof(*abooklist));
369 memset((void *) abooklist, 0, as.n_addrbk * sizeof(*abooklist));
372 if(abooklist)
373 switch(inabook & IAB_TYPE_MASK){
374 case IAB_YES:
375 case IAB_NO:
376 for(adrbknum = 0; adrbknum < as.n_addrbk; adrbknum++)
377 abooklist[adrbknum] = 1;
379 break;
381 case IAB_SPEC_YES:
382 case IAB_SPEC_NO:
383 /* figure out which address books we're going to look in */
384 for(adrbknum = 0; adrbknum < as.n_addrbk; adrbknum++){
385 pab = &as.adrbks[adrbknum];
387 * For each address book, check all of the address books
388 * in the pattern's list to see if they are it.
390 for(pat = abooks; pat; pat = pat->next){
391 if(!strcmp(pab->abnick, pat->substring)
392 || !strcmp(pab->filename, pat->substring)){
393 abooklist[adrbknum] = 1;
394 break;
399 break;
402 switch(inabook & IAB_TYPE_MASK){
403 case IAB_YES:
404 case IAB_SPEC_YES:
405 positive_match = 1;
406 break;
408 case IAB_NO:
409 case IAB_SPEC_NO:
410 positive_match = 0;
411 break;
415 if(count){
416 SEARCHSET **sset;
418 mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count);
421 * This causes the lookahead to fetch precisely
422 * the messages we want (in the searchset) instead
423 * of just fetching the next 20 sequential
424 * messages. If the searching so far has caused
425 * a sparse searchset in a large mailbox, the
426 * difference can be substantial.
427 * This resets automatically after the first fetch.
429 sset = (SEARCHSET **) mail_parameters(stream,
430 GET_FETCHLOOKAHEAD,
431 (void *) stream);
432 if(sset)
433 *sset = ss;
436 for(s = ss; s; s = s->next){
437 for(i = s->first; i <= s->last; i++){
438 if(i <= 0L || i > stream->nmsgs)
439 continue;
441 e = pine_mail_fetchenvelope(stream, i);
443 from = e ? e->from : NULL;
444 reply_to = e ? e->reply_to : NULL;
445 sender = e ? e->sender : NULL;
446 to = e ? e->to : NULL;
447 cc = e ? e->cc : NULL;
449 is_there = 0;
450 for(adrbknum = 0; !is_there && adrbknum < as.n_addrbk; adrbknum++){
451 if(!abooklist[adrbknum])
452 continue;
454 pab = &as.adrbks[adrbknum];
455 is_there = ((inabook & IAB_FROM) && addr_is_in_addrbook(pab, from))
456 || ((inabook & IAB_REPLYTO) && addr_is_in_addrbook(pab, reply_to))
457 || ((inabook & IAB_SENDER) && addr_is_in_addrbook(pab, sender))
458 || ((inabook & IAB_TO) && addr_is_in_addrbook(pab, to))
459 || ((inabook & IAB_CC) && addr_is_in_addrbook(pab, cc));
462 if(positive_match){
464 * We matched up until now. If it isn't there, then it
465 * isn't a match. If it is there, leave the searched bit
466 * set.
468 if(!is_there && i > 0L && i <= stream->nmsgs && (mc = mail_elt(stream, i)))
469 mc->searched = NIL;
471 else{
472 if(is_there && i > 0L && i <= stream->nmsgs && (mc = mail_elt(stream, i)))
473 mc->searched = NIL;
478 if(ss){
479 if(pith_opt_save_and_restore)
480 (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
482 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
483 ab_nesting_level--;
485 if(save_nesting_level)
486 fs_give((void **)&save_nesting_level);
489 /* restore sequence bits */
490 for(i = 1L; i <= stream->nmsgs; i++)
491 if((mc = mail_elt(stream, i)) != NULL)
492 mc->sequence = savebits[i];
494 fs_give((void **) &savebits);
496 if(ss)
497 mail_free_searchset(&ss);
499 if(abooklist)
500 fs_give((void **) &abooklist);
505 * Given two addresses, check to see if either is in the address book.
506 * Returns 1 if yes, 0 if not found.
509 addr_is_in_addrbook(PerAddrBook *pab, struct mail_address *addr)
511 AdrBk_Entry *abe = NULL;
512 int ret = 0;
513 char abuf[MAX_ADDR_FIELD+1];
515 if(!(pab && addr && addr->mailbox))
516 return(ret);
518 if(addr){
519 strncpy(abuf, addr->mailbox, sizeof(abuf)-1);
520 abuf[sizeof(abuf)-1] = '\0';
521 if(addr->host && addr->host[0]){
522 strncat(abuf, "@", sizeof(abuf)-strlen(abuf)-1);
523 strncat(abuf, addr->host, sizeof(abuf)-strlen(abuf)-1);
526 if(pab->ostatus != Open && pab->ostatus != NoDisplay)
527 init_abook(pab, NoDisplay);
529 abe = adrbk_lookup_by_addr(pab->address_book, abuf,
530 (adrbk_cntr_t *) NULL);
533 ret = abe ? 1 : 0;
535 return(ret);
540 * Look through addrbooks for nickname, opening addrbooks first
541 * if necessary. It is assumed that the caller will restore the
542 * state of the addrbooks if desired.
544 * Args: nickname -- the nickname to lookup
545 * clearrefs -- clear reference bits before lookup
546 * which_addrbook -- If matched, addrbook number it was found in.
547 * not_here -- If non-negative, skip looking in this abook.
549 * Results: A pointer to an AdrBk_Entry is returned, or NULL if not found.
550 * Stop at first match (so order of addrbooks is important).
552 AdrBk_Entry *
553 adrbk_lookup_with_opens_by_nick(char *nickname, int clearrefs, int *which_addrbook, int not_here)
555 AdrBk_Entry *abe = (AdrBk_Entry *)NULL;
556 int i;
557 PerAddrBook *pab;
559 dprint((5, "- adrbk_lookup_with_opens_by_nick(%s) -\n",
560 nickname ? nickname : "?"));
562 for(i = 0; i < as.n_addrbk; i++){
564 if(i == not_here)
565 continue;
567 pab = &as.adrbks[i];
569 if(pab->ostatus != Open && pab->ostatus != NoDisplay)
570 init_abook(pab, NoDisplay);
572 if(clearrefs)
573 adrbk_clearrefs(pab->address_book);
575 abe = adrbk_lookup_by_nick(pab->address_book,
576 nickname,
577 (adrbk_cntr_t *)NULL);
578 if(abe)
579 break;
582 if(abe && which_addrbook)
583 *which_addrbook = i;
585 return(abe);
590 * Find the addressbook entry that matches the argument address.
591 * Searches through all addressbooks looking for the match.
592 * Opens addressbooks if necessary. It is assumed that the caller
593 * will restore the state of the addrbooks if desired.
595 * Args: addr -- the address we're trying to match
597 * Returns: NULL -- no match found
598 * abe -- a pointer to the addrbook entry that matches
600 AdrBk_Entry *
601 address_to_abe(struct mail_address *addr)
603 register PerAddrBook *pab;
604 int adrbk_number;
605 AdrBk_Entry *abe = NULL;
606 char *abuf = NULL;
608 if(!(addr && addr->mailbox))
609 return (AdrBk_Entry *)NULL;
611 abuf = (char *)fs_get((MAX_ADDR_FIELD + 1) * sizeof(char));
613 strncpy(abuf, addr->mailbox, MAX_ADDR_FIELD);
614 abuf[MAX_ADDR_FIELD] = '\0';
615 if(addr->host && addr->host[0]){
616 strncat(abuf, "@", MAX_ADDR_FIELD+1-1-strlen(abuf));
617 strncat(abuf, addr->host, MAX_ADDR_FIELD+1-1-strlen(abuf));
620 /* for each addressbook */
621 for(adrbk_number = 0; adrbk_number < as.n_addrbk; adrbk_number++){
623 pab = &as.adrbks[adrbk_number];
625 if(pab->ostatus != Open && pab->ostatus != NoDisplay)
626 init_abook(pab, NoDisplay);
628 abe = adrbk_lookup_by_addr(pab->address_book,
629 abuf,
630 (adrbk_cntr_t *)NULL);
631 if(abe)
632 break;
635 if(abuf)
636 fs_give((void **)&abuf);
638 return(abe);
643 * Turn an AdrBk_Entry into an address list
645 * Args: abe -- the AdrBk_Entry
646 * dl -- the corresponding dl
647 * abook -- which addrbook the abe is in (only used for type ListEnt)
648 * how_many -- The number of addresses is returned here
650 * Result: allocated address list or NULL
652 ADDRESS *
653 abe_to_address(AdrBk_Entry *abe, AddrScrn_Disp *dl, AdrBk *abook, int *how_many)
655 char *fullname, *tmp_a_string;
656 char *list, *l1, **l2;
657 char *fakedomain = "@";
658 ADDRESS *addr = NULL;
659 size_t length;
660 int count = 0;
662 if(!dl || !abe)
663 return(NULL);
665 fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL;
667 switch(dl->type){
668 case Simple:
669 /* rfc822_parse_adrlist feels free to destroy input so send copy */
670 tmp_a_string = cpystr(abe->addr.addr);
671 rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
673 if(tmp_a_string)
674 fs_give((void **)&tmp_a_string);
676 if(addr && fullname){
677 #ifdef ENABLE_LDAP
678 if(strncmp(addr->mailbox, RUN_LDAP, LEN_RL) != 0)
679 #endif /* ENABLE_LDAP */
681 if(addr->personal)
682 fs_give((void **)&addr->personal);
684 addr->personal = adrbk_formatname(fullname, NULL, NULL);
688 if(addr)
689 count++;
691 break;
693 case ListEnt:
694 /* rfc822_parse_adrlist feels free to destroy input so send copy */
695 tmp_a_string = cpystr(listmem_from_dl(abook, dl));
696 rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
697 if(tmp_a_string)
698 fs_give((void **)&tmp_a_string);
700 if(addr)
701 count++;
703 break;
705 case ListHead:
706 length = 0;
707 for(l2 = abe->addr.list; *l2; l2++)
708 length += (strlen(*l2) + 1);
710 list = (char *)fs_get(length + 1);
711 l1 = list;
712 for(l2 = abe->addr.list; *l2; l2++){
713 if(l1 != list && length-(l1-list) > 0)
714 *l1++ = ',';
716 strncpy(l1, *l2, length-(l1-list));
717 list[length] = '\0';
718 l1 += strlen(l1);
719 count++;
722 rfc822_parse_adrlist(&addr, list, fakedomain);
723 if(list)
724 fs_give((void **)&list);
726 break;
728 default:
729 dprint((1, "default case in abe_to_address, shouldn't happen\n"));
730 break;
733 if(how_many)
734 *how_many = count;
736 return(addr);
741 * Turn an AdrBk_Entry into a nickname (if it has a nickname) or a
742 * formatted addr_string which has been rfc1522 decoded.
744 * Args: abe -- the AdrBk_Entry
745 * dl -- the corresponding dl
746 * abook -- which addrbook the abe is in (only used for type ListEnt)
748 * Result: allocated string is returned
750 char *
751 abe_to_nick_or_addr_string(AdrBk_Entry *abe, AddrScrn_Disp *dl, int abook_num)
753 ADDRESS *addr;
754 char *a_string;
756 if(!dl || !abe)
757 return(cpystr(""));
759 if((dl->type == Simple || dl->type == ListHead)
760 && abe->nickname && abe->nickname[0]){
761 char *fname;
762 int which_addrbook;
765 * We prefer to pass back the nickname since that allows the
766 * caller to keep track of which entry the address came from.
767 * This is useful in build_address so that the fcc line can
768 * be kept correct. However, if the nickname is also present in
769 * another addressbook then we have to be careful. If that other
770 * addressbook comes before this one then passing back the nickname
771 * will cause the wrong entry to get used. So check for that
772 * and pass back the addr_string in that case.
774 if((fname = addr_lookup(abe->nickname, &which_addrbook, abook_num)) != NULL){
775 fs_give((void **)&fname);
776 if(which_addrbook >= abook_num)
777 return(cpystr(abe->nickname));
779 else
780 return(cpystr(abe->nickname));
783 addr = abe_to_address(abe, dl, as.adrbks[abook_num].address_book, NULL);
784 a_string = addr_list_string(addr, NULL, 0); /* always returns a string */
785 if(addr)
786 mail_free_address(&addr);
788 return(a_string);
793 * Check to see if address is that of the current user running pine
795 * Args: a -- Address to check
796 * ps -- The pine_state structure
798 * Result: returns 1 if it matches, 0 otherwise.
800 * The mailbox must match the user name and the hostname must match.
801 * In matching the hostname, matches occur if the hostname in the address
802 * is blank, or if it matches the local hostname, or the full hostname
803 * with qualifying domain, or the qualifying domain without a specific host.
804 * Note, there is a very small chance that we will err on the
805 * non-conservative side here. That is, we may decide two addresses are
806 * the same even though they are different (because we do case-insensitive
807 * compares on the mailbox). That might cause a reply not to be sent to
808 * somebody because they look like they are us. This should be very,
809 * very rare.
811 * It is also considered a match if any of the addresses in alt-addresses
812 * matches a. The check there is simpler. It parses each address in
813 * the list, adding maildomain if there wasn't a domain, and compares
814 * mailbox and host in the ADDRESS's for equality.
817 address_is_us(struct mail_address *a, struct pine *ps)
819 char **t;
820 int ret;
821 char addrstr[500];
823 if(!a || a->mailbox == NULL || !ps)
824 ret = 0;
826 /* at least LHS must match, but case-independent */
827 else if(strucmp(a->mailbox, ps->VAR_USER_ID) == 0
829 && /* and hostname matches */
831 /* hostname matches if it's not there, */
832 (a->host == NULL ||
833 /* or if hostname and userdomain (the one user sets) match exactly, */
834 ((ps->userdomain && a->host && strucmp(a->host,ps->userdomain) == 0) ||
837 * or if(userdomain is either not set or it is set to be
838 * the same as the localdomain or hostname) and (the hostname
839 * of the address matches either localdomain or hostname)
841 ((ps->userdomain == NULL ||
842 strucmp(ps->userdomain, ps->localdomain) == 0 ||
843 strucmp(ps->userdomain, ps->hostname) == 0) &&
844 (strucmp(a->host, ps->hostname) == 0 ||
845 strucmp(a->host, ps->localdomain) == 0)))))
846 ret = 1;
849 * If no match yet, check to see if it matches any of the alternate
850 * addresses the user has specified.
852 else if(!ps_global->VAR_ALT_ADDRS ||
853 !ps_global->VAR_ALT_ADDRS[0] ||
854 !ps_global->VAR_ALT_ADDRS[0][0])
855 ret = 0; /* none defined */
857 else{
858 ret = 0;
859 if(a && a->host && a->mailbox)
860 snprintf(addrstr, sizeof(addrstr), "%s@%s", a->mailbox, a->host);
861 else
862 addrstr[0] = '\0';
864 for(t = ps_global->VAR_ALT_ADDRS; !ret && t[0] && t[0][0]; t++){
865 char *alt;
866 regex_t reg;
867 int err;
868 char ebuf[200];
870 alt = (*t);
872 if(F_ON(F_DISABLE_REGEX, ps_global) || !contains_regex_special_chars(alt)){
873 ADDRESS *alt_addr;
874 char *alt2;
876 alt2 = cpystr(alt);
877 alt_addr = NULL;
878 rfc822_parse_adrlist(&alt_addr, alt2, ps_global->maildomain);
879 if(alt2)
880 fs_give((void **) &alt2);
882 if(address_is_same(a, alt_addr))
883 ret = 1;
885 if(alt_addr)
886 mail_free_address(&alt_addr);
888 else{
889 /* treat alt as a regular expression */
890 ebuf[0] = '\0';
891 if(!(err=regcomp(&reg, alt, REG_ICASE | REG_NOSUB | REG_EXTENDED))){
892 err = regexec(&reg, addrstr, 0, NULL, 0);
893 if(err == 0)
894 ret = 1;
895 else if(err != REG_NOMATCH){
896 regerror(err, &reg, ebuf, sizeof(ebuf));
897 if(ebuf[0])
898 dprint((2, "- address_is_us regexec error: %s (%s)", ebuf, alt));
901 regfree(&reg);
903 else{
904 regerror(err, &reg, ebuf, sizeof(ebuf));
905 if(ebuf[0])
906 dprint((2, "- address_is_us regcomp error: %s (%s)", ebuf, alt));
912 return(ret);
917 * In an ad hoc way try to decide if str is meant to be a regular
918 * expression or not. Dot doesn't count * as regex stuff because
919 * we're worried about addresses.
921 * Returns 0 or 1
924 contains_regex_special_chars(char *str)
926 char special_chars[] = {'*', '|', '+', '?', '{', '[', '^', '$', '\\', '\0'};
927 char *c;
929 if(!str)
930 return 0;
933 * If any of special_chars are in str consider it a regex expression.
935 for(c = special_chars; *c; c++)
936 if(strindex(str, *c))
937 return 1;
939 return 0;
944 * Compare the two addresses, and return true if they're the same,
945 * false otherwise
947 * Args: a -- First address for comparison
948 * b -- Second address for comparison
950 * Result: returns 1 if it matches, 0 otherwise.
953 address_is_same(struct mail_address *a, struct mail_address *b)
955 return(a && b && a->mailbox && b->mailbox && a->host && b->host
956 && strucmp(a->mailbox, b->mailbox) == 0
957 && strucmp(a->host, b->host) == 0);
962 * Returns nonzero if the two address book entries are equal.
963 * Returns zero if not equal.
966 abes_are_equal(AdrBk_Entry *a, AdrBk_Entry *b)
968 int result = 0;
969 char **alist, **blist;
970 char *addra, *addrb;
972 if(a && b &&
973 a->tag == b->tag &&
974 ((!a->nickname && !b->nickname) ||
975 (a->nickname && b->nickname && strcmp(a->nickname,b->nickname) == 0)) &&
976 ((!a->fullname && !b->fullname) ||
977 (a->fullname && b->fullname && strcmp(a->fullname,b->fullname) == 0)) &&
978 ((!a->fcc && !b->fcc) ||
979 (a->fcc && b->fcc && strcmp(a->fcc,b->fcc) == 0)) &&
980 ((!a->extra && !b->extra) ||
981 (a->extra && b->extra && strcmp(a->extra,b->extra) == 0))){
983 /* If we made it in here, they still might be equal. */
984 if(a->tag == Single)
985 result = strcmp(a->addr.addr,b->addr.addr) == 0;
986 else{
987 alist = a->addr.list;
988 blist = b->addr.list;
989 if(!alist && !blist)
990 result = 1;
991 else{
992 /* compare the whole lists */
993 addra = *alist;
994 addrb = *blist;
995 while(addra && addrb && strcmp(addra,addrb) == 0){
996 alist++;
997 blist++;
998 addra = *alist;
999 addrb = *blist;
1002 if(!addra && !addrb)
1003 result = 1;
1008 return(result);
1013 * Interface to address book lookups for callers outside or inside this file.
1015 * Args: nickname -- The nickname to look up
1016 * which_addrbook -- If matched, addrbook number it was found in.
1017 * not_here -- If non-negative, skip looking in this abook.
1019 * Result: returns NULL or the corresponding fullname. The fullname is
1020 * allocated here so the caller must free it.
1022 * This opens the address books if they haven't been opened and restores
1023 * them to the state they were in upon entry.
1025 char *
1026 addr_lookup(char *nickname, int *which_addrbook, int not_here)
1028 AdrBk_Entry *abe;
1029 SAVE_STATE_S state;
1030 char *fullname;
1032 dprint((9, "- addr_lookup(%s) -\n",nickname ? nickname : "nil"));
1034 init_ab_if_needed();
1036 if(pith_opt_save_and_restore)
1037 (*pith_opt_save_and_restore)(SAR_SAVE, &state);
1039 abe = adrbk_lookup_with_opens_by_nick(nickname,0,which_addrbook,not_here);
1041 fullname = (abe && abe->fullname) ? cpystr(abe->fullname) : NULL;
1043 if(pith_opt_save_and_restore)
1044 (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
1046 return(fullname);
1051 * Look in all of the address books for all of the possible entries
1052 * that match the query string. The matches can be for the nickname,
1053 * for the fullname, or for the address@host part of the address.
1054 * All of the matches are at the starts of the strings, not a general
1055 * substring match. This is not true anymore. Fullname matches can be
1056 * at the start of the fullname or starting after a space in the fullname.
1057 * If flags has ALC_INCLUDE_LDAP defined then LDAP
1058 * entries are added to the end of the list. The LDAP queries are done
1059 * only for those servers that have the 'impl' feature turned on, which
1060 * means that lookups should be done implicitly. This feature also
1061 * controls whether or not lookups should be done when typing carriage
1062 * return (instead of this which is TAB).
1064 * Args query -- What the user has typed so far
1066 * Returns a list of possibilities for the given query string.
1068 * Caller needs to free the answer.
1070 COMPLETE_S *
1071 adrbk_list_of_completions(char *query, MAILSTREAM *stream, imapuid_t uid, int flags)
1073 int i;
1074 SAVE_STATE_S state;
1075 PerAddrBook *pab;
1076 ABOOK_ENTRY_S *list, *list2, *biglist = NULL;
1077 COMPLETE_S *return_list = NULL, *last_one_added = NULL, *new, *cp, *dp, *dprev;
1078 BuildTo toaddr;
1079 ADDRESS *addr;
1080 char buf[1000];
1081 char *newaddr = NULL, *simple_addr = NULL, *fcc = NULL;
1082 ENVELOPE *env = NULL;
1083 BODY *body = NULL;
1085 init_ab_if_needed();
1087 if(pith_opt_save_and_restore)
1088 (*pith_opt_save_and_restore)(SAR_SAVE, &state);
1090 for(i = 0; i < as.n_addrbk; i++){
1092 pab = &as.adrbks[i];
1094 if(pab->ostatus != Open && pab->ostatus != NoDisplay)
1095 init_abook(pab, NoDisplay);
1097 list = adrbk_list_of_possible_completions(pab ? pab->address_book : NULL, query);
1098 combine_abook_entry_lists(&biglist, list);
1102 * Eliminate duplicates by NO_NEXTing the entrynums.
1104 for(list = biglist; list; list = list->next)
1105 /* eliminate any dups further along in the list */
1106 if(list->entrynum != NO_NEXT)
1107 for(list2 = list->next; list2; list2 = list2->next)
1108 if(list2->entrynum == list->entrynum){
1109 list2->entrynum = NO_NEXT;
1110 list->matches_bitmap |= list2->matches_bitmap;
1113 /* build the return list */
1114 for(list = biglist; list; list = list->next)
1115 if(list->entrynum != NO_NEXT){
1116 fcc = NULL;
1117 toaddr.type = Abe;
1118 toaddr.arg.abe = adrbk_get_ae(list->ab, list->entrynum);
1119 if(our_build_address(toaddr, &newaddr, NULL, &fcc, NULL) == 0){
1120 char *reverse_fullname = NULL;
1123 * ALC_FULL is a regular FullName match and that will be
1124 * captured in the full_address field. If there was also
1125 * an ALC_REVFULL match that means that the user has the
1126 * FullName entered in their addrbook as Last, First and
1127 * that is where the match was. We want to put that in
1128 * the completions structure in the rev_fullname field.
1130 if(list->matches_bitmap & ALC_REVFULL
1131 && toaddr.arg.abe
1132 && toaddr.arg.abe->fullname && toaddr.arg.abe->fullname[0]
1133 && toaddr.arg.abe->fullname[0] != '"'
1134 && strindex(toaddr.arg.abe->fullname, ',') != NULL){
1136 reverse_fullname = toaddr.arg.abe->fullname;
1139 if(flags & ALC_INCLUDE_ADDRS){
1140 if(toaddr.arg.abe && toaddr.arg.abe->tag == Single
1141 && toaddr.arg.abe->addr.addr && toaddr.arg.abe->addr.addr[0]){
1142 char *tmp_a_string;
1143 char *fakedomain = "@";
1145 tmp_a_string = cpystr(toaddr.arg.abe->addr.addr);
1146 addr = NULL;
1147 rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
1148 if(tmp_a_string)
1149 fs_give((void **) &tmp_a_string);
1151 if(addr){
1152 if(addr->mailbox && addr->host
1153 && !(addr->host[0] == '@' && addr->host[1] == '\0'))
1154 simple_addr = simple_addr_string(addr, buf, sizeof(buf));
1156 mail_free_address(&addr);
1161 new = new_complete_s(toaddr.arg.abe ? toaddr.arg.abe->nickname : NULL,
1162 newaddr, simple_addr, reverse_fullname, fcc,
1163 list->matches_bitmap | ALC_ABOOK);
1165 /* add to end of list */
1166 if(return_list == NULL){
1167 return_list = new;
1168 last_one_added = new;
1170 else{
1171 last_one_added->next = new;
1172 last_one_added = new;
1176 if(newaddr)
1177 fs_give((void **) &newaddr);
1180 if(pith_opt_save_and_restore)
1181 (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
1183 free_abook_entry_s(&biglist);
1185 #ifdef ENABLE_LDAP
1186 if(flags & ALC_INCLUDE_LDAP){
1187 LDAP_SERV_RES_S *head_of_result_list = NULL, *res;
1188 LDAP_CHOOSE_S cs;
1189 WP_ERR_S wp_err;
1190 LDAPMessage *e;
1192 memset(&wp_err, 0, sizeof(wp_err));
1195 * This lookup covers all servers with the impl bit set.
1196 * It uses the regular LDAP search parameters that the
1197 * user has set, not necessarily just a prefix match
1198 * like the rest of the address completion above.
1200 head_of_result_list = ldap_lookup_all_work(query, as.n_serv, 0, NULL, &wp_err);
1201 for(res = head_of_result_list; res; res = res->next){
1202 for(e = ldap_first_entry(res->ld, res->res);
1203 e != NULL;
1204 e = ldap_next_entry(res->ld, e)){
1205 simple_addr = newaddr = NULL;
1206 cs.ld = res->ld;
1207 cs.selected_entry = e;
1208 cs.info_used = res->info_used;
1209 cs.serv = res->serv;
1210 addr = address_from_ldap(&cs);
1211 if(addr){
1212 add_addr_to_return_list(addr, ALC_LDAP, query, flags, &return_list);
1213 mail_free_address(&addr);
1218 if(wp_err.error)
1219 fs_give((void **) &wp_err.error);
1221 if(head_of_result_list)
1222 free_ldap_result_list(&head_of_result_list);
1224 #endif /* ENABLE_LDAP */
1226 /* add from current message */
1227 if(uid > 0 && stream)
1228 env = pine_mail_fetch_structure(stream, uid, &body, FT_UID);
1230 /* from the envelope addresses */
1231 if(env){
1232 for(addr = env->from; addr; addr = addr->next)
1233 add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
1235 for(addr = env->reply_to; addr; addr = addr->next)
1236 add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
1238 for(addr = env->sender; addr; addr = addr->next)
1239 add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
1241 for(addr = env->to; addr; addr = addr->next)
1242 add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
1244 for(addr = env->cc; addr; addr = addr->next)
1245 add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
1248 * May as well search the body for addresses.
1249 * Use this function written for TakeAddr.
1251 if(body){
1252 TA_S *talist = NULL, *tp;
1254 if(grab_addrs_from_body(stream, mail_msgno(stream,uid), body, &talist) > 0){
1255 if(talist){
1257 /* rewind to start */
1258 while(talist->prev)
1259 talist = talist->prev;
1261 for(tp = talist; tp; tp = tp->next){
1262 addr = tp->addr;
1263 if(addr)
1264 add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
1267 free_talines(&talist);
1275 * Check for and eliminate some duplicates.
1276 * The criteria for deciding what is a duplicate is
1277 * kind of ad hoc.
1279 for(cp = return_list; cp; cp = cp->next)
1280 for(dprev = cp, dp = cp->next; dp; ){
1281 if(cp->full_address && dp->full_address
1282 && !strucmp(dp->full_address, cp->full_address)
1283 && (((cp->matches_bitmap & ALC_ABOOK)
1284 && (dp->matches_bitmap & ALC_ABOOK)
1285 && (!(dp->matches_bitmap & ALC_NICK && dp->nickname && dp->nickname[0])
1286 || ((!cp->addr && !dp->addr) || (cp->addr && dp->addr && !strucmp(cp->addr, dp->addr)))))
1288 (dp->matches_bitmap & ALC_CURR)
1290 (dp->matches_bitmap & ALC_LDAP
1291 && dp->matches_bitmap & (ALC_FULL | ALC_ADDR))
1293 (cp->matches_bitmap == dp->matches_bitmap
1294 && (!(dp->matches_bitmap & ALC_NICK && dp->nickname && dp->nickname[0])
1295 || (dp->nickname && cp->nickname
1296 && !strucmp(cp->nickname, dp->nickname)))))){
1298 * dp is equivalent to cp so eliminate dp
1300 dprev->next = dp->next;
1301 dp->next = NULL;
1302 free_complete_s(&dp);
1303 dp = dprev;
1306 dprev = dp;
1307 dp = dp->next;
1310 return(return_list);
1314 void
1315 add_addr_to_return_list(ADDRESS *addr, unsigned bitmap, char *query,
1316 int flags, COMPLETE_S **return_list)
1318 char buf[1000];
1319 char *newaddr = NULL;
1320 char *simple_addr = NULL;
1321 COMPLETE_S *new = NULL, *cp;
1322 ADDRESS *savenext;
1324 if(return_list && query && addr && addr->mailbox && addr->host){
1326 savenext = addr->next;
1327 addr->next = NULL;
1328 newaddr = addr_list_string(addr, NULL, 0);
1329 addr->next = savenext;
1332 * If the start of the full_address actually matches the query
1333 * string then mark this as ALC_FULL. This might be helpful
1334 * when deciding on the longest unambiguous match.
1336 if(newaddr && newaddr[0] && !struncmp(newaddr, query, strlen(query)))
1337 bitmap |= ALC_FULL;
1339 if(newaddr && newaddr[0] && flags & ALC_INCLUDE_ADDRS){
1340 if(addr->mailbox && addr->host
1341 && !(addr->host[0] == '@' && addr->host[1] == '\0'))
1342 simple_addr = simple_addr_string(addr, buf, sizeof(buf));
1344 if(simple_addr && !simple_addr[0])
1345 simple_addr = NULL;
1347 if(simple_addr && !struncmp(simple_addr, query, strlen(query)))
1348 bitmap |= ALC_ADDR;
1352 * We used to require && bitmap & (ALC_FULL | ALC_ADDR) before
1353 * we would add a match but we think that we should match
1354 * other stuff that matches (like middle names), too, unless we
1355 * are adding an address from the current message.
1357 if((newaddr && newaddr[0])
1358 && (!(bitmap & ALC_CURR) || bitmap & (ALC_FULL | ALC_ADDR))){
1359 new = new_complete_s(NULL, newaddr, simple_addr, NULL, NULL, bitmap);
1361 /* add to end of list */
1362 if(*return_list == NULL){
1363 *return_list = new;
1365 else{
1366 for(cp = *return_list; cp->next; cp = cp->next)
1369 cp->next = new;
1373 if(newaddr)
1374 fs_give((void **) &newaddr);
1380 * nick = nickname
1381 * full = whole thing, like Some Body <someb@there.org>
1382 * addr = address part, like someb@there.org
1384 COMPLETE_S *
1385 new_complete_s(char *nick, char *full, char *addr,
1386 char *rev_fullname, char *fcc, unsigned matches_bitmap)
1388 COMPLETE_S *new = NULL;
1390 new = (COMPLETE_S *) fs_get(sizeof(*new));
1391 memset((void *) new, 0, sizeof(*new));
1392 new->nickname = nick ? cpystr(nick) : NULL;
1393 new->full_address = full ? cpystr(full) : NULL;
1394 new->addr = addr ? cpystr(addr) : NULL;
1395 new->rev_fullname = rev_fullname ? cpystr(rev_fullname) : NULL;
1396 new->fcc = fcc ? cpystr(fcc) : NULL;
1397 new->matches_bitmap = matches_bitmap;
1399 return(new);
1403 void
1404 free_complete_s(COMPLETE_S **compptr)
1406 if(compptr && *compptr){
1407 if((*compptr)->next)
1408 free_complete_s(&(*compptr)->next);
1410 if((*compptr)->nickname)
1411 fs_give((void **) &(*compptr)->nickname);
1413 if((*compptr)->full_address)
1414 fs_give((void **) &(*compptr)->full_address);
1416 if((*compptr)->addr)
1417 fs_give((void **) &(*compptr)->addr);
1419 if((*compptr)->rev_fullname)
1420 fs_give((void **) &(*compptr)->rev_fullname);
1422 if((*compptr)->fcc)
1423 fs_give((void **) &(*compptr)->fcc);
1425 fs_give((void **) compptr);
1430 ABOOK_ENTRY_S *
1431 new_abook_entry_s(AdrBk *ab, a_c_arg_t numarg, unsigned bit)
1433 ABOOK_ENTRY_S *new = NULL;
1434 adrbk_cntr_t entrynum;
1436 entrynum = (adrbk_cntr_t) numarg;
1438 new = (ABOOK_ENTRY_S *) fs_get(sizeof(*new));
1439 memset((void *) new, 0, sizeof(*new));
1440 new->ab = ab;
1441 new->entrynum = entrynum;
1442 new->matches_bitmap = bit;
1444 return(new);
1448 void
1449 free_abook_entry_s(ABOOK_ENTRY_S **aep)
1451 if(aep && *aep){
1452 if((*aep)->next)
1453 free_abook_entry_s(&(*aep)->next);
1455 fs_give((void **) aep);
1461 * Add the second list to the end of the first.
1463 void
1464 combine_abook_entry_lists(ABOOK_ENTRY_S **first, ABOOK_ENTRY_S *second)
1466 ABOOK_ENTRY_S *sl;
1468 if(!second)
1469 return;
1471 if(first){
1472 if(*first){
1473 for(sl = *first; sl->next; sl = sl->next)
1476 sl->next = second;
1478 else
1479 *first = second;
1484 ABOOK_ENTRY_S *
1485 adrbk_list_of_possible_completions(AdrBk *ab, char *prefix)
1487 ABOOK_ENTRY_S *list = NULL, *biglist = NULL;
1489 if(!ab || !prefix)
1490 return(biglist);
1492 if(ab->nick_trie){
1493 list = adrbk_list_of_possible_trie_completions(ab->nick_trie, ab, prefix, ALC_NICK);
1494 combine_abook_entry_lists(&biglist, list);
1497 if(ab->full_trie){
1498 list = adrbk_list_of_possible_trie_completions(ab->full_trie, ab, prefix, ALC_FULL);
1499 combine_abook_entry_lists(&biglist, list);
1502 if(ab->addr_trie){
1503 list = adrbk_list_of_possible_trie_completions(ab->addr_trie, ab, prefix, ALC_ADDR);
1504 combine_abook_entry_lists(&biglist, list);
1507 if(ab->revfull_trie){
1508 list = adrbk_list_of_possible_trie_completions(ab->revfull_trie, ab, prefix, ALC_REVFULL);
1509 combine_abook_entry_lists(&biglist, list);
1512 return(biglist);
1517 * Look in this address book for all nicknames, addresses, or fullnames
1518 * which begin with the prefix prefix, and return an allocated
1519 * list of them.
1521 ABOOK_ENTRY_S *
1522 adrbk_list_of_possible_trie_completions(AdrBk_Trie *trie, AdrBk *ab, char *prefix,
1523 unsigned bit)
1525 AdrBk_Trie *t;
1526 char *p, *lookthisup;
1527 char buf[1000];
1528 ABOOK_ENTRY_S *list = NULL;
1530 if(!ab || !prefix || !trie)
1531 return(list);
1533 t = trie;
1535 /* make lookup case independent */
1537 for(p = prefix; *p && !(*p & 0x80) && islower((unsigned char) *p); p++)
1540 if(*p){
1541 strncpy(buf, prefix, sizeof(buf));
1542 buf[sizeof(buf)-1] = '\0';
1543 for(p = buf; *p; p++)
1544 if(!(*p & 0x80) && isupper((unsigned char) *p))
1545 *p = tolower(*p);
1547 lookthisup = buf;
1549 else
1550 lookthisup = prefix;
1552 p = lookthisup;
1554 while(*p){
1555 /* search for character at this level */
1556 while(t->value != *p){
1557 if(t->right == NULL)
1558 return(list); /* no match */
1560 t = t->right;
1563 if(*++p == '\0') /* matched through end of prefix */
1564 break;
1566 /* need to go down to match next character */
1567 if(t->down == NULL) /* no match */
1568 return(list);
1570 t = t->down;
1574 * If we get here that means we found at least
1575 * one entry that matches up through prefix.
1576 * Gather_abook_list recursively adds the nicknames starting at
1577 * this node.
1579 if(t->entrynum != NO_NEXT){
1581 * Add it to the list.
1583 list = new_abook_entry_s(ab, t->entrynum, bit);
1586 gather_abook_entry_list(ab, t->down, prefix, &list, bit);
1588 return(list);
1592 void
1593 gather_abook_entry_list(AdrBk *ab, AdrBk_Trie *node, char *prefix, ABOOK_ENTRY_S **list, unsigned bit)
1595 char *next_prefix = NULL;
1596 size_t l;
1597 ABOOK_ENTRY_S *newlist = NULL;
1599 if(node){
1600 if(node->entrynum != NO_NEXT || node->down || node->right){
1601 l = strlen(prefix ? prefix : "");
1602 if(node->entrynum != NO_NEXT){
1604 * Add it to the list.
1606 newlist = new_abook_entry_s(ab, node->entrynum, bit);
1607 combine_abook_entry_lists(list, newlist);
1610 /* same prefix for node->right */
1611 if(node->right)
1612 gather_abook_entry_list(ab, node->right, prefix, list, bit);
1614 /* prefix is one longer for node->down */
1615 if(node->down){
1616 next_prefix = (char *) fs_get((l+2) * sizeof(char));
1617 strncpy(next_prefix, prefix ? prefix : "", l+2);
1618 next_prefix[l] = node->value;
1619 next_prefix[l+1] = '\0';
1620 gather_abook_entry_list(ab, node->down, next_prefix, list, bit);
1622 if(next_prefix)
1623 fs_give((void **) &next_prefix);