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*1621Seric static char SccsId[] = "@(#)deliver.c 1.10 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 */ 203*1621Seric /* make diagnostic output be standard output */ 204*1621Seric close(2); 205*1621Seric 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; 310294Seric 311294Seric i = stat - EX__BASE; 312294Seric if (i < 0 || i > N_SysEx) 313294Seric statmsg = NULL; 314294Seric else 315294Seric statmsg = SysExMsg[i]; 316294Seric if (stat == 0) 317294Seric statmsg = "ok"; 318294Seric else 319294Seric { 3201518Seric Errors++; 321294Seric if (statmsg == NULL && m->m_badstat != 0) 322294Seric { 323294Seric stat = m->m_badstat; 324294Seric i = stat - EX__BASE; 325294Seric # ifdef DEBUG 326294Seric if (i < 0 || i >= N_SysEx) 327294Seric syserr("Bad m_badstat %d", stat); 328294Seric else 329294Seric # endif DEBUG 330294Seric statmsg = SysExMsg[i]; 331294Seric } 332294Seric if (statmsg == NULL) 333294Seric usrerr("unknown mailer response %d", stat); 334294Seric else if (force || !flagset(M_QUIET, m->m_flags)) 335294Seric usrerr("%s", statmsg); 336294Seric } 337294Seric 338294Seric /* 339294Seric ** Final cleanup. 340294Seric ** Log a record of the transaction. Compute the new 341294Seric ** ExitStat -- if we already had an error, stick with 342294Seric ** that. 343294Seric */ 344294Seric 345294Seric # ifdef LOG 346294Seric if (statmsg == NULL) 347294Seric logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat); 348294Seric else 349294Seric logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg); 350294Seric # endif LOG 3511389Seric setstat(stat); 352294Seric return (stat); 353294Seric } 354294Seric /* 355294Seric ** PUTHEADER -- insert the From header into some mail 356294Seric ** 357294Seric ** For mailers such as 'msgs' that want the header inserted 358294Seric ** into the mail, this edit filter inserts the From line and 359294Seric ** then passes the rest of the message through. 360294Seric ** 361294Seric ** Parameters: 362294Seric ** fp -- the file pointer for the output. 363294Seric ** 364294Seric ** Returns: 365294Seric ** none 366294Seric ** 367294Seric ** Side Effects: 368294Seric ** Puts a "From" line in UNIX format, and then 369294Seric ** outputs the rest of the message. 370294Seric ** 371294Seric ** Called By: 372294Seric ** deliver 373294Seric */ 374294Seric 375294Seric putheader(fp) 376294Seric register FILE *fp; 377294Seric { 378294Seric char buf[MAXLINE + 1]; 379294Seric long tim; 380294Seric extern char *ctime(); 381294Seric 382294Seric time(&tim); 383294Seric fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim)); 384294Seric while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp)) 385294Seric fputs(buf, fp); 386294Seric if (ferror(fp)) 387294Seric { 388294Seric syserr("putheader: write error"); 389294Seric setstat(EX_IOERR); 390294Seric } 391294Seric } 392294Seric /* 393294Seric ** PIPESIG -- Handle broken pipe signals 394294Seric ** 395294Seric ** This just logs an error. 396294Seric ** 397294Seric ** Parameters: 398294Seric ** none 399294Seric ** 400294Seric ** Returns: 401294Seric ** none 402294Seric ** 403294Seric ** Side Effects: 404294Seric ** logs an error message. 405294Seric */ 406294Seric 407294Seric pipesig() 408294Seric { 409294Seric syserr("Broken pipe"); 410*1621Seric signal(SIGPIPE, SIG_IGN); 411294Seric } 412294Seric /* 413294Seric ** SENDTO -- Designate a send list. 414294Seric ** 415294Seric ** The parameter is a comma-separated list of people to send to. 416294Seric ** This routine arranges to send to all of them. 417294Seric ** 418294Seric ** Parameters: 419294Seric ** list -- the send list. 420294Seric ** copyf -- the copy flag; passed to parse. 421294Seric ** 422294Seric ** Returns: 423294Seric ** none 424294Seric ** 425294Seric ** Side Effects: 426294Seric ** none. 427294Seric ** 428294Seric ** Called By: 429294Seric ** main 430294Seric ** alias 431294Seric */ 432294Seric 433294Seric sendto(list, copyf) 434294Seric char *list; 435294Seric int copyf; 436294Seric { 437294Seric register char *p; 438294Seric register char *q; 439294Seric register char c; 440294Seric addrq *a; 441294Seric extern addrq *parse(); 442294Seric bool more; 443294Seric 444294Seric /* more keeps track of what the previous delimiter was */ 445294Seric more = TRUE; 446294Seric for (p = list; more; ) 447294Seric { 448294Seric /* find the end of this address */ 449294Seric q = p; 450294Seric while ((c = *p++) != '\0' && c != ',' && c != '\n') 451294Seric continue; 452294Seric more = c != '\0'; 453294Seric *--p = '\0'; 454294Seric if (more) 455294Seric p++; 456294Seric 457294Seric /* parse the address */ 458294Seric if ((a = parse(q, (addrq *) NULL, copyf)) == NULL) 459294Seric continue; 460294Seric 461294Seric /* arrange to send to this person */ 462294Seric recipient(a, &SendQ); 463294Seric } 464294Seric To = NULL; 465294Seric } 466294Seric /* 467294Seric ** RECIPIENT -- Designate a message recipient 468294Seric ** 469294Seric ** Saves the named person for future mailing. 470294Seric ** 471294Seric ** Designates a person as a recipient. This routine 472294Seric ** does the initial parsing, and checks to see if 473294Seric ** this person has already received the mail. 474294Seric ** It also supresses local network names and turns them into 475294Seric ** local names. 476294Seric ** 477294Seric ** Parameters: 478294Seric ** a -- the (preparsed) address header for the recipient. 479294Seric ** targetq -- the queue to add the name to. 480294Seric ** 481294Seric ** Returns: 482294Seric ** none. 483294Seric ** 484294Seric ** Side Effects: 485294Seric ** none. 486294Seric ** 487294Seric ** Called By: 488294Seric ** sendto 489294Seric ** main 490294Seric */ 491294Seric 492294Seric recipient(a, targetq) 493294Seric register addrq *a; 494294Seric addrq *targetq; 495294Seric { 496294Seric register addrq *q; 497294Seric register struct mailer *m; 498294Seric register char **pvp; 499294Seric extern char *xalloc(); 500294Seric extern bool forward(); 501294Seric extern int errno; 502294Seric extern bool sameaddr(); 503294Seric 504294Seric To = a->q_paddr; 505294Seric m = a->q_mailer; 506294Seric errno = 0; 507294Seric # ifdef DEBUG 508294Seric if (Debug) 509294Seric printf("recipient(%s)\n", To); 510294Seric # endif DEBUG 511294Seric 512294Seric /* 513294Seric ** Look up this person in the recipient list. If they 514294Seric ** are there already, return, otherwise continue. 515294Seric */ 516294Seric 517294Seric if (!ForceMail) 518294Seric { 519294Seric for (q = &SendQ; (q = nxtinq(q)) != NULL; ) 520294Seric if (sameaddr(q, a, FALSE)) 521294Seric { 522294Seric # ifdef DEBUG 523294Seric if (Debug) 524294Seric printf("(%s in SendQ)\n", a->q_paddr); 525294Seric # endif DEBUG 526294Seric return; 527294Seric } 528294Seric for (q = &AliasQ; (q = nxtinq(q)) != NULL; ) 529294Seric if (sameaddr(q, a, FALSE)) 530294Seric { 531294Seric # ifdef DEBUG 532294Seric if (Debug) 533294Seric printf("(%s in AliasQ)\n", a->q_paddr); 534294Seric # endif DEBUG 535294Seric return; 536294Seric } 537294Seric } 538294Seric 539294Seric /* 540294Seric ** See if the user wants hir mail forwarded. 541294Seric ** `Forward' must do the forwarding recursively. 542294Seric */ 543294Seric 544294Seric if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a)) 545294Seric return; 546294Seric 547294Seric /* 548294Seric ** Put the user onto the target queue. 549294Seric */ 550294Seric 551294Seric if (targetq != NULL) 552294Seric { 553294Seric putonq(a, targetq); 554294Seric } 555294Seric 556294Seric return; 557294Seric } 558294Seric /* 559294Seric ** BUILDARGV -- Build an argument vector for a mail server. 560294Seric ** 561294Seric ** Using a template defined in config.c, an argv is built. 562294Seric ** The format of the template is already a vector. The 563294Seric ** items of this vector are copied, unless a dollar sign 564294Seric ** is encountered. In this case, the next character 565294Seric ** specifies something else to copy in. These can be 566294Seric ** $f The from address. 567294Seric ** $h The host. 568294Seric ** $u The user. 569294Seric ** $c The hop count. 570294Seric ** The vector is built in a local buffer. A pointer to 571294Seric ** the static argv is returned. 572294Seric ** 573294Seric ** Parameters: 574294Seric ** tmplt -- a template for an argument vector. 575294Seric ** flags -- the flags for this server. 576294Seric ** host -- the host name to send to. 577294Seric ** user -- the user name to send to. 578294Seric ** from -- the person this mail is from. 579294Seric ** 580294Seric ** Returns: 581294Seric ** A pointer to an argv. 582294Seric ** 583294Seric ** Side Effects: 584294Seric ** none 585294Seric ** 586294Seric ** WARNING: 587294Seric ** Since the argv is staticly allocated, any subsequent 588294Seric ** calls will clobber the old argv. 589294Seric ** 590294Seric ** Called By: 591294Seric ** deliver 592294Seric */ 593294Seric 594294Seric char ** 595294Seric buildargv(tmplt, flags, host, user, from) 596294Seric char **tmplt; 597294Seric int flags; 598294Seric char *host; 599294Seric char *user; 600294Seric char *from; 601294Seric { 602294Seric register char *p; 603294Seric register char *q; 604294Seric static char *pv[MAXPV+1]; 605294Seric char **pvp; 606294Seric char **mvp; 607294Seric static char buf[512]; 608294Seric register char *bp; 609294Seric char pbuf[30]; 610294Seric 611294Seric /* 612294Seric ** Do initial argv setup. 613294Seric ** Insert the mailer name. Notice that $x expansion is 614294Seric ** NOT done on the mailer name. Then, if the mailer has 615294Seric ** a picky -f flag, we insert it as appropriate. This 616294Seric ** code does not check for 'pv' overflow; this places a 617294Seric ** manifest lower limit of 4 for MAXPV. 618294Seric */ 619294Seric 620294Seric pvp = pv; 621294Seric bp = buf; 622294Seric 623294Seric *pvp++ = tmplt[0]; 624294Seric 625294Seric /* insert -f or -r flag as appropriate */ 626294Seric if (flagset(M_FOPT|M_ROPT, flags) && FromFlag) 627294Seric { 628294Seric if (flagset(M_FOPT, flags)) 629294Seric *pvp++ = "-f"; 630294Seric else 631294Seric *pvp++ = "-r"; 632294Seric *pvp++ = From.q_paddr; 633294Seric } 634294Seric 635294Seric /* 636294Seric ** Build the rest of argv. 637294Seric ** For each prototype parameter, the prototype is 638294Seric ** scanned character at a time. If a dollar-sign is 639294Seric ** found, 'q' is set to the appropriate expansion, 640294Seric ** otherwise it is null. Then either the string 641294Seric ** pointed to by q, or the original character, is 642294Seric ** interpolated into the buffer. Buffer overflow is 643294Seric ** checked. 644294Seric */ 645294Seric 646294Seric for (mvp = tmplt; (p = *++mvp) != NULL; ) 647294Seric { 648294Seric if (pvp >= &pv[MAXPV]) 649294Seric { 650294Seric syserr("Too many parameters to %s", pv[0]); 651294Seric return (NULL); 652294Seric } 653294Seric *pvp++ = bp; 654294Seric for (; *p != '\0'; p++) 655294Seric { 656294Seric /* q will be the interpolated quantity */ 657294Seric q = NULL; 658294Seric if (*p == '$') 659294Seric { 660294Seric switch (*++p) 661294Seric { 662294Seric case 'f': /* from person */ 663294Seric q = from; 664294Seric break; 665294Seric 666294Seric case 'u': /* user */ 667294Seric q = user; 668294Seric break; 669294Seric 670294Seric case 'h': /* host */ 671294Seric q = host; 672294Seric break; 673294Seric 674294Seric case 'c': /* hop count */ 675294Seric sprintf(pbuf, "%d", HopCount); 676294Seric q = pbuf; 677294Seric break; 678294Seric } 679294Seric } 680294Seric 681294Seric /* 682294Seric ** Interpolate q or output one character 683294Seric ** Strip quote bits as we proceed..... 684294Seric */ 685294Seric 686294Seric if (q != NULL) 687294Seric { 688294Seric while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0') 689294Seric continue; 690294Seric bp--; 691294Seric } 692294Seric else if (bp < &buf[sizeof buf - 1]) 693294Seric *bp++ = *p; 694294Seric } 695294Seric *bp++ = '\0'; 696294Seric if (bp >= &buf[sizeof buf - 1]) 697294Seric return (NULL); 698294Seric } 699294Seric *pvp = NULL; 700294Seric 701294Seric # ifdef DEBUG 702294Seric if (Debug) 703294Seric { 704294Seric printf("Interpolated argv is:\n"); 705294Seric for (mvp = pv; *mvp != NULL; mvp++) 706294Seric printf("\t%s\n", *mvp); 707294Seric } 708294Seric # endif DEBUG 709294Seric 710294Seric return (pv); 711294Seric } 712294Seric /* 713294Seric ** MAILFILE -- Send a message to a file. 714294Seric ** 715294Seric ** Parameters: 716294Seric ** filename -- the name of the file to send to. 717294Seric ** 718294Seric ** Returns: 719294Seric ** The exit code associated with the operation. 720294Seric ** 721294Seric ** Side Effects: 722294Seric ** none. 723294Seric ** 724294Seric ** Called By: 725294Seric ** deliver 726294Seric */ 727294Seric 728294Seric mailfile(filename) 729294Seric char *filename; 730294Seric { 731294Seric char buf[MAXLINE]; 732294Seric register FILE *f; 733294Seric auto long tim; 734294Seric extern char *ctime(); 735294Seric 736294Seric f = fopen(filename, "a"); 737294Seric if (f == NULL) 738294Seric return (EX_CANTCREAT); 739294Seric 740294Seric /* output the timestamp */ 741294Seric time(&tim); 742294Seric fprintf(f, "From %s %s", From.q_paddr, ctime(&tim)); 743294Seric rewind(stdin); 744294Seric while (fgets(buf, sizeof buf, stdin) != NULL) 745294Seric { 746294Seric fputs(buf, f); 747294Seric if (ferror(f)) 748294Seric { 749294Seric fclose(f); 750294Seric return (EX_IOERR); 751294Seric } 752294Seric } 753294Seric fputs("\n", f); 754294Seric fclose(f); 755294Seric return (EX_OK); 756294Seric } 757