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>
26 #include "mime_parser.h"
27 #include "sysdep_decls.h"
32 void extract_key(char *target, char *source, char *key) {
35 strcpy(target, source);
36 for (a=0; a<strlen(target); ++a) {
37 if ((!strncasecmp(&target[a], key, strlen(key)))
38 && (target[a+strlen(key)]=='=')) {
39 strcpy(target, &target[a+strlen(key)+1]);
40 if (target[0]==34) strcpy(target, &target[1]);
41 for (b=0; b<strlen(target); ++b)
42 if (target[b]==34) target[b]=0;
52 * Utility function to "readline" from memory
53 * (returns new pointer)
55 char *memreadline(char *start, char *buf, int maxlen) {
60 memset(buf, 0, maxlen);
64 if ((ch==10)||(ch==0)) {
66 if (buf[strlen(buf)-1]==13)
67 buf[strlen(buf)-1] = 0;
70 if (strlen(buf) < (maxlen-1)) {
71 buf[strlen(buf)+1] = 0;
72 buf[strlen(buf)] = ch;
78 * Given a message or message-part body and a length, handle any necessary
79 * decoding and pass the request up the stack.
81 void mime_decode(char *partnum,
82 char *part_start, size_t length,
83 char *content_type, char *encoding,
85 char *name, char *filename,
101 size_t bytes_sent = 0;
102 size_t bytes_recv = 0;
106 lprintf(9, "mime_decode() called\n");
108 /* Some encodings aren't really encodings */
109 if (!strcasecmp(encoding, "7bit")) strcpy(encoding, "");
110 if (!strcasecmp(encoding, "8bit")) strcpy(encoding, "");
111 if (!strcasecmp(encoding, "binary")) strcpy(encoding, "");
113 /* If this part is not encoded, send as-is */
114 if (strlen(encoding)==0) {
115 CallBack(name, filename, partnum, disposition, part_start,
116 content_type, length);
120 if ( (strcasecmp(encoding, "base64"))
121 && (strcasecmp(encoding, "quoted-printable")) ) {
122 lprintf(5, "ERROR: unknown MIME encoding '%s'\n", encoding);
127 * Allocate a buffer for the decoded data. The output buffer is the
128 * same size as the input buffer; this assumes that the decoded data
129 * will never be larger than the encoded data. This is a safe
130 * assumption with base64, uuencode, and quoted-printable. Just to
131 * be safe, we still pad the buffer a bit.
133 decoded = mallok(length + 1024);
134 if (decoded == NULL) {
135 lprintf(5, "ERROR: cannot allocate memory.\n");
138 if (pipe(sendpipe) != 0) return;
139 if (pipe(recvpipe) != 0) return;
149 /* send stdio to the pipes */
150 if (dup2(sendpipe[0], 0)<0) lprintf(5, "ERROR dup2()\n");
151 if (dup2(recvpipe[1], 1)<0) lprintf(5, "ERROR dup2()\n");
152 close(sendpipe[1]); /* Close the ends we're not using */
154 if (!strcasecmp(encoding, "base64"))
155 execlp("./base64", "base64", "-d", NULL);
156 else if (!strcasecmp(encoding, "quoted-printable"))
157 execlp("./qpdecode", "qpdecode", NULL);
158 lprintf(5, "ERROR: cannot exec decoder for %s\n", encoding);
162 close(sendpipe[0]); /* Close the ends we're not using */
165 while ( (bytes_sent < length) && (write_error == 0) ) {
166 /* Empty the input pipe FIRST */
167 while (fstat(recvpipe[0], &statbuf), (statbuf.st_size > 0) ) {
168 blocksize = read(recvpipe[0], &decoded[bytes_recv],
171 lprintf(5, "ERROR: cannot read from pipe\n");
173 bytes_recv = bytes_recv + blocksize;
175 /* Then put some data into the output pipe */
176 blocksize = length - bytes_sent;
177 if (blocksize > 2048) blocksize = 2048;
178 if (write(sendpipe[1], &part_start[bytes_sent], blocksize) <0) {
179 lprintf(5, "ERROR: cannot write to pipe: %s\n",
183 bytes_sent = bytes_sent + blocksize;
186 /* Empty the input pipe */
187 while ( (blocksize = read(recvpipe[0], &decoded[bytes_recv], 1)),
189 bytes_recv = bytes_recv + blocksize;
193 CallBack(name, filename, partnum, disposition, decoded,
194 content_type, bytes_recv);
200 * Break out the components of a multipart message
201 * (This function expects to be fed HEADERS + CONTENT)
202 * Note: NULL can be supplied as content_end; in this case, the message is
203 * considered to have ended when the parser encounters a 0x00 byte.
205 void the_mime_parser(char *partnum,
206 char *content_start, char *content_end,
218 char *part_start, *part_end;
224 char content_type[256];
226 char disposition[256];
233 char nested_partnum[256];
235 lprintf(9, "the_mime_parser() called\n");
237 memset(boundary, 0, sizeof boundary);
238 memset(content_type, 0, sizeof content_type);
239 memset(encoding, 0, sizeof encoding);
240 memset(name, 0, sizeof name);
241 memset(filename, 0, sizeof filename);
243 /* Learn interesting things from the headers */
246 ptr = memreadline(ptr, buf, sizeof buf);
247 if (*ptr == 0) return; /* premature end of message */
248 if (content_end != NULL)
249 if (ptr >= content_end) return;
251 for (i=0; i<strlen(buf); ++i)
252 if (isspace(buf[i])) buf[i]=' ';
253 if (!isspace(buf[0])) {
254 if (!strncasecmp(header, "Content-type: ", 14)) {
255 strcpy(content_type, &header[14]);
256 extract_key(name, content_type, "name");
258 if (!strncasecmp(header, "Content-Disposition: ", 21)) {
259 strcpy(disposition, &header[21]);
260 extract_key(filename, disposition, "filename");
262 if (!strncasecmp(header,
263 "Content-transfer-encoding: ", 27))
264 strcpy(encoding, &header[27]);
265 if (strlen(boundary)==0)
266 extract_key(boundary, header, "boundary");
269 if ((strlen(header)+strlen(buf)+2)<sizeof(header))
271 } while ((strlen(buf) > 0) && (*ptr != 0));
273 for (i=0; i<strlen(disposition); ++i)
274 if (disposition[i]==';') disposition[i] = 0;
275 for (i=0; i<strlen(content_type); ++i)
276 if (content_type[i]==';') content_type[i] = 0;
278 if (strlen(boundary) > 0) {
285 /* If this is a multipart message, then recursively process it */
288 sprintf(startary, "--%s", boundary);
289 sprintf(endary, "--%s--", boundary);
292 ptr = memreadline(ptr, buf, sizeof buf);
293 if (*ptr == 0) return; /* premature end of message */
294 if (content_end != NULL)
295 if (ptr >= content_end) return;
296 if ((!strcasecmp(buf, startary))
297 ||(!strcasecmp(buf, endary))) {
298 if (part_start != NULL) {
299 sprintf(nested_partnum, "%s.%d",
300 partnum, ++part_seq);
301 the_mime_parser(nested_partnum,
302 part_start, part_end,
307 } while (strcasecmp(buf, endary));
310 /* If it's not a multipart message, then do something with it */
314 while ((*ptr != 0)&&((content_end==NULL)||(ptr<content_end))) {
320 content_type, encoding, disposition,
321 name, filename, CallBack);
327 * Entry point for the MIME parser.
328 * (This function expects to be fed HEADERS + CONTENT)
329 * Note: NULL can be supplied as content_end; in this case, the message is
330 * considered to have ended when the parser encounters a 0x00 byte.
332 void mime_parser(char *content_start, char *content_end,
343 lprintf(9, "mime_parser() called\n");
344 the_mime_parser("1", content_start, content_end, CallBack);