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