1294Seric # include <stdio.h> 2294Seric # include <pwd.h> 3294Seric # include <signal.h> 4294Seric # include "dlvrmail.h" 5294Seric # ifdef LOG 6294Seric # include <log.h> 7294Seric # endif LOG 8294Seric 9*1624Seric static char SccsId[] = "@(#)deliver.c 1.11 10/27/80"; 10405Seric 11294Seric /* 12294Seric ** DELIVER -- Deliver a message to a particular address. 13294Seric ** 14294Seric ** Algorithm: 15294Seric ** Compute receiving network (i.e., mailer), host, & user. 16294Seric ** If local, see if this is really a program name. 17294Seric ** Build argument for the mailer. 18294Seric ** Create pipe through edit fcn if appropriate. 19294Seric ** Fork. 20294Seric ** Child: call mailer 21294Seric ** Parent: call editfcn if specified. 22294Seric ** Wait for mailer to finish. 23294Seric ** Interpret exit status. 24294Seric ** 25294Seric ** Parameters: 26294Seric ** to -- the address to deliver the message to. 27294Seric ** editfcn -- if non-NULL, we want to call this function 28294Seric ** to output the letter (instead of just out- 29294Seric ** putting it raw). 30294Seric ** 31294Seric ** Returns: 32294Seric ** zero -- successfully delivered. 33294Seric ** else -- some failure, see ExitStat for more info. 34294Seric ** 35294Seric ** Side Effects: 36294Seric ** The standard input is passed off to someone. 37294Seric ** 38294Seric ** WARNING: 39294Seric ** The standard input is shared amongst all children, 40294Seric ** including the file pointer. It is critical that the 41294Seric ** parent waits for the child to finish before forking 42294Seric ** another child. 43294Seric ** 44294Seric ** Called By: 45294Seric ** main 46294Seric ** savemail 47294Seric ** 48294Seric ** Files: 49570Seric ** standard input -- must be opened to the message to 50294Seric ** deliver. 51294Seric */ 52294Seric 53294Seric deliver(to, editfcn) 54294Seric addrq *to; 55294Seric int (*editfcn)(); 56294Seric { 57294Seric register struct mailer *m; 58294Seric char *host; 59294Seric char *user; 60294Seric extern struct passwd *getpwnam(); 61294Seric char **pvp; 62294Seric extern char **buildargv(); 63294Seric auto int st; 64294Seric register int i; 65294Seric register char *p; 66294Seric int pid; 67294Seric int pvect[2]; 68294Seric extern FILE *fdopen(); 69294Seric extern int errno; 70294Seric FILE *mfile; 71294Seric extern putheader(); 72294Seric extern pipesig(); 73294Seric 74294Seric /* 75294Seric ** Compute receiving mailer, host, and to addreses. 76294Seric ** Do some initialization first. To is the to address 77294Seric ** for error messages. 78294Seric */ 79294Seric 80294Seric To = to->q_paddr; 81294Seric m = to->q_mailer; 82294Seric user = to->q_user; 83294Seric host = to->q_host; 841518Seric Errors = 0; 85294Seric errno = 0; 86294Seric # ifdef DEBUG 87294Seric if (Debug) 88294Seric printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user); 89294Seric # endif DEBUG 90294Seric 91294Seric /* 92294Seric ** Remove quote bits from user/host. 93294Seric */ 94294Seric 95294Seric for (p = user; (*p++ &= 0177) != '\0'; ) 96294Seric continue; 97294Seric if (host != NULL) 98294Seric for (p = host; (*p++ &= 0177) != '\0'; ) 99294Seric continue; 100294Seric 101294Seric /* 102294Seric ** Strip quote bits from names if the mailer wants it. 103294Seric */ 104294Seric 105294Seric if (flagset(M_STRIPQ, m->m_flags)) 106294Seric { 107294Seric stripquotes(user); 108294Seric stripquotes(host); 109294Seric } 110294Seric 111294Seric /* 112294Seric ** See if this user name is "special". 113294Seric ** If the user is a program, diddle with the mailer spec. 114294Seric ** If the user name has a slash in it, assume that this 115294Seric ** is a file -- send it off without further ado. 116294Seric ** Note that this means that editfcn's will not 117294Seric ** be applied to the message. 118294Seric */ 119294Seric 120294Seric if (m == &Mailer[0]) 121294Seric { 122294Seric if (*user == '|') 123294Seric { 124294Seric user++; 125294Seric m = &Mailer[1]; 126294Seric } 127294Seric else 128294Seric { 129294Seric if (index(user, '/') != NULL) 130294Seric { 131294Seric i = mailfile(user); 132294Seric giveresponse(i, TRUE, m); 133294Seric return (i); 134294Seric } 135294Seric } 136294Seric } 137294Seric 138294Seric /* 1391389Seric ** See if the user exists. 1401389Seric ** Strictly, this is only needed to print a pretty 1411389Seric ** error message. 1421389Seric ** 1431389Seric ** >>>>>>>>>> This clause assumes that the local mailer 1441389Seric ** >> NOTE >> cannot do any further aliasing; that 1451389Seric ** >>>>>>>>>> function is subsumed by delivermail. 146294Seric */ 147294Seric 148294Seric if (m == &Mailer[0]) 149294Seric { 150294Seric if (getpwnam(user) == NULL) 151294Seric { 152294Seric giveresponse(EX_NOUSER, TRUE, m); 153294Seric return (EX_NOUSER); 154294Seric } 155294Seric } 156294Seric 157294Seric /* 158294Seric ** If the mailer wants a From line, insert a new editfcn. 159294Seric */ 160294Seric 161294Seric if (flagset(M_HDR, m->m_flags) && editfcn == NULL) 162294Seric editfcn = putheader; 163294Seric 164294Seric /* 165294Seric ** Call the mailer. 166294Seric ** The argument vector gets built, pipes through 'editfcn' 167294Seric ** are created as necessary, and we fork & exec as 168294Seric ** appropriate. In the parent, we call 'editfcn'. 169294Seric */ 170294Seric 171294Seric pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr); 172294Seric if (pvp == NULL) 173294Seric { 174294Seric usrerr("name too long"); 175294Seric return (-1); 176294Seric } 177294Seric rewind(stdin); 178294Seric 179294Seric /* create a pipe if we will need one */ 180294Seric if (editfcn != NULL && pipe(pvect) < 0) 181294Seric { 182294Seric syserr("pipe"); 183294Seric return (-1); 184294Seric } 1851504Smark # ifdef VFORK 1861504Smark pid = vfork(); 1871504Smark # else 188294Seric pid = fork(); 1891504Smark # endif 190294Seric if (pid < 0) 191294Seric { 192294Seric syserr("Cannot fork"); 193294Seric if (editfcn != NULL) 194294Seric { 195294Seric close(pvect[0]); 196294Seric close(pvect[1]); 197294Seric } 198294Seric return (-1); 199294Seric } 200294Seric else if (pid == 0) 201294Seric { 202294Seric /* child -- set up input & exec mailer */ 2031621Seric /* make diagnostic output be standard output */ 2041621Seric close(2); 2051621Seric dup(1); 206294Seric signal(SIGINT, SIG_IGN); 207294Seric if (editfcn != NULL) 208294Seric { 209294Seric close(0); 210294Seric if (dup(pvect[0]) < 0) 211294Seric { 212294Seric syserr("Cannot dup to zero!"); 2131619Seric _exit(EX_OSERR); 214294Seric } 215294Seric close(pvect[0]); 216294Seric close(pvect[1]); 217294Seric } 218294Seric if (!flagset(M_RESTR, m->m_flags)) 219294Seric setuid(getuid()); 220294Seric # ifdef LOG 221294Seric initlog(NULL, 0, LOG_CLOSE); 222294Seric # endif LOG 2231504Smark # ifndef VFORK 2241504Smark /* 2251504Smark * We have to be careful with vfork - we can't mung up the 2261504Smark * memory but we don't want the mailer to inherit any extra 2271504Smark * open files. Chances are the mailer won't 2281504Smark * care about an extra file, but then again you never know. 2291504Smark * Actually, we would like to close(fileno(pwf)), but it's 2301504Smark * declared static so we can't. But if we fclose(pwf), which 2311504Smark * is what endpwent does, it closes it in the parent too and 2321504Smark * the next getpwnam will be slower. If you have a weird mailer 2331504Smark * that chokes on the extra file you should do the endpwent(). 2341504Smark */ 235294Seric endpwent(); 2361504Smark # endif 237294Seric execv(m->m_mailer, pvp); 238294Seric /* syserr fails because log is closed */ 239294Seric /* syserr("Cannot exec %s", m->m_mailer); */ 2401619Seric _exit(EX_UNAVAILABLE); 241294Seric } 242294Seric 243294Seric /* arrange to write out header message if error */ 244294Seric if (editfcn != NULL) 245294Seric { 246294Seric close(pvect[0]); 247294Seric signal(SIGPIPE, pipesig); 248294Seric mfile = fdopen(pvect[1], "w"); 249294Seric (*editfcn)(mfile); 250294Seric fclose(mfile); 251294Seric } 252294Seric 253294Seric /* 254294Seric ** Wait for child to die and report status. 255294Seric ** We should never get fatal errors (e.g., segmentation 256294Seric ** violation), so we report those specially. For other 257294Seric ** errors, we choose a status message (into statmsg), 258294Seric ** and if it represents an error, we print it. 259294Seric */ 260294Seric 261294Seric while ((i = wait(&st)) > 0 && i != pid) 262294Seric continue; 263294Seric if (i < 0) 264294Seric { 265294Seric syserr("wait"); 266294Seric return (-1); 267294Seric } 268294Seric if ((st & 0377) != 0) 269294Seric { 270294Seric syserr("%s: stat %o", pvp[0], st); 2711597Seric ExitStat = EX_UNAVAILABLE; 272294Seric return (-1); 273294Seric } 274294Seric i = (st >> 8) & 0377; 275294Seric giveresponse(i, FALSE, m); 276294Seric return (i); 277294Seric } 278294Seric /* 279294Seric ** GIVERESPONSE -- Interpret an error response from a mailer 280294Seric ** 281294Seric ** Parameters: 282294Seric ** stat -- the status code from the mailer (high byte 283294Seric ** only; core dumps must have been taken care of 284294Seric ** already). 285294Seric ** force -- if set, force an error message output, even 286294Seric ** if the mailer seems to like to print its own 287294Seric ** messages. 288294Seric ** m -- the mailer descriptor for this mailer. 289294Seric ** 290294Seric ** Returns: 291294Seric ** none. 292294Seric ** 293294Seric ** Side Effects: 2941518Seric ** Errors may be incremented. 295294Seric ** ExitStat may be set. 296294Seric ** 297294Seric ** Called By: 298294Seric ** deliver 299294Seric */ 300294Seric 301294Seric giveresponse(stat, force, m) 302294Seric int stat; 303294Seric int force; 304294Seric register struct mailer *m; 305294Seric { 306294Seric register char *statmsg; 307294Seric extern char *SysExMsg[]; 308294Seric register int i; 309294Seric extern int N_SysEx; 310*1624Seric extern long MsgSize; 311*1624Seric char buf[30]; 312294Seric 313294Seric i = stat - EX__BASE; 314294Seric if (i < 0 || i > N_SysEx) 315294Seric statmsg = NULL; 316294Seric else 317294Seric statmsg = SysExMsg[i]; 318294Seric if (stat == 0) 319294Seric statmsg = "ok"; 320294Seric else 321294Seric { 3221518Seric Errors++; 323294Seric if (statmsg == NULL && m->m_badstat != 0) 324294Seric { 325294Seric stat = m->m_badstat; 326294Seric i = stat - EX__BASE; 327294Seric # ifdef DEBUG 328294Seric if (i < 0 || i >= N_SysEx) 329294Seric syserr("Bad m_badstat %d", stat); 330294Seric else 331294Seric # endif DEBUG 332294Seric statmsg = SysExMsg[i]; 333294Seric } 334294Seric if (statmsg == NULL) 335294Seric usrerr("unknown mailer response %d", stat); 336294Seric else if (force || !flagset(M_QUIET, m->m_flags)) 337294Seric usrerr("%s", statmsg); 338294Seric } 339294Seric 340294Seric /* 341294Seric ** Final cleanup. 342294Seric ** Log a record of the transaction. Compute the new 343294Seric ** ExitStat -- if we already had an error, stick with 344294Seric ** that. 345294Seric */ 346294Seric 347*1624Seric if (statmsg == NULL) 348*1624Seric { 349*1624Seric sprintf(buf, "error %d", stat); 350*1624Seric statmsg = buf; 351*1624Seric } 352*1624Seric 353294Seric # ifdef LOG 354*1624Seric logmsg(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg); 355294Seric # endif LOG 3561389Seric setstat(stat); 357294Seric return (stat); 358294Seric } 359294Seric /* 360294Seric ** PUTHEADER -- insert the From header into some mail 361294Seric ** 362294Seric ** For mailers such as 'msgs' that want the header inserted 363294Seric ** into the mail, this edit filter inserts the From line and 364294Seric ** then passes the rest of the message through. 365294Seric ** 366294Seric ** Parameters: 367294Seric ** fp -- the file pointer for the output. 368294Seric ** 369294Seric ** Returns: 370294Seric ** none 371294Seric ** 372294Seric ** Side Effects: 373294Seric ** Puts a "From" line in UNIX format, and then 374294Seric ** outputs the rest of the message. 375294Seric ** 376294Seric ** Called By: 377294Seric ** deliver 378294Seric */ 379294Seric 380294Seric putheader(fp) 381294Seric register FILE *fp; 382294Seric { 383294Seric char buf[MAXLINE + 1]; 384294Seric long tim; 385294Seric extern char *ctime(); 386294Seric 387294Seric time(&tim); 388294Seric fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim)); 389294Seric while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp)) 390294Seric fputs(buf, fp); 391294Seric if (ferror(fp)) 392294Seric { 393294Seric syserr("putheader: write error"); 394294Seric setstat(EX_IOERR); 395294Seric } 396294Seric } 397294Seric /* 398294Seric ** PIPESIG -- Handle broken pipe signals 399294Seric ** 400294Seric ** This just logs an error. 401294Seric ** 402294Seric ** Parameters: 403294Seric ** none 404294Seric ** 405294Seric ** Returns: 406294Seric ** none 407294Seric ** 408294Seric ** Side Effects: 409294Seric ** logs an error message. 410294Seric */ 411294Seric 412294Seric pipesig() 413294Seric { 414294Seric syserr("Broken pipe"); 4151621Seric signal(SIGPIPE, SIG_IGN); 416294Seric } 417294Seric /* 418294Seric ** SENDTO -- Designate a send list. 419294Seric ** 420294Seric ** The parameter is a comma-separated list of people to send to. 421294Seric ** This routine arranges to send to all of them. 422294Seric ** 423294Seric ** Parameters: 424294Seric ** list -- the send list. 425294Seric ** copyf -- the copy flag; passed to parse. 426294Seric ** 427294Seric ** Returns: 428294Seric ** none 429294Seric ** 430294Seric ** Side Effects: 431294Seric ** none. 432294Seric ** 433294Seric ** Called By: 434294Seric ** main 435294Seric ** alias 436294Seric */ 437294Seric 438294Seric sendto(list, copyf) 439294Seric char *list; 440294Seric int copyf; 441294Seric { 442294Seric register char *p; 443294Seric register char *q; 444294Seric register char c; 445294Seric addrq *a; 446294Seric extern addrq *parse(); 447294Seric bool more; 448294Seric 449294Seric /* more keeps track of what the previous delimiter was */ 450294Seric more = TRUE; 451294Seric for (p = list; more; ) 452294Seric { 453294Seric /* find the end of this address */ 454294Seric q = p; 455294Seric while ((c = *p++) != '\0' && c != ',' && c != '\n') 456294Seric continue; 457294Seric more = c != '\0'; 458294Seric *--p = '\0'; 459294Seric if (more) 460294Seric p++; 461294Seric 462294Seric /* parse the address */ 463294Seric if ((a = parse(q, (addrq *) NULL, copyf)) == NULL) 464294Seric continue; 465294Seric 466294Seric /* arrange to send to this person */ 467294Seric recipient(a, &SendQ); 468294Seric } 469294Seric To = NULL; 470294Seric } 471294Seric /* 472294Seric ** RECIPIENT -- Designate a message recipient 473294Seric ** 474294Seric ** Saves the named person for future mailing. 475294Seric ** 476294Seric ** Designates a person as a recipient. This routine 477294Seric ** does the initial parsing, and checks to see if 478294Seric ** this person has already received the mail. 479294Seric ** It also supresses local network names and turns them into 480294Seric ** local names. 481294Seric ** 482294Seric ** Parameters: 483294Seric ** a -- the (preparsed) address header for the recipient. 484294Seric ** targetq -- the queue to add the name to. 485294Seric ** 486294Seric ** Returns: 487294Seric ** none. 488294Seric ** 489294Seric ** Side Effects: 490294Seric ** none. 491294Seric ** 492294Seric ** Called By: 493294Seric ** sendto 494294Seric ** main 495294Seric */ 496294Seric 497294Seric recipient(a, targetq) 498294Seric register addrq *a; 499294Seric addrq *targetq; 500294Seric { 501294Seric register addrq *q; 502294Seric register struct mailer *m; 503294Seric register char **pvp; 504294Seric extern char *xalloc(); 505294Seric extern bool forward(); 506294Seric extern int errno; 507294Seric extern bool sameaddr(); 508294Seric 509294Seric To = a->q_paddr; 510294Seric m = a->q_mailer; 511294Seric errno = 0; 512294Seric # ifdef DEBUG 513294Seric if (Debug) 514294Seric printf("recipient(%s)\n", To); 515294Seric # endif DEBUG 516294Seric 517294Seric /* 518294Seric ** Look up this person in the recipient list. If they 519294Seric ** are there already, return, otherwise continue. 520294Seric */ 521294Seric 522294Seric if (!ForceMail) 523294Seric { 524294Seric for (q = &SendQ; (q = nxtinq(q)) != NULL; ) 525294Seric if (sameaddr(q, a, FALSE)) 526294Seric { 527294Seric # ifdef DEBUG 528294Seric if (Debug) 529294Seric printf("(%s in SendQ)\n", a->q_paddr); 530294Seric # endif DEBUG 531294Seric return; 532294Seric } 533294Seric for (q = &AliasQ; (q = nxtinq(q)) != NULL; ) 534294Seric if (sameaddr(q, a, FALSE)) 535294Seric { 536294Seric # ifdef DEBUG 537294Seric if (Debug) 538294Seric printf("(%s in AliasQ)\n", a->q_paddr); 539294Seric # endif DEBUG 540294Seric return; 541294Seric } 542294Seric } 543294Seric 544294Seric /* 545294Seric ** See if the user wants hir mail forwarded. 546294Seric ** `Forward' must do the forwarding recursively. 547294Seric */ 548294Seric 549294Seric if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a)) 550294Seric return; 551294Seric 552294Seric /* 553294Seric ** Put the user onto the target queue. 554294Seric */ 555294Seric 556294Seric if (targetq != NULL) 557294Seric { 558294Seric putonq(a, targetq); 559294Seric } 560294Seric 561294Seric return; 562294Seric } 563294Seric /* 564294Seric ** BUILDARGV -- Build an argument vector for a mail server. 565294Seric ** 566294Seric ** Using a template defined in config.c, an argv is built. 567294Seric ** The format of the template is already a vector. The 568294Seric ** items of this vector are copied, unless a dollar sign 569294Seric ** is encountered. In this case, the next character 570294Seric ** specifies something else to copy in. These can be 571294Seric ** $f The from address. 572294Seric ** $h The host. 573294Seric ** $u The user. 574294Seric ** $c The hop count. 575294Seric ** The vector is built in a local buffer. A pointer to 576294Seric ** the static argv is returned. 577294Seric ** 578294Seric ** Parameters: 579294Seric ** tmplt -- a template for an argument vector. 580294Seric ** flags -- the flags for this server. 581294Seric ** host -- the host name to send to. 582294Seric ** user -- the user name to send to. 583294Seric ** from -- the person this mail is from. 584294Seric ** 585294Seric ** Returns: 586294Seric ** A pointer to an argv. 587294Seric ** 588294Seric ** Side Effects: 589294Seric ** none 590294Seric ** 591294Seric ** WARNING: 592294Seric ** Since the argv is staticly allocated, any subsequent 593294Seric ** calls will clobber the old argv. 594294Seric ** 595294Seric ** Called By: 596294Seric ** deliver 597294Seric */ 598294Seric 599294Seric char ** 600294Seric buildargv(tmplt, flags, host, user, from) 601294Seric char **tmplt; 602294Seric int flags; 603294Seric char *host; 604294Seric char *user; 605294Seric char *from; 606294Seric { 607294Seric register char *p; 608294Seric register char *q; 609294Seric static char *pv[MAXPV+1]; 610294Seric char **pvp; 611294Seric char **mvp; 612294Seric static char buf[512]; 613294Seric register char *bp; 614294Seric char pbuf[30]; 615294Seric 616294Seric /* 617294Seric ** Do initial argv setup. 618294Seric ** Insert the mailer name. Notice that $x expansion is 619294Seric ** NOT done on the mailer name. Then, if the mailer has 620294Seric ** a picky -f flag, we insert it as appropriate. This 621294Seric ** code does not check for 'pv' overflow; this places a 622294Seric ** manifest lower limit of 4 for MAXPV. 623294Seric */ 624294Seric 625294Seric pvp = pv; 626294Seric bp = buf; 627294Seric 628294Seric *pvp++ = tmplt[0]; 629294Seric 630294Seric /* insert -f or -r flag as appropriate */ 631294Seric if (flagset(M_FOPT|M_ROPT, flags) && FromFlag) 632294Seric { 633294Seric if (flagset(M_FOPT, flags)) 634294Seric *pvp++ = "-f"; 635294Seric else 636294Seric *pvp++ = "-r"; 637294Seric *pvp++ = From.q_paddr; 638294Seric } 639294Seric 640294Seric /* 641294Seric ** Build the rest of argv. 642294Seric ** For each prototype parameter, the prototype is 643294Seric ** scanned character at a time. If a dollar-sign is 644294Seric ** found, 'q' is set to the appropriate expansion, 645294Seric ** otherwise it is null. Then either the string 646294Seric ** pointed to by q, or the original character, is 647294Seric ** interpolated into the buffer. Buffer overflow is 648294Seric ** checked. 649294Seric */ 650294Seric 651294Seric for (mvp = tmplt; (p = *++mvp) != NULL; ) 652294Seric { 653294Seric if (pvp >= &pv[MAXPV]) 654294Seric { 655294Seric syserr("Too many parameters to %s", pv[0]); 656294Seric return (NULL); 657294Seric } 658294Seric *pvp++ = bp; 659294Seric for (; *p != '\0'; p++) 660294Seric { 661294Seric /* q will be the interpolated quantity */ 662294Seric q = NULL; 663294Seric if (*p == '$') 664294Seric { 665294Seric switch (*++p) 666294Seric { 667294Seric case 'f': /* from person */ 668294Seric q = from; 669294Seric break; 670294Seric 671294Seric case 'u': /* user */ 672294Seric q = user; 673294Seric break; 674294Seric 675294Seric case 'h': /* host */ 676294Seric q = host; 677294Seric break; 678294Seric 679294Seric case 'c': /* hop count */ 680294Seric sprintf(pbuf, "%d", HopCount); 681294Seric q = pbuf; 682294Seric break; 683294Seric } 684294Seric } 685294Seric 686294Seric /* 687294Seric ** Interpolate q or output one character 688294Seric ** Strip quote bits as we proceed..... 689294Seric */ 690294Seric 691294Seric if (q != NULL) 692294Seric { 693294Seric while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0') 694294Seric continue; 695294Seric bp--; 696294Seric } 697294Seric else if (bp < &buf[sizeof buf - 1]) 698294Seric *bp++ = *p; 699294Seric } 700294Seric *bp++ = '\0'; 701294Seric if (bp >= &buf[sizeof buf - 1]) 702294Seric return (NULL); 703294Seric } 704294Seric *pvp = NULL; 705294Seric 706294Seric # ifdef DEBUG 707294Seric if (Debug) 708294Seric { 709294Seric printf("Interpolated argv is:\n"); 710294Seric for (mvp = pv; *mvp != NULL; mvp++) 711294Seric printf("\t%s\n", *mvp); 712294Seric } 713294Seric # endif DEBUG 714294Seric 715294Seric return (pv); 716294Seric } 717294Seric /* 718294Seric ** MAILFILE -- Send a message to a file. 719294Seric ** 720294Seric ** Parameters: 721294Seric ** filename -- the name of the file to send to. 722294Seric ** 723294Seric ** Returns: 724294Seric ** The exit code associated with the operation. 725294Seric ** 726294Seric ** Side Effects: 727294Seric ** none. 728294Seric ** 729294Seric ** Called By: 730294Seric ** deliver 731294Seric */ 732294Seric 733294Seric mailfile(filename) 734294Seric char *filename; 735294Seric { 736294Seric char buf[MAXLINE]; 737294Seric register FILE *f; 738294Seric auto long tim; 739294Seric extern char *ctime(); 740294Seric 741294Seric f = fopen(filename, "a"); 742294Seric if (f == NULL) 743294Seric return (EX_CANTCREAT); 744294Seric 745294Seric /* output the timestamp */ 746294Seric time(&tim); 747294Seric fprintf(f, "From %s %s", From.q_paddr, ctime(&tim)); 748294Seric rewind(stdin); 749294Seric while (fgets(buf, sizeof buf, stdin) != NULL) 750294Seric { 751294Seric fputs(buf, f); 752294Seric if (ferror(f)) 753294Seric { 754294Seric fclose(f); 755294Seric return (EX_IOERR); 756294Seric } 757294Seric } 758294Seric fputs("\n", f); 759294Seric fclose(f); 760294Seric return (EX_OK); 761294Seric } 762