Magellan Linux

Annotation of /tags/mkinitrd-6_2_0/mailutils/mime.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 816 - (hide annotations) (download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 2 months ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/mailutils/mime.c
File MIME type: text/plain
File size: 10293 byte(s)
-updated to busybox-1.13.4
1 niro 816 /* 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 tarball for details.
9     */
10     #include "libbb.h"
11     #include "mail.h"
12    
13     /*
14     makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \
15     [-a "Header: Contents"] file
16     -m [ type ] [-o file] [-e encoding] [-a "Header: Contents"] file
17     -j [-o file] file1 file2
18     @file
19    
20     file: filename - read or write from filename
21     - - read or write from stdin or stdout
22     &n - read or write from file descriptor n
23     \( opts \) - read from child process, that generates [ opts ]
24    
25     Options:
26    
27     -c type - create a new MIME section from "file" with this
28     Content-Type: (default is application/octet-stream).
29     -C charset - MIME charset of a new text/plain section.
30     -N name - MIME content name of the new mime section.
31     -m [ type ] - create a multipart mime section from "file" of this
32     Content-Type: (default is multipart/mixed).
33     -e encoding - use the given encoding (7bit, 8bit, quoted-printable,
34     or base64), instead of guessing. Omit "-e" and use
35     -c auto to set Content-Type: to text/plain or
36     application/octet-stream based on picked encoding.
37     -j file1 file2 - join mime section file2 to multipart section file1.
38     -o file - write ther result to file, instead of stdout (not
39     allowed in child processes).
40     -a header - prepend an additional header to the output.
41    
42     @file - read all of the above options from file, one option or
43     value on each line.
44     */
45    
46     int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
47     int makemime_main(int argc UNUSED_PARAM, char **argv)
48     {
49     llist_t *opt_headers = NULL, *l;
50     const char *opt_output;
51     #define boundary opt_output
52    
53     enum {
54     OPT_c = 1 << 0, // Content-Type:
55     OPT_e = 1 << 1, // Content-Transfer-Encoding. Ignored. Assumed base64
56     OPT_o = 1 << 2, // output to
57     OPT_C = 1 << 3, // charset
58     OPT_N = 1 << 4, // COMPAT
59     OPT_a = 1 << 5, // additional headers
60     OPT_m = 1 << 6, // COMPAT
61     OPT_j = 1 << 7, // COMPAT
62     };
63    
64     INIT_G();
65    
66     // parse options
67     opt_complementary = "a::";
68     opts = getopt32(argv,
69     "c:e:o:C:N:a:m:j:",
70     &G.content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers, NULL, NULL
71     );
72     //argc -= optind;
73     argv += optind;
74    
75     // respect -o output
76     if (opts & OPT_o)
77     freopen(opt_output, "w", stdout);
78    
79     // no files given on command line? -> use stdin
80     if (!*argv)
81     *--argv = (char *)"-";
82    
83     // put additional headers
84     for (l = opt_headers; l; l = l->link)
85     puts(l->data);
86    
87     // make a random string -- it will delimit message parts
88     srand(monotonic_us());
89     boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
90    
91     // put multipart header
92     printf(
93     "Mime-Version: 1.0\n"
94     "Content-Type: multipart/mixed; boundary=\"%s\"\n"
95     , boundary
96     );
97    
98     // put attachments
99     while (*argv) {
100     printf(
101     "\n--%s\n"
102     "Content-Type: %s; charset=%s\n"
103     "Content-Disposition: inline; filename=\"%s\"\n"
104     "Content-Transfer-Encoding: base64\n"
105     , boundary
106     , G.content_type
107     , G.opt_charset
108     , bb_get_last_path_component_strip(*argv)
109     );
110     encode_base64(*argv++, (const char *)stdin, "");
111     }
112    
113     // put multipart footer
114     printf("\n--%s--\n" "\n", boundary);
115    
116     return EXIT_SUCCESS;
117     #undef boundary
118     }
119    
120     static const char *find_token(const char *const string_array[], const char *key, const char *defvalue)
121     {
122     const char *r = NULL;
123     for (int i = 0; string_array[i] != 0; i++) {
124     if (strcasecmp(string_array[i], key) == 0) {
125     r = (char *)string_array[i+1];
126     break;
127     }
128     }
129     return (r) ? r : defvalue;
130     }
131    
132     static const char *xfind_token(const char *const string_array[], const char *key)
133     {
134     const char *r = find_token(string_array, key, NULL);
135     if (r)
136     return r;
137     bb_error_msg_and_die("header: %s", key);
138     }
139    
140     enum {
141     OPT_x = 1 << 0,
142     OPT_X = 1 << 1,
143     #if ENABLE_FEATURE_REFORMIME_COMPAT
144     OPT_d = 1 << 2,
145     OPT_e = 1 << 3,
146     OPT_i = 1 << 4,
147     OPT_s = 1 << 5,
148     OPT_r = 1 << 6,
149     OPT_c = 1 << 7,
150     OPT_m = 1 << 8,
151     OPT_h = 1 << 9,
152     OPT_o = 1 << 10,
153     OPT_O = 1 << 11,
154     #endif
155     };
156    
157     static int parse(const char *boundary, char **argv)
158     {
159     char *line, *s, *p;
160     const char *type;
161     int boundary_len = strlen(boundary);
162     const char *delims = " ;\"\t\r\n";
163     const char *uniq;
164     int ntokens;
165     const char *tokens[32]; // 32 is enough
166    
167     // prepare unique string pattern
168     uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname());
169    
170     //bb_info_msg("PARSE[%s]", terminator);
171    
172     while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) {
173    
174     // seek to start of MIME section
175     // N.B. to avoid false positives let us seek to the _last_ occurance
176     p = NULL;
177     s = line;
178     while ((s=strcasestr(s, "Content-Type:")) != NULL)
179     p = s++;
180     if (!p)
181     goto next;
182     //bb_info_msg("L[%s]", p);
183    
184     // split to tokens
185     // TODO: strip of comments which are of form: (comment-text)
186     ntokens = 0;
187     tokens[ntokens] = NULL;
188     for (s = strtok(p, delims); s; s = strtok(NULL, delims)) {
189     tokens[ntokens] = s;
190     if (ntokens < ARRAY_SIZE(tokens) - 1)
191     ntokens++;
192     //bb_info_msg("L[%d][%s]", ntokens, s);
193     }
194     tokens[ntokens] = NULL;
195     //bb_info_msg("N[%d]", ntokens);
196    
197     // analyse tokens
198     type = find_token(tokens, "Content-Type:", "text/plain");
199     //bb_info_msg("T[%s]", type);
200     if (0 == strncasecmp(type, "multipart/", 10)) {
201     if (0 == strcasecmp(type+10, "mixed")) {
202     parse(xfind_token(tokens, "boundary="), argv);
203     } else
204     bb_error_msg_and_die("no support of content type '%s'", type);
205     } else {
206     pid_t pid = pid;
207     int rc;
208     FILE *fp;
209     // fetch charset
210     const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET);
211     // fetch encoding
212     const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
213     // compose target filename
214     char *filename = (char *)find_token(tokens, "filename=", NULL);
215     if (!filename)
216     filename = xasprintf(uniq, monotonic_us());
217     else
218     filename = bb_get_last_path_component_strip(xstrdup(filename));
219    
220     // start external helper, if any
221     if (opts & OPT_X) {
222     int fd[2];
223     xpipe(fd);
224     pid = vfork();
225     if (0 == pid) {
226     // child reads from fd[0]
227     xdup2(fd[0], STDIN_FILENO);
228     close(fd[0]); close(fd[1]);
229     xsetenv("CONTENT_TYPE", type);
230     xsetenv("CHARSET", charset);
231     xsetenv("ENCODING", encoding);
232     xsetenv("FILENAME", filename);
233     BB_EXECVP(*argv, argv);
234     _exit(EXIT_FAILURE);
235     }
236     // parent dumps to fd[1]
237     close(fd[0]);
238     fp = fdopen(fd[1], "w");
239     signal(SIGPIPE, SIG_IGN); // ignore EPIPE
240     // or create a file for dump
241     } else {
242     char *fname = xasprintf("%s%s", *argv, filename);
243     fp = xfopen_for_write(fname);
244     free(fname);
245     }
246    
247     // housekeeping
248     free(filename);
249    
250     // dump to fp
251     if (0 == strcasecmp(encoding, "base64")) {
252     decode_base64(stdin, fp);
253     } else if (0 != strcasecmp(encoding, "7bit")
254     && 0 != strcasecmp(encoding, "8bit")) {
255     // quoted-printable, binary, user-defined are unsupported so far
256     bb_error_msg_and_die("no support of encoding '%s'", encoding);
257     } else {
258     // N.B. we have written redundant \n. so truncate the file
259     // The following weird 2-tacts reading technique is due to
260     // we have to not write extra \n at the end of the file
261     // In case of -x option we could truncate the resulting file as
262     // fseek(fp, -1, SEEK_END);
263     // if (ftruncate(fileno(fp), ftell(fp)))
264     // bb_perror_msg("ftruncate");
265     // But in case of -X we have to be much more careful. There is
266     // no means to truncate what we already have sent to the helper.
267     p = xmalloc_fgets_str(stdin, "\r\n");
268     while (p) {
269     if ((s = xmalloc_fgets_str(stdin, "\r\n")) == NULL)
270     break;
271     if ('-' == s[0] && '-' == s[1]
272     && 0 == strncmp(s+2, boundary, boundary_len))
273     break;
274     fputs(p, fp);
275     p = s;
276     }
277    
278     /*
279     while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) {
280     if ('-' == s[0] && '-' == s[1]
281     && 0 == strncmp(s+2, boundary, boundary_len))
282     break;
283     fprintf(fp, "%s\n", s);
284     }
285     // N.B. we have written redundant \n. so truncate the file
286     fseek(fp, -1, SEEK_END);
287     if (ftruncate(fileno(fp), ftell(fp)))
288     bb_perror_msg("ftruncate");
289     */
290     }
291     fclose(fp);
292    
293     // finalize helper
294     if (opts & OPT_X) {
295     signal(SIGPIPE, SIG_DFL);
296     // exit if helper exited >0
297     rc = wait4pid(pid);
298     if (rc)
299     return rc+20;
300     }
301    
302     // check multipart finalized
303     if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) {
304     free(line);
305     break;
306     }
307     }
308     next:
309     free(line);
310     }
311    
312     //bb_info_msg("ENDPARSE[%s]", boundary);
313    
314     return EXIT_SUCCESS;
315     }
316    
317     /*
318     Usage: reformime [options]
319     -d - parse a delivery status notification.
320     -e - extract contents of MIME section.
321     -x - extract MIME section to a file.
322     -X - pipe MIME section to a program.
323     -i - show MIME info.
324     -s n.n.n.n - specify MIME section.
325     -r - rewrite message, filling in missing MIME headers.
326     -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.
327     -r8 - also convert quoted-printable encoding to 8bit, if possible.
328     -c charset - default charset for rewriting, -o, and -O.
329     -m [file] [file]... - create a MIME message digest.
330     -h "header" - decode RFC 2047-encoded header.
331     -o "header" - encode unstructured header using RFC 2047.
332     -O "header" - encode address list header using RFC 2047.
333     */
334    
335     int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
336     int reformime_main(int argc UNUSED_PARAM, char **argv)
337     {
338     const char *opt_prefix = "";
339    
340     INIT_G();
341    
342     // parse options
343     // N.B. only -x and -X are supported so far
344     opt_complementary = "x--X:X--x" USE_FEATURE_REFORMIME_COMPAT(":m::");
345     opts = getopt32(argv,
346     "x:X" USE_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
347     &opt_prefix
348     USE_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
349     );
350     //argc -= optind;
351     argv += optind;
352    
353     return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix);
354     }