1c2aa98e2SPeter Wemm /*
25dd76dd0SGregory Neil Shapiro * Copyright (c) 1998-2003 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro * All rights reserved.
4c2aa98e2SPeter Wemm * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
506f25ae9SGregory Neil Shapiro * Copyright (c) 1988, 1993
606f25ae9SGregory Neil Shapiro * The Regents of the University of California. All rights reserved.
7c2aa98e2SPeter Wemm *
8c2aa98e2SPeter Wemm * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm * the sendmail distribution.
1140266059SGregory Neil Shapiro *
12c2aa98e2SPeter Wemm */
13c2aa98e2SPeter Wemm
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
15c2aa98e2SPeter Wemm
164313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: alias.c,v 8.221 2013-11-22 20:51:54 ca Exp $")
17c2aa98e2SPeter Wemm
182fb4f839SGregory Neil Shapiro #include <sm/sendmail.h>
192fb4f839SGregory Neil Shapiro
2006f25ae9SGregory Neil Shapiro #define SEPARATOR ':'
2106f25ae9SGregory Neil Shapiro # define ALIAS_SPEC_SEPARATORS " ,/:"
22c2aa98e2SPeter Wemm
2306f25ae9SGregory Neil Shapiro static MAP *AliasFileMap = NULL; /* the actual aliases.files map */
2406f25ae9SGregory Neil Shapiro static int NAliasFileMaps; /* the number of entries in AliasFileMap */
2506f25ae9SGregory Neil Shapiro
2640266059SGregory Neil Shapiro static char *aliaslookup __P((char *, int *, char *));
2706f25ae9SGregory Neil Shapiro
2840266059SGregory Neil Shapiro /*
29c2aa98e2SPeter Wemm ** ALIAS -- Compute aliases.
30c2aa98e2SPeter Wemm **
31c2aa98e2SPeter Wemm ** Scans the alias file for an alias for the given address.
32c2aa98e2SPeter Wemm ** If found, it arranges to deliver to the alias list instead.
33c2aa98e2SPeter Wemm ** Uses libdbm database if -DDBM.
34c2aa98e2SPeter Wemm **
35c2aa98e2SPeter Wemm ** Parameters:
36c2aa98e2SPeter Wemm ** a -- address to alias.
37c2aa98e2SPeter Wemm ** sendq -- a pointer to the head of the send queue
38c2aa98e2SPeter Wemm ** to put the aliases in.
39c2aa98e2SPeter Wemm ** aliaslevel -- the current alias nesting depth.
40c2aa98e2SPeter Wemm ** e -- the current envelope.
41c2aa98e2SPeter Wemm **
42c2aa98e2SPeter Wemm ** Returns:
43c2aa98e2SPeter Wemm ** none
44c2aa98e2SPeter Wemm **
45c2aa98e2SPeter Wemm ** Side Effects:
46c2aa98e2SPeter Wemm ** Aliases found are expanded.
47c2aa98e2SPeter Wemm **
48c2aa98e2SPeter Wemm ** Deficiencies:
49c2aa98e2SPeter Wemm ** It should complain about names that are aliased to
50c2aa98e2SPeter Wemm ** nothing.
51c2aa98e2SPeter Wemm */
52c2aa98e2SPeter Wemm
53c2aa98e2SPeter Wemm void
alias(a,sendq,aliaslevel,e)54c2aa98e2SPeter Wemm alias(a, sendq, aliaslevel, e)
55c2aa98e2SPeter Wemm register ADDRESS *a;
56c2aa98e2SPeter Wemm ADDRESS **sendq;
57c2aa98e2SPeter Wemm int aliaslevel;
58c2aa98e2SPeter Wemm register ENVELOPE *e;
59c2aa98e2SPeter Wemm {
60c2aa98e2SPeter Wemm register char *p;
61c2aa98e2SPeter Wemm char *owner;
6206f25ae9SGregory Neil Shapiro auto int status = EX_OK;
632fb4f839SGregory Neil Shapiro char obuf[MAXNAME_I + 7];
64c2aa98e2SPeter Wemm
65c2aa98e2SPeter Wemm if (tTd(27, 1))
6640266059SGregory Neil Shapiro sm_dprintf("alias(%s)\n", a->q_user);
67c2aa98e2SPeter Wemm
68c2aa98e2SPeter Wemm /* don't realias already aliased names */
6906f25ae9SGregory Neil Shapiro if (!QS_IS_OK(a->q_state))
70c2aa98e2SPeter Wemm return;
71c2aa98e2SPeter Wemm
72c2aa98e2SPeter Wemm if (NoAlias)
73c2aa98e2SPeter Wemm return;
74c2aa98e2SPeter Wemm
75c2aa98e2SPeter Wemm e->e_to = a->q_paddr;
76c2aa98e2SPeter Wemm
77c2aa98e2SPeter Wemm /*
78c2aa98e2SPeter Wemm ** Look up this name.
79c2aa98e2SPeter Wemm **
80c2aa98e2SPeter Wemm ** If the map was unavailable, we will queue this message
81c2aa98e2SPeter Wemm ** until the map becomes available; otherwise, we could
82c2aa98e2SPeter Wemm ** bounce messages inappropriately.
83c2aa98e2SPeter Wemm */
84c2aa98e2SPeter Wemm
8506f25ae9SGregory Neil Shapiro #if _FFR_REDIRECTEMPTY
8606f25ae9SGregory Neil Shapiro /*
8706f25ae9SGregory Neil Shapiro ** envelope <> can't be sent to mailing lists, only owner-
8806f25ae9SGregory Neil Shapiro ** send spam of this type to owner- of the list
8906f25ae9SGregory Neil Shapiro ** ---- to stop spam from going to mailing lists!
9006f25ae9SGregory Neil Shapiro */
9140266059SGregory Neil Shapiro
9206f25ae9SGregory Neil Shapiro if (e->e_sender != NULL && *e->e_sender == '\0')
93c2aa98e2SPeter Wemm {
9406f25ae9SGregory Neil Shapiro /* Look for owner of alias */
95d0cef73dSGregory Neil Shapiro (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
9640266059SGregory Neil Shapiro if (aliaslookup(obuf, &status, a->q_host) != NULL)
9706f25ae9SGregory Neil Shapiro {
9806f25ae9SGregory Neil Shapiro if (LogLevel > 8)
99a7ec597cSGregory Neil Shapiro sm_syslog(LOG_WARNING, e->e_id,
10006f25ae9SGregory Neil Shapiro "possible spam from <> to list: %s, redirected to %s\n",
10106f25ae9SGregory Neil Shapiro a->q_user, obuf);
10240266059SGregory Neil Shapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf);
10306f25ae9SGregory Neil Shapiro }
10406f25ae9SGregory Neil Shapiro }
10506f25ae9SGregory Neil Shapiro #endif /* _FFR_REDIRECTEMPTY */
10606f25ae9SGregory Neil Shapiro
10740266059SGregory Neil Shapiro p = aliaslookup(a->q_user, &status, a->q_host);
10806f25ae9SGregory Neil Shapiro if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
10906f25ae9SGregory Neil Shapiro {
11006f25ae9SGregory Neil Shapiro a->q_state = QS_QUEUEUP;
111c2aa98e2SPeter Wemm if (e->e_message == NULL)
112d0cef73dSGregory Neil Shapiro e->e_message = sm_rpool_strdup_x(e->e_rpool,
113d0cef73dSGregory Neil Shapiro "alias database unavailable");
11440266059SGregory Neil Shapiro
11540266059SGregory Neil Shapiro /* XXX msg only per recipient? */
11640266059SGregory Neil Shapiro if (a->q_message == NULL)
11740266059SGregory Neil Shapiro a->q_message = "alias database unavailable";
118c2aa98e2SPeter Wemm return;
119c2aa98e2SPeter Wemm }
120c2aa98e2SPeter Wemm if (p == NULL)
121c2aa98e2SPeter Wemm return;
122c2aa98e2SPeter Wemm
123c2aa98e2SPeter Wemm /*
124c2aa98e2SPeter Wemm ** Match on Alias.
125c2aa98e2SPeter Wemm ** Deliver to the target list.
126c2aa98e2SPeter Wemm */
127c2aa98e2SPeter Wemm
128c2aa98e2SPeter Wemm if (tTd(27, 1))
12940266059SGregory Neil Shapiro sm_dprintf("%s (%s, %s) aliased to %s\n",
130c2aa98e2SPeter Wemm a->q_paddr, a->q_host, a->q_user, p);
131c2aa98e2SPeter Wemm if (bitset(EF_VRFYONLY, e->e_flags))
132c2aa98e2SPeter Wemm {
13306f25ae9SGregory Neil Shapiro a->q_state = QS_VERIFIED;
134c2aa98e2SPeter Wemm return;
135c2aa98e2SPeter Wemm }
136c2aa98e2SPeter Wemm message("aliased to %s", shortenstring(p, MAXSHORTSTR));
13706f25ae9SGregory Neil Shapiro if (LogLevel > 10)
138c2aa98e2SPeter Wemm sm_syslog(LOG_INFO, e->e_id,
139c2aa98e2SPeter Wemm "alias %.100s => %s",
140c2aa98e2SPeter Wemm a->q_paddr, shortenstring(p, MAXSHORTSTR));
141c2aa98e2SPeter Wemm a->q_flags &= ~QSELFREF;
142c2aa98e2SPeter Wemm if (tTd(27, 5))
143c2aa98e2SPeter Wemm {
14440266059SGregory Neil Shapiro sm_dprintf("alias: QS_EXPANDED ");
145e92d3f3fSGregory Neil Shapiro printaddr(sm_debug_file(), a, false);
146c2aa98e2SPeter Wemm }
14706f25ae9SGregory Neil Shapiro a->q_state = QS_EXPANDED;
14806f25ae9SGregory Neil Shapiro
14906f25ae9SGregory Neil Shapiro /*
15006f25ae9SGregory Neil Shapiro ** Always deliver aliased items as the default user.
15106f25ae9SGregory Neil Shapiro ** Setting q_gid to 0 forces deliver() to use DefUser
15206f25ae9SGregory Neil Shapiro ** instead of the alias name for the call to initgroups().
15306f25ae9SGregory Neil Shapiro */
15406f25ae9SGregory Neil Shapiro
15506f25ae9SGregory Neil Shapiro a->q_uid = DefUid;
15606f25ae9SGregory Neil Shapiro a->q_gid = 0;
15706f25ae9SGregory Neil Shapiro a->q_fullname = NULL;
15806f25ae9SGregory Neil Shapiro a->q_flags |= QGOODUID|QALIAS;
15906f25ae9SGregory Neil Shapiro
160c2aa98e2SPeter Wemm (void) sendtolist(p, a, sendq, aliaslevel + 1, e);
16140266059SGregory Neil Shapiro
16206f25ae9SGregory Neil Shapiro if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
16306f25ae9SGregory Neil Shapiro a->q_state = QS_OK;
164c2aa98e2SPeter Wemm
165c2aa98e2SPeter Wemm /*
166c2aa98e2SPeter Wemm ** Look for owner of alias
167c2aa98e2SPeter Wemm */
168c2aa98e2SPeter Wemm
169c2aa98e2SPeter Wemm if (strncmp(a->q_user, "owner-", 6) == 0 ||
170d0cef73dSGregory Neil Shapiro strlen(a->q_user) > sizeof(obuf) - 7)
171d0cef73dSGregory Neil Shapiro (void) sm_strlcpy(obuf, "owner-owner", sizeof(obuf));
172c2aa98e2SPeter Wemm else
173d0cef73dSGregory Neil Shapiro (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
17440266059SGregory Neil Shapiro owner = aliaslookup(obuf, &status, a->q_host);
175c2aa98e2SPeter Wemm if (owner == NULL)
176c2aa98e2SPeter Wemm return;
177c2aa98e2SPeter Wemm
178c2aa98e2SPeter Wemm /* reflect owner into envelope sender */
179c2aa98e2SPeter Wemm if (strpbrk(owner, ",:/|\"") != NULL)
180c2aa98e2SPeter Wemm owner = obuf;
18140266059SGregory Neil Shapiro a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
182c2aa98e2SPeter Wemm
183c2aa98e2SPeter Wemm /* announce delivery to this alias; NORECEIPT bit set later */
184c2aa98e2SPeter Wemm if (e->e_xfp != NULL)
18540266059SGregory Neil Shapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
18640266059SGregory Neil Shapiro "Message delivered to mailing list %s\n",
187c2aa98e2SPeter Wemm a->q_paddr);
188c2aa98e2SPeter Wemm e->e_flags |= EF_SENDRECEIPT;
189c2aa98e2SPeter Wemm a->q_flags |= QDELIVERED|QEXPANDED;
190c2aa98e2SPeter Wemm }
1912fb4f839SGregory Neil Shapiro
19240266059SGregory Neil Shapiro /*
193c2aa98e2SPeter Wemm ** ALIASLOOKUP -- look up a name in the alias file.
194c2aa98e2SPeter Wemm **
195c2aa98e2SPeter Wemm ** Parameters:
1962fb4f839SGregory Neil Shapiro ** name -- the name to look up [i]
197c2aa98e2SPeter Wemm ** pstat -- a pointer to a place to put the status.
19840266059SGregory Neil Shapiro ** av -- argument for %1 expansion.
199c2aa98e2SPeter Wemm **
200c2aa98e2SPeter Wemm ** Returns:
201c2aa98e2SPeter Wemm ** the value of name.
202c2aa98e2SPeter Wemm ** NULL if unknown.
203c2aa98e2SPeter Wemm **
204c2aa98e2SPeter Wemm ** Warnings:
205c2aa98e2SPeter Wemm ** The return value will be trashed across calls.
206c2aa98e2SPeter Wemm */
207c2aa98e2SPeter Wemm
20806f25ae9SGregory Neil Shapiro static char *
aliaslookup(name,pstat,av)20940266059SGregory Neil Shapiro aliaslookup(name, pstat, av)
210c2aa98e2SPeter Wemm char *name;
211c2aa98e2SPeter Wemm int *pstat;
21240266059SGregory Neil Shapiro char *av;
213c2aa98e2SPeter Wemm {
214c2aa98e2SPeter Wemm static MAP *map = NULL;
2152fb4f839SGregory Neil Shapiro char *res;
21640266059SGregory Neil Shapiro #if _FFR_ALIAS_DETAIL
21740266059SGregory Neil Shapiro int i;
21840266059SGregory Neil Shapiro char *argv[4];
2192fb4f839SGregory Neil Shapiro #else
2202fb4f839SGregory Neil Shapiro # define argv NULL
2212fb4f839SGregory Neil Shapiro #endif
2222fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
2232fb4f839SGregory Neil Shapiro char buf[MAXNAME]; /* EAI:ok */
2245b0945b5SGregory Neil Shapiro #endif
225c2aa98e2SPeter Wemm
226c2aa98e2SPeter Wemm if (map == NULL)
227c2aa98e2SPeter Wemm {
228c2aa98e2SPeter Wemm STAB *s = stab("aliases", ST_MAP, ST_FIND);
229c2aa98e2SPeter Wemm
230c2aa98e2SPeter Wemm if (s == NULL)
231c2aa98e2SPeter Wemm return NULL;
232c2aa98e2SPeter Wemm map = &s->s_map;
233c2aa98e2SPeter Wemm }
23406f25ae9SGregory Neil Shapiro DYNOPENMAP(map);
235c2aa98e2SPeter Wemm
236c2aa98e2SPeter Wemm /* special case POstMastER -- always use lower case */
2372fb4f839SGregory Neil Shapiro if (SM_STRCASEEQ(name, "postmaster"))
238c2aa98e2SPeter Wemm name = "postmaster";
2392fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
2402fb4f839SGregory Neil Shapiro (void) dequote_internal_chars(name, buf, sizeof(buf));
2412fb4f839SGregory Neil Shapiro /* check length? */
2422fb4f839SGregory Neil Shapiro name = buf;
2432fb4f839SGregory Neil Shapiro #endif /* _FFR_8BITENVADDR */
244c2aa98e2SPeter Wemm
24540266059SGregory Neil Shapiro #if _FFR_ALIAS_DETAIL
24640266059SGregory Neil Shapiro i = 0;
24740266059SGregory Neil Shapiro argv[i++] = name;
24840266059SGregory Neil Shapiro argv[i++] = av;
24940266059SGregory Neil Shapiro
25040266059SGregory Neil Shapiro /* XXX '+' is hardwired here as delimiter! */
25140266059SGregory Neil Shapiro if (av != NULL && *av == '+')
25240266059SGregory Neil Shapiro argv[i++] = av + 1;
25340266059SGregory Neil Shapiro argv[i++] = NULL;
25440266059SGregory Neil Shapiro #endif /* _FFR_ALIAS_DETAIL */
2552fb4f839SGregory Neil Shapiro res = (*map->map_class->map_lookup)(map, name, argv, pstat);
2562fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
2572fb4f839SGregory Neil Shapiro /* map_lookup() does a map_rewrite(), so no quoting here */
2582fb4f839SGregory Neil Shapiro #endif
2592fb4f839SGregory Neil Shapiro return res;
260c2aa98e2SPeter Wemm }
2612fb4f839SGregory Neil Shapiro
26240266059SGregory Neil Shapiro /*
263c2aa98e2SPeter Wemm ** SETALIAS -- set up an alias map
264c2aa98e2SPeter Wemm **
265c2aa98e2SPeter Wemm ** Called when reading configuration file.
266c2aa98e2SPeter Wemm **
267c2aa98e2SPeter Wemm ** Parameters:
268c2aa98e2SPeter Wemm ** spec -- the alias specification
269c2aa98e2SPeter Wemm **
270c2aa98e2SPeter Wemm ** Returns:
271c2aa98e2SPeter Wemm ** none.
272c2aa98e2SPeter Wemm */
273c2aa98e2SPeter Wemm
274c2aa98e2SPeter Wemm void
setalias(spec)275c2aa98e2SPeter Wemm setalias(spec)
276c2aa98e2SPeter Wemm char *spec;
277c2aa98e2SPeter Wemm {
278c2aa98e2SPeter Wemm register char *p;
279c2aa98e2SPeter Wemm register MAP *map;
280c2aa98e2SPeter Wemm char *class;
281c2aa98e2SPeter Wemm STAB *s;
282c2aa98e2SPeter Wemm
283c2aa98e2SPeter Wemm if (tTd(27, 8))
28440266059SGregory Neil Shapiro sm_dprintf("setalias(%s)\n", spec);
285c2aa98e2SPeter Wemm
286c2aa98e2SPeter Wemm for (p = spec; p != NULL; )
287c2aa98e2SPeter Wemm {
288c2aa98e2SPeter Wemm char buf[50];
289c2aa98e2SPeter Wemm
2905b0945b5SGregory Neil Shapiro while (SM_ISSPACE(*p))
291c2aa98e2SPeter Wemm p++;
292c2aa98e2SPeter Wemm if (*p == '\0')
293c2aa98e2SPeter Wemm break;
294c2aa98e2SPeter Wemm spec = p;
295c2aa98e2SPeter Wemm
296c2aa98e2SPeter Wemm if (NAliasFileMaps >= MAXMAPSTACK)
297c2aa98e2SPeter Wemm {
298c2aa98e2SPeter Wemm syserr("Too many alias databases defined, %d max",
299c2aa98e2SPeter Wemm MAXMAPSTACK);
300c2aa98e2SPeter Wemm return;
301c2aa98e2SPeter Wemm }
302c2aa98e2SPeter Wemm if (AliasFileMap == NULL)
303c2aa98e2SPeter Wemm {
30440266059SGregory Neil Shapiro (void) sm_strlcpy(buf, "aliases.files sequence",
305d0cef73dSGregory Neil Shapiro sizeof(buf));
306c2aa98e2SPeter Wemm AliasFileMap = makemapentry(buf);
307c2aa98e2SPeter Wemm if (AliasFileMap == NULL)
308c2aa98e2SPeter Wemm {
309c2aa98e2SPeter Wemm syserr("setalias: cannot create aliases.files map");
310c2aa98e2SPeter Wemm return;
311c2aa98e2SPeter Wemm }
312c2aa98e2SPeter Wemm }
313d0cef73dSGregory Neil Shapiro (void) sm_snprintf(buf, sizeof(buf), "Alias%d", NAliasFileMaps);
314c2aa98e2SPeter Wemm s = stab(buf, ST_MAP, ST_ENTER);
315c2aa98e2SPeter Wemm map = &s->s_map;
316d0cef73dSGregory Neil Shapiro memset(map, '\0', sizeof(*map));
317c2aa98e2SPeter Wemm map->map_mname = s->s_name;
31806f25ae9SGregory Neil Shapiro p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
31906f25ae9SGregory Neil Shapiro if (p != NULL && *p == SEPARATOR)
320c2aa98e2SPeter Wemm {
321c2aa98e2SPeter Wemm /* map name */
322c2aa98e2SPeter Wemm *p++ = '\0';
323c2aa98e2SPeter Wemm class = spec;
324c2aa98e2SPeter Wemm spec = p;
325c2aa98e2SPeter Wemm }
326c2aa98e2SPeter Wemm else
327c2aa98e2SPeter Wemm {
328c2aa98e2SPeter Wemm class = "implicit";
329c2aa98e2SPeter Wemm map->map_mflags = MF_INCLNULL;
330c2aa98e2SPeter Wemm }
331c2aa98e2SPeter Wemm
332c2aa98e2SPeter Wemm /* find end of spec */
333c2aa98e2SPeter Wemm if (p != NULL)
33406f25ae9SGregory Neil Shapiro {
33540266059SGregory Neil Shapiro bool quoted = false;
33606f25ae9SGregory Neil Shapiro
33706f25ae9SGregory Neil Shapiro for (; *p != '\0'; p++)
33806f25ae9SGregory Neil Shapiro {
33906f25ae9SGregory Neil Shapiro /*
34006f25ae9SGregory Neil Shapiro ** Don't break into a quoted string.
34106f25ae9SGregory Neil Shapiro ** Needed for ldap maps which use
34206f25ae9SGregory Neil Shapiro ** commas in their specifications.
34306f25ae9SGregory Neil Shapiro */
34406f25ae9SGregory Neil Shapiro
34506f25ae9SGregory Neil Shapiro if (*p == '"')
34606f25ae9SGregory Neil Shapiro quoted = !quoted;
34706f25ae9SGregory Neil Shapiro else if (*p == ',' && !quoted)
34806f25ae9SGregory Neil Shapiro break;
34906f25ae9SGregory Neil Shapiro }
35006f25ae9SGregory Neil Shapiro
35106f25ae9SGregory Neil Shapiro /* No more alias specifications follow */
35206f25ae9SGregory Neil Shapiro if (*p == '\0')
35306f25ae9SGregory Neil Shapiro p = NULL;
35406f25ae9SGregory Neil Shapiro }
355c2aa98e2SPeter Wemm if (p != NULL)
356c2aa98e2SPeter Wemm *p++ = '\0';
357c2aa98e2SPeter Wemm
358c2aa98e2SPeter Wemm if (tTd(27, 20))
35940266059SGregory Neil Shapiro sm_dprintf(" map %s:%s %s\n", class, s->s_name, spec);
360c2aa98e2SPeter Wemm
361c2aa98e2SPeter Wemm /* look up class */
362c2aa98e2SPeter Wemm s = stab(class, ST_MAPCLASS, ST_FIND);
363c2aa98e2SPeter Wemm if (s == NULL)
364c2aa98e2SPeter Wemm {
365c2aa98e2SPeter Wemm syserr("setalias: unknown alias class %s", class);
366c2aa98e2SPeter Wemm }
367c2aa98e2SPeter Wemm else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
368c2aa98e2SPeter Wemm {
369c2aa98e2SPeter Wemm syserr("setalias: map class %s can't handle aliases",
370c2aa98e2SPeter Wemm class);
371c2aa98e2SPeter Wemm }
372c2aa98e2SPeter Wemm else
373c2aa98e2SPeter Wemm {
374c2aa98e2SPeter Wemm map->map_class = &s->s_mapclass;
37540266059SGregory Neil Shapiro map->map_mflags |= MF_ALIAS;
376c2aa98e2SPeter Wemm if (map->map_class->map_parse(map, spec))
377c2aa98e2SPeter Wemm {
37840266059SGregory Neil Shapiro map->map_mflags |= MF_VALID;
379c2aa98e2SPeter Wemm AliasFileMap->map_stack[NAliasFileMaps++] = map;
380c2aa98e2SPeter Wemm }
381c2aa98e2SPeter Wemm }
382c2aa98e2SPeter Wemm }
383c2aa98e2SPeter Wemm }
384*d39bd2c1SGregory Neil Shapiro
38540266059SGregory Neil Shapiro /*
386c2aa98e2SPeter Wemm ** ALIASWAIT -- wait for distinguished @:@ token to appear.
387c2aa98e2SPeter Wemm **
388*d39bd2c1SGregory Neil Shapiro ** This can decide to reopen the alias file
389c2aa98e2SPeter Wemm **
390c2aa98e2SPeter Wemm ** Parameters:
391c2aa98e2SPeter Wemm ** map -- a pointer to the map descriptor for this alias file.
392c2aa98e2SPeter Wemm ** ext -- the filename extension (e.g., ".db") for the
393c2aa98e2SPeter Wemm ** database file.
394c2aa98e2SPeter Wemm ** isopen -- if set, the database is already open, and we
395c2aa98e2SPeter Wemm ** should check for validity; otherwise, we are
396c2aa98e2SPeter Wemm ** just checking to see if it should be created.
397c2aa98e2SPeter Wemm **
398c2aa98e2SPeter Wemm ** Returns:
39940266059SGregory Neil Shapiro ** true -- if the database is open when we return.
40040266059SGregory Neil Shapiro ** false -- if the database is closed when we return.
401c2aa98e2SPeter Wemm */
402c2aa98e2SPeter Wemm
403c2aa98e2SPeter Wemm bool
aliaswait(map,ext,isopen)404c2aa98e2SPeter Wemm aliaswait(map, ext, isopen)
405c2aa98e2SPeter Wemm MAP *map;
406*d39bd2c1SGregory Neil Shapiro const char *ext;
40706f25ae9SGregory Neil Shapiro bool isopen;
408c2aa98e2SPeter Wemm {
40940266059SGregory Neil Shapiro bool attimeout = false;
410c2aa98e2SPeter Wemm time_t mtime;
411c2aa98e2SPeter Wemm struct stat stb;
41294c01205SGregory Neil Shapiro char buf[MAXPATHLEN];
413c2aa98e2SPeter Wemm
414c2aa98e2SPeter Wemm if (tTd(27, 3))
415*d39bd2c1SGregory Neil Shapiro sm_dprintf("aliaswait(%s:%s), open=%d, wait=%d\n",
416*d39bd2c1SGregory Neil Shapiro map->map_class->map_cname, map->map_file,
417*d39bd2c1SGregory Neil Shapiro isopen, bitset(MF_ALIASWAIT, map->map_mflags));
418c2aa98e2SPeter Wemm if (bitset(MF_ALIASWAIT, map->map_mflags))
419c2aa98e2SPeter Wemm return isopen;
420c2aa98e2SPeter Wemm map->map_mflags |= MF_ALIASWAIT;
421c2aa98e2SPeter Wemm
422*d39bd2c1SGregory Neil Shapiro if (isopen && SafeAlias > 0)
423c2aa98e2SPeter Wemm {
424c2aa98e2SPeter Wemm auto int st;
425c2aa98e2SPeter Wemm unsigned int sleeptime = 2;
42640266059SGregory Neil Shapiro unsigned int loopcount = 0; /* only used for debugging */
42740266059SGregory Neil Shapiro time_t toolong = curtime() + SafeAlias;
428c2aa98e2SPeter Wemm
429c2aa98e2SPeter Wemm while (isopen &&
430c2aa98e2SPeter Wemm map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
431c2aa98e2SPeter Wemm {
432c2aa98e2SPeter Wemm if (curtime() > toolong)
433c2aa98e2SPeter Wemm {
434c2aa98e2SPeter Wemm /* we timed out */
43540266059SGregory Neil Shapiro attimeout = true;
436c2aa98e2SPeter Wemm break;
437c2aa98e2SPeter Wemm }
438c2aa98e2SPeter Wemm
439c2aa98e2SPeter Wemm /*
440c2aa98e2SPeter Wemm ** Close and re-open the alias database in case
441c2aa98e2SPeter Wemm ** the one is mv'ed instead of cp'ed in.
442c2aa98e2SPeter Wemm */
443c2aa98e2SPeter Wemm
444c2aa98e2SPeter Wemm if (tTd(27, 2))
44540266059SGregory Neil Shapiro {
44640266059SGregory Neil Shapiro loopcount++;
44740266059SGregory Neil Shapiro sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
44840266059SGregory Neil Shapiro sleeptime, loopcount);
44940266059SGregory Neil Shapiro }
450c2aa98e2SPeter Wemm
4518774250cSGregory Neil Shapiro map->map_mflags |= MF_CLOSING;
452c2aa98e2SPeter Wemm map->map_class->map_close(map);
453*d39bd2c1SGregory Neil Shapiro map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING|MF_CHKED_CHGD);
45406f25ae9SGregory Neil Shapiro (void) sleep(sleeptime);
455c2aa98e2SPeter Wemm sleeptime *= 2;
456c2aa98e2SPeter Wemm if (sleeptime > 60)
457c2aa98e2SPeter Wemm sleeptime = 60;
458c2aa98e2SPeter Wemm isopen = map->map_class->map_open(map, O_RDONLY);
459c2aa98e2SPeter Wemm }
460c2aa98e2SPeter Wemm }
461*d39bd2c1SGregory Neil Shapiro map->map_mflags &= ~MF_CHKED_CHGD;
462c2aa98e2SPeter Wemm
463c2aa98e2SPeter Wemm /* see if we need to go into auto-rebuild mode */
464c2aa98e2SPeter Wemm if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
465c2aa98e2SPeter Wemm {
466c2aa98e2SPeter Wemm if (tTd(27, 3))
46740266059SGregory Neil Shapiro sm_dprintf("aliaswait: not rebuildable\n");
468c2aa98e2SPeter Wemm map->map_mflags &= ~MF_ALIASWAIT;
469c2aa98e2SPeter Wemm return isopen;
470c2aa98e2SPeter Wemm }
471c2aa98e2SPeter Wemm if (stat(map->map_file, &stb) < 0)
472c2aa98e2SPeter Wemm {
473c2aa98e2SPeter Wemm if (tTd(27, 3))
47440266059SGregory Neil Shapiro sm_dprintf("aliaswait: no source file\n");
475c2aa98e2SPeter Wemm map->map_mflags &= ~MF_ALIASWAIT;
476c2aa98e2SPeter Wemm return isopen;
477c2aa98e2SPeter Wemm }
478c2aa98e2SPeter Wemm mtime = stb.st_mtime;
479d0cef73dSGregory Neil Shapiro if (sm_strlcpyn(buf, sizeof(buf), 2,
480d0cef73dSGregory Neil Shapiro map->map_file, ext == NULL ? "" : ext) >= sizeof(buf))
48194c01205SGregory Neil Shapiro {
48294c01205SGregory Neil Shapiro if (LogLevel > 3)
48394c01205SGregory Neil Shapiro sm_syslog(LOG_INFO, NOQID,
48494c01205SGregory Neil Shapiro "alias database %s%s name too long",
485c2aa98e2SPeter Wemm map->map_file, ext == NULL ? "" : ext);
48694c01205SGregory Neil Shapiro message("alias database %s%s name too long",
48794c01205SGregory Neil Shapiro map->map_file, ext == NULL ? "" : ext);
48894c01205SGregory Neil Shapiro }
48994c01205SGregory Neil Shapiro
490c2aa98e2SPeter Wemm if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
491c2aa98e2SPeter Wemm {
492c2aa98e2SPeter Wemm if (LogLevel > 3)
493c2aa98e2SPeter Wemm sm_syslog(LOG_INFO, NOQID,
49440266059SGregory Neil Shapiro "alias database %s out of date", buf);
495c2aa98e2SPeter Wemm message("Warning: alias database %s out of date", buf);
496c2aa98e2SPeter Wemm }
497c2aa98e2SPeter Wemm map->map_mflags &= ~MF_ALIASWAIT;
498c2aa98e2SPeter Wemm return isopen;
499c2aa98e2SPeter Wemm }
50040266059SGregory Neil Shapiro /*
501c2aa98e2SPeter Wemm ** REBUILDALIASES -- rebuild the alias database.
502c2aa98e2SPeter Wemm **
503c2aa98e2SPeter Wemm ** Parameters:
504c2aa98e2SPeter Wemm ** map -- the database to rebuild.
505c2aa98e2SPeter Wemm **
506c2aa98e2SPeter Wemm ** Returns:
50740266059SGregory Neil Shapiro ** true if successful; false otherwise.
508c2aa98e2SPeter Wemm **
509c2aa98e2SPeter Wemm ** Side Effects:
510*d39bd2c1SGregory Neil Shapiro ** Reads the text version of the database, builds the map.
511c2aa98e2SPeter Wemm */
512c2aa98e2SPeter Wemm
513c2aa98e2SPeter Wemm bool
rebuildaliases(map)514*d39bd2c1SGregory Neil Shapiro rebuildaliases(map)
515c2aa98e2SPeter Wemm register MAP *map;
516c2aa98e2SPeter Wemm {
51740266059SGregory Neil Shapiro SM_FILE_T *af;
51840266059SGregory Neil Shapiro bool nolock = false;
51940266059SGregory Neil Shapiro bool success = false;
52006f25ae9SGregory Neil Shapiro long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
521c2aa98e2SPeter Wemm sigfunc_t oldsigint, oldsigquit;
522c2aa98e2SPeter Wemm #ifdef SIGTSTP
523c2aa98e2SPeter Wemm sigfunc_t oldsigtstp;
5245b0945b5SGregory Neil Shapiro #endif
525c2aa98e2SPeter Wemm
526c2aa98e2SPeter Wemm if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
52740266059SGregory Neil Shapiro return false;
528c2aa98e2SPeter Wemm
52906f25ae9SGregory Neil Shapiro if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
530c2aa98e2SPeter Wemm sff |= SFF_NOWLINK;
53106f25ae9SGregory Neil Shapiro if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
532c2aa98e2SPeter Wemm sff |= SFF_NOGWFILES;
53306f25ae9SGregory Neil Shapiro if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
534c2aa98e2SPeter Wemm sff |= SFF_NOWWFILES;
535c2aa98e2SPeter Wemm
536c2aa98e2SPeter Wemm /* try to lock the source file */
537c2aa98e2SPeter Wemm if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
538c2aa98e2SPeter Wemm {
539c2aa98e2SPeter Wemm struct stat stb;
540c2aa98e2SPeter Wemm
541*d39bd2c1SGregory Neil Shapiro if ((errno != EACCES && errno != EROFS) ||
542c2aa98e2SPeter Wemm (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
543c2aa98e2SPeter Wemm {
544c2aa98e2SPeter Wemm int saveerr = errno;
545c2aa98e2SPeter Wemm
546c2aa98e2SPeter Wemm if (tTd(27, 1))
54740266059SGregory Neil Shapiro sm_dprintf("Can't open %s: %s\n",
54840266059SGregory Neil Shapiro map->map_file, sm_errstring(saveerr));
549*d39bd2c1SGregory Neil Shapiro if (!bitset(MF_OPTIONAL, map->map_mflags))
550c2aa98e2SPeter Wemm message("newaliases: cannot open %s: %s",
55140266059SGregory Neil Shapiro map->map_file, sm_errstring(saveerr));
552c2aa98e2SPeter Wemm errno = 0;
55340266059SGregory Neil Shapiro return false;
554c2aa98e2SPeter Wemm }
55540266059SGregory Neil Shapiro nolock = true;
556c2aa98e2SPeter Wemm if (tTd(27, 1) ||
55740266059SGregory Neil Shapiro fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
558c2aa98e2SPeter Wemm bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
559c2aa98e2SPeter Wemm message("warning: cannot lock %s: %s",
56040266059SGregory Neil Shapiro map->map_file, sm_errstring(errno));
561c2aa98e2SPeter Wemm }
562c2aa98e2SPeter Wemm
563c2aa98e2SPeter Wemm /* see if someone else is rebuilding the alias file */
564c2aa98e2SPeter Wemm if (!nolock &&
56540266059SGregory Neil Shapiro !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
56640266059SGregory Neil Shapiro NULL, LOCK_EX|LOCK_NB))
567c2aa98e2SPeter Wemm {
568c2aa98e2SPeter Wemm /* yes, they are -- wait until done */
569c2aa98e2SPeter Wemm message("Alias file %s is locked (maybe being rebuilt)",
570c2aa98e2SPeter Wemm map->map_file);
571c2aa98e2SPeter Wemm if (OpMode != MD_INITALIAS)
572c2aa98e2SPeter Wemm {
573c2aa98e2SPeter Wemm /* wait for other rebuild to complete */
57440266059SGregory Neil Shapiro (void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
57540266059SGregory Neil Shapiro map->map_file, NULL, LOCK_EX);
576c2aa98e2SPeter Wemm }
57740266059SGregory Neil Shapiro (void) sm_io_close(af, SM_TIME_DEFAULT);
578c2aa98e2SPeter Wemm errno = 0;
57940266059SGregory Neil Shapiro return false;
580c2aa98e2SPeter Wemm }
581c2aa98e2SPeter Wemm
58240266059SGregory Neil Shapiro oldsigint = sm_signal(SIGINT, SIG_IGN);
58340266059SGregory Neil Shapiro oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
584c2aa98e2SPeter Wemm #ifdef SIGTSTP
58540266059SGregory Neil Shapiro oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
5865b0945b5SGregory Neil Shapiro #endif
587c2aa98e2SPeter Wemm
588c2aa98e2SPeter Wemm if (map->map_class->map_open(map, O_RDWR))
589c2aa98e2SPeter Wemm {
590c2aa98e2SPeter Wemm if (LogLevel > 7)
591c2aa98e2SPeter Wemm {
592c2aa98e2SPeter Wemm sm_syslog(LOG_NOTICE, NOQID,
593*d39bd2c1SGregory Neil Shapiro "alias database %s rebuilt by %s",
594*d39bd2c1SGregory Neil Shapiro map->map_file, username());
595c2aa98e2SPeter Wemm }
596c2aa98e2SPeter Wemm map->map_mflags |= MF_OPEN|MF_WRITABLE;
59740266059SGregory Neil Shapiro map->map_pid = CurrentPid;
598*d39bd2c1SGregory Neil Shapiro readaliases(map, af, true, true);
59940266059SGregory Neil Shapiro success = true;
600c2aa98e2SPeter Wemm }
601c2aa98e2SPeter Wemm else
602c2aa98e2SPeter Wemm {
603c2aa98e2SPeter Wemm if (tTd(27, 1))
60440266059SGregory Neil Shapiro sm_dprintf("Can't create database for %s: %s\n",
60540266059SGregory Neil Shapiro map->map_file, sm_errstring(errno));
606c2aa98e2SPeter Wemm syserr("Cannot create database for alias file %s",
607c2aa98e2SPeter Wemm map->map_file);
608c2aa98e2SPeter Wemm }
609c2aa98e2SPeter Wemm
610c2aa98e2SPeter Wemm /* close the file, thus releasing locks */
61140266059SGregory Neil Shapiro (void) sm_io_close(af, SM_TIME_DEFAULT);
612c2aa98e2SPeter Wemm
613c2aa98e2SPeter Wemm /* add distinguished entries and close the database */
614c2aa98e2SPeter Wemm if (bitset(MF_OPEN, map->map_mflags))
615c2aa98e2SPeter Wemm {
6162fb4f839SGregory Neil Shapiro #if _FFR_TESTS
6172fb4f839SGregory Neil Shapiro if (tTd(78, 101))
6182fb4f839SGregory Neil Shapiro {
6192fb4f839SGregory Neil Shapiro int sl;
6202fb4f839SGregory Neil Shapiro
6212fb4f839SGregory Neil Shapiro sl = tTdlevel(78) - 100;
622*d39bd2c1SGregory Neil Shapiro sm_dprintf("rebuildaliases: sleep=%d, file=%s\n",
623*d39bd2c1SGregory Neil Shapiro sl, map->map_file);
6242fb4f839SGregory Neil Shapiro sleep(sl);
625*d39bd2c1SGregory Neil Shapiro sm_dprintf("rebuildaliases: done\n");
6262fb4f839SGregory Neil Shapiro }
6272fb4f839SGregory Neil Shapiro #endif
6288774250cSGregory Neil Shapiro map->map_mflags |= MF_CLOSING;
629c2aa98e2SPeter Wemm map->map_class->map_close(map);
6308774250cSGregory Neil Shapiro map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
631c2aa98e2SPeter Wemm }
632c2aa98e2SPeter Wemm
633c2aa98e2SPeter Wemm /* restore the old signals */
63440266059SGregory Neil Shapiro (void) sm_signal(SIGINT, oldsigint);
63540266059SGregory Neil Shapiro (void) sm_signal(SIGQUIT, oldsigquit);
636c2aa98e2SPeter Wemm #ifdef SIGTSTP
63740266059SGregory Neil Shapiro (void) sm_signal(SIGTSTP, oldsigtstp);
6385b0945b5SGregory Neil Shapiro #endif
639c2aa98e2SPeter Wemm return success;
640c2aa98e2SPeter Wemm }
641*d39bd2c1SGregory Neil Shapiro
642*d39bd2c1SGregory Neil Shapiro /*
643*d39bd2c1SGregory Neil Shapiro ** CONTLINE -- handle potential continuation line
644*d39bd2c1SGregory Neil Shapiro **
645*d39bd2c1SGregory Neil Shapiro ** Parameters:
646*d39bd2c1SGregory Neil Shapiro ** fp -- file to read
647*d39bd2c1SGregory Neil Shapiro ** line -- current line
648*d39bd2c1SGregory Neil Shapiro **
649*d39bd2c1SGregory Neil Shapiro ** Returns:
650*d39bd2c1SGregory Neil Shapiro ** pointer to end of current line if there is a continuation line
651*d39bd2c1SGregory Neil Shapiro ** NULL otherwise
652*d39bd2c1SGregory Neil Shapiro **
653*d39bd2c1SGregory Neil Shapiro ** Side Effects:
654*d39bd2c1SGregory Neil Shapiro ** Modifies line if it is a continuation line
655*d39bd2c1SGregory Neil Shapiro */
656*d39bd2c1SGregory Neil Shapiro
657*d39bd2c1SGregory Neil Shapiro static char *contline __P((SM_FILE_T *, char *));
658*d39bd2c1SGregory Neil Shapiro static char *
contline(fp,line)659*d39bd2c1SGregory Neil Shapiro contline(fp, line)
660*d39bd2c1SGregory Neil Shapiro SM_FILE_T *fp;
661*d39bd2c1SGregory Neil Shapiro char *line;
662*d39bd2c1SGregory Neil Shapiro {
663*d39bd2c1SGregory Neil Shapiro char *p;
664*d39bd2c1SGregory Neil Shapiro int c;
665*d39bd2c1SGregory Neil Shapiro
666*d39bd2c1SGregory Neil Shapiro if ((p = strchr(line, '\n')) != NULL && p > line && p[-1] == '\\')
667*d39bd2c1SGregory Neil Shapiro {
668*d39bd2c1SGregory Neil Shapiro *p = '\0';
669*d39bd2c1SGregory Neil Shapiro *--p = '\0';
670*d39bd2c1SGregory Neil Shapiro return p;
671*d39bd2c1SGregory Neil Shapiro }
672*d39bd2c1SGregory Neil Shapiro
673*d39bd2c1SGregory Neil Shapiro c = sm_io_getc(fp, SM_TIME_DEFAULT);
674*d39bd2c1SGregory Neil Shapiro if (!sm_io_eof(fp))
675*d39bd2c1SGregory Neil Shapiro (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
676*d39bd2c1SGregory Neil Shapiro if (c == ' ' || c == '\t')
677*d39bd2c1SGregory Neil Shapiro {
678*d39bd2c1SGregory Neil Shapiro char *nlp;
679*d39bd2c1SGregory Neil Shapiro
680*d39bd2c1SGregory Neil Shapiro p = line;
681*d39bd2c1SGregory Neil Shapiro nlp = &p[strlen(p)];
682*d39bd2c1SGregory Neil Shapiro if (nlp > p && nlp[-1] == '\n')
683*d39bd2c1SGregory Neil Shapiro *--nlp = '\0';
684*d39bd2c1SGregory Neil Shapiro return nlp;
685*d39bd2c1SGregory Neil Shapiro }
686*d39bd2c1SGregory Neil Shapiro return NULL;
687*d39bd2c1SGregory Neil Shapiro }
688*d39bd2c1SGregory Neil Shapiro
68940266059SGregory Neil Shapiro /*
690c2aa98e2SPeter Wemm ** READALIASES -- read and process the alias file.
691c2aa98e2SPeter Wemm **
692c2aa98e2SPeter Wemm ** This routine implements the part of initaliases that occurs
693c2aa98e2SPeter Wemm ** when we are not going to use the DBM stuff.
694c2aa98e2SPeter Wemm **
695c2aa98e2SPeter Wemm ** Parameters:
696c2aa98e2SPeter Wemm ** map -- the alias database descriptor.
697c2aa98e2SPeter Wemm ** af -- file to read the aliases from.
69806f25ae9SGregory Neil Shapiro ** announcestats -- announce statistics regarding number of
699c2aa98e2SPeter Wemm ** aliases, longest alias, etc.
700c2aa98e2SPeter Wemm ** logstats -- lot the same info.
701c2aa98e2SPeter Wemm **
702c2aa98e2SPeter Wemm ** Returns:
703c2aa98e2SPeter Wemm ** none.
704c2aa98e2SPeter Wemm **
705c2aa98e2SPeter Wemm ** Side Effects:
706c2aa98e2SPeter Wemm ** Reads aliasfile into the symbol table.
707c2aa98e2SPeter Wemm ** Optionally, builds the .dir & .pag files.
708c2aa98e2SPeter Wemm */
709c2aa98e2SPeter Wemm
710c2aa98e2SPeter Wemm void
readaliases(map,af,announcestats,logstats)711c2aa98e2SPeter Wemm readaliases(map, af, announcestats, logstats)
712c2aa98e2SPeter Wemm register MAP *map;
71340266059SGregory Neil Shapiro SM_FILE_T *af;
714c2aa98e2SPeter Wemm bool announcestats;
715c2aa98e2SPeter Wemm bool logstats;
716c2aa98e2SPeter Wemm {
717c2aa98e2SPeter Wemm register char *p;
718c2aa98e2SPeter Wemm char *rhs;
719c2aa98e2SPeter Wemm bool skipping;
720c2aa98e2SPeter Wemm long naliases, bytes, longest;
721c2aa98e2SPeter Wemm ADDRESS al, bl;
7222fb4f839SGregory Neil Shapiro char lbuf[BUFSIZ];
7232fb4f839SGregory Neil Shapiro char *line;
7242fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
7252fb4f839SGregory Neil Shapiro char lhsbuf[MAXNAME]; /* EAI:ok */
7262fb4f839SGregory Neil Shapiro char rhsbuf[BUFSIZ];
7272fb4f839SGregory Neil Shapiro int len;
7282fb4f839SGregory Neil Shapiro #endif
729c2aa98e2SPeter Wemm
730c2aa98e2SPeter Wemm /*
731c2aa98e2SPeter Wemm ** Read and interpret lines
732c2aa98e2SPeter Wemm */
733c2aa98e2SPeter Wemm
734c2aa98e2SPeter Wemm FileName = map->map_file;
735c2aa98e2SPeter Wemm LineNumber = 0;
736c2aa98e2SPeter Wemm naliases = bytes = longest = 0;
73740266059SGregory Neil Shapiro skipping = false;
7382fb4f839SGregory Neil Shapiro line = NULL;
739*d39bd2c1SGregory Neil Shapiro
7402fb4f839SGregory Neil Shapiro while (sm_io_fgets(af, SM_TIME_DEFAULT, lbuf, sizeof(lbuf)) >= 0)
741c2aa98e2SPeter Wemm {
742c2aa98e2SPeter Wemm int lhssize, rhssize;
743c2aa98e2SPeter Wemm int c;
744*d39bd2c1SGregory Neil Shapiro char *newp;
745c2aa98e2SPeter Wemm
746c2aa98e2SPeter Wemm LineNumber++;
74740266059SGregory Neil Shapiro
74840266059SGregory Neil Shapiro /* XXX what if line="a\\" ? */
749*d39bd2c1SGregory Neil Shapiro line = lbuf;
750*d39bd2c1SGregory Neil Shapiro p = line;
751*d39bd2c1SGregory Neil Shapiro while ((newp = contline(af, line)) != NULL)
752c2aa98e2SPeter Wemm {
753*d39bd2c1SGregory Neil Shapiro p = newp;
754*d39bd2c1SGregory Neil Shapiro if ((c = sm_io_fgets(af, SM_TIME_DEFAULT, p,
755*d39bd2c1SGregory Neil Shapiro SPACELEFT(lbuf, p))) < 0)
756*d39bd2c1SGregory Neil Shapiro {
757c2aa98e2SPeter Wemm break;
758c2aa98e2SPeter Wemm }
759*d39bd2c1SGregory Neil Shapiro LineNumber++;
760*d39bd2c1SGregory Neil Shapiro }
761*d39bd2c1SGregory Neil Shapiro #if _FFR_8BITENVADDR
762*d39bd2c1SGregory Neil Shapiro if (SMTP_UTF8 || EightBitAddrOK)
763*d39bd2c1SGregory Neil Shapiro {
764*d39bd2c1SGregory Neil Shapiro if (line != lbuf)
765*d39bd2c1SGregory Neil Shapiro SM_FREE(line);
766*d39bd2c1SGregory Neil Shapiro line = quote_internal_chars(lbuf, NULL, &len, NULL);
767*d39bd2c1SGregory Neil Shapiro }
768*d39bd2c1SGregory Neil Shapiro else
769*d39bd2c1SGregory Neil Shapiro #endif
770*d39bd2c1SGregory Neil Shapiro /* "else" in #if code above */
771*d39bd2c1SGregory Neil Shapiro line = lbuf;
772*d39bd2c1SGregory Neil Shapiro
773*d39bd2c1SGregory Neil Shapiro p = strchr(line, '\n');
774c2aa98e2SPeter Wemm if (p != NULL)
775c2aa98e2SPeter Wemm *p = '\0';
77640266059SGregory Neil Shapiro else if (!sm_io_eof(af))
777c2aa98e2SPeter Wemm {
778*d39bd2c1SGregory Neil Shapiro int prev;
779*d39bd2c1SGregory Neil Shapiro bool cl;
780*d39bd2c1SGregory Neil Shapiro
78106f25ae9SGregory Neil Shapiro errno = 0;
78206f25ae9SGregory Neil Shapiro syserr("554 5.3.0 alias line too long");
783c2aa98e2SPeter Wemm
784*d39bd2c1SGregory Neil Shapiro prev = '\0';
785*d39bd2c1SGregory Neil Shapiro cl = false;
786*d39bd2c1SGregory Neil Shapiro
787*d39bd2c1SGregory Neil Shapiro do {
788*d39bd2c1SGregory Neil Shapiro /* flush to end of "virtual" line */
78940266059SGregory Neil Shapiro while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
79040266059SGregory Neil Shapiro SM_IO_EOF && c != '\n')
791*d39bd2c1SGregory Neil Shapiro {
792*d39bd2c1SGregory Neil Shapiro prev = c;
793*d39bd2c1SGregory Neil Shapiro }
794*d39bd2c1SGregory Neil Shapiro cl = ('\\' == prev && '\n' == c);
795*d39bd2c1SGregory Neil Shapiro if (!cl)
796*d39bd2c1SGregory Neil Shapiro {
797*d39bd2c1SGregory Neil Shapiro c = sm_io_getc(af, SM_TIME_DEFAULT);
798*d39bd2c1SGregory Neil Shapiro if (!sm_io_eof(af))
799*d39bd2c1SGregory Neil Shapiro (void) sm_io_ungetc(af, SM_TIME_DEFAULT, c);
800*d39bd2c1SGregory Neil Shapiro cl = (c == ' ' || c == '\t');
801*d39bd2c1SGregory Neil Shapiro }
802*d39bd2c1SGregory Neil Shapiro } while (cl);
803c2aa98e2SPeter Wemm
804c2aa98e2SPeter Wemm continue;
805c2aa98e2SPeter Wemm }
806*d39bd2c1SGregory Neil Shapiro
807c2aa98e2SPeter Wemm switch (line[0])
808c2aa98e2SPeter Wemm {
809c2aa98e2SPeter Wemm case '#':
810c2aa98e2SPeter Wemm case '\0':
81140266059SGregory Neil Shapiro skipping = false;
812c2aa98e2SPeter Wemm continue;
813c2aa98e2SPeter Wemm
814c2aa98e2SPeter Wemm case ' ':
815c2aa98e2SPeter Wemm case '\t':
816c2aa98e2SPeter Wemm if (!skipping)
81706f25ae9SGregory Neil Shapiro syserr("554 5.3.5 Non-continuation line starts with space");
81840266059SGregory Neil Shapiro skipping = true;
819c2aa98e2SPeter Wemm continue;
820c2aa98e2SPeter Wemm }
82140266059SGregory Neil Shapiro skipping = false;
822c2aa98e2SPeter Wemm
823c2aa98e2SPeter Wemm /*
824c2aa98e2SPeter Wemm ** Process the LHS
825c2aa98e2SPeter Wemm ** Find the colon separator, and parse the address.
826c2aa98e2SPeter Wemm ** It should resolve to a local name -- this will
827c2aa98e2SPeter Wemm ** be checked later (we want to optionally do
828c2aa98e2SPeter Wemm ** parsing of the RHS first to maximize error
829c2aa98e2SPeter Wemm ** detection).
830c2aa98e2SPeter Wemm */
831c2aa98e2SPeter Wemm
832c2aa98e2SPeter Wemm for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
833c2aa98e2SPeter Wemm continue;
834c2aa98e2SPeter Wemm if (*p++ != ':')
835c2aa98e2SPeter Wemm {
83606f25ae9SGregory Neil Shapiro syserr("554 5.3.5 missing colon");
837c2aa98e2SPeter Wemm continue;
838c2aa98e2SPeter Wemm }
8392fb4f839SGregory Neil Shapiro /* XXX line must be [i] */
84040266059SGregory Neil Shapiro if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
84140266059SGregory Neil Shapiro == NULL)
842c2aa98e2SPeter Wemm {
84306f25ae9SGregory Neil Shapiro syserr("554 5.3.5 %.40s... illegal alias name", line);
844c2aa98e2SPeter Wemm continue;
845c2aa98e2SPeter Wemm }
846c2aa98e2SPeter Wemm
847c2aa98e2SPeter Wemm /*
848c2aa98e2SPeter Wemm ** Process the RHS.
849c2aa98e2SPeter Wemm ** 'al' is the internal form of the LHS address.
850c2aa98e2SPeter Wemm ** 'p' points to the text of the RHS.
851c2aa98e2SPeter Wemm */
852c2aa98e2SPeter Wemm
8535b0945b5SGregory Neil Shapiro while (SM_ISSPACE(*p))
854c2aa98e2SPeter Wemm p++;
855c2aa98e2SPeter Wemm rhs = p;
856c2aa98e2SPeter Wemm {
857c2aa98e2SPeter Wemm register char *nlp;
858c2aa98e2SPeter Wemm
859c2aa98e2SPeter Wemm nlp = &p[strlen(p)];
860193538b7SGregory Neil Shapiro if (nlp > p && nlp[-1] == '\n')
861c2aa98e2SPeter Wemm *--nlp = '\0';
862c2aa98e2SPeter Wemm
863c2aa98e2SPeter Wemm if (CheckAliases)
864c2aa98e2SPeter Wemm {
865c2aa98e2SPeter Wemm /* do parsing & compression of addresses */
866c2aa98e2SPeter Wemm while (*p != '\0')
867c2aa98e2SPeter Wemm {
868c2aa98e2SPeter Wemm auto char *delimptr;
869c2aa98e2SPeter Wemm
8705b0945b5SGregory Neil Shapiro while ((SM_ISSPACE(*p)) || *p == ',')
871c2aa98e2SPeter Wemm p++;
872c2aa98e2SPeter Wemm if (*p == '\0')
873c2aa98e2SPeter Wemm break;
8742fb4f839SGregory Neil Shapiro /* XXX p must be [i] */
875c2aa98e2SPeter Wemm if (parseaddr(p, &bl, RF_COPYNONE, ',',
87640266059SGregory Neil Shapiro &delimptr, CurEnv, true)
87740266059SGregory Neil Shapiro == NULL)
87806f25ae9SGregory Neil Shapiro usrerr("553 5.3.5 %s... bad address", p);
879c2aa98e2SPeter Wemm p = delimptr;
880c2aa98e2SPeter Wemm }
881c2aa98e2SPeter Wemm }
882c2aa98e2SPeter Wemm else
883c2aa98e2SPeter Wemm {
884c2aa98e2SPeter Wemm p = nlp;
885c2aa98e2SPeter Wemm }
886*d39bd2c1SGregory Neil Shapiro } while (0);
887c2aa98e2SPeter Wemm
888c2aa98e2SPeter Wemm if (skipping)
889c2aa98e2SPeter Wemm continue;
890c2aa98e2SPeter Wemm
891c2aa98e2SPeter Wemm if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
892c2aa98e2SPeter Wemm {
89306f25ae9SGregory Neil Shapiro syserr("554 5.3.5 %s... cannot alias non-local names",
894c2aa98e2SPeter Wemm al.q_paddr);
895c2aa98e2SPeter Wemm continue;
896c2aa98e2SPeter Wemm }
897c2aa98e2SPeter Wemm
898c2aa98e2SPeter Wemm /*
899c2aa98e2SPeter Wemm ** Insert alias into symbol table or database file.
900c2aa98e2SPeter Wemm **
901c2aa98e2SPeter Wemm ** Special case pOStmaStER -- always make it lower case.
902c2aa98e2SPeter Wemm */
903c2aa98e2SPeter Wemm
9042fb4f839SGregory Neil Shapiro if (SM_STRCASEEQ(al.q_user, "postmaster"))
9052fb4f839SGregory Neil Shapiro makelower_a(&al.q_user, CurEnv->e_rpool);
906c2aa98e2SPeter Wemm
907c2aa98e2SPeter Wemm lhssize = strlen(al.q_user);
908c2aa98e2SPeter Wemm rhssize = strlen(rhs);
90906f25ae9SGregory Neil Shapiro if (rhssize > 0)
91006f25ae9SGregory Neil Shapiro {
91106f25ae9SGregory Neil Shapiro /* is RHS empty (just spaces)? */
91206f25ae9SGregory Neil Shapiro p = rhs;
9135b0945b5SGregory Neil Shapiro while (SM_ISSPACE(*p))
91406f25ae9SGregory Neil Shapiro p++;
91506f25ae9SGregory Neil Shapiro }
91606f25ae9SGregory Neil Shapiro if (rhssize == 0 || *p == '\0')
91706f25ae9SGregory Neil Shapiro {
91806f25ae9SGregory Neil Shapiro syserr("554 5.3.5 %.40s... missing value for alias",
91906f25ae9SGregory Neil Shapiro line);
92006f25ae9SGregory Neil Shapiro }
92106f25ae9SGregory Neil Shapiro else
92206f25ae9SGregory Neil Shapiro {
9232fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
924*d39bd2c1SGregory Neil Shapiro if (SMTP_UTF8 || EightBitAddrOK)
925*d39bd2c1SGregory Neil Shapiro {
9262fb4f839SGregory Neil Shapiro dequote_internal_chars(al.q_user, lhsbuf, sizeof(lhsbuf));
9272fb4f839SGregory Neil Shapiro dequote_internal_chars(rhs, rhsbuf, sizeof(rhsbuf));
9282fb4f839SGregory Neil Shapiro map->map_class->map_store(map, lhsbuf, rhsbuf);
929*d39bd2c1SGregory Neil Shapiro }
930*d39bd2c1SGregory Neil Shapiro else
9312fb4f839SGregory Neil Shapiro #endif
932*d39bd2c1SGregory Neil Shapiro /* "else" in #if code above */
933*d39bd2c1SGregory Neil Shapiro map->map_class->map_store(map, al.q_user, rhs);
934c2aa98e2SPeter Wemm
93506f25ae9SGregory Neil Shapiro /* statistics */
93606f25ae9SGregory Neil Shapiro naliases++;
93706f25ae9SGregory Neil Shapiro bytes += lhssize + rhssize;
93806f25ae9SGregory Neil Shapiro if (rhssize > longest)
93906f25ae9SGregory Neil Shapiro longest = rhssize;
94006f25ae9SGregory Neil Shapiro }
941c2aa98e2SPeter Wemm }
942c2aa98e2SPeter Wemm
943c2aa98e2SPeter Wemm CurEnv->e_to = NULL;
944c2aa98e2SPeter Wemm FileName = NULL;
945c2aa98e2SPeter Wemm if (Verbose || announcestats)
94642e5d165SGregory Neil Shapiro message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
947c2aa98e2SPeter Wemm map->map_file, naliases, longest, bytes);
948c2aa98e2SPeter Wemm if (LogLevel > 7 && logstats)
949c2aa98e2SPeter Wemm sm_syslog(LOG_INFO, NOQID,
95042e5d165SGregory Neil Shapiro "%s: %ld aliases, longest %ld bytes, %ld bytes total",
951c2aa98e2SPeter Wemm map->map_file, naliases, longest, bytes);
952c2aa98e2SPeter Wemm }
95340266059SGregory Neil Shapiro /*
954c2aa98e2SPeter Wemm ** FORWARD -- Try to forward mail
955c2aa98e2SPeter Wemm **
956c2aa98e2SPeter Wemm ** This is similar but not identical to aliasing.
957c2aa98e2SPeter Wemm **
958c2aa98e2SPeter Wemm ** Parameters:
959c2aa98e2SPeter Wemm ** user -- the name of the user who's mail we would like
960c2aa98e2SPeter Wemm ** to forward to. It must have been verified --
9612fb4f839SGregory Neil Shapiro ** i.e., the q_home field must have been filled in.
962c2aa98e2SPeter Wemm ** sendq -- a pointer to the head of the send queue to
963c2aa98e2SPeter Wemm ** put this user's aliases in.
964c2aa98e2SPeter Wemm ** aliaslevel -- the current alias nesting depth.
965c2aa98e2SPeter Wemm ** e -- the current envelope.
966c2aa98e2SPeter Wemm **
967c2aa98e2SPeter Wemm ** Returns:
968c2aa98e2SPeter Wemm ** none.
969c2aa98e2SPeter Wemm **
970c2aa98e2SPeter Wemm ** Side Effects:
971c2aa98e2SPeter Wemm ** New names are added to send queues.
972c2aa98e2SPeter Wemm */
973c2aa98e2SPeter Wemm
974c2aa98e2SPeter Wemm void
forward(user,sendq,aliaslevel,e)975c2aa98e2SPeter Wemm forward(user, sendq, aliaslevel, e)
976c2aa98e2SPeter Wemm ADDRESS *user;
977c2aa98e2SPeter Wemm ADDRESS **sendq;
978c2aa98e2SPeter Wemm int aliaslevel;
979c2aa98e2SPeter Wemm register ENVELOPE *e;
980c2aa98e2SPeter Wemm {
981c2aa98e2SPeter Wemm char *pp;
982c2aa98e2SPeter Wemm char *ep;
983c2aa98e2SPeter Wemm bool got_transient;
984c2aa98e2SPeter Wemm
985c2aa98e2SPeter Wemm if (tTd(27, 1))
98640266059SGregory Neil Shapiro sm_dprintf("forward(%s)\n", user->q_paddr);
987c2aa98e2SPeter Wemm
988c2aa98e2SPeter Wemm if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
98906f25ae9SGregory Neil Shapiro !QS_IS_OK(user->q_state))
990c2aa98e2SPeter Wemm return;
99140266059SGregory Neil Shapiro if (ForwardPath != NULL && *ForwardPath == '\0')
99240266059SGregory Neil Shapiro return;
993c2aa98e2SPeter Wemm if (user->q_home == NULL)
994c2aa98e2SPeter Wemm {
99506f25ae9SGregory Neil Shapiro syserr("554 5.3.0 forward: no home");
996c2aa98e2SPeter Wemm user->q_home = "/no/such/directory";
997c2aa98e2SPeter Wemm }
998c2aa98e2SPeter Wemm
999c2aa98e2SPeter Wemm /* good address -- look for .forward file in home */
100040266059SGregory Neil Shapiro macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
100140266059SGregory Neil Shapiro macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
1002*d39bd2c1SGregory Neil Shapiro pp = user->q_host;
1003*d39bd2c1SGregory Neil Shapiro #if _FFR_8BITENVADDR
1004*d39bd2c1SGregory Neil Shapiro if (NULL != pp)
1005*d39bd2c1SGregory Neil Shapiro {
1006*d39bd2c1SGregory Neil Shapiro int len;
1007*d39bd2c1SGregory Neil Shapiro
1008*d39bd2c1SGregory Neil Shapiro pp = quote_internal_chars(pp, NULL, &len, NULL);
1009*d39bd2c1SGregory Neil Shapiro }
1010*d39bd2c1SGregory Neil Shapiro #endif
1011*d39bd2c1SGregory Neil Shapiro macdefine(&e->e_macro, A_PERM, 'h', pp);
1012c2aa98e2SPeter Wemm if (ForwardPath == NULL)
1013c2aa98e2SPeter Wemm ForwardPath = newstr("\201z/.forward");
1014c2aa98e2SPeter Wemm
101540266059SGregory Neil Shapiro got_transient = false;
1016c2aa98e2SPeter Wemm for (pp = ForwardPath; pp != NULL; pp = ep)
1017c2aa98e2SPeter Wemm {
1018c2aa98e2SPeter Wemm int err;
101994c01205SGregory Neil Shapiro char buf[MAXPATHLEN];
102006f25ae9SGregory Neil Shapiro struct stat st;
1021c2aa98e2SPeter Wemm
102206f25ae9SGregory Neil Shapiro ep = strchr(pp, SEPARATOR);
1023c2aa98e2SPeter Wemm if (ep != NULL)
1024c2aa98e2SPeter Wemm *ep = '\0';
1025d0cef73dSGregory Neil Shapiro expand(pp, buf, sizeof(buf), e);
1026c2aa98e2SPeter Wemm if (ep != NULL)
102706f25ae9SGregory Neil Shapiro *ep++ = SEPARATOR;
1028c2aa98e2SPeter Wemm if (buf[0] == '\0')
1029c2aa98e2SPeter Wemm continue;
1030c2aa98e2SPeter Wemm if (tTd(27, 3))
103140266059SGregory Neil Shapiro sm_dprintf("forward: trying %s\n", buf);
1032c2aa98e2SPeter Wemm
103340266059SGregory Neil Shapiro err = include(buf, true, user, sendq, aliaslevel, e);
1034c2aa98e2SPeter Wemm if (err == 0)
1035c2aa98e2SPeter Wemm break;
1036c2aa98e2SPeter Wemm else if (transienterror(err))
1037c2aa98e2SPeter Wemm {
1038c2aa98e2SPeter Wemm /* we may have to suspend this message */
103940266059SGregory Neil Shapiro got_transient = true;
1040c2aa98e2SPeter Wemm if (tTd(27, 2))
104140266059SGregory Neil Shapiro sm_dprintf("forward: transient error on %s\n",
104206f25ae9SGregory Neil Shapiro buf);
1043c2aa98e2SPeter Wemm if (LogLevel > 2)
104406f25ae9SGregory Neil Shapiro {
104506f25ae9SGregory Neil Shapiro char *curhost = CurHostName;
104606f25ae9SGregory Neil Shapiro
104706f25ae9SGregory Neil Shapiro CurHostName = NULL;
1048c2aa98e2SPeter Wemm sm_syslog(LOG_ERR, e->e_id,
1049c2aa98e2SPeter Wemm "forward %s: transient error: %s",
105040266059SGregory Neil Shapiro buf, sm_errstring(err));
105106f25ae9SGregory Neil Shapiro CurHostName = curhost;
105206f25ae9SGregory Neil Shapiro }
105306f25ae9SGregory Neil Shapiro
1054c2aa98e2SPeter Wemm }
1055c2aa98e2SPeter Wemm else
1056c2aa98e2SPeter Wemm {
1057c2aa98e2SPeter Wemm switch (err)
1058c2aa98e2SPeter Wemm {
1059c2aa98e2SPeter Wemm case ENOENT:
1060c2aa98e2SPeter Wemm break;
1061c2aa98e2SPeter Wemm
106206f25ae9SGregory Neil Shapiro case E_SM_WWDIR:
106306f25ae9SGregory Neil Shapiro case E_SM_GWDIR:
106406f25ae9SGregory Neil Shapiro /* check if it even exists */
106506f25ae9SGregory Neil Shapiro if (stat(buf, &st) < 0 && errno == ENOENT)
106606f25ae9SGregory Neil Shapiro {
106706f25ae9SGregory Neil Shapiro if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH,
106806f25ae9SGregory Neil Shapiro DontBlameSendmail))
106906f25ae9SGregory Neil Shapiro break;
107006f25ae9SGregory Neil Shapiro }
107106f25ae9SGregory Neil Shapiro /* FALLTHROUGH */
107206f25ae9SGregory Neil Shapiro
1073c2aa98e2SPeter Wemm #if _FFR_FORWARD_SYSERR
1074c2aa98e2SPeter Wemm case E_SM_NOSLINK:
1075c2aa98e2SPeter Wemm case E_SM_NOHLINK:
1076c2aa98e2SPeter Wemm case E_SM_REGONLY:
1077c2aa98e2SPeter Wemm case E_SM_ISEXEC:
1078c2aa98e2SPeter Wemm case E_SM_WWFILE:
1079c2aa98e2SPeter Wemm case E_SM_GWFILE:
108040266059SGregory Neil Shapiro syserr("forward: %s: %s", buf, sm_errstring(err));
1081c2aa98e2SPeter Wemm break;
108206f25ae9SGregory Neil Shapiro #endif /* _FFR_FORWARD_SYSERR */
1083c2aa98e2SPeter Wemm
1084c2aa98e2SPeter Wemm default:
1085c2aa98e2SPeter Wemm if (LogLevel > (RunAsUid == 0 ? 2 : 10))
1086c2aa98e2SPeter Wemm sm_syslog(LOG_WARNING, e->e_id,
1087c2aa98e2SPeter Wemm "forward %s: %s", buf,
108840266059SGregory Neil Shapiro sm_errstring(err));
1089c2aa98e2SPeter Wemm if (Verbose)
1090c2aa98e2SPeter Wemm message("forward: %s: %s",
109140266059SGregory Neil Shapiro buf, sm_errstring(err));
1092c2aa98e2SPeter Wemm break;
1093c2aa98e2SPeter Wemm }
1094c2aa98e2SPeter Wemm }
1095c2aa98e2SPeter Wemm }
1096c2aa98e2SPeter Wemm if (pp == NULL && got_transient)
1097c2aa98e2SPeter Wemm {
1098c2aa98e2SPeter Wemm /*
1099c2aa98e2SPeter Wemm ** There was no successful .forward open and at least one
1100c2aa98e2SPeter Wemm ** transient open. We have to defer this address for
1101c2aa98e2SPeter Wemm ** further delivery.
1102c2aa98e2SPeter Wemm */
1103c2aa98e2SPeter Wemm
1104c2aa98e2SPeter Wemm message("transient .forward open error: message queued");
110506f25ae9SGregory Neil Shapiro user->q_state = QS_QUEUEUP;
1106c2aa98e2SPeter Wemm return;
1107c2aa98e2SPeter Wemm }
1108c2aa98e2SPeter Wemm }
1109