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*1389Seric static char SccsId[] = "@(#)deliver.c 1.5 10/11/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; 84294Seric Error = 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 /* 139*1389Seric ** See if the user exists. 140*1389Seric ** Strictly, this is only needed to print a pretty 141*1389Seric ** error message. 142*1389Seric ** 143*1389Seric ** >>>>>>>>>> This clause assumes that the local mailer 144*1389Seric ** >> NOTE >> cannot do any further aliasing; that 145*1389Seric ** >>>>>>>>>> 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 } 185294Seric pid = fork(); 186294Seric if (pid < 0) 187294Seric { 188294Seric syserr("Cannot fork"); 189294Seric if (editfcn != NULL) 190294Seric { 191294Seric close(pvect[0]); 192294Seric close(pvect[1]); 193294Seric } 194294Seric return (-1); 195294Seric } 196294Seric else if (pid == 0) 197294Seric { 198294Seric /* child -- set up input & exec mailer */ 199294Seric signal(SIGINT, SIG_IGN); 200294Seric if (editfcn != NULL) 201294Seric { 202294Seric close(0); 203294Seric if (dup(pvect[0]) < 0) 204294Seric { 205294Seric syserr("Cannot dup to zero!"); 206294Seric exit(EX_OSERR); 207294Seric } 208294Seric close(pvect[0]); 209294Seric close(pvect[1]); 210294Seric } 211294Seric if (!flagset(M_RESTR, m->m_flags)) 212294Seric setuid(getuid()); 213294Seric # ifdef LOG 214294Seric initlog(NULL, 0, LOG_CLOSE); 215294Seric # endif LOG 216294Seric endpwent(); 217294Seric execv(m->m_mailer, pvp); 218294Seric /* syserr fails because log is closed */ 219294Seric /* syserr("Cannot exec %s", m->m_mailer); */ 220294Seric exit(EX_UNAVAIL); 221294Seric } 222294Seric 223294Seric /* arrange to write out header message if error */ 224294Seric if (editfcn != NULL) 225294Seric { 226294Seric close(pvect[0]); 227294Seric signal(SIGPIPE, pipesig); 228294Seric mfile = fdopen(pvect[1], "w"); 229294Seric (*editfcn)(mfile); 230294Seric fclose(mfile); 231294Seric } 232294Seric 233294Seric /* 234294Seric ** Wait for child to die and report status. 235294Seric ** We should never get fatal errors (e.g., segmentation 236294Seric ** violation), so we report those specially. For other 237294Seric ** errors, we choose a status message (into statmsg), 238294Seric ** and if it represents an error, we print it. 239294Seric */ 240294Seric 241294Seric while ((i = wait(&st)) > 0 && i != pid) 242294Seric continue; 243294Seric if (i < 0) 244294Seric { 245294Seric syserr("wait"); 246294Seric return (-1); 247294Seric } 248294Seric if ((st & 0377) != 0) 249294Seric { 250294Seric syserr("%s: stat %o", pvp[0], st); 251294Seric ExitStat = EX_UNAVAIL; 252294Seric return (-1); 253294Seric } 254294Seric i = (st >> 8) & 0377; 255294Seric giveresponse(i, FALSE, m); 256294Seric return (i); 257294Seric } 258294Seric /* 259294Seric ** GIVERESPONSE -- Interpret an error response from a mailer 260294Seric ** 261294Seric ** Parameters: 262294Seric ** stat -- the status code from the mailer (high byte 263294Seric ** only; core dumps must have been taken care of 264294Seric ** already). 265294Seric ** force -- if set, force an error message output, even 266294Seric ** if the mailer seems to like to print its own 267294Seric ** messages. 268294Seric ** m -- the mailer descriptor for this mailer. 269294Seric ** 270294Seric ** Returns: 271294Seric ** none. 272294Seric ** 273294Seric ** Side Effects: 274294Seric ** Error may be set. 275294Seric ** ExitStat may be set. 276294Seric ** 277294Seric ** Called By: 278294Seric ** deliver 279294Seric */ 280294Seric 281294Seric giveresponse(stat, force, m) 282294Seric int stat; 283294Seric int force; 284294Seric register struct mailer *m; 285294Seric { 286294Seric register char *statmsg; 287294Seric extern char *SysExMsg[]; 288294Seric register int i; 289294Seric extern int N_SysEx; 290294Seric 291294Seric i = stat - EX__BASE; 292294Seric if (i < 0 || i > N_SysEx) 293294Seric statmsg = NULL; 294294Seric else 295294Seric statmsg = SysExMsg[i]; 296294Seric if (stat == 0) 297294Seric statmsg = "ok"; 298294Seric else 299294Seric { 300294Seric Error++; 301294Seric if (statmsg == NULL && m->m_badstat != 0) 302294Seric { 303294Seric stat = m->m_badstat; 304294Seric i = stat - EX__BASE; 305294Seric # ifdef DEBUG 306294Seric if (i < 0 || i >= N_SysEx) 307294Seric syserr("Bad m_badstat %d", stat); 308294Seric else 309294Seric # endif DEBUG 310294Seric statmsg = SysExMsg[i]; 311294Seric } 312294Seric if (statmsg == NULL) 313294Seric usrerr("unknown mailer response %d", stat); 314294Seric else if (force || !flagset(M_QUIET, m->m_flags)) 315294Seric usrerr("%s", statmsg); 316294Seric } 317294Seric 318294Seric /* 319294Seric ** Final cleanup. 320294Seric ** Log a record of the transaction. Compute the new 321294Seric ** ExitStat -- if we already had an error, stick with 322294Seric ** that. 323294Seric */ 324294Seric 325294Seric # ifdef LOG 326294Seric if (statmsg == NULL) 327294Seric logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat); 328294Seric else 329294Seric logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg); 330294Seric # endif LOG 331*1389Seric setstat(stat); 332294Seric return (stat); 333294Seric } 334294Seric /* 335294Seric ** PUTHEADER -- insert the From header into some mail 336294Seric ** 337294Seric ** For mailers such as 'msgs' that want the header inserted 338294Seric ** into the mail, this edit filter inserts the From line and 339294Seric ** then passes the rest of the message through. 340294Seric ** 341294Seric ** Parameters: 342294Seric ** fp -- the file pointer for the output. 343294Seric ** 344294Seric ** Returns: 345294Seric ** none 346294Seric ** 347294Seric ** Side Effects: 348294Seric ** Puts a "From" line in UNIX format, and then 349294Seric ** outputs the rest of the message. 350294Seric ** 351294Seric ** Called By: 352294Seric ** deliver 353294Seric */ 354294Seric 355294Seric putheader(fp) 356294Seric register FILE *fp; 357294Seric { 358294Seric char buf[MAXLINE + 1]; 359294Seric long tim; 360294Seric extern char *ctime(); 361294Seric 362294Seric time(&tim); 363294Seric fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim)); 364294Seric while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp)) 365294Seric fputs(buf, fp); 366294Seric if (ferror(fp)) 367294Seric { 368294Seric syserr("putheader: write error"); 369294Seric setstat(EX_IOERR); 370294Seric } 371294Seric } 372294Seric /* 373294Seric ** PIPESIG -- Handle broken pipe signals 374294Seric ** 375294Seric ** This just logs an error. 376294Seric ** 377294Seric ** Parameters: 378294Seric ** none 379294Seric ** 380294Seric ** Returns: 381294Seric ** none 382294Seric ** 383294Seric ** Side Effects: 384294Seric ** logs an error message. 385294Seric */ 386294Seric 387294Seric pipesig() 388294Seric { 389294Seric syserr("Broken pipe"); 390294Seric } 391294Seric /* 392294Seric ** SENDTO -- Designate a send list. 393294Seric ** 394294Seric ** The parameter is a comma-separated list of people to send to. 395294Seric ** This routine arranges to send to all of them. 396294Seric ** 397294Seric ** Parameters: 398294Seric ** list -- the send list. 399294Seric ** copyf -- the copy flag; passed to parse. 400294Seric ** 401294Seric ** Returns: 402294Seric ** none 403294Seric ** 404294Seric ** Side Effects: 405294Seric ** none. 406294Seric ** 407294Seric ** Called By: 408294Seric ** main 409294Seric ** alias 410294Seric */ 411294Seric 412294Seric sendto(list, copyf) 413294Seric char *list; 414294Seric int copyf; 415294Seric { 416294Seric register char *p; 417294Seric register char *q; 418294Seric register char c; 419294Seric addrq *a; 420294Seric extern addrq *parse(); 421294Seric bool more; 422294Seric 423294Seric /* more keeps track of what the previous delimiter was */ 424294Seric more = TRUE; 425294Seric for (p = list; more; ) 426294Seric { 427294Seric /* find the end of this address */ 428294Seric q = p; 429294Seric while ((c = *p++) != '\0' && c != ',' && c != '\n') 430294Seric continue; 431294Seric more = c != '\0'; 432294Seric *--p = '\0'; 433294Seric if (more) 434294Seric p++; 435294Seric 436294Seric /* parse the address */ 437294Seric if ((a = parse(q, (addrq *) NULL, copyf)) == NULL) 438294Seric continue; 439294Seric 440294Seric /* arrange to send to this person */ 441294Seric recipient(a, &SendQ); 442294Seric } 443294Seric To = NULL; 444294Seric } 445294Seric /* 446294Seric ** RECIPIENT -- Designate a message recipient 447294Seric ** 448294Seric ** Saves the named person for future mailing. 449294Seric ** 450294Seric ** Designates a person as a recipient. This routine 451294Seric ** does the initial parsing, and checks to see if 452294Seric ** this person has already received the mail. 453294Seric ** It also supresses local network names and turns them into 454294Seric ** local names. 455294Seric ** 456294Seric ** Parameters: 457294Seric ** a -- the (preparsed) address header for the recipient. 458294Seric ** targetq -- the queue to add the name to. 459294Seric ** 460294Seric ** Returns: 461294Seric ** none. 462294Seric ** 463294Seric ** Side Effects: 464294Seric ** none. 465294Seric ** 466294Seric ** Called By: 467294Seric ** sendto 468294Seric ** main 469294Seric */ 470294Seric 471294Seric recipient(a, targetq) 472294Seric register addrq *a; 473294Seric addrq *targetq; 474294Seric { 475294Seric register addrq *q; 476294Seric register struct mailer *m; 477294Seric register char **pvp; 478294Seric extern char *xalloc(); 479294Seric extern bool forward(); 480294Seric extern int errno; 481294Seric extern bool sameaddr(); 482294Seric 483294Seric To = a->q_paddr; 484294Seric m = a->q_mailer; 485294Seric errno = 0; 486294Seric # ifdef DEBUG 487294Seric if (Debug) 488294Seric printf("recipient(%s)\n", To); 489294Seric # endif DEBUG 490294Seric 491294Seric /* 492294Seric ** Don't go to the net if already on the target host. 493294Seric ** This is important on the berkeley network, since 494294Seric ** it get confused if we ask to send to ourselves. 495294Seric ** For nets like the ARPANET, we probably will have 496294Seric ** the local list set to NULL to simplify testing. 497294Seric ** The canonical representation of the name is also set 498294Seric ** to be just the local name so the duplicate letter 499294Seric ** suppression algorithm will work. 500294Seric */ 501294Seric 502294Seric if ((pvp = m->m_local) != NULL) 503294Seric { 504294Seric while (*pvp != NULL) 505294Seric { 506294Seric if (strcmp(*pvp++, a->q_host) == 0) 507294Seric { 508294Seric a->q_mailer = m = &Mailer[0]; 509294Seric break; 510294Seric } 511294Seric } 512294Seric } 513294Seric 514294Seric /* 515294Seric ** Look up this person in the recipient list. If they 516294Seric ** are there already, return, otherwise continue. 517294Seric */ 518294Seric 519294Seric if (!ForceMail) 520294Seric { 521294Seric for (q = &SendQ; (q = nxtinq(q)) != NULL; ) 522294Seric if (sameaddr(q, a, FALSE)) 523294Seric { 524294Seric # ifdef DEBUG 525294Seric if (Debug) 526294Seric printf("(%s in SendQ)\n", a->q_paddr); 527294Seric # endif DEBUG 528294Seric return; 529294Seric } 530294Seric for (q = &AliasQ; (q = nxtinq(q)) != NULL; ) 531294Seric if (sameaddr(q, a, FALSE)) 532294Seric { 533294Seric # ifdef DEBUG 534294Seric if (Debug) 535294Seric printf("(%s in AliasQ)\n", a->q_paddr); 536294Seric # endif DEBUG 537294Seric return; 538294Seric } 539294Seric } 540294Seric 541294Seric /* 542294Seric ** See if the user wants hir mail forwarded. 543294Seric ** `Forward' must do the forwarding recursively. 544294Seric */ 545294Seric 546294Seric if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a)) 547294Seric return; 548294Seric 549294Seric /* 550294Seric ** Put the user onto the target queue. 551294Seric */ 552294Seric 553294Seric if (targetq != NULL) 554294Seric { 555294Seric putonq(a, targetq); 556294Seric } 557294Seric 558294Seric return; 559294Seric } 560294Seric /* 561294Seric ** BUILDARGV -- Build an argument vector for a mail server. 562294Seric ** 563294Seric ** Using a template defined in config.c, an argv is built. 564294Seric ** The format of the template is already a vector. The 565294Seric ** items of this vector are copied, unless a dollar sign 566294Seric ** is encountered. In this case, the next character 567294Seric ** specifies something else to copy in. These can be 568294Seric ** $f The from address. 569294Seric ** $h The host. 570294Seric ** $u The user. 571294Seric ** $c The hop count. 572294Seric ** The vector is built in a local buffer. A pointer to 573294Seric ** the static argv is returned. 574294Seric ** 575294Seric ** Parameters: 576294Seric ** tmplt -- a template for an argument vector. 577294Seric ** flags -- the flags for this server. 578294Seric ** host -- the host name to send to. 579294Seric ** user -- the user name to send to. 580294Seric ** from -- the person this mail is from. 581294Seric ** 582294Seric ** Returns: 583294Seric ** A pointer to an argv. 584294Seric ** 585294Seric ** Side Effects: 586294Seric ** none 587294Seric ** 588294Seric ** WARNING: 589294Seric ** Since the argv is staticly allocated, any subsequent 590294Seric ** calls will clobber the old argv. 591294Seric ** 592294Seric ** Called By: 593294Seric ** deliver 594294Seric */ 595294Seric 596294Seric char ** 597294Seric buildargv(tmplt, flags, host, user, from) 598294Seric char **tmplt; 599294Seric int flags; 600294Seric char *host; 601294Seric char *user; 602294Seric char *from; 603294Seric { 604294Seric register char *p; 605294Seric register char *q; 606294Seric static char *pv[MAXPV+1]; 607294Seric char **pvp; 608294Seric char **mvp; 609294Seric static char buf[512]; 610294Seric register char *bp; 611294Seric char pbuf[30]; 612294Seric 613294Seric /* 614294Seric ** Do initial argv setup. 615294Seric ** Insert the mailer name. Notice that $x expansion is 616294Seric ** NOT done on the mailer name. Then, if the mailer has 617294Seric ** a picky -f flag, we insert it as appropriate. This 618294Seric ** code does not check for 'pv' overflow; this places a 619294Seric ** manifest lower limit of 4 for MAXPV. 620294Seric */ 621294Seric 622294Seric pvp = pv; 623294Seric bp = buf; 624294Seric 625294Seric *pvp++ = tmplt[0]; 626294Seric 627294Seric /* insert -f or -r flag as appropriate */ 628294Seric if (flagset(M_FOPT|M_ROPT, flags) && FromFlag) 629294Seric { 630294Seric if (flagset(M_FOPT, flags)) 631294Seric *pvp++ = "-f"; 632294Seric else 633294Seric *pvp++ = "-r"; 634294Seric *pvp++ = From.q_paddr; 635294Seric } 636294Seric 637294Seric /* 638294Seric ** Build the rest of argv. 639294Seric ** For each prototype parameter, the prototype is 640294Seric ** scanned character at a time. If a dollar-sign is 641294Seric ** found, 'q' is set to the appropriate expansion, 642294Seric ** otherwise it is null. Then either the string 643294Seric ** pointed to by q, or the original character, is 644294Seric ** interpolated into the buffer. Buffer overflow is 645294Seric ** checked. 646294Seric */ 647294Seric 648294Seric for (mvp = tmplt; (p = *++mvp) != NULL; ) 649294Seric { 650294Seric if (pvp >= &pv[MAXPV]) 651294Seric { 652294Seric syserr("Too many parameters to %s", pv[0]); 653294Seric return (NULL); 654294Seric } 655294Seric *pvp++ = bp; 656294Seric for (; *p != '\0'; p++) 657294Seric { 658294Seric /* q will be the interpolated quantity */ 659294Seric q = NULL; 660294Seric if (*p == '$') 661294Seric { 662294Seric switch (*++p) 663294Seric { 664294Seric case 'f': /* from person */ 665294Seric q = from; 666294Seric break; 667294Seric 668294Seric case 'u': /* user */ 669294Seric q = user; 670294Seric break; 671294Seric 672294Seric case 'h': /* host */ 673294Seric q = host; 674294Seric break; 675294Seric 676294Seric case 'c': /* hop count */ 677294Seric sprintf(pbuf, "%d", HopCount); 678294Seric q = pbuf; 679294Seric break; 680294Seric } 681294Seric } 682294Seric 683294Seric /* 684294Seric ** Interpolate q or output one character 685294Seric ** Strip quote bits as we proceed..... 686294Seric */ 687294Seric 688294Seric if (q != NULL) 689294Seric { 690294Seric while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0') 691294Seric continue; 692294Seric bp--; 693294Seric } 694294Seric else if (bp < &buf[sizeof buf - 1]) 695294Seric *bp++ = *p; 696294Seric } 697294Seric *bp++ = '\0'; 698294Seric if (bp >= &buf[sizeof buf - 1]) 699294Seric return (NULL); 700294Seric } 701294Seric *pvp = NULL; 702294Seric 703294Seric # ifdef DEBUG 704294Seric if (Debug) 705294Seric { 706294Seric printf("Interpolated argv is:\n"); 707294Seric for (mvp = pv; *mvp != NULL; mvp++) 708294Seric printf("\t%s\n", *mvp); 709294Seric } 710294Seric # endif DEBUG 711294Seric 712294Seric return (pv); 713294Seric } 714294Seric /* 715294Seric ** MAILFILE -- Send a message to a file. 716294Seric ** 717294Seric ** Parameters: 718294Seric ** filename -- the name of the file to send to. 719294Seric ** 720294Seric ** Returns: 721294Seric ** The exit code associated with the operation. 722294Seric ** 723294Seric ** Side Effects: 724294Seric ** none. 725294Seric ** 726294Seric ** Called By: 727294Seric ** deliver 728294Seric */ 729294Seric 730294Seric mailfile(filename) 731294Seric char *filename; 732294Seric { 733294Seric char buf[MAXLINE]; 734294Seric register FILE *f; 735294Seric auto long tim; 736294Seric extern char *ctime(); 737294Seric 738294Seric f = fopen(filename, "a"); 739294Seric if (f == NULL) 740294Seric return (EX_CANTCREAT); 741294Seric 742294Seric /* output the timestamp */ 743294Seric time(&tim); 744294Seric fprintf(f, "From %s %s", From.q_paddr, ctime(&tim)); 745294Seric rewind(stdin); 746294Seric while (fgets(buf, sizeof buf, stdin) != NULL) 747294Seric { 748294Seric fputs(buf, f); 749294Seric if (ferror(f)) 750294Seric { 751294Seric fclose(f); 752294Seric return (EX_IOERR); 753294Seric } 754294Seric } 755294Seric fputs("\n", f); 756294Seric fclose(f); 757294Seric return (EX_OK); 758294Seric } 759