]> code.citadel.org Git - citadel.git/blob - citadel/mime_parser.c
made some progress with MIME
[citadel.git] / citadel / mime_parser.c
1 /*
2  * mime_parser.c
3  *
4  * This is a really bad attempt at writing a parser to handle MIME-encoded
5  * messages.
6  *
7  * Copyright (c) 1998-1999 by Art Cancro
8  * This code is distributed under the terms of the GNU General Public License.
9  *
10  */
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <signal.h>
16 #include <sys/types.h>
17 #include <ctype.h>
18 #include <string.h>
19 #include "mime_parser.h"
20
21
22
23 void extract_key(char *target, char *source, char *key) {
24         int a, b;
25
26         strcpy(target, source);
27         for (a=0; a<strlen(target); ++a) {
28                 if ((!strncasecmp(&target[a], key, strlen(key)))
29                    && (target[a+strlen(key)]=='=')) {
30                         strcpy(target, &target[a+strlen(key)+1]);
31                         if (target[0]==34) strcpy(target, &target[1]);
32                         for (b=0; b<strlen(target); ++b)
33                                 if (target[b]==34) target[b]=0;
34                         return;
35                         }
36                 }
37         strcpy(target, "");
38         }
39
40
41
42         /**** OTHERWISE, HERE'S WHERE WE HANDLE THE STUFF!! *****
43
44         CallBack(name, filename, "", content, content_type, length);
45
46         **** END OF STUFF-HANDLER ****/
47
48
49 /* 
50  * Utility function to "readline" from memory
51  * (returns new pointer)
52  */
53 char *memreadline(char *start, char *buf, int maxlen) {
54         char ch;
55         char *ptr;
56
57         ptr = start;
58         bzero(buf, maxlen);
59
60         while(1) {
61                 ch = *ptr++;
62                 if ((ch==10)||(ch==0)) {
63                         if (strlen(buf)>0)
64                                 if (buf[strlen(buf)-1]==13)
65                                         buf[strlen(buf)-1] = 0;
66                         return ptr;
67                         }
68                 if (strlen(buf) < (maxlen-1)) {
69                         buf[strlen(buf)+1] = 0;
70                         buf[strlen(buf)] = ch;
71                         }
72                 }
73         }
74
75 /*
76  * Given a message or message-part body and a length, handle any necessary
77  * decoding and pass the request up the stack.
78  */
79 void mime_decode(char *partnum,
80                 char *part_start, size_t length,
81                 char *content_type, char *encoding,
82                 void (*CallBack)
83                         (char *cbname,
84                         char *cbfilename,
85                         char *cbencoding,
86                         void *cbcontent,
87                         char *cbtype,
88                         size_t cblength)
89                 ) {
90
91         cprintf("part=%s, type=%s, length=%d, encoding=%s\n",
92                 partnum, content_type, length, encoding);
93
94         }
95
96 /*
97  * Break out the components of a multipart message
98  * (This function expects to be fed HEADERS + CONTENT)
99  * Note: NULL can be supplied as content_end; in this case, the message is
100  * considered to have ended when the parser encounters a 0x00 byte.
101  */
102 void the_mime_parser(char *partnum,
103                 char *content_start, char *content_end,
104                 void (*CallBack)
105                         (char *cbname,
106                         char *cbfilename,
107                         char *cbencoding,
108                         void *cbcontent,
109                         char *cbtype,
110                         size_t cblength)
111                 ) {
112
113         char *ptr;
114         char *part_start, *part_end;
115         char buf[256];
116         char header[256];
117         char boundary[256];
118         char startary[256];
119         char endary[256];
120         char content_type[256];
121         char encoding[256];
122         int is_multipart;
123         int part_seq = 0;
124         int i;
125         size_t length;
126         char nested_partnum[256];
127
128         ptr = content_start;
129         bzero(boundary, sizeof boundary);
130         bzero(content_type, sizeof content_type);
131         bzero(encoding, sizeof encoding);
132
133         /* Learn interesting things from the headers */
134         strcpy(header, "");
135         do {
136                 ptr = memreadline(ptr, buf, sizeof buf);
137                 if (*ptr == 0) return; /* premature end of message */
138                 if (content_end != NULL)
139                         if (ptr >= content_end) return;
140
141                 for (i=0; i<strlen(buf); ++i)
142                         if (isspace(buf[i])) buf[i]=' ';
143                 if (!isspace(buf[0])) {
144                         if (!strncasecmp(header, "Content-type: ", 14))
145                                 strcpy(content_type, &header[14]);
146                         if (!strncasecmp(header,
147                                 "Content-transfer-encoding: ", 27))
148                                         strcpy(encoding, &header[27]);
149                         if (strlen(boundary)==0)
150                                 extract_key(boundary, header, "boundary");
151                         strcpy(header, "");
152                         }
153                 if ((strlen(header)+strlen(buf)+2)<sizeof(header))
154                         strcat(header, buf);
155                 } while ((strlen(buf) > 0) && (*ptr != 0));
156
157         for (i=0; i<strlen(content_type); ++i) 
158                 if (content_type[i]==';') content_type[i] = 0;
159
160         if (strlen(boundary) > 0) {
161                 is_multipart = 1;
162                 }
163         else {
164                 is_multipart = 0;
165                 }
166
167         /* If this is a multipart message, then recursively process it */
168         part_start = NULL;
169         if (is_multipart) {
170                 sprintf(startary, "--%s", boundary);
171                 sprintf(endary, "--%s--", boundary);
172                 do {
173                         part_end = ptr;
174                         ptr = memreadline(ptr, buf, sizeof buf);
175                         if (*ptr == 0) return; /* premature end of message */
176                         if (content_end != NULL)
177                                 if (ptr >= content_end) return;
178                         if ((!strcasecmp(buf, startary))
179                             ||(!strcasecmp(buf, endary))) {
180                                 if (part_start != NULL) {
181                                         sprintf(nested_partnum, "%s.%d",
182                                                 partnum, ++part_seq);
183                                         the_mime_parser(nested_partnum,
184                                                         part_start, part_end,
185                                                         CallBack);
186                                         }
187                                 part_start = ptr;
188                                 }
189                         } while (strcasecmp(buf, endary));
190                 }
191
192         /* If it's not a multipart message, then do something with it */
193         if (!is_multipart) {
194                 part_start = ptr;
195                 length = 0;
196                 while ((*ptr != 0)&&((content_end==NULL)||(ptr<content_end))) {
197                         ++length;
198                         part_end = ptr++;
199                         }
200                 mime_decode(partnum,
201                                 part_start, length,
202                                 content_type, encoding, CallBack);
203                 }
204         
205         }
206
207 /*
208  * Entry point for the MIME parser.
209  * (This function expects to be fed HEADERS + CONTENT)
210  * Note: NULL can be supplied as content_end; in this case, the message is
211  * considered to have ended when the parser encounters a 0x00 byte.
212  */
213 void mime_parser(char *content_start, char *content_end,
214                 void (*CallBack)
215                         (char *cbname,
216                         char *cbfilename,
217                         char *cbencoding,
218                         void *cbcontent,
219                         char *cbtype,
220                         size_t cblength)
221                 ) {
222
223         the_mime_parser("1", content_start, content_end, CallBack);
224         }