4 * This is the MIME parser for Citadel. Sometimes it actually works.
6 * Copyright (c) 1998-2001 by Art Cancro
7 * This code is distributed under the terms of the GNU General Public License.
19 #include <sys/types.h>
26 #include "dynloader.h"
27 #include "sysdep_decls.h"
28 #include "mime_parser.h"
32 void extract_key(char *target, char *source, char *key)
36 strcpy(target, source);
37 for (a = 0; a < strlen(target); ++a) {
38 if ((!strncasecmp(&target[a], key, strlen(key)))
39 && (target[a + strlen(key)] == '=')) {
40 strcpy(target, &target[a + strlen(key) + 1]);
42 strcpy(target, &target[1]);
43 for (b = 0; b < strlen(target); ++b)
54 * For non-multipart messages, we need to generate a quickie partnum of "1"
55 * to return to callback functions. Some callbacks demand it.
57 char *fixed_partnum(char *supplied_partnum) {
58 if (supplied_partnum == NULL) return "1";
59 if (strlen(supplied_partnum)==0) return "1";
60 return supplied_partnum;
66 * Convert "quoted-printable" to binary. Returns number of bytes decoded.
68 int decode_quoted_printable(char *decoded, char *encoded, int sourcelen) {
71 int soft_line_break = 0;
73 int decoded_length = 0;
81 for (i = 0; i < sourcelen; ++i) {
83 buf[buf_length++] = encoded[i];
85 if ( (encoded[i] == '\n')
87 || (i == (sourcelen-1)) ) {
88 buf[buf_length++] = 0;
90 /*** begin -- process one line ***/
92 if (buf[strlen(buf)-1] == '\n') {
93 buf[strlen(buf)-1] = 0;
95 if (buf[strlen(buf)-1] == '\r') {
96 buf[strlen(buf)-1] = 0;
98 while (isspace(buf[strlen(buf)-1])) {
99 buf[strlen(buf)-1] = 0;
103 while (strlen(buf) > 0) {
104 if (!strcmp(buf, "=")) {
107 } else if ((strlen(buf)>=3) && (buf[0]=='=')) {
108 sscanf(&buf[1], "%02x", &ch);
109 decoded[decoded_length++] = ch;
110 strcpy(buf, &buf[3]);
112 decoded[decoded_length++] = buf[0];
113 strcpy(buf, &buf[1]);
116 if (soft_line_break == 0) {
117 decoded[decoded_length++] = '\r';
118 decoded[decoded_length++] = '\n';
121 /*** end -- process one line ***/
125 decoded[decoded_length++] = 0;
126 return(decoded_length);
130 * Given a message or message-part body and a length, handle any necessary
131 * decoding and pass the request up the stack.
133 void mime_decode(char *partnum,
134 char *part_start, size_t length,
135 char *content_type, char *encoding,
137 char *name, char *filename,
148 void (*PreMultiPartCallBack)
158 void (*PostMultiPartCallBack)
174 size_t bytes_decoded = 0;
176 lprintf(9, "mime_decode() called\n");
178 /* Some encodings aren't really encodings */
179 if (!strcasecmp(encoding, "7bit"))
180 strcpy(encoding, "");
181 if (!strcasecmp(encoding, "8bit"))
182 strcpy(encoding, "");
183 if (!strcasecmp(encoding, "binary"))
184 strcpy(encoding, "");
186 /* If this part is not encoded, send as-is */
187 if ( (strlen(encoding) == 0) || (dont_decode)) {
188 if (CallBack != NULL) {
189 CallBack(name, filename, fixed_partnum(partnum),
190 disposition, part_start,
191 content_type, length, encoding, userdata);
195 if ((strcasecmp(encoding, "base64"))
196 && (strcasecmp(encoding, "quoted-printable"))) {
197 lprintf(9, "ERROR: unknown MIME encoding '%s'\n", encoding);
201 * Allocate a buffer for the decoded data. The output buffer is the
202 * same size as the input buffer; this assumes that the decoded data
203 * will never be larger than the encoded data. This is a safe
204 * assumption with base64, uuencode, and quoted-printable. Just to
205 * be safe, we still pad the buffer a bit.
207 decoded = mallok(length + 1024);
208 if (decoded == NULL) {
209 lprintf(9, "ERROR: cannot allocate memory.\n");
213 if (!strcasecmp(encoding, "base64")) {
214 bytes_decoded = decode_base64(decoded, part_start);
216 else if (!strcasecmp(encoding, "quoted-printable")) {
217 bytes_decoded = decode_quoted_printable(decoded,
221 if (bytes_decoded > 0) if (CallBack != NULL) {
222 CallBack(name, filename, fixed_partnum(partnum),
223 disposition, decoded,
224 content_type, bytes_decoded, "binary", userdata);
231 * Break out the components of a multipart message
232 * (This function expects to be fed HEADERS + CONTENT)
233 * Note: NULL can be supplied as content_end; in this case, the message is
234 * considered to have ended when the parser encounters a 0x00 byte.
236 void the_mime_parser(char *partnum,
237 char *content_start, char *content_end,
248 void (*PreMultiPartCallBack)
258 void (*PostMultiPartCallBack)
274 char *part_start, *part_end = NULL;
280 char content_type[SIZ];
281 size_t content_length;
283 char disposition[SIZ];
290 char nested_partnum[SIZ];
292 lprintf(9, "the_mime_parser() called\n");
294 memset(boundary, 0, sizeof boundary);
295 memset(content_type, 0, sizeof content_type);
296 memset(encoding, 0, sizeof encoding);
297 memset(name, 0, sizeof name);
298 memset(filename, 0, sizeof filename);
299 memset(disposition, 0, sizeof disposition);
302 /* If the caller didn't supply an endpointer, generate one by measure */
303 if (content_end == NULL) {
304 content_end = &content_start[strlen(content_start)];
307 /* Learn interesting things from the headers */
310 ptr = memreadline(ptr, buf, sizeof buf);
311 if (ptr >= content_end)
314 for (i = 0; i < strlen(buf); ++i)
317 if (!isspace(buf[0])) {
318 if (!strncasecmp(header, "Content-type: ", 14)) {
319 strcpy(content_type, &header[14]);
320 extract_key(name, content_type, "name");
321 lprintf(9, "Extracted content-type <%s>\n",
324 if (!strncasecmp(header, "Content-Disposition: ", 21)) {
325 strcpy(disposition, &header[21]);
326 extract_key(filename, disposition, "filename");
328 if (!strncasecmp(header, "Content-length: ", 16)) {
329 content_length = (size_t) atol(&header[16]);
331 if (!strncasecmp(header,
332 "Content-transfer-encoding: ", 27))
333 strcpy(encoding, &header[27]);
334 if (strlen(boundary) == 0)
335 extract_key(boundary, header, "boundary");
338 if ((strlen(header) + strlen(buf) + 2) < sizeof(header))
340 } while ((strlen(buf) > 0) && (*ptr != 0));
342 for (i = 0; i < strlen(disposition); ++i)
343 if (disposition[i] == ';')
345 while (isspace(disposition[0]))
346 strcpy(disposition, &disposition[1]);
347 for (i = 0; i < strlen(content_type); ++i)
348 if (content_type[i] == ';')
350 while (isspace(content_type[0]))
351 strcpy(content_type, &content_type[1]);
353 if (strlen(boundary) > 0) {
359 lprintf(9, "is_multipart=%d, boundary=<%s>\n",
360 is_multipart, boundary);
362 /* If this is a multipart message, then recursively process it */
366 /* Tell the client about this message's multipartedness */
367 if (PreMultiPartCallBack != NULL) {
368 PreMultiPartCallBack("", "", partnum, "",
370 0, encoding, userdata);
373 /* Figure out where the boundaries are */
374 sprintf(startary, "--%s", boundary);
375 sprintf(endary, "--%s--", boundary);
377 if ( (!strncasecmp(ptr, startary, strlen(startary)))
378 || (!strncasecmp(ptr, endary, strlen(endary))) ) {
379 lprintf(9, "hit boundary!\n");
380 if (part_start != NULL) {
381 if (strlen(partnum) > 0) {
382 sprintf(nested_partnum, "%s.%d",
383 partnum, ++part_seq);
386 sprintf(nested_partnum, "%d",
389 the_mime_parser(nested_partnum,
390 part_start, part_end,
392 PreMultiPartCallBack,
393 PostMultiPartCallBack,
397 ptr = memreadline(ptr, buf, sizeof(buf));
404 } while ( (strcasecmp(ptr, endary)) && (ptr <= content_end) );
405 if (PostMultiPartCallBack != NULL) {
406 PostMultiPartCallBack("", "", partnum, "", NULL,
407 content_type, 0, encoding, userdata);
412 /* If it's not a multipart message, then do something with it */
414 lprintf(9, "doing non-multipart thing\n");
417 while (ptr < content_end) {
421 part_end = content_end;
423 /* Truncate if the header told us to */
424 if ( (content_length > 0) && (length > content_length) ) {
425 length = content_length;
426 lprintf(9, "truncated to %ld\n", (long)content_length);
431 content_type, encoding, disposition,
433 CallBack, NULL, NULL,
434 userdata, dont_decode);
441 * Entry point for the MIME parser.
442 * (This function expects to be fed HEADERS + CONTENT)
443 * Note: NULL can be supplied as content_end; in this case, the message is
444 * considered to have ended when the parser encounters a 0x00 byte.
446 void mime_parser(char *content_start,
460 void (*PreMultiPartCallBack)
471 void (*PostMultiPartCallBack)
487 lprintf(9, "mime_parser() called\n");
488 the_mime_parser("", content_start, content_end,
490 PreMultiPartCallBack,
491 PostMultiPartCallBack,
492 userdata, dont_decode);