Commit | Line | Data |
---|---|---|
b3cbe40f EA |
1 | # include <stdio.h> |
2 | # include <pwd.h> | |
3 | # include <signal.h> | |
4 | # include "dlvrmail.h" | |
5 | # ifdef LOG | |
6 | # include <log.h> | |
7 | # endif LOG | |
8 | ||
cb590f52 | 9 | static char SccsId[] = "@(#)deliver.c 1.5 %G%"; |
916b3375 | 10 | |
b3cbe40f EA |
11 | /* |
12 | ** DELIVER -- Deliver a message to a particular address. | |
13 | ** | |
14 | ** Algorithm: | |
15 | ** Compute receiving network (i.e., mailer), host, & user. | |
16 | ** If local, see if this is really a program name. | |
17 | ** Build argument for the mailer. | |
18 | ** Create pipe through edit fcn if appropriate. | |
19 | ** Fork. | |
20 | ** Child: call mailer | |
21 | ** Parent: call editfcn if specified. | |
22 | ** Wait for mailer to finish. | |
23 | ** Interpret exit status. | |
24 | ** | |
25 | ** Parameters: | |
26 | ** to -- the address to deliver the message to. | |
27 | ** editfcn -- if non-NULL, we want to call this function | |
28 | ** to output the letter (instead of just out- | |
29 | ** putting it raw). | |
30 | ** | |
31 | ** Returns: | |
32 | ** zero -- successfully delivered. | |
33 | ** else -- some failure, see ExitStat for more info. | |
34 | ** | |
35 | ** Side Effects: | |
36 | ** The standard input is passed off to someone. | |
37 | ** | |
38 | ** WARNING: | |
39 | ** The standard input is shared amongst all children, | |
40 | ** including the file pointer. It is critical that the | |
41 | ** parent waits for the child to finish before forking | |
42 | ** another child. | |
43 | ** | |
b3cbe40f EA |
44 | ** Called By: |
45 | ** main | |
46 | ** savemail | |
47 | ** | |
48 | ** Files: | |
f895a45d | 49 | ** standard input -- must be opened to the message to |
b3cbe40f | 50 | ** deliver. |
b3cbe40f EA |
51 | */ |
52 | ||
53 | deliver(to, editfcn) | |
54 | addrq *to; | |
55 | int (*editfcn)(); | |
56 | { | |
57 | register struct mailer *m; | |
58 | char *host; | |
59 | char *user; | |
60 | extern struct passwd *getpwnam(); | |
61 | char **pvp; | |
62 | extern char **buildargv(); | |
63 | auto int st; | |
64 | register int i; | |
65 | register char *p; | |
66 | int pid; | |
67 | int pvect[2]; | |
68 | extern FILE *fdopen(); | |
69 | extern int errno; | |
70 | FILE *mfile; | |
71 | extern putheader(); | |
72 | extern pipesig(); | |
73 | ||
74 | /* | |
75 | ** Compute receiving mailer, host, and to addreses. | |
76 | ** Do some initialization first. To is the to address | |
77 | ** for error messages. | |
78 | */ | |
79 | ||
80 | To = to->q_paddr; | |
81 | m = to->q_mailer; | |
82 | user = to->q_user; | |
83 | host = to->q_host; | |
84 | Error = 0; | |
85 | errno = 0; | |
86 | # ifdef DEBUG | |
87 | if (Debug) | |
88 | printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user); | |
89 | # endif DEBUG | |
90 | ||
91 | /* | |
92 | ** Remove quote bits from user/host. | |
93 | */ | |
94 | ||
95 | for (p = user; (*p++ &= 0177) != '\0'; ) | |
96 | continue; | |
97 | if (host != NULL) | |
98 | for (p = host; (*p++ &= 0177) != '\0'; ) | |
99 | continue; | |
100 | ||
101 | /* | |
102 | ** Strip quote bits from names if the mailer wants it. | |
103 | */ | |
104 | ||
105 | if (flagset(M_STRIPQ, m->m_flags)) | |
106 | { | |
107 | stripquotes(user); | |
108 | stripquotes(host); | |
109 | } | |
110 | ||
111 | /* | |
112 | ** See if this user name is "special". | |
113 | ** If the user is a program, diddle with the mailer spec. | |
114 | ** If the user name has a slash in it, assume that this | |
115 | ** is a file -- send it off without further ado. | |
116 | ** Note that this means that editfcn's will not | |
117 | ** be applied to the message. | |
118 | */ | |
119 | ||
120 | if (m == &Mailer[0]) | |
121 | { | |
122 | if (*user == '|') | |
123 | { | |
124 | user++; | |
125 | m = &Mailer[1]; | |
126 | } | |
127 | else | |
128 | { | |
129 | if (index(user, '/') != NULL) | |
130 | { | |
131 | i = mailfile(user); | |
132 | giveresponse(i, TRUE, m); | |
133 | return (i); | |
134 | } | |
135 | } | |
136 | } | |
137 | ||
b3cbe40f | 138 | /* |
cb590f52 EA |
139 | ** See if the user exists. |
140 | ** Strictly, this is only needed to print a pretty | |
141 | ** error message. | |
142 | ** | |
143 | ** >>>>>>>>>> This clause assumes that the local mailer | |
144 | ** >> NOTE >> cannot do any further aliasing; that | |
145 | ** >>>>>>>>>> function is subsumed by delivermail. | |
b3cbe40f EA |
146 | */ |
147 | ||
148 | if (m == &Mailer[0]) | |
149 | { | |
150 | if (getpwnam(user) == NULL) | |
151 | { | |
152 | giveresponse(EX_NOUSER, TRUE, m); | |
153 | return (EX_NOUSER); | |
154 | } | |
155 | } | |
b3cbe40f EA |
156 | |
157 | /* | |
158 | ** If the mailer wants a From line, insert a new editfcn. | |
159 | */ | |
160 | ||
161 | if (flagset(M_HDR, m->m_flags) && editfcn == NULL) | |
162 | editfcn = putheader; | |
163 | ||
164 | /* | |
165 | ** Call the mailer. | |
166 | ** The argument vector gets built, pipes through 'editfcn' | |
167 | ** are created as necessary, and we fork & exec as | |
168 | ** appropriate. In the parent, we call 'editfcn'. | |
169 | */ | |
170 | ||
171 | pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr); | |
172 | if (pvp == NULL) | |
173 | { | |
174 | usrerr("name too long"); | |
175 | return (-1); | |
176 | } | |
177 | rewind(stdin); | |
178 | ||
179 | /* create a pipe if we will need one */ | |
180 | if (editfcn != NULL && pipe(pvect) < 0) | |
181 | { | |
182 | syserr("pipe"); | |
183 | return (-1); | |
184 | } | |
185 | pid = fork(); | |
186 | if (pid < 0) | |
187 | { | |
188 | syserr("Cannot fork"); | |
189 | if (editfcn != NULL) | |
190 | { | |
191 | close(pvect[0]); | |
192 | close(pvect[1]); | |
193 | } | |
194 | return (-1); | |
195 | } | |
196 | else if (pid == 0) | |
197 | { | |
198 | /* child -- set up input & exec mailer */ | |
199 | signal(SIGINT, SIG_IGN); | |
200 | if (editfcn != NULL) | |
201 | { | |
202 | close(0); | |
203 | if (dup(pvect[0]) < 0) | |
204 | { | |
205 | syserr("Cannot dup to zero!"); | |
206 | exit(EX_OSERR); | |
207 | } | |
208 | close(pvect[0]); | |
209 | close(pvect[1]); | |
210 | } | |
211 | if (!flagset(M_RESTR, m->m_flags)) | |
212 | setuid(getuid()); | |
213 | # ifdef LOG | |
214 | initlog(NULL, 0, LOG_CLOSE); | |
215 | # endif LOG | |
216 | endpwent(); | |
217 | execv(m->m_mailer, pvp); | |
218 | /* syserr fails because log is closed */ | |
219 | /* syserr("Cannot exec %s", m->m_mailer); */ | |
220 | exit(EX_UNAVAIL); | |
221 | } | |
222 | ||
223 | /* arrange to write out header message if error */ | |
224 | if (editfcn != NULL) | |
225 | { | |
226 | close(pvect[0]); | |
227 | signal(SIGPIPE, pipesig); | |
228 | mfile = fdopen(pvect[1], "w"); | |
229 | (*editfcn)(mfile); | |
230 | fclose(mfile); | |
231 | } | |
232 | ||
233 | /* | |
234 | ** Wait for child to die and report status. | |
235 | ** We should never get fatal errors (e.g., segmentation | |
236 | ** violation), so we report those specially. For other | |
237 | ** errors, we choose a status message (into statmsg), | |
238 | ** and if it represents an error, we print it. | |
239 | */ | |
240 | ||
241 | while ((i = wait(&st)) > 0 && i != pid) | |
242 | continue; | |
243 | if (i < 0) | |
244 | { | |
245 | syserr("wait"); | |
246 | return (-1); | |
247 | } | |
248 | if ((st & 0377) != 0) | |
249 | { | |
250 | syserr("%s: stat %o", pvp[0], st); | |
251 | ExitStat = EX_UNAVAIL; | |
252 | return (-1); | |
253 | } | |
254 | i = (st >> 8) & 0377; | |
255 | giveresponse(i, FALSE, m); | |
256 | return (i); | |
257 | } | |
258 | \f/* | |
259 | ** GIVERESPONSE -- Interpret an error response from a mailer | |
260 | ** | |
261 | ** Parameters: | |
262 | ** stat -- the status code from the mailer (high byte | |
263 | ** only; core dumps must have been taken care of | |
264 | ** already). | |
265 | ** force -- if set, force an error message output, even | |
266 | ** if the mailer seems to like to print its own | |
267 | ** messages. | |
268 | ** m -- the mailer descriptor for this mailer. | |
269 | ** | |
270 | ** Returns: | |
271 | ** none. | |
272 | ** | |
273 | ** Side Effects: | |
274 | ** Error may be set. | |
275 | ** ExitStat may be set. | |
276 | ** | |
b3cbe40f EA |
277 | ** Called By: |
278 | ** deliver | |
b3cbe40f EA |
279 | */ |
280 | ||
281 | giveresponse(stat, force, m) | |
282 | int stat; | |
283 | int force; | |
284 | register struct mailer *m; | |
285 | { | |
286 | register char *statmsg; | |
287 | extern char *SysExMsg[]; | |
288 | register int i; | |
289 | extern int N_SysEx; | |
290 | ||
291 | i = stat - EX__BASE; | |
292 | if (i < 0 || i > N_SysEx) | |
293 | statmsg = NULL; | |
294 | else | |
295 | statmsg = SysExMsg[i]; | |
296 | if (stat == 0) | |
297 | statmsg = "ok"; | |
298 | else | |
299 | { | |
300 | Error++; | |
301 | if (statmsg == NULL && m->m_badstat != 0) | |
302 | { | |
303 | stat = m->m_badstat; | |
304 | i = stat - EX__BASE; | |
305 | # ifdef DEBUG | |
306 | if (i < 0 || i >= N_SysEx) | |
307 | syserr("Bad m_badstat %d", stat); | |
308 | else | |
309 | # endif DEBUG | |
310 | statmsg = SysExMsg[i]; | |
311 | } | |
312 | if (statmsg == NULL) | |
313 | usrerr("unknown mailer response %d", stat); | |
314 | else if (force || !flagset(M_QUIET, m->m_flags)) | |
315 | usrerr("%s", statmsg); | |
316 | } | |
317 | ||
318 | /* | |
319 | ** Final cleanup. | |
320 | ** Log a record of the transaction. Compute the new | |
321 | ** ExitStat -- if we already had an error, stick with | |
322 | ** that. | |
323 | */ | |
324 | ||
325 | # ifdef LOG | |
326 | if (statmsg == NULL) | |
327 | logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat); | |
328 | else | |
329 | logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg); | |
330 | # endif LOG | |
cb590f52 | 331 | setstat(stat); |
b3cbe40f EA |
332 | return (stat); |
333 | } | |
334 | \f/* | |
335 | ** PUTHEADER -- insert the From header into some mail | |
336 | ** | |
337 | ** For mailers such as 'msgs' that want the header inserted | |
338 | ** into the mail, this edit filter inserts the From line and | |
339 | ** then passes the rest of the message through. | |
340 | ** | |
341 | ** Parameters: | |
342 | ** fp -- the file pointer for the output. | |
343 | ** | |
344 | ** Returns: | |
345 | ** none | |
346 | ** | |
347 | ** Side Effects: | |
348 | ** Puts a "From" line in UNIX format, and then | |
349 | ** outputs the rest of the message. | |
350 | ** | |
b3cbe40f EA |
351 | ** Called By: |
352 | ** deliver | |
b3cbe40f EA |
353 | */ |
354 | ||
355 | putheader(fp) | |
356 | register FILE *fp; | |
357 | { | |
358 | char buf[MAXLINE + 1]; | |
359 | long tim; | |
360 | extern char *ctime(); | |
361 | ||
362 | time(&tim); | |
363 | fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim)); | |
364 | while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp)) | |
365 | fputs(buf, fp); | |
366 | if (ferror(fp)) | |
367 | { | |
368 | syserr("putheader: write error"); | |
369 | setstat(EX_IOERR); | |
370 | } | |
371 | } | |
372 | \f/* | |
373 | ** PIPESIG -- Handle broken pipe signals | |
374 | ** | |
375 | ** This just logs an error. | |
376 | ** | |
377 | ** Parameters: | |
378 | ** none | |
379 | ** | |
380 | ** Returns: | |
381 | ** none | |
382 | ** | |
383 | ** Side Effects: | |
384 | ** logs an error message. | |
b3cbe40f EA |
385 | */ |
386 | ||
387 | pipesig() | |
388 | { | |
389 | syserr("Broken pipe"); | |
390 | } | |
391 | \f/* | |
392 | ** SENDTO -- Designate a send list. | |
393 | ** | |
394 | ** The parameter is a comma-separated list of people to send to. | |
395 | ** This routine arranges to send to all of them. | |
396 | ** | |
397 | ** Parameters: | |
398 | ** list -- the send list. | |
399 | ** copyf -- the copy flag; passed to parse. | |
400 | ** | |
401 | ** Returns: | |
402 | ** none | |
403 | ** | |
404 | ** Side Effects: | |
405 | ** none. | |
406 | ** | |
b3cbe40f EA |
407 | ** Called By: |
408 | ** main | |
409 | ** alias | |
b3cbe40f EA |
410 | */ |
411 | ||
412 | sendto(list, copyf) | |
413 | char *list; | |
414 | int copyf; | |
415 | { | |
416 | register char *p; | |
417 | register char *q; | |
418 | register char c; | |
419 | addrq *a; | |
420 | extern addrq *parse(); | |
421 | bool more; | |
422 | ||
423 | /* more keeps track of what the previous delimiter was */ | |
424 | more = TRUE; | |
425 | for (p = list; more; ) | |
426 | { | |
427 | /* find the end of this address */ | |
428 | q = p; | |
429 | while ((c = *p++) != '\0' && c != ',' && c != '\n') | |
430 | continue; | |
431 | more = c != '\0'; | |
432 | *--p = '\0'; | |
433 | if (more) | |
434 | p++; | |
435 | ||
436 | /* parse the address */ | |
437 | if ((a = parse(q, (addrq *) NULL, copyf)) == NULL) | |
438 | continue; | |
439 | ||
440 | /* arrange to send to this person */ | |
441 | recipient(a, &SendQ); | |
442 | } | |
443 | To = NULL; | |
444 | } | |
445 | \f/* | |
446 | ** RECIPIENT -- Designate a message recipient | |
447 | ** | |
448 | ** Saves the named person for future mailing. | |
449 | ** | |
450 | ** Designates a person as a recipient. This routine | |
451 | ** does the initial parsing, and checks to see if | |
452 | ** this person has already received the mail. | |
453 | ** It also supresses local network names and turns them into | |
454 | ** local names. | |
455 | ** | |
456 | ** Parameters: | |
457 | ** a -- the (preparsed) address header for the recipient. | |
458 | ** targetq -- the queue to add the name to. | |
459 | ** | |
460 | ** Returns: | |
461 | ** none. | |
462 | ** | |
463 | ** Side Effects: | |
464 | ** none. | |
465 | ** | |
b3cbe40f EA |
466 | ** Called By: |
467 | ** sendto | |
468 | ** main | |
b3cbe40f EA |
469 | */ |
470 | ||
471 | recipient(a, targetq) | |
472 | register addrq *a; | |
473 | addrq *targetq; | |
474 | { | |
475 | register addrq *q; | |
476 | register struct mailer *m; | |
477 | register char **pvp; | |
478 | extern char *xalloc(); | |
479 | extern bool forward(); | |
480 | extern int errno; | |
481 | extern bool sameaddr(); | |
482 | ||
483 | To = a->q_paddr; | |
484 | m = a->q_mailer; | |
485 | errno = 0; | |
486 | # ifdef DEBUG | |
487 | if (Debug) | |
488 | printf("recipient(%s)\n", To); | |
489 | # endif DEBUG | |
490 | ||
491 | /* | |
492 | ** Don't go to the net if already on the target host. | |
493 | ** This is important on the berkeley network, since | |
494 | ** it get confused if we ask to send to ourselves. | |
495 | ** For nets like the ARPANET, we probably will have | |
496 | ** the local list set to NULL to simplify testing. | |
497 | ** The canonical representation of the name is also set | |
498 | ** to be just the local name so the duplicate letter | |
499 | ** suppression algorithm will work. | |
500 | */ | |
501 | ||
502 | if ((pvp = m->m_local) != NULL) | |
503 | { | |
504 | while (*pvp != NULL) | |
505 | { | |
506 | if (strcmp(*pvp++, a->q_host) == 0) | |
507 | { | |
508 | a->q_mailer = m = &Mailer[0]; | |
509 | break; | |
510 | } | |
511 | } | |
512 | } | |
513 | ||
514 | /* | |
515 | ** Look up this person in the recipient list. If they | |
516 | ** are there already, return, otherwise continue. | |
517 | */ | |
518 | ||
519 | if (!ForceMail) | |
520 | { | |
521 | for (q = &SendQ; (q = nxtinq(q)) != NULL; ) | |
522 | if (sameaddr(q, a, FALSE)) | |
523 | { | |
524 | # ifdef DEBUG | |
525 | if (Debug) | |
526 | printf("(%s in SendQ)\n", a->q_paddr); | |
527 | # endif DEBUG | |
528 | return; | |
529 | } | |
530 | for (q = &AliasQ; (q = nxtinq(q)) != NULL; ) | |
531 | if (sameaddr(q, a, FALSE)) | |
532 | { | |
533 | # ifdef DEBUG | |
534 | if (Debug) | |
535 | printf("(%s in AliasQ)\n", a->q_paddr); | |
536 | # endif DEBUG | |
537 | return; | |
538 | } | |
539 | } | |
540 | ||
541 | /* | |
542 | ** See if the user wants hir mail forwarded. | |
543 | ** `Forward' must do the forwarding recursively. | |
544 | */ | |
545 | ||
546 | if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a)) | |
547 | return; | |
548 | ||
549 | /* | |
550 | ** Put the user onto the target queue. | |
551 | */ | |
552 | ||
553 | if (targetq != NULL) | |
554 | { | |
555 | putonq(a, targetq); | |
556 | } | |
557 | ||
558 | return; | |
559 | } | |
560 | \f/* | |
561 | ** BUILDARGV -- Build an argument vector for a mail server. | |
562 | ** | |
563 | ** Using a template defined in config.c, an argv is built. | |
564 | ** The format of the template is already a vector. The | |
565 | ** items of this vector are copied, unless a dollar sign | |
566 | ** is encountered. In this case, the next character | |
567 | ** specifies something else to copy in. These can be | |
568 | ** $f The from address. | |
569 | ** $h The host. | |
570 | ** $u The user. | |
571 | ** $c The hop count. | |
572 | ** The vector is built in a local buffer. A pointer to | |
573 | ** the static argv is returned. | |
574 | ** | |
575 | ** Parameters: | |
576 | ** tmplt -- a template for an argument vector. | |
577 | ** flags -- the flags for this server. | |
578 | ** host -- the host name to send to. | |
579 | ** user -- the user name to send to. | |
580 | ** from -- the person this mail is from. | |
581 | ** | |
582 | ** Returns: | |
583 | ** A pointer to an argv. | |
584 | ** | |
585 | ** Side Effects: | |
586 | ** none | |
587 | ** | |
588 | ** WARNING: | |
589 | ** Since the argv is staticly allocated, any subsequent | |
590 | ** calls will clobber the old argv. | |
591 | ** | |
b3cbe40f EA |
592 | ** Called By: |
593 | ** deliver | |
b3cbe40f EA |
594 | */ |
595 | ||
596 | char ** | |
597 | buildargv(tmplt, flags, host, user, from) | |
598 | char **tmplt; | |
599 | int flags; | |
600 | char *host; | |
601 | char *user; | |
602 | char *from; | |
603 | { | |
604 | register char *p; | |
605 | register char *q; | |
606 | static char *pv[MAXPV+1]; | |
607 | char **pvp; | |
608 | char **mvp; | |
609 | static char buf[512]; | |
610 | register char *bp; | |
611 | char pbuf[30]; | |
612 | ||
613 | /* | |
614 | ** Do initial argv setup. | |
615 | ** Insert the mailer name. Notice that $x expansion is | |
616 | ** NOT done on the mailer name. Then, if the mailer has | |
617 | ** a picky -f flag, we insert it as appropriate. This | |
618 | ** code does not check for 'pv' overflow; this places a | |
619 | ** manifest lower limit of 4 for MAXPV. | |
620 | */ | |
621 | ||
622 | pvp = pv; | |
623 | bp = buf; | |
624 | ||
625 | *pvp++ = tmplt[0]; | |
626 | ||
627 | /* insert -f or -r flag as appropriate */ | |
628 | if (flagset(M_FOPT|M_ROPT, flags) && FromFlag) | |
629 | { | |
630 | if (flagset(M_FOPT, flags)) | |
631 | *pvp++ = "-f"; | |
632 | else | |
633 | *pvp++ = "-r"; | |
634 | *pvp++ = From.q_paddr; | |
635 | } | |
636 | ||
637 | /* | |
638 | ** Build the rest of argv. | |
639 | ** For each prototype parameter, the prototype is | |
640 | ** scanned character at a time. If a dollar-sign is | |
641 | ** found, 'q' is set to the appropriate expansion, | |
642 | ** otherwise it is null. Then either the string | |
643 | ** pointed to by q, or the original character, is | |
644 | ** interpolated into the buffer. Buffer overflow is | |
645 | ** checked. | |
646 | */ | |
647 | ||
648 | for (mvp = tmplt; (p = *++mvp) != NULL; ) | |
649 | { | |
650 | if (pvp >= &pv[MAXPV]) | |
651 | { | |
652 | syserr("Too many parameters to %s", pv[0]); | |
653 | return (NULL); | |
654 | } | |
655 | *pvp++ = bp; | |
656 | for (; *p != '\0'; p++) | |
657 | { | |
658 | /* q will be the interpolated quantity */ | |
659 | q = NULL; | |
660 | if (*p == '$') | |
661 | { | |
662 | switch (*++p) | |
663 | { | |
664 | case 'f': /* from person */ | |
665 | q = from; | |
666 | break; | |
667 | ||
668 | case 'u': /* user */ | |
669 | q = user; | |
670 | break; | |
671 | ||
672 | case 'h': /* host */ | |
673 | q = host; | |
674 | break; | |
675 | ||
676 | case 'c': /* hop count */ | |
677 | sprintf(pbuf, "%d", HopCount); | |
678 | q = pbuf; | |
679 | break; | |
680 | } | |
681 | } | |
682 | ||
683 | /* | |
684 | ** Interpolate q or output one character | |
685 | ** Strip quote bits as we proceed..... | |
686 | */ | |
687 | ||
688 | if (q != NULL) | |
689 | { | |
690 | while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0') | |
691 | continue; | |
692 | bp--; | |
b3cbe40f EA |
693 | } |
694 | else if (bp < &buf[sizeof buf - 1]) | |
695 | *bp++ = *p; | |
696 | } | |
697 | *bp++ = '\0'; | |
698 | if (bp >= &buf[sizeof buf - 1]) | |
699 | return (NULL); | |
700 | } | |
701 | *pvp = NULL; | |
702 | ||
703 | # ifdef DEBUG | |
704 | if (Debug) | |
705 | { | |
706 | printf("Interpolated argv is:\n"); | |
707 | for (mvp = pv; *mvp != NULL; mvp++) | |
708 | printf("\t%s\n", *mvp); | |
709 | } | |
710 | # endif DEBUG | |
711 | ||
712 | return (pv); | |
713 | } | |
714 | \f/* | |
715 | ** MAILFILE -- Send a message to a file. | |
716 | ** | |
717 | ** Parameters: | |
718 | ** filename -- the name of the file to send to. | |
719 | ** | |
720 | ** Returns: | |
721 | ** The exit code associated with the operation. | |
722 | ** | |
723 | ** Side Effects: | |
724 | ** none. | |
725 | ** | |
b3cbe40f EA |
726 | ** Called By: |
727 | ** deliver | |
b3cbe40f EA |
728 | */ |
729 | ||
730 | mailfile(filename) | |
731 | char *filename; | |
732 | { | |
733 | char buf[MAXLINE]; | |
734 | register FILE *f; | |
735 | auto long tim; | |
736 | extern char *ctime(); | |
737 | ||
738 | f = fopen(filename, "a"); | |
739 | if (f == NULL) | |
740 | return (EX_CANTCREAT); | |
741 | ||
742 | /* output the timestamp */ | |
743 | time(&tim); | |
744 | fprintf(f, "From %s %s", From.q_paddr, ctime(&tim)); | |
745 | rewind(stdin); | |
746 | while (fgets(buf, sizeof buf, stdin) != NULL) | |
747 | { | |
748 | fputs(buf, f); | |
749 | if (ferror(f)) | |
750 | { | |
751 | fclose(f); | |
752 | return (EX_IOERR); | |
753 | } | |
754 | } | |
755 | fputs("\n", f); | |
756 | fclose(f); | |
757 | return (EX_OK); | |
758 | } |