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