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