Add support for tab-completion when selecting by rule
[alpine.git] / pith / bldaddr.c
blobc01b865b73bde96dc9cc138168367053379a6918
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 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 /*======================================================================
16 buildaddr.c
17 Support for build_address function and low-level display cache.
18 ====*/
21 #include "../pith/headers.h"
22 #include "../pith/bldaddr.h"
23 #include "../pith/ldap.h"
24 #include "../pith/conf.h"
25 #include "../pith/adrbklib.h"
26 #include "../pith/copyaddr.h"
27 #include "../pith/remote.h"
28 #include "../pith/status.h"
29 #include "../pith/search.h"
30 #include "../pith/addrstring.h"
31 #include "../pith/util.h"
32 #include "../pith/ablookup.h"
33 #include "../pith/news.h"
34 #include "../pith/stream.h"
35 #include "../pith/mailcmd.h"
36 #include "../pith/osdep/pw_stuff.h"
40 * Jump back to this location if we discover that one of the open addrbooks
41 * has been changed by some other process.
43 jmp_buf addrbook_changed_unexpectedly;
47 * Sometimes the address book code calls something outside of the address
48 * book and that calls back to the address book. For example, you could be
49 * in the address book mgmt screen, call ab_compose, and then a ^T could
50 * call back to an address book select screen. Or moving out of a line
51 * calls back to build_address. This keeps track of the nesting level so
52 * that we can know when it is safe to update an out of date address book.
53 * For example, if we know an address book is open and displayed, it isn't
54 * safe to update it because that would pull the cache out from under the
55 * displayed lines. If we don't have it displayed, then it is ok to close
56 * it and re-open it (adrbk_check_and_fix()).
58 int ab_nesting_level = 0;
63 * This is like build_address() only it doesn't close
64 * everything down when it is done, and it doesn't open addrbooks that
65 * are already open. Other than that, it has the same functionality.
66 * It opens addrbooks that haven't been opened and saves and restores the
67 * addrbooks open states (if save_and_restore is set).
69 * Args: to -- the address to attempt expanding (see the
70 * description in expand_address)
71 * full_to -- a pointer to result
72 * This will be allocated here and freed by the caller.
73 * error -- a pointer to an error message, if non-null
74 * fcc -- a pointer to returned fcc, if non-null
75 * This will be allocated here and freed by the caller.
76 * *fcc should be null on entry.
77 * save_and_restore -- restore addrbook states when finished
79 * Results: 0 -- address is ok
80 * -1 -- address is not ok
81 * full_to contains the expanded address on success, or a copy of to
82 * on failure
83 * *error will point to an error message on failure it it is non-null
85 * Side effect: Can flush addrbook entry cache entries so they need to be
86 * re-fetched afterwords.
88 int
89 our_build_address(BuildTo to, char **full_to, char **error, char **fcc,
90 void (*save_and_restore_f)(int, SAVE_STATE_S *))
92 int ret;
94 dprint((7, "- our_build_address - (%s)\n",
95 (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
96 : (to.arg.abe->nickname ? to.arg.abe->nickname
97 : "no nick")));
99 if((to.type == Str && !to.arg.str) || (to.type == Abe && !to.arg.abe)){
100 if(full_to)
101 *full_to = cpystr("");
102 ret = 0;
104 else
105 ret = build_address_internal(to, full_to, error, fcc, NULL, NULL,
106 save_and_restore_f, 0, NULL);
108 dprint((8, " our_build_address says %s address\n",
109 ret ? "BAD" : "GOOD"));
111 return(ret);
116 #define FCC_SET 1 /* Fcc was set */
117 #define LCC_SET 2 /* Lcc was set */
118 #define FCC_NOREPO 4 /* Fcc was set in a non-reproducible way */
119 #define LCC_NOREPO 8 /* Lcc was set in a non-reproducible way */
121 * Given an address, expand it based on address books, local domain, etc.
122 * This will open addrbooks if needed before checking (actually one of
123 * its children will open them).
125 * Args: to -- The given address to expand (see the description
126 * in expand_address)
127 * full_to -- Returned value after parsing to.
128 * error -- This gets pointed at error message, if any
129 * fcc -- Returned value of fcc for first addr in to
130 * no_repo -- Returned value, set to 1 if the fcc or lcc we're
131 * returning is not reproducible from the expanded
132 * address. That is, if we were to run
133 * build_address_internal again on the resulting full_to,
134 * we wouldn't get back the fcc again. For example,
135 * if we expand a list and use the list fcc from the
136 * addrbook, the full_to no longer contains the
137 * information that this was originally list foo.
138 * save_and_restore -- restore addrbook state when done
140 * Result: 0 is returned if address was OK,
141 * -1 if address wasn't OK.
142 * The address is expanded, fully-qualified, and personal name added.
144 * Input may have more than one address separated by commas.
146 * Side effect: Can flush addrbook entry cache entries so they need to be
147 * re-fetched afterwords.
150 build_address_internal(BuildTo to, char **full_to, char **error, char **fcc,
151 int *no_repo, char **lcc,
152 void (*save_and_restore_f)(int, SAVE_STATE_S *),
153 int simple_verify, int *mangled)
155 ADDRESS *a;
156 int loop, i;
157 int tried_route_addr_hack = 0;
158 int did_set = 0;
159 char *tmp = NULL;
160 SAVE_STATE_S state;
161 PerAddrBook *pab;
163 dprint((8, "- build_address_internal - (%s)\n",
164 (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
165 : (to.arg.abe->nickname ? to.arg.abe->nickname
166 : "no nick")));
168 init_ab_if_needed();
169 if(save_and_restore_f)
170 (*save_and_restore_f)(SAR_SAVE, &state);
172 start:
173 loop = 0;
174 ps_global->c_client_error[0] = '\0';
175 wp_exit = wp_nobail = 0;
177 a = expand_address(to, ps_global->maildomain,
178 F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
179 ? ps_global->maildomain : NULL,
180 &loop, fcc, &did_set, lcc, error,
181 0, simple_verify, mangled);
184 * If the address is a route-addr, expand_address() will have rejected
185 * it unless it was enclosed in brackets, since route-addrs can't stand
186 * alone. Try it again with brackets. We should really be checking
187 * each address in the list of addresses instead of assuming there is
188 * only one address, but we don't want to have this function know
189 * all about parsing rfc822 addrs.
191 if(!tried_route_addr_hack &&
192 ps_global->c_client_error[0] != '\0' &&
193 ((to.type == Str && to.arg.str && to.arg.str[0] == '@') ||
194 (to.type == Abe && to.arg.abe->tag == Single &&
195 to.arg.abe->addr.addr[0] == '@'))){
196 BuildTo bldto;
198 tried_route_addr_hack++;
200 tmp = (char *)fs_get((size_t)(MAX_ADDR_FIELD + 3));
202 /* add brackets to whole thing */
203 strncpy(tmp, "<", MAX_ADDR_FIELD+3);
204 tmp[MAX_ADDR_FIELD+3-1] = '\0';
205 if(to.type == Str)
206 strncat(tmp, to.arg.str, MAX_ADDR_FIELD+3-strlen(tmp)-1);
207 else
208 strncat(tmp, to.arg.abe->addr.addr, MAX_ADDR_FIELD+3-strlen(tmp)-1);
210 tmp[MAX_ADDR_FIELD+3-1] = '\0';
211 strncat(tmp, ">", MAX_ADDR_FIELD+3-strlen(tmp)-1);
212 tmp[MAX_ADDR_FIELD+3-1] = '\0';
214 loop = 0;
215 ps_global->c_client_error[0] = '\0';
217 bldto.type = Str;
218 bldto.arg.str = tmp;
220 if(a)
221 mail_free_address(&a);
223 /* try it */
224 a = expand_address(bldto, ps_global->maildomain,
225 F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
226 ? ps_global->maildomain : NULL,
227 &loop, fcc, &did_set, lcc, error,
228 0, simple_verify, mangled);
230 /* if no error this time, use it */
231 if(ps_global->c_client_error[0] == '\0'){
232 if(save_and_restore_f)
233 (*save_and_restore_f)(SAR_RESTORE, &state);
236 * Clear references so that Addrbook Entry caching in adrbklib.c
237 * is allowed to throw them out of cache.
239 for(i = 0; i < as.n_addrbk; i++){
240 pab = &as.adrbks[i];
241 if(pab->ostatus == Open || pab->ostatus == NoDisplay)
242 adrbk_clearrefs(pab->address_book);
245 goto ok;
247 else{ /* go back and use what we had before, so we get the error */
248 if(tmp)
249 fs_give((void **)&tmp);
251 tmp = NULL;
252 goto start;
256 if(save_and_restore_f)
257 (*save_and_restore_f)(SAR_RESTORE, &state);
260 * Clear references so that Addrbook Entry caching in adrbklib.c
261 * is allowed to throw them out of cache.
263 for(i = 0; i < as.n_addrbk; i++){
264 pab = &as.adrbks[i];
265 if(pab->ostatus == Open || pab->ostatus == NoDisplay)
266 adrbk_clearrefs(pab->address_book);
269 if(ps_global->c_client_error[0] != '\0'){
270 /* Parse error. Return string as is and error message */
271 if(full_to){
272 if(to.type == Str)
273 *full_to = cpystr(to.arg.str);
274 else{
275 if(to.arg.abe->nickname && to.arg.abe->nickname[0])
276 *full_to = cpystr(to.arg.abe->nickname);
277 else if(to.arg.abe->tag == Single)
278 *full_to = cpystr(to.arg.abe->addr.addr);
279 else
280 *full_to = cpystr("");
284 if(error != NULL){
285 /* display previous error and add new one */
286 if(*error){
287 q_status_message(SM_ORDER, 3, 5, *error);
288 display_message('x');
289 fs_give((void **)error);
292 *error = cpystr(ps_global->c_client_error);
295 dprint((2,
296 "build_address_internal returning parse error: %s\n",
297 ps_global->c_client_error ? ps_global->c_client_error : "?"));
298 if(a)
299 mail_free_address(&a);
301 if(tmp)
302 fs_give((void **)&tmp);
304 if(mangled){
305 if(ps_global->mangled_screen)
306 *mangled |= BUILDER_SCREEN_MANGLED;
307 else if(ps_global->mangled_footer)
308 *mangled |= BUILDER_FOOTER_MANGLED;
311 return -1;
314 if(mangled){
315 if(ps_global->mangled_screen)
316 *mangled |= BUILDER_SCREEN_MANGLED;
317 else if(ps_global->mangled_footer)
318 *mangled |= BUILDER_FOOTER_MANGLED;
322 * If there's a loop in the addressbook, we modify the address and
323 * send an error back, but we still return 0.
326 if(loop && error != NULL){
327 /* display previous error and add new one */
328 if(*error){
329 q_status_message(SM_ORDER, 3, 5, *error);
330 display_message('x');
331 fs_give((void **)error);
334 *error = cpystr(_("Loop or Duplicate detected in addressbook!"));
338 if(full_to){
339 if(simple_verify){
340 if(tmp){
341 *full_to = tmp; /* add the brackets to route addr */
342 tmp = NULL;
344 else{
345 /* try to return what they sent us */
346 if(to.type == Str)
347 *full_to = cpystr(to.arg.str);
348 else{
349 if(to.arg.abe->nickname && to.arg.abe->nickname[0])
350 *full_to = cpystr(to.arg.abe->nickname);
351 else if(to.arg.abe->tag == Single)
352 *full_to = cpystr(to.arg.abe->addr.addr);
353 else
354 *full_to = cpystr("");
358 else{
359 RFC822BUFFER rbuf;
360 size_t len;
362 len = est_size(a);
363 *full_to = (char *) fs_get(len * sizeof(char));
364 (*full_to)[0] = '\0';
366 * Assume that quotes surrounding the whole personal name are
367 * not meant to be literal quotes. That is, the name
368 * "Joe College, PhD." is quoted so that we won't do the
369 * switcheroo of Last, First, not so that the quotes will be
370 * literal. Rfc822_write_address will put the quotes back if they
371 * are needed, so Joe College would end up looking like
372 * "Joe College, PhD." <joe@somewhere.edu> but not like
373 * "\"Joe College, PhD.\"" <joe@somewhere.edu>.
375 strip_personal_quotes(a);
376 rbuf.f = dummy_soutr;
377 rbuf.s = NULL;
378 rbuf.beg = *full_to;
379 rbuf.cur = *full_to;
380 rbuf.end = (*full_to)+len-1;
381 rfc822_output_address_list(&rbuf, a, 0L, NULL);
382 *rbuf.cur = '\0';
386 if(no_repo && (did_set & FCC_NOREPO || did_set & LCC_NOREPO))
387 *no_repo = 1;
390 * The condition in the leading if means that addressbook fcc's
391 * override the fcc-rule (because did_set will be set).
393 if(fcc && !(did_set & FCC_SET)){
394 char *fcc_got = NULL;
396 if((ps_global->fcc_rule == FCC_RULE_LAST
397 || ps_global->fcc_rule == FCC_RULE_CURRENT)
398 && strcmp(fcc_got = get_fcc(NULL), ps_global->VAR_DEFAULT_FCC)){
399 if(*fcc)
400 fs_give((void **)fcc);
402 *fcc = cpystr(fcc_got);
404 else if(a && a->host){ /* not group syntax */
405 if(*fcc)
406 fs_give((void **)fcc);
408 if(!tmp)
409 tmp = (char *)fs_get((size_t)200);
411 if((ps_global->fcc_rule == FCC_RULE_RECIP ||
412 ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
413 get_uname(a ? a->mailbox : NULL, tmp, 200))
414 *fcc = cpystr(tmp);
415 else
416 *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
418 else{ /* first addr is group syntax */
419 if(!*fcc)
420 *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
421 /* else, leave it alone */
424 if(fcc_got)
425 fs_give((void **)&fcc_got);
428 if(a)
429 mail_free_address(&a);
431 if(tmp)
432 fs_give((void **)&tmp);
434 if(wp_exit)
435 return -1;
437 return 0;
442 * Expand an address string against the address books, local names, and domain.
444 * Args: to -- this is either an address string to parse (one or more
445 * address strings separated by commas) or it is an
446 * AdrBk_Entry, in which case it refers to a single addrbook
447 * entry. If it is an abe, then it is treated the same as
448 * if the nickname of this entry was passed in and we
449 * looked it up in the addrbook, except that it doesn't
450 * actually have to have a non-null nickname.
451 * userdomain -- domain the user is in
452 * localdomain -- domain of the password file (usually same as userdomain)
453 * loop_detected -- pointer to an int we set if we detect a loop in the
454 * address books (or a duplicate in a list)
455 * fcc -- Returned value of fcc for first addr in a_string
456 * did_set -- expand_address set the fcc (need this in case somebody
457 * sets fcc explicitly to a value equal to default-fcc)
458 * simple_verify -- don't add list full names or expand 2nd level lists
460 * Result: An adrlist of expanded addresses is returned
462 * If the localdomain is NULL, then no lookup against the password file will
463 * be done.
465 ADDRESS *
466 expand_address(BuildTo to, char *userdomain, char *localdomain, int *loop_detected,
467 char **fcc, int *did_set, char **lcc, char **error, int recursing,
468 int simple_verify, int *mangled)
470 size_t domain_length, length;
471 ADDRESS *adr, *a, *a_tail, *adr2 = NULL, *a2, *a_temp, *wp_a;
472 AdrBk_Entry *abe, *abe2;
473 char *list, *l1, **l2;
474 char *tmp_a_string, *q;
475 BuildTo bldto;
476 static char *fakedomain;
478 dprint((9, "- expand_address - (%s)\n",
479 (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
480 : (to.arg.abe->nickname ? to.arg.abe->nickname
481 : "no nick")));
483 * We use the domain "@" to detect an unqualified address. If it comes
484 * back from rfc822_parse_adrlist with the host part set to "@", then
485 * we know it must have been unqualified (so we should look it up in the
486 * addressbook). Later, we also use a c-client hack. If an ADDRESS has
487 * a host part that begins with @ then rfc822_write_address()
488 * will write only the local part and leave off the @domain part.
490 * We also malloc enough space here so that we can strcpy over host below.
492 domain_length = MAX(localdomain!=NULL ? strlen(localdomain) : (size_t)0,
493 userdomain!=NULL ? strlen(userdomain) : (size_t)0);
494 if(!recursing){
495 fakedomain = (char *)fs_get(domain_length + 1);
496 memset((void *)fakedomain, '@', domain_length);
497 fakedomain[domain_length] = '\0';
500 adr = NULL;
502 if(to.type == Str){
503 /* rfc822_parse_adrlist feels free to destroy input so send copy */
504 tmp_a_string = cpystr(to.arg.str);
505 /* remove trailing comma */
506 for(q = tmp_a_string + strlen(tmp_a_string) - 1;
507 q >= tmp_a_string && (*q == SPACE || *q == ',');
508 q--)
509 *q = '\0';
511 if(as.n_impl)
512 mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
514 rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
516 if(as.n_impl)
517 mail_parameters(NIL, SET_PARSEPHRASE, NULL);
520 * Short circuit the process if there was a parsing error.
522 if(!recursing && ps_global->c_client_error[0] != '\0')
523 mail_free_address(&adr);
525 fs_give((void **)&tmp_a_string);
527 else{
528 if(!to.arg.abe ||
529 (to.arg.abe->tag == Single &&
530 (!to.arg.abe->addr.addr || to.arg.abe->addr.addr[0] == '\0')) ||
531 (to.arg.abe->tag == List &&
532 (!to.arg.abe->addr.list || !to.arg.abe->addr.list[0] ||
533 to.arg.abe->addr.list[0][0] == '\0'))){
534 adr = NULL;
536 else{
537 /* if we've already looked it up, fake an adr */
538 adr = mail_newaddr();
539 adr->mailbox = cpystr(to.arg.abe->nickname);
540 adr->host = cpystr(fakedomain);
544 for(a = adr, a_tail = adr; a;){
546 /* start or end of c-client group syntax */
547 if(!a->host){
548 a_tail = a;
549 a = a->next;
550 continue;
552 else if(a->host[0] != '@'){
553 /* Already fully qualified hostname */
554 a_tail = a;
555 a = a->next;
557 else{
559 * Hostname is "@" indicating name wasn't qualified.
560 * Need to look up in address book, and the password file.
561 * If no match then fill in the local domain for host.
563 if(to.type == Str)
564 abe = adrbk_lookup_with_opens_by_nick(a->mailbox,
565 !recursing,
566 (int *)NULL, -1);
567 else
568 abe = to.arg.abe;
570 if(simple_verify && abe == NULL){
571 /*--- Move to next address in list -----*/
572 a_tail = a;
573 a = a->next;
575 else if(abe == NULL){
576 WP_ERR_S wp_err;
578 if(F_OFF(F_COMPOSE_REJECTS_UNQUAL, ps_global)){
579 if(localdomain != NULL && a->personal == NULL){
580 /* lookup in passwd file for local full name */
581 a->personal = local_name_lookup(a->mailbox);
582 /* we know a->host is long enough for localdomain */
583 if(a->personal){
584 /* we know a->host is long enough for userdomain */
585 strncpy(a->host, localdomain, domain_length+1);
586 a->host[domain_length] = '\0';
592 * Didn't find it in address book or password
593 * file, try white pages.
595 memset(&wp_err, 0, sizeof(wp_err));
596 wp_err.mangled = mangled;
597 if(!wp_exit && a->personal == NULL &&
598 (wp_a = wp_lookups(a->mailbox, &wp_err, recursing))){
599 if(wp_a->mailbox && wp_a->mailbox[0] &&
600 wp_a->host && wp_a->host[0]){
601 a->personal = wp_a->personal;
602 if(a->adl)fs_give((void **)&a->adl);
603 a->adl = wp_a->adl;
604 if(a->mailbox)fs_give((void **)&a->mailbox);
605 a->mailbox = wp_a->mailbox;
606 if(a->host)fs_give((void **)&a->host);
607 a->host = wp_a->host;
610 fs_give((void **)&wp_a);
613 if(wp_err.error){
615 * If wp_err has already been displayed long enough
616 * just get rid of it. Otherwise, try to fit it in
617 * with any other error messages we have had.
618 * In that case we may display error messages in a
619 * weird order. We'll have to see if this is a problem
620 * in real life.
622 if(status_message_remaining() && error && !wp_exit){
623 if(*error){
624 q_status_message(SM_ORDER, 3, 5, *error);
625 display_message('x');
626 fs_give((void **)error);
629 *error = wp_err.error;
631 else
632 fs_give((void **)&wp_err.error);
635 /* still haven't found it */
636 if(a->host[0] == '@' && !wp_nobail){
637 int space_phrase;
640 * Figure out if there is a space in the mailbox so
641 * that user probably meant it to resolve on the
642 * directory server.
644 space_phrase = (a->mailbox && strindex(a->mailbox, SPACE));
646 if(!wp_nobail && (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global) ||
647 (space_phrase && as.n_impl))){
648 char ebuf[200];
650 /* TRANSLATORS: The first %s is a mailbox, the second is either
651 directory or addressbook */
652 snprintf(ebuf, sizeof(ebuf), _("Address for \"%s\" not in %s"),
653 a->mailbox,
654 (space_phrase && as.n_impl) ? _("directory")
655 : _("addressbook"));
657 if(error){
658 /* display previous error and add new one */
659 if(*error){
660 if(!wp_exit){
661 q_status_message(SM_ORDER, 3, 5, *error);
662 display_message('x');
665 fs_give((void **)error);
668 if(!wp_exit)
669 *error = cpystr(ebuf);
672 if(!wp_exit)
673 strncpy(ps_global->c_client_error, ebuf, 200);
675 if(!recursing)
676 fs_give((void **)&fakedomain);
678 if(adr)
679 mail_free_address(&adr);
681 return(adr);
683 else if(wp_err.wp_err_occurred){
684 if(!recursing){
685 if(error && *error && !wp_exit)
686 strncpy(ps_global->c_client_error, *error, sizeof(ps_global->c_client_error));
687 else
688 strncpy(ps_global->c_client_error, " ", sizeof(ps_global->c_client_error));
690 ps_global->c_client_error[sizeof(ps_global->c_client_error)-1] = '\0';
692 fs_give((void **)&fakedomain);
693 if(adr)
694 mail_free_address(&adr);
696 return(adr);
699 else{
700 /* we know a->host is long enough for userdomain */
701 strncpy(a->host, userdomain, domain_length+1);
702 a->host[domain_length] = '\0';
706 /*--- Move to next address in list -----*/
707 a_tail = a;
708 a = a->next;
711 /* expand first list, but not others if simple_verify */
712 else if(abe->tag == List && simple_verify && recursing){
713 /*--- Move to next address in list -----*/
714 a_tail = a;
715 a = a->next;
717 else{
719 * There was a match in the address book. We have to do a lot
720 * here because the item from the address book might be a
721 * distribution list. Treat the string just like an address
722 * passed in to parse and recurse on it. Then combine
723 * the personal names from address book. Lastly splice
724 * result into address list being processed
727 /* first addr in list and fcc needs to be filled in */
728 if(!recursing && a == adr && fcc && !(*did_set & FCC_SET)){
730 * Easy case for fcc. This is a nickname that has
731 * an fcc associated with it.
733 if(abe->fcc && abe->fcc[0]){
734 if(*fcc)
735 fs_give((void **)fcc);
737 if(!strcmp(abe->fcc, "\"\""))
738 *fcc = cpystr("");
739 else
740 *fcc = cpystr(abe->fcc);
743 * After we expand the list, we no longer remember
744 * that it came from this address book entry, so
745 * we wouldn't be able to set the fcc again based
746 * on the result. This tells our caller to remember
747 * that for us.
749 *did_set |= (FCC_SET | FCC_NOREPO);
752 * else if fcc-rule=fcc-by-nickname, use that
754 else if(abe->nickname && abe->nickname[0] &&
755 (ps_global->fcc_rule == FCC_RULE_NICK ||
756 ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
757 if(*fcc)
758 fs_give((void **)fcc);
760 *fcc = cpystr(abe->nickname);
762 * After we expand the list, we no longer remember
763 * that it came from this address book entry, so
764 * we wouldn't be able to set the fcc again based
765 * on the result. This tells our caller to remember
766 * that for us.
768 *did_set |= (FCC_SET | FCC_NOREPO);
772 /* lcc needs to be filled in */
773 if(a == adr &&
774 lcc &&
775 (!*lcc || !**lcc)){
776 ADDRESS *atmp;
777 char *tmp;
779 /* return fullname for To line */
780 if(abe->fullname && *abe->fullname){
781 RFC822BUFFER rbuf;
782 size_t l, len;
784 if(*lcc)
785 fs_give((void **)lcc);
787 atmp = mail_newaddr();
788 atmp->mailbox = cpystr(abe->fullname);
789 len = est_size(atmp);
790 tmp = (char *) fs_get(len * sizeof(char));
791 tmp[0] = '\0';
792 /* write the phrase with quoting */
793 rbuf.f = dummy_soutr;
794 rbuf.s = NULL;
795 rbuf.beg = tmp;
796 rbuf.cur = tmp;
797 rbuf.end = tmp+len-1;
798 rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
799 *rbuf.cur = '\0';
800 l = strlen(tmp)+1;
801 *lcc = (char *) fs_get((l+1) * sizeof(char));
802 strncpy(*lcc, tmp, l);
803 (*lcc)[l] = '\0';
804 strncat(*lcc, ";", l+1-strlen(*lcc)-1);
805 (*lcc)[l] = '\0';
806 mail_free_address(&atmp);
807 fs_give((void **)&tmp);
808 *did_set |= (LCC_SET | LCC_NOREPO);
812 if(recursing && abe->referenced){
813 /*---- caught an address loop! ----*/
814 fs_give(((void **)&a->host));
815 (*loop_detected)++;
816 a->host = cpystr("***address-loop-in-addressbooks***");
817 continue;
820 abe->referenced++; /* For address loop detection */
821 if(abe->tag == List){
822 length = 0;
823 for(l2 = abe->addr.list; *l2; l2++)
824 length += (strlen(*l2) + 1);
826 list = (char *)fs_get(length + 1);
827 list[0] = '\0';
828 l1 = list;
829 for(l2 = abe->addr.list; *l2; l2++){
830 if(l1 != list && length+1-(l1-list) > 0)
831 *l1++ = ',';
833 strncpy(l1, *l2, length+1-(l1-list));
834 if(l1 > &list[length])
835 l1 = &list[length];
837 list[length] = '\0';
839 l1 += strlen(l1);
842 bldto.type = Str;
843 bldto.arg.str = list;
844 adr2 = expand_address(bldto, userdomain, localdomain,
845 loop_detected, fcc, did_set,
846 lcc, error, 1, simple_verify,
847 mangled);
848 fs_give((void **)&list);
850 else if(abe->tag == Single){
851 if(strucmp(abe->addr.addr, a->mailbox)){
852 bldto.type = Str;
853 bldto.arg.str = abe->addr.addr;
854 adr2 = expand_address(bldto, userdomain,
855 localdomain, loop_detected,
856 fcc, did_set, lcc,
857 error, 1, simple_verify,
858 mangled);
860 else{
862 * A loop within plain single entry is ignored.
863 * Set up so later code thinks we expanded.
865 adr2 = mail_newaddr();
866 adr2->mailbox = cpystr(abe->addr.addr);
867 adr2->host = cpystr(userdomain);
868 adr2->adl = cpystr(a->adl);
872 abe->referenced--; /* Janet Jackson <janet@dialix.oz.au> */
873 if(adr2 == NULL){
874 /* expanded to nothing, hack out of list */
875 a_temp = a;
876 if(a == adr){
877 adr = a->next;
878 a = adr;
879 a_tail = adr;
881 else{
882 a_tail->next = a->next;
883 a = a->next;
886 a_temp->next = NULL; /* So free won't do whole list */
887 mail_free_address(&a_temp);
888 continue;
892 * Personal names: If the expanded address has a personal
893 * name and the address book entry is a list with a fullname,
894 * tack the full name from the address book on in front.
895 * This mainly occurs with a distribution list where the
896 * list has a full name, and the first person in the list also
897 * has a full name.
899 * This algorithm doesn't work very well if lists are
900 * included within lists, but it's not clear what would
901 * be better.
903 if(abe->fullname && abe->fullname[0]){
904 if(adr2->personal && adr2->personal[0]){
905 if(abe->tag == List){
906 /* combine list name and existing name */
907 char *name;
909 if(!simple_verify){
910 size_t l;
912 l = strlen(adr2->personal) + strlen(abe->fullname) + 4;
913 name = (char *) fs_get((l+1) * sizeof(char));
914 snprintf(name, l+1, "%s -- %s", abe->fullname,
915 adr2->personal);
916 fs_give((void **)&adr2->personal);
917 adr2->personal = name;
920 else{
921 /* replace with nickname fullname */
922 fs_give((void **)&adr2->personal);
923 adr2->personal = adrbk_formatname(abe->fullname,
924 NULL, NULL);
927 else{
928 if(abe->tag != List || !simple_verify){
929 if(adr2->personal)
930 fs_give((void **)&adr2->personal);
932 adr2->personal = adrbk_formatname(abe->fullname,
933 NULL, NULL);
938 /* splice new list into old list and remove replaced addr */
939 for(a2 = adr2; a2->next != NULL; a2 = a2->next)
940 ;/* do nothing */
942 a2->next = a->next;
943 if(a == adr)
944 adr = adr2;
945 else
946 a_tail->next = adr2;
948 /* advance to next item, and free replaced ADDRESS */
949 a_tail = a2;
950 a_temp = a;
951 a = a->next;
952 a_temp->next = NULL; /* So free won't do whole list */
953 mail_free_address(&a_temp);
957 if((a_tail == adr && fcc && !(*did_set & FCC_SET))
958 || !a_tail->personal)
960 * This looks for the addressbook entry that matches the given
961 * address. It looks through all the addressbooks
962 * looking for an exact match and then returns that entry.
964 abe2 = address_to_abe(a_tail);
965 else
966 abe2 = NULL;
969 * If there is no personal name yet but we found the address in
970 * an address book, then we take the fullname from that address
971 * book entry and use it. One consequence of this is that if I
972 * have an address book entry with address hubert@cac.washington.edu
973 * and a fullname of Steve Hubert, then there is no way I can
974 * send mail to hubert@cac.washington.edu without having the
975 * personal name filled in for me.
977 if(!a_tail->personal && abe2 && abe2->fullname && abe2->fullname[0])
978 a_tail->personal = adrbk_formatname(abe2->fullname, NULL, NULL);
980 /* if it's first addr in list and fcc hasn't been set yet */
981 if(!recursing && a_tail == adr && fcc && !(*did_set & FCC_SET)){
982 if(abe2 && abe2->fcc && abe2->fcc[0]){
983 if(*fcc)
984 fs_give((void **)fcc);
986 if(!strcmp(abe2->fcc, "\"\""))
987 *fcc = cpystr("");
988 else
989 *fcc = cpystr(abe2->fcc);
991 *did_set |= FCC_SET;
993 else if(abe2 && abe2->nickname && abe2->nickname[0] &&
994 (ps_global->fcc_rule == FCC_RULE_NICK ||
995 ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
996 if(*fcc)
997 fs_give((void **)fcc);
999 *fcc = cpystr(abe2->nickname);
1000 *did_set |= FCC_SET;
1005 * Lcc needs to be filled in.
1006 * Bug: if ^T select was used to put the list in the lcc field, then
1007 * the list will have been expanded already and the fullname for
1008 * the list will be mixed with the initial fullname in the list,
1009 * and we don't have anyway to tell them apart. We could look for
1010 * the --. We could change expand_address so it doesn't combine
1011 * those two addresses.
1013 if(adr &&
1014 a_tail == adr &&
1015 lcc &&
1016 (!*lcc || !**lcc)){
1017 if(adr->personal){
1018 ADDRESS *atmp;
1019 char *tmp;
1020 RFC822BUFFER rbuf;
1021 size_t l, len;
1023 if(*lcc)
1024 fs_give((void **)lcc);
1026 atmp = mail_newaddr();
1027 atmp->mailbox = cpystr(adr->personal);
1028 len = est_size(atmp);
1029 tmp = (char *) fs_get(len * sizeof(char));
1030 tmp[0] = '\0';
1031 /* write the phrase with quoting */
1032 rbuf.f = dummy_soutr;
1033 rbuf.s = NULL;
1034 rbuf.beg = tmp;
1035 rbuf.cur = tmp;
1036 rbuf.end = tmp+len-1;
1037 rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
1038 *rbuf.cur = '\0';
1039 l = strlen(tmp)+1;
1040 *lcc = (char *) fs_get((l+1) * sizeof(char));
1041 strncpy(*lcc, tmp, l);
1042 (*lcc)[l] = '\0';
1043 strncat(*lcc, ";", l+1-strlen(*lcc)-1);
1044 (*lcc)[l] = '\0';
1045 mail_free_address(&atmp);
1046 fs_give((void **)&tmp);
1047 *did_set |= (LCC_SET | LCC_NOREPO);
1052 if(!recursing)
1053 fs_give((void **)&fakedomain);
1055 return(adr);
1060 * This is a call back from rfc822_parse_adrlist. If we return NULL then
1061 * it does its regular parsing. However, if we return and ADDRESS then it
1062 * returns that address to us.
1064 * We want a phrase with no address to be parsed just like a nickname would
1065 * be in expand_address. That is, the phrase gets put in the mailbox name
1066 * and the host is set to the fakedomain. Then expand_address will try to
1067 * look that up using wp_lookup().
1069 * Args phrase -- The start of the phrase of the address
1070 * end -- The first character after the phrase
1071 * host -- The defaulthost that was passed to rfc822_parse_adrlist
1073 * Returns An allocated address, or NULL.
1075 ADDRESS *
1076 massage_phrase_addr(char *phrase, char *end, char *host)
1078 ADDRESS *adr = NULL;
1079 size_t size;
1081 if((size = end - phrase) > 0){
1082 char *mycopy;
1084 mycopy = (char *)fs_get((size+1) * sizeof(char));
1085 strncpy(mycopy, phrase, size);
1086 mycopy[size] = '\0';
1087 removing_trailing_white_space(mycopy);
1090 * If it is quoted we want to leave it alone. It will be treated
1091 * like an atom in parse_adrlist anyway, which is what we want to
1092 * have happen, and we'd have to remove the quotes ourselves here
1093 * if we did it. The problem then is that we don't know if we
1094 * removed the quotes here or they just weren't there in the
1095 * first place.
1097 if(*mycopy == '"' && mycopy[strlen(mycopy)-1] == '"')
1098 fs_give((void **)&mycopy);
1099 else{
1100 adr = mail_newaddr();
1101 adr->mailbox = mycopy;
1102 adr->host = cpystr(host);
1106 return(adr);
1111 * Run through the adrlist "adr" and strip off any enclosing quotes
1112 * around personal names. That is, change "Joe L. Normal" to
1113 * Joe L. Normal.
1115 void
1116 strip_personal_quotes(struct mail_address *adr)
1118 int len;
1119 register char *p, *q;
1121 while(adr){
1122 if(adr->personal){
1123 len = strlen(adr->personal);
1124 if(len > 1
1125 && adr->personal[0] == '"'
1126 && adr->personal[len-1] == '"'){
1127 adr->personal[len-1] = '\0';
1128 p = adr->personal;
1129 q = p + 1;
1130 while((*p++ = *q++) != '\0')
1135 adr = adr->next;
1140 static char *last_fcc_used;
1142 * Returns alloc'd fcc.
1144 char *
1145 get_fcc(char *fcc_arg)
1147 char *fcc;
1150 * Use passed in arg unless it is the same as default (and then
1151 * may use that anyway below).
1153 if(fcc_arg && strcmp(fcc_arg, ps_global->VAR_DEFAULT_FCC))
1154 fcc = cpystr(fcc_arg);
1155 else{
1156 if(ps_global->fcc_rule == FCC_RULE_LAST && last_fcc_used)
1157 fcc = cpystr(last_fcc_used);
1158 else if(ps_global->fcc_rule == FCC_RULE_CURRENT
1159 && ps_global->mail_stream
1160 && !sp_flagged(ps_global->mail_stream, SP_INBOX)
1161 && !IS_NEWS(ps_global->mail_stream)
1162 && ps_global->cur_folder
1163 && ps_global->cur_folder[0]){
1164 CONTEXT_S *cntxt = ps_global->context_current;
1165 char *rs = NULL;
1167 if(((cntxt->use) & CNTXT_SAVEDFLT))
1168 rs = ps_global->cur_folder;
1169 else
1170 rs = ps_global->mail_stream->mailbox;
1172 fcc = cpystr((rs&&*rs) ? rs : ps_global->VAR_DEFAULT_FCC);
1174 else
1175 fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
1178 return(fcc);
1183 * Save the fcc for use with next composition.
1185 void
1186 set_last_fcc(char *fcc)
1188 size_t l;
1190 if(fcc){
1191 if(!last_fcc_used)
1192 last_fcc_used = cpystr(fcc);
1193 else if(strcmp(last_fcc_used, fcc)){
1194 if((l=strlen(last_fcc_used)) >= strlen(fcc)){
1195 strncpy(last_fcc_used, fcc, l+1);
1196 last_fcc_used[l] = '\0';
1198 else{
1199 fs_give((void **)&last_fcc_used);
1200 last_fcc_used = cpystr(fcc);
1208 * Figure out what the fcc is based on the given to address.
1210 * Returns an allocated copy of the fcc, to be freed by the caller, or NULL.
1212 char *
1213 get_fcc_based_on_to(struct mail_address *to)
1215 ADDRESS *next_addr;
1216 char *bufp, *fcc;
1217 BuildTo bldto;
1218 size_t len;
1220 if(!to || !to->host || to->host[0] == '.')
1221 return(NULL);
1223 /* pick off first address */
1224 next_addr = to->next;
1225 to->next = NULL;
1226 len = est_size(to);
1227 bufp = (char *) fs_get(len * sizeof(char));
1228 bufp[0] = '\0';
1230 bldto.type = Str;
1231 bldto.arg.str = cpystr(addr_string(to, bufp, len));
1233 fs_give((void **)&bufp);
1234 to->next = next_addr;
1236 fcc = NULL;
1238 (void) build_address_internal(bldto, NULL, NULL, &fcc, NULL, NULL, NULL, 0, NULL);
1240 if(bldto.arg.str)
1241 fs_give((void **) &bldto.arg.str);
1243 return(fcc);
1248 * Free storage in headerentry.bldr_private.
1250 void
1251 free_privatetop(PrivateTop **pt)
1253 if(pt && *pt){
1254 if((*pt)->affector)
1255 fs_give((void **)&(*pt)->affector);
1257 fs_give((void **)pt);
1261 void
1262 free_bldaddr_module_globals(void)
1264 if(last_fcc_used) fs_give((void **)&last_fcc_used);