1 /* $OpenBSD: util.c,v 1.1 2020/12/15 00:50:01 daniel 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 } 644