first cut at putting mailing list maintainers in -- there is a
[unix-history] / usr / src / usr.sbin / sendmail / src / alias.c
CommitLineData
b3cbe40f 1# include <pwd.h>
9c3f729b
EA
2# include <sys/types.h>
3# include <sys/stat.h>
96faada8 4# include "sendmail.h"
b3cbe40f 5
9e25d1f0 6# ifdef DBM
1bcdf0a2 7SCCSID(@(#)alias.c 3.33.1.1 %G% (with DBM));
9e25d1f0 8# else DBM
1bcdf0a2 9SCCSID(@(#)alias.c 3.33.1.1 %G% (without DBM));
9e25d1f0 10# endif DBM
916b3375 11
b3cbe40f
EA
12/*
13** ALIAS -- Compute aliases.
14**
cdb17311 15** Scans the file /usr/lib/aliases for a set of aliases.
9e3c0a28
EA
16** If found, it arranges to deliver to them. Uses libdbm
17** database if -DDBM.
b3cbe40f
EA
18**
19** Parameters:
bdfaa762 20** a -- address to alias.
d4f42161
EA
21** sendq -- a pointer to the head of the send queue
22** to put the aliases in.
b3cbe40f
EA
23**
24** Returns:
25** none
26**
27** Side Effects:
9e3c0a28 28** Aliases found are expanded.
b3cbe40f 29**
b3cbe40f 30** Files:
cdb17311 31** /usr/lib/aliases -- the mail aliases. The format is
f895a45d
EA
32** a series of lines of the form:
33** alias:name1,name2,name3,...
34** where 'alias' expands to all of
35** 'name[i]'. Continuations begin with
36** space or tab.
cdb17311 37** /usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version
c9b9c7a2
MH
38** of alias file. Keys are aliases, datums
39** (data?) are name1,name2, ...
b3cbe40f
EA
40**
41** Notes:
42** If NoAlias (the "-n" flag) is set, no aliasing is
43** done.
44**
45** Deficiencies:
46** It should complain about names that are aliased to
47** nothing.
48** It is unsophisticated about line overflows.
b3cbe40f
EA
49*/
50
51
c9b9c7a2 52#ifdef DBM
a530c75f
EA
53typedef struct
54{
55 char *dptr;
f4dbf345
EA
56 int dsize;
57} DATUM;
f4dbf345 58extern DATUM fetch();
c9b9c7a2 59#endif DBM
b3cbe40f 60
d4f42161 61alias(a, sendq)
bdfaa762 62 register ADDRESS *a;
d4f42161 63 ADDRESS **sendq;
b3cbe40f 64{
29871fef 65 register char *p;
abae7b2d 66 extern ADDRESS *sendto();
35cc3fad 67 extern char *aliaslookup();
b3cbe40f
EA
68
69 if (NoAlias)
70 return;
71# ifdef DEBUG
72 if (Debug)
cdb17311 73 printf("alias(%s)\n", a->q_paddr);
b3cbe40f
EA
74# endif
75
cdb17311
EA
76 /* don't realias already aliased names */
77 if (bitset(QDONTSEND, a->q_flags))
78 return;
79
2654b031 80 CurEnv->e_to = a->q_paddr;
cdb17311 81
74c5fe7c
EA
82 /*
83 ** Look up this name
84 */
85
35cc3fad 86 p = aliaslookup(a->q_user);
cdb17311
EA
87 if (p == NULL)
88 return;
b3cbe40f 89
d916f0ca 90 /*
cdb17311
EA
91 ** Match on Alias.
92 ** Deliver to the target list.
d916f0ca
EA
93 */
94
cdb17311
EA
95# ifdef DEBUG
96 if (Debug)
97 printf("%s (%s, %s) aliased to %s\n",
98 a->q_paddr, a->q_host, a->q_user, p);
99# endif
100 if (Verbose)
15b28570 101 message(Arpa_Info, "aliased to %s", p);
cdb17311 102 AliasLevel++;
abae7b2d 103 a->q_child = sendto(p, 1, a, 0);
cdb17311
EA
104 AliasLevel--;
105}
106\f/*
35cc3fad
EA
107** ALIASLOOKUP -- look up a name in the alias file.
108**
109** Parameters:
110** name -- the name to look up.
111**
112** Returns:
113** the value of name.
114** NULL if unknown.
115**
116** Side Effects:
117** none.
118**
119** Warnings:
120** The return value will be trashed across calls.
121*/
122
123char *
124aliaslookup(name)
125 char *name;
126{
127# ifdef DBM
128 DATUM rhs, lhs;
129
130 /* create a key for fetch */
131 lhs.dptr = name;
132 lhs.dsize = strlen(name) + 1;
133 rhs = fetch(lhs);
134 return (rhs.dptr);
135# else DBM
136 register STAB *s;
137
138 s = stab(name, ST_ALIAS, ST_FIND);
139 if (s == NULL)
140 return (NULL);
141 return (s->s_alias);
142# endif DBM
143}
144\f/*
cdb17311
EA
145** INITALIASES -- initialize for aliasing
146**
147** Very different depending on whether we are running DBM or not.
148**
149** Parameters:
150** aliasfile -- location of aliases.
f4dbf345 151** init -- if set and if DBM, initialize the DBM files.
cdb17311
EA
152**
153** Returns:
154** none.
155**
156** Side Effects:
157** initializes aliases:
158** if DBM: opens the database.
159** if ~DBM: reads the aliases into the symbol table.
160*/
161
f4dbf345
EA
162# define DBMMODE 0666
163
164initaliases(aliasfile, init)
cdb17311 165 char *aliasfile;
f4dbf345 166 bool init;
cdb17311 167{
d6b27179
EA
168 char buf[MAXNAME];
169 struct stat stb;
170 time_t modtime;
171
172 /*
173 ** See if the DBM version of the file is out of date with
174 ** the text version. If so, go into 'init' mode automatically.
175 ** This only happens if our effective userid owns the DBM
f9ce44de 176 ** version or if the mode of the database is 666 -- this
d6b27179
EA
177 ** is an attempt to avoid protection problems. Note the
178 ** unpalatable hack to see if the stat succeeded.
179 */
180
181 if (stat(aliasfile, &stb) < 0)
182 return;
cdb17311 183# ifdef DBM
d6b27179
EA
184 modtime = stb.st_mtime;
185 (void) strcpy(buf, aliasfile);
186 (void) strcat(buf, ".pag");
187 stb.st_ino = 0;
188 if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init)
f4dbf345 189 {
d6b27179 190 if (stb.st_ino != 0 &&
f9ce44de 191 ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid()))
d6b27179
EA
192 {
193 init = TRUE;
194 if (Verbose)
195 message(Arpa_Info, "rebuilding alias database");
196 }
197 else
198 {
199 message(Arpa_Info, "Warning: alias database out of date");
200 }
201 }
202# endif DBM
f4dbf345 203
d6b27179
EA
204 /*
205 ** If initializing, create the new files.
206 ** We should lock the alias file here to prevent other
207 ** instantiations of sendmail from reading an incomplete
208 ** file -- or worse yet, doing a concurrent initialize.
209 */
210
211# ifdef DBM
212 if (init)
213 {
f4dbf345
EA
214 (void) strcpy(buf, aliasfile);
215 (void) strcat(buf, ".dir");
216 if (close(creat(buf, DBMMODE)) < 0)
217 {
218 syserr("cannot make %s", buf);
219 return;
220 }
221 (void) strcpy(buf, aliasfile);
222 (void) strcat(buf, ".pag");
223 if (close(creat(buf, DBMMODE)) < 0)
224 {
225 syserr("cannot make %s", buf);
226 return;
227 }
228 }
d6b27179
EA
229
230 /*
231 ** Open and, if necessary, load the DBM file.
232 ** If running without DBM, load the symbol table.
233 */
234
cdb17311 235 dbminit(aliasfile);
f4dbf345
EA
236 if (init)
237 readaliases(aliasfile, TRUE);
cdb17311 238# else DBM
f4dbf345
EA
239 readaliases(aliasfile, init);
240# endif DBM
241}
242\f/*
243** READALIASES -- read and process the alias file.
244**
245** This routine implements the part of initaliases that occurs
246** when we are not going to use the DBM stuff.
247**
248** Parameters:
249** aliasfile -- the pathname of the alias file master.
250** init -- if set, initialize the DBM stuff.
251**
252** Returns:
253** none.
254**
255** Side Effects:
256** Reads aliasfile into the symbol table.
257** Optionally, builds the .dir & .pag files.
258*/
259
260static
261readaliases(aliasfile, init)
262 char *aliasfile;
263 bool init;
264{
cdb17311
EA
265 char line[BUFSIZ];
266 register char *p;
267 char *p2;
268 char *rhs;
269 bool skipping;
270 ADDRESS al, bl;
9e25d1f0
EA
271 FILE *af;
272 int lineno;
273 register STAB *s;
d6b27179 274 int naliases, bytes, longest;
cdb17311
EA
275
276 if ((af = fopen(aliasfile, "r")) == NULL)
d916f0ca 277 {
cdb17311
EA
278# ifdef DEBUG
279 if (Debug)
9e25d1f0 280 printf("Can't open %s\n", aliasfile);
cdb17311
EA
281# endif
282 errno = 0;
283 NoAlias++;
284 return;
285 }
74c5fe7c
EA
286
287 /*
288 ** Read and interpret lines
289 */
290
cdb17311 291 lineno = 0;
d6b27179 292 naliases = bytes = longest = 0;
cdb17311
EA
293 skipping = FALSE;
294 while (fgets(line, sizeof (line), af) != NULL)
295 {
d6b27179
EA
296 int lhssize, rhssize;
297
cdb17311
EA
298 lineno++;
299 switch (line[0])
300 {
301 case '#':
302 case '\n':
303 case '\0':
304 skipping = FALSE;
305 continue;
bdfaa762 306
cdb17311
EA
307 case ' ':
308 case '\t':
309 if (!skipping)
310 syserr("aliases: %d: Non-continuation line starts with space", lineno);
311 skipping = TRUE;
bdfaa762 312 continue;
cdb17311
EA
313 }
314 skipping = FALSE;
bdfaa762 315
74c5fe7c
EA
316 /*
317 ** Process the LHS
318 ** Find the final colon, and parse the address.
319 ** It should resolve to a local name -- this will
320 ** be checked later (we want to optionally do
321 ** parsing of the RHS first to maximize error
322 ** detection).
323 */
324
cdb17311 325 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
bdfaa762 326 continue;
cdb17311
EA
327 if (*p == '\0' || *p == '\n')
328 {
329 syntaxerr:
1bcdf0a2 330 syserr("%s, line %d: syntax error", aliasfile, lineno);
bdfaa762 331 continue;
cdb17311
EA
332 }
333 *p++ = '\0';
334 if (parse(line, &al, 1) == NULL)
335 {
336 *--p = ':';
337 goto syntaxerr;
338 }
74c5fe7c
EA
339
340 /*
341 ** Process the RHS.
342 ** 'al' is the internal form of the LHS address.
343 ** 'p' points to the text of the RHS.
1bcdf0a2
EA
344 ** 'p' may begin with a colon (i.e., the
345 ** separator was "::") which will use the
346 ** first address as the person to send
347 ** errors to -- i.e., designates the
348 ** list maintainer.
74c5fe7c
EA
349 */
350
1bcdf0a2
EA
351 if (*p == ':')
352 {
353 ADDRESS *maint;
354
355 while (isspace(*++p))
356 continue;
357 rhs = p;
358 while (*p != '\0' && *p != ',')
359 p++;
360 if (*p != ',')
361 goto syntaxerr;
362 *p++ = '\0';
363 maint = parse(p, (ADDRESS *) NULL, 1);
364 if (maint == NULL)
365 syserr("Illegal list maintainer for list %s", al.q_user);
366 else if (CurEnv->e_returnto == &CurEnv->e_from)
367 {
368 CurEnv->e_returnto = maint;
369 MailBack++;
370 }
371 }
372
cdb17311 373 rhs = p;
cdb17311
EA
374 for (;;)
375 {
376 register char c;
d916f0ca 377
f4dbf345 378 if (init)
cdb17311 379 {
f4dbf345 380 /* do parsing & compression of addresses */
cdb17311 381 c = *p;
f4dbf345 382 while (c != '\0')
cdb17311 383 {
f4dbf345
EA
384 p2 = p;
385 while (*p != '\n' && *p != ',' && *p != '\0')
386 p++;
387 c = *p;
388 *p++ = '\0';
d6b27179
EA
389 if (c == '\n')
390 c = '\0';
f4dbf345
EA
391 if (*p2 == '\0')
392 {
393 p[-1] = c;
394 continue;
395 }
74c5fe7c 396 (void) parse(p2, &bl, -1);
cdb17311 397 p[-1] = c;
f4dbf345
EA
398 while (isspace(*p))
399 p++;
cdb17311 400 }
cdb17311 401 }
f4dbf345
EA
402 else
403 p = &p[strlen(p)];
d916f0ca 404
cdb17311 405 /* see if there should be a continuation line */
9e25d1f0
EA
406 c = fgetc(af);
407 if (!feof(af))
74c5fe7c 408 (void) ungetc(c, af);
9e25d1f0 409 if (c != ' ' && c != '\t')
cdb17311
EA
410 break;
411
412 /* read continuation line */
413 p--;
414 if (fgets(p, sizeof line - (p - line), af) == NULL)
415 break;
416 lineno++;
cdb17311 417 }
179c1218 418 if (al.q_mailer != LocalMailer)
cdb17311
EA
419 {
420 syserr("aliases: %d: cannot alias non-local names", lineno);
421 continue;
422 }
74c5fe7c
EA
423
424 /*
425 ** Insert alias into symbol table or DBM file
426 */
427
d6b27179
EA
428 lhssize = strlen(al.q_user) + 1;
429 rhssize = strlen(rhs) + 1;
430
f4dbf345
EA
431# ifdef DBM
432 if (init)
433 {
434 DATUM key, content;
435
d6b27179 436 key.dsize = lhssize;
f4dbf345 437 key.dptr = al.q_user;
d6b27179 438 content.dsize = rhssize;
f4dbf345
EA
439 content.dptr = rhs;
440 store(key, content);
441 }
442 else
443# endif DBM
444 {
445 s = stab(al.q_user, ST_ALIAS, ST_ENTER);
446 s->s_alias = newstr(rhs);
447 }
d6b27179
EA
448
449 /* statistics */
450 naliases++;
451 bytes += lhssize + rhssize;
452 if (rhssize > longest)
453 longest = rhssize;
d916f0ca 454 }
cdb17311 455 (void) fclose(af);
2654b031 456 CurEnv->e_to = NULL;
d6b27179
EA
457 if (Verbose)
458 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
459 naliases, longest, bytes);
b3cbe40f
EA
460}
461\f/*
462** FORWARD -- Try to forward mail
463**
464** This is similar but not identical to aliasing.
465**
b3cbe40f 466** Parameters:
74c5fe7c
EA
467** user -- the name of the user who's mail we would like
468** to forward to. It must have been verified --
469** i.e., the q_home field must have been filled
470** in.
d4f42161
EA
471** sendq -- a pointer to the head of the send queue to
472** put this user's aliases in.
b3cbe40f
EA
473**
474** Returns:
cdb17311 475** none.
b3cbe40f
EA
476**
477** Side Effects:
9e3c0a28 478** New names are added to send queues.
b3cbe40f
EA
479*/
480
d4f42161 481forward(user, sendq)
a530c75f 482 ADDRESS *user;
d4f42161 483 ADDRESS **sendq;
b3cbe40f 484{
4bb4503e 485 char buf[60];
f6a0cc15 486 extern bool safefile();
56678245 487
cdb17311
EA
488# ifdef DEBUG
489 if (Debug)
490 printf("forward(%s)\n", user->q_paddr);
491# endif DEBUG
492
179c1218 493 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
cdb17311 494 return;
74c5fe7c
EA
495# ifdef DEBUG
496 if (user->q_home == NULL)
497 syserr("forward: no home");
498# endif DEBUG
56678245 499
56678245 500 /* good address -- look for .forward file in home */
8163ce8d 501 define('z', user->q_home);
dd1fe05b 502 expand("$z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
f6a0cc15 503 if (!safefile(buf, user->q_uid, S_IREAD))
cdb17311 504 return;
56678245
EA
505
506 /* we do have an address to forward to -- do it */
d4f42161 507 include(buf, "forwarding", user, sendq);
b3cbe40f 508}
35cc3fad
EA
509\f/*
510** HOSTALIAS -- alias a host name
511**
512** Given a host name, look it up in the host alias table
513** and return it's value. If nothing, return NULL.
514**
515** Parameters:
516** a -- address to alias.
517**
518** Returns:
519** text result of aliasing.
520** NULL if none.
521**
522** Side Effects:
523** none.
524*/
525
526char *
527hostalias(a)
528 register ADDRESS *a;
529{
530 char buf[MAXNAME+2];
531 register char *p;
532
f9566d23
EA
533 (void) strcpy(buf, "/");
534 (void) strcat(buf, a->q_host);
35cc3fad
EA
535 makelower(buf);
536
537 p = aliaslookup(buf);
538 if (p == NULL)
539 return (NULL);
40e27d12 540 (void) sprintf(buf, p, a->q_user);
35cc3fad
EA
541 return (newstr(buf));
542}