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