Commit | Line | Data |
---|---|---|
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 | ||
22896b25 RK |
28 | #include <stdio.h> |
29 | ||
460b9539 | 30 | #include "mem.h" |
31 | #include "mime.h" | |
32 | #include "vector.h" | |
33 | #include "hex.h" | |
34 | ||
35 | static int whitespace(int c) { | |
36 | switch(c) { | |
37 | case ' ': | |
38 | case '\t': | |
39 | case '\r': | |
40 | case '\n': | |
41 | return 1; | |
42 | default: | |
43 | return 0; | |
44 | } | |
45 | } | |
46 | ||
47 | static int tspecial(int c) { | |
48 | switch(c) { | |
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 | case '?': | |
63 | case '=': | |
64 | return 1; | |
65 | default: | |
66 | return 0; | |
67 | } | |
68 | } | |
69 | ||
70 | static const char *skipwhite(const char *s) { | |
71 | int c, depth; | |
72 | ||
73 | for(;;) { | |
74 | switch(c = *s) { | |
75 | case ' ': | |
76 | case '\t': | |
77 | case '\r': | |
78 | case '\n': | |
79 | ++s; | |
80 | break; | |
81 | case '(': | |
82 | ++s; | |
83 | depth = 1; | |
84 | while(*s && depth) { | |
85 | c = *s++; | |
86 | switch(c) { | |
87 | case '(': ++depth; break; | |
88 | case ')': --depth; break; | |
89 | case '\\': | |
90 | if(!*s) return 0; | |
91 | ++s; | |
92 | break; | |
93 | } | |
94 | } | |
95 | if(depth) return 0; | |
96 | break; | |
97 | default: | |
98 | return s; | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | static const char *parsestring(const char *s, char **valuep) { | |
104 | struct dynstr value; | |
105 | int c; | |
106 | ||
107 | dynstr_init(&value); | |
108 | ++s; | |
109 | while((c = *s++) != '"') { | |
110 | switch(c) { | |
111 | case '\\': | |
112 | if(!(c = *s++)) return 0; | |
113 | default: | |
114 | dynstr_append(&value, c); | |
115 | break; | |
116 | } | |
117 | } | |
118 | if(!c) return 0; | |
119 | dynstr_terminate(&value); | |
120 | *valuep = value.vec; | |
121 | return s; | |
122 | } | |
123 | ||
124 | int mime_content_type(const char *s, | |
125 | char **typep, | |
126 | char **parameternamep, | |
127 | char **parametervaluep) { | |
128 | struct dynstr type, parametername, parametervalue; | |
129 | ||
130 | dynstr_init(&type); | |
131 | if(!(s = skipwhite(s))) return -1; | |
132 | if(!*s) return -1; | |
133 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
134 | dynstr_append(&type, tolower((unsigned char)*s++)); | |
135 | if(!(s = skipwhite(s))) return -1; | |
136 | if(*s++ != '/') return -1; | |
137 | dynstr_append(&type, '/'); | |
138 | if(!(s = skipwhite(s))) return -1; | |
139 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
140 | dynstr_append(&type, tolower((unsigned char)*s++)); | |
141 | if(!(s = skipwhite(s))) return -1; | |
142 | ||
143 | if(*s == ';') { | |
144 | dynstr_init(¶metername); | |
145 | ++s; | |
146 | if(!(s = skipwhite(s))) return -1; | |
147 | if(!*s) return -1; | |
148 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
149 | dynstr_append(¶metername, tolower((unsigned char)*s++)); | |
150 | if(!(s = skipwhite(s))) return -1; | |
151 | if(*s++ != '=') return -1; | |
152 | if(!(s = skipwhite(s))) return -1; | |
153 | if(*s == '"') { | |
154 | if(!(s = parsestring(s, parametervaluep))) return -1; | |
155 | } else { | |
156 | dynstr_init(¶metervalue); | |
157 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
158 | dynstr_append(¶metervalue, *s++); | |
159 | dynstr_terminate(¶metervalue); | |
160 | *parametervaluep = parametervalue.vec; | |
161 | } | |
162 | if(!(s = skipwhite(s))) return -1; | |
163 | dynstr_terminate(¶metername); | |
164 | *parameternamep = parametername.vec; | |
165 | } else | |
166 | *parametervaluep = *parameternamep = 0; | |
167 | dynstr_terminate(&type); | |
168 | *typep = type.vec; | |
169 | return 0; | |
170 | } | |
171 | ||
172 | static int iscrlf(const char *ptr) { | |
173 | return ptr[0] == '\r' && ptr[1] == '\n'; | |
174 | } | |
175 | ||
176 | const char *mime_parse(const char *s, | |
177 | int (*callback)(const char *name, const char *value, | |
178 | void *u), | |
179 | void *u) { | |
180 | struct dynstr name, value; | |
181 | char *cte = 0, *p; | |
182 | ||
183 | while(*s && !iscrlf(s)) { | |
184 | dynstr_init(&name); | |
185 | dynstr_init(&value); | |
186 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
187 | dynstr_append(&name, tolower((unsigned char)*s++)); | |
188 | if(!(s = skipwhite(s))) return 0; | |
189 | if(*s != ':') return 0; | |
190 | ++s; | |
191 | while(*s && !(*s == '\n' && !(s[1] == ' ' || s[1] == '\t'))) | |
192 | dynstr_append(&value, *s++); | |
193 | if(*s) ++s; | |
194 | dynstr_terminate(&name); | |
195 | dynstr_terminate(&value); | |
196 | if(!strcmp(name.vec, "content-transfer-encoding")) { | |
197 | cte = xstrdup(value.vec); | |
198 | for(p = cte; *p; p++) | |
199 | *p = tolower((unsigned char)*p); | |
200 | } | |
201 | if(callback(name.vec, value.vec, u)) return 0; | |
202 | } | |
203 | if(*s) s += 2; | |
204 | if(cte) { | |
205 | if(!strcmp(cte, "base64")) return mime_base64(s); | |
206 | if(!strcmp(cte, "quoted-printable")) return mime_qp(s); | |
207 | } | |
208 | return s; | |
209 | } | |
210 | ||
211 | static int isboundary(const char *ptr, const char *boundary, size_t bl) { | |
212 | return (ptr[0] == '-' | |
213 | && ptr[1] == '-' | |
214 | && !strncmp(ptr + 2, boundary, bl) | |
215 | && (iscrlf(ptr + bl + 2) | |
216 | || (ptr[bl + 2] == '-' | |
217 | && ptr[bl + 3] == '-' | |
22896b25 | 218 | && (iscrlf(ptr + bl + 4) || *(ptr + bl + 4) == 0)))); |
460b9539 | 219 | } |
220 | ||
221 | static int isfinal(const char *ptr, const char *boundary, size_t bl) { | |
222 | return (ptr[0] == '-' | |
223 | && ptr[1] == '-' | |
224 | && !strncmp(ptr + 2, boundary, bl) | |
225 | && ptr[bl + 2] == '-' | |
226 | && ptr[bl + 3] == '-' | |
22896b25 | 227 | && (iscrlf(ptr + bl + 4) || *(ptr + bl + 4) == 0)); |
460b9539 | 228 | } |
229 | ||
230 | int mime_multipart(const char *s, | |
231 | int (*callback)(const char *s, void *u), | |
232 | const char *boundary, | |
233 | void *u) { | |
234 | size_t bl = strlen(boundary); | |
235 | const char *start, *e; | |
236 | int ret; | |
237 | ||
22896b25 RK |
238 | /* We must start with a boundary string */ |
239 | if(!isboundary(s, boundary, bl)) | |
240 | return -1; | |
241 | /* Keep going until we hit a final boundary */ | |
460b9539 | 242 | while(!isfinal(s, boundary, bl)) { |
243 | s = strstr(s, "\r\n") + 2; | |
244 | start = s; | |
245 | while(!isboundary(s, boundary, bl)) { | |
22896b25 RK |
246 | if(!(e = strstr(s, "\r\n"))) |
247 | return -1; | |
460b9539 | 248 | s = e + 2; |
249 | } | |
250 | if((ret = callback(xstrndup(start, | |
251 | s == start ? 0 : s - start - 2), | |
252 | u))) | |
253 | return ret; | |
254 | } | |
255 | return 0; | |
256 | } | |
257 | ||
258 | int mime_rfc2388_content_disposition(const char *s, | |
259 | char **dispositionp, | |
260 | char **parameternamep, | |
261 | char **parametervaluep) { | |
262 | struct dynstr disposition, parametername, parametervalue; | |
263 | ||
264 | dynstr_init(&disposition); | |
265 | if(!(s = skipwhite(s))) return -1; | |
266 | if(!*s) return -1; | |
267 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
268 | dynstr_append(&disposition, tolower((unsigned char)*s++)); | |
269 | if(!(s = skipwhite(s))) return -1; | |
270 | ||
271 | if(*s == ';') { | |
272 | dynstr_init(¶metername); | |
273 | ++s; | |
274 | if(!(s = skipwhite(s))) return -1; | |
275 | if(!*s) return -1; | |
276 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
277 | dynstr_append(¶metername, tolower((unsigned char)*s++)); | |
278 | if(!(s = skipwhite(s))) return -1; | |
279 | if(*s++ != '=') return -1; | |
280 | if(!(s = skipwhite(s))) return -1; | |
281 | if(*s == '"') { | |
282 | if(!(s = parsestring(s, parametervaluep))) return -1; | |
283 | } else { | |
284 | dynstr_init(¶metervalue); | |
285 | while(*s && !tspecial(*s) && !whitespace(*s)) | |
286 | dynstr_append(¶metervalue, *s++); | |
287 | dynstr_terminate(¶metervalue); | |
288 | *parametervaluep = parametervalue.vec; | |
289 | } | |
290 | if(!(s = skipwhite(s))) return -1; | |
291 | dynstr_terminate(¶metername); | |
292 | *parameternamep = parametername.vec; | |
293 | } else | |
294 | *parametervaluep = *parameternamep = 0; | |
295 | dynstr_terminate(&disposition); | |
296 | *dispositionp = disposition.vec; | |
297 | return 0; | |
298 | } | |
299 | ||
300 | char *mime_qp(const char *s) { | |
301 | struct dynstr d; | |
302 | int c, a, b; | |
303 | const char *t; | |
304 | ||
305 | dynstr_init(&d); | |
306 | while((c = *s++)) { | |
307 | switch(c) { | |
308 | case '=': | |
309 | if((a = unhexdigitq(s[0])) != -1 | |
310 | && (b = unhexdigitq(s[1])) != -1) { | |
311 | dynstr_append(&d, a * 16 + b); | |
312 | s += 2; | |
313 | } else { | |
314 | t = s; | |
315 | while(*t == ' ' || *t == '\t') ++t; | |
316 | if(iscrlf(t)) { | |
317 | /* soft line break */ | |
318 | s = t + 2; | |
319 | } else | |
320 | return 0; | |
321 | } | |
322 | break; | |
323 | case ' ': | |
324 | case '\t': | |
325 | t = s; | |
326 | while(*t == ' ' || *t == '\t') ++t; | |
327 | if(iscrlf(t)) | |
328 | /* trailing space is always eliminated */ | |
329 | s = t; | |
330 | else | |
331 | dynstr_append(&d, c); | |
332 | break; | |
333 | default: | |
334 | dynstr_append(&d, c); | |
335 | break; | |
336 | } | |
337 | } | |
338 | dynstr_terminate(&d); | |
339 | return d.vec; | |
340 | } | |
341 | ||
342 | char *mime_base64(const char *s) { | |
343 | struct dynstr d; | |
344 | const char *t; | |
345 | int b[4], n, c; | |
346 | static const char table[] = | |
347 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
348 | ||
349 | dynstr_init(&d); | |
350 | n = 0; | |
351 | while((c = (unsigned char)*s++)) { | |
352 | if((t = strchr(table, c))) { | |
353 | b[n++] = t - table; | |
354 | if(n == 4) { | |
355 | dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); | |
356 | dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); | |
357 | dynstr_append(&d, (b[2] << 6) + b[3]); | |
358 | n = 0; | |
359 | } | |
360 | } else if(c == '=') { | |
361 | if(n >= 2) { | |
362 | dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); | |
363 | if(n == 3) | |
364 | dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); | |
365 | } | |
366 | break; | |
367 | } | |
368 | } | |
369 | dynstr_terminate(&d); | |
370 | return d.vec; | |
371 | } | |
372 | ||
373 | /* | |
374 | Local Variables: | |
375 | c-basic-offset:2 | |
376 | comment-column:40 | |
377 | fill-column:79 | |
378 | End: | |
379 | */ |