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