Implementation of mhn utility
Showing
1 changed file
with
871 additions
and
0 deletions
mh/mhn.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 2003 Free Software Foundation, Inc. | ||
3 | |||
4 | GNU Mailutils is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 2, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | GNU Mailutils is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GNU Mailutils; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | /* MH mhn command */ | ||
19 | |||
20 | #include <mh.h> | ||
21 | |||
22 | const char *argp_program_version = "mhn (" PACKAGE_STRING ")"; | ||
23 | static char doc[] = N_("GNU MH mhn\v" | ||
24 | "Options marked with `*' are not yet implemented.\n" | ||
25 | "Use -help to obtain the list of traditional MH options."); | ||
26 | static char args_doc[] = "[msgs]"; | ||
27 | |||
28 | static struct argp_option options[] = { | ||
29 | {"folder", ARG_FOLDER, N_("FOLDER"), 0, | ||
30 | N_("Specify folder to operate upon"), 0}, | ||
31 | {"file", ARG_FILE, N_("FILE"), 0, | ||
32 | N_("Specify file to operate upon"), 0}, | ||
33 | |||
34 | {N_("MIME editing options"), 0, NULL, OPTION_DOC, NULL, 5}, | ||
35 | {"compose", ARG_COMPOSE, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
36 | N_("* Compose the MIME message (default)"), 6}, | ||
37 | {"nocompose", ARG_NOCOMPOSE, NULL, OPTION_HIDDEN, "", 6}, | ||
38 | |||
39 | {N_("Listing options"), 0, NULL, OPTION_DOC, NULL, 0}, | ||
40 | {"list", ARG_LIST, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
41 | N_("List the table of contents"), 11 }, | ||
42 | {"nolist", ARG_NOLIST, NULL, OPTION_HIDDEN, "", 11 }, | ||
43 | {"headers", ARG_HEADER, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
44 | N_("Print the banner above the listing"), 12}, | ||
45 | {"noheaders", ARG_NOHEADERS, NULL, OPTION_HIDDEN, "", 12 }, | ||
46 | {"realsize", ARG_REALSIZE, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
47 | N_("List the decoded sizes"), 12}, | ||
48 | {"norealsize", ARG_NOREALSIZE, NULL, OPTION_HIDDEN, "", 12 }, | ||
49 | |||
50 | {N_("Display options"), 0, NULL, OPTION_DOC, NULL, 20}, | ||
51 | {"show", ARG_SHOW, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
52 | N_("Display the contents of the messages"), 21}, | ||
53 | {"noshow", ARG_NOSHOW, NULL, OPTION_HIDDEN, "", 21 }, | ||
54 | {"serialonly", ARG_SERIALONLY, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
55 | N_("* Display messages serially"), 22}, | ||
56 | {"noserialonly", ARG_NOSERIALONLY, NULL, OPTION_HIDDEN, "", 22 }, | ||
57 | {"form", ARG_FORM, N_("FILE"), 0, | ||
58 | N_("Read mhl format from FILE"), 22}, | ||
59 | {"pause", ARG_PAUSE, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
60 | N_("* Pause prior to displaying content"), 22}, | ||
61 | {"nopause", ARG_NOPAUSE, NULL, OPTION_HIDDEN, "", 22 }, | ||
62 | |||
63 | {N_("Saving options"), 0, NULL, OPTION_DOC, NULL, 30}, | ||
64 | {"store", ARG_STORE, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
65 | N_("* Store the contents of the messages on disk"), 31}, | ||
66 | {"nostore", ARG_NOSTORE, NULL, OPTION_HIDDEN, "", 31 }, | ||
67 | {"auto", ARG_AUTO, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
68 | N_("* Use filenames from the content headers"), 31}, | ||
69 | {"noauto", ARG_NOAUTO, NULL, OPTION_HIDDEN, "", 31 }, | ||
70 | |||
71 | {N_("Other options"), 0, NULL, OPTION_DOC, NULL, 40}, | ||
72 | {"part", ARG_PART, N_("PART"), 0, | ||
73 | N_("Limit the scope of the operation to the given part"), 41}, | ||
74 | {"type", ARG_TYPE, N_("CONTENT"), 0, | ||
75 | N_("Operate on message part with given multipart content"), 41 }, | ||
76 | {"verbose", ARG_VERBOSE, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
77 | N_("Print additional information"), 41 }, | ||
78 | {"noverbose", ARG_NOVERBOSE, NULL, OPTION_HIDDEN, "", 41 }, | ||
79 | {NULL} | ||
80 | }; | ||
81 | |||
82 | /* Traditional MH options */ | ||
83 | struct mh_option mh_option[] = { | ||
84 | {"file", 2, 0, "filename"}, | ||
85 | {"list", 1, MH_OPT_BOOL, NULL}, | ||
86 | {"headers", 1, MH_OPT_BOOL, NULL}, | ||
87 | {"realsize", 1, MH_OPT_BOOL, NULL}, | ||
88 | {"show", 2, MH_OPT_BOOL, NULL}, | ||
89 | {"serialonly", 2, MH_OPT_BOOL, NULL}, | ||
90 | {"form", 2, 0, "formfile"}, | ||
91 | {"pause", 3, MH_OPT_BOOL, NULL}, | ||
92 | {"store", 1, MH_OPT_BOOL, NULL}, | ||
93 | {"auto", 1, MH_OPT_BOOL, NULL}, | ||
94 | {"part", 3, 0, "part"}, | ||
95 | {"type", 1, 0, "type"}, | ||
96 | {"verbose", 1, MH_OPT_BOOL, NULL}, | ||
97 | {NULL} | ||
98 | }; | ||
99 | |||
100 | typedef struct _msg_part *msg_part_t; | ||
101 | |||
102 | static msg_part_t msg_part_create __P((size_t num)); | ||
103 | static void msg_part_destroy __P((msg_part_t p)); | ||
104 | static int msg_part_eq __P((msg_part_t a, msg_part_t b)); | ||
105 | static void msg_part_incr __P((msg_part_t p)); | ||
106 | static void msg_part_decr __P((msg_part_t p)); | ||
107 | static void msg_part_set_subpart __P((msg_part_t p, size_t subpart)); | ||
108 | static void msg_part_print __P((msg_part_t p, int width)); | ||
109 | static msg_part_t msg_part_parse __P((char *str)); | ||
110 | static int msg_part_level __P((msg_part_t p)); | ||
111 | static size_t msg_part_subpart __P((msg_part_t p, int level)); | ||
112 | |||
113 | enum mhn_mode { | ||
114 | mode_compose, | ||
115 | mode_list, | ||
116 | mode_show, | ||
117 | mode_store, | ||
118 | }; | ||
119 | |||
120 | static enum mhn_mode mode = mode_compose; | ||
121 | |||
122 | #define OPT_HEADERS 001 | ||
123 | #define OPT_REALSIZE 002 | ||
124 | #define OPT_PAUSE 004 | ||
125 | #define OPT_AUTO 010 | ||
126 | #define OPT_SERIALONLY 020 | ||
127 | #define OPT_VERBOSE 040 | ||
128 | |||
129 | static int mode_options = OPT_HEADERS; | ||
130 | static char *formfile; | ||
131 | static char *content_type; | ||
132 | static char *content_subtype; | ||
133 | static char *input_file; | ||
134 | static int width = 80; | ||
135 | |||
136 | static mh_msgset_t msgset; | ||
137 | static mailbox_t mbox; | ||
138 | static message_t message; | ||
139 | static msg_part_t req_part; | ||
140 | |||
141 | void | ||
142 | sfree (char **ptr) | ||
143 | { | ||
144 | if (!*ptr) | ||
145 | return; | ||
146 | free (*ptr); | ||
147 | *ptr = NULL; | ||
148 | } | ||
149 | |||
150 | void | ||
151 | split_content (const char *content, char **type, char **subtype) | ||
152 | { | ||
153 | char *p = strchr (content, '/'); | ||
154 | if (p) | ||
155 | { | ||
156 | int len = p - content; | ||
157 | *type = xmalloc (len + 1); | ||
158 | memcpy (*type, content, len); | ||
159 | (*type)[len] = 0; | ||
160 | |||
161 | p++; | ||
162 | *subtype = xmalloc (strlen (p) + 1); | ||
163 | strcpy (*subtype, p); | ||
164 | } | ||
165 | else | ||
166 | { | ||
167 | *type = xmalloc (strlen (content) + 1); | ||
168 | strcpy (*type, content); | ||
169 | *subtype = NULL; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | opt_handler (int key, char *arg, void *unused, struct argp_state *state) | ||
175 | { | ||
176 | switch (key) | ||
177 | { | ||
178 | case ARG_FOLDER: | ||
179 | current_folder = arg; | ||
180 | break; | ||
181 | |||
182 | case ARG_FILE: | ||
183 | input_file = arg; | ||
184 | break; | ||
185 | |||
186 | /* Operation mode */ | ||
187 | case ARG_COMPOSE: | ||
188 | if (is_true (arg)) | ||
189 | { | ||
190 | mode = mode_compose; | ||
191 | break; | ||
192 | } | ||
193 | /*FALLTHRU*/ | ||
194 | case ARG_NOCOMPOSE: | ||
195 | if (mode == mode_compose) | ||
196 | mode = mode_compose; | ||
197 | break; | ||
198 | |||
199 | case ARG_LIST: | ||
200 | if (is_true (arg)) | ||
201 | { | ||
202 | mode = mode_list; | ||
203 | break; | ||
204 | } | ||
205 | /*FALLTHRU*/ | ||
206 | case ARG_NOLIST: | ||
207 | if (mode == mode_list) | ||
208 | mode = mode_compose; | ||
209 | break; | ||
210 | |||
211 | case ARG_SHOW: | ||
212 | if (is_true (arg)) | ||
213 | { | ||
214 | mode = mode_show; | ||
215 | break; | ||
216 | } | ||
217 | /*FALLTHRU*/ | ||
218 | case ARG_NOSHOW: | ||
219 | if (mode == mode_show) | ||
220 | mode = mode_compose; | ||
221 | break; | ||
222 | |||
223 | case ARG_STORE: | ||
224 | if (is_true (arg)) | ||
225 | { | ||
226 | mode = mode_store; | ||
227 | break; | ||
228 | } | ||
229 | /*FALLTHRU*/ | ||
230 | case ARG_NOSTORE: | ||
231 | if (mode == mode_store) | ||
232 | mode = mode_compose; | ||
233 | break; | ||
234 | |||
235 | /* List options */ | ||
236 | case ARG_HEADER: | ||
237 | if (is_true (arg)) | ||
238 | { | ||
239 | mode_options |= OPT_HEADERS; | ||
240 | break; | ||
241 | } | ||
242 | /*FALLTHRU*/ | ||
243 | case ARG_NOHEADERS: | ||
244 | mode_options &= ~OPT_HEADERS; | ||
245 | break; | ||
246 | |||
247 | case ARG_REALSIZE: | ||
248 | if (is_true (arg)) | ||
249 | { | ||
250 | mode_options |= OPT_REALSIZE; | ||
251 | break; | ||
252 | } | ||
253 | /*FALLTHRU*/ | ||
254 | case ARG_NOREALSIZE: | ||
255 | mode_options &= ~OPT_REALSIZE; | ||
256 | break; | ||
257 | |||
258 | /* Display options */ | ||
259 | |||
260 | case ARG_SERIALONLY: | ||
261 | if (is_true (arg)) | ||
262 | { | ||
263 | mode_options |= OPT_SERIALONLY; | ||
264 | break; | ||
265 | } | ||
266 | /*FALLTHRU*/ | ||
267 | case ARG_NOSERIALONLY: | ||
268 | mode_options &= ~OPT_SERIALONLY; | ||
269 | break; | ||
270 | |||
271 | case ARG_PAUSE: | ||
272 | if (is_true (arg)) | ||
273 | { | ||
274 | mode_options |= OPT_PAUSE; | ||
275 | break; | ||
276 | } | ||
277 | /*FALLTHRU*/ | ||
278 | case ARG_NOPAUSE: | ||
279 | mode_options &= ~OPT_PAUSE; | ||
280 | break; | ||
281 | |||
282 | /* Store options */ | ||
283 | case ARG_AUTO: | ||
284 | if (is_true (arg)) | ||
285 | { | ||
286 | mode_options |= OPT_AUTO; | ||
287 | break; | ||
288 | } | ||
289 | /*FALLTHRU*/ | ||
290 | case ARG_NOAUTO: | ||
291 | mode_options &= ~OPT_AUTO; | ||
292 | break; | ||
293 | |||
294 | case ARG_FORM: | ||
295 | formfile = arg; | ||
296 | break; | ||
297 | |||
298 | /* Common options */ | ||
299 | case ARG_VERBOSE: | ||
300 | if (is_true (arg)) | ||
301 | { | ||
302 | mode_options |= OPT_VERBOSE; | ||
303 | break; | ||
304 | } | ||
305 | /*FALLTHRU*/ | ||
306 | case ARG_NOVERBOSE: | ||
307 | mode_options &= ~OPT_VERBOSE; | ||
308 | break; | ||
309 | |||
310 | case ARG_TYPE: | ||
311 | sfree (&content_type); | ||
312 | sfree (&content_subtype); | ||
313 | split_content (arg, &content_type, &content_subtype); | ||
314 | break; | ||
315 | |||
316 | case ARG_PART: | ||
317 | req_part = msg_part_parse (arg); | ||
318 | break; | ||
319 | |||
320 | default: | ||
321 | return 1; | ||
322 | } | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | |||
327 | /* *********************** Message part functions ************************* */ | ||
328 | |||
329 | struct _msg_part { | ||
330 | int level; | ||
331 | int maxlevel; | ||
332 | size_t *part; | ||
333 | }; | ||
334 | |||
335 | msg_part_t | ||
336 | msg_part_create (size_t num) | ||
337 | { | ||
338 | msg_part_t p = xmalloc (sizeof (*p)); | ||
339 | p->maxlevel = 16; | ||
340 | p->part = xmalloc (sizeof (*p->part) * p->maxlevel); | ||
341 | p->part[0] = num; | ||
342 | p->level = 0; | ||
343 | return p; | ||
344 | } | ||
345 | |||
346 | void | ||
347 | msg_part_destroy (msg_part_t p) | ||
348 | { | ||
349 | free (p->part); | ||
350 | free (p); | ||
351 | } | ||
352 | |||
353 | int | ||
354 | msg_part_eq (msg_part_t a, msg_part_t b) | ||
355 | { | ||
356 | int i, level = a->level < b->level ? a->level : b->level; | ||
357 | |||
358 | for (i = 1; i <= level; i++) | ||
359 | if (a->part[i] != b->part[i]) | ||
360 | return 1; | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | void | ||
365 | msg_part_incr (msg_part_t p) | ||
366 | { | ||
367 | if (p->level == p->maxlevel) | ||
368 | { | ||
369 | p->maxlevel += 16; | ||
370 | p->part = xrealloc (p->part, sizeof (*p->part) * p->maxlevel); | ||
371 | } | ||
372 | p->level++; | ||
373 | } | ||
374 | |||
375 | void | ||
376 | msg_part_decr (msg_part_t p) | ||
377 | { | ||
378 | if (p->level <= 0) | ||
379 | abort (); | ||
380 | p->level--; | ||
381 | } | ||
382 | |||
383 | void | ||
384 | msg_part_set_subpart (msg_part_t p, size_t subpart) | ||
385 | { | ||
386 | p->part[p->level] = subpart; | ||
387 | } | ||
388 | |||
389 | void | ||
390 | msg_part_print (msg_part_t p, int max_width) | ||
391 | { | ||
392 | int i; | ||
393 | int width = 0; | ||
394 | |||
395 | for (i = 1; i <= p->level; i++) | ||
396 | { | ||
397 | if (i > 1) | ||
398 | { | ||
399 | printf ("."); | ||
400 | width++; | ||
401 | } | ||
402 | width += printf ("%lu", (unsigned long) p->part[i]); | ||
403 | } | ||
404 | for (; width < max_width; width++) | ||
405 | putchar (' '); | ||
406 | } | ||
407 | |||
408 | msg_part_t | ||
409 | msg_part_parse (char *str) | ||
410 | { | ||
411 | msg_part_t p = msg_part_create (0); | ||
412 | |||
413 | do | ||
414 | { | ||
415 | char *endp; | ||
416 | size_t num = strtoul (str, &endp, 10); | ||
417 | |||
418 | switch (*endp) | ||
419 | { | ||
420 | case '.': | ||
421 | str = endp + 1; | ||
422 | break; | ||
423 | case 0: | ||
424 | str = endp; | ||
425 | break; | ||
426 | default: | ||
427 | mh_error (_("malformed part specification (near %s)"), endp); | ||
428 | exit (1); | ||
429 | } | ||
430 | msg_part_incr (p); | ||
431 | msg_part_set_subpart (p, num); | ||
432 | } | ||
433 | while (*str); | ||
434 | return p; | ||
435 | } | ||
436 | |||
437 | int | ||
438 | msg_part_level (msg_part_t p) | ||
439 | { | ||
440 | return p->level; | ||
441 | } | ||
442 | |||
443 | size_t | ||
444 | msg_part_subpart (msg_part_t p, int level) | ||
445 | { | ||
446 | if (level <= p->level) | ||
447 | return p->part[level]; | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | |||
452 | /* ************************** Message iterators *************************** */ | ||
453 | |||
454 | int | ||
455 | _get_hdr_value (header_t hdr, const char *name, char **value) | ||
456 | { | ||
457 | int status = header_aget_value (hdr, name, value); | ||
458 | if (status == 0) | ||
459 | { | ||
460 | /* Remove the newlines. */ | ||
461 | char *nl; | ||
462 | while ((nl = strchr (*value, '\n')) != NULL) | ||
463 | *nl = ' '; | ||
464 | } | ||
465 | return status; | ||
466 | } | ||
467 | |||
468 | int | ||
469 | _get_content_type (header_t hdr, char **value) | ||
470 | { | ||
471 | char *type = NULL; | ||
472 | _get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type); | ||
473 | if (type == NULL || *type == '\0') | ||
474 | { | ||
475 | if (type) | ||
476 | free (type); | ||
477 | type = strdup ("text/plain"); /* Default. */ | ||
478 | } | ||
479 | else | ||
480 | { | ||
481 | char *p = strchr (type, ';'); | ||
482 | if (p) | ||
483 | *p = 0; | ||
484 | } | ||
485 | *value = type; | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static int | ||
490 | _get_content_encoding (header_t hdr, char **value) | ||
491 | { | ||
492 | char *encoding = NULL; | ||
493 | _get_hdr_value (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING, &encoding); | ||
494 | if (encoding == NULL || *encoding == '\0') | ||
495 | { | ||
496 | if (encoding) | ||
497 | free (encoding); | ||
498 | encoding = strdup ("7bit"); /* Default. */ | ||
499 | } | ||
500 | *value = encoding; | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | typedef int (*msg_handler_t) __PMT((message_t msg, msg_part_t part, | ||
505 | char *type, char *encoding, | ||
506 | void *data)); | ||
507 | |||
508 | int | ||
509 | match_content (char *content) | ||
510 | { | ||
511 | int rc; | ||
512 | char *type, *subtype; | ||
513 | |||
514 | if (!content_type && !content_subtype) | ||
515 | return 0; | ||
516 | if (!content) | ||
517 | return 0; | ||
518 | |||
519 | split_content (content, &type, &subtype); | ||
520 | |||
521 | if (strcasecmp (content_type, type) == 0) | ||
522 | rc = strcasecmp (content_subtype, subtype); | ||
523 | else | ||
524 | rc = strcasecmp (content_type, subtype); | ||
525 | |||
526 | free (type); | ||
527 | free (subtype); | ||
528 | return rc; | ||
529 | } | ||
530 | |||
531 | int | ||
532 | call_handler (message_t msg, msg_part_t part, char *type, char *encoding, | ||
533 | msg_handler_t fun, void *data) | ||
534 | { | ||
535 | if (req_part && msg_part_eq (req_part, part)) | ||
536 | return 0; | ||
537 | if (match_content (type)) | ||
538 | return 0; | ||
539 | return fun (msg, part, type, encoding, data); | ||
540 | } | ||
541 | |||
542 | int | ||
543 | handle_message (message_t msg, msg_part_t part, msg_handler_t fun, void *data) | ||
544 | { | ||
545 | header_t hdr; | ||
546 | char *type = NULL; | ||
547 | char *encoding; | ||
548 | int ismime = 0; | ||
549 | |||
550 | message_get_header (msg, &hdr); | ||
551 | _get_content_type (hdr, &type); | ||
552 | _get_content_encoding (hdr, &encoding); | ||
553 | |||
554 | fun (msg, part, type, encoding, data); | ||
555 | sfree (&type); | ||
556 | sfree (&encoding); | ||
557 | |||
558 | message_is_multipart (msg, &ismime); | ||
559 | if (ismime) | ||
560 | { | ||
561 | unsigned int i, nparts; | ||
562 | |||
563 | message_get_num_parts (msg, &nparts); | ||
564 | |||
565 | msg_part_incr (part); | ||
566 | for (i = 1; i <= nparts; i++) | ||
567 | { | ||
568 | message_t message = NULL; | ||
569 | |||
570 | if (message_get_part (msg, i, &message) == 0) | ||
571 | { | ||
572 | message_get_header (message, &hdr); | ||
573 | _get_content_type (hdr, &type); | ||
574 | _get_content_encoding (hdr, &encoding); | ||
575 | |||
576 | msg_part_set_subpart (part, i); | ||
577 | |||
578 | if (strcasecmp (type, "multipart/mixed") == 0) | ||
579 | handle_message (message, part, fun, data); | ||
580 | else | ||
581 | call_handler (message, part, type, encoding, fun, data); | ||
582 | sfree (&type); | ||
583 | sfree (&encoding); | ||
584 | } | ||
585 | } | ||
586 | msg_part_decr (part); | ||
587 | } | ||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | int | ||
592 | mhn_message_size (message_t msg, size_t *psize) | ||
593 | { | ||
594 | body_t body; | ||
595 | *psize = 0; | ||
596 | message_get_body (msg, &body); | ||
597 | if (mode_options & OPT_REALSIZE) | ||
598 | { | ||
599 | stream_t dstr = NULL, bstr = NULL; | ||
600 | |||
601 | if (body_get_stream (body, &bstr) == 0) | ||
602 | { | ||
603 | header_t hdr; | ||
604 | char *encoding; | ||
605 | size_t size = 0; | ||
606 | int rc; | ||
607 | |||
608 | message_get_header (msg, &hdr); | ||
609 | _get_content_encoding (hdr, &encoding); | ||
610 | |||
611 | rc = filter_create(&dstr, bstr, encoding, | ||
612 | MU_FILTER_DECODE, MU_STREAM_READ); | ||
613 | free (encoding); | ||
614 | if (rc == 0) | ||
615 | { | ||
616 | char buf[512]; | ||
617 | size_t n; | ||
618 | |||
619 | while (stream_sequential_read (dstr, buf, sizeof buf, &n) == 0 | ||
620 | && n > 0) | ||
621 | size += n; | ||
622 | |||
623 | stream_destroy (&dstr, NULL); | ||
624 | *psize = size; | ||
625 | return 0; | ||
626 | } | ||
627 | } | ||
628 | } | ||
629 | |||
630 | return body_size (body, psize); | ||
631 | } | ||
632 | |||
633 | |||
634 | /* ***************************** List Mode ******************************* */ | ||
635 | |||
636 | int | ||
637 | list_handler (message_t msg, msg_part_t part, char *type, char *encoding, | ||
638 | void *data) | ||
639 | { | ||
640 | size_t size; | ||
641 | header_t hdr; | ||
642 | |||
643 | if (msg_part_level (part) == 0) | ||
644 | printf ("%3lu ", (unsigned long) msg_part_subpart (part, 0)); | ||
645 | else | ||
646 | { | ||
647 | printf (" "); | ||
648 | msg_part_print (part, 5); | ||
649 | putchar (' '); | ||
650 | } | ||
651 | printf ("%-26s", type); | ||
652 | |||
653 | mhn_message_size (msg, &size); | ||
654 | printf ("%4lu", (unsigned long) size); | ||
655 | |||
656 | if (message_get_header (msg, &hdr) == 0) | ||
657 | { | ||
658 | char *descr; | ||
659 | if (header_aget_value (hdr, "Content-Description", &descr) == 0) | ||
660 | { | ||
661 | printf (" %s", descr); | ||
662 | free (descr); | ||
663 | } | ||
664 | } | ||
665 | |||
666 | printf ("\n"); | ||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | int | ||
671 | list_message (message_t msg, size_t num) | ||
672 | { | ||
673 | msg_part_t part = msg_part_create (num); | ||
674 | handle_message (msg, part, list_handler, NULL); | ||
675 | msg_part_destroy (part); | ||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | void | ||
680 | list_iterator (mailbox_t mbox, message_t msg, size_t num, void *data) | ||
681 | { | ||
682 | list_message (msg, num); | ||
683 | } | ||
684 | |||
685 | int | ||
686 | mhn_list () | ||
687 | { | ||
688 | int rc; | ||
689 | |||
690 | if (mode_options & OPT_HEADERS) | ||
691 | printf (_("msg part type/subtype size description\n")); | ||
692 | |||
693 | if (message) | ||
694 | rc = list_message (message, 0); | ||
695 | else | ||
696 | rc = mh_iterate (mbox, &msgset, list_iterator, NULL); | ||
697 | return rc; | ||
698 | } | ||
699 | |||
700 | |||
701 | /* ***************************** Show Mode ******************************* */ | ||
702 | |||
703 | static list_t mhl_format; | ||
704 | |||
705 | void | ||
706 | cat_message (stream_t out, stream_t in) | ||
707 | { | ||
708 | int rc = 0; | ||
709 | char buf[512]; | ||
710 | size_t n; | ||
711 | |||
712 | stream_seek (in, 0, SEEK_SET); | ||
713 | while (rc == 0 | ||
714 | && stream_sequential_read (in, buf, sizeof buf, &n) == 0 | ||
715 | && n > 0) | ||
716 | rc = stream_sequential_write (out, buf, n); | ||
717 | } | ||
718 | |||
719 | int | ||
720 | show_handler (message_t msg, msg_part_t part, char *type, char *encoding, | ||
721 | void *data) | ||
722 | { | ||
723 | stream_t out = data; | ||
724 | |||
725 | if (strncasecmp (type, "multipart", 9) == 0) | ||
726 | return 0; | ||
727 | if (mhl_format) | ||
728 | mhl_format_run (mhl_format, width, 0, MHL_DECODE, msg, out); | ||
729 | else | ||
730 | { | ||
731 | int rc; | ||
732 | body_t body = NULL; | ||
733 | stream_t dstr, bstr; | ||
734 | |||
735 | if ((rc = message_get_body (msg, &body))) | ||
736 | { | ||
737 | mh_error (_("%lu: can't get message body: %s"), | ||
738 | (unsigned long) msg_part_subpart (part, 0), | ||
739 | mu_strerror (rc)); | ||
740 | return 0; | ||
741 | } | ||
742 | body_get_stream (body, &bstr); | ||
743 | rc = filter_create(&dstr, bstr, encoding, | ||
744 | MU_FILTER_DECODE, MU_STREAM_READ); | ||
745 | if (rc == 0) | ||
746 | bstr = dstr; | ||
747 | cat_message (out, bstr); | ||
748 | if (dstr) | ||
749 | stream_destroy (&dstr, stream_get_owner (dstr)); | ||
750 | } | ||
751 | } | ||
752 | |||
753 | int | ||
754 | show_message (message_t msg, size_t num, void *data) | ||
755 | { | ||
756 | msg_part_t part = msg_part_create (num); | ||
757 | handle_message (msg, part, show_handler, data); | ||
758 | msg_part_destroy (part); | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | void | ||
763 | show_iterator (mailbox_t mbox, message_t msg, size_t num, void *data) | ||
764 | { | ||
765 | show_message (msg, num, data); | ||
766 | } | ||
767 | |||
768 | int | ||
769 | mhn_show () | ||
770 | { | ||
771 | int rc; | ||
772 | stream_t ostr; | ||
773 | |||
774 | rc = stdio_stream_create (&ostr, stdout, 0); | ||
775 | if (rc) | ||
776 | { | ||
777 | mh_error (_("can't create output stream: %s"), mu_strerror (rc)); | ||
778 | exit (1); | ||
779 | } | ||
780 | rc = stream_open (ostr); | ||
781 | if (rc) | ||
782 | { | ||
783 | mh_error (_("can't open output stream: %s"), mu_strerror (rc)); | ||
784 | exit (1); | ||
785 | } | ||
786 | |||
787 | if (formfile) | ||
788 | { | ||
789 | char *s = mh_expand_name (MHLIBDIR, "mhl.headers", 0); | ||
790 | if (access (s, R_OK) == 0) | ||
791 | formfile = "mhl.headers"; | ||
792 | free (s); | ||
793 | } | ||
794 | |||
795 | if (formfile) | ||
796 | { | ||
797 | char *s = mh_expand_name (MHLIBDIR, formfile, 0); | ||
798 | mhl_format = mhl_format_compile (s); | ||
799 | if (!mhl_format) | ||
800 | exit (1); | ||
801 | free (s); | ||
802 | } | ||
803 | |||
804 | if (message) | ||
805 | rc = show_message (message, 0, ostr); | ||
806 | else | ||
807 | rc = mh_iterate (mbox, &msgset, show_iterator, ostr); | ||
808 | return rc; | ||
809 | } | ||
810 | |||
811 | |||
812 | |||
813 | /* *************************** Main Entry Point ************************** */ | ||
814 | |||
815 | int | ||
816 | main (int argc, char **argv) | ||
817 | { | ||
818 | int rc; | ||
819 | int index; | ||
820 | |||
821 | mu_init_nls (); | ||
822 | |||
823 | mh_argp_parse (argc, argv, 0, options, mh_option, args_doc, doc, | ||
824 | opt_handler, NULL, &index); | ||
825 | |||
826 | argc -= index; | ||
827 | argv += index; | ||
828 | |||
829 | if (input_file) | ||
830 | { | ||
831 | if (argc) | ||
832 | { | ||
833 | mh_error (_("extra arguments")); | ||
834 | return 1; | ||
835 | } | ||
836 | message = mh_file_to_message (mu_path_folder_dir, input_file); | ||
837 | if (!message) | ||
838 | return 1; | ||
839 | } | ||
840 | else | ||
841 | { | ||
842 | mbox = mh_open_folder (current_folder, 0); | ||
843 | mh_msgset_parse (mbox, &msgset, argc, argv, "cur"); | ||
844 | } | ||
845 | |||
846 | switch (mode) | ||
847 | { | ||
848 | case mode_compose: | ||
849 | mh_error ("mode is not yet implemented"); | ||
850 | rc = 1; | ||
851 | break; | ||
852 | |||
853 | case mode_list: | ||
854 | rc = mhn_list (); | ||
855 | break; | ||
856 | |||
857 | case mode_show: | ||
858 | rc = mhn_show (); | ||
859 | break; | ||
860 | |||
861 | case mode_store: | ||
862 | mh_error ("mode is not yet implemented"); | ||
863 | rc = 1; | ||
864 | break; | ||
865 | |||
866 | default: | ||
867 | abort (); | ||
868 | } | ||
869 | return rc ? 1 : 0; | ||
870 | } | ||
871 |
-
Please register or sign in to post a comment