util.c revision 74769
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 36static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93"; 37#endif 38static const char rcsid[] = 39 "$FreeBSD: head/usr.bin/mail/aux.c 74769 2001-03-25 04:57:05Z mikeh $"; 40#endif /* not lint */ 41 42#include "rcv.h" 43#include "extern.h" 44 45/* 46 * Mail -- a mail program 47 * 48 * Auxiliary functions. 49 */ 50 51/* 52 * Return a pointer to a dynamic copy of the argument. 53 */ 54char * 55savestr(str) 56 char *str; 57{ 58 char *new; 59 int size = strlen(str) + 1; 60 61 if ((new = salloc(size)) != NOSTR) 62 bcopy(str, new, size); 63 return new; 64} 65 66/* 67 * Make a copy of new argument incorporating old one. 68 */ 69char * 70save2str(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)) != NOSTR) { 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 */ 92void 93touch(mp) 94 register 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 */ 106int 107isdir(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 */ 120int 121argcount(argv) 122 char **argv; 123{ 124 register char **ap; 125 126 for (ap = argv; *ap++ != NOSTR;) 127 ; 128 return ap - argv - 1; 129} 130 131/* 132 * Return the desired header line from the passed message 133 * pointer (or NOSTR if the desired header field is not available). 134 */ 135char * 136hfield(field, mp) 137 char field[]; 138 struct message *mp; 139{ 140 register FILE *ibuf; 141 char linebuf[LINESIZE]; 142 register int lc; 143 register char *hfield; 144 char *colon, *oldhfield = NOSTR; 145 146 ibuf = setinput(mp); 147 if ((lc = mp->m_lines - 1) < 0) 148 return NOSTR; 149 if (readline(ibuf, linebuf, LINESIZE) < 0) 150 return NOSTR; 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 */ 166int 167gethfield(f, linebuf, rem, colon) 168 register FILE *f; 169 char linebuf[]; 170 register int rem; 171 char **colon; 172{ 173 char line2[LINESIZE]; 174 register char *cp, *cp2; 175 register 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(*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 225char* 226ishfield(linebuf, colon, field) 227 char linebuf[], field[]; 228 char *colon; 229{ 230 register char *cp = colon; 231 232 *cp = 0; 233 if (strcasecmp(linebuf, field) != 0) { 234 *cp = ':'; 235 return 0; 236 } 237 *cp = ':'; 238 for (cp++; *cp == ' ' || *cp == '\t'; cp++) 239 ; 240 return cp; 241} 242 243/* 244 * Copy a string and lowercase the result. 245 * dsize: space left in buffer (including space for NULL) 246 */ 247void 248istrncpy(dest, src, dsize) 249 register char *dest, *src; 250 size_t dsize; 251{ 252 253 strlcpy(dest, src, dsize); 254 while (*dest) 255 *dest++ = tolower(*dest); 256} 257 258/* 259 * The following code deals with input stacking to do source 260 * commands. All but the current file pointer are saved on 261 * the stack. 262 */ 263 264static int ssp; /* Top of file stack */ 265struct sstack { 266 FILE *s_file; /* File we were in. */ 267 int s_cond; /* Saved state of conditionals */ 268 int s_loading; /* Loading .mailrc, etc. */ 269}; 270#define SSTACK_SIZE 64 /* XXX was NOFILE. */ 271static struct sstack sstack[SSTACK_SIZE]; 272 273/* 274 * Pushdown current input file and switch to a new one. 275 * Set the global flag "sourcing" so that others will realize 276 * that they are no longer reading from a tty (in all probability). 277 */ 278int 279source(arglist) 280 char **arglist; 281{ 282 FILE *fi; 283 char *cp; 284 285 if ((cp = expand(*arglist)) == NOSTR) 286 return(1); 287 if ((fi = Fopen(cp, "r")) == NULL) { 288 warn("%s", cp); 289 return(1); 290 } 291 if (ssp >= SSTACK_SIZE - 1) { 292 printf("Too much \"sourcing\" going on.\n"); 293 Fclose(fi); 294 return(1); 295 } 296 sstack[ssp].s_file = input; 297 sstack[ssp].s_cond = cond; 298 sstack[ssp].s_loading = loading; 299 ssp++; 300 loading = 0; 301 cond = CANY; 302 input = fi; 303 sourcing++; 304 return(0); 305} 306 307/* 308 * Pop the current input back to the previous level. 309 * Update the "sourcing" flag as appropriate. 310 */ 311int 312unstack() 313{ 314 if (ssp <= 0) { 315 printf("\"Source\" stack over-pop.\n"); 316 sourcing = 0; 317 return(1); 318 } 319 Fclose(input); 320 if (cond != CANY) 321 printf("Unmatched \"if\"\n"); 322 ssp--; 323 cond = sstack[ssp].s_cond; 324 loading = sstack[ssp].s_loading; 325 input = sstack[ssp].s_file; 326 if (ssp == 0) 327 sourcing = loading; 328 return(0); 329} 330 331/* 332 * Touch the indicated file. 333 * This is nifty for the shell. 334 */ 335void 336alter(name) 337 char *name; 338{ 339 struct stat sb; 340 struct timeval tv[2]; 341 time_t time(); 342 343 if (stat(name, &sb)) 344 return; 345 tv[0].tv_sec = time((time_t *)0) + 1; 346 tv[1].tv_sec = sb.st_mtime; 347 tv[0].tv_usec = tv[1].tv_usec = 0; 348 (void)utimes(name, tv); 349} 350 351/* 352 * Get sender's name from this message. If the message has 353 * a bunch of arpanet stuff in it, we may have to skin the name 354 * before returning it. 355 */ 356char * 357nameof(mp, reptype) 358 register struct message *mp; 359 int reptype; 360{ 361 register 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 */ 379char * 380skip_comment(cp) 381 register char *cp; 382{ 383 register nesting = 1; 384 385 for (; nesting > 0 && *cp; cp++) { 386 switch (*cp) { 387 case '\\': 388 if (cp[1]) 389 cp++; 390 break; 391 case '(': 392 nesting++; 393 break; 394 case ')': 395 nesting--; 396 break; 397 } 398 } 399 return cp; 400} 401 402/* 403 * Skin an arpa net address according to the RFC 822 interpretation 404 * of "host-phrase." 405 */ 406char * 407skin(name) 408 char *name; 409{ 410 register int c; 411 register char *cp, *cp2; 412 char *bufend, *nbuf; 413 int gotlt, lastsp; 414 415 if (name == NOSTR) 416 return(NOSTR); 417 if (strchr(name, '(') == NOSTR && strchr(name, '<') == NOSTR 418 && strchr(name, ' ') == NOSTR) 419 return(name); 420 421 /* We assume that length(input) <= length(output) */ 422 if ((nbuf = (char *)malloc(strlen(name) + 1)) == NULL) 423 err(1, "Out of memory"); 424 gotlt = 0; 425 lastsp = 0; 426 bufend = nbuf; 427 for (cp = name, cp2 = bufend; c = *cp++; ) { 428 switch (c) { 429 case '(': 430 cp = skip_comment(cp); 431 lastsp = 0; 432 break; 433 434 case '"': 435 /* 436 * Start of a "quoted-string". 437 * Copy it in its entirety. 438 */ 439 while (c = *cp) { 440 cp++; 441 if (c == '"') 442 break; 443 if (c != '\\') 444 *cp2++ = c; 445 else if (c = *cp) { 446 *cp2++ = c; 447 cp++; 448 } 449 } 450 lastsp = 0; 451 break; 452 453 case ' ': 454 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 455 cp += 3, *cp2++ = '@'; 456 else 457 if (cp[0] == '@' && cp[1] == ' ') 458 cp += 2, *cp2++ = '@'; 459 else 460 lastsp = 1; 461 break; 462 463 case '<': 464 cp2 = bufend; 465 gotlt++; 466 lastsp = 0; 467 break; 468 469 case '>': 470 if (gotlt) { 471 gotlt = 0; 472 while ((c = *cp) && c != ',') { 473 cp++; 474 if (c == '(') 475 cp = skip_comment(cp); 476 else if (c == '"') 477 while (c = *cp) { 478 cp++; 479 if (c == '"') 480 break; 481 if (c == '\\' && *cp) 482 cp++; 483 } 484 } 485 lastsp = 0; 486 break; 487 } 488 /* Fall into . . . */ 489 490 default: 491 if (lastsp) { 492 lastsp = 0; 493 *cp2++ = ' '; 494 } 495 *cp2++ = c; 496 if (c == ',' && !gotlt) { 497 *cp2++ = ' '; 498 for (; *cp == ' '; cp++) 499 ; 500 lastsp = 0; 501 bufend = cp2; 502 } 503 } 504 } 505 *cp2 = 0; 506 507 if ((nbuf = (char *)realloc(nbuf, strlen(nbuf) + 1)) == NULL) 508 err(1, "Out of memory"); 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 */ 519char * 520name1(mp, reptype) 521 register struct message *mp; 522 int reptype; 523{ 524 char namebuf[LINESIZE]; 525 char linebuf[LINESIZE]; 526 register char *cp, *cp2; 527 register FILE *ibuf; 528 int first = 1; 529 530 if ((cp = hfield("from", mp)) != NOSTR) 531 return cp; 532 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 533 return cp; 534 ibuf = setinput(mp); 535 namebuf[0] = '\0'; 536 if (readline(ibuf, linebuf, LINESIZE) < 0) 537 return(savestr(namebuf)); 538newname: 539 for (cp = linebuf; *cp && *cp != ' '; cp++) 540 ; 541 for (; *cp == ' ' || *cp == '\t'; cp++) 542 ; 543 for (cp2 = &namebuf[strlen(namebuf)]; 544 *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 545 *cp2++ = *cp++; 546 *cp2 = '\0'; 547 if (readline(ibuf, linebuf, LINESIZE) < 0) 548 return(savestr(namebuf)); 549 if ((cp = strchr(linebuf, 'F')) == NULL) 550 return(savestr(namebuf)); 551 if (strncmp(cp, "From", 4) != 0) 552 return(savestr(namebuf)); 553 while ((cp = strchr(cp, 'r')) != NULL) { 554 if (strncmp(cp, "remote", 6) == 0) { 555 if ((cp = strchr(cp, 'f')) == NULL) 556 break; 557 if (strncmp(cp, "from", 4) != 0) 558 break; 559 if ((cp = strchr(cp, ' ')) == NULL) 560 break; 561 cp++; 562 if (first) { 563 cp2 = namebuf; 564 first = 0; 565 } else 566 cp2 = strrchr(namebuf, '!') + 1; 567 strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1); 568 strcat(namebuf, "!"); 569 goto newname; 570 } 571 cp++; 572 } 573 return(savestr(namebuf)); 574} 575 576/* 577 * Count the occurances of c in str 578 */ 579int 580charcount(str, c) 581 char *str; 582 int c; 583{ 584 register char *cp; 585 register int i; 586 587 for (i = 0, cp = str; *cp; cp++) 588 if (*cp == c) 589 i++; 590 return(i); 591} 592 593/* 594 * See if the given header field is supposed to be ignored. 595 */ 596int 597isign(field, ignore) 598 char *field; 599 struct ignoretab ignore[2]; 600{ 601 char realfld[LINESIZE]; 602 603 if (ignore == ignoreall) 604 return 1; 605 /* 606 * Lower-case the string, so that "Status" and "status" 607 * will hash to the same place. 608 */ 609 istrncpy(realfld, field, sizeof(realfld)); 610 if (ignore[1].i_count > 0) 611 return (!member(realfld, ignore + 1)); 612 else 613 return (member(realfld, ignore)); 614} 615 616int 617member(realfield, table) 618 register char *realfield; 619 struct ignoretab *table; 620{ 621 register struct ignore *igp; 622 623 for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 624 if (*igp->i_field == *realfield && 625 equal(igp->i_field, realfield)) 626 return (1); 627 return (0); 628} 629