1 /* $NetBSD: cmd2.c,v 1.23 2007/10/27 15:14:50 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[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93"; 36 #else 37 __RCSID("$NetBSD: cmd2.c,v 1.23 2007/10/27 15:14:50 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include "rcv.h" 42 #include <util.h> 43 #include "extern.h" 44 #ifdef MIME_SUPPORT 45 #include "mime.h" 46 #endif 47 #include "thread.h" 48 49 /* 50 * Mail -- a mail program 51 * 52 * More user commands. 53 */ 54 55 /* 56 * If any arguments were given, go to the next applicable argument 57 * following dot, otherwise, go to the next applicable message. 58 * If given as first command with no arguments, print first message. 59 */ 60 PUBLIC int 61 next(void *v) 62 { 63 int *msgvec = v; 64 struct message *mp; 65 int *ip, *ip2; 66 int list[2], mdot; 67 68 if (*msgvec != 0) { 69 70 /* 71 * If some messages were supplied, find the 72 * first applicable one following dot using 73 * wrap around. 74 */ 75 mdot = get_msgnum(dot); 76 77 /* 78 * Find the first message in the supplied 79 * message list which follows dot. 80 */ 81 82 for (ip = msgvec; *ip != 0; ip++) 83 if (*ip > mdot) 84 break; 85 if (*ip == 0) 86 ip = msgvec; 87 ip2 = ip; 88 do { 89 mp = get_message(*ip2); 90 if ((mp->m_flag & MDELETED) == 0) { 91 dot = mp; 92 goto hitit; 93 } 94 if (*ip2 != 0) 95 ip2++; 96 if (*ip2 == 0) 97 ip2 = msgvec; 98 } while (ip2 != ip); 99 (void)printf("No messages applicable\n"); 100 return 1; 101 } 102 103 /* 104 * If this is the first command, select message 1. 105 * Note that this must exist for us to get here at all. 106 */ 107 108 if (!sawcom) 109 goto hitit; 110 111 /* 112 * Just find the next good message after dot, no 113 * wraparound. 114 */ 115 116 for (mp = next_message(dot); mp; mp = next_message(mp)) 117 if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 118 break; 119 120 if (mp == NULL) { 121 (void)printf("At EOF\n"); 122 return 0; 123 } 124 dot = mp; 125 hitit: 126 /* 127 * Print dot. 128 */ 129 130 list[0] = get_msgnum(dot); 131 list[1] = 0; 132 return type(list); 133 } 134 135 /* 136 * Snarf the file from the end of the command line and 137 * return a pointer to it. If there is no file attached, 138 * just return NULL. Put a null in front of the file 139 * name so that the message list processing won't see it, 140 * unless the file name is the only thing on the line, in 141 * which case, return 0 in the reference flag variable. 142 */ 143 static char * 144 snarf(char linebuf[], int *flag, const char *string) 145 { 146 char *cp; 147 148 *flag = 1; 149 cp = strlen(linebuf) + linebuf - 1; 150 151 /* 152 * Strip away trailing blanks. 153 */ 154 155 while (cp > linebuf && isspace((unsigned char)*cp)) 156 cp--; 157 *++cp = 0; 158 159 /* 160 * Now search for the beginning of the file name. 161 */ 162 163 while (cp > linebuf && !isspace((unsigned char)*cp)) 164 cp--; 165 if (*cp == '\0') { 166 (void)printf("No %s specified.\n", string); 167 return NULL; 168 } 169 if (isspace((unsigned char)*cp)) 170 *cp++ = 0; 171 else 172 *flag = 0; 173 return cp; 174 } 175 176 struct save1_core_args_s { 177 FILE *obuf; 178 struct ignoretab *igtab; 179 int markmsg; 180 }; 181 static int 182 save1_core(struct message *mp, void *v) 183 { 184 struct save1_core_args_s *args; 185 args = v; 186 187 touch(mp); 188 189 if (sendmessage(mp, args->obuf, args->igtab, NULL, NULL) < 0) 190 return -1; 191 192 if (args->markmsg) 193 mp->m_flag |= MSAVED; 194 195 return 0; 196 } 197 198 /* 199 * Save/copy the indicated messages at the end of the passed file name. 200 * If markmsg is true, mark the message "saved." 201 */ 202 static int 203 save1(char str[], int markmsg, const char *cmd, struct ignoretab *igtab) 204 { 205 int *ip; 206 const char *fn; 207 const char *disp; 208 int f, *msgvec; 209 int msgCount; 210 FILE *obuf; 211 212 msgCount = get_msgCount(); 213 msgvec = salloc((msgCount + 2) * sizeof(*msgvec)); 214 if ((fn = snarf(str, &f, "file")) == NULL) 215 return 1; 216 if (!f) { 217 *msgvec = first(0, MMNORM); 218 if (*msgvec == 0) { 219 (void)printf("No messages to %s.\n", cmd); 220 return 1; 221 } 222 msgvec[1] = 0; 223 } 224 if (f && getmsglist(str, msgvec, 0) < 0) 225 return 1; 226 if ((fn = expand(fn)) == NULL) 227 return 1; 228 (void)printf("\"%s\" ", fn); 229 (void)fflush(stdout); 230 if (access(fn, 0) >= 0) 231 disp = "[Appended]"; 232 else 233 disp = "[New file]"; 234 if ((obuf = Fopen(fn, "a")) == NULL) { 235 warn(NULL); 236 return 1; 237 } 238 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 239 struct save1_core_args_s args; 240 struct message *mp; 241 242 args.obuf = obuf; 243 args.igtab = igtab; 244 args.markmsg = markmsg; 245 mp = get_message(*ip); 246 if (thread_recursion(mp, save1_core, &args)) { 247 warn("%s", fn); 248 (void)Fclose(obuf); 249 return 1; 250 } 251 } 252 (void)fflush(obuf); 253 if (ferror(obuf)) 254 warn("%s", fn); 255 (void)Fclose(obuf); 256 (void)printf("%s\n", disp); 257 return 0; 258 } 259 260 /* 261 * Save a message in a file. Mark the message as saved 262 * so we can discard when the user quits. 263 */ 264 PUBLIC int 265 save(void *v) 266 { 267 char *str = v; 268 269 return save1(str, 1, "save", saveignore); 270 } 271 272 /* 273 * Save a message in a file. Mark the message as saved 274 * so we can discard when the user quits. Save all fields 275 * overriding saveignore and saveretain. 276 */ 277 PUBLIC int 278 Save(void *v) 279 { 280 char *str = v; 281 282 return save1(str, 1, "Save", NULL); 283 } 284 285 /* 286 * Copy a message to a file without affected its saved-ness 287 */ 288 PUBLIC int 289 copycmd(void *v) 290 { 291 char *str = v; 292 293 return save1(str, 0, "copy", saveignore); 294 } 295 296 /* 297 * Write the indicated messages at the end of the passed 298 * file name, minus header and trailing blank line. 299 */ 300 PUBLIC int 301 swrite(void *v) 302 { 303 char *str = v; 304 305 return save1(str, 1, "write", ignoreall); 306 } 307 308 /* 309 * Delete the indicated messages. 310 * Set dot to some nice place afterwards. 311 * Internal interface. 312 */ 313 static int 314 delm(int *msgvec) 315 { 316 struct message *mp; 317 int *ip; 318 int last; 319 320 last = 0; 321 for (ip = msgvec; *ip != 0; ip++) { 322 mp = set_m_flag(*ip, 323 ~(MPRESERVE|MSAVED|MBOX|MDELETED|MTOUCH), MDELETED|MTOUCH); 324 touch(mp); 325 last = *ip; 326 } 327 if (last != 0) { 328 dot = get_message(last); 329 last = first(0, MDELETED); 330 if (last != 0) { 331 dot = get_message(last); 332 return 0; 333 } 334 else { 335 dot = get_message(1); 336 return -1; 337 } 338 } 339 340 /* 341 * Following can't happen -- it keeps lint happy 342 */ 343 return -1; 344 } 345 346 /* 347 * Delete messages. 348 */ 349 PUBLIC int 350 delete(void *v) 351 { 352 int *msgvec = v; 353 (void)delm(msgvec); 354 return 0; 355 } 356 357 /* 358 * Delete messages, then type the new dot. 359 */ 360 PUBLIC int 361 deltype(void *v) 362 { 363 int *msgvec = v; 364 int list[2]; 365 int lastdot; 366 367 lastdot = get_msgnum(dot); 368 if (delm(msgvec) >= 0) { 369 list[0] = get_msgnum(dot); 370 if (list[0] > lastdot) { 371 touch(dot); 372 list[1] = 0; 373 return type(list); 374 } 375 (void)printf("At EOF\n"); 376 } else 377 (void)printf("No more messages\n"); 378 return 0; 379 } 380 381 /* 382 * Undelete the indicated messages. 383 */ 384 PUBLIC int 385 undeletecmd(void *v) 386 { 387 int msgCount; 388 int *msgvec; 389 int *ip; 390 391 msgvec = v; 392 msgCount = get_msgCount(); 393 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 394 dot = set_m_flag(*ip, ~MDELETED, 0); 395 touch(dot); 396 dot->m_flag &= ~MDELETED; 397 } 398 return 0; 399 } 400 401 /*************************************************************************/ 402 403 /* 404 * Interactively dump core on "core" 405 */ 406 /*ARGSUSED*/ 407 PUBLIC int 408 core(void *v __unused) 409 { 410 int pid; 411 412 switch (pid = vfork()) { 413 case -1: 414 warn("fork"); 415 return 1; 416 case 0: 417 abort(); 418 _exit(1); 419 } 420 (void)printf("Okie dokie"); 421 (void)fflush(stdout); 422 (void)wait_child(pid); 423 if (WCOREDUMP(wait_status)) 424 (void)printf(" -- Core dumped.\n"); 425 else 426 (void)printf(" -- Can't dump core.\n"); 427 return 0; 428 } 429 430 /* 431 * Clobber the stack. 432 */ 433 static void 434 clob1(int n) 435 { 436 char buf[512]; 437 char *cp; 438 439 if (n <= 0) 440 return; 441 for (cp = buf; cp < &buf[512]; *cp++ = (char)0xFF) 442 continue; 443 clob1(n - 1); 444 } 445 446 /* 447 * Clobber as many bytes of stack as the user requests. 448 */ 449 PUBLIC int 450 clobber(void *v) 451 { 452 char **argv = v; 453 int times; 454 455 if (argv[0] == 0) 456 times = 1; 457 else 458 times = (atoi(argv[0]) + 511) / 512; 459 clob1(times); 460 return 0; 461 } 462 463 /* 464 * Compare two names for sorting ignored field list. 465 */ 466 static int 467 igcomp(const void *l, const void *r) 468 { 469 return strcmp(*(const char *const *)l, *(const char *const *)r); 470 } 471 472 /* 473 * Print out all currently retained fields. 474 */ 475 static int 476 igshow(struct ignoretab *tab, const char *which) 477 { 478 int h; 479 struct ignore *igp; 480 char **ap, **ring; 481 482 if (tab->i_count == 0) { 483 (void)printf("No fields currently being %s.\n", which); 484 return 0; 485 } 486 ring = salloc((tab->i_count + 1) * sizeof(char *)); 487 ap = ring; 488 for (h = 0; h < HSHSIZE; h++) 489 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) 490 *ap++ = igp->i_field; 491 *ap = 0; 492 qsort(ring, tab->i_count, sizeof(char *), igcomp); 493 for (ap = ring; *ap != 0; ap++) 494 (void)printf("%s\n", *ap); 495 return 0; 496 } 497 498 /* 499 * core ignore routine. 500 */ 501 static int 502 ignore1(char *list[], struct ignoretab *tab, const char *which) 503 { 504 char **ap; 505 506 if (*list == NULL) 507 return igshow(tab, which); 508 509 for (ap = list; *ap != 0; ap++) 510 add_ignore(*ap, tab); 511 512 return 0; 513 } 514 515 /* 516 * Add the given header fields to the retained list. 517 * If no arguments, print the current list of retained fields. 518 */ 519 PUBLIC int 520 retfield(void *v) 521 { 522 char **list = v; 523 524 return ignore1(list, ignore + 1, "retained"); 525 } 526 527 /* 528 * Add the given header fields to the ignored list. 529 * If no arguments, print the current list of ignored fields. 530 */ 531 PUBLIC int 532 igfield(void *v) 533 { 534 char **list = v; 535 536 return ignore1(list, ignore, "ignored"); 537 } 538 539 /* 540 * Add the given header fields to the save retained list. 541 * If no arguments, print the current list of save retained fields. 542 */ 543 PUBLIC int 544 saveretfield(void *v) 545 { 546 char **list = v; 547 548 return ignore1(list, saveignore + 1, "retained"); 549 } 550 551 /* 552 * Add the given header fields to the save ignored list. 553 * If no arguments, print the current list of save ignored fields. 554 */ 555 PUBLIC int 556 saveigfield(void *v) 557 { 558 char **list = v; 559 560 return ignore1(list, saveignore, "ignored"); 561 } 562 563 #ifdef MIME_SUPPORT 564 565 static char* 566 check_dirname(char *filename) 567 { 568 struct stat sb; 569 char *fname; 570 char canon_buf[MAXPATHLEN]; 571 char *canon_name; 572 573 canon_name = canon_buf; 574 fname = filename; 575 if (fname[0] == '~' && fname[1] == '/') { 576 if (homedir && homedir[0] != '~') 577 (void)easprintf(&fname, "%s/%s", 578 homedir, fname + 2); 579 } 580 if (realpath(fname, canon_name) == NULL) { 581 warn("realpath: %s", filename); 582 canon_name = NULL; 583 goto done; 584 } 585 if (stat(canon_name, &sb) == -1) { 586 warn("stat: %s", canon_name); 587 canon_name = NULL; 588 goto done; 589 } 590 if (!S_ISDIR(sb.st_mode)) { 591 warnx("stat: %s is not a directory", canon_name); 592 canon_name = NULL; 593 goto done; 594 } 595 if (access(canon_name, W_OK|X_OK) == -1) { 596 warnx("access: %s is not writable", canon_name); 597 canon_name = NULL; 598 /* goto done; */ 599 } 600 done: 601 if (fname != filename) 602 free(fname); 603 604 return canon_name ? savestr(canon_name) : NULL; 605 } 606 607 608 struct detach1_core_args_s { 609 struct message *parent; 610 struct ignoretab *igtab; 611 const char *dstdir; 612 }; 613 static int 614 detach1_core(struct message *mp, void *v) 615 { 616 struct mime_info *mip; 617 struct detach1_core_args_s *args; 618 619 args = v; 620 touch(mp); 621 show_msgnum(stdout, mp, args->parent); 622 mip = mime_decode_open(mp); 623 mime_detach_msgnum(mip, sget_msgnum(mp, args->parent)); 624 (void)mime_sendmessage(mp, NULL, args->igtab, args->dstdir, mip); 625 mime_decode_close(mip); 626 return 0; 627 } 628 629 /* 630 * detach attachments. 631 */ 632 static int 633 detach1(void *v, int do_unnamed) 634 { 635 int recursive; 636 int f; 637 int msgCount; 638 int *msgvec; 639 int *ip; 640 char *str; 641 char *dstdir; 642 643 str = v; 644 645 /* 646 * Get the destination directory. 647 */ 648 if ((dstdir = snarf(str, &f, "directory")) == NULL && 649 (dstdir = value(ENAME_MIME_DETACH_DIR)) == NULL && 650 (dstdir = origdir) == NULL) 651 return 1; 652 653 if ((dstdir = check_dirname(dstdir)) == NULL) 654 return 1; 655 656 /* 657 * Setup the message list. 658 */ 659 msgCount = get_msgCount(); 660 msgvec = salloc((msgCount + 2) * sizeof(*msgvec)); 661 if (!f) { 662 *msgvec = first(0, MMNORM); 663 if (*msgvec == 0) { 664 (void)printf("No messages to detach.\n"); 665 return 1; 666 } 667 msgvec[1] = 0; 668 } 669 if (f && getmsglist(str, msgvec, 0) < 0) 670 return 1; 671 672 if (mime_detach_control() != 0) 673 return 1; 674 675 /* 676 * do 'dot' if nothing else was selected. 677 */ 678 if (msgvec[0] == 0 && dot != NULL) { 679 msgvec[0] = get_msgnum(dot); 680 msgvec[1] = 0; 681 } 682 recursive = do_recursion(); 683 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 684 struct detach1_core_args_s args; 685 struct message *mp; 686 mp = get_message(*ip); 687 dot = mp; 688 args.parent = recursive ? mp : NULL; 689 args.igtab = do_unnamed ? detachall : ignoreall; 690 args.dstdir = dstdir; 691 (void)thread_recursion(mp, detach1_core, &args); 692 } 693 return 0; 694 } 695 696 /* 697 * detach named attachments. 698 */ 699 PUBLIC int 700 detach(void *v) 701 { 702 return detach1(v, 0); 703 } 704 705 /* 706 * detach all attachments. 707 */ 708 PUBLIC int 709 Detach(void *v) 710 { 711 return detach1(v, 1); 712 } 713 #endif /* MIME_SUPPORT */ 714