[BACK]Return to send.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mail

Annotation of src/usr.bin/mail/send.c, Revision 1.10.2.1

1.10.2.1! jason       1: /*     $OpenBSD: send.c,v 1.11 2000/06/30 16:00:18 millert Exp $       */
1.2       deraadt     2: /*     $NetBSD: send.c,v 1.6 1996/06/08 19:48:39 christos Exp $        */
                      3:
1.1       deraadt     4: /*
                      5:  * Copyright (c) 1980, 1993
                      6:  *     The Regents of the University of California.  All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
                     16:  * 3. All advertising materials mentioning features or use of this software
                     17:  *    must display the following acknowledgement:
                     18:  *     This product includes software developed by the University of
                     19:  *     California, Berkeley and its contributors.
                     20:  * 4. Neither the name of the University nor the names of its contributors
                     21:  *    may be used to endorse or promote products derived from this software
                     22:  *    without specific prior written permission.
                     23:  *
                     24:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     25:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     26:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     27:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     28:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     29:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     30:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     31:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     32:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     33:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     34:  * SUCH DAMAGE.
                     35:  */
                     36:
                     37: #ifndef lint
1.2       deraadt    38: #if 0
                     39: static char sccsid[] = "@(#)send.c     8.1 (Berkeley) 6/6/93";
                     40: #else
1.10.2.1! jason      41: static char rcsid[] = "$OpenBSD: send.c,v 1.11 2000/06/30 16:00:18 millert Exp $";
1.2       deraadt    42: #endif
1.1       deraadt    43: #endif /* not lint */
                     44:
                     45: #include "rcv.h"
                     46: #include "extern.h"
                     47:
                     48: /*
                     49:  * Mail -- a mail program
                     50:  *
                     51:  * Mail to others.
                     52:  */
                     53:
                     54: /*
                     55:  * Send message described by the passed pointer to the
                     56:  * passed output buffer.  Return -1 on error.
                     57:  * Adjust the status: field if need be.
                     58:  * If doign is given, suppress ignored header fields.
                     59:  * prefix is a string to prepend to each output line.
                     60:  */
                     61: int
1.10      millert    62: sendmessage(mp, obuf, doign, prefix)
1.9       millert    63:        struct message *mp;
1.1       deraadt    64:        FILE *obuf;
                     65:        struct ignoretab *doign;
                     66:        char *prefix;
                     67: {
1.7       millert    68:        int count;
1.9       millert    69:        FILE *ibuf;
1.1       deraadt    70:        char line[LINESIZE];
1.2       deraadt    71:        int ishead, infld, ignoring = 0, dostat, firstline;
1.9       millert    72:        char *cp, *cp2;
                     73:        int c = 0;
1.1       deraadt    74:        int length;
1.2       deraadt    75:        int prefixlen = 0;
1.1       deraadt    76:
                     77:        /*
                     78:         * Compute the prefix string, without trailing whitespace
                     79:         */
1.5       millert    80:        if (prefix != NULL) {
1.1       deraadt    81:                cp2 = 0;
                     82:                for (cp = prefix; *cp; cp++)
                     83:                        if (*cp != ' ' && *cp != '\t')
                     84:                                cp2 = cp;
                     85:                prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
                     86:        }
                     87:        ibuf = setinput(mp);
                     88:        count = mp->m_size;
                     89:        ishead = 1;
                     90:        dostat = doign == 0 || !isign("status", doign);
                     91:        infld = 0;
                     92:        firstline = 1;
                     93:        /*
                     94:         * Process headers first
                     95:         */
                     96:        while (count > 0 && ishead) {
1.3       millert    97:                if (fgets(line, sizeof(line), ibuf) == NULL)
1.1       deraadt    98:                        break;
                     99:                count -= length = strlen(line);
                    100:                if (firstline) {
1.3       millert   101:                        /*
1.1       deraadt   102:                         * First line is the From line, so no headers
                    103:                         * there to worry about
                    104:                         */
                    105:                        firstline = 0;
                    106:                        ignoring = doign == ignoreall;
                    107:                } else if (line[0] == '\n') {
                    108:                        /*
                    109:                         * If line is blank, we've reached end of
                    110:                         * headers, so force out status: field
                    111:                         * and note that we are no longer in header
                    112:                         * fields
                    113:                         */
                    114:                        if (dostat) {
                    115:                                statusput(mp, obuf, prefix);
                    116:                                dostat = 0;
                    117:                        }
                    118:                        ishead = 0;
                    119:                        ignoring = doign == ignoreall;
                    120:                } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
                    121:                        /*
                    122:                         * If this line is a continuation (via space or tab)
                    123:                         * of a previous header field, just echo it
                    124:                         * (unless the field should be ignored).
                    125:                         * In other words, nothing to do.
                    126:                         */
                    127:                } else {
                    128:                        /*
                    129:                         * Pick up the header field if we have one.
                    130:                         */
                    131:                        for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
                    132:                                ;
                    133:                        cp2 = --cp;
                    134:                        while (isspace(*cp++))
                    135:                                ;
                    136:                        if (cp[-1] != ':') {
                    137:                                /*
                    138:                                 * Not a header line, force out status:
                    139:                                 * This happens in uucp style mail where
                    140:                                 * there are no headers at all.
                    141:                                 */
                    142:                                if (dostat) {
                    143:                                        statusput(mp, obuf, prefix);
                    144:                                        dostat = 0;
                    145:                                }
                    146:                                if (doign != ignoreall)
                    147:                                        /* add blank line */
1.4       millert   148:                                        (void)putc('\n', obuf);
1.1       deraadt   149:                                ishead = 0;
                    150:                                ignoring = 0;
                    151:                        } else {
                    152:                                /*
                    153:                                 * If it is an ignored field and
                    154:                                 * we care about such things, skip it.
                    155:                                 */
                    156:                                *cp2 = 0;       /* temporarily null terminate */
                    157:                                if (doign && isign(line, doign))
                    158:                                        ignoring = 1;
                    159:                                else if ((line[0] == 's' || line[0] == 'S') &&
                    160:                                         strcasecmp(line, "status") == 0) {
                    161:                                        /*
                    162:                                         * If the field is "status," go compute
                    163:                                         * and print the real Status: field
                    164:                                         */
                    165:                                        if (dostat) {
                    166:                                                statusput(mp, obuf, prefix);
                    167:                                                dostat = 0;
                    168:                                        }
                    169:                                        ignoring = 1;
                    170:                                } else {
                    171:                                        ignoring = 0;
                    172:                                        *cp2 = c;       /* restore */
                    173:                                }
                    174:                                infld = 1;
                    175:                        }
                    176:                }
                    177:                if (!ignoring) {
                    178:                        /*
                    179:                         * Strip trailing whitespace from prefix
                    180:                         * if line is blank.
                    181:                         */
1.5       millert   182:                        if (prefix != NULL)
1.1       deraadt   183:                                if (length > 1)
                    184:                                        fputs(prefix, obuf);
                    185:                                else
1.4       millert   186:                                        (void)fwrite(prefix, sizeof(*prefix),
1.1       deraadt   187:                                                        prefixlen, obuf);
1.4       millert   188:                        (void)fwrite(line, sizeof(*line), length, obuf);
1.1       deraadt   189:                        if (ferror(obuf))
1.3       millert   190:                                return(-1);
1.1       deraadt   191:                }
                    192:        }
                    193:        /*
                    194:         * Copy out message body
                    195:         */
                    196:        if (doign == ignoreall)
                    197:                count--;                /* skip final blank line */
1.5       millert   198:        if (prefix != NULL)
1.1       deraadt   199:                while (count > 0) {
1.3       millert   200:                        if (fgets(line, sizeof(line), ibuf) == NULL) {
1.1       deraadt   201:                                c = 0;
                    202:                                break;
                    203:                        }
                    204:                        count -= c = strlen(line);
                    205:                        /*
                    206:                         * Strip trailing whitespace from prefix
                    207:                         * if line is blank.
                    208:                         */
                    209:                        if (c > 1)
                    210:                                fputs(prefix, obuf);
                    211:                        else
1.4       millert   212:                                (void)fwrite(prefix, sizeof(*prefix),
1.1       deraadt   213:                                                prefixlen, obuf);
1.4       millert   214:                        (void)fwrite(line, sizeof(*line), c, obuf);
1.1       deraadt   215:                        if (ferror(obuf))
1.3       millert   216:                                return(-1);
1.1       deraadt   217:                }
                    218:        else
                    219:                while (count > 0) {
                    220:                        c = count < LINESIZE ? count : LINESIZE;
1.3       millert   221:                        if ((c = fread(line, sizeof(*line), c, ibuf)) <= 0)
1.1       deraadt   222:                                break;
                    223:                        count -= c;
1.3       millert   224:                        if (fwrite(line, sizeof(*line), c, obuf) != c)
                    225:                                return(-1);
1.1       deraadt   226:                }
                    227:        if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
                    228:                /* no final blank line */
                    229:                if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
1.3       millert   230:                        return(-1);
                    231:        return(0);
1.1       deraadt   232: }
                    233:
                    234: /*
                    235:  * Output a reasonable looking status field.
                    236:  */
                    237: void
                    238: statusput(mp, obuf, prefix)
1.9       millert   239:        struct message *mp;
1.1       deraadt   240:        FILE *obuf;
                    241:        char *prefix;
                    242: {
                    243:        char statout[3];
1.9       millert   244:        char *cp = statout;
1.1       deraadt   245:
                    246:        if (mp->m_flag & MREAD)
                    247:                *cp++ = 'R';
                    248:        if ((mp->m_flag & MNEW) == 0)
                    249:                *cp++ = 'O';
                    250:        *cp = 0;
                    251:        if (statout[0])
                    252:                fprintf(obuf, "%sStatus: %s\n",
1.5       millert   253:                        prefix == NULL ? "" : prefix, statout);
1.1       deraadt   254: }
                    255:
                    256: /*
                    257:  * Interface between the argument list and the mail1 routine
                    258:  * which does all the dirty work.
                    259:  */
                    260: int
                    261: mail(to, cc, bcc, smopts, subject)
                    262:        struct name *to, *cc, *bcc, *smopts;
                    263:        char *subject;
                    264: {
                    265:        struct header head;
                    266:
                    267:        head.h_to = to;
                    268:        head.h_subject = subject;
                    269:        head.h_cc = cc;
                    270:        head.h_bcc = bcc;
                    271:        head.h_smopts = smopts;
                    272:        mail1(&head, 0);
                    273:        return(0);
                    274: }
                    275:
                    276:
                    277: /*
                    278:  * Send mail to a bunch of user names.  The interface is through
                    279:  * the mail routine below.
                    280:  */
                    281: int
1.2       deraadt   282: sendmail(v)
                    283:        void *v;
1.1       deraadt   284: {
1.2       deraadt   285:        char *str = v;
1.1       deraadt   286:        struct header head;
                    287:
                    288:        head.h_to = extract(str, GTO);
1.5       millert   289:        head.h_subject = NULL;
1.1       deraadt   290:        head.h_cc = NIL;
                    291:        head.h_bcc = NIL;
                    292:        head.h_smopts = NIL;
                    293:        mail1(&head, 0);
                    294:        return(0);
                    295: }
                    296:
                    297: /*
                    298:  * Mail a message on standard input to the people indicated
                    299:  * in the passed header.  (Internal interface).
                    300:  */
                    301: void
                    302: mail1(hp, printheaders)
                    303:        struct header *hp;
                    304:        int printheaders;
                    305: {
                    306:        char *cp;
                    307:        int pid;
                    308:        char **namelist;
                    309:        struct name *to;
                    310:        FILE *mtf;
                    311:
                    312:        /*
                    313:         * Collect user's mail from standard input.
                    314:         * Get the result as mtf.
                    315:         */
                    316:        if ((mtf = collect(hp, printheaders)) == NULL)
                    317:                return;
                    318:        if (fsize(mtf) == 0)
1.5       millert   319:                if (hp->h_subject == NULL)
1.3       millert   320:                        puts("No message, no subject; hope that's ok");
1.1       deraadt   321:                else
1.3       millert   322:                        puts("Null message body; hope that's ok");
1.1       deraadt   323:        /*
                    324:         * Now, take the user names from the combined
                    325:         * to and cc lists and do all the alias
                    326:         * processing.
                    327:         */
                    328:        senderr = 0;
                    329:        to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
                    330:        if (to == NIL) {
1.3       millert   331:                puts("No recipients specified");
1.1       deraadt   332:                senderr++;
                    333:        }
                    334:        /*
                    335:         * Look through the recipient list for names with /'s
                    336:         * in them which we write to as files directly.
                    337:         */
                    338:        to = outof(to, mtf, hp);
                    339:        if (senderr)
                    340:                savedeadletter(mtf);
                    341:        to = elide(to);
                    342:        if (count(to) == 0)
                    343:                goto out;
                    344:        fixhead(hp, to);
                    345:        if ((mtf = infix(hp, mtf)) == NULL) {
1.3       millert   346:                fputs(". . . message lost, sorry.\n", stderr);
1.1       deraadt   347:                return;
                    348:        }
                    349:        namelist = unpack(cat(hp->h_smopts, to));
                    350:        if (debug) {
                    351:                char **t;
                    352:
1.3       millert   353:                fputs("Sendmail arguments:", stdout);
1.5       millert   354:                for (t = namelist; *t != NULL; t++)
1.1       deraadt   355:                        printf(" \"%s\"", *t);
1.3       millert   356:                putchar('\n');
1.1       deraadt   357:                goto out;
                    358:        }
1.5       millert   359:        if ((cp = value("record")) != NULL)
1.4       millert   360:                (void)savemail(expand(cp), mtf);
1.1       deraadt   361:        /*
                    362:         * Fork, set up the temporary mail file as standard
                    363:         * input for "mail", and exec with the user list we generated
                    364:         * far above.
                    365:         */
                    366:        pid = fork();
                    367:        if (pid == -1) {
1.3       millert   368:                warn("fork");
1.1       deraadt   369:                savedeadletter(mtf);
                    370:                goto out;
                    371:        }
                    372:        if (pid == 0) {
1.2       deraadt   373:                sigset_t nset;
1.6       millert   374:
1.2       deraadt   375:                sigemptyset(&nset);
                    376:                sigaddset(&nset, SIGHUP);
                    377:                sigaddset(&nset, SIGINT);
                    378:                sigaddset(&nset, SIGQUIT);
                    379:                sigaddset(&nset, SIGTSTP);
                    380:                sigaddset(&nset, SIGTTIN);
                    381:                sigaddset(&nset, SIGTTOU);
                    382:                prepare_child(&nset, fileno(mtf), -1);
1.5       millert   383:                if ((cp = value("sendmail")) != NULL)
1.1       deraadt   384:                        cp = expand(cp);
                    385:                else
                    386:                        cp = _PATH_SENDMAIL;
                    387:                execv(cp, namelist);
1.10.2.1! jason     388:                warn("%s", cp);
1.1       deraadt   389:                _exit(1);
                    390:        }
1.5       millert   391:        if (value("verbose") != NULL)
1.4       millert   392:                (void)wait_child(pid);
1.1       deraadt   393:        else
                    394:                free_child(pid);
                    395: out:
1.3       millert   396:        (void)Fclose(mtf);
1.1       deraadt   397: }
                    398:
                    399: /*
                    400:  * Fix the header by glopping all of the expanded names from
                    401:  * the distribution list into the appropriate fields.
                    402:  */
                    403: void
                    404: fixhead(hp, tolist)
                    405:        struct header *hp;
                    406:        struct name *tolist;
                    407: {
1.9       millert   408:        struct name *np;
1.1       deraadt   409:
                    410:        hp->h_to = NIL;
                    411:        hp->h_cc = NIL;
                    412:        hp->h_bcc = NIL;
                    413:        for (np = tolist; np != NIL; np = np->n_flink)
                    414:                if ((np->n_type & GMASK) == GTO)
                    415:                        hp->h_to =
                    416:                                cat(hp->h_to, nalloc(np->n_name, np->n_type));
                    417:                else if ((np->n_type & GMASK) == GCC)
                    418:                        hp->h_cc =
                    419:                                cat(hp->h_cc, nalloc(np->n_name, np->n_type));
                    420:                else if ((np->n_type & GMASK) == GBCC)
                    421:                        hp->h_bcc =
                    422:                                cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
                    423: }
                    424:
                    425: /*
                    426:  * Prepend a header in front of the collected stuff
                    427:  * and return the new file.
                    428:  */
                    429: FILE *
                    430: infix(hp, fi)
                    431:        struct header *hp;
                    432:        FILE *fi;
                    433: {
1.9       millert   434:        FILE *nfo, *nfi;
1.8       millert   435:        int c, fd;
                    436:        char tempname[PATHSIZE];
1.1       deraadt   437:
1.8       millert   438:        (void)snprintf(tempname, sizeof(tempname),
                    439:            "%s/mail.RsXXXXXXXXXX", tmpdir);
                    440:        if ((fd = mkstemp(tempname)) == -1 ||
                    441:            (nfo = Fdopen(fd, "w")) == NULL) {
1.10.2.1! jason     442:                warn("%s", tempname);
1.1       deraadt   443:                return(fi);
                    444:        }
1.8       millert   445:        if ((nfi = Fopen(tempname, "r")) == NULL) {
1.10.2.1! jason     446:                warn("%s", tempname);
1.3       millert   447:                (void)Fclose(nfo);
1.8       millert   448:                (void)rm(tempname);
1.1       deraadt   449:                return(fi);
                    450:        }
1.8       millert   451:        (void)rm(tempname);
1.4       millert   452:        (void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
1.1       deraadt   453:        c = getc(fi);
                    454:        while (c != EOF) {
1.4       millert   455:                (void)putc(c, nfo);
1.1       deraadt   456:                c = getc(fi);
                    457:        }
                    458:        if (ferror(fi)) {
1.3       millert   459:                warn("read");
1.1       deraadt   460:                rewind(fi);
                    461:                return(fi);
                    462:        }
1.4       millert   463:        (void)fflush(nfo);
1.1       deraadt   464:        if (ferror(nfo)) {
1.10.2.1! jason     465:                warn("%s", tempname);
1.3       millert   466:                (void)Fclose(nfo);
                    467:                (void)Fclose(nfi);
1.1       deraadt   468:                rewind(fi);
                    469:                return(fi);
                    470:        }
1.3       millert   471:        (void)Fclose(nfo);
                    472:        (void)Fclose(fi);
1.1       deraadt   473:        rewind(nfi);
                    474:        return(nfi);
                    475: }
                    476:
                    477: /*
                    478:  * Dump the to, subject, cc header on the
                    479:  * passed file buffer.
                    480:  */
                    481: int
                    482: puthead(hp, fo, w)
                    483:        struct header *hp;
                    484:        FILE *fo;
                    485:        int w;
                    486: {
1.9       millert   487:        int gotcha;
1.1       deraadt   488:
                    489:        gotcha = 0;
                    490:        if (hp->h_to != NIL && w & GTO)
                    491:                fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
1.5       millert   492:        if (hp->h_subject != NULL && w & GSUBJECT)
1.1       deraadt   493:                fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
                    494:        if (hp->h_cc != NIL && w & GCC)
                    495:                fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
                    496:        if (hp->h_bcc != NIL && w & GBCC)
                    497:                fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
                    498:        if (gotcha && w & GNL)
1.4       millert   499:                (void)putc('\n', fo);
1.1       deraadt   500:        return(0);
                    501: }
                    502:
                    503: /*
                    504:  * Format the given header line to not exceed 72 characters.
                    505:  */
                    506: void
                    507: fmt(str, np, fo, comma)
                    508:        char *str;
1.9       millert   509:        struct name *np;
1.1       deraadt   510:        FILE *fo;
                    511:        int comma;
                    512: {
1.9       millert   513:        int col, len;
1.1       deraadt   514:
                    515:        comma = comma ? 1 : 0;
                    516:        col = strlen(str);
                    517:        if (col)
                    518:                fputs(str, fo);
                    519:        for (; np != NIL; np = np->n_flink) {
                    520:                if (np->n_flink == NIL)
                    521:                        comma = 0;
                    522:                len = strlen(np->n_name);
                    523:                col++;          /* for the space */
                    524:                if (col + len + comma > 72 && col > 4) {
                    525:                        fputs("\n    ", fo);
                    526:                        col = 4;
                    527:                } else
                    528:                        putc(' ', fo);
                    529:                fputs(np->n_name, fo);
                    530:                if (comma)
                    531:                        putc(',', fo);
                    532:                col += len + comma;
                    533:        }
                    534:        putc('\n', fo);
                    535: }
                    536:
                    537: /*
                    538:  * Save the outgoing mail on the passed file.
                    539:  */
                    540:
                    541: /*ARGSUSED*/
                    542: int
                    543: savemail(name, fi)
                    544:        char name[];
1.9       millert   545:        FILE *fi;
1.1       deraadt   546: {
1.9       millert   547:        FILE *fo;
1.1       deraadt   548:        char buf[BUFSIZ];
1.9       millert   549:        int i;
1.2       deraadt   550:        time_t now;
1.1       deraadt   551:
                    552:        if ((fo = Fopen(name, "a")) == NULL) {
1.10.2.1! jason     553:                warn("%s", name);
1.3       millert   554:                return(-1);
1.1       deraadt   555:        }
1.4       millert   556:        (void)time(&now);
1.1       deraadt   557:        fprintf(fo, "From %s %s", myname, ctime(&now));
1.3       millert   558:        while ((i = fread(buf, 1, sizeof(buf), fi)) > 0)
1.4       millert   559:                (void)fwrite(buf, 1, i, fo);
                    560:        (void)putc('\n', fo);
                    561:        (void)fflush(fo);
1.1       deraadt   562:        if (ferror(fo))
1.10.2.1! jason     563:                warn("%s", name);
1.3       millert   564:        (void)Fclose(fo);
1.1       deraadt   565:        rewind(fi);
1.3       millert   566:        return(0);
1.1       deraadt   567: }