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,
80 char *name, char *filename,
95 size_t bytes_sent = 0;
96 size_t bytes_recv = 0;
100 /* If this part is not encoded, send as-is */
101 if (strlen(encoding)==0) {
102 CallBack(name, filename, partnum, part_start,
103 content_type, length);
107 if ( (strcasecmp(encoding, "base64"))
108 && (strcasecmp(encoding, "7bit")) ) {
109 lprintf(5, "ERROR: unknown MIME encoding '%s'\n", encoding);
114 * Allocate a buffer for the decoded data. The output buffer is the
115 * same size as the input buffer; this assumes that the decoded data
116 * will never be larger than the encoded data. This is a safe
117 * assumption with base64, uuencode, and quoted-printable.
119 decoded = mallok(length);
120 if (decoded == NULL) {
121 lprintf(5, "ERROR: cannot allocate memory.\n");
124 if (pipe(sendpipe) != 0) return;
125 if (pipe(recvpipe) != 0) return;
128 lprintf(9, "fork() returned %d\n", childpid);
135 /* close(2); FIX uncomment this when solid */
136 /* send stdio to the pipes */
137 if (dup2(sendpipe[0], 0)<0) lprintf(5, "ERROR dup2()\n");
138 if (dup2(recvpipe[1], 1)<0) lprintf(5, "ERROR dup2()\n");
139 close(sendpipe[1]); /* Close the ends we're not using */
141 if (!strcasecmp(encoding, "base64"))
142 execlp("./base64", "base64", "-d", NULL);
143 else if (!strcasecmp(encoding, "7bit"))
144 execlp("/bin/dd", "dd", NULL);
145 lprintf(5, "ERROR: cannot exec decoder for %s\n", encoding);
149 close(sendpipe[0]); /* Close the ends we're not using */
152 lprintf(9, "ready to send %d bytes\n", length);
154 while ( (bytes_sent < length) && (write_error == 0) ) {
155 /* Empty the input pipe FIRST */
156 while (fstat(recvpipe[0], &statbuf), (statbuf.st_size > 0) ) {
157 blocksize = read(recvpipe[0], &decoded[bytes_recv],
160 lprintf(5, "ERROR: cannot read from pipe\n");
162 bytes_recv = bytes_recv + blocksize;
164 /* Then put some data into the output pipe */
165 blocksize = length - bytes_sent;
166 if (blocksize > 2048) blocksize = 2048;
167 if (write(sendpipe[1], &part_start[bytes_sent], blocksize) <0) {
168 lprintf(5, "ERROR: cannot write to pipe: %s\n",
172 bytes_sent = bytes_sent + blocksize;
175 /* Empty the input pipe */
176 while ( (blocksize = read(recvpipe[0], &decoded[bytes_recv], 1)),
178 bytes_recv = bytes_recv + blocksize;
181 lprintf(9, "Decoded length = %d\n", bytes_recv);
184 CallBack(name, filename, partnum, decoded,
185 content_type, bytes_recv);
190 * Break out the components of a multipart message
191 * (This function expects to be fed HEADERS + CONTENT)
192 * Note: NULL can be supplied as content_end; in this case, the message is
193 * considered to have ended when the parser encounters a 0x00 byte.
195 void the_mime_parser(char *partnum,
196 char *content_start, char *content_end,
207 char *part_start, *part_end;
213 char content_type[256];
221 char nested_partnum[256];
224 memset(boundary, 0, sizeof boundary);
225 memset(content_type, 0, sizeof content_type);
226 memset(encoding, 0, sizeof encoding);
227 memset(name, 0, sizeof name);
228 memset(filename, 0, sizeof filename);
230 /* Learn interesting things from the headers */
233 ptr = memreadline(ptr, buf, sizeof buf);
234 if (*ptr == 0) return; /* premature end of message */
235 if (content_end != NULL)
236 if (ptr >= content_end) return;
238 for (i=0; i<strlen(buf); ++i)
239 if (isspace(buf[i])) buf[i]=' ';
240 if (!isspace(buf[0])) {
241 if (!strncasecmp(header, "Content-type: ", 14)) {
242 strcpy(content_type, &header[14]);
243 extract_key(name, content_type, "name");
245 if (!strncasecmp(header, "Content-Disposition: ", 21)) {
246 extract_key(filename, header, "filename");
248 if (!strncasecmp(header,
249 "Content-transfer-encoding: ", 27))
250 strcpy(encoding, &header[27]);
251 if (strlen(boundary)==0)
252 extract_key(boundary, header, "boundary");
255 if ((strlen(header)+strlen(buf)+2)<sizeof(header))
257 } while ((strlen(buf) > 0) && (*ptr != 0));
259 for (i=0; i<strlen(content_type); ++i)
260 if (content_type[i]==';') content_type[i] = 0;
262 if (strlen(boundary) > 0) {
269 /* If this is a multipart message, then recursively process it */
272 sprintf(startary, "--%s", boundary);
273 sprintf(endary, "--%s--", boundary);
276 ptr = memreadline(ptr, buf, sizeof buf);
277 if (*ptr == 0) return; /* premature end of message */
278 if (content_end != NULL)
279 if (ptr >= content_end) return;
280 if ((!strcasecmp(buf, startary))
281 ||(!strcasecmp(buf, endary))) {
282 if (part_start != NULL) {
283 sprintf(nested_partnum, "%s.%d",
284 partnum, ++part_seq);
285 the_mime_parser(nested_partnum,
286 part_start, part_end,
291 } while (strcasecmp(buf, endary));
294 /* If it's not a multipart message, then do something with it */
298 while ((*ptr != 0)&&((content_end==NULL)||(ptr<content_end))) {
304 content_type, encoding,
305 name, filename, CallBack);
311 * Entry point for the MIME parser.
312 * (This function expects to be fed HEADERS + CONTENT)
313 * Note: NULL can be supplied as content_end; in this case, the message is
314 * considered to have ended when the parser encounters a 0x00 byte.
316 void mime_parser(char *content_start, char *content_end,
326 the_mime_parser("1", content_start, content_end, CallBack);