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