1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: bldaddr.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2016 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 /*======================================================================
21 Support for build_address function and low-level display cache.
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
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.
93 our_build_address(BuildTo to
, char **full_to
, char **error
, char **fcc
,
94 void (*save_and_restore_f
)(int, SAVE_STATE_S
*))
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
103 if((to
.type
== Str
&& !to
.arg
.str
) || (to
.type
== Abe
&& !to
.arg
.abe
)){
105 *full_to
= cpystr("");
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"));
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
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
)
161 int tried_route_addr_hack
= 0;
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
173 if(save_and_restore_f
)
174 (*save_and_restore_f
)(SAR_SAVE
, &state
);
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] == '@'))){
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';
210 strncat(tmp
, to
.arg
.str
, MAX_ADDR_FIELD
+3-strlen(tmp
)-1);
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';
219 ps_global
->c_client_error
[0] = '\0';
225 mail_free_address(&a
);
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
++){
245 if(pab
->ostatus
== Open
|| pab
->ostatus
== NoDisplay
)
246 adrbk_clearrefs(pab
->address_book
);
251 else{ /* go back and use what we had before, so we get the error */
253 fs_give((void **)&tmp
);
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
++){
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 */
277 *full_to
= cpystr(to
.arg
.str
);
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
);
284 *full_to
= cpystr("");
289 /* display previous error and add new one */
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
);
300 "build_address_internal returning parse error: %s\n",
301 ps_global
->c_client_error
? ps_global
->c_client_error
: "?"));
303 mail_free_address(&a
);
306 fs_give((void **)&tmp
);
309 if(ps_global
->mangled_screen
)
310 *mangled
|= BUILDER_SCREEN_MANGLED
;
311 else if(ps_global
->mangled_footer
)
312 *mangled
|= BUILDER_FOOTER_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 */
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!"));
345 *full_to
= tmp
; /* add the brackets to route addr */
349 /* try to return what they sent us */
351 *full_to
= cpystr(to
.arg
.str
);
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
);
358 *full_to
= cpystr("");
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
;
384 rbuf
.end
= (*full_to
)+len
-1;
385 rfc822_output_address_list(&rbuf
, a
, 0L, NULL
);
390 if(no_repo
&& (did_set
& FCC_NOREPO
|| did_set
& LCC_NOREPO
))
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
)){
404 fs_give((void **)fcc
);
406 *fcc
= cpystr(fcc_got
);
408 else if(a
&& a
->host
){ /* not group syntax */
410 fs_give((void **)fcc
);
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))
420 *fcc
= cpystr(ps_global
->VAR_DEFAULT_FCC
);
422 else{ /* first addr is group syntax */
424 *fcc
= cpystr(ps_global
->VAR_DEFAULT_FCC
);
425 /* else, leave it alone */
429 fs_give((void **)&fcc_got
);
433 mail_free_address(&a
);
436 fs_give((void **)&tmp
);
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
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
;
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
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);
499 fakedomain
= (char *)fs_get(domain_length
+ 1);
500 memset((void *)fakedomain
, '@', domain_length
);
501 fakedomain
[domain_length
] = '\0';
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
== ',');
516 mail_parameters(NIL
, SET_PARSEPHRASE
, (void *)massage_phrase_addr
);
518 rfc822_parse_adrlist(&adr
, tmp_a_string
, fakedomain
);
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
);
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'))){
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 */
556 else if(a
->host
[0] != '@'){
557 /* Already fully qualified hostname */
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.
568 abe
= adrbk_lookup_with_opens_by_nick(a
->mailbox
,
574 if(simple_verify
&& abe
== NULL
){
575 /*--- Move to next address in list -----*/
579 else if(abe
== NULL
){
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 */
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
);
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
);
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
626 if(status_message_remaining() && error
&& !wp_exit
){
628 q_status_message(SM_ORDER
, 3, 5, *error
);
629 display_message('x');
630 fs_give((void **)error
);
633 *error
= wp_err
.error
;
636 fs_give((void **)&wp_err
.error
);
639 /* still haven't found it */
640 if(a
->host
[0] == '@' && !wp_nobail
){
644 * Figure out if there is a space in the mailbox so
645 * that user probably meant it to resolve on the
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
))){
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"),
658 (space_phrase
&& as
.n_impl
) ? _("directory")
662 /* display previous error and add new one */
665 q_status_message(SM_ORDER
, 3, 5, *error
);
666 display_message('x');
669 fs_give((void **)error
);
673 *error
= cpystr(ebuf
);
677 strncpy(ps_global
->c_client_error
, ebuf
, 200);
680 fs_give((void **)&fakedomain
);
683 mail_free_address(&adr
);
687 else if(wp_err
.wp_err_occurred
){
689 if(error
&& *error
&& !wp_exit
)
690 strncpy(ps_global
->c_client_error
, *error
, sizeof(ps_global
->c_client_error
));
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
);
698 mail_free_address(&adr
);
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 -----*/
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 -----*/
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]){
739 fs_give((void **)fcc
);
741 if(!strcmp(abe
->fcc
, "\"\""))
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
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
)){
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
772 *did_set
|= (FCC_SET
| FCC_NOREPO
);
776 /* lcc needs to be filled in */
783 /* return fullname for To line */
784 if(abe
->fullname
&& *abe
->fullname
){
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));
796 /* write the phrase with quoting */
797 rbuf
.f
= dummy_soutr
;
801 rbuf
.end
= tmp
+len
-1;
802 rfc822_output_address_list(&rbuf
, atmp
, 0L, NULL
);
805 *lcc
= (char *) fs_get((l
+1) * sizeof(char));
806 strncpy(*lcc
, tmp
, l
);
808 strncat(*lcc
, ";", l
+1-strlen(*lcc
)-1);
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
));
820 a
->host
= cpystr("***address-loop-in-addressbooks***");
824 abe
->referenced
++; /* For address loop detection */
825 if(abe
->tag
== List
){
827 for(l2
= abe
->addr
.list
; *l2
; l2
++)
828 length
+= (strlen(*l2
) + 1);
830 list
= (char *)fs_get(length
+ 1);
833 for(l2
= abe
->addr
.list
; *l2
; l2
++){
834 if(l1
!= list
&& length
+1-(l1
-list
) > 0)
837 strncpy(l1
, *l2
, length
+1-(l1
-list
));
838 if(l1
> &list
[length
])
847 bldto
.arg
.str
= list
;
848 adr2
= expand_address(bldto
, userdomain
, localdomain
,
849 loop_detected
, fcc
, did_set
,
850 lcc
, error
, 1, simple_verify
,
852 fs_give((void **)&list
);
854 else if(abe
->tag
== Single
){
855 if(strucmp(abe
->addr
.addr
, a
->mailbox
)){
857 bldto
.arg
.str
= abe
->addr
.addr
;
858 adr2
= expand_address(bldto
, userdomain
,
859 localdomain
, loop_detected
,
861 error
, 1, simple_verify
,
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> */
878 /* expanded to nothing, hack out of list */
886 a_tail
->next
= a
->next
;
890 a_temp
->next
= NULL
; /* So free won't do whole list */
891 mail_free_address(&a_temp
);
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
903 * This algorithm doesn't work very well if lists are
904 * included within lists, but it's not clear what would
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 */
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
,
920 fs_give((void **)&adr2
->personal
);
921 adr2
->personal
= name
;
925 /* replace with nickname fullname */
926 fs_give((void **)&adr2
->personal
);
927 adr2
->personal
= adrbk_formatname(abe
->fullname
,
932 if(abe
->tag
!= List
|| !simple_verify
){
934 fs_give((void **)&adr2
->personal
);
936 adr2
->personal
= adrbk_formatname(abe
->fullname
,
942 /* splice new list into old list and remove replaced addr */
943 for(a2
= adr2
; a2
->next
!= NULL
; a2
= a2
->next
)
952 /* advance to next item, and free replaced ADDRESS */
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
);
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]){
988 fs_give((void **)fcc
);
990 if(!strcmp(abe2
->fcc
, "\"\""))
993 *fcc
= cpystr(abe2
->fcc
);
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
)){
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.
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));
1035 /* write the phrase with quoting */
1036 rbuf
.f
= dummy_soutr
;
1040 rbuf
.end
= tmp
+len
-1;
1041 rfc822_output_address_list(&rbuf
, atmp
, 0L, NULL
);
1044 *lcc
= (char *) fs_get((l
+1) * sizeof(char));
1045 strncpy(*lcc
, tmp
, l
);
1047 strncat(*lcc
, ";", l
+1-strlen(*lcc
)-1);
1049 mail_free_address(&atmp
);
1050 fs_give((void **)&tmp
);
1051 *did_set
|= (LCC_SET
| LCC_NOREPO
);
1057 fs_give((void **)&fakedomain
);
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.
1080 massage_phrase_addr(char *phrase
, char *end
, char *host
)
1082 ADDRESS
*adr
= NULL
;
1085 if((size
= end
- phrase
) > 0){
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
1101 if(*mycopy
== '"' && mycopy
[strlen(mycopy
)-1] == '"')
1102 fs_give((void **)&mycopy
);
1104 adr
= mail_newaddr();
1105 adr
->mailbox
= mycopy
;
1106 adr
->host
= cpystr(host
);
1115 * Run through the adrlist "adr" and strip off any enclosing quotes
1116 * around personal names. That is, change "Joe L. Normal" to
1120 strip_personal_quotes(struct mail_address
*adr
)
1123 register char *p
, *q
;
1127 len
= strlen(adr
->personal
);
1129 && adr
->personal
[0] == '"'
1130 && adr
->personal
[len
-1] == '"'){
1131 adr
->personal
[len
-1] = '\0';
1134 while((*p
++ = *q
++) != '\0')
1144 static char *last_fcc_used
;
1146 * Returns alloc'd fcc.
1149 get_fcc(char *fcc_arg
)
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
);
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
;
1171 if(((cntxt
->use
) & CNTXT_SAVEDFLT
))
1172 rs
= ps_global
->cur_folder
;
1174 rs
= ps_global
->mail_stream
->mailbox
;
1176 fcc
= cpystr((rs
&&*rs
) ? rs
: ps_global
->VAR_DEFAULT_FCC
);
1179 fcc
= cpystr(ps_global
->VAR_DEFAULT_FCC
);
1187 * Save the fcc for use with next composition.
1190 set_last_fcc(char *fcc
)
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';
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.
1217 get_fcc_based_on_to(struct mail_address
*to
)
1224 if(!to
|| !to
->host
|| to
->host
[0] == '.')
1227 /* pick off first address */
1228 next_addr
= to
->next
;
1231 bufp
= (char *) fs_get(len
* sizeof(char));
1235 bldto
.arg
.str
= cpystr(addr_string(to
, bufp
, len
));
1237 fs_give((void **)&bufp
);
1238 to
->next
= next_addr
;
1242 (void) build_address_internal(bldto
, NULL
, NULL
, &fcc
, NULL
, NULL
, NULL
, 0, NULL
);
1245 fs_give((void **) &bldto
.arg
.str
);
1252 * Free storage in headerentry.bldr_private.
1255 free_privatetop(PrivateTop
**pt
)
1259 fs_give((void **)&(*pt
)->affector
);
1261 fs_give((void **)pt
);