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