4 * This is a really bad attempt at writing a parser to handle MIME-encoded
7 * Copyright (c) 1998-1999 by Art Cancro
8 * This code is distributed under the terms of the GNU General Public License.
17 #include <sys/types.h>
25 #include "mime_parser.h"
26 #include "sysdep_decls.h"
31 void extract_key(char *target, char *source, char *key) {
34 strcpy(target, source);
35 for (a=0; a<strlen(target); ++a) {
36 if ((!strncasecmp(&target[a], key, strlen(key)))
37 && (target[a+strlen(key)]=='=')) {
38 strcpy(target, &target[a+strlen(key)+1]);
39 if (target[0]==34) strcpy(target, &target[1]);
40 for (b=0; b<strlen(target); ++b)
41 if (target[b]==34) target[b]=0;
51 * Utility function to "readline" from memory
52 * (returns new pointer)
54 char *memreadline(char *start, char *buf, int maxlen) {
59 memset(buf, 0, maxlen);
63 if ((ch==10)||(ch==0)) {
65 if (buf[strlen(buf)-1]==13)
66 buf[strlen(buf)-1] = 0;
69 if (strlen(buf) < (maxlen-1)) {
70 buf[strlen(buf)+1] = 0;
71 buf[strlen(buf)] = ch;
77 * Given a message or message-part body and a length, handle any necessary
78 * decoding and pass the request up the stack.
80 void mime_decode(char *partnum,
81 char *part_start, size_t length,
82 char *content_type, char *encoding,
84 char *name, char *filename,
100 size_t bytes_sent = 0;
101 size_t bytes_recv = 0;
105 lprintf(9, "mime_decode() called\n");
107 /* Some encodings aren't really encodings */
108 if (!strcasecmp(encoding, "7bit")) strcpy(encoding, "");
109 if (!strcasecmp(encoding, "8bit")) strcpy(encoding, "");
110 if (!strcasecmp(encoding, "binary")) strcpy(encoding, "");
112 /* If this part is not encoded, send as-is */
113 if (strlen(encoding)==0) {
114 CallBack(name, filename, partnum, disposition, part_start,
115 content_type, length);
119 if ( (strcasecmp(encoding, "base64"))
120 && (strcasecmp(encoding, "quoted-printable")) ) {
121 lprintf(5, "ERROR: unknown MIME encoding '%s'\n", encoding);
126 * Allocate a buffer for the decoded data. The output buffer is the
127 * same size as the input buffer; this assumes that the decoded data
128 * will never be larger than the encoded data. This is a safe
129 * assumption with base64, uuencode, and quoted-printable. Just to
130 * be safe, we still pad the buffer a bit.
132 decoded = mallok(length + 1024);
133 if (decoded == NULL) {
134 lprintf(5, "ERROR: cannot allocate memory.\n");
137 if (pipe(sendpipe) != 0) return;
138 if (pipe(recvpipe) != 0) return;
148 /* send stdio to the pipes */
149 if (dup2(sendpipe[0], 0)<0) lprintf(5, "ERROR dup2()\n");
150 if (dup2(recvpipe[1], 1)<0) lprintf(5, "ERROR dup2()\n");
151 close(sendpipe[1]); /* Close the ends we're not using */
153 if (!strcasecmp(encoding, "base64"))
154 execlp("./base64", "base64", "-d", NULL);
155 else if (!strcasecmp(encoding, "quoted-printable"))
156 execlp("./qpdecode", "qpdecode", NULL);
157 lprintf(5, "ERROR: cannot exec decoder for %s\n", encoding);
161 close(sendpipe[0]); /* Close the ends we're not using */
164 while ( (bytes_sent < length) && (write_error == 0) ) {
165 /* Empty the input pipe FIRST */
166 while (fstat(recvpipe[0], &statbuf), (statbuf.st_size > 0) ) {
167 blocksize = read(recvpipe[0], &decoded[bytes_recv],
170 lprintf(5, "ERROR: cannot read from pipe\n");
172 bytes_recv = bytes_recv + blocksize;
174 /* Then put some data into the output pipe */
175 blocksize = length - bytes_sent;
176 if (blocksize > 2048) blocksize = 2048;
177 if (write(sendpipe[1], &part_start[bytes_sent], blocksize) <0) {
178 lprintf(5, "ERROR: cannot write to pipe: %s\n",
182 bytes_sent = bytes_sent + blocksize;
185 /* Empty the input pipe */
186 while ( (blocksize = read(recvpipe[0], &decoded[bytes_recv], 1)),
188 bytes_recv = bytes_recv + blocksize;
192 CallBack(name, filename, partnum, disposition, decoded,
193 content_type, bytes_recv);
199 * Break out the components of a multipart message
200 * (This function expects to be fed HEADERS + CONTENT)
201 * Note: NULL can be supplied as content_end; in this case, the message is
202 * considered to have ended when the parser encounters a 0x00 byte.
204 void the_mime_parser(char *partnum,
205 char *content_start, char *content_end,
217 char *part_start, *part_end;
223 char content_type[256];
225 char disposition[256];
232 char nested_partnum[256];
234 lprintf(9, "the_mime_parser() called\n");
236 memset(boundary, 0, sizeof boundary);
237 memset(content_type, 0, sizeof content_type);
238 memset(encoding, 0, sizeof encoding);
239 memset(name, 0, sizeof name);
240 memset(filename, 0, sizeof filename);
242 /* Learn interesting things from the headers */
245 ptr = memreadline(ptr, buf, sizeof buf);
246 if (*ptr == 0) return; /* premature end of message */
247 if (content_end != NULL)
248 if (ptr >= content_end) return;
250 for (i=0; i<strlen(buf); ++i)
251 if (isspace(buf[i])) buf[i]=' ';
252 if (!isspace(buf[0])) {
253 if (!strncasecmp(header, "Content-type: ", 14)) {
254 strcpy(content_type, &header[14]);
255 extract_key(name, content_type, "name");
257 if (!strncasecmp(header, "Content-Disposition: ", 21)) {
258 strcpy(disposition, &header[21]);
259 extract_key(filename, disposition, "filename");
261 if (!strncasecmp(header,
262 "Content-transfer-encoding: ", 27))
263 strcpy(encoding, &header[27]);
264 if (strlen(boundary)==0)
265 extract_key(boundary, header, "boundary");
268 if ((strlen(header)+strlen(buf)+2)<sizeof(header))
270 } while ((strlen(buf) > 0) && (*ptr != 0));
272 for (i=0; i<strlen(disposition); ++i)
273 if (disposition[i]==';') disposition[i] = 0;
274 for (i=0; i<strlen(content_type); ++i)
275 if (content_type[i]==';') content_type[i] = 0;
277 if (strlen(boundary) > 0) {
284 /* If this is a multipart message, then recursively process it */
287 sprintf(startary, "--%s", boundary);
288 sprintf(endary, "--%s--", boundary);
291 ptr = memreadline(ptr, buf, sizeof buf);
292 if (*ptr == 0) return; /* premature end of message */
293 if (content_end != NULL)
294 if (ptr >= content_end) return;
295 if ((!strcasecmp(buf, startary))
296 ||(!strcasecmp(buf, endary))) {
297 if (part_start != NULL) {
298 sprintf(nested_partnum, "%s.%d",
299 partnum, ++part_seq);
300 the_mime_parser(nested_partnum,
301 part_start, part_end,
306 } while (strcasecmp(buf, endary));
309 /* If it's not a multipart message, then do something with it */
313 while ((*ptr != 0)&&((content_end==NULL)||(ptr<content_end))) {
319 content_type, encoding, disposition,
320 name, filename, CallBack);
326 * Entry point for the MIME parser.
327 * (This function expects to be fed HEADERS + CONTENT)
328 * Note: NULL can be supplied as content_end; in this case, the message is
329 * considered to have ended when the parser encounters a 0x00 byte.
331 void mime_parser(char *content_start, char *content_end,
342 lprintf(9, "mime_parser() called\n");
343 the_mime_parser("1", content_start, content_end, CallBack);