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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 #if 0 36 static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93"; 37 #endif 38 #endif /* not lint */ 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 41 42 #include <sys/time.h> 43 44 #include "rcv.h" 45 #include "extern.h" 46 47 /* 48 * Mail -- a mail program 49 * 50 * Auxiliary functions. 51 */ 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(str) 60 char *str; 61 { 62 char *new; 63 int size = strlen(str) + 1; 64 65 if ((new = salloc(size)) != NULL) 66 bcopy(str, new, 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)) != NULL) { 82 if (oldsize) { 83 bcopy(old, new, oldsize); 84 new[oldsize - 1] = ' '; 85 } 86 bcopy(str, new + oldsize, 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++ != NULL;) 131 ; 132 return (ap - argv - 1); 133 } 134 135 /* 136 * Return the desired header line from the passed message 137 * pointer (or NULL if the desired header field is not available). 138 */ 139 char * 140 hfield(field, mp) 141 const char *field; 142 struct message *mp; 143 { 144 FILE *ibuf; 145 char linebuf[LINESIZE]; 146 int lc; 147 char *hfield; 148 char *colon, *oldhfield = NULL; 149 150 ibuf = setinput(mp); 151 if ((lc = mp->m_lines - 1) < 0) 152 return (NULL); 153 if (readline(ibuf, linebuf, LINESIZE) < 0) 154 return (NULL); 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 bcopy(cp2, cp, 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[]; 232 char *colon; 233 const char *field; 234 { 235 char *cp = colon; 236 237 *cp = 0; 238 if (strcasecmp(linebuf, field) != 0) { 239 *cp = ':'; 240 return (0); 241 } 242 *cp = ':'; 243 for (cp++; *cp == ' ' || *cp == '\t'; cp++) 244 ; 245 return (cp); 246 } 247 248 /* 249 * Copy a string and lowercase the result. 250 * dsize: space left in buffer (including space for NULL) 251 */ 252 void 253 istrncpy(dest, src, dsize) 254 char *dest; 255 const char *src; 256 size_t dsize; 257 { 258 259 strlcpy(dest, src, dsize); 260 while (*dest) 261 *dest++ = tolower((unsigned char)*dest); 262 } 263 264 /* 265 * The following code deals with input stacking to do source 266 * commands. All but the current file pointer are saved on 267 * the stack. 268 */ 269 270 static int ssp; /* Top of file stack */ 271 struct sstack { 272 FILE *s_file; /* File we were in. */ 273 int s_cond; /* Saved state of conditionals */ 274 int s_loading; /* Loading .mailrc, etc. */ 275 }; 276 #define SSTACK_SIZE 64 /* XXX was NOFILE. */ 277 static struct sstack sstack[SSTACK_SIZE]; 278 279 /* 280 * Pushdown current input file and switch to a new one. 281 * Set the global flag "sourcing" so that others will realize 282 * that they are no longer reading from a tty (in all probability). 283 */ 284 int 285 source(arglist) 286 char **arglist; 287 { 288 FILE *fi; 289 char *cp; 290 291 if ((cp = expand(*arglist)) == NULL) 292 return (1); 293 if ((fi = Fopen(cp, "r")) == NULL) { 294 warn("%s", cp); 295 return (1); 296 } 297 if (ssp >= SSTACK_SIZE - 1) { 298 printf("Too much \"sourcing\" going on.\n"); 299 (void)Fclose(fi); 300 return (1); 301 } 302 sstack[ssp].s_file = input; 303 sstack[ssp].s_cond = cond; 304 sstack[ssp].s_loading = loading; 305 ssp++; 306 loading = 0; 307 cond = CANY; 308 input = fi; 309 sourcing++; 310 return (0); 311 } 312 313 /* 314 * Pop the current input back to the previous level. 315 * Update the "sourcing" flag as appropriate. 316 */ 317 int 318 unstack() 319 { 320 if (ssp <= 0) { 321 printf("\"Source\" stack over-pop.\n"); 322 sourcing = 0; 323 return (1); 324 } 325 (void)Fclose(input); 326 if (cond != CANY) 327 printf("Unmatched \"if\"\n"); 328 ssp--; 329 cond = sstack[ssp].s_cond; 330 loading = sstack[ssp].s_loading; 331 input = sstack[ssp].s_file; 332 if (ssp == 0) 333 sourcing = loading; 334 return (0); 335 } 336 337 /* 338 * Touch the indicated file. 339 * This is nifty for the shell. 340 */ 341 void 342 alter(name) 343 char *name; 344 { 345 struct stat sb; 346 struct timeval tv[2]; 347 348 if (stat(name, &sb)) 349 return; 350 (void)gettimeofday(&tv[0], (struct timezone *)NULL); 351 tv[0].tv_sec++; 352 TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); 353 (void)utimes(name, tv); 354 } 355 356 /* 357 * Get sender's name from this message. If the message has 358 * a bunch of arpanet stuff in it, we may have to skin the name 359 * before returning it. 360 */ 361 char * 362 nameof(mp, reptype) 363 struct message *mp; 364 int reptype; 365 { 366 char *cp, *cp2; 367 368 cp = skin(name1(mp, reptype)); 369 if (reptype != 0 || charcount(cp, '!') < 2) 370 return (cp); 371 cp2 = strrchr(cp, '!'); 372 cp2--; 373 while (cp2 > cp && *cp2 != '!') 374 cp2--; 375 if (*cp2 == '!') 376 return (cp2 + 1); 377 return (cp); 378 } 379 380 /* 381 * Start of a "comment". 382 * Ignore it. 383 */ 384 char * 385 skip_comment(cp) 386 char *cp; 387 { 388 int nesting = 1; 389 390 for (; nesting > 0 && *cp; cp++) { 391 switch (*cp) { 392 case '\\': 393 if (cp[1]) 394 cp++; 395 break; 396 case '(': 397 nesting++; 398 break; 399 case ')': 400 nesting--; 401 break; 402 } 403 } 404 return (cp); 405 } 406 407 /* 408 * Skin an arpa net address according to the RFC 822 interpretation 409 * of "host-phrase." 410 */ 411 char * 412 skin(name) 413 char *name; 414 { 415 char *nbuf, *bufend, *cp, *cp2; 416 int c, gotlt, lastsp; 417 418 if (name == NULL) 419 return (NULL); 420 if (strchr(name, '(') == NULL && strchr(name, '<') == NULL 421 && strchr(name, ' ') == NULL) 422 return (name); 423 424 /* We assume that length(input) <= length(output) */ 425 if ((nbuf = malloc(strlen(name) + 1)) == NULL) 426 err(1, "Out of memory"); 427 gotlt = 0; 428 lastsp = 0; 429 bufend = nbuf; 430 for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) { 431 switch (c) { 432 case '(': 433 cp = skip_comment(cp); 434 lastsp = 0; 435 break; 436 437 case '"': 438 /* 439 * Start of a "quoted-string". 440 * Copy it in its entirety. 441 */ 442 while ((c = *cp) != '\0') { 443 cp++; 444 if (c == '"') 445 break; 446 if (c != '\\') 447 *cp2++ = c; 448 else if ((c = *cp) != '\0') { 449 *cp2++ = c; 450 cp++; 451 } 452 } 453 lastsp = 0; 454 break; 455 456 case ' ': 457 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 458 cp += 3, *cp2++ = '@'; 459 else 460 if (cp[0] == '@' && cp[1] == ' ') 461 cp += 2, *cp2++ = '@'; 462 else 463 lastsp = 1; 464 break; 465 466 case '<': 467 cp2 = bufend; 468 gotlt++; 469 lastsp = 0; 470 break; 471 472 case '>': 473 if (gotlt) { 474 gotlt = 0; 475 while ((c = *cp) != '\0' && c != ',') { 476 cp++; 477 if (c == '(') 478 cp = skip_comment(cp); 479 else if (c == '"') 480 while ((c = *cp) != '\0') { 481 cp++; 482 if (c == '"') 483 break; 484 if (c == '\\' && *cp != '\0') 485 cp++; 486 } 487 } 488 lastsp = 0; 489 break; 490 } 491 /* FALLTHROUGH */ 492 493 default: 494 if (lastsp) { 495 lastsp = 0; 496 *cp2++ = ' '; 497 } 498 *cp2++ = c; 499 if (c == ',' && *cp == ' ' && !gotlt) { 500 *cp2++ = ' '; 501 while (*++cp == ' ') 502 ; 503 lastsp = 0; 504 bufend = cp2; 505 } 506 } 507 } 508 *cp2 = '\0'; 509 510 if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL) 511 nbuf = cp; 512 return (nbuf); 513 } 514 515 /* 516 * Fetch the sender's name from the passed message. 517 * Reptype can be 518 * 0 -- get sender's name for display purposes 519 * 1 -- get sender's name for reply 520 * 2 -- get sender's name for Reply 521 */ 522 char * 523 name1(mp, reptype) 524 struct message *mp; 525 int reptype; 526 { 527 char namebuf[LINESIZE]; 528 char linebuf[LINESIZE]; 529 char *cp, *cp2; 530 FILE *ibuf; 531 int first = 1; 532 533 if ((cp = hfield("from", mp)) != NULL) 534 return (cp); 535 if (reptype == 0 && (cp = hfield("sender", mp)) != NULL) 536 return (cp); 537 ibuf = setinput(mp); 538 namebuf[0] = '\0'; 539 if (readline(ibuf, linebuf, LINESIZE) < 0) 540 return (savestr(namebuf)); 541 newname: 542 for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++) 543 ; 544 for (; *cp == ' ' || *cp == '\t'; cp++) 545 ; 546 for (cp2 = &namebuf[strlen(namebuf)]; 547 *cp != '\0' && *cp != ' ' && *cp != '\t' && 548 cp2 < namebuf + LINESIZE - 1;) 549 *cp2++ = *cp++; 550 *cp2 = '\0'; 551 if (readline(ibuf, linebuf, LINESIZE) < 0) 552 return (savestr(namebuf)); 553 if ((cp = strchr(linebuf, 'F')) == NULL) 554 return (savestr(namebuf)); 555 if (strncmp(cp, "From", 4) != 0) 556 return (savestr(namebuf)); 557 while ((cp = strchr(cp, 'r')) != NULL) { 558 if (strncmp(cp, "remote", 6) == 0) { 559 if ((cp = strchr(cp, 'f')) == NULL) 560 break; 561 if (strncmp(cp, "from", 4) != 0) 562 break; 563 if ((cp = strchr(cp, ' ')) == NULL) 564 break; 565 cp++; 566 if (first) { 567 cp2 = namebuf; 568 first = 0; 569 } else 570 cp2 = strrchr(namebuf, '!') + 1; 571 strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1); 572 strcat(namebuf, "!"); 573 goto newname; 574 } 575 cp++; 576 } 577 return (savestr(namebuf)); 578 } 579 580 /* 581 * Count the occurances of c in str 582 */ 583 int 584 charcount(str, c) 585 char *str; 586 int c; 587 { 588 char *cp; 589 int i; 590 591 for (i = 0, cp = str; *cp != '\0'; cp++) 592 if (*cp == c) 593 i++; 594 return (i); 595 } 596 597 /* 598 * See if the given header field is supposed to be ignored. 599 */ 600 int 601 isign(field, ignore) 602 const char *field; 603 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 istrncpy(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(realfield, table) 622 char *realfield; 623 struct ignoretab *table; 624 { 625 struct ignore *igp; 626 627 for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link) 628 if (*igp->i_field == *realfield && 629 equal(igp->i_field, realfield)) 630 return (1); 631 return (0); 632 } 633