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