1 /* $NetBSD: support.c,v 1.16 2006/10/31 20:07:32 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93"; 36 #else 37 __RCSID("$NetBSD: support.c,v 1.16 2006/10/31 20:07:32 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include "rcv.h" 42 #include "extern.h" 43 #include "mime.h" 44 45 /* 46 * Mail -- a mail program 47 * 48 * Auxiliary functions. 49 */ 50 static char *save2str(char *, char *); 51 static int gethfield(FILE *, char [], int, char **); /* don't call this outside hfield() 52 or decoding will not get done */ 53 /* 54 * Return a pointer to a dynamic copy of the argument. 55 */ 56 char * 57 savestr(const char *str) 58 { 59 char *new; 60 size_t size = strlen(str) + 1; 61 62 if ((new = salloc(size)) != NULL) 63 (void)memmove(new, str, size); 64 return new; 65 } 66 67 /* 68 * Make a copy of new argument incorporating old one. 69 */ 70 static char * 71 save2str(char *str, char *old) 72 { 73 char *new; 74 size_t newsize = strlen(str) + 1; 75 size_t oldsize = old ? strlen(old) + 1 : 0; 76 77 if ((new = salloc(newsize + oldsize)) != NULL) { 78 if (oldsize) { 79 (void)memmove(new, old, oldsize); 80 new[oldsize - 1] = ' '; 81 } 82 (void)memmove(new + oldsize, str, 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 */ 92 void 93 touch(struct message *mp) 94 { 95 96 mp->m_flag |= MTOUCH; 97 if ((mp->m_flag & MREAD) == 0) 98 mp->m_flag |= MREAD|MSTATUS; 99 } 100 101 /* 102 * Test to see if the passed file name is a directory. 103 * Return true if it is. 104 */ 105 int 106 isdir(const char name[]) 107 { 108 struct stat sbuf; 109 110 if (stat(name, &sbuf) < 0) 111 return(0); 112 return (S_ISDIR(sbuf.st_mode)); 113 } 114 115 /* 116 * Count the number of arguments in the given string raw list. 117 */ 118 int 119 argcount(char **argv) 120 { 121 char **ap; 122 123 for (ap = argv; *ap++ != NULL;) 124 ; 125 return ap - argv - 1; 126 } 127 128 /* 129 * Return the desired header line from the passed message 130 * pointer (or NULL if the desired header field is not available). 131 */ 132 char * 133 hfield(const char field[], const struct message *mp) 134 { 135 FILE *ibuf; 136 char linebuf[LINESIZE]; 137 int lc; 138 char *headerfield; 139 char *colon, *oldhfield = NULL; 140 #ifdef MIME_SUPPORT 141 int decode; 142 143 decode = value(ENAME_MIME_DECODE_MSG) && 144 value(ENAME_MIME_DECODE_HDR); 145 #endif 146 147 ibuf = setinput(mp); 148 if ((lc = mp->m_lines - 1) < 0) 149 return NULL; 150 if (mail_readline(ibuf, linebuf, LINESIZE) < 0) 151 return NULL; 152 while (lc > 0) { 153 if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 154 return oldhfield; 155 #ifdef MIME_SUPPORT 156 if ((headerfield = ishfield(linebuf, colon, field)) != NULL) { 157 char linebuf2[LINESIZE]; 158 if (decode && colon) 159 headerfield = mime_decode_hfield(linebuf2, sizeof(linebuf2), headerfield); 160 oldhfield = save2str(headerfield, oldhfield); 161 } 162 #else 163 if ((headerfield = ishfield(linebuf, colon, field)) != NULL) 164 oldhfield = save2str(headerfield, oldhfield); 165 #endif 166 } 167 return oldhfield; 168 } 169 170 /* 171 * Return the next header field found in the given message. 172 * Return >= 0 if something found, < 0 elsewise. 173 * "colon" is set to point to the colon in the header. 174 * Must deal with \ continuations & other such fraud. 175 */ 176 static int 177 gethfield(FILE *f, char linebuf[], int rem, 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 = mail_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 (void)ungetc(c = getc(f), f); 206 if (c != ' ' && c != '\t') 207 break; 208 if ((c = mail_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 (void)memmove(cp, cp2, (size_t)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 231 char* 232 ishfield(const char linebuf[], char *colon, const char field[]) 233 { 234 char *cp = colon; 235 236 *cp = 0; 237 if (strcasecmp(linebuf, field) != 0) { 238 *cp = ':'; 239 return 0; 240 } 241 *cp = ':'; 242 for (cp++; *cp == ' ' || *cp == '\t'; cp++) 243 ; 244 return cp; 245 } 246 247 /* 248 * Copy a string, lowercasing it as we go. 249 */ 250 void 251 istrcpy(char *dest, const char *src) 252 { 253 254 do { 255 *dest++ = tolower((unsigned char)*src); 256 } while (*src++ != 0); 257 } 258 259 /* 260 * The following code deals with input stacking to do source 261 * commands. All but the current file pointer are saved on 262 * the stack. 263 */ 264 265 static int ssp; /* Top of file stack */ 266 struct sstack { 267 FILE *s_file; /* File we were in. */ 268 int s_cond; /* Saved state of conditionals */ 269 int s_loading; /* Loading .mailrc, etc. */ 270 } sstack[NOFILE]; 271 272 /* 273 * Pushdown current input file and switch to a new one. 274 * Set the global flag "sourcing" so that others will realize 275 * that they are no longer reading from a tty (in all probability). 276 */ 277 int 278 source(void *v) 279 { 280 char **arglist = v; 281 FILE *fi; 282 const char *cp; 283 284 if ((cp = expand(*arglist)) == NULL) 285 return(1); 286 if ((fi = Fopen(cp, "r")) == NULL) { 287 warn("%s", cp); 288 return(1); 289 } 290 if (ssp >= NOFILE - 1) { 291 (void)printf("Too much \"sourcing\" going on.\n"); 292 (void)Fclose(fi); 293 return(1); 294 } 295 sstack[ssp].s_file = input; 296 sstack[ssp].s_cond = cond; 297 sstack[ssp].s_loading = loading; 298 ssp++; 299 loading = 0; 300 cond = CANY; 301 input = fi; 302 sourcing++; 303 return(0); 304 } 305 306 /* 307 * Pop the current input back to the previous level. 308 * Update the "sourcing" flag as appropriate. 309 */ 310 int 311 unstack(void) 312 { 313 if (ssp <= 0) { 314 (void)printf("\"Source\" stack over-pop.\n"); 315 sourcing = 0; 316 return(1); 317 } 318 (void)Fclose(input); 319 if (cond != CANY) 320 (void)printf("Unmatched \"if\"\n"); 321 ssp--; 322 cond = sstack[ssp].s_cond; 323 loading = sstack[ssp].s_loading; 324 input = sstack[ssp].s_file; 325 if (ssp == 0) 326 sourcing = loading; 327 return(0); 328 } 329 330 /* 331 * Touch the indicated file. 332 * This is nifty for the shell. 333 */ 334 void 335 alter(char *name) 336 { 337 struct stat sb; 338 struct timeval tv[2]; 339 340 if (stat(name, &sb)) 341 return; 342 (void)gettimeofday(&tv[0], NULL); 343 tv[0].tv_sec++; 344 TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); 345 (void)utimes(name, tv); 346 } 347 348 /* 349 * Examine the passed line buffer and 350 * return true if it is all blanks and tabs. 351 */ 352 int 353 blankline(char linebuf[]) 354 { 355 char *cp; 356 357 for (cp = linebuf; *cp; cp++) 358 if (*cp != ' ' && *cp != '\t') 359 return(0); 360 return(1); 361 } 362 363 /* 364 * Get sender's name from this message. If the message has 365 * a bunch of arpanet stuff in it, we may have to skin the name 366 * before returning it. 367 */ 368 char * 369 nameof(struct message *mp, int reptype) 370 { 371 char *cp, *cp2; 372 373 cp = skin(name1(mp, reptype)); 374 if (reptype != 0 || charcount(cp, '!') < 2) 375 return(cp); 376 cp2 = strrchr(cp, '!'); 377 cp2--; 378 while (cp2 > cp && *cp2 != '!') 379 cp2--; 380 if (*cp2 == '!') 381 return(cp2 + 1); 382 return(cp); 383 } 384 385 /* 386 * Start of a "comment". 387 * Ignore it. 388 */ 389 char * 390 skip_comment(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 */ 415 char * 416 skin(char *name) 417 { 418 int c; 419 char *cp, *cp2; 420 char *bufend; 421 int gotlt, lastsp; 422 char nbuf[BUFSIZ]; 423 424 if (name == NULL) 425 return(NULL); 426 if (strchr(name, '(') == NULL && strchr(name, '<') == NULL 427 && strchr(name, ' ') == NULL) 428 return(name); 429 gotlt = 0; 430 lastsp = 0; 431 bufend = nbuf; 432 for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; /* EMPTY */) { 433 switch (c) { 434 case '(': 435 cp = skip_comment(cp); 436 lastsp = 0; 437 break; 438 439 case '"': 440 /* 441 * Start of a "quoted-string". 442 * Copy it in its entirety. 443 */ 444 while ((c = *cp) != '\0') { 445 cp++; 446 if (c == '"') 447 break; 448 if (c != '\\') 449 *cp2++ = c; 450 else if ((c = *cp) != '\0') { 451 *cp2++ = c; 452 cp++; 453 } 454 } 455 lastsp = 0; 456 break; 457 458 case ' ': 459 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 460 cp += 3, *cp2++ = '@'; 461 else 462 if (cp[0] == '@' && cp[1] == ' ') 463 cp += 2, *cp2++ = '@'; 464 else 465 lastsp = 1; 466 break; 467 468 case '<': 469 cp2 = bufend; 470 gotlt++; 471 lastsp = 0; 472 break; 473 474 case '>': 475 if (gotlt) { 476 gotlt = 0; 477 while ((c = *cp) && c != ',') { 478 cp++; 479 if (c == '(') 480 cp = skip_comment(cp); 481 else if (c == '"') 482 while ((c = *cp) != '\0') { 483 cp++; 484 if (c == '"') 485 break; 486 if (c == '\\' && *cp) 487 cp++; 488 } 489 } 490 lastsp = 0; 491 break; 492 } 493 /* FALLTHROUGH */ 494 495 default: 496 if (lastsp) { 497 lastsp = 0; 498 *cp2++ = ' '; 499 } 500 *cp2++ = c; 501 if (c == ',' && !gotlt) { 502 *cp2++ = ' '; 503 for (; *cp == ' '; cp++) 504 ; 505 lastsp = 0; 506 bufend = cp2; 507 } 508 } 509 } 510 *cp2 = 0; 511 512 return(savestr(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(struct message *mp, int reptype) 524 { 525 char namebuf[LINESIZE]; 526 char linebuf[LINESIZE]; 527 char *cp, *cp2; 528 FILE *ibuf; 529 int firstrun = 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 (mail_readline(ibuf, linebuf, LINESIZE) < 0) 538 return(savestr(namebuf)); 539 newname: 540 for (cp = linebuf; *cp && *cp != ' '; cp++) 541 ; 542 for (; *cp == ' ' || *cp == '\t'; cp++) 543 ; 544 for (cp2 = &namebuf[strlen(namebuf)]; 545 *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 546 *cp2++ = *cp++; 547 *cp2 = '\0'; 548 if (mail_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 (firstrun) { 564 cp2 = namebuf; 565 firstrun = 0; 566 } else 567 cp2 = strrchr(namebuf, '!') + 1; 568 while (*cp && cp2 < namebuf + LINESIZE - 1) 569 *cp2++ = *cp++; 570 if (cp2 < namebuf + LINESIZE - 1) 571 *cp2++ = '!'; 572 *cp2 = '\0'; 573 if (cp2 < namebuf + LINESIZE - 1) 574 goto newname; 575 else 576 break; 577 } 578 cp++; 579 } 580 return(savestr(namebuf)); 581 } 582 583 /* 584 * Count the occurrences of c in str 585 */ 586 int 587 charcount(char *str, int c) 588 { 589 char *cp; 590 int i; 591 592 for (i = 0, cp = str; *cp; cp++) 593 if (*cp == c) 594 i++; 595 return(i); 596 } 597 598 /* 599 * Convert c to upper case 600 */ 601 int 602 upcase(int c) 603 { 604 605 if (islower(c)) 606 return toupper(c); 607 return c; 608 } 609 610 /* 611 * Copy s1 to s2, return pointer to null in s2. 612 */ 613 char * 614 copy(char *s1, char *s2) 615 { 616 617 while ((*s2++ = *s1++) != '\0') 618 ; 619 return s2 - 1; 620 } 621 622 /* 623 * See if the given header field is supposed to be ignored. 624 */ 625 int 626 isign(const char *field, struct ignoretab ignoretabs[2]) 627 { 628 char realfld[LINESIZE]; 629 630 if (ignoretabs == ignoreall) 631 return 1; 632 /* 633 * Lower-case the string, so that "Status" and "status" 634 * will hash to the same place. 635 */ 636 istrcpy(realfld, field); 637 if (ignoretabs[1].i_count > 0) 638 return (!member(realfld, ignoretabs + 1)); 639 else 640 return (member(realfld, ignoretabs)); 641 } 642 643 int 644 member(char *realfield, struct ignoretab *table) 645 { 646 struct ignore *igp; 647 648 for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 649 if (*igp->i_field == *realfield && 650 equal(igp->i_field, realfield)) 651 return (1); 652 return (0); 653 } 654