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