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

Annotation of src/usr.bin/mail/util.c, Revision 1.2

1.2     ! jmc         1: /*     $OpenBSD: util.c,v 1.1 2020/12/15 00:50:01 daniel Exp $ */
1.1       daniel      2: /*     $NetBSD: aux.c,v 1.5 1997/05/13 06:15:52 mikel Exp $    */
                      3:
                      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. Neither the name of the University nor the names of its contributors
                     17:  *    may be used to endorse or promote products derived from this software
                     18:  *    without specific prior written permission.
                     19:  *
                     20:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     21:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     22:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     23:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     24:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     25:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     26:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     27:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     28:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     29:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     30:  * SUCH DAMAGE.
                     31:  */
                     32:
                     33: #include "rcv.h"
                     34: #include <fcntl.h>
                     35: #include "extern.h"
                     36:
                     37: /*
                     38:  * Mail -- a mail program
                     39:  *
                     40:  * Auxiliary functions.
                     41:  */
                     42: static char *save2str(char *, char *);
                     43:
                     44: /*
                     45:  * Return a pointer to a dynamic copy of the argument.
                     46:  */
                     47: char *
                     48: savestr(const char *str)
                     49: {
                     50:        char *new;
                     51:        int size = strlen(str) + 1;
                     52:
                     53:        if ((new = salloc(size)) != NULL)
                     54:                (void)memcpy(new, str, size);
                     55:        return(new);
                     56: }
                     57:
                     58: /*
                     59:  * Make a copy of new argument incorporating old one.
                     60:  */
                     61: static char *
                     62: save2str(char *str, char *old)
                     63: {
                     64:        char *new;
                     65:        int newsize = strlen(str) + 1;
                     66:        int oldsize = old ? strlen(old) + 1 : 0;
                     67:
                     68:        if ((new = salloc(newsize + oldsize)) != NULL) {
                     69:                if (oldsize) {
                     70:                        (void)memcpy(new, old, oldsize);
                     71:                        new[oldsize - 1] = ' ';
                     72:                }
                     73:                (void)memcpy(new + oldsize, str, newsize);
                     74:        }
                     75:        return(new);
                     76: }
                     77:
                     78: /*
                     79:  * Touch the named message by setting its MTOUCH flag.
                     80:  * Touched messages have the effect of not being sent
                     81:  * back to the system mailbox on exit.
                     82:  */
                     83: void
                     84: touch(struct message *mp)
                     85: {
                     86:
                     87:        mp->m_flag |= MTOUCH;
                     88:        if ((mp->m_flag & MREAD) == 0)
                     89:                mp->m_flag |= MREAD|MSTATUS;
                     90: }
                     91:
                     92: /*
                     93:  * Test to see if the passed file name is a directory.
                     94:  * Return true if it is.
                     95:  */
                     96: int
                     97: isdir(char *name)
                     98: {
                     99:        struct stat sbuf;
                    100:
                    101:        if (stat(name, &sbuf) == -1)
                    102:                return(0);
                    103:        return(S_ISDIR(sbuf.st_mode));
                    104: }
                    105:
                    106: /*
                    107:  * Count the number of arguments in the given string raw list.
                    108:  */
                    109: int
                    110: argcount(char **argv)
                    111: {
                    112:        char **ap;
                    113:
                    114:        for (ap = argv; *ap++ != NULL;)
                    115:                ;
                    116:        return(ap - argv - 1);
                    117: }
                    118:
                    119: /*
                    120:  * Return the desired header line from the passed message
                    121:  * pointer (or NULL if the desired header field is not available).
                    122:  */
                    123: char *
                    124: hfield(char *field, struct message *mp)
                    125: {
                    126:        FILE *ibuf;
                    127:        char linebuf[LINESIZE];
                    128:        int lc;
                    129:        char *hfield;
                    130:        char *colon, *oldhfield = NULL;
                    131:
                    132:        ibuf = setinput(mp);
                    133:        if ((lc = mp->m_lines - 1) < 0)
                    134:                return(NULL);
                    135:        if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
                    136:                return(NULL);
                    137:        while (lc > 0) {
                    138:                if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
                    139:                        return(oldhfield);
                    140:                if ((hfield = ishfield(linebuf, colon, field)) != NULL)
                    141:                        oldhfield = save2str(hfield, oldhfield);
                    142:        }
                    143:        return(oldhfield);
                    144: }
                    145:
                    146: /*
                    147:  * Return the next header field found in the given message.
                    148:  * Return >= 0 if something found, < 0 elsewise.
                    149:  * "colon" is set to point to the colon in the header.
                    150:  * Must deal with \ continuations & other such fraud.
                    151:  */
                    152: int
                    153: gethfield(FILE *f, char *linebuf, int rem, char **colon)
                    154: {
                    155:        char line2[LINESIZE];
                    156:        char *cp, *cp2;
                    157:        int c;
                    158:
                    159:        for (;;) {
                    160:                if (--rem < 0)
                    161:                        return(-1);
                    162:                if ((c = readline(f, linebuf, LINESIZE, NULL)) <= 0)
                    163:                        return(-1);
                    164:                for (cp = linebuf;
                    165:                    isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
                    166:                    cp++)
                    167:                        ;
                    168:                if (*cp != ':' || cp == linebuf)
                    169:                        continue;
                    170:                /*
                    171:                 * I guess we got a headline.
                    172:                 * Handle wraparounding
                    173:                 */
                    174:                *colon = cp;
                    175:                cp = linebuf + c;
                    176:                for (;;) {
                    177:                        while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
                    178:                                ;
                    179:                        cp++;
                    180:                        if (rem <= 0)
                    181:                                break;
                    182:                        ungetc(c = getc(f), f);
                    183:                        if (c != ' ' && c != '\t')
                    184:                                break;
                    185:                        if ((c = readline(f, line2, LINESIZE, NULL)) < 0)
                    186:                                break;
                    187:                        rem--;
                    188:                        for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
                    189:                                ;
                    190:                        c -= cp2 - line2;
                    191:                        if (cp + c >= linebuf + LINESIZE - 2)
                    192:                                break;
                    193:                        *cp++ = ' ';
                    194:                        (void)memcpy(cp, cp2, c);
                    195:                        cp += c;
                    196:                }
                    197:                *cp = 0;
                    198:                return(rem);
                    199:        }
                    200:        /* NOTREACHED */
                    201: }
                    202:
                    203: /*
                    204:  * Check whether the passed line is a header line of
                    205:  * the desired breed.  Return the field body, or 0.
                    206:  */
                    207:
                    208: char*
                    209: ishfield(char *linebuf, char *colon, char *field)
                    210: {
                    211:        char *cp = colon;
                    212:
                    213:        *cp = 0;
                    214:        if (strcasecmp(linebuf, field) != 0) {
                    215:                *cp = ':';
                    216:                return(0);
                    217:        }
                    218:        *cp = ':';
                    219:        for (cp++; *cp == ' ' || *cp == '\t'; cp++)
                    220:                ;
                    221:        return(cp);
                    222: }
                    223:
                    224: /*
                    225:  * Copy a string, lowercasing it as we go.  ``dsize'' should be
                    226:  * the real size (not len) of the dest string (guarantee NUL term).
                    227:  */
                    228: size_t
                    229: istrlcpy(char *dst, const char *src, size_t dsize)
                    230: {
                    231:        char *d = dst;
                    232:        const char *s = src;
                    233:        size_t n = dsize;
                    234:
                    235:        /* Copy as many bytes as will fit */
                    236:        if (n != 0 && --n != 0) {
                    237:                do {
                    238:                        if ((*d++ = tolower((unsigned char)*s++)) == 0)
                    239:                                break;
                    240:                } while (--n != 0);
                    241:        }
                    242:
                    243:        /* Not enough room in dst, add NUL and traverse rest of src */
                    244:        if (n == 0) {
                    245:                if (dsize != 0)
                    246:                        *d = '\0';              /* NUL-terminate dst */
                    247:                while (*s++)
                    248:                        ;
                    249:        }
                    250:
                    251:        return(s - src - 1);    /* count does not include NUL */
                    252: }
                    253:
                    254: /*
                    255:  * The following code deals with input stacking to do source
                    256:  * commands.  All but the current file pointer are saved on
                    257:  * the stack.
                    258:  */
                    259: static int     ssp;                    /* Top of file stack */
                    260: struct sstack {
                    261:        FILE    *s_file;                /* File we were in. */
                    262:        int     s_cond;                 /* Saved state of conditionals */
                    263:        int     s_loading;              /* Loading .mailrc, etc. */
                    264: } sstack[OPEN_MAX];
                    265:
                    266: /*
                    267:  * Pushdown current input file and switch to a new one.
                    268:  * Set the global flag "sourcing" so that others will realize
                    269:  * that they are no longer reading from a tty (in all probability).
                    270:  */
                    271: int
                    272: source(void *v)
                    273: {
                    274:        char **arglist = v;
                    275:        FILE *fi;
                    276:        char *cp;
                    277:
                    278:        if ((cp = expand(*arglist)) == NULL)
                    279:                return(1);
                    280:        if ((fi = Fopen(cp, "r")) == NULL) {
                    281:                warn("%s", cp);
                    282:                return(1);
                    283:        }
                    284:        if (ssp >= OPEN_MAX - 1) {
                    285:                puts("Too much \"sourcing\" going on.");
                    286:                (void)Fclose(fi);
                    287:                return(1);
                    288:        }
                    289:        sstack[ssp].s_file = input;
                    290:        sstack[ssp].s_cond = cond;
                    291:        sstack[ssp].s_loading = loading;
                    292:        ssp++;
                    293:        loading = 0;
                    294:        cond = CANY;
                    295:        input = fi;
                    296:        sourcing++;
                    297:        return(0);
                    298: }
                    299:
                    300: /*
                    301:  * Pop the current input back to the previous level.
                    302:  * Update the "sourcing" flag as appropriate.
                    303:  */
                    304: int
                    305: unstack(void)
                    306: {
                    307:
                    308:        if (ssp <= 0) {
                    309:                puts("\"Source\" stack over-pop.");
                    310:                sourcing = 0;
                    311:                return(1);
                    312:        }
                    313:        (void)Fclose(input);
                    314:        if (cond != CANY)
                    315:                puts("Unmatched \"if\"");
                    316:        ssp--;
                    317:        cond = sstack[ssp].s_cond;
                    318:        loading = sstack[ssp].s_loading;
                    319:        input = sstack[ssp].s_file;
                    320:        if (ssp == 0)
                    321:                sourcing = loading;
                    322:        return(0);
                    323: }
                    324:
                    325: /*
                    326:  * Touch the indicated file.
                    327:  * This is nifty for the shell.
                    328:  */
                    329: void
                    330: alter(char *name)
                    331: {
                    332:        struct timespec ts[2];
                    333:
                    334:        clock_gettime(CLOCK_REALTIME, &ts[0]);
                    335:        ts[0].tv_sec++;
                    336:        ts[1].tv_nsec = UTIME_OMIT;
                    337:        (void)utimensat(AT_FDCWD, name, ts, 0);
                    338: }
                    339:
                    340: /*
                    341:  * Examine the passed line buffer and
                    342:  * return true if it is all blanks and tabs.
                    343:  */
                    344: int
                    345: blankline(char *linebuf)
                    346: {
                    347:        char *cp;
                    348:
                    349:        for (cp = linebuf; *cp; cp++)
                    350:                if (*cp != ' ' && *cp != '\t')
                    351:                        return(0);
                    352:        return(1);
                    353: }
                    354:
                    355: /*
                    356:  * Get sender's name from this message.  If the message has
                    357:  * a bunch of arpanet stuff in it, we may have to skin the name
                    358:  * before returning it.
                    359:  */
                    360: char *
                    361: nameof(struct message *mp, int reptype)
                    362: {
                    363:        char *cp, *cp2;
                    364:
                    365:        cp = skin(name1(mp, reptype));
                    366:        if (reptype != 0 || charcount(cp, '!') < 2)
                    367:                return(cp);
                    368:        cp2 = strrchr(cp, '!');
                    369:        cp2--;
                    370:        while (cp2 > cp && *cp2 != '!')
                    371:                cp2--;
                    372:        if (*cp2 == '!')
                    373:                return(cp2 + 1);
                    374:        return(cp);
                    375: }
                    376:
                    377: /*
                    378:  * Start of a "comment".
                    379:  * Ignore it.
                    380:  */
                    381: char *
                    382: skip_comment(char *cp)
                    383: {
                    384:        int nesting = 1;
                    385:
                    386:        for (; nesting > 0 && *cp; cp++) {
                    387:                switch (*cp) {
                    388:                case '\\':
                    389:                        if (cp[1])
                    390:                                cp++;
                    391:                        break;
                    392:                case '(':
                    393:                        nesting++;
                    394:                        break;
                    395:                case ')':
                    396:                        nesting--;
                    397:                        break;
                    398:                }
                    399:        }
                    400:        return(cp);
                    401: }
                    402:
                    403: /*
                    404:  * Skin an arpa net address according to the RFC 822 interpretation
                    405:  * of "host-phrase."
                    406:  */
                    407: char *
                    408: skin(char *name)
                    409: {
                    410:        char *nbuf, *bufend, *cp, *cp2;
                    411:        int c, gotlt, lastsp;
                    412:
                    413:        if (name == NULL)
                    414:                return(NULL);
                    415:        if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
                    416:            && strchr(name, ' ') == NULL)
                    417:                return(name);
                    418:
                    419:        /* We assume that length(input) <= length(output) */
                    420:        if ((nbuf = malloc(strlen(name) + 1)) == NULL)
                    421:                err(1, "malloc");
                    422:        gotlt = 0;
                    423:        lastsp = 0;
                    424:        bufend = nbuf;
                    425:        for (cp = name, cp2 = bufend; (c = (unsigned char)*cp++) != '\0'; ) {
                    426:                switch (c) {
                    427:                case '(':
                    428:                        cp = skip_comment(cp);
                    429:                        lastsp = 0;
                    430:                        break;
                    431:
                    432:                case '"':
                    433:                        /*
                    434:                         * Start of a "quoted-string".
                    435:                         * Copy it in its entirety.
                    436:                         */
                    437:                        while ((c = (unsigned char)*cp) != '\0') {
                    438:                                cp++;
                    439:                                if (c == '"')
                    440:                                        break;
                    441:                                if (c != '\\')
                    442:                                        *cp2++ = c;
                    443:                                else if ((c = (unsigned char)*cp) != '\0') {
                    444:                                        *cp2++ = c;
                    445:                                        cp++;
                    446:                                }
                    447:                        }
                    448:                        lastsp = 0;
                    449:                        break;
                    450:
                    451:                case ' ':
                    452:                        if (strncmp(cp, "at ", 3) == 0)
                    453:                                cp += 3, *cp2++ = '@';
                    454:                        else
                    455:                        if (strncmp(cp, "@ ", 2) == 0)
                    456:                                cp += 2, *cp2++ = '@';
                    457:                        else
                    458:                                lastsp = 1;
                    459:                        break;
                    460:
                    461:                case '<':
                    462:                        cp2 = bufend;
                    463:                        gotlt++;
                    464:                        lastsp = 0;
                    465:                        break;
                    466:
                    467:                case '>':
                    468:                        if (gotlt) {
                    469:                                gotlt = 0;
                    470:                                while ((c = (unsigned char)*cp) && c != ',') {
                    471:                                        cp++;
                    472:                                        if (c == '(')
                    473:                                                cp = skip_comment(cp);
                    474:                                        else if (c == '"')
                    475:                                                while ((c = (unsigned char)*cp) != '\0') {
                    476:                                                        cp++;
                    477:                                                        if (c == '"')
                    478:                                                                break;
                    479:                                                        if (c == '\\' && *cp)
                    480:                                                                cp++;
                    481:                                                }
                    482:                                }
                    483:                                lastsp = 0;
                    484:                                break;
                    485:                        }
                    486:                        /* Fall into . . . */
                    487:
                    488:                default:
                    489:                        if (lastsp) {
                    490:                                lastsp = 0;
                    491:                                *cp2++ = ' ';
                    492:                        }
                    493:                        *cp2++ = c;
                    494:                        if (c == ',' && *cp == ' ' && !gotlt) {
                    495:                                *cp2++ = ' ';
                    496:                                while (*++cp == ' ')
                    497:                                        ;
                    498:                                lastsp = 0;
                    499:                                bufend = cp2;
                    500:                        }
                    501:                }
                    502:        }
                    503:        *cp2 = 0;
                    504:
                    505:        if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
                    506:                nbuf = cp;
                    507:        return(nbuf);
                    508: }
                    509:
                    510: /*
                    511:  * Fetch the sender's name from the passed message.
                    512:  * Reptype can be
                    513:  *     0 -- get sender's name for display purposes
                    514:  *     1 -- get sender's name for reply
                    515:  *     2 -- get sender's name for Reply
                    516:  */
                    517: char *
                    518: name1(struct message *mp, int reptype)
                    519: {
                    520:        char namebuf[LINESIZE];
                    521:        char linebuf[LINESIZE];
                    522:        char *cp, *cp2;
                    523:        FILE *ibuf;
                    524:        int first = 1;
                    525:
                    526:        if ((cp = hfield("from", mp)) != NULL)
                    527:                return(cp);
                    528:        if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
                    529:                return(cp);
                    530:        ibuf = setinput(mp);
                    531:        namebuf[0] = '\0';
                    532:        if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
                    533:                return(savestr(namebuf));
                    534: newname:
                    535:        for (cp = linebuf; *cp && *cp != ' '; cp++)
                    536:                ;
                    537:        for (; *cp == ' ' || *cp == '\t'; cp++)
                    538:                ;
                    539:        for (cp2 = &namebuf[strlen(namebuf)];
                    540:             *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
                    541:                *cp2++ = *cp++;
                    542:        *cp2 = '\0';
                    543:        if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
                    544:                return(savestr(namebuf));
                    545:        if ((cp = strchr(linebuf, 'F')) == NULL)
                    546:                return(savestr(namebuf));
                    547:        if (strncmp(cp, "From", 4) != 0)
                    548:                return(savestr(namebuf));
                    549:        while ((cp = strchr(cp, 'r')) != NULL) {
                    550:                if (strncmp(cp, "remote", 6) == 0) {
                    551:                        if ((cp = strchr(cp, 'f')) == NULL)
                    552:                                break;
                    553:                        if (strncmp(cp, "from", 4) != 0)
                    554:                                break;
                    555:                        if ((cp = strchr(cp, ' ')) == NULL)
                    556:                                break;
                    557:                        cp++;
                    558:                        if (first) {
                    559:                                cp2 = namebuf;
                    560:                                first = 0;
                    561:                        } else
                    562:                                cp2 = strrchr(namebuf, '!') + 1;
                    563:                        strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
                    564:                        strlcat(namebuf, "!", sizeof(namebuf));
                    565:                        goto newname;
                    566:                }
                    567:                cp++;
                    568:        }
                    569:        return(savestr(namebuf));
                    570: }
                    571:
                    572: /*
1.2     ! jmc       573:  * Count the occurrences of c in str
1.1       daniel    574:  */
                    575: int
                    576: charcount(char *str, int c)
                    577: {
                    578:        char *cp;
                    579:        int i;
                    580:
                    581:        for (i = 0, cp = str; *cp; cp++)
                    582:                if (*cp == c)
                    583:                        i++;
                    584:        return(i);
                    585: }
                    586:
                    587: /*
                    588:  * Copy s1 to s2, return pointer to null in s2.
                    589:  */
                    590: char *
                    591: copy(char *s1, char *s2)
                    592: {
                    593:
                    594:        while ((*s2++ = *s1++) != '\0')
                    595:                ;
                    596:        return(s2 - 1);
                    597: }
                    598:
                    599: /*
                    600:  * See if the given header field is supposed to be ignored.
                    601:  */
                    602: int
                    603: isign(char *field, struct ignoretab ignore[2])
                    604: {
                    605:        char realfld[LINESIZE];
                    606:
                    607:        if (ignore == ignoreall)
                    608:                return(1);
                    609:        /*
                    610:         * Lower-case the string, so that "Status" and "status"
                    611:         * will hash to the same place.
                    612:         */
                    613:        istrlcpy(realfld, field, sizeof(realfld));
                    614:        if (ignore[1].i_count > 0)
                    615:                return(!member(realfld, ignore + 1));
                    616:        else
                    617:                return(member(realfld, ignore));
                    618: }
                    619:
                    620: int
                    621: member(char *realfield, struct ignoretab *table)
                    622: {
                    623:        struct ignore *igp;
                    624:
                    625:        for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
                    626:                if (*igp->i_field == *realfield &&
                    627:                    equal(igp->i_field, realfield))
                    628:                        return(1);
                    629:        return(0);
                    630: }
                    631:
                    632: void
                    633: clearnew(void)
                    634: {
                    635:        struct message *mp;
                    636:
                    637:        for (mp = &message[0]; mp < &message[msgCount]; mp++) {
                    638:                if (mp->m_flag & MNEW) {
                    639:                        mp->m_flag &= ~MNEW;
                    640:                        mp->m_flag |= MSTATUS;
                    641:                }
                    642:        }
                    643: }