aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2011-05-22 03:46:33 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2011-05-22 03:46:33 +0200
commitd616ab6bbb6c3768efb9474fa18d1e2f98c4793b (patch)
treeefbcd76a62c4075092ea7850174a5b3bc2988e26
parent9b90d9b503c7be343ae26ce7f834b1865ab66013 (diff)
downloadbusybox-w32-d616ab6bbb6c3768efb9474fa18d1e2f98c4793b.tar.gz
busybox-w32-d616ab6bbb6c3768efb9474fa18d1e2f98c4793b.tar.bz2
busybox-w32-d616ab6bbb6c3768efb9474fa18d1e2f98c4793b.zip
reformime: do not require \r\n
function old new delta parse 958 1063 +105 packed_usage 28691 28712 +21 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--editors/diff.c4
-rw-r--r--mailutils/Kbuild.src4
-rw-r--r--mailutils/makemime.c (renamed from mailutils/mime.c)337
-rw-r--r--mailutils/popmaildir.c2
-rw-r--r--mailutils/reformime.c280
-rw-r--r--mailutils/sendmail.c2
6 files changed, 331 insertions, 298 deletions
diff --git a/editors/diff.c b/editors/diff.c
index daa58af9b..8b1e92783 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -101,9 +101,9 @@
101#include "libbb.h" 101#include "libbb.h"
102 102
103#if 0 103#if 0
104//#define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) 104# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
105#else 105#else
106#define dbg_error_msg(...) ((void)0) 106# define dbg_error_msg(...) ((void)0)
107#endif 107#endif
108 108
109enum { /* print_status() and diffreg() return values */ 109enum { /* print_status() and diffreg() return values */
diff --git a/mailutils/Kbuild.src b/mailutils/Kbuild.src
index 20220dac8..6b4fb7470 100644
--- a/mailutils/Kbuild.src
+++ b/mailutils/Kbuild.src
@@ -7,7 +7,3 @@
7lib-y:= 7lib-y:=
8 8
9INSERT 9INSERT
10lib-$(CONFIG_MAKEMIME) += mime.o mail.o
11lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o
12lib-$(CONFIG_REFORMIME) += mime.o mail.o
13lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o
diff --git a/mailutils/mime.c b/mailutils/makemime.c
index 0aff8b1d7..628619bb8 100644
--- a/mailutils/mime.c
+++ b/mailutils/makemime.c
@@ -8,35 +8,17 @@
8 * Licensed under GPLv2, see file LICENSE in this source tree. 8 * Licensed under GPLv2, see file LICENSE in this source tree.
9 */ 9 */
10 10
11//usage:#define makemime_trivial_usage 11//kbuild:lib-$(CONFIG_MAKEMIME) += makemime.o mail.o
12//usage: "[OPTIONS] [FILE]..."
13//usage:#define makemime_full_usage "\n\n"
14//usage: "Create multipart MIME-encoded message from FILEs\n"
15/* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */
16//usage: "\nOptions:"
17//usage: "\n -o FILE Output. Default: stdout"
18//usage: "\n -a HDR Add header. Examples:"
19//usage: "\n \"From: user@host.org\", \"Date: `date -R`\""
20//usage: "\n -c CT Content type. Default: text/plain"
21//usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET
22/* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */
23//usage: "\n"
24//usage: "\nOther options are silently ignored"
25
26//usage:#define reformime_trivial_usage
27//usage: "[OPTIONS] [FILE]..."
28//usage:#define reformime_full_usage "\n\n"
29//usage: "Parse MIME-encoded message\n"
30//usage: "\nOptions:"
31//usage: "\n -x PREFIX Extract content of MIME sections to files"
32//usage: "\n -X PROG ARGS Filter content of MIME sections through PROG"
33//usage: "\n Must be the last option"
34//usage: "\n"
35//usage: "\nOther options are silently ignored"
36 12
37#include "libbb.h" 13#include "libbb.h"
38#include "mail.h" 14#include "mail.h"
39 15
16#if 0
17# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
18#else
19# define dbg_error_msg(...) ((void)0)
20#endif
21
40/* 22/*
41 makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \ 23 makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \
42 [-a "Header: Contents"] file 24 [-a "Header: Contents"] file
@@ -50,7 +32,6 @@
50 \( opts \) - read from child process, that generates [ opts ] 32 \( opts \) - read from child process, that generates [ opts ]
51 33
52Options: 34Options:
53
54 -c type - create a new MIME section from "file" with this 35 -c type - create a new MIME section from "file" with this
55 Content-Type: (default is application/octet-stream). 36 Content-Type: (default is application/octet-stream).
56 -C charset - MIME charset of a new text/plain section. 37 -C charset - MIME charset of a new text/plain section.
@@ -70,6 +51,28 @@ Options:
70 value on each line. 51 value on each line.
71 {which version of makemime is this? What do we support?} 52 {which version of makemime is this? What do we support?}
72*/ 53*/
54/* man makemime:
55
56 * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE
57 * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE
58 * The -C option sets the MIME charset attribute for text/plain content.
59 * The -N option sets the name attribute for Content-Type:
60 * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64.
61
62 * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE
63 * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE
64 * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type.
65 * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified.
66 * Finally, filename must be a MIME-formatted section, NOT a regular file.
67 * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename.
68 * The collection is written to standard output, or the pipe or to outputfile.
69
70 * -j FILE1: add a section to a multipart MIME collection
71 * makemime -j FILE1 [-o OUTFILE] FILE2
72 * FILE1 must be a MIME collection that was previously created by the -m option.
73 * FILE2 must be a MIME section that was previously created by the -c option.
74 * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1.
75 */
73 76
74 77
75/* In busybox 1.15.0.svn, makemime generates output like this 78/* In busybox 1.15.0.svn, makemime generates output like this
@@ -92,10 +95,8 @@ Content-Transfer-Encoding: base64
92...file B contents... 95...file B contents...
93--24269534-2145583448-1655890676-- 96--24269534-2145583448-1655890676--
94 97
95*/ 98 *
96 99 * For reference: here is an example email to LKML which has
97
98/* For reference: here is an example email to LKML which has
99 * 1st unnamed part (so it serves as an email body) 100 * 1st unnamed part (so it serves as an email body)
100 * and one attached file: 101 * and one attached file:
101...other headers... 102...other headers...
@@ -126,28 +127,21 @@ Content-Transfer-Encoding: 7bit
126...random junk added by mailing list robots and such... 127...random junk added by mailing list robots and such...
127*/ 128*/
128 129
129/* man makemime: 130//usage:#define makemime_trivial_usage
130 131//usage: "[OPTIONS] [FILE]..."
131 * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE 132//usage:#define makemime_full_usage "\n\n"
132 * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE 133//usage: "Create multipart MIME-encoded message from FILEs\n"
133 * The -C option sets the MIME charset attribute for text/plain content. 134/* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */
134 * The -N option sets the name attribute for Content-Type: 135//usage: "\nOptions:"
135 * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64. 136//usage: "\n -o FILE Output. Default: stdout"
136 137//usage: "\n -a HDR Add header. Examples:"
137 * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE 138//usage: "\n \"From: user@host.org\", \"Date: `date -R`\""
138 * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE 139//usage: "\n -c CT Content type. Default: text/plain"
139 * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type. 140//usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET
140 * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified. 141/* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */
141 * Finally, filename must be a MIME-formatted section, NOT a regular file. 142//usage: "\n"
142 * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename. 143//usage: "\nOther options are silently ignored"
143 * The collection is written to standard output, or the pipe or to outputfile.
144 144
145 * -j FILE1: add a section to a multipart MIME collection
146 * makemime -j FILE1 [-o OUTFILE] FILE2
147 * FILE1 must be a MIME collection that was previously created by the -m option.
148 * FILE2 must be a MIME section that was previously created by the -c option.
149 * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1.
150 */
151int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 145int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
152int makemime_main(int argc UNUSED_PARAM, char **argv) 146int makemime_main(int argc UNUSED_PARAM, char **argv)
153{ 147{
@@ -222,244 +216,3 @@ int makemime_main(int argc UNUSED_PARAM, char **argv)
222 return EXIT_SUCCESS; 216 return EXIT_SUCCESS;
223#undef boundary 217#undef boundary
224} 218}
225
226static const char *find_token(const char *const string_array[], const char *key, const char *defvalue)
227{
228 const char *r = NULL;
229 int i;
230 for (i = 0; string_array[i] != NULL; i++) {
231 if (strcasecmp(string_array[i], key) == 0) {
232 r = (char *)string_array[i+1];
233 break;
234 }
235 }
236 return (r) ? r : defvalue;
237}
238
239static const char *xfind_token(const char *const string_array[], const char *key)
240{
241 const char *r = find_token(string_array, key, NULL);
242 if (r)
243 return r;
244 bb_error_msg_and_die("header: %s", key);
245}
246
247enum {
248 OPT_x = 1 << 0,
249 OPT_X = 1 << 1,
250#if ENABLE_FEATURE_REFORMIME_COMPAT
251 OPT_d = 1 << 2,
252 OPT_e = 1 << 3,
253 OPT_i = 1 << 4,
254 OPT_s = 1 << 5,
255 OPT_r = 1 << 6,
256 OPT_c = 1 << 7,
257 OPT_m = 1 << 8,
258 OPT_h = 1 << 9,
259 OPT_o = 1 << 10,
260 OPT_O = 1 << 11,
261#endif
262};
263
264static int parse(const char *boundary, char **argv)
265{
266 char *line, *s, *p;
267 const char *type;
268 int boundary_len = strlen(boundary);
269 const char *delims = " ;\"\t\r\n";
270 const char *uniq;
271 int ntokens;
272 const char *tokens[32]; // 32 is enough
273
274 // prepare unique string pattern
275 uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname());
276
277//bb_info_msg("PARSE[%s]", uniq);
278
279 while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) {
280
281 // seek to start of MIME section
282 // N.B. to avoid false positives let us seek to the _last_ occurance
283 p = NULL;
284 s = line;
285 while ((s = strcasestr(s, "Content-Type:")) != NULL)
286 p = s++;
287 if (!p)
288 goto next;
289//bb_info_msg("L[%s]", p);
290
291 // split to tokens
292 // TODO: strip of comments which are of form: (comment-text)
293 ntokens = 0;
294 tokens[ntokens] = NULL;
295 for (s = strtok(p, delims); s; s = strtok(NULL, delims)) {
296 tokens[ntokens] = s;
297 if (ntokens < ARRAY_SIZE(tokens) - 1)
298 ntokens++;
299//bb_info_msg("L[%d][%s]", ntokens, s);
300 }
301 tokens[ntokens] = NULL;
302//bb_info_msg("N[%d]", ntokens);
303
304 // analyse tokens
305 type = find_token(tokens, "Content-Type:", "text/plain");
306//bb_info_msg("T[%s]", type);
307 if (0 == strncasecmp(type, "multipart/", 10)) {
308 if (0 == strcasecmp(type+10, "mixed")) {
309 parse(xfind_token(tokens, "boundary="), argv);
310 } else
311 bb_error_msg_and_die("no support of content type '%s'", type);
312 } else {
313 pid_t pid = pid;
314 int rc;
315 FILE *fp;
316 // fetch charset
317 const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET);
318 // fetch encoding
319 const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
320 // compose target filename
321 char *filename = (char *)find_token(tokens, "filename=", NULL);
322 if (!filename)
323 filename = xasprintf(uniq, monotonic_us());
324 else
325 filename = bb_get_last_path_component_strip(xstrdup(filename));
326
327 // start external helper, if any
328 if (opts & OPT_X) {
329 int fd[2];
330 xpipe(fd);
331 pid = vfork();
332 if (0 == pid) {
333 // child reads from fd[0]
334 close(fd[1]);
335 xmove_fd(fd[0], STDIN_FILENO);
336 xsetenv("CONTENT_TYPE", type);
337 xsetenv("CHARSET", charset);
338 xsetenv("ENCODING", encoding);
339 xsetenv("FILENAME", filename);
340 BB_EXECVP_or_die(argv);
341 }
342 // parent dumps to fd[1]
343 close(fd[0]);
344 fp = xfdopen_for_write(fd[1]);
345 signal(SIGPIPE, SIG_IGN); // ignore EPIPE
346 // or create a file for dump
347 } else {
348 char *fname = xasprintf("%s%s", *argv, filename);
349 fp = xfopen_for_write(fname);
350 free(fname);
351 }
352
353 // housekeeping
354 free(filename);
355
356 // dump to fp
357 if (0 == strcasecmp(encoding, "base64")) {
358 read_base64(stdin, fp, '-');
359 } else if (0 != strcasecmp(encoding, "7bit")
360 && 0 != strcasecmp(encoding, "8bit")
361 ) {
362 // quoted-printable, binary, user-defined are unsupported so far
363 bb_error_msg_and_die("no support of encoding '%s'", encoding);
364 } else {
365 // N.B. we have written redundant \n. so truncate the file
366 // The following weird 2-tacts reading technique is due to
367 // we have to not write extra \n at the end of the file
368 // In case of -x option we could truncate the resulting file as
369 // fseek(fp, -1, SEEK_END);
370 // if (ftruncate(fileno(fp), ftell(fp)))
371 // bb_perror_msg("ftruncate");
372 // But in case of -X we have to be much more careful. There is
373 // no means to truncate what we already have sent to the helper.
374 p = xmalloc_fgets_str(stdin, "\r\n");
375 while (p) {
376 s = xmalloc_fgets_str(stdin, "\r\n");
377 if (s == NULL)
378 break;
379 if ('-' == s[0]
380 && '-' == s[1]
381 && 0 == strncmp(s+2, boundary, boundary_len)
382 ) {
383 break;
384 }
385 fputs(p, fp);
386 p = s;
387 }
388
389/*
390 while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) {
391 if ('-' == s[0] && '-' == s[1]
392 && 0 == strncmp(s+2, boundary, boundary_len))
393 break;
394 fprintf(fp, "%s\n", s);
395 }
396 // N.B. we have written redundant \n. so truncate the file
397 fseek(fp, -1, SEEK_END);
398 if (ftruncate(fileno(fp), ftell(fp)))
399 bb_perror_msg("ftruncate");
400*/
401 }
402 fclose(fp);
403
404 // finalize helper
405 if (opts & OPT_X) {
406 signal(SIGPIPE, SIG_DFL);
407 // exit if helper exited >0
408 rc = (wait4pid(pid) & 0xff);
409 if (rc)
410 return rc+20;
411 }
412
413 // check multipart finalized
414 if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) {
415 free(line);
416 break;
417 }
418 }
419 next:
420 free(line);
421 }
422
423//bb_info_msg("ENDPARSE[%s]", boundary);
424
425 return EXIT_SUCCESS;
426}
427
428/*
429Usage: reformime [options]
430 -d - parse a delivery status notification.
431 -e - extract contents of MIME section.
432 -x - extract MIME section to a file.
433 -X - pipe MIME section to a program.
434 -i - show MIME info.
435 -s n.n.n.n - specify MIME section.
436 -r - rewrite message, filling in missing MIME headers.
437 -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.
438 -r8 - also convert quoted-printable encoding to 8bit, if possible.
439 -c charset - default charset for rewriting, -o, and -O.
440 -m [file] [file]... - create a MIME message digest.
441 -h "header" - decode RFC 2047-encoded header.
442 -o "header" - encode unstructured header using RFC 2047.
443 -O "header" - encode address list header using RFC 2047.
444*/
445
446int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
447int reformime_main(int argc UNUSED_PARAM, char **argv)
448{
449 const char *opt_prefix = "";
450
451 INIT_G();
452
453 // parse options
454 // N.B. only -x and -X are supported so far
455 opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::");
456 opts = getopt32(argv,
457 "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
458 &opt_prefix
459 IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
460 );
461 //argc -= optind;
462 argv += optind;
463
464 return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix);
465}
diff --git a/mailutils/popmaildir.c b/mailutils/popmaildir.c
index 642657919..c9d22aa28 100644
--- a/mailutils/popmaildir.c
+++ b/mailutils/popmaildir.c
@@ -10,6 +10,8 @@
10 * Licensed under GPLv2, see file LICENSE in this source tree. 10 * Licensed under GPLv2, see file LICENSE in this source tree.
11 */ 11 */
12 12
13//kbuild:lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o
14
13//usage:#define popmaildir_trivial_usage 15//usage:#define popmaildir_trivial_usage
14//usage: "[OPTIONS] MAILDIR [CONN_HELPER ARGS]" 16//usage: "[OPTIONS] MAILDIR [CONN_HELPER ARGS]"
15//usage:#define popmaildir_full_usage "\n\n" 17//usage:#define popmaildir_full_usage "\n\n"
diff --git a/mailutils/reformime.c b/mailutils/reformime.c
new file mode 100644
index 000000000..aa5e3b1c0
--- /dev/null
+++ b/mailutils/reformime.c
@@ -0,0 +1,280 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * makemime: create MIME-encoded message
4 * reformime: parse MIME-encoded message
5 *
6 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
7 *
8 * Licensed under GPLv2, see file LICENSE in this source tree.
9 */
10
11//kbuild:lib-$(CONFIG_REFORMIME) += reformime.o mail.o
12
13#include "libbb.h"
14#include "mail.h"
15
16#if 0
17# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
18#else
19# define dbg_error_msg(...) ((void)0)
20#endif
21
22static const char *find_token(const char *const string_array[], const char *key, const char *defvalue)
23{
24 const char *r = NULL;
25 int i;
26 for (i = 0; string_array[i] != NULL; i++) {
27 if (strcasecmp(string_array[i], key) == 0) {
28 r = (char *)string_array[i+1];
29 break;
30 }
31 }
32 return (r) ? r : defvalue;
33}
34
35static const char *xfind_token(const char *const string_array[], const char *key)
36{
37 const char *r = find_token(string_array, key, NULL);
38 if (r)
39 return r;
40 bb_error_msg_and_die("not found: '%s'", key);
41}
42
43enum {
44 OPT_x = 1 << 0,
45 OPT_X = 1 << 1,
46#if ENABLE_FEATURE_REFORMIME_COMPAT
47 OPT_d = 1 << 2,
48 OPT_e = 1 << 3,
49 OPT_i = 1 << 4,
50 OPT_s = 1 << 5,
51 OPT_r = 1 << 6,
52 OPT_c = 1 << 7,
53 OPT_m = 1 << 8,
54 OPT_h = 1 << 9,
55 OPT_o = 1 << 10,
56 OPT_O = 1 << 11,
57#endif
58};
59
60static int parse(const char *boundary, char **argv)
61{
62 int boundary_len = strlen(boundary);
63 char uniq[sizeof("%%llu.%u") + sizeof(int)*3];
64
65 dbg_error_msg("BOUNDARY[%s]", boundary);
66
67 // prepare unique string pattern
68 sprintf(uniq, "%%llu.%u", (unsigned)getpid());
69 dbg_error_msg("UNIQ[%s]", uniq);
70
71 while (1) {
72 char *header;
73 const char *tokens[32]; /* 32 is enough */
74 const char *type;
75
76 /* Read the header (everything up to two \n) */
77 {
78 unsigned header_idx = 0;
79 int last_ch = 0;
80 header = NULL;
81 while (1) {
82 int ch = fgetc(stdin);
83 if (ch == '\r') /* Support both line endings */
84 continue;
85 if (ch == EOF)
86 break;
87 if (ch == '\n' && last_ch == ch)
88 break;
89 if (!(header_idx & 0xff))
90 header = xrealloc(header, header_idx + 0x101);
91 header[header_idx++] = last_ch = ch;
92 }
93 if (!header) {
94 dbg_error_msg("EOF");
95 break;
96 }
97 header[header_idx] = '\0';
98 dbg_error_msg("H:'%s'", p);
99 }
100
101 /* Split to tokens */
102 {
103 char *s, *p;
104 unsigned ntokens;
105 const char *delims = ";=\" \t\n";
106
107 /* Skip to last Content-Type: */
108 s = p = header;
109 while ((p = strchr(p, '\n')) != NULL) {
110 p++;
111 if (strncasecmp(p, "Content-Type:", sizeof("Content-Type:")-1) == 0)
112 s = p;
113 }
114 dbg_error_msg("L:'%s'", p);
115 ntokens = 0;
116 s = strtok(s, delims);
117 while (s) {
118 tokens[ntokens] = s;
119 if (ntokens < ARRAY_SIZE(tokens) - 1)
120 ntokens++;
121 dbg_error_msg("L[%d]='%s'", ntokens, s);
122 s = strtok(NULL, delims);
123 }
124 tokens[ntokens] = NULL;
125 dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens);
126 if (ntokens == 0)
127 break;
128 }
129
130 /* Is it multipart? */
131 type = find_token(tokens, "Content-Type:", "text/plain");
132 dbg_error_msg("TYPE:'%s'", type);
133 if (0 == strncasecmp(type, "multipart/", 10)) {
134 /* Yes, recurse */
135 if (strcasecmp(type + 10, "mixed") != 0)
136 bb_error_msg_and_die("no support of content type '%s'", type);
137 parse(xfind_token(tokens, "boundary"), argv);
138
139 } else {
140 /* No, process one non-multipart section */
141 char *end;
142 pid_t pid = pid;
143 FILE *fp;
144
145 const char *charset = find_token(tokens, "charset", CONFIG_FEATURE_MIME_CHARSET);
146 const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
147
148 /* Compose target filename */
149 char *filename = (char *)find_token(tokens, "filename", NULL);
150 if (!filename)
151 filename = xasprintf(uniq, monotonic_us());
152 else
153 filename = bb_get_last_path_component_strip(xstrdup(filename));
154
155 if (opts & OPT_X) {
156 int fd[2];
157
158 /* start external helper */
159 xpipe(fd);
160 pid = vfork();
161 if (0 == pid) {
162 /* child reads from fd[0] */
163 close(fd[1]);
164 xmove_fd(fd[0], STDIN_FILENO);
165 xsetenv("CONTENT_TYPE", type);
166 xsetenv("CHARSET", charset);
167 xsetenv("ENCODING", encoding);
168 xsetenv("FILENAME", filename);
169 BB_EXECVP_or_die(argv);
170 }
171 /* parent will write to fd[1] */
172 close(fd[0]);
173 fp = xfdopen_for_write(fd[1]);
174 signal(SIGPIPE, SIG_IGN);
175 } else {
176 /* write to file */
177 char *fname = xasprintf("%s%s", *argv, filename);
178 fp = xfopen_for_write(fname);
179 free(fname);
180 }
181 free(filename);
182
183 /* write to fp */
184 end = NULL;
185 if (0 == strcasecmp(encoding, "base64")) {
186 read_base64(stdin, fp, '-');
187 } else
188 if (0 != strcasecmp(encoding, "7bit")
189 && 0 != strcasecmp(encoding, "8bit")
190 ) {
191 /* quoted-printable, binary, user-defined are unsupported so far */
192 bb_error_msg_and_die("encoding '%s' not supported", encoding);
193 } else {
194 /* plain 7bit or 8bit */
195 while ((end = xmalloc_fgets(stdin)) != NULL) {
196 if ('-' == end[0]
197 && '-' == end[1]
198 && strncmp(end + 2, boundary, boundary_len) == 0
199 ) {
200 break;
201 }
202 fputs(end, fp);
203 }
204 }
205 fclose(fp);
206
207 /* Wait for child */
208 if (opts & OPT_X) {
209 int rc;
210 signal(SIGPIPE, SIG_DFL);
211 rc = (wait4pid(pid) & 0xff);
212 if (rc != 0)
213 return rc + 20;
214 }
215
216 /* Multipart ended? */
217 if (end && '-' == end[2 + boundary_len] && '-' == end[2 + boundary_len + 1]) {
218 dbg_error_msg("FINISHED MPART:'%s'", end);
219 break;
220 }
221 dbg_error_msg("FINISHED:'%s'", end);
222 free(end);
223 } /* end of "handle one non-multipart block" */
224
225 free(header);
226 } /* while (1) */
227
228 dbg_error_msg("ENDPARSE[%s]", boundary);
229
230 return EXIT_SUCCESS;
231}
232
233//usage:#define reformime_trivial_usage
234//usage: "[OPTIONS]"
235//usage:#define reformime_full_usage "\n\n"
236//usage: "Parse MIME-encoded message on stdin\n"
237//usage: "\nOptions:"
238//usage: "\n -x PREFIX Extract content of MIME sections to files"
239//usage: "\n -X PROG ARGS Filter content of MIME sections through PROG"
240//usage: "\n Must be the last option"
241//usage: "\n"
242//usage: "\nOther options are silently ignored"
243
244/*
245Usage: reformime [options]
246 -d - parse a delivery status notification.
247 -e - extract contents of MIME section.
248 -x - extract MIME section to a file.
249 -X - pipe MIME section to a program.
250 -i - show MIME info.
251 -s n.n.n.n - specify MIME section.
252 -r - rewrite message, filling in missing MIME headers.
253 -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.
254 -r8 - also convert quoted-printable encoding to 8bit, if possible.
255 -c charset - default charset for rewriting, -o, and -O.
256 -m [file] [file]... - create a MIME message digest.
257 -h "header" - decode RFC 2047-encoded header.
258 -o "header" - encode unstructured header using RFC 2047.
259 -O "header" - encode address list header using RFC 2047.
260*/
261
262int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
263int reformime_main(int argc UNUSED_PARAM, char **argv)
264{
265 const char *opt_prefix = "";
266
267 INIT_G();
268
269 // parse options
270 // N.B. only -x and -X are supported so far
271 opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::");
272 opts = getopt32(argv,
273 "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
274 &opt_prefix
275 IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
276 );
277 argv += optind;
278
279 return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix);
280}
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index 8096288ef..e0aff20fb 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -7,6 +7,8 @@
7 * Licensed under GPLv2, see file LICENSE in this source tree. 7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */ 8 */
9 9
10//kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o
11
10//usage:#define sendmail_trivial_usage 12//usage:#define sendmail_trivial_usage
11//usage: "[OPTIONS] [RECIPIENT_EMAIL]..." 13//usage: "[OPTIONS] [RECIPIENT_EMAIL]..."
12//usage:#define sendmail_full_usage "\n\n" 14//usage:#define sendmail_full_usage "\n\n"