* clear out some warnings by gcc 9.3.1.
[alpine.git] / pith / bldaddr.c
blob88ae86508c0a70e88a7bbd5a03ad39e81772371d
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: bldaddr.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 buildaddr.c
21 Support for build_address function and low-level display cache.
22 ====*/
25 #include "../pith/headers.h"
26 #include "../pith/bldaddr.h"
27 #include "../pith/ldap.h"
28 #include "../pith/conf.h"
29 #include "../pith/adrbklib.h"
30 #include "../pith/copyaddr.h"
31 #include "../pith/remote.h"
32 #include "../pith/status.h"
33 #include "../pith/search.h"
34 #include "../pith/addrstring.h"
35 #include "../pith/util.h"
36 #include "../pith/ablookup.h"
37 #include "../pith/news.h"
38 #include "../pith/stream.h"
39 #include "../pith/mailcmd.h"
40 #include "../pith/osdep/pw_stuff.h"
44 * Jump back to this location if we discover that one of the open addrbooks
45 * has been changed by some other process.
47 jmp_buf addrbook_changed_unexpectedly;
51 * Sometimes the address book code calls something outside of the address
52 * book and that calls back to the address book. For example, you could be
53 * in the address book mgmt screen, call ab_compose, and then a ^T could
54 * call back to an address book select screen. Or moving out of a line
55 * calls back to build_address. This keeps track of the nesting level so
56 * that we can know when it is safe to update an out of date address book.
57 * For example, if we know an address book is open and displayed, it isn't
58 * safe to update it because that would pull the cache out from under the
59 * displayed lines. If we don't have it displayed, then it is ok to close
60 * it and re-open it (adrbk_check_and_fix()).
62 int ab_nesting_level = 0;
67 * This is like build_address() only it doesn't close
68 * everything down when it is done, and it doesn't open addrbooks that
69 * are already open. Other than that, it has the same functionality.
70 * It opens addrbooks that haven't been opened and saves and restores the
71 * addrbooks open states (if save_and_restore is set).
73 * Args: to -- the address to attempt expanding (see the
74 * description in expand_address)
75 * full_to -- a pointer to result
76 * This will be allocated here and freed by the caller.
77 * error -- a pointer to an error message, if non-null
78 * fcc -- a pointer to returned fcc, if non-null
79 * This will be allocated here and freed by the caller.
80 * *fcc should be null on entry.
81 * save_and_restore -- restore addrbook states when finished
83 * Results: 0 -- address is ok
84 * -1 -- address is not ok
85 * full_to contains the expanded address on success, or a copy of to
86 * on failure
87 * *error will point to an error message on failure it it is non-null
89 * Side effect: Can flush addrbook entry cache entries so they need to be
90 * re-fetched afterwords.
92 int
93 our_build_address(BuildTo to, char **full_to, char **error, char **fcc,
94 void (*save_and_restore_f)(int, SAVE_STATE_S *))
96 int ret;
98 dprint((7, "- our_build_address - (%s)\n",
99 (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
100 : (to.arg.abe->nickname ? to.arg.abe->nickname
101 : "no nick")));
103 if((to.type == Str && !to.arg.str) || (to.type == Abe && !to.arg.abe)){
104 if(full_to)
105 *full_to = cpystr("");
106 ret = 0;
108 else
109 ret = build_address_internal(to, full_to, error, fcc, NULL, NULL,
110 save_and_restore_f, 0, NULL);
112 dprint((8, " our_build_address says %s address\n",
113 ret ? "BAD" : "GOOD"));
115 return(ret);
120 #define FCC_SET 1 /* Fcc was set */
121 #define LCC_SET 2 /* Lcc was set */
122 #define FCC_NOREPO 4 /* Fcc was set in a non-reproducible way */
123 #define LCC_NOREPO 8 /* Lcc was set in a non-reproducible way */
125 * Given an address, expand it based on address books, local domain, etc.
126 * This will open addrbooks if needed before checking (actually one of
127 * its children will open them).
129 * Args: to -- The given address to expand (see the description
130 * in expand_address)
131 * full_to -- Returned value after parsing to.
132 * error -- This gets pointed at error message, if any
133 * fcc -- Returned value of fcc for first addr in to
134 * no_repo -- Returned value, set to 1 if the fcc or lcc we're
135 * returning is not reproducible from the expanded
136 * address. That is, if we were to run
137 * build_address_internal again on the resulting full_to,
138 * we wouldn't get back the fcc again. For example,
139 * if we expand a list and use the list fcc from the
140 * addrbook, the full_to no longer contains the
141 * information that this was originally list foo.
142 * save_and_restore -- restore addrbook state when done
144 * Result: 0 is returned if address was OK,
145 * -1 if address wasn't OK.
146 * The address is expanded, fully-qualified, and personal name added.
148 * Input may have more than one address separated by commas.
150 * Side effect: Can flush addrbook entry cache entries so they need to be
151 * re-fetched afterwords.
154 build_address_internal(BuildTo to, char **full_to, char **error, char **fcc,
155 int *no_repo, char **lcc,
156 void (*save_and_restore_f)(int, SAVE_STATE_S *),
157 int simple_verify, int *mangled)
159 ADDRESS *a;
160 int loop, i;
161 int tried_route_addr_hack = 0;
162 int did_set = 0;
163 char *tmp = NULL;
164 SAVE_STATE_S state;
165 PerAddrBook *pab;
167 dprint((8, "- build_address_internal - (%s)\n",
168 (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
169 : (to.arg.abe->nickname ? to.arg.abe->nickname
170 : "no nick")));
172 init_ab_if_needed();
173 if(save_and_restore_f)
174 (*save_and_restore_f)(SAR_SAVE, &state);
176 start:
177 loop = 0;
178 ps_global->c_client_error[0] = '\0';
179 wp_exit = wp_nobail = 0;
181 a = expand_address(to, ps_global->maildomain,
182 F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
183 ? ps_global->maildomain : NULL,
184 &loop, fcc, &did_set, lcc, error,
185 0, simple_verify, mangled);
188 * If the address is a route-addr, expand_address() will have rejected
189 * it unless it was enclosed in brackets, since route-addrs can't stand
190 * alone. Try it again with brackets. We should really be checking
191 * each address in the list of addresses instead of assuming there is
192 * only one address, but we don't want to have this function know
193 * all about parsing rfc822 addrs.
195 if(!tried_route_addr_hack &&
196 ps_global->c_client_error[0] != '\0' &&
197 ((to.type == Str && to.arg.str && to.arg.str[0] == '@') ||
198 (to.type == Abe && to.arg.abe->tag == Single &&
199 to.arg.abe->addr.addr[0] == '@'))){
200 BuildTo bldto;
202 tried_route_addr_hack++;
204 tmp = (char *)fs_get((size_t)(MAX_ADDR_FIELD + 3));
206 /* add brackets to whole thing */
207 strncpy(tmp, "<", MAX_ADDR_FIELD+3);
208 tmp[MAX_ADDR_FIELD+3-1] = '\0';
209 if(to.type == Str)
210 strncat(tmp, to.arg.str, MAX_ADDR_FIELD+3-strlen(tmp)-1);
211 else
212 strncat(tmp, to.arg.abe->addr.addr, MAX_ADDR_FIELD+3-strlen(tmp)-1);
214 tmp[MAX_ADDR_FIELD+3-1] = '\0';
215 strncat(tmp, ">", MAX_ADDR_FIELD+3-strlen(tmp)-1);
216 tmp[MAX_ADDR_FIELD+3-1] = '\0';
218 loop = 0;
219 ps_global->c_client_error[0] = '\0';
221 bldto.type = Str;
222 bldto.arg.str = tmp;
224 if(a)
225 mail_free_address(&a);
227 /* try it */
228 a = expand_address(bldto, ps_global->maildomain,
229 F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
230 ? ps_global->maildomain : NULL,
231 &loop, fcc, &did_set, lcc, error,
232 0, simple_verify, mangled);
234 /* if no error this time, use it */
235 if(ps_global->c_client_error[0] == '\0'){
236 if(save_and_restore_f)
237 (*save_and_restore_f)(SAR_RESTORE, &state);
240 * Clear references so that Addrbook Entry caching in adrbklib.c
241 * is allowed to throw them out of cache.
243 for(i = 0; i < as.n_addrbk; i++){
244 pab = &as.adrbks[i];
245 if(pab->ostatus == Open || pab->ostatus == NoDisplay)
246 adrbk_clearrefs(pab->address_book);
249 goto ok;
251 else{ /* go back and use what we had before, so we get the error */
252 if(tmp)
253 fs_give((void **)&tmp);
255 tmp = NULL;
256 goto start;
260 if(save_and_restore_f)
261 (*save_and_restore_f)(SAR_RESTORE, &state);
264 * Clear references so that Addrbook Entry caching in adrbklib.c
265 * is allowed to throw them out of cache.
267 for(i = 0; i < as.n_addrbk; i++){
268 pab = &as.adrbks[i];
269 if(pab->ostatus == Open || pab->ostatus == NoDisplay)
270 adrbk_clearrefs(pab->address_book);
273 if(ps_global->c_client_error[0] != '\0'){
274 /* Parse error. Return string as is and error message */
275 if(full_to){
276 if(to.type == Str)
277 *full_to = cpystr(to.arg.str);
278 else{
279 if(to.arg.abe->nickname && to.arg.abe->nickname[0])
280 *full_to = cpystr(to.arg.abe->nickname);
281 else if(to.arg.abe->tag == Single)
282 *full_to = cpystr(to.arg.abe->addr.addr);
283 else
284 *full_to = cpystr("");
288 if(error != NULL){
289 /* display previous error and add new one */
290 if(*error){
291 q_status_message(SM_ORDER, 3, 5, *error);
292 display_message('x');
293 fs_give((void **)error);
296 *error = cpystr(ps_global->c_client_error);
299 dprint((2,
300 "build_address_internal returning parse error: %s\n",
301 ps_global->c_client_error ? ps_global->c_client_error : "?"));
302 if(a)
303 mail_free_address(&a);
305 if(tmp)
306 fs_give((void **)&tmp);
308 if(mangled){
309 if(ps_global->mangled_screen)
310 *mangled |= BUILDER_SCREEN_MANGLED;
311 else if(ps_global->mangled_footer)
312 *mangled |= BUILDER_FOOTER_MANGLED;
315 return -1;
318 if(mangled){
319 if(ps_global->mangled_screen)
320 *mangled |= BUILDER_SCREEN_MANGLED;
321 else if(ps_global->mangled_footer)
322 *mangled |= BUILDER_FOOTER_MANGLED;
326 * If there's a loop in the addressbook, we modify the address and
327 * send an error back, but we still return 0.
330 if(loop && error != NULL){
331 /* display previous error and add new one */
332 if(*error){
333 q_status_message(SM_ORDER, 3, 5, *error);
334 display_message('x');
335 fs_give((void **)error);
338 *error = cpystr(_("Loop or Duplicate detected in addressbook!"));
342 if(full_to){
343 if(simple_verify){
344 if(tmp){
345 *full_to = tmp; /* add the brackets to route addr */
346 tmp = NULL;
348 else{
349 /* try to return what they sent us */
350 if(to.type == Str)
351 *full_to = cpystr(to.arg.str);
352 else{
353 if(to.arg.abe->nickname && to.arg.abe->nickname[0])
354 *full_to = cpystr(to.arg.abe->nickname);
355 else if(to.arg.abe->tag == Single)
356 *full_to = cpystr(to.arg.abe->addr.addr);
357 else
358 *full_to = cpystr("");
362 else{
363 RFC822BUFFER rbuf;
364 size_t len;
366 len = est_size(a);
367 *full_to = (char *) fs_get(len * sizeof(char));
368 (*full_to)[0] = '\0';
370 * Assume that quotes surrounding the whole personal name are
371 * not meant to be literal quotes. That is, the name
372 * "Joe College, PhD." is quoted so that we won't do the
373 * switcheroo of Last, First, not so that the quotes will be
374 * literal. Rfc822_write_address will put the quotes back if they
375 * are needed, so Joe College would end up looking like
376 * "Joe College, PhD." <joe@somewhere.edu> but not like
377 * "\"Joe College, PhD.\"" <joe@somewhere.edu>.
379 strip_personal_quotes(a);
380 rbuf.f = dummy_soutr;
381 rbuf.s = NULL;
382 rbuf.beg = *full_to;
383 rbuf.cur = *full_to;
384 rbuf.end = (*full_to)+len-1;
385 rfc822_output_address_list(&rbuf, a, 0L, NULL);
386 *rbuf.cur = '\0';
390 if(no_repo && (did_set & FCC_NOREPO || did_set & LCC_NOREPO))
391 *no_repo = 1;
394 * The condition in the leading if means that addressbook fcc's
395 * override the fcc-rule (because did_set will be set).
397 if(fcc && !(did_set & FCC_SET)){
398 char *fcc_got = NULL;
400 if((ps_global->fcc_rule == FCC_RULE_LAST
401 || ps_global->fcc_rule == FCC_RULE_CURRENT)
402 && strcmp(fcc_got = get_fcc(NULL), ps_global->VAR_DEFAULT_FCC)){
403 if(*fcc)
404 fs_give((void **)fcc);
406 *fcc = cpystr(fcc_got);
408 else if(a && a->host){ /* not group syntax */
409 if(*fcc)
410 fs_give((void **)fcc);
412 if(!tmp)
413 tmp = (char *)fs_get((size_t)200);
415 if((ps_global->fcc_rule == FCC_RULE_RECIP ||
416 ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
417 get_uname(a ? a->mailbox : NULL, tmp, 200))
418 *fcc = cpystr(tmp);
419 else
420 *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
422 else{ /* first addr is group syntax */
423 if(!*fcc)
424 *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
425 /* else, leave it alone */
428 if(fcc_got)
429 fs_give((void **)&fcc_got);
432 if(a)
433 mail_free_address(&a);
435 if(tmp)
436 fs_give((void **)&tmp);
438 if(wp_exit)
439 return -1;
441 return 0;
446 * Expand an address string against the address books, local names, and domain.
448 * Args: to -- this is either an address string to parse (one or more
449 * address strings separated by commas) or it is an
450 * AdrBk_Entry, in which case it refers to a single addrbook
451 * entry. If it is an abe, then it is treated the same as
452 * if the nickname of this entry was passed in and we
453 * looked it up in the addrbook, except that it doesn't
454 * actually have to have a non-null nickname.
455 * userdomain -- domain the user is in
456 * localdomain -- domain of the password file (usually same as userdomain)
457 * loop_detected -- pointer to an int we set if we detect a loop in the
458 * address books (or a duplicate in a list)
459 * fcc -- Returned value of fcc for first addr in a_string
460 * did_set -- expand_address set the fcc (need this in case somebody
461 * sets fcc explicitly to a value equal to default-fcc)
462 * simple_verify -- don't add list full names or expand 2nd level lists
464 * Result: An adrlist of expanded addresses is returned
466 * If the localdomain is NULL, then no lookup against the password file will
467 * be done.
469 ADDRESS *
470 expand_address(BuildTo to, char *userdomain, char *localdomain, int *loop_detected,
471 char **fcc, int *did_set, char **lcc, char **error, int recursing,
472 int simple_verify, int *mangled)
474 size_t domain_length, length;
475 ADDRESS *adr, *a, *a_tail, *adr2, *a2, *a_temp, *wp_a;
476 AdrBk_Entry *abe, *abe2;
477 char *list, *l1, **l2;
478 char *tmp_a_string, *q;
479 BuildTo bldto;
480 static char *fakedomain;
482 dprint((9, "- expand_address - (%s)\n",
483 (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
484 : (to.arg.abe->nickname ? to.arg.abe->nickname
485 : "no nick")));
487 * We use the domain "@" to detect an unqualified address. If it comes
488 * back from rfc822_parse_adrlist with the host part set to "@", then
489 * we know it must have been unqualified (so we should look it up in the
490 * addressbook). Later, we also use a c-client hack. If an ADDRESS has
491 * a host part that begins with @ then rfc822_write_address()
492 * will write only the local part and leave off the @domain part.
494 * We also malloc enough space here so that we can strcpy over host below.
496 domain_length = MAX(localdomain!=NULL ? strlen(localdomain) : (size_t)0,
497 userdomain!=NULL ? strlen(userdomain) : (size_t)0);
498 if(!recursing){
499 fakedomain = (char *)fs_get(domain_length + 1);
500 memset((void *)fakedomain, '@', domain_length);
501 fakedomain[domain_length] = '\0';
504 adr = NULL;
506 if(to.type == Str){
507 /* rfc822_parse_adrlist feels free to destroy input so send copy */
508 tmp_a_string = cpystr(to.arg.str);
509 /* remove trailing comma */
510 for(q = tmp_a_string + strlen(tmp_a_string) - 1;
511 q >= tmp_a_string && (*q == SPACE || *q == ',');
512 q--)
513 *q = '\0';
515 if(as.n_impl)
516 mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
518 rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
520 if(as.n_impl)
521 mail_parameters(NIL, SET_PARSEPHRASE, NULL);
524 * Short circuit the process if there was a parsing error.
526 if(!recursing && ps_global->c_client_error[0] != '\0')
527 mail_free_address(&adr);
529 fs_give((void **)&tmp_a_string);
531 else{
532 if(!to.arg.abe ||
533 (to.arg.abe->tag == Single &&
534 (!to.arg.abe->addr.addr || to.arg.abe->addr.addr[0] == '\0')) ||
535 (to.arg.abe->tag == List &&
536 (!to.arg.abe->addr.list || !to.arg.abe->addr.list[0] ||
537 to.arg.abe->addr.list[0][0] == '\0'))){
538 adr = NULL;
540 else{
541 /* if we've already looked it up, fake an adr */
542 adr = mail_newaddr();
543 adr->mailbox = cpystr(to.arg.abe->nickname);
544 adr->host = cpystr(fakedomain);
548 for(a = adr, a_tail = adr; a;){
550 /* start or end of c-client group syntax */
551 if(!a->host){
552 a_tail = a;
553 a = a->next;
554 continue;
556 else if(a->host[0] != '@'){
557 /* Already fully qualified hostname */
558 a_tail = a;
559 a = a->next;
561 else{
563 * Hostname is "@" indicating name wasn't qualified.
564 * Need to look up in address book, and the password file.
565 * If no match then fill in the local domain for host.
567 if(to.type == Str)
568 abe = adrbk_lookup_with_opens_by_nick(a->mailbox,
569 !recursing,
570 (int *)NULL, -1);
571 else
572 abe = to.arg.abe;
574 if(simple_verify && abe == NULL){
575 /*--- Move to next address in list -----*/
576 a_tail = a;
577 a = a->next;
579 else if(abe == NULL){
580 WP_ERR_S wp_err;
582 if(F_OFF(F_COMPOSE_REJECTS_UNQUAL, ps_global)){
583 if(localdomain != NULL && a->personal == NULL){
584 /* lookup in passwd file for local full name */
585 a->personal = local_name_lookup(a->mailbox);
586 /* we know a->host is long enough for localdomain */
587 if(a->personal){
588 /* we know a->host is long enough for userdomain */
589 strncpy(a->host, localdomain, domain_length+1);
590 a->host[domain_length] = '\0';
596 * Didn't find it in address book or password
597 * file, try white pages.
599 memset(&wp_err, 0, sizeof(wp_err));
600 wp_err.mangled = mangled;
601 if(!wp_exit && a->personal == NULL &&
602 (wp_a = wp_lookups(a->mailbox, &wp_err, recursing))){
603 if(wp_a->mailbox && wp_a->mailbox[0] &&
604 wp_a->host && wp_a->host[0]){
605 a->personal = wp_a->personal;
606 if(a->adl)fs_give((void **)&a->adl);
607 a->adl = wp_a->adl;
608 if(a->mailbox)fs_give((void **)&a->mailbox);
609 a->mailbox = wp_a->mailbox;
610 if(a->host)fs_give((void **)&a->host);
611 a->host = wp_a->host;
614 fs_give((void **)&wp_a);
617 if(wp_err.error){
619 * If wp_err has already been displayed long enough
620 * just get rid of it. Otherwise, try to fit it in
621 * with any other error messages we have had.
622 * In that case we may display error messages in a
623 * weird order. We'll have to see if this is a problem
624 * in real life.
626 if(status_message_remaining() && error && !wp_exit){
627 if(*error){
628 q_status_message(SM_ORDER, 3, 5, *error);
629 display_message('x');
630 fs_give((void **)error);
633 *error = wp_err.error;
635 else
636 fs_give((void **)&wp_err.error);
639 /* still haven't found it */
640 if(a->host[0] == '@' && !wp_nobail){
641 int space_phrase;
644 * Figure out if there is a space in the mailbox so
645 * that user probably meant it to resolve on the
646 * directory server.
648 space_phrase = (a->mailbox && strindex(a->mailbox, SPACE));
650 if(!wp_nobail && (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global) ||
651 (space_phrase && as.n_impl))){
652 char ebuf[200];
654 /* TRANSLATORS: The first %s is a mailbox, the second is either
655 directory or addressbook */
656 snprintf(ebuf, sizeof(ebuf), _("Address for \"%s\" not in %s"),
657 a->mailbox,
658 (space_phrase && as.n_impl) ? _("directory")
659 : _("addressbook"));
661 if(error){
662 /* display previous error and add new one */
663 if(*error){
664 if(!wp_exit){
665 q_status_message(SM_ORDER, 3, 5, *error);
666 display_message('x');
669 fs_give((void **)error);
672 if(!wp_exit)
673 *error = cpystr(ebuf);
676 if(!wp_exit)
677 strncpy(ps_global->c_client_error, ebuf, 200);
679 if(!recursing)
680 fs_give((void **)&fakedomain);
682 if(adr)
683 mail_free_address(&adr);
685 return(adr);
687 else if(wp_err.wp_err_occurred){
688 if(!recursing){
689 if(error && *error && !wp_exit)
690 strncpy(ps_global->c_client_error, *error, sizeof(ps_global->c_client_error));
691 else
692 strncpy(ps_global->c_client_error, " ", sizeof(ps_global->c_client_error));
694 ps_global->c_client_error[sizeof(ps_global->c_client_error)-1] = '\0';
696 fs_give((void **)&fakedomain);
697 if(adr)
698 mail_free_address(&adr);
700 return(adr);
703 else{
704 /* we know a->host is long enough for userdomain */
705 strncpy(a->host, userdomain, domain_length+1);
706 a->host[domain_length] = '\0';
710 /*--- Move to next address in list -----*/
711 a_tail = a;
712 a = a->next;
715 /* expand first list, but not others if simple_verify */
716 else if(abe->tag == List && simple_verify && recursing){
717 /*--- Move to next address in list -----*/
718 a_tail = a;
719 a = a->next;
721 else{
723 * There was a match in the address book. We have to do a lot
724 * here because the item from the address book might be a
725 * distribution list. Treat the string just like an address
726 * passed in to parse and recurse on it. Then combine
727 * the personal names from address book. Lastly splice
728 * result into address list being processed
731 /* first addr in list and fcc needs to be filled in */
732 if(!recursing && a == adr && fcc && !(*did_set & FCC_SET)){
734 * Easy case for fcc. This is a nickname that has
735 * an fcc associated with it.
737 if(abe->fcc && abe->fcc[0]){
738 if(*fcc)
739 fs_give((void **)fcc);
741 if(!strcmp(abe->fcc, "\"\""))
742 *fcc = cpystr("");
743 else
744 *fcc = cpystr(abe->fcc);
747 * After we expand the list, we no longer remember
748 * that it came from this address book entry, so
749 * we wouldn't be able to set the fcc again based
750 * on the result. This tells our caller to remember
751 * that for us.
753 *did_set |= (FCC_SET | FCC_NOREPO);
756 * else if fcc-rule=fcc-by-nickname, use that
758 else if(abe->nickname && abe->nickname[0] &&
759 (ps_global->fcc_rule == FCC_RULE_NICK ||
760 ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
761 if(*fcc)
762 fs_give((void **)fcc);
764 *fcc = cpystr(abe->nickname);
766 * After we expand the list, we no longer remember
767 * that it came from this address book entry, so
768 * we wouldn't be able to set the fcc again based
769 * on the result. This tells our caller to remember
770 * that for us.
772 *did_set |= (FCC_SET | FCC_NOREPO);
776 /* lcc needs to be filled in */
777 if(a == adr &&
778 lcc &&
779 (!*lcc || !**lcc)){
780 ADDRESS *atmp;
781 char *tmp;
783 /* return fullname for To line */
784 if(abe->fullname && *abe->fullname){
785 RFC822BUFFER rbuf;
786 size_t l, len;
788 if(*lcc)
789 fs_give((void **)lcc);
791 atmp = mail_newaddr();
792 atmp->mailbox = cpystr(abe->fullname);
793 len = est_size(atmp);
794 tmp = (char *) fs_get(len * sizeof(char));
795 tmp[0] = '\0';
796 /* write the phrase with quoting */
797 rbuf.f = dummy_soutr;
798 rbuf.s = NULL;
799 rbuf.beg = tmp;
800 rbuf.cur = tmp;
801 rbuf.end = tmp+len-1;
802 rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
803 *rbuf.cur = '\0';
804 l = strlen(tmp)+1;
805 *lcc = (char *) fs_get((l+1) * sizeof(char));
806 strncpy(*lcc, tmp, l);
807 (*lcc)[l] = '\0';
808 strncat(*lcc, ";", l+1-strlen(*lcc)-1);
809 (*lcc)[l] = '\0';
810 mail_free_address(&atmp);
811 fs_give((void **)&tmp);
812 *did_set |= (LCC_SET | LCC_NOREPO);
816 if(recursing && abe->referenced){
817 /*---- caught an address loop! ----*/
818 fs_give(((void **)&a->host));
819 (*loop_detected)++;
820 a->host = cpystr("***address-loop-in-addressbooks***");
821 continue;
824 abe->referenced++; /* For address loop detection */
825 if(abe->tag == List){
826 length = 0;
827 for(l2 = abe->addr.list; *l2; l2++)
828 length += (strlen(*l2) + 1);
830 list = (char *)fs_get(length + 1);
831 list[0] = '\0';
832 l1 = list;
833 for(l2 = abe->addr.list; *l2; l2++){
834 if(l1 != list && length+1-(l1-list) > 0)
835 *l1++ = ',';
837 strncpy(l1, *l2, length+1-(l1-list));
838 if(l1 > &list[length])
839 l1 = &list[length];
841 list[length] = '\0';
843 l1 += strlen(l1);
846 bldto.type = Str;
847 bldto.arg.str = list;
848 adr2 = expand_address(bldto, userdomain, localdomain,
849 loop_detected, fcc, did_set,
850 lcc, error, 1, simple_verify,
851 mangled);
852 fs_give((void **)&list);
854 else if(abe->tag == Single){
855 if(strucmp(abe->addr.addr, a->mailbox)){
856 bldto.type = Str;
857 bldto.arg.str = abe->addr.addr;
858 adr2 = expand_address(bldto, userdomain,
859 localdomain, loop_detected,
860 fcc, did_set, lcc,
861 error, 1, simple_verify,
862 mangled);
864 else{
866 * A loop within plain single entry is ignored.
867 * Set up so later code thinks we expanded.
869 adr2 = mail_newaddr();
870 adr2->mailbox = cpystr(abe->addr.addr);
871 adr2->host = cpystr(userdomain);
872 adr2->adl = cpystr(a->adl);
876 abe->referenced--; /* Janet Jackson <janet@dialix.oz.au> */
877 if(adr2 == NULL){
878 /* expanded to nothing, hack out of list */
879 a_temp = a;
880 if(a == adr){
881 adr = a->next;
882 a = adr;
883 a_tail = adr;
885 else{
886 a_tail->next = a->next;
887 a = a->next;
890 a_temp->next = NULL; /* So free won't do whole list */
891 mail_free_address(&a_temp);
892 continue;
896 * Personal names: If the expanded address has a personal
897 * name and the address book entry is a list with a fullname,
898 * tack the full name from the address book on in front.
899 * This mainly occurs with a distribution list where the
900 * list has a full name, and the first person in the list also
901 * has a full name.
903 * This algorithm doesn't work very well if lists are
904 * included within lists, but it's not clear what would
905 * be better.
907 if(abe->fullname && abe->fullname[0]){
908 if(adr2->personal && adr2->personal[0]){
909 if(abe->tag == List){
910 /* combine list name and existing name */
911 char *name;
913 if(!simple_verify){
914 size_t l;
916 l = strlen(adr2->personal) + strlen(abe->fullname) + 4;
917 name = (char *) fs_get((l+1) * sizeof(char));
918 snprintf(name, l+1, "%s -- %s", abe->fullname,
919 adr2->personal);
920 fs_give((void **)&adr2->personal);
921 adr2->personal = name;
924 else{
925 /* replace with nickname fullname */
926 fs_give((void **)&adr2->personal);
927 adr2->personal = adrbk_formatname(abe->fullname,
928 NULL, NULL);
931 else{
932 if(abe->tag != List || !simple_verify){
933 if(adr2->personal)
934 fs_give((void **)&adr2->personal);
936 adr2->personal = adrbk_formatname(abe->fullname,
937 NULL, NULL);
942 /* splice new list into old list and remove replaced addr */
943 for(a2 = adr2; a2->next != NULL; a2 = a2->next)
944 ;/* do nothing */
946 a2->next = a->next;
947 if(a == adr)
948 adr = adr2;
949 else
950 a_tail->next = adr2;
952 /* advance to next item, and free replaced ADDRESS */
953 a_tail = a2;
954 a_temp = a;
955 a = a->next;
956 a_temp->next = NULL; /* So free won't do whole list */
957 mail_free_address(&a_temp);
961 if((a_tail == adr && fcc && !(*did_set & FCC_SET))
962 || !a_tail->personal)
964 * This looks for the addressbook entry that matches the given
965 * address. It looks through all the addressbooks
966 * looking for an exact match and then returns that entry.
968 abe2 = address_to_abe(a_tail);
969 else
970 abe2 = NULL;
973 * If there is no personal name yet but we found the address in
974 * an address book, then we take the fullname from that address
975 * book entry and use it. One consequence of this is that if I
976 * have an address book entry with address hubert@cac.washington.edu
977 * and a fullname of Steve Hubert, then there is no way I can
978 * send mail to hubert@cac.washington.edu without having the
979 * personal name filled in for me.
981 if(!a_tail->personal && abe2 && abe2->fullname && abe2->fullname[0])
982 a_tail->personal = adrbk_formatname(abe2->fullname, NULL, NULL);
984 /* if it's first addr in list and fcc hasn't been set yet */
985 if(!recursing && a_tail == adr && fcc && !(*did_set & FCC_SET)){
986 if(abe2 && abe2->fcc && abe2->fcc[0]){
987 if(*fcc)
988 fs_give((void **)fcc);
990 if(!strcmp(abe2->fcc, "\"\""))
991 *fcc = cpystr("");
992 else
993 *fcc = cpystr(abe2->fcc);
995 *did_set |= FCC_SET;
997 else if(abe2 && abe2->nickname && abe2->nickname[0] &&
998 (ps_global->fcc_rule == FCC_RULE_NICK ||
999 ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
1000 if(*fcc)
1001 fs_give((void **)fcc);
1003 *fcc = cpystr(abe2->nickname);
1004 *did_set |= FCC_SET;
1009 * Lcc needs to be filled in.
1010 * Bug: if ^T select was used to put the list in the lcc field, then
1011 * the list will have been expanded already and the fullname for
1012 * the list will be mixed with the initial fullname in the list,
1013 * and we don't have anyway to tell them apart. We could look for
1014 * the --. We could change expand_address so it doesn't combine
1015 * those two addresses.
1017 if(adr &&
1018 a_tail == adr &&
1019 lcc &&
1020 (!*lcc || !**lcc)){
1021 if(adr->personal){
1022 ADDRESS *atmp;
1023 char *tmp;
1024 RFC822BUFFER rbuf;
1025 size_t l, len;
1027 if(*lcc)
1028 fs_give((void **)lcc);
1030 atmp = mail_newaddr();
1031 atmp->mailbox = cpystr(adr->personal);
1032 len = est_size(atmp);
1033 tmp = (char *) fs_get(len * sizeof(char));
1034 tmp[0] = '\0';
1035 /* write the phrase with quoting */
1036 rbuf.f = dummy_soutr;
1037 rbuf.s = NULL;
1038 rbuf.beg = tmp;
1039 rbuf.cur = tmp;
1040 rbuf.end = tmp+len-1;
1041 rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
1042 *rbuf.cur = '\0';
1043 l = strlen(tmp)+1;
1044 *lcc = (char *) fs_get((l+1) * sizeof(char));
1045 strncpy(*lcc, tmp, l);
1046 (*lcc)[l] = '\0';
1047 strncat(*lcc, ";", l+1-strlen(*lcc)-1);
1048 (*lcc)[l] = '\0';
1049 mail_free_address(&atmp);
1050 fs_give((void **)&tmp);
1051 *did_set |= (LCC_SET | LCC_NOREPO);
1056 if(!recursing)
1057 fs_give((void **)&fakedomain);
1059 return(adr);
1064 * This is a call back from rfc822_parse_adrlist. If we return NULL then
1065 * it does its regular parsing. However, if we return and ADDRESS then it
1066 * returns that address to us.
1068 * We want a phrase with no address to be parsed just like a nickname would
1069 * be in expand_address. That is, the phrase gets put in the mailbox name
1070 * and the host is set to the fakedomain. Then expand_address will try to
1071 * look that up using wp_lookup().
1073 * Args phrase -- The start of the phrase of the address
1074 * end -- The first character after the phrase
1075 * host -- The defaulthost that was passed to rfc822_parse_adrlist
1077 * Returns An allocated address, or NULL.
1079 ADDRESS *
1080 massage_phrase_addr(char *phrase, char *end, char *host)
1082 ADDRESS *adr = NULL;
1083 size_t size;
1085 if((size = end - phrase) > 0){
1086 char *mycopy;
1088 mycopy = (char *)fs_get((size+1) * sizeof(char));
1089 strncpy(mycopy, phrase, size);
1090 mycopy[size] = '\0';
1091 removing_trailing_white_space(mycopy);
1094 * If it is quoted we want to leave it alone. It will be treated
1095 * like an atom in parse_adrlist anyway, which is what we want to
1096 * have happen, and we'd have to remove the quotes ourselves here
1097 * if we did it. The problem then is that we don't know if we
1098 * removed the quotes here or they just weren't there in the
1099 * first place.
1101 if(*mycopy == '"' && mycopy[strlen(mycopy)-1] == '"')
1102 fs_give((void **)&mycopy);
1103 else{
1104 adr = mail_newaddr();
1105 adr->mailbox = mycopy;
1106 adr->host = cpystr(host);
1110 return(adr);
1115 * Run through the adrlist "adr" and strip off any enclosing quotes
1116 * around personal names. That is, change "Joe L. Normal" to
1117 * Joe L. Normal.
1119 void
1120 strip_personal_quotes(struct mail_address *adr)
1122 int len;
1123 register char *p, *q;
1125 while(adr){
1126 if(adr->personal){
1127 len = strlen(adr->personal);
1128 if(len > 1
1129 && adr->personal[0] == '"'
1130 && adr->personal[len-1] == '"'){
1131 adr->personal[len-1] = '\0';
1132 p = adr->personal;
1133 q = p + 1;
1134 while((*p++ = *q++) != '\0')
1139 adr = adr->next;
1144 static char *last_fcc_used;
1146 * Returns alloc'd fcc.
1148 char *
1149 get_fcc(char *fcc_arg)
1151 char *fcc;
1154 * Use passed in arg unless it is the same as default (and then
1155 * may use that anyway below).
1157 if(fcc_arg && strcmp(fcc_arg, ps_global->VAR_DEFAULT_FCC))
1158 fcc = cpystr(fcc_arg);
1159 else{
1160 if(ps_global->fcc_rule == FCC_RULE_LAST && last_fcc_used)
1161 fcc = cpystr(last_fcc_used);
1162 else if(ps_global->fcc_rule == FCC_RULE_CURRENT
1163 && ps_global->mail_stream
1164 && !sp_flagged(ps_global->mail_stream, SP_INBOX)
1165 && !IS_NEWS(ps_global->mail_stream)
1166 && ps_global->cur_folder
1167 && ps_global->cur_folder[0]){
1168 CONTEXT_S *cntxt = ps_global->context_current;
1169 char *rs = NULL;
1171 if(((cntxt->use) & CNTXT_SAVEDFLT))
1172 rs = ps_global->cur_folder;
1173 else
1174 rs = ps_global->mail_stream->mailbox;
1176 fcc = cpystr((rs&&*rs) ? rs : ps_global->VAR_DEFAULT_FCC);
1178 else
1179 fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
1182 return(fcc);
1187 * Save the fcc for use with next composition.
1189 void
1190 set_last_fcc(char *fcc)
1192 size_t l;
1194 if(fcc){
1195 if(!last_fcc_used)
1196 last_fcc_used = cpystr(fcc);
1197 else if(strcmp(last_fcc_used, fcc)){
1198 if((l=strlen(last_fcc_used)) >= strlen(fcc)){
1199 strncpy(last_fcc_used, fcc, l+1);
1200 last_fcc_used[l] = '\0';
1202 else{
1203 fs_give((void **)&last_fcc_used);
1204 last_fcc_used = cpystr(fcc);
1212 * Figure out what the fcc is based on the given to address.
1214 * Returns an allocated copy of the fcc, to be freed by the caller, or NULL.
1216 char *
1217 get_fcc_based_on_to(struct mail_address *to)
1219 ADDRESS *next_addr;
1220 char *bufp, *fcc;
1221 BuildTo bldto;
1222 size_t len;
1224 if(!to || !to->host || to->host[0] == '.')
1225 return(NULL);
1227 /* pick off first address */
1228 next_addr = to->next;
1229 to->next = NULL;
1230 len = est_size(to);
1231 bufp = (char *) fs_get(len * sizeof(char));
1232 bufp[0] = '\0';
1234 bldto.type = Str;
1235 bldto.arg.str = cpystr(addr_string(to, bufp, len));
1237 fs_give((void **)&bufp);
1238 to->next = next_addr;
1240 fcc = NULL;
1242 (void) build_address_internal(bldto, NULL, NULL, &fcc, NULL, NULL, NULL, 0, NULL);
1244 if(bldto.arg.str)
1245 fs_give((void **) &bldto.arg.str);
1247 return(fcc);
1252 * Free storage in headerentry.bldr_private.
1254 void
1255 free_privatetop(PrivateTop **pt)
1257 if(pt && *pt){
1258 if((*pt)->affector)
1259 fs_give((void **)&(*pt)->affector);
1261 fs_give((void **)pt);
1265 void
1266 free_bldaddr_module_globals(void)
1268 if(last_fcc_used) fs_give((void **)&last_fcc_used);