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