util.c revision 216370
1171626Scognet/* 2171626Scognet * Copyright (c) 1980, 1993 3171626Scognet * The Regents of the University of California. All rights reserved. 4171626Scognet * 5171626Scognet * Redistribution and use in source and binary forms, with or without 6171626Scognet * modification, are permitted provided that the following conditions 7171626Scognet * are met: 8171626Scognet * 1. Redistributions of source code must retain the above copyright 9171626Scognet * notice, this list of conditions and the following disclaimer. 10171626Scognet * 2. Redistributions in binary form must reproduce the above copyright 11171626Scognet * notice, this list of conditions and the following disclaimer in the 12171626Scognet * documentation and/or other materials provided with the distribution. 13171626Scognet * 4. Neither the name of the University nor the names of its contributors 14171626Scognet * may be used to endorse or promote products derived from this software 15171626Scognet * without specific prior written permission. 16171626Scognet * 17171626Scognet * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18171626Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19171626Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20171626Scognet * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21171626Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22171626Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23171626Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24171626Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25171626Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26171626Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27171626Scognet * SUCH DAMAGE. 28171626Scognet */ 29171626Scognet 30171626Scognet#ifndef lint 31171626Scognet#if 0 32171626Scognetstatic char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93"; 33171626Scognet#endif 34171626Scognet#endif /* not lint */ 35171626Scognet#include <sys/cdefs.h> 36171626Scognet__FBSDID("$FreeBSD: head/usr.bin/mail/util.c 216370 2010-12-11 08:32:16Z joel $"); 37171626Scognet 38171626Scognet#include <sys/time.h> 39171626Scognet 40171626Scognet#include "rcv.h" 41171626Scognet#include "extern.h" 42171626Scognet 43236987Simp/* 44171626Scognet * Mail -- a mail program 45171626Scognet * 46171626Scognet * Auxiliary functions. 47171626Scognet */ 48171626Scognet 49171626Scognetstatic char *save2str(char *, char *); 50171626Scognet 51171626Scognet/* 52171626Scognet * Return a pointer to a dynamic copy of the argument. 53171626Scognet */ 54171626Scognetchar * 55171626Scognetsavestr(str) 56171626Scognet char *str; 57171626Scognet{ 58171626Scognet char *new; 59171626Scognet int size = strlen(str) + 1; 60171626Scognet 61171626Scognet if ((new = salloc(size)) != NULL) 62171626Scognet bcopy(str, new, size); 63171626Scognet return (new); 64171626Scognet} 65171626Scognet 66171626Scognet/* 67171626Scognet * Make a copy of new argument incorporating old one. 68171626Scognet */ 69171626Scognetstatic char * 70171626Scognetsave2str(str, old) 71171626Scognet char *str, *old; 72171626Scognet{ 73171626Scognet char *new; 74171626Scognet int newsize = strlen(str) + 1; 75171626Scognet int oldsize = old ? strlen(old) + 1 : 0; 76171626Scognet 77171626Scognet if ((new = salloc(newsize + oldsize)) != NULL) { 78171626Scognet if (oldsize) { 79171626Scognet bcopy(old, new, oldsize); 80171626Scognet new[oldsize - 1] = ' '; 81257660Sian } 82171626Scognet bcopy(str, new + oldsize, newsize); 83171626Scognet } 84171626Scognet return (new); 85171626Scognet} 86171626Scognet 87171626Scognet/* 88171626Scognet * Touch the named message by setting its MTOUCH flag. 89261649Sian * Touched messages have the effect of not being sent 90171626Scognet * back to the system mailbox on exit. 91171626Scognet */ 92171626Scognetvoid 93171626Scognettouch(mp) 94171626Scognet struct message *mp; 95171626Scognet{ 96171626Scognet 97171626Scognet mp->m_flag |= MTOUCH; 98171626Scognet if ((mp->m_flag & MREAD) == 0) 99171626Scognet mp->m_flag |= MREAD|MSTATUS; 100171626Scognet} 101171626Scognet 102171626Scognet/* 103171626Scognet * Test to see if the passed file name is a directory. 104171626Scognet * Return true if it is. 105171626Scognet */ 106171626Scognetint 107171626Scognetisdir(name) 108171626Scognet char name[]; 109171626Scognet{ 110171626Scognet struct stat sbuf; 111171626Scognet 112171626Scognet if (stat(name, &sbuf) < 0) 113171626Scognet return (0); 114171626Scognet return (S_ISDIR(sbuf.st_mode)); 115171626Scognet} 116171626Scognet 117171626Scognet/* 118171626Scognet * Count the number of arguments in the given string raw list. 119171626Scognet */ 120171626Scognetint 121257660Sianargcount(argv) 122171626Scognet char **argv; 123171626Scognet{ 124171626Scognet char **ap; 125171626Scognet 126171626Scognet for (ap = argv; *ap++ != NULL;) 127265852Sian ; 128171626Scognet return (ap - argv - 1); 129171626Scognet} 130171626Scognet 131171626Scognet/* 132171626Scognet * Return the desired header line from the passed message 133171626Scognet * pointer (or NULL if the desired header field is not available). 134171626Scognet */ 135171626Scognetchar * 136171626Scognethfield(field, mp) 137171626Scognet const char *field; 138265852Sian struct message *mp; 139171626Scognet{ 140172297Scognet FILE *ibuf; 141172297Scognet char linebuf[LINESIZE]; 142172297Scognet int lc; 143172297Scognet char *hfield; 144172297Scognet char *colon, *oldhfield = NULL; 145265852Sian 146172297Scognet ibuf = setinput(mp); 147236987Simp if ((lc = mp->m_lines - 1) < 0) 148171626Scognet return (NULL); 149171626Scognet if (readline(ibuf, linebuf, LINESIZE) < 0) 150171626Scognet return (NULL); 151171626Scognet while (lc > 0) { 152171626Scognet if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 153171626Scognet return (oldhfield); 154171626Scognet if ((hfield = ishfield(linebuf, colon, field)) != NULL) 155171626Scognet oldhfield = save2str(hfield, oldhfield); 156171626Scognet } 157171626Scognet return (oldhfield); 158171626Scognet} 159171626Scognet 160171626Scognet/* 161236524Simp * Return the next header field found in the given message. 162171626Scognet * Return >= 0 if something found, < 0 elsewise. 163171626Scognet * "colon" is set to point to the colon in the header. 164194784Sjeff * Must deal with \ continuations & other such fraud. 165177883Simp */ 166171626Scognetint 167171626Scognetgethfield(f, linebuf, rem, colon) 168171626Scognet FILE *f; 169171626Scognet char linebuf[]; 170171626Scognet int rem; 171171626Scognet char **colon; 172171626Scognet{ 173171626Scognet char line2[LINESIZE]; 174237040Simp char *cp, *cp2; 175261649Sian int c; 176171626Scognet 177171626Scognet for (;;) { 178171626Scognet if (--rem < 0) 179171626Scognet return (-1); 180220836Spluknet if ((c = readline(f, linebuf, LINESIZE)) <= 0) 181220836Spluknet return (-1); 182220836Spluknet for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':'; 183171626Scognet cp++) 184171626Scognet ; 185171626Scognet if (*cp != ':' || cp == linebuf) 186171626Scognet continue; 187171626Scognet /* 188171626Scognet * I guess we got a headline. 189171626Scognet * Handle wraparounding 190171626Scognet */ 191171626Scognet *colon = cp; 192171626Scognet cp = linebuf + c; 193171626Scognet for (;;) { 194171626Scognet while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 195171626Scognet ; 196171626Scognet cp++; 197171626Scognet if (rem <= 0) 198171626Scognet break; 199171626Scognet ungetc(c = getc(f), f); 200171626Scognet if (c != ' ' && c != '\t') 201171626Scognet break; 202171626Scognet if ((c = readline(f, line2, LINESIZE)) < 0) 203171626Scognet break; 204171626Scognet rem--; 205236987Simp for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 206171626Scognet ; 207171626Scognet c -= cp2 - line2; 208171626Scognet if (cp + c >= linebuf + LINESIZE - 2) 209171626Scognet break; 210171626Scognet *cp++ = ' '; 211171626Scognet bcopy(cp2, cp, c); 212171626Scognet cp += c; 213171626Scognet } 214171626Scognet *cp = 0; 215171626Scognet return (rem); 216171626Scognet } 217171626Scognet /* NOTREACHED */ 218194784Sjeff} 219194784Sjeff 220194784Sjeff/* 221194784Sjeff * Check whether the passed line is a header line of 222171626Scognet * the desired breed. Return the field body, or 0. 223171626Scognet */ 224171626Scognet 225171626Scognetchar* 226171626Scognetishfield(linebuf, colon, field) 227217688Spluknet char linebuf[]; 228171626Scognet char *colon; 229171626Scognet const char *field; 230171626Scognet{ 231171626Scognet char *cp = colon; 232171626Scognet 233171626Scognet *cp = 0; 234171626Scognet if (strcasecmp(linebuf, field) != 0) { 235171626Scognet *cp = ':'; 236171626Scognet return (0); 237171626Scognet } 238171626Scognet *cp = ':'; 239171626Scognet for (cp++; *cp == ' ' || *cp == '\t'; cp++) 240171626Scognet ; 241171626Scognet return (cp); 242171626Scognet} 243171626Scognet 244171626Scognet/* 245171626Scognet * Copy a string and lowercase the result. 246171626Scognet * dsize: space left in buffer (including space for NULL) 247171626Scognet */ 248236987Simpvoid 249171626Scognetistrncpy(dest, src, dsize) 250171626Scognet char *dest; 251171626Scognet const char *src; 252171626Scognet size_t dsize; 253171626Scognet{ 254171626Scognet 255171626Scognet strlcpy(dest, src, dsize); 256171626Scognet while (*dest) 257171626Scognet *dest++ = tolower((unsigned char)*dest); 258171626Scognet} 259257660Sian 260171626Scognet/* 261171626Scognet * The following code deals with input stacking to do source 262171626Scognet * commands. All but the current file pointer are saved on 263171626Scognet * the stack. 264171626Scognet */ 265171626Scognet 266171626Scognetstatic int ssp; /* Top of file stack */ 267171626Scognetstruct sstack { 268171626Scognet FILE *s_file; /* File we were in. */ 269171626Scognet int s_cond; /* Saved state of conditionals */ 270171626Scognet int s_loading; /* Loading .mailrc, etc. */ 271171626Scognet}; 272171626Scognet#define SSTACK_SIZE 64 /* XXX was NOFILE. */ 273171626Scognetstatic struct sstack sstack[SSTACK_SIZE]; 274171626Scognet 275171626Scognet/* 276171626Scognet * Pushdown current input file and switch to a new one. 277171626Scognet * Set the global flag "sourcing" so that others will realize 278171626Scognet * that they are no longer reading from a tty (in all probability). 279171626Scognet */ 280240802Sandrewint 281171626Scognetsource(arglist) 282171626Scognet char **arglist; 283171626Scognet{ 284171626Scognet FILE *fi; 285171626Scognet char *cp; 286171626Scognet 287171626Scognet if ((cp = expand(*arglist)) == NULL) 288171626Scognet return (1); 289185513Sstas if ((fi = Fopen(cp, "r")) == NULL) { 290171626Scognet warn("%s", cp); 291171626Scognet return (1); 292171626Scognet } 293258412Sian if (ssp >= SSTACK_SIZE - 1) { 294258412Sian printf("Too much \"sourcing\" going on.\n"); 295171626Scognet (void)Fclose(fi); 296171626Scognet return (1); 297171626Scognet } 298171626Scognet sstack[ssp].s_file = input; 299171626Scognet sstack[ssp].s_cond = cond; 300171626Scognet sstack[ssp].s_loading = loading; 301171626Scognet ssp++; 302171626Scognet loading = 0; 303236828Sandrew cond = CANY; 304171626Scognet input = fi; 305171626Scognet sourcing++; 306171626Scognet return (0); 307171626Scognet} 308261642Sian 309256712Scognet/* 310247046Salc * Pop the current input back to the previous level. 311171626Scognet * Update the "sourcing" flag as appropriate. 312217688Spluknet */ 313171626Scognetint 314261698Sianunstack() 315261698Sian{ 316261698Sian if (ssp <= 0) { 317261698Sian printf("\"Source\" stack over-pop.\n"); 318261698Sian sourcing = 0; 319261698Sian return (1); 320261698Sian } 321261698Sian (void)Fclose(input); 322261698Sian if (cond != CANY) 323261698Sian printf("Unmatched \"if\"\n"); 324261698Sian ssp--; 325261698Sian cond = sstack[ssp].s_cond; 326266850Scognet loading = sstack[ssp].s_loading; 327266850Scognet input = sstack[ssp].s_file; 328266850Scognet if (ssp == 0) 329266850Scognet sourcing = loading; 330261698Sian return (0); 331261698Sian} 332261698Sian 333261698Sian/* 334171626Scognet * Touch the indicated file. 335171626Scognet * This is nifty for the shell. 336171626Scognet */ 337171626Scognetvoid 338171626Scognetalter(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 */ 357char * 358nameof(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 */ 380char * 381skip_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 */ 407char * 408skin(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 */ 519char * 520name1(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)); 538newname: 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 */ 580int 581charcount(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 */ 597int 598isign(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 617int 618member(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