Annotation of src/usr.bin/mail/util.c, Revision 1.1
1.1 ! daniel 1: /* $OpenBSD: aux.c,v 1.31 2019/06/28 13:35:01 deraadt Exp $ */
! 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: /*
! 573: * Count the occurances of c in str
! 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: }