460b9539 |
1 | /* |
2 | * This file is part of DisOrder |
3 | * Copyright (C) 2005 Richard Kettlewell |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, but |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
18 | * USA |
19 | */ |
20 | |
21 | |
22 | #include <config.h> |
23 | #include "types.h" |
24 | |
25 | #include <string.h> |
26 | #include <ctype.h> |
27 | |
28 | #include "mem.h" |
29 | #include "mime.h" |
30 | #include "vector.h" |
31 | #include "hex.h" |
32 | |
33 | static int whitespace(int c) { |
34 | switch(c) { |
35 | case ' ': |
36 | case '\t': |
37 | case '\r': |
38 | case '\n': |
39 | return 1; |
40 | default: |
41 | return 0; |
42 | } |
43 | } |
44 | |
45 | static int tspecial(int c) { |
46 | switch(c) { |
47 | case '(': |
48 | case ')': |
49 | case '<': |
50 | case '>': |
51 | case '@': |
52 | case ',': |
53 | case ';': |
54 | case ':': |
55 | case '\\': |
56 | case '"': |
57 | case '/': |
58 | case '[': |
59 | case ']': |
60 | case '?': |
61 | case '=': |
62 | return 1; |
63 | default: |
64 | return 0; |
65 | } |
66 | } |
67 | |
68 | static const char *skipwhite(const char *s) { |
69 | int c, depth; |
70 | |
71 | for(;;) { |
72 | switch(c = *s) { |
73 | case ' ': |
74 | case '\t': |
75 | case '\r': |
76 | case '\n': |
77 | ++s; |
78 | break; |
79 | case '(': |
80 | ++s; |
81 | depth = 1; |
82 | while(*s && depth) { |
83 | c = *s++; |
84 | switch(c) { |
85 | case '(': ++depth; break; |
86 | case ')': --depth; break; |
87 | case '\\': |
88 | if(!*s) return 0; |
89 | ++s; |
90 | break; |
91 | } |
92 | } |
93 | if(depth) return 0; |
94 | break; |
95 | default: |
96 | return s; |
97 | } |
98 | } |
99 | } |
100 | |
101 | static const char *parsestring(const char *s, char **valuep) { |
102 | struct dynstr value; |
103 | int c; |
104 | |
105 | dynstr_init(&value); |
106 | ++s; |
107 | while((c = *s++) != '"') { |
108 | switch(c) { |
109 | case '\\': |
110 | if(!(c = *s++)) return 0; |
111 | default: |
112 | dynstr_append(&value, c); |
113 | break; |
114 | } |
115 | } |
116 | if(!c) return 0; |
117 | dynstr_terminate(&value); |
118 | *valuep = value.vec; |
119 | return s; |
120 | } |
121 | |
122 | int mime_content_type(const char *s, |
123 | char **typep, |
124 | char **parameternamep, |
125 | char **parametervaluep) { |
126 | struct dynstr type, parametername, parametervalue; |
127 | |
128 | dynstr_init(&type); |
129 | if(!(s = skipwhite(s))) return -1; |
130 | if(!*s) return -1; |
131 | while(*s && !tspecial(*s) && !whitespace(*s)) |
132 | dynstr_append(&type, tolower((unsigned char)*s++)); |
133 | if(!(s = skipwhite(s))) return -1; |
134 | if(*s++ != '/') return -1; |
135 | dynstr_append(&type, '/'); |
136 | if(!(s = skipwhite(s))) return -1; |
137 | while(*s && !tspecial(*s) && !whitespace(*s)) |
138 | dynstr_append(&type, tolower((unsigned char)*s++)); |
139 | if(!(s = skipwhite(s))) return -1; |
140 | |
141 | if(*s == ';') { |
142 | dynstr_init(¶metername); |
143 | ++s; |
144 | if(!(s = skipwhite(s))) return -1; |
145 | if(!*s) return -1; |
146 | while(*s && !tspecial(*s) && !whitespace(*s)) |
147 | dynstr_append(¶metername, tolower((unsigned char)*s++)); |
148 | if(!(s = skipwhite(s))) return -1; |
149 | if(*s++ != '=') return -1; |
150 | if(!(s = skipwhite(s))) return -1; |
151 | if(*s == '"') { |
152 | if(!(s = parsestring(s, parametervaluep))) return -1; |
153 | } else { |
154 | dynstr_init(¶metervalue); |
155 | while(*s && !tspecial(*s) && !whitespace(*s)) |
156 | dynstr_append(¶metervalue, *s++); |
157 | dynstr_terminate(¶metervalue); |
158 | *parametervaluep = parametervalue.vec; |
159 | } |
160 | if(!(s = skipwhite(s))) return -1; |
161 | dynstr_terminate(¶metername); |
162 | *parameternamep = parametername.vec; |
163 | } else |
164 | *parametervaluep = *parameternamep = 0; |
165 | dynstr_terminate(&type); |
166 | *typep = type.vec; |
167 | return 0; |
168 | } |
169 | |
170 | static int iscrlf(const char *ptr) { |
171 | return ptr[0] == '\r' && ptr[1] == '\n'; |
172 | } |
173 | |
174 | const char *mime_parse(const char *s, |
175 | int (*callback)(const char *name, const char *value, |
176 | void *u), |
177 | void *u) { |
178 | struct dynstr name, value; |
179 | char *cte = 0, *p; |
180 | |
181 | while(*s && !iscrlf(s)) { |
182 | dynstr_init(&name); |
183 | dynstr_init(&value); |
184 | while(*s && !tspecial(*s) && !whitespace(*s)) |
185 | dynstr_append(&name, tolower((unsigned char)*s++)); |
186 | if(!(s = skipwhite(s))) return 0; |
187 | if(*s != ':') return 0; |
188 | ++s; |
189 | while(*s && !(*s == '\n' && !(s[1] == ' ' || s[1] == '\t'))) |
190 | dynstr_append(&value, *s++); |
191 | if(*s) ++s; |
192 | dynstr_terminate(&name); |
193 | dynstr_terminate(&value); |
194 | if(!strcmp(name.vec, "content-transfer-encoding")) { |
195 | cte = xstrdup(value.vec); |
196 | for(p = cte; *p; p++) |
197 | *p = tolower((unsigned char)*p); |
198 | } |
199 | if(callback(name.vec, value.vec, u)) return 0; |
200 | } |
201 | if(*s) s += 2; |
202 | if(cte) { |
203 | if(!strcmp(cte, "base64")) return mime_base64(s); |
204 | if(!strcmp(cte, "quoted-printable")) return mime_qp(s); |
205 | } |
206 | return s; |
207 | } |
208 | |
209 | static int isboundary(const char *ptr, const char *boundary, size_t bl) { |
210 | return (ptr[0] == '-' |
211 | && ptr[1] == '-' |
212 | && !strncmp(ptr + 2, boundary, bl) |
213 | && (iscrlf(ptr + bl + 2) |
214 | || (ptr[bl + 2] == '-' |
215 | && ptr[bl + 3] == '-' |
216 | && iscrlf(ptr + bl + 4)))); |
217 | } |
218 | |
219 | static int isfinal(const char *ptr, const char *boundary, size_t bl) { |
220 | return (ptr[0] == '-' |
221 | && ptr[1] == '-' |
222 | && !strncmp(ptr + 2, boundary, bl) |
223 | && ptr[bl + 2] == '-' |
224 | && ptr[bl + 3] == '-' |
225 | && iscrlf(ptr + bl + 4)); |
226 | } |
227 | |
228 | int mime_multipart(const char *s, |
229 | int (*callback)(const char *s, void *u), |
230 | const char *boundary, |
231 | void *u) { |
232 | size_t bl = strlen(boundary); |
233 | const char *start, *e; |
234 | int ret; |
235 | |
236 | if(!isboundary(s, boundary, bl)) return -1; |
237 | while(!isfinal(s, boundary, bl)) { |
238 | s = strstr(s, "\r\n") + 2; |
239 | start = s; |
240 | while(!isboundary(s, boundary, bl)) { |
241 | if(!(e = strstr(s, "\r\n"))) return -1; |
242 | s = e + 2; |
243 | } |
244 | if((ret = callback(xstrndup(start, |
245 | s == start ? 0 : s - start - 2), |
246 | u))) |
247 | return ret; |
248 | } |
249 | return 0; |
250 | } |
251 | |
252 | int mime_rfc2388_content_disposition(const char *s, |
253 | char **dispositionp, |
254 | char **parameternamep, |
255 | char **parametervaluep) { |
256 | struct dynstr disposition, parametername, parametervalue; |
257 | |
258 | dynstr_init(&disposition); |
259 | if(!(s = skipwhite(s))) return -1; |
260 | if(!*s) return -1; |
261 | while(*s && !tspecial(*s) && !whitespace(*s)) |
262 | dynstr_append(&disposition, tolower((unsigned char)*s++)); |
263 | if(!(s = skipwhite(s))) return -1; |
264 | |
265 | if(*s == ';') { |
266 | dynstr_init(¶metername); |
267 | ++s; |
268 | if(!(s = skipwhite(s))) return -1; |
269 | if(!*s) return -1; |
270 | while(*s && !tspecial(*s) && !whitespace(*s)) |
271 | dynstr_append(¶metername, tolower((unsigned char)*s++)); |
272 | if(!(s = skipwhite(s))) return -1; |
273 | if(*s++ != '=') return -1; |
274 | if(!(s = skipwhite(s))) return -1; |
275 | if(*s == '"') { |
276 | if(!(s = parsestring(s, parametervaluep))) return -1; |
277 | } else { |
278 | dynstr_init(¶metervalue); |
279 | while(*s && !tspecial(*s) && !whitespace(*s)) |
280 | dynstr_append(¶metervalue, *s++); |
281 | dynstr_terminate(¶metervalue); |
282 | *parametervaluep = parametervalue.vec; |
283 | } |
284 | if(!(s = skipwhite(s))) return -1; |
285 | dynstr_terminate(¶metername); |
286 | *parameternamep = parametername.vec; |
287 | } else |
288 | *parametervaluep = *parameternamep = 0; |
289 | dynstr_terminate(&disposition); |
290 | *dispositionp = disposition.vec; |
291 | return 0; |
292 | } |
293 | |
294 | char *mime_qp(const char *s) { |
295 | struct dynstr d; |
296 | int c, a, b; |
297 | const char *t; |
298 | |
299 | dynstr_init(&d); |
300 | while((c = *s++)) { |
301 | switch(c) { |
302 | case '=': |
303 | if((a = unhexdigitq(s[0])) != -1 |
304 | && (b = unhexdigitq(s[1])) != -1) { |
305 | dynstr_append(&d, a * 16 + b); |
306 | s += 2; |
307 | } else { |
308 | t = s; |
309 | while(*t == ' ' || *t == '\t') ++t; |
310 | if(iscrlf(t)) { |
311 | /* soft line break */ |
312 | s = t + 2; |
313 | } else |
314 | return 0; |
315 | } |
316 | break; |
317 | case ' ': |
318 | case '\t': |
319 | t = s; |
320 | while(*t == ' ' || *t == '\t') ++t; |
321 | if(iscrlf(t)) |
322 | /* trailing space is always eliminated */ |
323 | s = t; |
324 | else |
325 | dynstr_append(&d, c); |
326 | break; |
327 | default: |
328 | dynstr_append(&d, c); |
329 | break; |
330 | } |
331 | } |
332 | dynstr_terminate(&d); |
333 | return d.vec; |
334 | } |
335 | |
336 | char *mime_base64(const char *s) { |
337 | struct dynstr d; |
338 | const char *t; |
339 | int b[4], n, c; |
340 | static const char table[] = |
341 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
342 | |
343 | dynstr_init(&d); |
344 | n = 0; |
345 | while((c = (unsigned char)*s++)) { |
346 | if((t = strchr(table, c))) { |
347 | b[n++] = t - table; |
348 | if(n == 4) { |
349 | dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); |
350 | dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); |
351 | dynstr_append(&d, (b[2] << 6) + b[3]); |
352 | n = 0; |
353 | } |
354 | } else if(c == '=') { |
355 | if(n >= 2) { |
356 | dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); |
357 | if(n == 3) |
358 | dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); |
359 | } |
360 | break; |
361 | } |
362 | } |
363 | dynstr_terminate(&d); |
364 | return d.vec; |
365 | } |
366 | |
367 | /* |
368 | Local Variables: |
369 | c-basic-offset:2 |
370 | comment-column:40 |
371 | fill-column:79 |
372 | End: |
373 | */ |