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