]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/alias.c
Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / alias.c
1 /*
2  * Copyright (c) 1998-2003 Proofpoint, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13
14 #include <sendmail.h>
15
16 SM_RCSID("@(#)$Id: alias.c,v 8.221 2013-11-22 20:51:54 ca Exp $")
17
18 #include <sm/sendmail.h>
19
20 #define SEPARATOR ':'
21 # define ALIAS_SPEC_SEPARATORS  " ,/:"
22
23 static MAP      *AliasFileMap = NULL;   /* the actual aliases.files map */
24 static int      NAliasFileMaps; /* the number of entries in AliasFileMap */
25
26 static char     *aliaslookup __P((char *, int *, char *));
27
28 /*
29 **  ALIAS -- Compute aliases.
30 **
31 **      Scans the alias file for an alias for the given address.
32 **      If found, it arranges to deliver to the alias list instead.
33 **      Uses libdbm database if -DDBM.
34 **
35 **      Parameters:
36 **              a -- address to alias.
37 **              sendq -- a pointer to the head of the send queue
38 **                      to put the aliases in.
39 **              aliaslevel -- the current alias nesting depth.
40 **              e -- the current envelope.
41 **
42 **      Returns:
43 **              none
44 **
45 **      Side Effects:
46 **              Aliases found are expanded.
47 **
48 **      Deficiencies:
49 **              It should complain about names that are aliased to
50 **                      nothing.
51 */
52
53 void
54 alias(a, sendq, aliaslevel, e)
55         register ADDRESS *a;
56         ADDRESS **sendq;
57         int aliaslevel;
58         register ENVELOPE *e;
59 {
60         register char *p;
61         char *owner;
62         auto int status = EX_OK;
63         char obuf[MAXNAME_I + 7];
64
65         if (tTd(27, 1))
66                 sm_dprintf("alias(%s)\n", a->q_user);
67
68         /* don't realias already aliased names */
69         if (!QS_IS_OK(a->q_state))
70                 return;
71
72         if (NoAlias)
73                 return;
74
75         e->e_to = a->q_paddr;
76
77         /*
78         **  Look up this name.
79         **
80         **      If the map was unavailable, we will queue this message
81         **      until the map becomes available; otherwise, we could
82         **      bounce messages inappropriately.
83         */
84
85 #if _FFR_REDIRECTEMPTY
86         /*
87         **  envelope <> can't be sent to mailing lists, only owner-
88         **  send spam of this type to owner- of the list
89         **  ----  to stop spam from going to mailing lists!
90         */
91
92         if (e->e_sender != NULL && *e->e_sender == '\0')
93         {
94                 /* Look for owner of alias */
95                 (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
96                 if (aliaslookup(obuf, &status, a->q_host) != NULL)
97                 {
98                         if (LogLevel > 8)
99                                 sm_syslog(LOG_WARNING, e->e_id,
100                                        "possible spam from <> to list: %s, redirected to %s\n",
101                                        a->q_user, obuf);
102                         a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf);
103                 }
104         }
105 #endif /* _FFR_REDIRECTEMPTY */
106
107         p = aliaslookup(a->q_user, &status, a->q_host);
108         if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
109         {
110                 a->q_state = QS_QUEUEUP;
111                 if (e->e_message == NULL)
112                         e->e_message = sm_rpool_strdup_x(e->e_rpool,
113                                                 "alias database unavailable");
114
115                 /* XXX msg only per recipient? */
116                 if (a->q_message == NULL)
117                         a->q_message = "alias database unavailable";
118                 return;
119         }
120         if (p == NULL)
121                 return;
122
123         /*
124         **  Match on Alias.
125         **      Deliver to the target list.
126         */
127
128         if (tTd(27, 1))
129                 sm_dprintf("%s (%s, %s) aliased to %s\n",
130                            a->q_paddr, a->q_host, a->q_user, p);
131         if (bitset(EF_VRFYONLY, e->e_flags))
132         {
133                 a->q_state = QS_VERIFIED;
134                 return;
135         }
136         message("aliased to %s", shortenstring(p, MAXSHORTSTR));
137         if (LogLevel > 10)
138                 sm_syslog(LOG_INFO, e->e_id,
139                           "alias %.100s => %s",
140                           a->q_paddr, shortenstring(p, MAXSHORTSTR));
141         a->q_flags &= ~QSELFREF;
142         if (tTd(27, 5))
143         {
144                 sm_dprintf("alias: QS_EXPANDED ");
145                 printaddr(sm_debug_file(), a, false);
146         }
147         a->q_state = QS_EXPANDED;
148
149         /*
150         **  Always deliver aliased items as the default user.
151         **  Setting q_gid to 0 forces deliver() to use DefUser
152         **  instead of the alias name for the call to initgroups().
153         */
154
155         a->q_uid = DefUid;
156         a->q_gid = 0;
157         a->q_fullname = NULL;
158         a->q_flags |= QGOODUID|QALIAS;
159
160         (void) sendtolist(p, a, sendq, aliaslevel + 1, e);
161
162         if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
163                 a->q_state = QS_OK;
164
165         /*
166         **  Look for owner of alias
167         */
168
169         if (strncmp(a->q_user, "owner-", 6) == 0 ||
170             strlen(a->q_user) > sizeof(obuf) - 7)
171                 (void) sm_strlcpy(obuf, "owner-owner", sizeof(obuf));
172         else
173                 (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
174         owner = aliaslookup(obuf, &status, a->q_host);
175         if (owner == NULL)
176                 return;
177
178         /* reflect owner into envelope sender */
179         if (strpbrk(owner, ",:/|\"") != NULL)
180                 owner = obuf;
181         a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
182
183         /* announce delivery to this alias; NORECEIPT bit set later */
184         if (e->e_xfp != NULL)
185                 (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
186                                 "Message delivered to mailing list %s\n",
187                                 a->q_paddr);
188         e->e_flags |= EF_SENDRECEIPT;
189         a->q_flags |= QDELIVERED|QEXPANDED;
190 }
191
192 /*
193 **  ALIASLOOKUP -- look up a name in the alias file.
194 **
195 **      Parameters:
196 **              name -- the name to look up [i]
197 **              pstat -- a pointer to a place to put the status.
198 **              av -- argument for %1 expansion.
199 **
200 **      Returns:
201 **              the value of name.
202 **              NULL if unknown.
203 **
204 **      Warnings:
205 **              The return value will be trashed across calls.
206 */
207
208 static char *
209 aliaslookup(name, pstat, av)
210         char *name;
211         int *pstat;
212         char *av;
213 {
214         static MAP *map = NULL;
215         char *res;
216 #if _FFR_ALIAS_DETAIL
217         int i;
218         char *argv[4];
219 #else
220 # define argv NULL
221 #endif
222 #if _FFR_8BITENVADDR
223         char buf[MAXNAME];      /* EAI:ok */
224 #endif
225
226         if (map == NULL)
227         {
228                 STAB *s = stab("aliases", ST_MAP, ST_FIND);
229
230                 if (s == NULL)
231                         return NULL;
232                 map = &s->s_map;
233         }
234         DYNOPENMAP(map);
235
236         /* special case POstMastER -- always use lower case */
237         if (SM_STRCASEEQ(name, "postmaster"))
238                 name = "postmaster";
239 #if _FFR_8BITENVADDR
240         (void) dequote_internal_chars(name, buf, sizeof(buf));
241         /* check length? */
242         name = buf;
243 #endif /* _FFR_8BITENVADDR */
244
245 #if _FFR_ALIAS_DETAIL
246         i = 0;
247         argv[i++] = name;
248         argv[i++] = av;
249
250         /* XXX '+' is hardwired here as delimiter! */
251         if (av != NULL && *av == '+')
252                 argv[i++] = av + 1;
253         argv[i++] = NULL;
254 #endif /* _FFR_ALIAS_DETAIL */
255         res = (*map->map_class->map_lookup)(map, name, argv, pstat);
256 #if _FFR_8BITENVADDR
257         /* map_lookup() does a map_rewrite(), so no quoting here */
258 #endif
259         return res;
260 }
261
262 /*
263 **  SETALIAS -- set up an alias map
264 **
265 **      Called when reading configuration file.
266 **
267 **      Parameters:
268 **              spec -- the alias specification
269 **
270 **      Returns:
271 **              none.
272 */
273
274 void
275 setalias(spec)
276         char *spec;
277 {
278         register char *p;
279         register MAP *map;
280         char *class;
281         STAB *s;
282
283         if (tTd(27, 8))
284                 sm_dprintf("setalias(%s)\n", spec);
285
286         for (p = spec; p != NULL; )
287         {
288                 char buf[50];
289
290                 while (SM_ISSPACE(*p))
291                         p++;
292                 if (*p == '\0')
293                         break;
294                 spec = p;
295
296                 if (NAliasFileMaps >= MAXMAPSTACK)
297                 {
298                         syserr("Too many alias databases defined, %d max",
299                                 MAXMAPSTACK);
300                         return;
301                 }
302                 if (AliasFileMap == NULL)
303                 {
304                         (void) sm_strlcpy(buf, "aliases.files sequence",
305                                           sizeof(buf));
306                         AliasFileMap = makemapentry(buf);
307                         if (AliasFileMap == NULL)
308                         {
309                                 syserr("setalias: cannot create aliases.files map");
310                                 return;
311                         }
312                 }
313                 (void) sm_snprintf(buf, sizeof(buf), "Alias%d", NAliasFileMaps);
314                 s = stab(buf, ST_MAP, ST_ENTER);
315                 map = &s->s_map;
316                 memset(map, '\0', sizeof(*map));
317                 map->map_mname = s->s_name;
318                 p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
319                 if (p != NULL && *p == SEPARATOR)
320                 {
321                         /* map name */
322                         *p++ = '\0';
323                         class = spec;
324                         spec = p;
325                 }
326                 else
327                 {
328                         class = "implicit";
329                         map->map_mflags = MF_INCLNULL;
330                 }
331
332                 /* find end of spec */
333                 if (p != NULL)
334                 {
335                         bool quoted = false;
336
337                         for (; *p != '\0'; p++)
338                         {
339                                 /*
340                                 **  Don't break into a quoted string.
341                                 **  Needed for ldap maps which use
342                                 **  commas in their specifications.
343                                 */
344
345                                 if (*p == '"')
346                                         quoted = !quoted;
347                                 else if (*p == ',' && !quoted)
348                                         break;
349                         }
350
351                         /* No more alias specifications follow */
352                         if (*p == '\0')
353                                 p = NULL;
354                 }
355                 if (p != NULL)
356                         *p++ = '\0';
357
358                 if (tTd(27, 20))
359                         sm_dprintf("  map %s:%s %s\n", class, s->s_name, spec);
360
361                 /* look up class */
362                 s = stab(class, ST_MAPCLASS, ST_FIND);
363                 if (s == NULL)
364                 {
365                         syserr("setalias: unknown alias class %s", class);
366                 }
367                 else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
368                 {
369                         syserr("setalias: map class %s can't handle aliases",
370                                 class);
371                 }
372                 else
373                 {
374                         map->map_class = &s->s_mapclass;
375                         map->map_mflags |= MF_ALIAS;
376                         if (map->map_class->map_parse(map, spec))
377                         {
378                                 map->map_mflags |= MF_VALID;
379                                 AliasFileMap->map_stack[NAliasFileMaps++] = map;
380                         }
381                 }
382         }
383 }
384
385 /*
386 **  ALIASWAIT -- wait for distinguished @:@ token to appear.
387 **
388 **      This can decide to reopen the alias file
389 **
390 **      Parameters:
391 **              map -- a pointer to the map descriptor for this alias file.
392 **              ext -- the filename extension (e.g., ".db") for the
393 **                      database file.
394 **              isopen -- if set, the database is already open, and we
395 **                      should check for validity; otherwise, we are
396 **                      just checking to see if it should be created.
397 **
398 **      Returns:
399 **              true -- if the database is open when we return.
400 **              false -- if the database is closed when we return.
401 */
402
403 bool
404 aliaswait(map, ext, isopen)
405         MAP *map;
406         const char *ext;
407         bool isopen;
408 {
409         bool attimeout = false;
410         time_t mtime;
411         struct stat stb;
412         char buf[MAXPATHLEN];
413
414         if (tTd(27, 3))
415                 sm_dprintf("aliaswait(%s:%s), open=%d, wait=%d\n",
416                            map->map_class->map_cname, map->map_file,
417                            isopen, bitset(MF_ALIASWAIT, map->map_mflags));
418         if (bitset(MF_ALIASWAIT, map->map_mflags))
419                 return isopen;
420         map->map_mflags |= MF_ALIASWAIT;
421
422         if (isopen && SafeAlias > 0)
423         {
424                 auto int st;
425                 unsigned int sleeptime = 2;
426                 unsigned int loopcount = 0;     /* only used for debugging */
427                 time_t toolong = curtime() + SafeAlias;
428
429                 while (isopen &&
430                        map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
431                 {
432                         if (curtime() > toolong)
433                         {
434                                 /* we timed out */
435                                 attimeout = true;
436                                 break;
437                         }
438
439                         /*
440                         **  Close and re-open the alias database in case
441                         **  the one is mv'ed instead of cp'ed in.
442                         */
443
444                         if (tTd(27, 2))
445                         {
446                                 loopcount++;
447                                 sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
448                                            sleeptime, loopcount);
449                         }
450
451                         map->map_mflags |= MF_CLOSING;
452                         map->map_class->map_close(map);
453                         map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING|MF_CHKED_CHGD);
454                         (void) sleep(sleeptime);
455                         sleeptime *= 2;
456                         if (sleeptime > 60)
457                                 sleeptime = 60;
458                         isopen = map->map_class->map_open(map, O_RDONLY);
459                 }
460         }
461         map->map_mflags &= ~MF_CHKED_CHGD;
462
463         /* see if we need to go into auto-rebuild mode */
464         if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
465         {
466                 if (tTd(27, 3))
467                         sm_dprintf("aliaswait: not rebuildable\n");
468                 map->map_mflags &= ~MF_ALIASWAIT;
469                 return isopen;
470         }
471         if (stat(map->map_file, &stb) < 0)
472         {
473                 if (tTd(27, 3))
474                         sm_dprintf("aliaswait: no source file\n");
475                 map->map_mflags &= ~MF_ALIASWAIT;
476                 return isopen;
477         }
478         mtime = stb.st_mtime;
479         if (sm_strlcpyn(buf, sizeof(buf), 2,
480                         map->map_file, ext == NULL ? "" : ext) >= sizeof(buf))
481         {
482                 if (LogLevel > 3)
483                         sm_syslog(LOG_INFO, NOQID,
484                                   "alias database %s%s name too long",
485                                   map->map_file, ext == NULL ? "" : ext);
486                 message("alias database %s%s name too long",
487                         map->map_file, ext == NULL ? "" : ext);
488         }
489
490         if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
491         {
492                 if (LogLevel > 3)
493                         sm_syslog(LOG_INFO, NOQID,
494                                   "alias database %s out of date", buf);
495                 message("Warning: alias database %s out of date", buf);
496         }
497         map->map_mflags &= ~MF_ALIASWAIT;
498         return isopen;
499 }
500 /*
501 **  REBUILDALIASES -- rebuild the alias database.
502 **
503 **      Parameters:
504 **              map -- the database to rebuild.
505 **
506 **      Returns:
507 **              true if successful; false otherwise.
508 **
509 **      Side Effects:
510 **              Reads the text version of the database, builds the map.
511 */
512
513 bool
514 rebuildaliases(map)
515         register MAP *map;
516 {
517         SM_FILE_T *af;
518         bool nolock = false;
519         bool success = false;
520         long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
521         sigfunc_t oldsigint, oldsigquit;
522 #ifdef SIGTSTP
523         sigfunc_t oldsigtstp;
524 #endif
525
526         if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
527                 return false;
528
529         if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
530                 sff |= SFF_NOWLINK;
531         if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
532                 sff |= SFF_NOGWFILES;
533         if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
534                 sff |= SFF_NOWWFILES;
535
536         /* try to lock the source file */
537         if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
538         {
539                 struct stat stb;
540
541                 if ((errno != EACCES && errno != EROFS) ||
542                     (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
543                 {
544                         int saveerr = errno;
545
546                         if (tTd(27, 1))
547                                 sm_dprintf("Can't open %s: %s\n",
548                                         map->map_file, sm_errstring(saveerr));
549                         if (!bitset(MF_OPTIONAL, map->map_mflags))
550                                 message("newaliases: cannot open %s: %s",
551                                         map->map_file, sm_errstring(saveerr));
552                         errno = 0;
553                         return false;
554                 }
555                 nolock = true;
556                 if (tTd(27, 1) ||
557                     fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
558                     bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
559                         message("warning: cannot lock %s: %s",
560                                 map->map_file, sm_errstring(errno));
561         }
562
563         /* see if someone else is rebuilding the alias file */
564         if (!nolock &&
565             !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
566                       NULL, LOCK_EX|LOCK_NB))
567         {
568                 /* yes, they are -- wait until done */
569                 message("Alias file %s is locked (maybe being rebuilt)",
570                         map->map_file);
571                 if (OpMode != MD_INITALIAS)
572                 {
573                         /* wait for other rebuild to complete */
574                         (void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
575                                         map->map_file, NULL, LOCK_EX);
576                 }
577                 (void) sm_io_close(af, SM_TIME_DEFAULT);
578                 errno = 0;
579                 return false;
580         }
581
582         oldsigint = sm_signal(SIGINT, SIG_IGN);
583         oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
584 #ifdef SIGTSTP
585         oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
586 #endif
587
588         if (map->map_class->map_open(map, O_RDWR))
589         {
590                 if (LogLevel > 7)
591                 {
592                         sm_syslog(LOG_NOTICE, NOQID,
593                                 "alias database %s rebuilt by %s",
594                                 map->map_file, username());
595                 }
596                 map->map_mflags |= MF_OPEN|MF_WRITABLE;
597                 map->map_pid = CurrentPid;
598                 readaliases(map, af, true, true);
599                 success = true;
600         }
601         else
602         {
603                 if (tTd(27, 1))
604                         sm_dprintf("Can't create database for %s: %s\n",
605                                 map->map_file, sm_errstring(errno));
606                 syserr("Cannot create database for alias file %s",
607                         map->map_file);
608         }
609
610         /* close the file, thus releasing locks */
611         (void) sm_io_close(af, SM_TIME_DEFAULT);
612
613         /* add distinguished entries and close the database */
614         if (bitset(MF_OPEN, map->map_mflags))
615         {
616 #if _FFR_TESTS
617                 if (tTd(78, 101))
618                 {
619                         int sl;
620
621                         sl = tTdlevel(78) - 100;
622                         sm_dprintf("rebuildaliases: sleep=%d, file=%s\n",
623                                 sl, map->map_file);
624                         sleep(sl);
625                         sm_dprintf("rebuildaliases: done\n");
626                 }
627 #endif
628                 map->map_mflags |= MF_CLOSING;
629                 map->map_class->map_close(map);
630                 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
631         }
632
633         /* restore the old signals */
634         (void) sm_signal(SIGINT, oldsigint);
635         (void) sm_signal(SIGQUIT, oldsigquit);
636 #ifdef SIGTSTP
637         (void) sm_signal(SIGTSTP, oldsigtstp);
638 #endif
639         return success;
640 }
641
642 /*
643 **  CONTLINE -- handle potential continuation line
644 **
645 **      Parameters:
646 **              fp -- file to read
647 **              line -- current line
648 **
649 **      Returns:
650 **              pointer to end of current line if there is a continuation line
651 **              NULL otherwise
652 **
653 **      Side Effects:
654 **              Modifies line if it is a continuation line
655 */
656
657 static char *contline __P((SM_FILE_T *, char *));
658 static char *
659 contline(fp, line)
660         SM_FILE_T *fp;
661         char *line;
662 {
663         char *p;
664         int c;
665
666         if ((p = strchr(line, '\n')) != NULL && p > line && p[-1] == '\\')
667         {
668                 *p = '\0';
669                 *--p = '\0';
670                 return p;
671         }
672
673         c = sm_io_getc(fp, SM_TIME_DEFAULT);
674         if (!sm_io_eof(fp))
675                 (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
676         if (c == ' ' || c == '\t')
677         {
678                 char *nlp;
679
680                 p = line;
681                 nlp = &p[strlen(p)];
682                 if (nlp > p && nlp[-1] == '\n')
683                         *--nlp = '\0';
684                 return nlp;
685         }
686         return NULL;
687 }
688
689 /*
690 **  READALIASES -- read and process the alias file.
691 **
692 **      This routine implements the part of initaliases that occurs
693 **      when we are not going to use the DBM stuff.
694 **
695 **      Parameters:
696 **              map -- the alias database descriptor.
697 **              af -- file to read the aliases from.
698 **              announcestats -- announce statistics regarding number of
699 **                      aliases, longest alias, etc.
700 **              logstats -- lot the same info.
701 **
702 **      Returns:
703 **              none.
704 **
705 **      Side Effects:
706 **              Reads aliasfile into the symbol table.
707 **              Optionally, builds the .dir & .pag files.
708 */
709
710 void
711 readaliases(map, af, announcestats, logstats)
712         register MAP *map;
713         SM_FILE_T *af;
714         bool announcestats;
715         bool logstats;
716 {
717         register char *p;
718         char *rhs;
719         bool skipping;
720         long naliases, bytes, longest;
721         ADDRESS al, bl;
722         char lbuf[BUFSIZ];
723         char *line;
724 #if _FFR_8BITENVADDR
725         char lhsbuf[MAXNAME];   /* EAI:ok */
726         char rhsbuf[BUFSIZ];
727         int len;
728 #endif
729
730         /*
731         **  Read and interpret lines
732         */
733
734         FileName = map->map_file;
735         LineNumber = 0;
736         naliases = bytes = longest = 0;
737         skipping = false;
738         line = NULL;
739
740         while (sm_io_fgets(af, SM_TIME_DEFAULT, lbuf, sizeof(lbuf)) >= 0)
741         {
742                 int lhssize, rhssize;
743                 int c;
744                 char *newp;
745
746                 LineNumber++;
747
748                 /* XXX what if line="a\\" ? */
749                 line = lbuf;
750                 p = line;
751                 while ((newp = contline(af, line)) != NULL)
752                 {
753                         p = newp;
754                         if ((c = sm_io_fgets(af, SM_TIME_DEFAULT, p,
755                                         SPACELEFT(lbuf, p))) < 0)
756                         {
757                                 break;
758                         }
759                         LineNumber++;
760                 }
761 #if _FFR_8BITENVADDR
762                 if (SMTP_UTF8 || EightBitAddrOK)
763                 {
764                         if (line != lbuf)
765                                 SM_FREE(line);
766                         line = quote_internal_chars(lbuf, NULL, &len, NULL);
767                 }
768                 else
769 #endif
770                 /* "else" in #if code above */
771                 line = lbuf;
772
773                 p = strchr(line, '\n');
774                 if (p != NULL)
775                         *p = '\0';
776                 else if (!sm_io_eof(af))
777                 {
778                         int prev;
779                         bool cl;
780
781                         errno = 0;
782                         syserr("554 5.3.0 alias line too long");
783
784                         prev = '\0';
785                         cl = false;
786
787                         do {
788                                 /* flush to end of "virtual" line */
789                                 while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
790                                         SM_IO_EOF && c != '\n')
791                                 {
792                                         prev = c;
793                                 }
794                                 cl = ('\\' == prev && '\n' == c);
795                                 if (!cl)
796                                 {
797                                         c = sm_io_getc(af, SM_TIME_DEFAULT);
798                                         if (!sm_io_eof(af))
799                                                 (void) sm_io_ungetc(af, SM_TIME_DEFAULT, c);
800                                         cl = (c == ' ' || c == '\t');
801                                 }
802                         } while (cl);
803
804                         continue;
805                 }
806
807                 switch (line[0])
808                 {
809                   case '#':
810                   case '\0':
811                         skipping = false;
812                         continue;
813
814                   case ' ':
815                   case '\t':
816                         if (!skipping)
817                                 syserr("554 5.3.5 Non-continuation line starts with space");
818                         skipping = true;
819                         continue;
820                 }
821                 skipping = false;
822
823                 /*
824                 **  Process the LHS
825                 **      Find the colon separator, and parse the address.
826                 **      It should resolve to a local name -- this will
827                 **      be checked later (we want to optionally do
828                 **      parsing of the RHS first to maximize error
829                 **      detection).
830                 */
831
832                 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
833                         continue;
834                 if (*p++ != ':')
835                 {
836                         syserr("554 5.3.5 missing colon");
837                         continue;
838                 }
839 /* XXX line must be [i] */
840                 if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
841                     == NULL)
842                 {
843                         syserr("554 5.3.5 %.40s... illegal alias name", line);
844                         continue;
845                 }
846
847                 /*
848                 **  Process the RHS.
849                 **      'al' is the internal form of the LHS address.
850                 **      'p' points to the text of the RHS.
851                 */
852
853                 while (SM_ISSPACE(*p))
854                         p++;
855                 rhs = p;
856                 {
857                         register char *nlp;
858
859                         nlp = &p[strlen(p)];
860                         if (nlp > p && nlp[-1] == '\n')
861                                 *--nlp = '\0';
862
863                         if (CheckAliases)
864                         {
865                                 /* do parsing & compression of addresses */
866                                 while (*p != '\0')
867                                 {
868                                         auto char *delimptr;
869
870                                         while ((SM_ISSPACE(*p)) || *p == ',')
871                                                 p++;
872                                         if (*p == '\0')
873                                                 break;
874 /* XXX p must be [i] */
875                                         if (parseaddr(p, &bl, RF_COPYNONE, ',',
876                                                       &delimptr, CurEnv, true)
877                                             == NULL)
878                                                 usrerr("553 5.3.5 %s... bad address", p);
879                                         p = delimptr;
880                                 }
881                         }
882                         else
883                         {
884                                 p = nlp;
885                         }
886                 } while (0);
887
888                 if (skipping)
889                         continue;
890
891                 if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
892                 {
893                         syserr("554 5.3.5 %s... cannot alias non-local names",
894                                 al.q_paddr);
895                         continue;
896                 }
897
898                 /*
899                 **  Insert alias into symbol table or database file.
900                 **
901                 **      Special case pOStmaStER -- always make it lower case.
902                 */
903
904                 if (SM_STRCASEEQ(al.q_user, "postmaster"))
905                         makelower_a(&al.q_user, CurEnv->e_rpool);
906
907                 lhssize = strlen(al.q_user);
908                 rhssize = strlen(rhs);
909                 if (rhssize > 0)
910                 {
911                         /* is RHS empty (just spaces)? */
912                         p = rhs;
913                         while (SM_ISSPACE(*p))
914                                 p++;
915                 }
916                 if (rhssize == 0 || *p == '\0')
917                 {
918                         syserr("554 5.3.5 %.40s... missing value for alias",
919                                line);
920                 }
921                 else
922                 {
923 #if _FFR_8BITENVADDR
924                         if (SMTP_UTF8 || EightBitAddrOK)
925                         {
926                                 dequote_internal_chars(al.q_user, lhsbuf, sizeof(lhsbuf));
927                                 dequote_internal_chars(rhs, rhsbuf, sizeof(rhsbuf));
928                                 map->map_class->map_store(map, lhsbuf, rhsbuf);
929                         }
930                         else
931 #endif
932                         /* "else" in #if code above */
933                         map->map_class->map_store(map, al.q_user, rhs);
934
935                         /* statistics */
936                         naliases++;
937                         bytes += lhssize + rhssize;
938                         if (rhssize > longest)
939                                 longest = rhssize;
940                 }
941         }
942
943         CurEnv->e_to = NULL;
944         FileName = NULL;
945         if (Verbose || announcestats)
946                 message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
947                         map->map_file, naliases, longest, bytes);
948         if (LogLevel > 7 && logstats)
949                 sm_syslog(LOG_INFO, NOQID,
950                         "%s: %ld aliases, longest %ld bytes, %ld bytes total",
951                         map->map_file, naliases, longest, bytes);
952 }
953 /*
954 **  FORWARD -- Try to forward mail
955 **
956 **      This is similar but not identical to aliasing.
957 **
958 **      Parameters:
959 **              user -- the name of the user who's mail we would like
960 **                      to forward to.  It must have been verified --
961 **                      i.e., the q_home field must have been filled in.
962 **              sendq -- a pointer to the head of the send queue to
963 **                      put this user's aliases in.
964 **              aliaslevel -- the current alias nesting depth.
965 **              e -- the current envelope.
966 **
967 **      Returns:
968 **              none.
969 **
970 **      Side Effects:
971 **              New names are added to send queues.
972 */
973
974 void
975 forward(user, sendq, aliaslevel, e)
976         ADDRESS *user;
977         ADDRESS **sendq;
978         int aliaslevel;
979         register ENVELOPE *e;
980 {
981         char *pp;
982         char *ep;
983         bool got_transient;
984
985         if (tTd(27, 1))
986                 sm_dprintf("forward(%s)\n", user->q_paddr);
987
988         if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
989             !QS_IS_OK(user->q_state))
990                 return;
991         if (ForwardPath != NULL && *ForwardPath == '\0')
992                 return;
993         if (user->q_home == NULL)
994         {
995                 syserr("554 5.3.0 forward: no home");
996                 user->q_home = "/no/such/directory";
997         }
998
999         /* good address -- look for .forward file in home */
1000         macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
1001         macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
1002         pp = user->q_host;
1003 #if _FFR_8BITENVADDR
1004         if (NULL != pp)
1005         {
1006                 int len;
1007
1008                 pp = quote_internal_chars(pp, NULL, &len, NULL);
1009         }
1010 #endif
1011         macdefine(&e->e_macro, A_PERM, 'h', pp);
1012         if (ForwardPath == NULL)
1013                 ForwardPath = newstr("\201z/.forward");
1014
1015         got_transient = false;
1016         for (pp = ForwardPath; pp != NULL; pp = ep)
1017         {
1018                 int err;
1019                 char buf[MAXPATHLEN];
1020                 struct stat st;
1021
1022                 ep = strchr(pp, SEPARATOR);
1023                 if (ep != NULL)
1024                         *ep = '\0';
1025                 expand(pp, buf, sizeof(buf), e);
1026                 if (ep != NULL)
1027                         *ep++ = SEPARATOR;
1028                 if (buf[0] == '\0')
1029                         continue;
1030                 if (tTd(27, 3))
1031                         sm_dprintf("forward: trying %s\n", buf);
1032
1033                 err = include(buf, true, user, sendq, aliaslevel, e);
1034                 if (err == 0)
1035                         break;
1036                 else if (transienterror(err))
1037                 {
1038                         /* we may have to suspend this message */
1039                         got_transient = true;
1040                         if (tTd(27, 2))
1041                                 sm_dprintf("forward: transient error on %s\n",
1042                                            buf);
1043                         if (LogLevel > 2)
1044                         {
1045                                 char *curhost = CurHostName;
1046
1047                                 CurHostName = NULL;
1048                                 sm_syslog(LOG_ERR, e->e_id,
1049                                           "forward %s: transient error: %s",
1050                                           buf, sm_errstring(err));
1051                                 CurHostName = curhost;
1052                         }
1053
1054                 }
1055                 else
1056                 {
1057                         switch (err)
1058                         {
1059                           case ENOENT:
1060                                 break;
1061
1062                           case E_SM_WWDIR:
1063                           case E_SM_GWDIR:
1064                                 /* check if it even exists */
1065                                 if (stat(buf, &st) < 0 && errno == ENOENT)
1066                                 {
1067                                         if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH,
1068                                                     DontBlameSendmail))
1069                                                 break;
1070                                 }
1071                                 /* FALLTHROUGH */
1072
1073 #if _FFR_FORWARD_SYSERR
1074                           case E_SM_NOSLINK:
1075                           case E_SM_NOHLINK:
1076                           case E_SM_REGONLY:
1077                           case E_SM_ISEXEC:
1078                           case E_SM_WWFILE:
1079                           case E_SM_GWFILE:
1080                                 syserr("forward: %s: %s", buf, sm_errstring(err));
1081                                 break;
1082 #endif /* _FFR_FORWARD_SYSERR */
1083
1084                           default:
1085                                 if (LogLevel > (RunAsUid == 0 ? 2 : 10))
1086                                         sm_syslog(LOG_WARNING, e->e_id,
1087                                                   "forward %s: %s", buf,
1088                                                   sm_errstring(err));
1089                                 if (Verbose)
1090                                         message("forward: %s: %s",
1091                                                 buf, sm_errstring(err));
1092                                 break;
1093                         }
1094                 }
1095         }
1096         if (pp == NULL && got_transient)
1097         {
1098                 /*
1099                 **  There was no successful .forward open and at least one
1100                 **  transient open.  We have to defer this address for
1101                 **  further delivery.
1102                 */
1103
1104                 message("transient .forward open error: message queued");
1105                 user->q_state = QS_QUEUEUP;
1106                 return;
1107         }
1108 }