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