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 /*======================================================================
17 Support for build_address function and low-level display cache.
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
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.
89 our_build_address(BuildTo to
, char **full_to
, char **error
, char **fcc
,
90 void (*save_and_restore_f
)(int, SAVE_STATE_S
*))
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
99 if((to
.type
== Str
&& !to
.arg
.str
) || (to
.type
== Abe
&& !to
.arg
.abe
)){
101 *full_to
= cpystr("");
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"));
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
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
)
157 int tried_route_addr_hack
= 0;
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
169 if(save_and_restore_f
)
170 (*save_and_restore_f
)(SAR_SAVE
, &state
);
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] == '@'))){
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';
206 strncat(tmp
, to
.arg
.str
, MAX_ADDR_FIELD
+3-strlen(tmp
)-1);
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';
215 ps_global
->c_client_error
[0] = '\0';
221 mail_free_address(&a
);
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
++){
241 if(pab
->ostatus
== Open
|| pab
->ostatus
== NoDisplay
)
242 adrbk_clearrefs(pab
->address_book
);
247 else{ /* go back and use what we had before, so we get the error */
249 fs_give((void **)&tmp
);
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
++){
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 */
273 *full_to
= cpystr(to
.arg
.str
);
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
);
280 *full_to
= cpystr("");
285 /* display previous error and add new one */
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
);
296 "build_address_internal returning parse error: %s\n",
297 ps_global
->c_client_error
? ps_global
->c_client_error
: "?"));
299 mail_free_address(&a
);
302 fs_give((void **)&tmp
);
305 if(ps_global
->mangled_screen
)
306 *mangled
|= BUILDER_SCREEN_MANGLED
;
307 else if(ps_global
->mangled_footer
)
308 *mangled
|= BUILDER_FOOTER_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 */
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!"));
341 *full_to
= tmp
; /* add the brackets to route addr */
345 /* try to return what they sent us */
347 *full_to
= cpystr(to
.arg
.str
);
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
);
354 *full_to
= cpystr("");
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
;
380 rbuf
.end
= (*full_to
)+len
-1;
381 rfc822_output_address_list(&rbuf
, a
, 0L, NULL
);
386 if(no_repo
&& (did_set
& FCC_NOREPO
|| did_set
& LCC_NOREPO
))
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
)){
400 fs_give((void **)fcc
);
402 *fcc
= cpystr(fcc_got
);
404 else if(a
&& a
->host
){ /* not group syntax */
406 fs_give((void **)fcc
);
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))
416 *fcc
= cpystr(ps_global
->VAR_DEFAULT_FCC
);
418 else{ /* first addr is group syntax */
420 *fcc
= cpystr(ps_global
->VAR_DEFAULT_FCC
);
421 /* else, leave it alone */
425 fs_give((void **)&fcc_got
);
429 mail_free_address(&a
);
432 fs_give((void **)&tmp
);
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
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
;
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
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);
495 fakedomain
= (char *)fs_get(domain_length
+ 1);
496 memset((void *)fakedomain
, '@', domain_length
);
497 fakedomain
[domain_length
] = '\0';
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
== ',');
512 mail_parameters(NIL
, SET_PARSEPHRASE
, (void *)massage_phrase_addr
);
514 rfc822_parse_adrlist(&adr
, tmp_a_string
, fakedomain
);
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
);
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'))){
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 */
552 else if(a
->host
[0] != '@'){
553 /* Already fully qualified hostname */
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.
564 abe
= adrbk_lookup_with_opens_by_nick(a
->mailbox
,
570 if(simple_verify
&& abe
== NULL
){
571 /*--- Move to next address in list -----*/
575 else if(abe
== NULL
){
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 */
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
);
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
);
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
622 if(status_message_remaining() && error
&& !wp_exit
){
624 q_status_message(SM_ORDER
, 3, 5, *error
);
625 display_message('x');
626 fs_give((void **)error
);
629 *error
= wp_err
.error
;
632 fs_give((void **)&wp_err
.error
);
635 /* still haven't found it */
636 if(a
->host
[0] == '@' && !wp_nobail
){
640 * Figure out if there is a space in the mailbox so
641 * that user probably meant it to resolve on the
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
))){
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"),
654 (space_phrase
&& as
.n_impl
) ? _("directory")
658 /* display previous error and add new one */
661 q_status_message(SM_ORDER
, 3, 5, *error
);
662 display_message('x');
665 fs_give((void **)error
);
669 *error
= cpystr(ebuf
);
673 strncpy(ps_global
->c_client_error
, ebuf
, 200);
676 fs_give((void **)&fakedomain
);
679 mail_free_address(&adr
);
683 else if(wp_err
.wp_err_occurred
){
685 if(error
&& *error
&& !wp_exit
)
686 strncpy(ps_global
->c_client_error
, *error
, sizeof(ps_global
->c_client_error
));
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
);
694 mail_free_address(&adr
);
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 -----*/
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 -----*/
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]){
735 fs_give((void **)fcc
);
737 if(!strcmp(abe
->fcc
, "\"\""))
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
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
)){
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
768 *did_set
|= (FCC_SET
| FCC_NOREPO
);
772 /* lcc needs to be filled in */
779 /* return fullname for To line */
780 if(abe
->fullname
&& *abe
->fullname
){
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));
792 /* write the phrase with quoting */
793 rbuf
.f
= dummy_soutr
;
797 rbuf
.end
= tmp
+len
-1;
798 rfc822_output_address_list(&rbuf
, atmp
, 0L, NULL
);
801 *lcc
= (char *) fs_get((l
+1) * sizeof(char));
802 strncpy(*lcc
, tmp
, l
);
804 strncat(*lcc
, ";", l
+1-strlen(*lcc
)-1);
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
));
816 a
->host
= cpystr("***address-loop-in-addressbooks***");
820 abe
->referenced
++; /* For address loop detection */
821 if(abe
->tag
== List
){
823 for(l2
= abe
->addr
.list
; *l2
; l2
++)
824 length
+= (strlen(*l2
) + 1);
826 list
= (char *)fs_get(length
+ 1);
829 for(l2
= abe
->addr
.list
; *l2
; l2
++){
830 if(l1
!= list
&& length
+1-(l1
-list
) > 0)
833 strncpy(l1
, *l2
, length
+1-(l1
-list
));
834 if(l1
> &list
[length
])
843 bldto
.arg
.str
= list
;
844 adr2
= expand_address(bldto
, userdomain
, localdomain
,
845 loop_detected
, fcc
, did_set
,
846 lcc
, error
, 1, simple_verify
,
848 fs_give((void **)&list
);
850 else if(abe
->tag
== Single
){
851 if(strucmp(abe
->addr
.addr
, a
->mailbox
)){
853 bldto
.arg
.str
= abe
->addr
.addr
;
854 adr2
= expand_address(bldto
, userdomain
,
855 localdomain
, loop_detected
,
857 error
, 1, simple_verify
,
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> */
874 /* expanded to nothing, hack out of list */
882 a_tail
->next
= a
->next
;
886 a_temp
->next
= NULL
; /* So free won't do whole list */
887 mail_free_address(&a_temp
);
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
899 * This algorithm doesn't work very well if lists are
900 * included within lists, but it's not clear what would
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 */
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
,
916 fs_give((void **)&adr2
->personal
);
917 adr2
->personal
= name
;
921 /* replace with nickname fullname */
922 fs_give((void **)&adr2
->personal
);
923 adr2
->personal
= adrbk_formatname(abe
->fullname
,
928 if(abe
->tag
!= List
|| !simple_verify
){
930 fs_give((void **)&adr2
->personal
);
932 adr2
->personal
= adrbk_formatname(abe
->fullname
,
938 /* splice new list into old list and remove replaced addr */
939 for(a2
= adr2
; a2
->next
!= NULL
; a2
= a2
->next
)
948 /* advance to next item, and free replaced ADDRESS */
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
);
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]){
984 fs_give((void **)fcc
);
986 if(!strcmp(abe2
->fcc
, "\"\""))
989 *fcc
= cpystr(abe2
->fcc
);
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
)){
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.
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));
1031 /* write the phrase with quoting */
1032 rbuf
.f
= dummy_soutr
;
1036 rbuf
.end
= tmp
+len
-1;
1037 rfc822_output_address_list(&rbuf
, atmp
, 0L, NULL
);
1040 *lcc
= (char *) fs_get((l
+1) * sizeof(char));
1041 strncpy(*lcc
, tmp
, l
);
1043 strncat(*lcc
, ";", l
+1-strlen(*lcc
)-1);
1045 mail_free_address(&atmp
);
1046 fs_give((void **)&tmp
);
1047 *did_set
|= (LCC_SET
| LCC_NOREPO
);
1053 fs_give((void **)&fakedomain
);
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.
1076 massage_phrase_addr(char *phrase
, char *end
, char *host
)
1078 ADDRESS
*adr
= NULL
;
1081 if((size
= end
- phrase
) > 0){
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
1097 if(*mycopy
== '"' && mycopy
[strlen(mycopy
)-1] == '"')
1098 fs_give((void **)&mycopy
);
1100 adr
= mail_newaddr();
1101 adr
->mailbox
= mycopy
;
1102 adr
->host
= cpystr(host
);
1111 * Run through the adrlist "adr" and strip off any enclosing quotes
1112 * around personal names. That is, change "Joe L. Normal" to
1116 strip_personal_quotes(struct mail_address
*adr
)
1119 register char *p
, *q
;
1123 len
= strlen(adr
->personal
);
1125 && adr
->personal
[0] == '"'
1126 && adr
->personal
[len
-1] == '"'){
1127 adr
->personal
[len
-1] = '\0';
1130 while((*p
++ = *q
++) != '\0')
1140 static char *last_fcc_used
;
1142 * Returns alloc'd fcc.
1145 get_fcc(char *fcc_arg
)
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
);
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
;
1167 if(((cntxt
->use
) & CNTXT_SAVEDFLT
))
1168 rs
= ps_global
->cur_folder
;
1170 rs
= ps_global
->mail_stream
->mailbox
;
1172 fcc
= cpystr((rs
&&*rs
) ? rs
: ps_global
->VAR_DEFAULT_FCC
);
1175 fcc
= cpystr(ps_global
->VAR_DEFAULT_FCC
);
1183 * Save the fcc for use with next composition.
1186 set_last_fcc(char *fcc
)
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';
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.
1213 get_fcc_based_on_to(struct mail_address
*to
)
1220 if(!to
|| !to
->host
|| to
->host
[0] == '.')
1223 /* pick off first address */
1224 next_addr
= to
->next
;
1227 bufp
= (char *) fs_get(len
* sizeof(char));
1231 bldto
.arg
.str
= cpystr(addr_string(to
, bufp
, len
));
1233 fs_give((void **)&bufp
);
1234 to
->next
= next_addr
;
1238 (void) build_address_internal(bldto
, NULL
, NULL
, &fcc
, NULL
, NULL
, NULL
, 0, NULL
);
1241 fs_give((void **) &bldto
.arg
.str
);
1248 * Free storage in headerentry.bldr_private.
1251 free_privatetop(PrivateTop
**pt
)
1255 fs_give((void **)&(*pt
)->affector
);
1257 fs_give((void **)pt
);
1262 free_bldaddr_module_globals(void)
1264 if(last_fcc_used
) fs_give((void **)&last_fcc_used
);