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.
16 #include <sys/types.h>
22 #include "mime_parser.h"
23 #include "sysdep_decls.h"
28 void extract_key(char *target, char *source, char *key) {
31 strcpy(target, source);
32 for (a=0; a<strlen(target); ++a) {
33 if ((!strncasecmp(&target[a], key, strlen(key)))
34 && (target[a+strlen(key)]=='=')) {
35 strcpy(target, &target[a+strlen(key)+1]);
36 if (target[0]==34) strcpy(target, &target[1]);
37 for (b=0; b<strlen(target); ++b)
38 if (target[b]==34) target[b]=0;
48 * Utility function to "readline" from memory
49 * (returns new pointer)
51 char *memreadline(char *start, char *buf, int maxlen) {
56 memset(buf, 0, maxlen);
60 if ((ch==10)||(ch==0)) {
62 if (buf[strlen(buf)-1]==13)
63 buf[strlen(buf)-1] = 0;
66 if (strlen(buf) < (maxlen-1)) {
67 buf[strlen(buf)+1] = 0;
68 buf[strlen(buf)] = ch;
74 * Given a message or message-part body and a length, handle any necessary
75 * decoding and pass the request up the stack.
77 void mime_decode(char *partnum,
78 char *part_start, size_t length,
79 char *content_type, char *encoding,
81 char *name, char *filename,
97 size_t bytes_sent = 0;
98 size_t bytes_recv = 0;
102 lprintf(9, "mime_decode() called\n");
104 /* Some encodings aren't really encodings */
105 if (!strcasecmp(encoding, "7bit")) strcpy(encoding, "");
106 if (!strcasecmp(encoding, "8bit")) strcpy(encoding, "");
107 if (!strcasecmp(encoding, "binary")) strcpy(encoding, "");
109 /* If this part is not encoded, send as-is */
110 if (strlen(encoding)==0) {
111 CallBack(name, filename, partnum, disposition, part_start,
112 content_type, length);
116 if ( (strcasecmp(encoding, "base64"))
117 && (strcasecmp(encoding, "quoted-printable")) ) {
118 lprintf(5, "ERROR: unknown MIME encoding '%s'\n", encoding);
123 * Allocate a buffer for the decoded data. The output buffer is the
124 * same size as the input buffer; this assumes that the decoded data
125 * will never be larger than the encoded data. This is a safe
126 * assumption with base64, uuencode, and quoted-printable. Just to
127 * be safe, we still pad the buffer a bit.
129 decoded = mallok(length + 1024);
130 if (decoded == NULL) {
131 lprintf(5, "ERROR: cannot allocate memory.\n");
134 if (pipe(sendpipe) != 0) return;
135 if (pipe(recvpipe) != 0) return;
145 /* send stdio to the pipes */
146 if (dup2(sendpipe[0], 0)<0) lprintf(5, "ERROR dup2()\n");
147 if (dup2(recvpipe[1], 1)<0) lprintf(5, "ERROR dup2()\n");
148 close(sendpipe[1]); /* Close the ends we're not using */
150 if (!strcasecmp(encoding, "base64"))
151 execlp("./base64", "base64", "-d", NULL);
152 else if (!strcasecmp(encoding, "quoted-printable"))
153 execlp("./qpdecode", "qpdecode", NULL);
154 lprintf(5, "ERROR: cannot exec decoder for %s\n", encoding);
158 close(sendpipe[0]); /* Close the ends we're not using */
161 while ( (bytes_sent < length) && (write_error == 0) ) {
162 /* Empty the input pipe FIRST */
163 while (fstat(recvpipe[0], &statbuf), (statbuf.st_size > 0) ) {
164 blocksize = read(recvpipe[0], &decoded[bytes_recv],
167 lprintf(5, "ERROR: cannot read from pipe\n");
169 bytes_recv = bytes_recv + blocksize;
171 /* Then put some data into the output pipe */
172 blocksize = length - bytes_sent;
173 if (blocksize > 2048) blocksize = 2048;
174 if (write(sendpipe[1], &part_start[bytes_sent], blocksize) <0) {
175 lprintf(5, "ERROR: cannot write to pipe: %s\n",
179 bytes_sent = bytes_sent + blocksize;
182 /* Empty the input pipe */
183 while ( (blocksize = read(recvpipe[0], &decoded[bytes_recv], 1)),
185 bytes_recv = bytes_recv + blocksize;
189 CallBack(name, filename, partnum, disposition, decoded,
190 content_type, bytes_recv);
196 * Break out the components of a multipart message
197 * (This function expects to be fed HEADERS + CONTENT)
198 * Note: NULL can be supplied as content_end; in this case, the message is
199 * considered to have ended when the parser encounters a 0x00 byte.
201 void the_mime_parser(char *partnum,
202 char *content_start, char *content_end,
214 char *part_start, *part_end;
220 char content_type[256];
222 char disposition[256];
229 char nested_partnum[256];
231 lprintf(9, "the_mime_parser() called\n");
233 memset(boundary, 0, sizeof boundary);
234 memset(content_type, 0, sizeof content_type);
235 memset(encoding, 0, sizeof encoding);
236 memset(name, 0, sizeof name);
237 memset(filename, 0, sizeof filename);
239 /* Learn interesting things from the headers */
242 ptr = memreadline(ptr, buf, sizeof buf);
243 if (*ptr == 0) return; /* premature end of message */
244 if (content_end != NULL)
245 if (ptr >= content_end) return;
247 for (i=0; i<strlen(buf); ++i)
248 if (isspace(buf[i])) buf[i]=' ';
249 if (!isspace(buf[0])) {
250 if (!strncasecmp(header, "Content-type: ", 14)) {
251 strcpy(content_type, &header[14]);
252 extract_key(name, content_type, "name");
254 if (!strncasecmp(header, "Content-Disposition: ", 21)) {
255 strcpy(disposition, &header[21]);
256 extract_key(filename, disposition, "filename");
258 if (!strncasecmp(header,
259 "Content-transfer-encoding: ", 27))
260 strcpy(encoding, &header[27]);
261 if (strlen(boundary)==0)
262 extract_key(boundary, header, "boundary");
265 if ((strlen(header)+strlen(buf)+2)<sizeof(header))
267 } while ((strlen(buf) > 0) && (*ptr != 0));
269 for (i=0; i<strlen(disposition); ++i)
270 if (disposition[i]==';') disposition[i] = 0;
271 for (i=0; i<strlen(content_type); ++i)
272 if (content_type[i]==';') content_type[i] = 0;
274 if (strlen(boundary) > 0) {
281 /* If this is a multipart message, then recursively process it */
284 sprintf(startary, "--%s", boundary);
285 sprintf(endary, "--%s--", boundary);
288 ptr = memreadline(ptr, buf, sizeof buf);
289 if (*ptr == 0) return; /* premature end of message */
290 if (content_end != NULL)
291 if (ptr >= content_end) return;
292 if ((!strcasecmp(buf, startary))
293 ||(!strcasecmp(buf, endary))) {
294 if (part_start != NULL) {
295 sprintf(nested_partnum, "%s.%d",
296 partnum, ++part_seq);
297 the_mime_parser(nested_partnum,
298 part_start, part_end,
303 } while (strcasecmp(buf, endary));
306 /* If it's not a multipart message, then do something with it */
310 while ((*ptr != 0)&&((content_end==NULL)||(ptr<content_end))) {
316 content_type, encoding, disposition,
317 name, filename, CallBack);
323 * Entry point for the MIME parser.
324 * (This function expects to be fed HEADERS + CONTENT)
325 * Note: NULL can be supplied as content_end; in this case, the message is
326 * considered to have ended when the parser encounters a 0x00 byte.
328 void mime_parser(char *content_start, char *content_end,
339 lprintf(9, "mime_parser() called\n");
340 the_mime_parser("1", content_start, content_end, CallBack);