Deleted Added
full compact
mail.local.c (38089) mail.local.c (42580)
1/*-
1/*
2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
3 * Copyright (c) 1990, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 * By using this file, you agree to the terms and conditions set
7 * forth in the LICENSE file which can be found at the top level of
8 * the sendmail distribution.
9 *
10 */
11
12#ifndef lint
13static char copyright[] =
14"@(#) Copyright (c) 1990, 1993, 1994\n\
15 The Regents of the University of California. All rights reserved.\n";
16#endif /* not lint */
17
18#ifndef lint
2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
3 * Copyright (c) 1990, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 * By using this file, you agree to the terms and conditions set
7 * forth in the LICENSE file which can be found at the top level of
8 * the sendmail distribution.
9 *
10 */
11
12#ifndef lint
13static char copyright[] =
14"@(#) Copyright (c) 1990, 1993, 1994\n\
15 The Regents of the University of California. All rights reserved.\n";
16#endif /* not lint */
17
18#ifndef lint
19static char sccsid[] = "@(#)mail.local.c 8.78 (Berkeley) 5/19/98";
19static char sccsid[] = "@(#)mail.local.c 8.83 (Berkeley) 12/17/1998";
20#endif /* not lint */
21
22/*
23 * This is not intended to work on System V derived systems
24 * such as Solaris or HP-UX, since they use a totally different
25 * approach to mailboxes (essentially, they have a setgid program
26 * rather than setuid, and they rely on the ability to "give away"
27 * files to do their work). IT IS NOT A BUG that this doesn't
28 * work on such architectures.
29 */
30
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <sys/socket.h>
34#include <sys/file.h>
35
36#include <netinet/in.h>
37
38#include <errno.h>
39#include <fcntl.h>
40#include <netdb.h>
41#include <pwd.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <syslog.h>
46#include <time.h>
47#include <unistd.h>
48#ifdef EX_OK
49# undef EX_OK /* unistd.h may have another use for this */
50#endif
51#include <sysexits.h>
52#include <ctype.h>
53
54#ifdef __STDC__
55#include <stdarg.h>
56#else
57#include <varargs.h>
58#endif
59
60#if (defined(sun) && defined(__svr4__)) || defined(__SVR4)
61# define USE_LOCKF 1
62# define USE_SETEUID 1
63# define _PATH_MAILDIR "/var/mail"
64#endif
65
66#if (defined(sun) && !defined(__svr4__)) && !defined(__SVR4)
67# ifdef __dead
68# undef __dead
69# define __dead
70# endif
71#endif
72
73#if defined(_AIX)
74# define USE_LOCKF 1
75# define USE_SETEUID 1
76# define USE_VSYSLOG 0
77#endif
78
79#if defined(__hpux)
80# define USE_LOCKF 1
81# define USE_SETRESUID 1
82# define USE_VSYSLOG 0
83# ifdef __dead
84# undef __dead
85# define __dead
86# endif
87#endif
88
89#if defined(_CRAY)
90# if !defined(MAXPATHLEN)
91# define MAXPATHLEN PATHSIZE
92# endif
93# define USE_VSYSLOG 0
94# define _PATH_MAILDIR "/usr/spool/mail"
95#endif
96
97#if defined(ultrix)
98# define USE_VSYSLOG 0
99#endif
100
101#if defined(__osf__)
102# define USE_VSYSLOG 0
103#endif
104
20#endif /* not lint */
21
22/*
23 * This is not intended to work on System V derived systems
24 * such as Solaris or HP-UX, since they use a totally different
25 * approach to mailboxes (essentially, they have a setgid program
26 * rather than setuid, and they rely on the ability to "give away"
27 * files to do their work). IT IS NOT A BUG that this doesn't
28 * work on such architectures.
29 */
30
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <sys/socket.h>
34#include <sys/file.h>
35
36#include <netinet/in.h>
37
38#include <errno.h>
39#include <fcntl.h>
40#include <netdb.h>
41#include <pwd.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <syslog.h>
46#include <time.h>
47#include <unistd.h>
48#ifdef EX_OK
49# undef EX_OK /* unistd.h may have another use for this */
50#endif
51#include <sysexits.h>
52#include <ctype.h>
53
54#ifdef __STDC__
55#include <stdarg.h>
56#else
57#include <varargs.h>
58#endif
59
60#if (defined(sun) && defined(__svr4__)) || defined(__SVR4)
61# define USE_LOCKF 1
62# define USE_SETEUID 1
63# define _PATH_MAILDIR "/var/mail"
64#endif
65
66#if (defined(sun) && !defined(__svr4__)) && !defined(__SVR4)
67# ifdef __dead
68# undef __dead
69# define __dead
70# endif
71#endif
72
73#if defined(_AIX)
74# define USE_LOCKF 1
75# define USE_SETEUID 1
76# define USE_VSYSLOG 0
77#endif
78
79#if defined(__hpux)
80# define USE_LOCKF 1
81# define USE_SETRESUID 1
82# define USE_VSYSLOG 0
83# ifdef __dead
84# undef __dead
85# define __dead
86# endif
87#endif
88
89#if defined(_CRAY)
90# if !defined(MAXPATHLEN)
91# define MAXPATHLEN PATHSIZE
92# endif
93# define USE_VSYSLOG 0
94# define _PATH_MAILDIR "/usr/spool/mail"
95#endif
96
97#if defined(ultrix)
98# define USE_VSYSLOG 0
99#endif
100
101#if defined(__osf__)
102# define USE_VSYSLOG 0
103#endif
104
105#if defined(NeXT)
105#if defined(NeXT) && !defined(__APPLE__)
106# include <libc.h>
107# define _PATH_MAILDIR "/usr/spool/mail"
108# define __dead /* empty */
109# define S_IRUSR S_IREAD
110# define S_IWUSR S_IWRITE
111#endif
112
113#if defined(IRIX64) || defined(IRIX5) || defined(IRIX6)
114# include <paths.h>
115# define HASSTRERROR 1 /* has strerror(3) */
116#endif
117
118/*
119 * If you don't have flock, you could try using lockf instead.
120 */
121
122#ifdef USE_LOCKF
123# define flock(a, b) lockf(a, b, 0)
124# define LOCK_EX F_LOCK
125#endif
126
127#ifndef USE_VSYSLOG
128# define USE_VSYSLOG 1
129#endif
130
131#ifndef LOCK_EX
132# include <sys/file.h>
133#endif
134
135#if defined(BSD4_4) || defined(__GLIBC__)
136# include "pathnames.h"
137#endif
138
139#ifndef __P
140# ifdef __STDC__
141# define __P(protos) protos
142# else
143# define __P(protos) ()
144# define const
145# endif
146#endif
147#ifndef __dead
148# if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
149# define __dead __volatile
150# else
151# define __dead
152# endif
153#endif
154
155#ifdef BSD4_4
156# define HAS_ST_GEN 1
157#else
158# ifndef _BSD_VA_LIST_
159# define _BSD_VA_LIST_ va_list
160# endif
161#endif
162
163#if defined(BSD4_4) || defined(linux)
164# define HASSNPRINTF 1
165#else
166# ifndef ultrix
167extern FILE *fdopen __P((int, const char *));
168# endif
169#endif
170
171#if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206)
172# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */
173#endif
174
175#if !HASSNPRINTF
176extern int snprintf __P((char *, size_t, const char *, ...));
177# ifndef _CRAY
178extern int vsnprintf __P((char *, size_t, const char *, ...));
179# endif
180#endif
181
182#if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__)
183# ifndef HASSTRERROR
184# define HASSTRERROR 1
185# endif
186#endif
187
188#if !HASSTRERROR
189extern char *strerror __P((int));
190#endif
191
192/*
193 * If you don't have setreuid, and you have saved uids, and you have
194 * a seteuid() call that doesn't try to emulate using setuid(), then
195 * you can try defining USE_SETEUID.
196 */
197#ifdef USE_SETEUID
198# define setreuid(r, e) seteuid(e)
199#endif
200
201/*
202 * And of course on hpux you have setresuid()
203 */
204#ifdef USE_SETRESUID
205# define setreuid(r, e) setresuid(-1, e, -1)
206#endif
207
208#ifndef _PATH_LOCTMP
209# define _PATH_LOCTMP "/tmp/local.XXXXXX"
210#endif
211#ifndef _PATH_MAILDIR
212# define _PATH_MAILDIR "/var/spool/mail"
213#endif
214
215#ifndef S_ISREG
216# define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG)
217#endif
218
106# include <libc.h>
107# define _PATH_MAILDIR "/usr/spool/mail"
108# define __dead /* empty */
109# define S_IRUSR S_IREAD
110# define S_IWUSR S_IWRITE
111#endif
112
113#if defined(IRIX64) || defined(IRIX5) || defined(IRIX6)
114# include <paths.h>
115# define HASSTRERROR 1 /* has strerror(3) */
116#endif
117
118/*
119 * If you don't have flock, you could try using lockf instead.
120 */
121
122#ifdef USE_LOCKF
123# define flock(a, b) lockf(a, b, 0)
124# define LOCK_EX F_LOCK
125#endif
126
127#ifndef USE_VSYSLOG
128# define USE_VSYSLOG 1
129#endif
130
131#ifndef LOCK_EX
132# include <sys/file.h>
133#endif
134
135#if defined(BSD4_4) || defined(__GLIBC__)
136# include "pathnames.h"
137#endif
138
139#ifndef __P
140# ifdef __STDC__
141# define __P(protos) protos
142# else
143# define __P(protos) ()
144# define const
145# endif
146#endif
147#ifndef __dead
148# if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
149# define __dead __volatile
150# else
151# define __dead
152# endif
153#endif
154
155#ifdef BSD4_4
156# define HAS_ST_GEN 1
157#else
158# ifndef _BSD_VA_LIST_
159# define _BSD_VA_LIST_ va_list
160# endif
161#endif
162
163#if defined(BSD4_4) || defined(linux)
164# define HASSNPRINTF 1
165#else
166# ifndef ultrix
167extern FILE *fdopen __P((int, const char *));
168# endif
169#endif
170
171#if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206)
172# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */
173#endif
174
175#if !HASSNPRINTF
176extern int snprintf __P((char *, size_t, const char *, ...));
177# ifndef _CRAY
178extern int vsnprintf __P((char *, size_t, const char *, ...));
179# endif
180#endif
181
182#if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__)
183# ifndef HASSTRERROR
184# define HASSTRERROR 1
185# endif
186#endif
187
188#if !HASSTRERROR
189extern char *strerror __P((int));
190#endif
191
192/*
193 * If you don't have setreuid, and you have saved uids, and you have
194 * a seteuid() call that doesn't try to emulate using setuid(), then
195 * you can try defining USE_SETEUID.
196 */
197#ifdef USE_SETEUID
198# define setreuid(r, e) seteuid(e)
199#endif
200
201/*
202 * And of course on hpux you have setresuid()
203 */
204#ifdef USE_SETRESUID
205# define setreuid(r, e) setresuid(-1, e, -1)
206#endif
207
208#ifndef _PATH_LOCTMP
209# define _PATH_LOCTMP "/tmp/local.XXXXXX"
210#endif
211#ifndef _PATH_MAILDIR
212# define _PATH_MAILDIR "/var/spool/mail"
213#endif
214
215#ifndef S_ISREG
216# define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG)
217#endif
218
219#ifndef MAILER_DAEMON
220# define MAILER_DAEMON "MAILER-DAEMON"
221#endif
222
219int eval = EX_OK; /* sysexits.h error value. */
220int lmtpmode = 0;
221u_char tTdvect[100];
222
223void deliver __P((int, char *, int, int));
224void e_to_sys __P((int));
223int eval = EX_OK; /* sysexits.h error value. */
224int lmtpmode = 0;
225u_char tTdvect[100];
226
227void deliver __P((int, char *, int, int));
228void e_to_sys __P((int));
225void err __P((const char *, ...)) __dead2;
226void notifybiff __P((char *));
227int store __P((char *, int));
228void usage __P((void));
229void vwarn __P((const char *, _BSD_VA_LIST_));
229void notifybiff __P((char *));
230int store __P((char *, int));
231void usage __P((void));
232void vwarn __P((const char *, _BSD_VA_LIST_));
230void warn __P((const char *, ...));
231void lockmbox __P((char *));
232void unlockmbox __P((void));
233void mailerr __P((const char *, const char *, ...));
234void dolmtp __P((int, int));
235
236int
237main(argc, argv)
238 int argc;
239 char *argv[];
240{
241 struct passwd *pw;
242 int ch, fd, nobiff, nofsync;
243 uid_t uid;
244 char *from;
245 extern char *optarg;
246 extern int optind;
247
248 /* make sure we have some open file descriptors */
249 for (fd = 10; fd < 30; fd++)
250 (void) close(fd);
251
252 /* use a reasonable umask */
253 (void) umask(0077);
254
255#ifdef LOG_MAIL
256 openlog("mail.local", 0, LOG_MAIL);
257#else
258 openlog("mail.local", 0);
259#endif
260
261 from = NULL;
262 nobiff = 0;
263 nofsync = 0;
264 while ((ch = getopt(argc, argv, "bdf:r:ls")) != -1)
265 switch(ch) {
266 case 'b':
267 nobiff++;
268 break;
269 case 'd': /* Backward compatible. */
270 break;
271 case 'f':
272 case 'r': /* Backward compatible. */
273 if (from != NULL) {
233void lockmbox __P((char *));
234void unlockmbox __P((void));
235void mailerr __P((const char *, const char *, ...));
236void dolmtp __P((int, int));
237
238int
239main(argc, argv)
240 int argc;
241 char *argv[];
242{
243 struct passwd *pw;
244 int ch, fd, nobiff, nofsync;
245 uid_t uid;
246 char *from;
247 extern char *optarg;
248 extern int optind;
249
250 /* make sure we have some open file descriptors */
251 for (fd = 10; fd < 30; fd++)
252 (void) close(fd);
253
254 /* use a reasonable umask */
255 (void) umask(0077);
256
257#ifdef LOG_MAIL
258 openlog("mail.local", 0, LOG_MAIL);
259#else
260 openlog("mail.local", 0);
261#endif
262
263 from = NULL;
264 nobiff = 0;
265 nofsync = 0;
266 while ((ch = getopt(argc, argv, "bdf:r:ls")) != -1)
267 switch(ch) {
268 case 'b':
269 nobiff++;
270 break;
271 case 'd': /* Backward compatible. */
272 break;
273 case 'f':
274 case 'r': /* Backward compatible. */
275 if (from != NULL) {
274 warn("multiple -f options");
276 mailerr(NULL, "multiple -f options");
275 usage();
276 }
277 from = optarg;
278 break;
279 case 'l':
280 lmtpmode++;
281 break;
282 case 's':
283 nofsync++;
284 break;
285 case '?':
286 default:
287 usage();
288 }
289 argc -= optind;
290 argv += optind;
291
292 if (lmtpmode)
293 dolmtp(nobiff, nofsync);
294
295 if (!*argv)
296 usage();
297
298 /*
299 * If from not specified, use the name from getlogin() if the
300 * uid matches, otherwise, use the name from the password file
301 * corresponding to the uid.
302 */
303 uid = getuid();
304 if (!from && (!(from = getlogin()) ||
305 !(pw = getpwnam(from)) || pw->pw_uid != uid))
306 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
307
308 /*
309 * There is no way to distinguish the error status of one delivery
310 * from the rest of the deliveries. So, if we failed hard on one
311 * or more deliveries, but had no failures on any of the others, we
312 * return a hard failure. If we failed temporarily on one or more
313 * deliveries, we return a temporary failure regardless of the other
314 * failures. This results in the delivery being reattempted later
315 * at the expense of repeated failures and multiple deliveries.
316 */
317 for (fd = store(from, 0); *argv; ++argv)
318 deliver(fd, *argv, nobiff, nofsync);
319 exit(eval);
320}
321
322char *
323parseaddr(s)
324 char *s;
325{
326 char *p;
327 int len;
328
329 if (*s++ != '<')
330 return NULL;
331
332 p = s;
333
334 /* at-domain-list */
335 while (*p == '@') {
336 p++;
337 if (*p == '[') {
338 p++;
339 while (isascii(*p) &&
340 (isalnum(*p) || *p == '.' ||
341 *p == '-' || *p == ':'))
342 p++;
343 if (*p++ != ']')
344 return NULL;
345 } else {
346 while ((isascii(*p) && isalnum(*p)) ||
277 usage();
278 }
279 from = optarg;
280 break;
281 case 'l':
282 lmtpmode++;
283 break;
284 case 's':
285 nofsync++;
286 break;
287 case '?':
288 default:
289 usage();
290 }
291 argc -= optind;
292 argv += optind;
293
294 if (lmtpmode)
295 dolmtp(nobiff, nofsync);
296
297 if (!*argv)
298 usage();
299
300 /*
301 * If from not specified, use the name from getlogin() if the
302 * uid matches, otherwise, use the name from the password file
303 * corresponding to the uid.
304 */
305 uid = getuid();
306 if (!from && (!(from = getlogin()) ||
307 !(pw = getpwnam(from)) || pw->pw_uid != uid))
308 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
309
310 /*
311 * There is no way to distinguish the error status of one delivery
312 * from the rest of the deliveries. So, if we failed hard on one
313 * or more deliveries, but had no failures on any of the others, we
314 * return a hard failure. If we failed temporarily on one or more
315 * deliveries, we return a temporary failure regardless of the other
316 * failures. This results in the delivery being reattempted later
317 * at the expense of repeated failures and multiple deliveries.
318 */
319 for (fd = store(from, 0); *argv; ++argv)
320 deliver(fd, *argv, nobiff, nofsync);
321 exit(eval);
322}
323
324char *
325parseaddr(s)
326 char *s;
327{
328 char *p;
329 int len;
330
331 if (*s++ != '<')
332 return NULL;
333
334 p = s;
335
336 /* at-domain-list */
337 while (*p == '@') {
338 p++;
339 if (*p == '[') {
340 p++;
341 while (isascii(*p) &&
342 (isalnum(*p) || *p == '.' ||
343 *p == '-' || *p == ':'))
344 p++;
345 if (*p++ != ']')
346 return NULL;
347 } else {
348 while ((isascii(*p) && isalnum(*p)) ||
347 *p == '.' || *p == '-')
349 strchr(".-_", *p))
348 p++;
349 }
350 if (*p == ',' && p[1] == '@')
351 p++;
352 else if (*p == ':' && p[1] != '@')
353 p++;
354 else
355 return NULL;
356 }
357
350 p++;
351 }
352 if (*p == ',' && p[1] == '@')
353 p++;
354 else if (*p == ':' && p[1] != '@')
355 p++;
356 else
357 return NULL;
358 }
359
360 s = p;
361
358 /* local-part */
359 if (*p == '\"') {
360 p++;
361 while (*p && *p != '\"') {
362 if (*p == '\\') {
363 if (!*++p)
364 return NULL;
365 }
366 p++;
367 }
368 if (!*p++)
369 return NULL;
370 } else {
371 while (*p && *p != '@' && *p != '>') {
372 if (*p == '\\') {
373 if (!*++p)
374 return NULL;
375 } else {
376 if (*p <= ' ' || (*p & 128) ||
377 strchr("<>()[]\\,;:\"", *p))
378 return NULL;
379 }
380 p++;
381 }
382 }
383
384 /* @domain */
385 if (*p == '@') {
386 p++;
387 if (*p == '[') {
388 p++;
389 while (isascii(*p) &&
390 (isalnum(*p) || *p == '.' ||
391 *p == '-' || *p == ':'))
392 p++;
393 if (*p++ != ']')
394 return NULL;
395 } else {
396 while ((isascii(*p) && isalnum(*p)) ||
362 /* local-part */
363 if (*p == '\"') {
364 p++;
365 while (*p && *p != '\"') {
366 if (*p == '\\') {
367 if (!*++p)
368 return NULL;
369 }
370 p++;
371 }
372 if (!*p++)
373 return NULL;
374 } else {
375 while (*p && *p != '@' && *p != '>') {
376 if (*p == '\\') {
377 if (!*++p)
378 return NULL;
379 } else {
380 if (*p <= ' ' || (*p & 128) ||
381 strchr("<>()[]\\,;:\"", *p))
382 return NULL;
383 }
384 p++;
385 }
386 }
387
388 /* @domain */
389 if (*p == '@') {
390 p++;
391 if (*p == '[') {
392 p++;
393 while (isascii(*p) &&
394 (isalnum(*p) || *p == '.' ||
395 *p == '-' || *p == ':'))
396 p++;
397 if (*p++ != ']')
398 return NULL;
399 } else {
400 while ((isascii(*p) && isalnum(*p)) ||
397 *p == '.' || *p == '-')
401 strchr(".-_", *p))
398 p++;
399 }
400 }
401
402 if (*p++ != '>')
403 return NULL;
404 if (*p && *p != ' ')
405 return NULL;
406 len = p - s - 1;
402 p++;
403 }
404 }
405
406 if (*p++ != '>')
407 return NULL;
408 if (*p && *p != ' ')
409 return NULL;
410 len = p - s - 1;
411 if (*s == '\0' || len <= 0)
412 {
413 s = MAILER_DAEMON;
414 len = strlen(s);
415 }
407
408 p = malloc(len + 1);
409 if (p == NULL) {
410 printf("421 4.3.0 memory exhausted\r\n");
411 exit(EX_TEMPFAIL);
412 }
413
414 strncpy(p, s, len);
415 p[len] = '\0';
416 return p;
417}
418
419char *
420process_recipient(addr)
421 char *addr;
422{
423 if (getpwnam(addr) == NULL) {
424 return "550 5.1.1 user unknown";
425 }
426
427 return NULL;
428}
429
430
431#define RCPT_GROW 30
432
433void
434dolmtp(nobiff, nofsync)
435 int nobiff, nofsync;
436{
437 char *return_path = NULL;
438 char **rcpt_addr = NULL;
439 int rcpt_num = 0;
440 int rcpt_alloc = 0;
441 char myhostname[1024];
442 char buf[4096];
443 char *err;
444 int msgfd;
445 char *p;
446 int i;
447
448 gethostname(myhostname, sizeof myhostname - 1);
449
450 printf("220 %s LMTP ready\r\n", myhostname);
451 for (;;) {
452 fflush(stdout);
453 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
454 exit(EX_OK);
455 }
456 p = buf + strlen(buf) - 1;
457 if (p >= buf && *p == '\n')
458 *p-- = '\0';
459 if (p >= buf && *p == '\r')
460 *p-- = '\0';
461
462 switch (buf[0]) {
463
464 case 'd':
465 case 'D':
466 if (strcasecmp(buf, "data") == 0) {
467 if (rcpt_num == 0) {
468 printf("503 5.5.1 No recipients\r\n");
469 continue;
470 }
471 msgfd = store(return_path, rcpt_num);
472 if (msgfd == -1)
473 continue;
474
475 for (i = 0; i < rcpt_num; i++) {
476 p = strchr(rcpt_addr[i], '+');
477 if (p != NULL)
478 *p++ = '\0';
479 deliver(msgfd, rcpt_addr[i], nobiff,
480 nofsync);
481 }
482 close(msgfd);
483 goto rset;
484 }
485 goto syntaxerr;
486
487 case 'l':
488 case 'L':
489 if (strncasecmp(buf, "lhlo ", 5) == 0) {
490 printf("250-%s\r\n250-8BITMIME\r\n250-ENHANCEDSTATUSCODES\r\n250 PIPELINING\r\n",
491 myhostname);
492 continue;
493 }
494 goto syntaxerr;
495
496 case 'm':
497 case 'M':
498 if (strncasecmp(buf, "mail ", 5) == 0) {
499 if (return_path != NULL) {
500 printf("503 5.5.1 Nested MAIL command\r\n");
501 continue;
502 }
503 if (strncasecmp(buf+5, "from:", 5) != 0 ||
504 ((return_path = parseaddr(buf+10)) == NULL)) {
505 printf("501 5.5.4 Syntax error in parameters\r\n");
506 continue;
507 }
508 printf("250 2.5.0 ok\r\n");
509 continue;
510 }
511 goto syntaxerr;
512
513 case 'n':
514 case 'N':
515 if (strcasecmp(buf, "noop") == 0) {
516 printf("250 2.0.0 ok\r\n");
517 continue;
518 }
519 goto syntaxerr;
520
521 case 'q':
522 case 'Q':
523 if (strcasecmp(buf, "quit") == 0) {
524 printf("221 2.0.0 bye\r\n");
525 exit(EX_OK);
526 }
527 goto syntaxerr;
528
529 case 'r':
530 case 'R':
531 if (strncasecmp(buf, "rcpt ", 5) == 0) {
532 if (return_path == NULL) {
533 printf("503 5.5.1 Need MAIL command\r\n");
534 continue;
535 }
536 if (rcpt_num >= rcpt_alloc) {
537 rcpt_alloc += RCPT_GROW;
538 rcpt_addr = (char **)
539 realloc((char *)rcpt_addr,
540 rcpt_alloc * sizeof(char **));
541 if (rcpt_addr == NULL) {
542 printf("421 4.3.0 memory exhausted\r\n");
543 exit(EX_TEMPFAIL);
544 }
545 }
546 if (strncasecmp(buf+5, "to:", 3) != 0 ||
547 ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
548 printf("501 5.5.4 Syntax error in parameters\r\n");
549 continue;
550 }
551 if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
552 printf("%s\r\n", err);
553 continue;
554 }
555 rcpt_num++;
556 printf("250 2.1.5 ok\r\n");
557 continue;
558 }
559 else if (strcasecmp(buf, "rset") == 0) {
560 printf("250 2.0.0 ok\r\n");
561
562 rset:
563 while (rcpt_num) {
564 free(rcpt_addr[--rcpt_num]);
565 }
566 if (return_path != NULL)
567 free(return_path);
568 return_path = NULL;
569 continue;
570 }
571 goto syntaxerr;
572
573 case 'v':
574 case 'V':
575 if (strncasecmp(buf, "vrfy ", 5) == 0) {
576 printf("252 2.3.3 try RCPT to attempt delivery\r\n");
577 continue;
578 }
579 goto syntaxerr;
580
581 default:
582 syntaxerr:
583 printf("500 5.5.2 Syntax error\r\n");
584 continue;
585 }
586 }
587}
588
589int
590store(from, lmtprcpts)
591 char *from;
592 int lmtprcpts;
593{
416
417 p = malloc(len + 1);
418 if (p == NULL) {
419 printf("421 4.3.0 memory exhausted\r\n");
420 exit(EX_TEMPFAIL);
421 }
422
423 strncpy(p, s, len);
424 p[len] = '\0';
425 return p;
426}
427
428char *
429process_recipient(addr)
430 char *addr;
431{
432 if (getpwnam(addr) == NULL) {
433 return "550 5.1.1 user unknown";
434 }
435
436 return NULL;
437}
438
439
440#define RCPT_GROW 30
441
442void
443dolmtp(nobiff, nofsync)
444 int nobiff, nofsync;
445{
446 char *return_path = NULL;
447 char **rcpt_addr = NULL;
448 int rcpt_num = 0;
449 int rcpt_alloc = 0;
450 char myhostname[1024];
451 char buf[4096];
452 char *err;
453 int msgfd;
454 char *p;
455 int i;
456
457 gethostname(myhostname, sizeof myhostname - 1);
458
459 printf("220 %s LMTP ready\r\n", myhostname);
460 for (;;) {
461 fflush(stdout);
462 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
463 exit(EX_OK);
464 }
465 p = buf + strlen(buf) - 1;
466 if (p >= buf && *p == '\n')
467 *p-- = '\0';
468 if (p >= buf && *p == '\r')
469 *p-- = '\0';
470
471 switch (buf[0]) {
472
473 case 'd':
474 case 'D':
475 if (strcasecmp(buf, "data") == 0) {
476 if (rcpt_num == 0) {
477 printf("503 5.5.1 No recipients\r\n");
478 continue;
479 }
480 msgfd = store(return_path, rcpt_num);
481 if (msgfd == -1)
482 continue;
483
484 for (i = 0; i < rcpt_num; i++) {
485 p = strchr(rcpt_addr[i], '+');
486 if (p != NULL)
487 *p++ = '\0';
488 deliver(msgfd, rcpt_addr[i], nobiff,
489 nofsync);
490 }
491 close(msgfd);
492 goto rset;
493 }
494 goto syntaxerr;
495
496 case 'l':
497 case 'L':
498 if (strncasecmp(buf, "lhlo ", 5) == 0) {
499 printf("250-%s\r\n250-8BITMIME\r\n250-ENHANCEDSTATUSCODES\r\n250 PIPELINING\r\n",
500 myhostname);
501 continue;
502 }
503 goto syntaxerr;
504
505 case 'm':
506 case 'M':
507 if (strncasecmp(buf, "mail ", 5) == 0) {
508 if (return_path != NULL) {
509 printf("503 5.5.1 Nested MAIL command\r\n");
510 continue;
511 }
512 if (strncasecmp(buf+5, "from:", 5) != 0 ||
513 ((return_path = parseaddr(buf+10)) == NULL)) {
514 printf("501 5.5.4 Syntax error in parameters\r\n");
515 continue;
516 }
517 printf("250 2.5.0 ok\r\n");
518 continue;
519 }
520 goto syntaxerr;
521
522 case 'n':
523 case 'N':
524 if (strcasecmp(buf, "noop") == 0) {
525 printf("250 2.0.0 ok\r\n");
526 continue;
527 }
528 goto syntaxerr;
529
530 case 'q':
531 case 'Q':
532 if (strcasecmp(buf, "quit") == 0) {
533 printf("221 2.0.0 bye\r\n");
534 exit(EX_OK);
535 }
536 goto syntaxerr;
537
538 case 'r':
539 case 'R':
540 if (strncasecmp(buf, "rcpt ", 5) == 0) {
541 if (return_path == NULL) {
542 printf("503 5.5.1 Need MAIL command\r\n");
543 continue;
544 }
545 if (rcpt_num >= rcpt_alloc) {
546 rcpt_alloc += RCPT_GROW;
547 rcpt_addr = (char **)
548 realloc((char *)rcpt_addr,
549 rcpt_alloc * sizeof(char **));
550 if (rcpt_addr == NULL) {
551 printf("421 4.3.0 memory exhausted\r\n");
552 exit(EX_TEMPFAIL);
553 }
554 }
555 if (strncasecmp(buf+5, "to:", 3) != 0 ||
556 ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
557 printf("501 5.5.4 Syntax error in parameters\r\n");
558 continue;
559 }
560 if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
561 printf("%s\r\n", err);
562 continue;
563 }
564 rcpt_num++;
565 printf("250 2.1.5 ok\r\n");
566 continue;
567 }
568 else if (strcasecmp(buf, "rset") == 0) {
569 printf("250 2.0.0 ok\r\n");
570
571 rset:
572 while (rcpt_num) {
573 free(rcpt_addr[--rcpt_num]);
574 }
575 if (return_path != NULL)
576 free(return_path);
577 return_path = NULL;
578 continue;
579 }
580 goto syntaxerr;
581
582 case 'v':
583 case 'V':
584 if (strncasecmp(buf, "vrfy ", 5) == 0) {
585 printf("252 2.3.3 try RCPT to attempt delivery\r\n");
586 continue;
587 }
588 goto syntaxerr;
589
590 default:
591 syntaxerr:
592 printf("500 5.5.2 Syntax error\r\n");
593 continue;
594 }
595 }
596}
597
598int
599store(from, lmtprcpts)
600 char *from;
601 int lmtprcpts;
602{
594 FILE *fp;
603 FILE *fp = NULL;
595 time_t tval;
596 int fd, eline;
597 char line[2048];
598 char tmpbuf[sizeof _PATH_LOCTMP + 1];
599
600 strcpy(tmpbuf, _PATH_LOCTMP);
601 if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
602 if (lmtprcpts) {
603 printf("451 4.3.0 unable to open temporary file\r\n");
604 return -1;
605 } else {
604 time_t tval;
605 int fd, eline;
606 char line[2048];
607 char tmpbuf[sizeof _PATH_LOCTMP + 1];
608
609 strcpy(tmpbuf, _PATH_LOCTMP);
610 if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
611 if (lmtprcpts) {
612 printf("451 4.3.0 unable to open temporary file\r\n");
613 return -1;
614 } else {
606 e_to_sys(errno);
607 err("unable to open temporary file");
615 mailerr("451 4.3.0", "unable to open temporary file");
616 exit(eval);
608 }
609 }
610 (void)unlink(tmpbuf);
611
612 if (lmtpmode) {
613 printf("354 go ahead\r\n");
614 fflush(stdout);
615 }
616
617 (void)time(&tval);
618 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
619
620 line[0] = '\0';
621 for (eline = 1; fgets(line, sizeof(line), stdin);) {
617 }
618 }
619 (void)unlink(tmpbuf);
620
621 if (lmtpmode) {
622 printf("354 go ahead\r\n");
623 fflush(stdout);
624 }
625
626 (void)time(&tval);
627 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
628
629 line[0] = '\0';
630 for (eline = 1; fgets(line, sizeof(line), stdin);) {
622 if (line[strlen(line)-2] == '\r') {
623 strcpy(line+strlen(line)-2, "\n");
631 size_t line_len = strlen(line);
632
633 if (line_len >= 2 &&
634 line[line_len - 2] == '\r' &&
635 line[line_len - 1] == '\n') {
636 strcpy(line + line_len - 2, "\n");
624 }
625 if (lmtprcpts && line[0] == '.') {
637 }
638 if (lmtprcpts && line[0] == '.') {
639 char *src = line + 1, *dest = line;
640
626 if (line[1] == '\n')
627 goto lmtpdot;
641 if (line[1] == '\n')
642 goto lmtpdot;
628 strcpy(line, line+1);
643 while (*src != '\0')
644 *dest++ = *src++;
645 *dest = '\0';
629 }
630 if (line[0] == '\n')
631 eline = 1;
632 else {
633 if (eline && line[0] == 'F' &&
634 !memcmp(line, "From ", 5))
635 (void)putc('>', fp);
636 eline = 0;
637 }
638 (void)fprintf(fp, "%s", line);
639 if (ferror(fp)) {
640 if (lmtprcpts) {
641 while (lmtprcpts--) {
642 printf("451 4.3.0 temporary file write error\r\n");
643 }
644 fclose(fp);
645 return -1;
646 } else {
646 }
647 if (line[0] == '\n')
648 eline = 1;
649 else {
650 if (eline && line[0] == 'F' &&
651 !memcmp(line, "From ", 5))
652 (void)putc('>', fp);
653 eline = 0;
654 }
655 (void)fprintf(fp, "%s", line);
656 if (ferror(fp)) {
657 if (lmtprcpts) {
658 while (lmtprcpts--) {
659 printf("451 4.3.0 temporary file write error\r\n");
660 }
661 fclose(fp);
662 return -1;
663 } else {
647 e_to_sys(errno);
648 err("temporary file write error");
664 mailerr("451 4.3.0",
665 "temporary file write error");
666 fclose(fp);
667 exit(eval);
649 }
650 }
651 }
652
653 if (lmtprcpts) {
654 /* Got a premature EOF -- toss message and exit */
655 exit(EX_OK);
656 }
657
658 /* If message not newline terminated, need an extra. */
659 if (strchr(line, '\n') == NULL)
660 (void)putc('\n', fp);
661
662 lmtpdot:
663
664 /* Output a newline; note, empty messages are allowed. */
665 (void)putc('\n', fp);
666
667 if (fflush(fp) == EOF || ferror(fp)) {
668 if (lmtprcpts) {
669 while (lmtprcpts--) {
670 printf("451 4.3.0 temporary file write error\r\n");
671 }
672 fclose(fp);
673 return -1;
674 } else {
668 }
669 }
670 }
671
672 if (lmtprcpts) {
673 /* Got a premature EOF -- toss message and exit */
674 exit(EX_OK);
675 }
676
677 /* If message not newline terminated, need an extra. */
678 if (strchr(line, '\n') == NULL)
679 (void)putc('\n', fp);
680
681 lmtpdot:
682
683 /* Output a newline; note, empty messages are allowed. */
684 (void)putc('\n', fp);
685
686 if (fflush(fp) == EOF || ferror(fp)) {
687 if (lmtprcpts) {
688 while (lmtprcpts--) {
689 printf("451 4.3.0 temporary file write error\r\n");
690 }
691 fclose(fp);
692 return -1;
693 } else {
675 e_to_sys(errno);
676 err("temporary file write error");
694 mailerr("451 4.3.0", "temporary file write error");
695 fclose(fp);
696 exit(eval);
677 }
678 }
679 return (fd);
680}
681
682void
683deliver(fd, name, nobiff, nofsync)
684 int fd;
685 char *name;
686 int nobiff, nofsync;
687{
688 struct stat fsb, sb;
689 struct passwd *pw;
690 int mbfd, nr, nw, off;
691 char *p;
692 char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
693 off_t curoff;
694 extern char *quad_to_string();
695
696 /*
697 * Disallow delivery to unknown names -- special mailboxes can be
698 * handled in the sendmail aliases file.
699 */
700 if ((pw = getpwnam(name)) == NULL) {
701 if (eval != EX_TEMPFAIL)
702 eval = EX_UNAVAILABLE;
703 if (lmtpmode) {
704 if (eval == EX_TEMPFAIL) {
705 printf("451 4.3.0 cannot lookup name: %s\r\n", name);
706 } else {
707 printf("550 5.1.1 unknown name: %s\r\n", name);
708 }
709 }
710 else {
697 }
698 }
699 return (fd);
700}
701
702void
703deliver(fd, name, nobiff, nofsync)
704 int fd;
705 char *name;
706 int nobiff, nofsync;
707{
708 struct stat fsb, sb;
709 struct passwd *pw;
710 int mbfd, nr, nw, off;
711 char *p;
712 char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
713 off_t curoff;
714 extern char *quad_to_string();
715
716 /*
717 * Disallow delivery to unknown names -- special mailboxes can be
718 * handled in the sendmail aliases file.
719 */
720 if ((pw = getpwnam(name)) == NULL) {
721 if (eval != EX_TEMPFAIL)
722 eval = EX_UNAVAILABLE;
723 if (lmtpmode) {
724 if (eval == EX_TEMPFAIL) {
725 printf("451 4.3.0 cannot lookup name: %s\r\n", name);
726 } else {
727 printf("550 5.1.1 unknown name: %s\r\n", name);
728 }
729 }
730 else {
711 warn("unknown name: %s", name);
731 char *errcode = NULL;
732
733 if (eval == EX_TEMPFAIL)
734 errcode = "451 4.3.0";
735 else
736 errcode = "550 5.1.1";
737 mailerr(errcode, "unknown name: %s", name);
712 }
713 return;
714 }
715 endpwent();
716
717 /*
718 * Keep name reasonably short to avoid buffer overruns.
719 * This isn't necessary on BSD because of the proper
720 * definition of snprintf(), but it can cause problems
721 * on other systems.
722 * Also, clear out any bogus characters.
723 */
724
725 if (strlen(name) > 40)
726 name[40] = '\0';
727 for (p = name; *p != '\0'; p++)
728 {
729 if (!isascii(*p))
730 *p &= 0x7f;
731 else if (!isprint(*p))
732 *p = '.';
733 }
734
735 (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
736
737 /*
738 * If the mailbox is linked or a symlink, fail. There's an obvious
739 * race here, that the file was replaced with a symbolic link after
740 * the lstat returned, but before the open. We attempt to detect
741 * this by comparing the original stat information and information
742 * returned by an fstat of the file descriptor returned by the open.
743 *
744 * NB: this is a symptom of a larger problem, that the mail spooling
745 * directory is writeable by the wrong users. If that directory is
746 * writeable, system security is compromised for other reasons, and
747 * it cannot be fixed here.
748 *
749 * If we created the mailbox, set the owner/group. If that fails,
750 * just return. Another process may have already opened it, so we
751 * can't unlink it. Historically, binmail set the owner/group at
752 * each mail delivery. We no longer do this, assuming that if the
753 * ownership or permissions were changed there was a reason.
754 *
755 * XXX
756 * open(2) should support flock'ing the file.
757 */
758tryagain:
759 lockmbox(path);
760 if (lstat(path, &sb) < 0) {
761 mbfd = open(path,
762 O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
763 if (lstat(path, &sb) < 0)
764 {
765 eval = EX_CANTCREAT;
738 }
739 return;
740 }
741 endpwent();
742
743 /*
744 * Keep name reasonably short to avoid buffer overruns.
745 * This isn't necessary on BSD because of the proper
746 * definition of snprintf(), but it can cause problems
747 * on other systems.
748 * Also, clear out any bogus characters.
749 */
750
751 if (strlen(name) > 40)
752 name[40] = '\0';
753 for (p = name; *p != '\0'; p++)
754 {
755 if (!isascii(*p))
756 *p &= 0x7f;
757 else if (!isprint(*p))
758 *p = '.';
759 }
760
761 (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
762
763 /*
764 * If the mailbox is linked or a symlink, fail. There's an obvious
765 * race here, that the file was replaced with a symbolic link after
766 * the lstat returned, but before the open. We attempt to detect
767 * this by comparing the original stat information and information
768 * returned by an fstat of the file descriptor returned by the open.
769 *
770 * NB: this is a symptom of a larger problem, that the mail spooling
771 * directory is writeable by the wrong users. If that directory is
772 * writeable, system security is compromised for other reasons, and
773 * it cannot be fixed here.
774 *
775 * If we created the mailbox, set the owner/group. If that fails,
776 * just return. Another process may have already opened it, so we
777 * can't unlink it. Historically, binmail set the owner/group at
778 * each mail delivery. We no longer do this, assuming that if the
779 * ownership or permissions were changed there was a reason.
780 *
781 * XXX
782 * open(2) should support flock'ing the file.
783 */
784tryagain:
785 lockmbox(path);
786 if (lstat(path, &sb) < 0) {
787 mbfd = open(path,
788 O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
789 if (lstat(path, &sb) < 0)
790 {
791 eval = EX_CANTCREAT;
766 warn("%s: lstat: file changed after open", path);
792 mailerr("550 5.2.0",
793 "%s: lstat: file changed after open", path);
767 goto err1;
768 }
769 else
770 sb.st_uid = pw->pw_uid;
771 if (mbfd == -1) {
772 if (errno == EEXIST)
773 goto tryagain;
774 } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
775 mailerr("451 4.3.0", "chown %u.%u: %s",
776 pw->pw_uid, pw->pw_gid, name);
777 goto err1;
778 }
779 } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
780 mailerr("550 5.2.0", "%s: irregular file", path);
781 goto err0;
782 } else if (sb.st_uid != pw->pw_uid) {
783 eval = EX_CANTCREAT;
784 mailerr("550 5.2.0", "%s: wrong ownership (%d)",
785 path, sb.st_uid);
786 goto err0;
787 } else {
788 mbfd = open(path, O_APPEND|O_WRONLY, 0);
789 }
790
791 if (mbfd == -1) {
792 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
793 goto err0;
794 } else if (fstat(mbfd, &fsb) < 0 ||
795 fsb.st_nlink != 1 ||
796 sb.st_nlink != 1 ||
797 !S_ISREG(fsb.st_mode) ||
798 sb.st_dev != fsb.st_dev ||
799 sb.st_ino != fsb.st_ino ||
800#if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */
801 sb.st_gen != fsb.st_gen ||
802#endif
803 sb.st_uid != fsb.st_uid) {
804 eval = EX_TEMPFAIL;
794 goto err1;
795 }
796 else
797 sb.st_uid = pw->pw_uid;
798 if (mbfd == -1) {
799 if (errno == EEXIST)
800 goto tryagain;
801 } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
802 mailerr("451 4.3.0", "chown %u.%u: %s",
803 pw->pw_uid, pw->pw_gid, name);
804 goto err1;
805 }
806 } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
807 mailerr("550 5.2.0", "%s: irregular file", path);
808 goto err0;
809 } else if (sb.st_uid != pw->pw_uid) {
810 eval = EX_CANTCREAT;
811 mailerr("550 5.2.0", "%s: wrong ownership (%d)",
812 path, sb.st_uid);
813 goto err0;
814 } else {
815 mbfd = open(path, O_APPEND|O_WRONLY, 0);
816 }
817
818 if (mbfd == -1) {
819 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
820 goto err0;
821 } else if (fstat(mbfd, &fsb) < 0 ||
822 fsb.st_nlink != 1 ||
823 sb.st_nlink != 1 ||
824 !S_ISREG(fsb.st_mode) ||
825 sb.st_dev != fsb.st_dev ||
826 sb.st_ino != fsb.st_ino ||
827#if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */
828 sb.st_gen != fsb.st_gen ||
829#endif
830 sb.st_uid != fsb.st_uid) {
831 eval = EX_TEMPFAIL;
805 warn("%s: fstat: file changed after open", path);
832 mailerr("550 5.2.0", "%s: fstat: file changed after open",
833 path);
806 goto err1;
807 }
808
809 /* Wait until we can get a lock on the file. */
810 if (flock(mbfd, LOCK_EX)) {
811 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
812 goto err1;
813 }
814
815 if (!nobiff) {
816 /* Get the starting offset of the new message for biff. */
817 curoff = lseek(mbfd, (off_t)0, SEEK_END);
818 if (sizeof curoff > sizeof(long))
819 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n",
820 name, quad_to_string(curoff));
821 else
822 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n",
823 name, curoff);
824 }
825
826 /* Copy the message into the file. */
827 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
828 mailerr("450 4.2.0", "temporary file: %s",
829 strerror(errno));
830 goto err1;
831 }
832 if (setreuid(0, pw->pw_uid) < 0) {
833 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
834 pw->pw_uid, strerror(errno), getuid(), geteuid());
835 goto err1;
836 }
837#ifdef DEBUG
838 printf("new euid = %d\n", geteuid());
839#endif
840 while ((nr = read(fd, buf, sizeof(buf))) > 0)
841 for (off = 0; off < nr; off += nw)
842 if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
843 mailerr("450 4.2.0", "%s: %s",
844 path, strerror(errno));
845 goto err3;
846 }
847 if (nr < 0) {
848 mailerr("450 4.2.0", "temporary file: %s",
849 strerror(errno));
850 goto err3;
851 }
852
853 /* Flush to disk, don't wait for update. */
854 if (!nofsync && fsync(mbfd)) {
855 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
856err3:
857 if (setreuid(0, 0) < 0) {
834 goto err1;
835 }
836
837 /* Wait until we can get a lock on the file. */
838 if (flock(mbfd, LOCK_EX)) {
839 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
840 goto err1;
841 }
842
843 if (!nobiff) {
844 /* Get the starting offset of the new message for biff. */
845 curoff = lseek(mbfd, (off_t)0, SEEK_END);
846 if (sizeof curoff > sizeof(long))
847 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n",
848 name, quad_to_string(curoff));
849 else
850 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n",
851 name, curoff);
852 }
853
854 /* Copy the message into the file. */
855 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
856 mailerr("450 4.2.0", "temporary file: %s",
857 strerror(errno));
858 goto err1;
859 }
860 if (setreuid(0, pw->pw_uid) < 0) {
861 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
862 pw->pw_uid, strerror(errno), getuid(), geteuid());
863 goto err1;
864 }
865#ifdef DEBUG
866 printf("new euid = %d\n", geteuid());
867#endif
868 while ((nr = read(fd, buf, sizeof(buf))) > 0)
869 for (off = 0; off < nr; off += nw)
870 if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
871 mailerr("450 4.2.0", "%s: %s",
872 path, strerror(errno));
873 goto err3;
874 }
875 if (nr < 0) {
876 mailerr("450 4.2.0", "temporary file: %s",
877 strerror(errno));
878 goto err3;
879 }
880
881 /* Flush to disk, don't wait for update. */
882 if (!nofsync && fsync(mbfd)) {
883 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
884err3:
885 if (setreuid(0, 0) < 0) {
886#if 0
887 /* already printed an error above for this recipient */
858 e_to_sys(errno);
859 mailerr("450 4.2.0", "setreuid(0, 0): %s",
860 strerror(errno));
888 e_to_sys(errno);
889 mailerr("450 4.2.0", "setreuid(0, 0): %s",
890 strerror(errno));
891#endif
861 }
862#ifdef DEBUG
863 printf("reset euid = %d\n", geteuid());
864#endif
865 (void)ftruncate(mbfd, curoff);
866err1: (void)close(mbfd);
867err0: unlockmbox();
868 return;
869 }
870
871 /* Close and check -- NFS doesn't write until the close. */
872 if (close(mbfd)) {
873 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
874 truncate(path, curoff);
875 } else if (!nobiff)
876 notifybiff(biffmsg);
877
878 if (setreuid(0, 0) < 0) {
879 mailerr("450 4.2.0", "setreuid(0, 0): %s",
880 strerror(errno));
881 goto err0;
882 }
883#ifdef DEBUG
884 printf("reset euid = %d\n", geteuid());
885#endif
886 unlockmbox();
887 if (lmtpmode) {
888 printf("250 2.1.5 %s OK\r\n", name);
889 }
890}
891
892/*
893 * user.lock files are necessary for compatibility with other
894 * systems, e.g., when the mail spool file is NFS exported.
895 * Alas, mailbox locking is more than just a local matter.
896 * EPA 11/94.
897 */
898
899char lockname[MAXPATHLEN];
900int locked = 0;
901
902void
903lockmbox(path)
904 char *path;
905{
906 int statfailed = 0;
907
908 if (locked)
909 return;
910 if (strlen(path) + 6 > sizeof lockname)
911 return;
912 snprintf(lockname, sizeof lockname, "%s.lock", path);
913 for (;; sleep(5)) {
914 int fd;
915 struct stat st;
916 time_t now;
917
918 fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
919 if (fd >= 0) {
920 /* defeat lock checking programs which test pid */
921 write(fd, "0", 2);
922 locked = 1;
923 close(fd);
924 return;
925 }
926 if (stat(lockname, &st) < 0) {
927 if (statfailed++ > 5)
928 return;
929 continue;
930 }
931 statfailed = 0;
932 time(&now);
933 if (now < st.st_ctime + 300)
934 continue;
935 unlink(lockname);
936 }
937}
938
939void
940unlockmbox()
941{
942 if (!locked)
943 return;
944 unlink(lockname);
945 locked = 0;
946}
947
948void
949notifybiff(msg)
950 char *msg;
951{
952 static struct sockaddr_in addr;
953 static int f = -1;
954 struct hostent *hp;
955 struct servent *sp;
956 int len;
957
958 if (addr.sin_family == 0) {
959 /* Be silent if biff service not available. */
960 if ((sp = getservbyname("biff", "udp")) == NULL)
961 return;
962 if ((hp = gethostbyname("localhost")) == NULL) {
892 }
893#ifdef DEBUG
894 printf("reset euid = %d\n", geteuid());
895#endif
896 (void)ftruncate(mbfd, curoff);
897err1: (void)close(mbfd);
898err0: unlockmbox();
899 return;
900 }
901
902 /* Close and check -- NFS doesn't write until the close. */
903 if (close(mbfd)) {
904 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
905 truncate(path, curoff);
906 } else if (!nobiff)
907 notifybiff(biffmsg);
908
909 if (setreuid(0, 0) < 0) {
910 mailerr("450 4.2.0", "setreuid(0, 0): %s",
911 strerror(errno));
912 goto err0;
913 }
914#ifdef DEBUG
915 printf("reset euid = %d\n", geteuid());
916#endif
917 unlockmbox();
918 if (lmtpmode) {
919 printf("250 2.1.5 %s OK\r\n", name);
920 }
921}
922
923/*
924 * user.lock files are necessary for compatibility with other
925 * systems, e.g., when the mail spool file is NFS exported.
926 * Alas, mailbox locking is more than just a local matter.
927 * EPA 11/94.
928 */
929
930char lockname[MAXPATHLEN];
931int locked = 0;
932
933void
934lockmbox(path)
935 char *path;
936{
937 int statfailed = 0;
938
939 if (locked)
940 return;
941 if (strlen(path) + 6 > sizeof lockname)
942 return;
943 snprintf(lockname, sizeof lockname, "%s.lock", path);
944 for (;; sleep(5)) {
945 int fd;
946 struct stat st;
947 time_t now;
948
949 fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
950 if (fd >= 0) {
951 /* defeat lock checking programs which test pid */
952 write(fd, "0", 2);
953 locked = 1;
954 close(fd);
955 return;
956 }
957 if (stat(lockname, &st) < 0) {
958 if (statfailed++ > 5)
959 return;
960 continue;
961 }
962 statfailed = 0;
963 time(&now);
964 if (now < st.st_ctime + 300)
965 continue;
966 unlink(lockname);
967 }
968}
969
970void
971unlockmbox()
972{
973 if (!locked)
974 return;
975 unlink(lockname);
976 locked = 0;
977}
978
979void
980notifybiff(msg)
981 char *msg;
982{
983 static struct sockaddr_in addr;
984 static int f = -1;
985 struct hostent *hp;
986 struct servent *sp;
987 int len;
988
989 if (addr.sin_family == 0) {
990 /* Be silent if biff service not available. */
991 if ((sp = getservbyname("biff", "udp")) == NULL)
992 return;
993 if ((hp = gethostbyname("localhost")) == NULL) {
963 warn("localhost: %s", strerror(errno));
964 return;
965 }
966 addr.sin_family = hp->h_addrtype;
967 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
968 addr.sin_port = sp->s_port;
969 }
970 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
994 return;
995 }
996 addr.sin_family = hp->h_addrtype;
997 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
998 addr.sin_port = sp->s_port;
999 }
1000 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
971 warn("socket: %s", strerror(errno));
972 return;
973 }
974 len = strlen(msg) + 1;
1001 return;
1002 }
1003 len = strlen(msg) + 1;
975 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
976 != len)
977 warn("sendto biff: %s", strerror(errno));
1004 (void) sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr));
978}
979
980void
981usage()
982{
983 eval = EX_USAGE;
1005}
1006
1007void
1008usage()
1009{
1010 eval = EX_USAGE;
984 err("usage: mail.local [-b] [-l] [-f from] [-s] user ...");
1011 mailerr(NULL, "usage: mail.local [-b] [-l] [-f from] [-s] user ...");
1012 exit(eval);
985}
986
987void
988#ifdef __STDC__
989mailerr(const char *hdr, const char *fmt, ...)
990#else
991mailerr(hdr, fmt, va_alist)
992 const char *hdr;
993 const char *fmt;
994 va_dcl
995#endif
996{
997 va_list ap;
998
999#ifdef __STDC__
1000 va_start(ap, fmt);
1001#else
1002 va_start(ap);
1003#endif
1004 if (lmtpmode)
1005 {
1013}
1014
1015void
1016#ifdef __STDC__
1017mailerr(const char *hdr, const char *fmt, ...)
1018#else
1019mailerr(hdr, fmt, va_alist)
1020 const char *hdr;
1021 const char *fmt;
1022 va_dcl
1023#endif
1024{
1025 va_list ap;
1026
1027#ifdef __STDC__
1028 va_start(ap, fmt);
1029#else
1030 va_start(ap);
1031#endif
1032 if (lmtpmode)
1033 {
1006 printf("%s ", hdr);
1034 if (hdr != NULL)
1035 printf("%s ", hdr);
1007 vprintf(fmt, ap);
1008 printf("\r\n");
1009 }
1010 else
1011 {
1012 e_to_sys(errno);
1013 vwarn(fmt, ap);
1014 }
1015}
1016
1036 vprintf(fmt, ap);
1037 printf("\r\n");
1038 }
1039 else
1040 {
1041 e_to_sys(errno);
1042 vwarn(fmt, ap);
1043 }
1044}
1045
1017#ifdef __STDC__
1018void
1046void
1019err(const char *fmt, ...)
1020#else
1021void
1022err(fmt, va_alist)
1023 const char *fmt;
1024 va_dcl
1025#endif
1026{
1027 va_list ap;
1028
1029#ifdef __STDC__
1030 va_start(ap, fmt);
1031#else
1032 va_start(ap);
1033#endif
1034 vwarn(fmt, ap);
1035 va_end(ap);
1036
1037 exit(eval);
1038}
1039
1040void
1041#ifdef __STDC__
1042warn(const char *fmt, ...)
1043#else
1044warn(fmt, va_alist)
1045 const char *fmt;
1046 va_dcl
1047#endif
1048{
1049 va_list ap;
1050
1051#ifdef __STDC__
1052 va_start(ap, fmt);
1053#else
1054 va_start(ap);
1055#endif
1056 vwarn(fmt, ap);
1057 va_end(ap);
1058}
1059
1060void
1061vwarn(fmt, ap)
1062 const char *fmt;
1063 _BSD_VA_LIST_ ap;
1064{
1065 /*
1066 * Log the message to stderr.
1067 *
1068 * Don't use LOG_PERROR as an openlog() flag to do this,
1069 * it's not portable enough.
1070 */
1071 if (eval != EX_USAGE)
1072 (void)fprintf(stderr, "mail.local: ");
1073 (void)vfprintf(stderr, fmt, ap);
1074 (void)fprintf(stderr, "\n");
1075
1076#if USE_VSYSLOG
1077 /* Log the message to syslog. */
1078 vsyslog(LOG_ERR, fmt, ap);
1079#else
1080 {
1081 char fmtbuf[10240];
1082
1083 (void) vsnprintf(fmtbuf, sizeof fmtbuf, fmt, ap);
1084 syslog(LOG_ERR, "%s", fmtbuf);
1085 }
1086#endif
1087}
1088
1089/*
1090 * e_to_sys --
1091 * Guess which errno's are temporary. Gag me.
1092 */
1093void
1094e_to_sys(num)
1095 int num;
1096{
1097 /* Temporary failures override hard errors. */
1098 if (eval == EX_TEMPFAIL)
1099 return;
1100
1101 switch(num) { /* Hopefully temporary errors. */
1102#ifdef EAGAIN
1103 case EAGAIN: /* Resource temporarily unavailable */
1104#endif
1105#ifdef EDQUOT
1106 case EDQUOT: /* Disc quota exceeded */
1107#endif
1108#ifdef EBUSY
1109 case EBUSY: /* Device busy */
1110#endif
1111#ifdef EPROCLIM
1112 case EPROCLIM: /* Too many processes */
1113#endif
1114#ifdef EUSERS
1115 case EUSERS: /* Too many users */
1116#endif
1117#ifdef ECONNABORTED
1118 case ECONNABORTED: /* Software caused connection abort */
1119#endif
1120#ifdef ECONNREFUSED
1121 case ECONNREFUSED: /* Connection refused */
1122#endif
1123#ifdef ECONNRESET
1124 case ECONNRESET: /* Connection reset by peer */
1125#endif
1126#ifdef EDEADLK
1127 case EDEADLK: /* Resource deadlock avoided */
1128#endif
1129#ifdef EFBIG
1130 case EFBIG: /* File too large */
1131#endif
1132#ifdef EHOSTDOWN
1133 case EHOSTDOWN: /* Host is down */
1134#endif
1135#ifdef EHOSTUNREACH
1136 case EHOSTUNREACH: /* No route to host */
1137#endif
1138#ifdef EMFILE
1139 case EMFILE: /* Too many open files */
1140#endif
1141#ifdef ENETDOWN
1142 case ENETDOWN: /* Network is down */
1143#endif
1144#ifdef ENETRESET
1145 case ENETRESET: /* Network dropped connection on reset */
1146#endif
1147#ifdef ENETUNREACH
1148 case ENETUNREACH: /* Network is unreachable */
1149#endif
1150#ifdef ENFILE
1151 case ENFILE: /* Too many open files in system */
1152#endif
1153#ifdef ENOBUFS
1154 case ENOBUFS: /* No buffer space available */
1155#endif
1156#ifdef ENOMEM
1157 case ENOMEM: /* Cannot allocate memory */
1158#endif
1159#ifdef ENOSPC
1160 case ENOSPC: /* No space left on device */
1161#endif
1162#ifdef EROFS
1163 case EROFS: /* Read-only file system */
1164#endif
1165#ifdef ESTALE
1166 case ESTALE: /* Stale NFS file handle */
1167#endif
1168#ifdef ETIMEDOUT
1169 case ETIMEDOUT: /* Connection timed out */
1170#endif
1171#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1172 case EWOULDBLOCK: /* Operation would block. */
1173#endif
1174 eval = EX_TEMPFAIL;
1175 break;
1176 default:
1177 eval = EX_UNAVAILABLE;
1178 break;
1179 }
1180}
1181
1182#if !HASSTRERROR
1183
1184char *
1185strerror(eno)
1186 int eno;
1187{
1188 extern int sys_nerr;
1189 extern char *sys_errlist[];
1190 static char ebuf[60];
1191
1192 if (eno >= 0 && eno < sys_nerr)
1193 return sys_errlist[eno];
1194 (void) sprintf(ebuf, "Error %d", eno);
1195 return ebuf;
1196}
1197
1198#endif /* !HASSTRERROR */
1199
1200#if defined(ultrix) || defined(_CRAY)
1201
1202/*
1203 * Copyright (c) 1987, 1993
1204 * The Regents of the University of California. All rights reserved.
1205 *
1206 * Redistribution and use in source and binary forms, with or without
1207 * modification, are permitted provided that the following conditions
1208 * are met:
1209 * 1. Redistributions of source code must retain the above copyright
1210 * notice, this list of conditions and the following disclaimer.
1211 * 2. Redistributions in binary form must reproduce the above copyright
1212 * notice, this list of conditions and the following disclaimer in the
1213 * documentation and/or other materials provided with the distribution.
1214 * 3. All advertising materials mentioning features or use of this software
1215 * must display the following acknowledgement:
1216 * This product includes software developed by the University of
1217 * California, Berkeley and its contributors.
1218 * 4. Neither the name of the University nor the names of its contributors
1219 * may be used to endorse or promote products derived from this software
1220 * without specific prior written permission.
1221 *
1222 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1223 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1224 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1225 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1226 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1227 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1228 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1229 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1230 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1231 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1232 * SUCH DAMAGE.
1233 */
1234
1235#if defined(LIBC_SCCS) && !defined(lint)
1236static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
1237#endif /* LIBC_SCCS and not lint */
1238
1239#include <sys/types.h>
1240#include <sys/stat.h>
1241#include <fcntl.h>
1242#include <errno.h>
1243#include <stdio.h>
1244#include <ctype.h>
1245
1246static int _gettemp();
1247
1248mkstemp(path)
1249 char *path;
1250{
1251 int fd;
1252
1253 return (_gettemp(path, &fd) ? fd : -1);
1254}
1255
1256/*
1257char *
1258mktemp(path)
1259 char *path;
1260{
1261 return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
1262}
1263*/
1264
1265static
1266_gettemp(path, doopen)
1267 char *path;
1268 register int *doopen;
1269{
1270 extern int errno;
1271 register char *start, *trv;
1272 struct stat sbuf;
1273 u_int pid;
1274
1275 pid = getpid();
1276 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
1277 while (*--trv == 'X') {
1278 *trv = (pid % 10) + '0';
1279 pid /= 10;
1280 }
1281
1282 /*
1283 * check the target directory; if you have six X's and it
1284 * doesn't exist this runs for a *very* long time.
1285 */
1286 for (start = trv + 1;; --trv) {
1287 if (trv <= path)
1288 break;
1289 if (*trv == '/') {
1290 *trv = '\0';
1291 if (stat(path, &sbuf) < 0)
1292 return(0);
1293 if (!S_ISDIR(sbuf.st_mode)) {
1294 errno = ENOTDIR;
1295 return(0);
1296 }
1297 *trv = '/';
1298 break;
1299 }
1300 }
1301
1302 for (;;) {
1303 if (doopen) {
1304 if ((*doopen =
1305 open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
1306 return(1);
1307 if (errno != EEXIST)
1308 return(0);
1309 }
1310 else if (stat(path, &sbuf) < 0)
1311 return(errno == ENOENT ? 1 : 0);
1312
1313 /* tricky little algorithm for backward compatibility */
1314 for (trv = start;;) {
1315 if (!*trv)
1316 return(0);
1317 if (*trv == 'z')
1318 *trv++ = 'a';
1319 else {
1320 if (isascii(*trv) && isdigit(*trv))
1321 *trv = 'a';
1322 else
1323 ++*trv;
1324 break;
1325 }
1326 }
1327 }
1328 /*NOTREACHED*/
1329}
1330
1331#endif /* ultrix */
1047vwarn(fmt, ap)
1048 const char *fmt;
1049 _BSD_VA_LIST_ ap;
1050{
1051 /*
1052 * Log the message to stderr.
1053 *
1054 * Don't use LOG_PERROR as an openlog() flag to do this,
1055 * it's not portable enough.
1056 */
1057 if (eval != EX_USAGE)
1058 (void)fprintf(stderr, "mail.local: ");
1059 (void)vfprintf(stderr, fmt, ap);
1060 (void)fprintf(stderr, "\n");
1061
1062#if USE_VSYSLOG
1063 /* Log the message to syslog. */
1064 vsyslog(LOG_ERR, fmt, ap);
1065#else
1066 {
1067 char fmtbuf[10240];
1068
1069 (void) vsnprintf(fmtbuf, sizeof fmtbuf, fmt, ap);
1070 syslog(LOG_ERR, "%s", fmtbuf);
1071 }
1072#endif
1073}
1074
1075/*
1076 * e_to_sys --
1077 * Guess which errno's are temporary. Gag me.
1078 */
1079void
1080e_to_sys(num)
1081 int num;
1082{
1083 /* Temporary failures override hard errors. */
1084 if (eval == EX_TEMPFAIL)
1085 return;
1086
1087 switch(num) { /* Hopefully temporary errors. */
1088#ifdef EAGAIN
1089 case EAGAIN: /* Resource temporarily unavailable */
1090#endif
1091#ifdef EDQUOT
1092 case EDQUOT: /* Disc quota exceeded */
1093#endif
1094#ifdef EBUSY
1095 case EBUSY: /* Device busy */
1096#endif
1097#ifdef EPROCLIM
1098 case EPROCLIM: /* Too many processes */
1099#endif
1100#ifdef EUSERS
1101 case EUSERS: /* Too many users */
1102#endif
1103#ifdef ECONNABORTED
1104 case ECONNABORTED: /* Software caused connection abort */
1105#endif
1106#ifdef ECONNREFUSED
1107 case ECONNREFUSED: /* Connection refused */
1108#endif
1109#ifdef ECONNRESET
1110 case ECONNRESET: /* Connection reset by peer */
1111#endif
1112#ifdef EDEADLK
1113 case EDEADLK: /* Resource deadlock avoided */
1114#endif
1115#ifdef EFBIG
1116 case EFBIG: /* File too large */
1117#endif
1118#ifdef EHOSTDOWN
1119 case EHOSTDOWN: /* Host is down */
1120#endif
1121#ifdef EHOSTUNREACH
1122 case EHOSTUNREACH: /* No route to host */
1123#endif
1124#ifdef EMFILE
1125 case EMFILE: /* Too many open files */
1126#endif
1127#ifdef ENETDOWN
1128 case ENETDOWN: /* Network is down */
1129#endif
1130#ifdef ENETRESET
1131 case ENETRESET: /* Network dropped connection on reset */
1132#endif
1133#ifdef ENETUNREACH
1134 case ENETUNREACH: /* Network is unreachable */
1135#endif
1136#ifdef ENFILE
1137 case ENFILE: /* Too many open files in system */
1138#endif
1139#ifdef ENOBUFS
1140 case ENOBUFS: /* No buffer space available */
1141#endif
1142#ifdef ENOMEM
1143 case ENOMEM: /* Cannot allocate memory */
1144#endif
1145#ifdef ENOSPC
1146 case ENOSPC: /* No space left on device */
1147#endif
1148#ifdef EROFS
1149 case EROFS: /* Read-only file system */
1150#endif
1151#ifdef ESTALE
1152 case ESTALE: /* Stale NFS file handle */
1153#endif
1154#ifdef ETIMEDOUT
1155 case ETIMEDOUT: /* Connection timed out */
1156#endif
1157#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1158 case EWOULDBLOCK: /* Operation would block. */
1159#endif
1160 eval = EX_TEMPFAIL;
1161 break;
1162 default:
1163 eval = EX_UNAVAILABLE;
1164 break;
1165 }
1166}
1167
1168#if !HASSTRERROR
1169
1170char *
1171strerror(eno)
1172 int eno;
1173{
1174 extern int sys_nerr;
1175 extern char *sys_errlist[];
1176 static char ebuf[60];
1177
1178 if (eno >= 0 && eno < sys_nerr)
1179 return sys_errlist[eno];
1180 (void) sprintf(ebuf, "Error %d", eno);
1181 return ebuf;
1182}
1183
1184#endif /* !HASSTRERROR */
1185
1186#if defined(ultrix) || defined(_CRAY)
1187
1188/*
1189 * Copyright (c) 1987, 1993
1190 * The Regents of the University of California. All rights reserved.
1191 *
1192 * Redistribution and use in source and binary forms, with or without
1193 * modification, are permitted provided that the following conditions
1194 * are met:
1195 * 1. Redistributions of source code must retain the above copyright
1196 * notice, this list of conditions and the following disclaimer.
1197 * 2. Redistributions in binary form must reproduce the above copyright
1198 * notice, this list of conditions and the following disclaimer in the
1199 * documentation and/or other materials provided with the distribution.
1200 * 3. All advertising materials mentioning features or use of this software
1201 * must display the following acknowledgement:
1202 * This product includes software developed by the University of
1203 * California, Berkeley and its contributors.
1204 * 4. Neither the name of the University nor the names of its contributors
1205 * may be used to endorse or promote products derived from this software
1206 * without specific prior written permission.
1207 *
1208 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1209 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1210 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1211 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1212 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1213 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1214 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1215 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1216 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1217 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1218 * SUCH DAMAGE.
1219 */
1220
1221#if defined(LIBC_SCCS) && !defined(lint)
1222static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
1223#endif /* LIBC_SCCS and not lint */
1224
1225#include <sys/types.h>
1226#include <sys/stat.h>
1227#include <fcntl.h>
1228#include <errno.h>
1229#include <stdio.h>
1230#include <ctype.h>
1231
1232static int _gettemp();
1233
1234mkstemp(path)
1235 char *path;
1236{
1237 int fd;
1238
1239 return (_gettemp(path, &fd) ? fd : -1);
1240}
1241
1242/*
1243char *
1244mktemp(path)
1245 char *path;
1246{
1247 return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
1248}
1249*/
1250
1251static
1252_gettemp(path, doopen)
1253 char *path;
1254 register int *doopen;
1255{
1256 extern int errno;
1257 register char *start, *trv;
1258 struct stat sbuf;
1259 u_int pid;
1260
1261 pid = getpid();
1262 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
1263 while (*--trv == 'X') {
1264 *trv = (pid % 10) + '0';
1265 pid /= 10;
1266 }
1267
1268 /*
1269 * check the target directory; if you have six X's and it
1270 * doesn't exist this runs for a *very* long time.
1271 */
1272 for (start = trv + 1;; --trv) {
1273 if (trv <= path)
1274 break;
1275 if (*trv == '/') {
1276 *trv = '\0';
1277 if (stat(path, &sbuf) < 0)
1278 return(0);
1279 if (!S_ISDIR(sbuf.st_mode)) {
1280 errno = ENOTDIR;
1281 return(0);
1282 }
1283 *trv = '/';
1284 break;
1285 }
1286 }
1287
1288 for (;;) {
1289 if (doopen) {
1290 if ((*doopen =
1291 open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
1292 return(1);
1293 if (errno != EEXIST)
1294 return(0);
1295 }
1296 else if (stat(path, &sbuf) < 0)
1297 return(errno == ENOENT ? 1 : 0);
1298
1299 /* tricky little algorithm for backward compatibility */
1300 for (trv = start;;) {
1301 if (!*trv)
1302 return(0);
1303 if (*trv == 'z')
1304 *trv++ = 'a';
1305 else {
1306 if (isascii(*trv) && isdigit(*trv))
1307 *trv = 'a';
1308 else
1309 ++*trv;
1310 break;
1311 }
1312 }
1313 }
1314 /*NOTREACHED*/
1315}
1316
1317#endif /* ultrix */