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.
128 decoded = mallok(length);
129 if (decoded == NULL) {
130 lprintf(5, "ERROR: cannot allocate memory.\n");
133 if (pipe(sendpipe) != 0) return;
134 if (pipe(recvpipe) != 0) return;
143 /* close(2); FIX uncomment this when solid */
144 /* send stdio to the pipes */
145 if (dup2(sendpipe[0], 0)<0) lprintf(5, "ERROR dup2()\n");
146 if (dup2(recvpipe[1], 1)<0) lprintf(5, "ERROR dup2()\n");
147 close(sendpipe[1]); /* Close the ends we're not using */
149 if (!strcasecmp(encoding, "base64"))
150 execlp("./base64", "base64", "-d", NULL);
151 else if (!strcasecmp(encoding, "quoted-printable"))
152 execlp("./qpdecode", "qpdecode", NULL);
153 lprintf(5, "ERROR: cannot exec decoder for %s\n", encoding);
157 close(sendpipe[0]); /* Close the ends we're not using */
160 while ( (bytes_sent < length) && (write_error == 0) ) {
161 /* Empty the input pipe FIRST */
162 while (fstat(recvpipe[0], &statbuf), (statbuf.st_size > 0) ) {
163 blocksize = read(recvpipe[0], &decoded[bytes_recv],
166 lprintf(5, "ERROR: cannot read from pipe\n");
168 bytes_recv = bytes_recv + blocksize;
170 /* Then put some data into the output pipe */
171 blocksize = length - bytes_sent;
172 if (blocksize > 2048) blocksize = 2048;
173 if (write(sendpipe[1], &part_start[bytes_sent], blocksize) <0) {
174 lprintf(5, "ERROR: cannot write to pipe: %s\n",
178 bytes_sent = bytes_sent + blocksize;
181 /* Empty the input pipe */
182 while ( (blocksize = read(recvpipe[0], &decoded[bytes_recv], 1)),
184 bytes_recv = bytes_recv + blocksize;
188 CallBack(name, filename, partnum, disposition, decoded,
189 content_type, bytes_recv);
194 * Break out the components of a multipart message
195 * (This function expects to be fed HEADERS + CONTENT)
196 * Note: NULL can be supplied as content_end; in this case, the message is
197 * considered to have ended when the parser encounters a 0x00 byte.
199 void the_mime_parser(char *partnum,
200 char *content_start, char *content_end,
212 char *part_start, *part_end;
218 char content_type[256];
220 char disposition[256];
227 char nested_partnum[256];
229 lprintf(9, "the_mime_parser() called\n");
231 memset(boundary, 0, sizeof boundary);
232 memset(content_type, 0, sizeof content_type);
233 memset(encoding, 0, sizeof encoding);
234 memset(name, 0, sizeof name);
235 memset(filename, 0, sizeof filename);
237 /* Learn interesting things from the headers */
240 ptr = memreadline(ptr, buf, sizeof buf);
241 if (*ptr == 0) return; /* premature end of message */
242 if (content_end != NULL)
243 if (ptr >= content_end) return;
245 for (i=0; i<strlen(buf); ++i)
246 if (isspace(buf[i])) buf[i]=' ';
247 if (!isspace(buf[0])) {
248 if (!strncasecmp(header, "Content-type: ", 14)) {
249 strcpy(content_type, &header[14]);
250 extract_key(name, content_type, "name");
252 if (!strncasecmp(header, "Content-Disposition: ", 21)) {
253 strcpy(disposition, &header[21]);
254 extract_key(filename, disposition, "filename");
256 if (!strncasecmp(header,
257 "Content-transfer-encoding: ", 27))
258 strcpy(encoding, &header[27]);
259 if (strlen(boundary)==0)
260 extract_key(boundary, header, "boundary");
263 if ((strlen(header)+strlen(buf)+2)<sizeof(header))
265 } while ((strlen(buf) > 0) && (*ptr != 0));
267 for (i=0; i<strlen(disposition); ++i)
268 if (disposition[i]==';') disposition[i] = 0;
269 for (i=0; i<strlen(content_type); ++i)
270 if (content_type[i]==';') content_type[i] = 0;
272 if (strlen(boundary) > 0) {
279 /* If this is a multipart message, then recursively process it */
282 sprintf(startary, "--%s", boundary);
283 sprintf(endary, "--%s--", boundary);
286 ptr = memreadline(ptr, buf, sizeof buf);
287 if (*ptr == 0) return; /* premature end of message */
288 if (content_end != NULL)
289 if (ptr >= content_end) return;
290 if ((!strcasecmp(buf, startary))
291 ||(!strcasecmp(buf, endary))) {
292 if (part_start != NULL) {
293 sprintf(nested_partnum, "%s.%d",
294 partnum, ++part_seq);
295 the_mime_parser(nested_partnum,
296 part_start, part_end,
301 } while (strcasecmp(buf, endary));
304 /* If it's not a multipart message, then do something with it */
308 while ((*ptr != 0)&&((content_end==NULL)||(ptr<content_end))) {
314 content_type, encoding, disposition,
315 name, filename, CallBack);
321 * Entry point for the MIME parser.
322 * (This function expects to be fed HEADERS + CONTENT)
323 * Note: NULL can be supplied as content_end; in this case, the message is
324 * considered to have ended when the parser encounters a 0x00 byte.
326 void mime_parser(char *content_start, char *content_end,
337 lprintf(9, "mime_parser() called\n");
338 the_mime_parser("1", content_start, content_end, CallBack);