-rw-r--r-- | libical/src/libical/sspm.c | 1621 |
1 files changed, 1621 insertions, 0 deletions
diff --git a/libical/src/libical/sspm.c b/libical/src/libical/sspm.c new file mode 100644 index 0000000..2df581b --- a/dev/null +++ b/libical/src/libical/sspm.c | |||
@@ -0,0 +1,1621 @@ | |||
1 | /* -*- Mode: C -*- | ||
2 | ====================================================================== | ||
3 | FILE: sspm.c Parse Mime | ||
4 | CREATOR: eric 25 June 2000 | ||
5 | |||
6 | $Id$ | ||
7 | $Locker$ | ||
8 | |||
9 | The contents of this file are subject to the Mozilla Public License | ||
10 | Version 1.0 (the "License"); you may not use this file except in | ||
11 | compliance with the License. You may obtain a copy of the License at | ||
12 | http://www.mozilla.org/MPL/ | ||
13 | |||
14 | Software distributed under the License is distributed on an "AS IS" | ||
15 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | ||
16 | the License for the specific language governing rights and | ||
17 | limitations under the License. | ||
18 | |||
19 | |||
20 | This program is free software; you can redistribute it and/or modify | ||
21 | it under the terms of either: | ||
22 | |||
23 | The LGPL as published by the Free Software Foundation, version | ||
24 | 2.1, available at: http://www.fsf.org/copyleft/lesser.html | ||
25 | |||
26 | Or: | ||
27 | |||
28 | The Mozilla Public License Version 1.0. You may obtain a copy of | ||
29 | the License at http://www.mozilla.org/MPL/ | ||
30 | |||
31 | The Initial Developer of the Original Code is Eric Busboom | ||
32 | |||
33 | (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org | ||
34 | ======================================================================*/ | ||
35 | |||
36 | #include <stdio.h> | ||
37 | #include <string.h> | ||
38 | #include "sspm.h" | ||
39 | #include <assert.h> | ||
40 | #include <ctype.h> /* for tolower */ | ||
41 | #include <stdlib.h> /* for malloc, free */ | ||
42 | #include <string.h> /* for strcasecmp */ | ||
43 | |||
44 | // Eugen C. <eug@thekompany.com> | ||
45 | #ifdef _WIN32 | ||
46 | #define strcasecmp _stricmp | ||
47 | #endif | ||
48 | // Eugen C. <eug@thekompany.com> | ||
49 | |||
50 | int snprintf(char *str, size_t n, char const *fmt, ...); | ||
51 | |||
52 | #ifdef DMALLOC | ||
53 | #include "dmalloc.h" | ||
54 | #endif | ||
55 | |||
56 | #define TMP_BUF_SIZE 1024 | ||
57 | |||
58 | |||
59 | enum mime_state { | ||
60 | UNKNOWN_STATE, | ||
61 | IN_HEADER, | ||
62 | END_OF_HEADER, | ||
63 | IN_BODY, | ||
64 | OPENING_PART, | ||
65 | END_OF_PART, | ||
66 | TERMINAL_END_OF_PART, | ||
67 | END_OF_INPUT | ||
68 | }; | ||
69 | |||
70 | struct mime_impl{ | ||
71 | struct sspm_part *parts; | ||
72 | size_t max_parts; | ||
73 | int part_no; | ||
74 | int level; | ||
75 | struct sspm_action_map *actions; | ||
76 | char* (*get_string)(char *s, size_t size, void* data); | ||
77 | void* get_string_data; | ||
78 | char temp[TMP_BUF_SIZE]; | ||
79 | enum mime_state state; | ||
80 | }; | ||
81 | |||
82 | void sspm_free_header(struct sspm_header *header); | ||
83 | void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header); | ||
84 | void sspm_read_header(struct mime_impl *impl,struct sspm_header *header); | ||
85 | |||
86 | char* sspm_strdup(char* str){ | ||
87 | |||
88 | char* s; | ||
89 | |||
90 | s = strdup(str); | ||
91 | |||
92 | return s; | ||
93 | } | ||
94 | |||
95 | |||
96 | struct major_content_type_map | ||
97 | { | ||
98 | enum sspm_major_type type; | ||
99 | char* str; | ||
100 | |||
101 | } major_content_type_map[] = | ||
102 | { | ||
103 | {SSPM_MULTIPART_MAJOR_TYPE,"multipart" }, | ||
104 | {SSPM_TEXT_MAJOR_TYPE,"text" }, | ||
105 | {SSPM_TEXT_MAJOR_TYPE,"text" }, | ||
106 | {SSPM_IMAGE_MAJOR_TYPE,"image" }, | ||
107 | {SSPM_AUDIO_MAJOR_TYPE,"audio" }, | ||
108 | {SSPM_VIDEO_MAJOR_TYPE,"video" }, | ||
109 | {SSPM_APPLICATION_MAJOR_TYPE,"application" }, | ||
110 | {SSPM_MULTIPART_MAJOR_TYPE,"multipart" }, | ||
111 | {SSPM_MESSAGE_MAJOR_TYPE,"message" }, | ||
112 | {SSPM_UNKNOWN_MAJOR_TYPE,"" }, | ||
113 | }; | ||
114 | |||
115 | struct minor_content_type_map | ||
116 | { | ||
117 | enum sspm_minor_type type; | ||
118 | char* str; | ||
119 | |||
120 | } minor_content_type_map[] = | ||
121 | { | ||
122 | {SSPM_ANY_MINOR_TYPE,"*" }, | ||
123 | {SSPM_PLAIN_MINOR_TYPE,"plain" }, | ||
124 | {SSPM_RFC822_MINOR_TYPE,"rfc822" }, | ||
125 | {SSPM_DIGEST_MINOR_TYPE,"digest" }, | ||
126 | {SSPM_CALENDAR_MINOR_TYPE,"calendar" }, | ||
127 | {SSPM_MIXED_MINOR_TYPE,"mixed" }, | ||
128 | {SSPM_RELATED_MINOR_TYPE,"related" }, | ||
129 | {SSPM_ALTERNATIVE_MINOR_TYPE,"alternative" }, | ||
130 | {SSPM_PARALLEL_MINOR_TYPE, "parallel" }, | ||
131 | {SSPM_UNKNOWN_MINOR_TYPE,"" } | ||
132 | }; | ||
133 | |||
134 | |||
135 | |||
136 | struct encoding_map { | ||
137 | enum sspm_encoding encoding; | ||
138 | char* str; | ||
139 | } sspm_encoding_map[] = | ||
140 | { | ||
141 | {SSPM_NO_ENCODING,""}, | ||
142 | {SSPM_QUOTED_PRINTABLE_ENCODING,"quoted-printable"}, | ||
143 | {SSPM_8BIT_ENCODING,"8bit"}, | ||
144 | {SSPM_7BIT_ENCODING,"7bit"}, | ||
145 | {SSPM_BINARY_ENCODING,"binary"}, | ||
146 | {SSPM_BASE64_ENCODING,"base64"}, | ||
147 | {SSPM_UNKNOWN_ENCODING,""} | ||
148 | |||
149 | }; | ||
150 | |||
151 | |||
152 | char* sspm_get_parameter(char* line, char* parameter) | ||
153 | { | ||
154 | char *p,*s,*q; | ||
155 | static char name[1024]; | ||
156 | |||
157 | /* Find where the parameter name is in the line */ | ||
158 | p = strstr(line,parameter); | ||
159 | |||
160 | if( p == 0){ | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | /* skip over the parameter name, the '=' and any blank spaces */ | ||
165 | |||
166 | p+=strlen(parameter); | ||
167 | |||
168 | while(*p==' ' || *p == '='){ | ||
169 | p++; | ||
170 | } | ||
171 | |||
172 | /*now find the next semicolon*/ | ||
173 | |||
174 | s = strchr(p,';'); | ||
175 | |||
176 | /* Strip of leading quote */ | ||
177 | q = strchr(p,'\"'); | ||
178 | |||
179 | if(q !=0){ | ||
180 | p = q+1; | ||
181 | } | ||
182 | |||
183 | if(s != 0){ | ||
184 | strncpy(name,p,(size_t)s-(size_t)p); | ||
185 | } else { | ||
186 | strcpy(name,p); | ||
187 | } | ||
188 | |||
189 | /* Strip off trailing quote, if it exists */ | ||
190 | |||
191 | q = strrchr(name,'\"'); | ||
192 | |||
193 | if (q != 0){ | ||
194 | *q='\0'; | ||
195 | } | ||
196 | |||
197 | return name; | ||
198 | } | ||
199 | |||
200 | char* sspm_property_name(char* line) | ||
201 | { | ||
202 | static char name[1024]; | ||
203 | char *c = strchr(line,':'); | ||
204 | |||
205 | if(c != 0){ | ||
206 | strncpy(name,line,(size_t)c-(size_t)line); | ||
207 | name[(size_t)c-(size_t)line] = '\0'; | ||
208 | return name; | ||
209 | } else { | ||
210 | return 0; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | char* sspm_value(char* line) | ||
215 | { | ||
216 | static char value[1024]; | ||
217 | |||
218 | char *c,*s, *p; | ||
219 | |||
220 | /* Find the first colon and the next semicolon */ | ||
221 | |||
222 | c = strchr(line,':'); | ||
223 | s = strchr(c,';'); | ||
224 | |||
225 | /* Skip the colon */ | ||
226 | c++; | ||
227 | |||
228 | if (s == 0){ | ||
229 | s = c+strlen(line); | ||
230 | } | ||
231 | |||
232 | for(p=value; c != s; c++){ | ||
233 | if(*c!=' ' && *c!='\n'){ | ||
234 | *(p++) = *c; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | *p='\0'; | ||
239 | |||
240 | return value; | ||
241 | |||
242 | } | ||
243 | |||
244 | char *mime_headers[] = { | ||
245 | "Content-Type", | ||
246 | "Content-Transfer-Encoding", | ||
247 | "Content-Disposition", | ||
248 | "Content-Id", | ||
249 | "Mime-Version", | ||
250 | 0 | ||
251 | }; | ||
252 | |||
253 | |||
254 | void* sspm_default_new_part() | ||
255 | { | ||
256 | return 0; | ||
257 | } | ||
258 | void sspm_default_add_line(void *part, struct sspm_header *header, | ||
259 | char* line, size_t size) | ||
260 | { | ||
261 | } | ||
262 | |||
263 | void* sspm_default_end_part(void* part) | ||
264 | { | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | void sspm_default_free_part(void *part) | ||
269 | { | ||
270 | } | ||
271 | |||
272 | |||
273 | |||
274 | struct sspm_action_map sspm_action_map[] = | ||
275 | { | ||
276 | {SSPM_UNKNOWN_MAJOR_TYPE,SSPM_UNKNOWN_MINOR_TYPE,sspm_default_new_part,sspm_default_add_line,sspm_default_end_part,sspm_default_free_part}, | ||
277 | }; | ||
278 | |||
279 | int sspm_is_mime_header(char *line) | ||
280 | { | ||
281 | char *name = sspm_property_name(line); | ||
282 | int i; | ||
283 | |||
284 | if(name == 0){ | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | for(i = 0; mime_headers[i] != 0; i++){ | ||
289 | if(strcasecmp(name, mime_headers[i]) == 0) | ||
290 | return 1; | ||
291 | } | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | int sspm_is_mail_header(char* line) | ||
297 | { | ||
298 | char *name = sspm_property_name(line); | ||
299 | |||
300 | if (name != 0){ | ||
301 | return 1; | ||
302 | } | ||
303 | |||
304 | return 0; | ||
305 | |||
306 | } | ||
307 | |||
308 | int sspm_is_blank(char* line) | ||
309 | { | ||
310 | char *p; | ||
311 | char c =0; | ||
312 | |||
313 | for(p=line; *p!=0; p++){ | ||
314 | if( ! (*p == ' '|| *p == '\t' || *p=='\n') ){ | ||
315 | c++; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | if (c==0){ | ||
320 | return 1; | ||
321 | } | ||
322 | |||
323 | return 0; | ||
324 | |||
325 | } | ||
326 | |||
327 | int sspm_is_continuation_line(char* line) | ||
328 | { | ||
329 | if (line[0] == ' '|| line[0] == '\t' ) { | ||
330 | return 1; | ||
331 | } | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | int sspm_is_mime_boundary(char *line) | ||
337 | { | ||
338 | if( line[0] == '-' && line[1] == '-') { | ||
339 | return 1; | ||
340 | } | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | int sspm_is_mime_terminating_boundary(char *line) | ||
346 | { | ||
347 | |||
348 | |||
349 | if (sspm_is_mime_boundary(line) && | ||
350 | strstr(line,"--\n")){ | ||
351 | return 1; | ||
352 | } | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | enum line_type { | ||
358 | EMPTY, | ||
359 | BLANK, | ||
360 | MIME_HEADER, | ||
361 | MAIL_HEADER, | ||
362 | HEADER_CONTINUATION, | ||
363 | BOUNDARY, | ||
364 | TERMINATING_BOUNDARY, | ||
365 | UNKNOWN_TYPE | ||
366 | }; | ||
367 | |||
368 | |||
369 | enum line_type get_line_type(char* line){ | ||
370 | |||
371 | if (line == 0){ | ||
372 | return EMPTY; | ||
373 | } else if(sspm_is_blank(line)){ | ||
374 | return BLANK; | ||
375 | } else if (sspm_is_mime_header(line)){ | ||
376 | return MIME_HEADER; | ||
377 | } else if (sspm_is_mail_header(line)){ | ||
378 | return MAIL_HEADER; | ||
379 | } else if (sspm_is_continuation_line(line)){ | ||
380 | return HEADER_CONTINUATION; | ||
381 | } else if (sspm_is_mime_terminating_boundary(line)){ | ||
382 | return TERMINATING_BOUNDARY; | ||
383 | } else if (sspm_is_mime_boundary(line)) { | ||
384 | return BOUNDARY; | ||
385 | } else { | ||
386 | return UNKNOWN_TYPE; | ||
387 | } | ||
388 | |||
389 | |||
390 | } | ||
391 | |||
392 | |||
393 | struct sspm_action_map get_action(struct mime_impl *impl, | ||
394 | enum sspm_major_type major, | ||
395 | enum sspm_minor_type minor) | ||
396 | { | ||
397 | int i; | ||
398 | |||
399 | /* Read caller suppled action map */ | ||
400 | |||
401 | if (impl->actions != 0){ | ||
402 | for(i=0; impl->actions[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){ | ||
403 | if((major == impl->actions[i].major && | ||
404 | minor == impl->actions[i].minor) || | ||
405 | (major == impl->actions[i].major && | ||
406 | minor == SSPM_ANY_MINOR_TYPE)){ | ||
407 | return impl->actions[i]; | ||
408 | } | ||
409 | } | ||
410 | } | ||
411 | |||
412 | /* Else, read default action map */ | ||
413 | |||
414 | for(i=0; sspm_action_map[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){ | ||
415 | if((major == sspm_action_map[i].major && | ||
416 | minor == sspm_action_map[i].minor) || | ||
417 | (major == sspm_action_map[i].major && | ||
418 | minor == SSPM_ANY_MINOR_TYPE)){ | ||
419 | break; | ||
420 | } | ||
421 | } | ||
422 | |||
423 | return sspm_action_map[i]; | ||
424 | } | ||
425 | |||
426 | |||
427 | char* sspm_lowercase(char* str) | ||
428 | { | ||
429 | char* p = 0; | ||
430 | char* new = sspm_strdup(str); | ||
431 | |||
432 | if(str ==0){ | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | for(p = new; *p!=0; p++){ | ||
437 | *p = tolower(*p); | ||
438 | } | ||
439 | |||
440 | return new; | ||
441 | } | ||
442 | |||
443 | enum sspm_major_type sspm_find_major_content_type(char* type) | ||
444 | { | ||
445 | int i; | ||
446 | |||
447 | char* ltype = sspm_lowercase(type); | ||
448 | |||
449 | for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; i++){ | ||
450 | if(strncmp(ltype, major_content_type_map[i].str, | ||
451 | strlen(major_content_type_map[i].str))==0){ | ||
452 | free(ltype); | ||
453 | return major_content_type_map[i].type; | ||
454 | } | ||
455 | } | ||
456 | free(ltype); | ||
457 | return major_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ | ||
458 | } | ||
459 | |||
460 | enum sspm_minor_type sspm_find_minor_content_type(char* type) | ||
461 | { | ||
462 | int i; | ||
463 | char* ltype = sspm_lowercase(type); | ||
464 | |||
465 | char *p = strchr(ltype,'/'); | ||
466 | |||
467 | if (p==0){ | ||
468 | return SSPM_UNKNOWN_MINOR_TYPE; | ||
469 | } | ||
470 | |||
471 | p++; /* Skip the '/' */ | ||
472 | |||
473 | for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; i++){ | ||
474 | if(strncmp(p, minor_content_type_map[i].str, | ||
475 | strlen(minor_content_type_map[i].str))==0){ | ||
476 | free(ltype); | ||
477 | return minor_content_type_map[i].type; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | free(ltype); | ||
482 | return minor_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ | ||
483 | } | ||
484 | |||
485 | char* sspm_major_type_string(enum sspm_major_type type) | ||
486 | { | ||
487 | int i; | ||
488 | |||
489 | for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; | ||
490 | i++){ | ||
491 | |||
492 | if(type == major_content_type_map[i].type){ | ||
493 | return major_content_type_map[i].str; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | return major_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ | ||
498 | } | ||
499 | |||
500 | char* sspm_minor_type_string(enum sspm_minor_type type) | ||
501 | { | ||
502 | int i; | ||
503 | for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; | ||
504 | i++){ | ||
505 | if(type == minor_content_type_map[i].type){ | ||
506 | return minor_content_type_map[i].str; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | return minor_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ | ||
511 | } | ||
512 | |||
513 | |||
514 | char* sspm_encoding_string(enum sspm_encoding type) | ||
515 | { | ||
516 | int i; | ||
517 | for (i=0; sspm_encoding_map[i].encoding != SSPM_UNKNOWN_ENCODING; | ||
518 | i++){ | ||
519 | if(type == sspm_encoding_map[i].encoding){ | ||
520 | return sspm_encoding_map[i].str; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | return sspm_encoding_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ | ||
525 | } | ||
526 | |||
527 | /* Interpret a header line and add its data to the header | ||
528 | structure. */ | ||
529 | void sspm_build_header(struct sspm_header *header, char* line) | ||
530 | { | ||
531 | char *prop; | ||
532 | char *val; | ||
533 | |||
534 | val = sspm_strdup(sspm_value(line)); | ||
535 | prop = sspm_strdup(sspm_property_name(line)); | ||
536 | |||
537 | if(strcmp(prop,"Content-Type") == 0){ | ||
538 | |||
539 | /* Create a new mime_header, fill in content-type | ||
540 | and possibly boundary */ | ||
541 | |||
542 | char* boundary= sspm_get_parameter(line,"boundary"); | ||
543 | |||
544 | header->def = 0; | ||
545 | header->major = sspm_find_major_content_type(val); | ||
546 | header->minor = sspm_find_minor_content_type(val); | ||
547 | |||
548 | if(header->minor == SSPM_UNKNOWN_MINOR_TYPE){ | ||
549 | char *p = strchr(val,'/'); | ||
550 | |||
551 | if (p != 0){ | ||
552 | p++; /* Skip the '/' */ | ||
553 | |||
554 | header->minor_text = sspm_strdup(p); | ||
555 | } else { | ||
556 | /* Error, malformed content type */ | ||
557 | header->minor_text = sspm_strdup("unknown"); | ||
558 | } | ||
559 | } | ||
560 | if (boundary != 0){ | ||
561 | header->boundary = sspm_strdup(boundary); | ||
562 | } | ||
563 | |||
564 | } else if(strcmp(prop,"Content-Transfer-Encoding")==0){ | ||
565 | char* encoding = sspm_value(line); | ||
566 | char* lencoding = sspm_lowercase(encoding); | ||
567 | |||
568 | if(strcmp(lencoding,"base64")==0){ | ||
569 | header->encoding = SSPM_BASE64_ENCODING; | ||
570 | } else if(strcmp(lencoding,"quoted-printable")==0){ | ||
571 | header->encoding = SSPM_QUOTED_PRINTABLE_ENCODING; | ||
572 | } else if(strcmp(lencoding,"binary")==0){ | ||
573 | header->encoding = SSPM_BINARY_ENCODING; | ||
574 | } else if(strcmp(lencoding,"7bit")==0){ | ||
575 | header->encoding = SSPM_7BIT_ENCODING; | ||
576 | } else if(strcmp(lencoding,"8bit")==0){ | ||
577 | header->encoding = SSPM_8BIT_ENCODING; | ||
578 | } else { | ||
579 | header->encoding = SSPM_UNKNOWN_ENCODING; | ||
580 | } | ||
581 | |||
582 | |||
583 | free(lencoding); | ||
584 | |||
585 | header->def = 0; | ||
586 | |||
587 | } else if(strcmp(prop,"Content-Id")==0){ | ||
588 | char* cid = sspm_value(line); | ||
589 | header->content_id = sspm_strdup(cid); | ||
590 | header->def = 0; | ||
591 | |||
592 | } | ||
593 | free(val); | ||
594 | free(prop); | ||
595 | } | ||
596 | |||
597 | char* sspm_get_next_line(struct mime_impl *impl) | ||
598 | { | ||
599 | char* s; | ||
600 | s = impl->get_string(impl->temp,TMP_BUF_SIZE,impl->get_string_data); | ||
601 | |||
602 | if(s == 0){ | ||
603 | impl->state = END_OF_INPUT; | ||
604 | } | ||
605 | return s; | ||
606 | } | ||
607 | |||
608 | |||
609 | void sspm_store_part(struct mime_impl *impl, struct sspm_header header, | ||
610 | int level, void *part, size_t size) | ||
611 | { | ||
612 | |||
613 | impl->parts[impl->part_no].header = header; | ||
614 | impl->parts[impl->part_no].level = level; | ||
615 | impl->parts[impl->part_no].data = part; | ||
616 | impl->parts[impl->part_no].data_size = size; | ||
617 | impl->part_no++; | ||
618 | } | ||
619 | |||
620 | void sspm_set_error(struct sspm_header* header, enum sspm_error error, | ||
621 | char* message) | ||
622 | { | ||
623 | header->error = error; | ||
624 | |||
625 | if(header->error_text!=0){ | ||
626 | free(header->error_text); | ||
627 | } | ||
628 | |||
629 | header->def = 0; | ||
630 | |||
631 | if(message != 0){ | ||
632 | header->error_text = sspm_strdup(message); | ||
633 | } else { | ||
634 | header->error_text = 0; | ||
635 | } | ||
636 | |||
637 | } | ||
638 | |||
639 | void* sspm_make_part(struct mime_impl *impl, | ||
640 | struct sspm_header *header, | ||
641 | struct sspm_header *parent_header, | ||
642 | void **end_part, | ||
643 | size_t *size) | ||
644 | { | ||
645 | |||
646 | /* For a single part type, read to the boundary, if there is a | ||
647 | boundary. Otherwise, read until the end of input. This routine | ||
648 | assumes that the caller has read the header and has left the input | ||
649 | at the first blank line */ | ||
650 | |||
651 | char *line; | ||
652 | void *part; | ||
653 | int end = 0; | ||
654 | |||
655 | struct sspm_action_map action = get_action( | ||
656 | impl, | ||
657 | header->major, | ||
658 | header->minor); | ||
659 | |||
660 | *size = 0; | ||
661 | part =action.new_part(); | ||
662 | |||
663 | impl->state = IN_BODY; | ||
664 | |||
665 | while(end == 0 && (line = sspm_get_next_line(impl)) != 0){ | ||
666 | |||
667 | if(sspm_is_mime_boundary(line)){ | ||
668 | |||
669 | /* If there is a boundary, then this must be a multipart | ||
670 | part, so there must be a parent_header. */ | ||
671 | if(parent_header == 0){ | ||
672 | char* boundary; | ||
673 | end = 1; | ||
674 | *end_part = 0; | ||
675 | |||
676 | sspm_set_error(header,SSPM_UNEXPECTED_BOUNDARY_ERROR,line); | ||
677 | |||
678 | /* Read until the paired terminating boundary */ | ||
679 | if((boundary = (char*)malloc(strlen(line)+5)) == 0){ | ||
680 | fprintf(stderr,"Out of memory"); | ||
681 | abort(); | ||
682 | } | ||
683 | strcpy(boundary,line); | ||
684 | strcat(boundary,"--"); | ||
685 | while((line = sspm_get_next_line(impl)) != 0){ | ||
686 | /*printf("Error: %s\n",line);*/ | ||
687 | if(strcmp(boundary,line)==0){ | ||
688 | break; | ||
689 | } | ||
690 | } | ||
691 | free(boundary); | ||
692 | |||
693 | break; | ||
694 | } | ||
695 | |||
696 | if(strncmp((line+2),parent_header->boundary, | ||
697 | sizeof(parent_header->boundary)) == 0){ | ||
698 | *end_part = action.end_part(part); | ||
699 | |||
700 | if(sspm_is_mime_boundary(line)){ | ||
701 | impl->state = END_OF_PART; | ||
702 | } else if ( sspm_is_mime_terminating_boundary(line)){ | ||
703 | impl->state = TERMINAL_END_OF_PART; | ||
704 | } | ||
705 | end = 1; | ||
706 | } else { | ||
707 | /* Error, this is not the correct terminating boundary*/ | ||
708 | |||
709 | /* read and discard until we get the right boundary. */ | ||
710 | char* boundary; | ||
711 | char msg[256]; | ||
712 | |||
713 | snprintf(msg,256, | ||
714 | "Expected: %s--. Got: %s", | ||
715 | parent_header->boundary,line); | ||
716 | |||
717 | sspm_set_error(parent_header, | ||
718 | SSPM_WRONG_BOUNDARY_ERROR,msg); | ||
719 | |||
720 | /* Read until the paired terminating boundary */ | ||
721 | if((boundary = (char*)malloc(strlen(line)+5)) == 0){ | ||
722 | fprintf(stderr,"Out of memory"); | ||
723 | abort(); | ||
724 | } | ||
725 | strcpy(boundary,line); | ||
726 | strcat(boundary,"--"); | ||
727 | while((line = sspm_get_next_line(impl)) != 0){ | ||
728 | if(strcmp(boundary,line)==0){ | ||
729 | break; | ||
730 | } | ||
731 | } | ||
732 | free(boundary); | ||
733 | |||
734 | } | ||
735 | } else { | ||
736 | char* data=0; | ||
737 | char* rtrn=0; | ||
738 | *size = strlen(line); | ||
739 | |||
740 | data = (char*)malloc(*size+2); | ||
741 | assert(data != 0); | ||
742 | if (header->encoding == SSPM_BASE64_ENCODING){ | ||
743 | rtrn = decode_base64(data,line,size); | ||
744 | } else if(header->encoding == SSPM_QUOTED_PRINTABLE_ENCODING){ | ||
745 | rtrn = decode_quoted_printable(data,line,size); | ||
746 | } | ||
747 | |||
748 | if(rtrn == 0){ | ||
749 | strcpy(data,line); | ||
750 | } | ||
751 | |||
752 | /* add a end-of-string after the data, just in case binary | ||
753 | data from decode64 gets passed to a tring handling | ||
754 | routine in add_line */ | ||
755 | data[*size+1]='\0'; | ||
756 | |||
757 | action.add_line(part,header,data,*size); | ||
758 | |||
759 | free(data); | ||
760 | } | ||
761 | } | ||
762 | |||
763 | if (end == 0){ | ||
764 | /* End the part if the input is exhausted */ | ||
765 | *end_part = action.end_part(part); | ||
766 | } | ||
767 | |||
768 | return end_part; | ||
769 | } | ||
770 | |||
771 | |||
772 | void* sspm_make_multipart_subpart(struct mime_impl *impl, | ||
773 | struct sspm_header *parent_header) | ||
774 | { | ||
775 | struct sspm_header header; | ||
776 | char *line; | ||
777 | void* part; | ||
778 | size_t size; | ||
779 | |||
780 | if(parent_header->boundary == 0){ | ||
781 | /* Error. Multipart headers must have a boundary*/ | ||
782 | |||
783 | sspm_set_error(parent_header,SSPM_NO_BOUNDARY_ERROR,0); | ||
784 | /* read all of the reamining lines */ | ||
785 | while((line = sspm_get_next_line(impl)) != 0){ | ||
786 | } | ||
787 | |||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | |||
792 | /* Step 1: Read the opening boundary */ | ||
793 | |||
794 | if(get_line_type(impl->temp) != BOUNDARY){ | ||
795 | while((line=sspm_get_next_line(impl)) != 0 ){ | ||
796 | if(sspm_is_mime_boundary(line)){ | ||
797 | |||
798 | assert(parent_header != 0); | ||
799 | |||
800 | /* Check if it is the right boundary */ | ||
801 | if(!sspm_is_mime_terminating_boundary(line) && | ||
802 | strncmp((line+2),parent_header->boundary, | ||
803 | sizeof(parent_header->boundary)) | ||
804 | == 0){ | ||
805 | /* The +2 in strncmp skips over the leading "--" */ | ||
806 | |||
807 | break; | ||
808 | } else { | ||
809 | /* Got the wrong boundary, so read and discard | ||
810 | until we get the right boundary. */ | ||
811 | char* boundary; | ||
812 | char msg[256]; | ||
813 | |||
814 | snprintf(msg,256, | ||
815 | "Expected: %s. Got: %s", | ||
816 | parent_header->boundary,line); | ||
817 | |||
818 | sspm_set_error(parent_header, | ||
819 | SSPM_WRONG_BOUNDARY_ERROR,msg); | ||
820 | |||
821 | /* Read until the paired terminating boundary */ | ||
822 | if((boundary = (char*)malloc(strlen(line)+5)) == 0){ | ||
823 | fprintf(stderr,"Out of memory"); | ||
824 | abort(); | ||
825 | } | ||
826 | strcpy(boundary,line); | ||
827 | strcat(boundary,"--"); | ||
828 | while((line = sspm_get_next_line(impl)) != 0){ | ||
829 | if(strcmp(boundary,line)==0){ | ||
830 | break; | ||
831 | } | ||
832 | } | ||
833 | free(boundary); | ||
834 | |||
835 | return 0; | ||
836 | } | ||
837 | } | ||
838 | } | ||
839 | } | ||
840 | |||
841 | /* Step 2: Get the part header */ | ||
842 | sspm_read_header(impl,&header); | ||
843 | |||
844 | /* If the header is still listed as default, there was probably an | ||
845 | error */ | ||
846 | if(header.def == 1 && header.error != SSPM_NO_ERROR){ | ||
847 | sspm_set_error(&header,SSPM_NO_HEADER_ERROR,0); | ||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | if(header.error!= SSPM_NO_ERROR){ | ||
852 | sspm_store_part(impl,header,impl->level,0,0); | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | /* Step 3: read the body */ | ||
857 | |||
858 | if(header.major == SSPM_MULTIPART_MAJOR_TYPE){ | ||
859 | struct sspm_header *child_header; | ||
860 | child_header = &(impl->parts[impl->part_no].header); | ||
861 | |||
862 | /* Store the multipart part */ | ||
863 | sspm_store_part(impl,header,impl->level,0,0); | ||
864 | |||
865 | /* now get all of the sub-parts */ | ||
866 | part = sspm_make_multipart_part(impl,child_header); | ||
867 | |||
868 | if(get_line_type(impl->temp) != TERMINATING_BOUNDARY){ | ||
869 | |||
870 | sspm_set_error(child_header,SSPM_NO_BOUNDARY_ERROR,impl->temp); | ||
871 | return 0; | ||
872 | } | ||
873 | |||
874 | sspm_get_next_line(impl); /* Step past the terminating boundary */ | ||
875 | |||
876 | } else { | ||
877 | sspm_make_part(impl, &header,parent_header,&part,&size); | ||
878 | |||
879 | memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part)); | ||
880 | |||
881 | sspm_store_part(impl,header,impl->level,part,size); | ||
882 | |||
883 | } | ||
884 | |||
885 | return part; | ||
886 | } | ||
887 | |||
888 | void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header) | ||
889 | { | ||
890 | void *part=0; | ||
891 | |||
892 | /* Now descend a level into each of the children of this part */ | ||
893 | impl->level++; | ||
894 | |||
895 | /* Now we are working on the CHILD */ | ||
896 | memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part)); | ||
897 | |||
898 | do{ | ||
899 | part = sspm_make_multipart_subpart(impl,header); | ||
900 | |||
901 | if (part==0){ | ||
902 | /* Clean up the part in progress */ | ||
903 | impl->parts[impl->part_no].header.major | ||
904 | = SSPM_NO_MAJOR_TYPE; | ||
905 | impl->parts[impl->part_no].header.minor | ||
906 | = SSPM_NO_MINOR_TYPE; | ||
907 | |||
908 | } | ||
909 | |||
910 | |||
911 | } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY && | ||
912 | impl->state != END_OF_INPUT); | ||
913 | |||
914 | impl->level--; | ||
915 | |||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | |||
920 | void sspm_read_header(struct mime_impl *impl,struct sspm_header *header) | ||
921 | { | ||
922 | #define BUF_SIZE 1024 | ||
923 | #define MAX_HEADER_LINES 25 | ||
924 | |||
925 | char *buf; | ||
926 | char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits */ | ||
927 | int current_line = -1; | ||
928 | int end = 0; | ||
929 | |||
930 | memset(header_lines,0,sizeof(header_lines)); | ||
931 | memset(header,0,sizeof(struct sspm_header)); | ||
932 | |||
933 | /* Set up default header */ | ||
934 | header->def = 1; | ||
935 | header->major = SSPM_TEXT_MAJOR_TYPE; | ||
936 | header->minor = SSPM_PLAIN_MINOR_TYPE; | ||
937 | header->error = SSPM_NO_ERROR; | ||
938 | header->error_text = 0; | ||
939 | |||
940 | /* Read all of the lines into memory */ | ||
941 | while(end==0&& (buf=sspm_get_next_line(impl)) != 0){ | ||
942 | |||
943 | enum line_type line_type = get_line_type(buf); | ||
944 | |||
945 | switch(line_type){ | ||
946 | case BLANK: { | ||
947 | end = 1; | ||
948 | impl->state = END_OF_HEADER; | ||
949 | break; | ||
950 | } | ||
951 | |||
952 | case MAIL_HEADER: | ||
953 | case MIME_HEADER: { | ||
954 | impl->state = IN_HEADER; | ||
955 | current_line++; | ||
956 | |||
957 | assert(strlen(buf) < BUF_SIZE); | ||
958 | |||
959 | strcpy(header_lines[current_line],buf); | ||
960 | |||
961 | break; | ||
962 | } | ||
963 | |||
964 | case HEADER_CONTINUATION: { | ||
965 | char* last_line, *end; | ||
966 | char *buf_start; | ||
967 | |||
968 | if(current_line < 0){ | ||
969 | /* This is not really a continuation line, since | ||
970 | we have not see any header line yet */ | ||
971 | sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf); | ||
972 | return; | ||
973 | } | ||
974 | |||
975 | last_line = header_lines[current_line]; | ||
976 | end = (char*) ( (size_t)strlen(last_line)+ | ||
977 | (size_t)last_line); | ||
978 | |||
979 | impl->state = IN_HEADER; | ||
980 | |||
981 | |||
982 | /* skip over the spaces in buf start, and remove the new | ||
983 | line at the end of the lat line */ | ||
984 | if (last_line[strlen(last_line)-1] == '\n'){ | ||
985 | last_line[strlen(last_line)-1] = '\0'; | ||
986 | } | ||
987 | buf_start = buf; | ||
988 | while(*buf_start == ' ' ||*buf_start == '\t' ){ | ||
989 | buf_start++; | ||
990 | } | ||
991 | |||
992 | assert( strlen(buf_start) + strlen(last_line) < BUF_SIZE); | ||
993 | |||
994 | strcat(last_line,buf_start); | ||
995 | |||
996 | break; | ||
997 | } | ||
998 | |||
999 | default: { | ||
1000 | sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf); | ||
1001 | return; | ||
1002 | } | ||
1003 | } | ||
1004 | } | ||
1005 | |||
1006 | |||
1007 | for(current_line = 0; | ||
1008 | current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0; | ||
1009 | current_line++){ | ||
1010 | |||
1011 | sspm_build_header(header,header_lines[current_line]); | ||
1012 | } | ||
1013 | |||
1014 | |||
1015 | } | ||
1016 | |||
1017 | /* Root routine for parsing mime entries*/ | ||
1018 | int sspm_parse_mime(struct sspm_part *parts, | ||
1019 | size_t max_parts, | ||
1020 | struct sspm_action_map *actions, | ||
1021 | char* (*get_string)(char *s, size_t size, void* data), | ||
1022 | void *get_string_data, | ||
1023 | struct sspm_header *first_header | ||
1024 | ) | ||
1025 | { | ||
1026 | struct mime_impl impl; | ||
1027 | struct sspm_header header; | ||
1028 | void *part; | ||
1029 | int i; | ||
1030 | |||
1031 | /* Initialize all of the data */ | ||
1032 | memset(&impl,0,sizeof(struct mime_impl)); | ||
1033 | memset(&header,0,sizeof(struct sspm_header)); | ||
1034 | |||
1035 | for(i = 0; i<(int)max_parts; i++){ | ||
1036 | parts[i].header.major = SSPM_NO_MAJOR_TYPE; | ||
1037 | parts[i].header.minor = SSPM_NO_MINOR_TYPE; | ||
1038 | } | ||
1039 | |||
1040 | impl.parts = parts; | ||
1041 | impl.max_parts = max_parts; | ||
1042 | impl.part_no = 0; | ||
1043 | impl.actions = actions; | ||
1044 | impl.get_string = get_string; | ||
1045 | impl.get_string_data = get_string_data; | ||
1046 | |||
1047 | /* Read the header of the message. This will be the email header, | ||
1048 | unless first_header is specified. But ( HACK) that var is not | ||
1049 | currently being used */ | ||
1050 | sspm_read_header(&impl,&header); | ||
1051 | |||
1052 | if(header.major == SSPM_MULTIPART_MAJOR_TYPE){ | ||
1053 | struct sspm_header *child_header; | ||
1054 | child_header = &(impl.parts[impl.part_no].header); | ||
1055 | |||
1056 | sspm_store_part(&impl,header,impl.level,0,0); | ||
1057 | |||
1058 | part = sspm_make_multipart_part(&impl,child_header); | ||
1059 | |||
1060 | } else { | ||
1061 | void *part; | ||
1062 | size_t size; | ||
1063 | sspm_make_part(&impl, &header, 0,&part,&size); | ||
1064 | |||
1065 | memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part)); | ||
1066 | |||
1067 | sspm_store_part(&impl,header,impl.level,part,size); | ||
1068 | } | ||
1069 | |||
1070 | return 0; | ||
1071 | } | ||
1072 | |||
1073 | void sspm_free_parts(struct sspm_part *parts, size_t max_parts) | ||
1074 | { | ||
1075 | int i; | ||
1076 | |||
1077 | for(i = 0; i<(int)max_parts && parts[i].header.major != SSPM_NO_MAJOR_TYPE; | ||
1078 | i++){ | ||
1079 | sspm_free_header(&(parts[i].header)); | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | void sspm_free_header(struct sspm_header *header) | ||
1084 | { | ||
1085 | if(header->boundary!=0){ | ||
1086 | free(header->boundary); | ||
1087 | } | ||
1088 | if(header->minor_text!=0){ | ||
1089 | free(header->minor_text); | ||
1090 | } | ||
1091 | if(header->charset!=0){ | ||
1092 | free(header->charset); | ||
1093 | } | ||
1094 | if(header->filename!=0){ | ||
1095 | free(header->filename); | ||
1096 | } | ||
1097 | if(header->content_id!=0){ | ||
1098 | free(header->content_id); | ||
1099 | } | ||
1100 | if(header->error_text!=0){ | ||
1101 | free(header->error_text); | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | /*********************************************************************** | ||
1106 | The remaining code is beased on code from the mimelite distribution, | ||
1107 | which has the following notice: | ||
1108 | |||
1109 | | Authorship: | ||
1110 | | Copyright (c) 1994 Gisle Hannemyr. | ||
1111 | | Permission is granted to hack, make and distribute copies of this | ||
1112 | | program as long as this copyright notice is not removed. | ||
1113 | | Flames, bug reports, comments and improvements to: | ||
1114 | | snail: Gisle Hannemyr, Brageveien 3A, 0452 Oslo, Norway | ||
1115 | | email: Inet: gisle@oslonett.no | ||
1116 | |||
1117 | The code is heavily modified by Eric Busboom. | ||
1118 | |||
1119 | ***********************************************************************/ | ||
1120 | |||
1121 | char *decode_quoted_printable(char *dest, | ||
1122 | char *src, | ||
1123 | size_t *size) | ||
1124 | { | ||
1125 | int cc; | ||
1126 | size_t i=0; | ||
1127 | |||
1128 | while (*src != 0 && i < *size) { | ||
1129 | if (*src == '=') { | ||
1130 | |||
1131 | src++; | ||
1132 | if (!*src) { | ||
1133 | break; | ||
1134 | } | ||
1135 | |||
1136 | /* remove soft line breaks*/ | ||
1137 | if ((*src == '\n') || (*src == '\r')){ | ||
1138 | src++; | ||
1139 | if ((*src == '\n') || (*src == '\r')){ | ||
1140 | src++; | ||
1141 | } | ||
1142 | continue; | ||
1143 | } | ||
1144 | |||
1145 | cc = isdigit(*src) ? (*src - '0') : (*src - 55); | ||
1146 | cc *= 0x10; | ||
1147 | src++; | ||
1148 | if (!*src) { | ||
1149 | break; | ||
1150 | } | ||
1151 | cc += isdigit(*src) ? (*src - '0') : (*src - 55); | ||
1152 | |||
1153 | *dest = cc; | ||
1154 | |||
1155 | } else { | ||
1156 | *dest = *src; | ||
1157 | } | ||
1158 | |||
1159 | dest++; | ||
1160 | src++; | ||
1161 | i++; | ||
1162 | } | ||
1163 | |||
1164 | *dest = '\0'; | ||
1165 | |||
1166 | *size = i; | ||
1167 | return(dest); | ||
1168 | } | ||
1169 | |||
1170 | char *decode_base64(char *dest, | ||
1171 | char *src, | ||
1172 | size_t *size) | ||
1173 | { | ||
1174 | int cc; | ||
1175 | char buf[4] = {0,0,0,0}; | ||
1176 | int p = 0; | ||
1177 | int valid_data = 0; | ||
1178 | size_t size_out=0; | ||
1179 | |||
1180 | while (*src && p<(int)*size && (cc!= -1)) { | ||
1181 | |||
1182 | /* convert a character into the Base64 alphabet */ | ||
1183 | cc = *src++; | ||
1184 | |||
1185 | if((cc >= 'A') && (cc <= 'Z')) cc = cc - 'A'; | ||
1186 | else if ((cc >= 'a') && (cc <= 'z')) cc = cc - 'a' + 26; | ||
1187 | else if ((cc >= '0') && (cc <= '9')) cc = cc - '0' + 52; | ||
1188 | else if (cc == '/') cc = 63; | ||
1189 | else if (cc == '+') cc = 62; | ||
1190 | else cc = -1; | ||
1191 | |||
1192 | assert(cc<64); | ||
1193 | |||
1194 | /* If we've reached the end, fill the remaining slots in | ||
1195 | the bucket and do a final conversion */ | ||
1196 | if(cc== -1){ | ||
1197 | if(valid_data == 0){ | ||
1198 | return 0; | ||
1199 | } | ||
1200 | |||
1201 | while(p%4!=3){ | ||
1202 | p++; | ||
1203 | buf[p%4] = 0; | ||
1204 | } | ||
1205 | } else { | ||
1206 | buf[p%4] = cc; | ||
1207 | size_out++; | ||
1208 | valid_data = 1; | ||
1209 | } | ||
1210 | |||
1211 | |||
1212 | /* When we have 4 base64 letters, convert them into three | ||
1213 | bytes */ | ||
1214 | if (p%4 == 3) { | ||
1215 | *dest++ =(buf[0]<< 2)|((buf[1] & 0x30) >> 4); | ||
1216 | *dest++ =((buf[1] & 0x0F) << 4)|((buf[2] & 0x3C) >> 2); | ||
1217 | *dest++ =((buf[2] & 0x03) << 6)|(buf[3] & 0x3F); | ||
1218 | |||
1219 | memset(buf,0,4); | ||
1220 | } | ||
1221 | |||
1222 | p++; | ||
1223 | |||
1224 | } | ||
1225 | /* Calculate the size of the converted data*/ | ||
1226 | *size = ((int)(size_out/4))*3; | ||
1227 | if(size_out%4 == 2) *size+=1; | ||
1228 | if(size_out%4 == 3) *size+=2; | ||
1229 | |||
1230 | return(dest); | ||
1231 | } | ||
1232 | |||
1233 | |||
1234 | /*********************************************************************** | ||
1235 | |||
1236 | Routines to output MIME | ||
1237 | |||
1238 | **********************************************************************/ | ||
1239 | |||
1240 | |||
1241 | struct sspm_buffer { | ||
1242 | char* buffer; | ||
1243 | char* pos; | ||
1244 | size_t buf_size; | ||
1245 | int line_pos; | ||
1246 | }; | ||
1247 | |||
1248 | void sspm_append_string(struct sspm_buffer* buf, char* string); | ||
1249 | void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part, int *part_num); | ||
1250 | |||
1251 | void sspm_append_hex(struct sspm_buffer* buf, char ch) | ||
1252 | { | ||
1253 | char tmp[3]; | ||
1254 | |||
1255 | sprintf(tmp,"=%02X",ch); | ||
1256 | |||
1257 | sspm_append_string(buf,tmp); | ||
1258 | } | ||
1259 | |||
1260 | /* a copy of icalmemory_append_char */ | ||
1261 | void sspm_append_char(struct sspm_buffer* buf, char ch) | ||
1262 | { | ||
1263 | char *new_buf; | ||
1264 | char *new_pos; | ||
1265 | |||
1266 | size_t data_length, final_length; | ||
1267 | |||
1268 | data_length = (size_t)buf->pos - (size_t)buf->buffer; | ||
1269 | |||
1270 | final_length = data_length + 2; | ||
1271 | |||
1272 | if ( final_length > (size_t) buf->buf_size ) { | ||
1273 | |||
1274 | buf->buf_size = (buf->buf_size) * 2 + final_length +1; | ||
1275 | |||
1276 | new_buf = realloc(buf->buffer,buf->buf_size); | ||
1277 | |||
1278 | new_pos = (void*)((size_t)new_buf + data_length); | ||
1279 | |||
1280 | buf->pos = new_pos; | ||
1281 | buf->buffer = new_buf; | ||
1282 | } | ||
1283 | |||
1284 | *(buf->pos) = ch; | ||
1285 | buf->pos += 1; | ||
1286 | *(buf->pos) = 0; | ||
1287 | } | ||
1288 | /* A copy of icalmemory_append_string */ | ||
1289 | void sspm_append_string(struct sspm_buffer* buf, char* string) | ||
1290 | { | ||
1291 | char *new_buf; | ||
1292 | char *new_pos; | ||
1293 | |||
1294 | size_t data_length, final_length, string_length; | ||
1295 | |||
1296 | string_length = strlen(string); | ||
1297 | data_length = (size_t)buf->pos - (size_t)buf->buffer; | ||
1298 | final_length = data_length + string_length; | ||
1299 | |||
1300 | if ( final_length >= (size_t) buf->buf_size) { | ||
1301 | |||
1302 | |||
1303 | buf->buf_size = (buf->buf_size) * 2 + final_length; | ||
1304 | |||
1305 | new_buf = realloc(buf->buffer,buf->buf_size); | ||
1306 | |||
1307 | new_pos = (void*)((size_t)new_buf + data_length); | ||
1308 | |||
1309 | buf->pos = new_pos; | ||
1310 | buf->buffer = new_buf; | ||
1311 | } | ||
1312 | |||
1313 | strcpy(buf->pos, string); | ||
1314 | |||
1315 | buf->pos += string_length; | ||
1316 | } | ||
1317 | |||
1318 | |||
1319 | |||
1320 | static int sspm_is_printable(char c) | ||
1321 | { | ||
1322 | return (c >= 33) && (c <= 126) && (c != '='); | ||
1323 | |||
1324 | } | ||
1325 | |||
1326 | |||
1327 | void sspm_encode_quoted_printable(struct sspm_buffer *buf, char* data) | ||
1328 | { | ||
1329 | char *p; | ||
1330 | int lpos = 0; | ||
1331 | |||
1332 | for(p = data; *p != 0; p++){ | ||
1333 | |||
1334 | if(sspm_is_printable(*p)){ | ||
1335 | /* plain characters can represent themselves */ | ||
1336 | /* RFC2045 Rule #2 */ | ||
1337 | sspm_append_char(buf,*p); | ||
1338 | lpos++; | ||
1339 | } else if ( *p == '\t' || *p == ' ' ) { | ||
1340 | |||
1341 | /* For tabs and spaces, only encode if they appear at the | ||
1342 | end of the line */ | ||
1343 | /* RFC2045 Rule #3 */ | ||
1344 | |||
1345 | char n = *(p+1); | ||
1346 | |||
1347 | if( n == '\n' || n == '\r'){ | ||
1348 | sspm_append_hex(buf,*p); | ||
1349 | lpos += 3; | ||
1350 | } else { | ||
1351 | sspm_append_char(buf,*p); | ||
1352 | lpos++; | ||
1353 | } | ||
1354 | |||
1355 | } else if( *p == '\n' || *p == '\r'){ | ||
1356 | sspm_append_char(buf,*p); | ||
1357 | |||
1358 | lpos=0; | ||
1359 | |||
1360 | } else { | ||
1361 | /* All others need to be encoded */ | ||
1362 | sspm_append_hex(buf,*p); | ||
1363 | lpos+=3; | ||
1364 | } | ||
1365 | |||
1366 | |||
1367 | /* Add line breaks */ | ||
1368 | if (lpos > 72){ | ||
1369 | lpos = 0; | ||
1370 | sspm_append_string(buf,"=\n"); | ||
1371 | } | ||
1372 | } | ||
1373 | } | ||
1374 | |||
1375 | static char BaseTable[64] = { | ||
1376 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', | ||
1377 | 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', | ||
1378 | 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', | ||
1379 | 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' | ||
1380 | }; | ||
1381 | |||
1382 | void sspm_write_base64(struct sspm_buffer *buf, char* inbuf,int size ) | ||
1383 | { | ||
1384 | |||
1385 | char outbuf[4]; | ||
1386 | int i; | ||
1387 | |||
1388 | outbuf[0] = outbuf[1] = outbuf[2] = outbuf[3] = 65; | ||
1389 | |||
1390 | switch(size){ | ||
1391 | |||
1392 | case 4: | ||
1393 | outbuf[3] = inbuf[2] & 0x3F; | ||
1394 | |||
1395 | case 3: | ||
1396 | outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6); | ||
1397 | |||
1398 | case 2: | ||
1399 | outbuf[0] = (inbuf[0] & 0xFC) >> 2; | ||
1400 | outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4); | ||
1401 | break; | ||
1402 | |||
1403 | default: | ||
1404 | assert(0); | ||
1405 | } | ||
1406 | |||
1407 | for(i = 0; i < 4; i++){ | ||
1408 | |||
1409 | if(outbuf[i] == 65){ | ||
1410 | sspm_append_char(buf,'='); | ||
1411 | } else { | ||
1412 | sspm_append_char(buf,BaseTable[(int)outbuf[i]]); | ||
1413 | } | ||
1414 | } | ||
1415 | } | ||
1416 | |||
1417 | void sspm_encode_base64(struct sspm_buffer *buf, char* data, size_t size) | ||
1418 | { | ||
1419 | |||
1420 | char *p; | ||
1421 | char inbuf[3]; | ||
1422 | int i = 0; | ||
1423 | int first = 1; | ||
1424 | int lpos = 0; | ||
1425 | |||
1426 | inbuf[0] = inbuf[1] = inbuf[2] = 0; | ||
1427 | |||
1428 | for (p = data; *p !=0; p++){ | ||
1429 | |||
1430 | if (i%3 == 0 && first == 0){ | ||
1431 | |||
1432 | sspm_write_base64(buf, inbuf, 4); | ||
1433 | lpos+=4; | ||
1434 | |||
1435 | inbuf[0] = inbuf[1] = inbuf[2] = 0; | ||
1436 | } | ||
1437 | |||
1438 | assert(lpos%4 == 0); | ||
1439 | |||
1440 | if (lpos == 72){ | ||
1441 | sspm_append_string(buf,"\n"); | ||
1442 | lpos = 0; | ||
1443 | } | ||
1444 | |||
1445 | inbuf[i%3] = *p; | ||
1446 | |||
1447 | i++; | ||
1448 | first = 0; | ||
1449 | |||
1450 | } | ||
1451 | |||
1452 | |||
1453 | /* If the inbuf was not exactly filled on the last byte, we need | ||
1454 | to spit out the odd bytes that did get in -- either one or | ||
1455 | two. This will result in an output of two bytes and '==' or | ||
1456 | three bytes and '=', respectively */ | ||
1457 | |||
1458 | if (i%3 == 1 && first == 0){ | ||
1459 | sspm_write_base64(buf, inbuf, 2); | ||
1460 | } else if (i%3 == 2 && first == 0){ | ||
1461 | sspm_write_base64(buf, inbuf, 3); | ||
1462 | } | ||
1463 | |||
1464 | } | ||
1465 | |||
1466 | void sspm_write_header(struct sspm_buffer *buf,struct sspm_header *header) | ||
1467 | { | ||
1468 | |||
1469 | int i; | ||
1470 | char temp[TMP_BUF_SIZE]; | ||
1471 | char* major; | ||
1472 | char* minor; | ||
1473 | |||
1474 | /* Content-type */ | ||
1475 | |||
1476 | major = sspm_major_type_string(header->major); | ||
1477 | minor = sspm_minor_type_string(header->minor); | ||
1478 | |||
1479 | if(header->minor == SSPM_UNKNOWN_MINOR_TYPE ){ | ||
1480 | assert(header->minor_text !=0); | ||
1481 | minor = header->minor_text; | ||
1482 | } | ||
1483 | |||
1484 | sprintf(temp,"Content-Type: %s/%s",major,minor); | ||
1485 | |||
1486 | sspm_append_string(buf,temp); | ||
1487 | |||
1488 | if(header->boundary != 0){ | ||
1489 | sprintf(temp,";boundary=\"%s\"",header->boundary); | ||
1490 | sspm_append_string(buf,temp); | ||
1491 | } | ||
1492 | |||
1493 | /* Append any content type parameters */ | ||
1494 | if(header->content_type_params != 0){ | ||
1495 | for(i=0; *(header->content_type_params[i])!= 0;i++){ | ||
1496 | sprintf(temp,header->content_type_params[i]); | ||
1497 | sspm_append_char(buf,';'); | ||
1498 | sspm_append_string(buf,temp); | ||
1499 | } | ||
1500 | } | ||
1501 | |||
1502 | sspm_append_char(buf,'\n'); | ||
1503 | |||
1504 | /*Content-Transfer-Encoding */ | ||
1505 | |||
1506 | if(header->encoding != SSPM_UNKNOWN_ENCODING && | ||
1507 | header->encoding != SSPM_NO_ENCODING){ | ||
1508 | sprintf(temp,"Content-Transfer-Encoding: %s\n", | ||
1509 | sspm_encoding_string(header->encoding)); | ||
1510 | } | ||
1511 | |||
1512 | sspm_append_char(buf,'\n'); | ||
1513 | |||
1514 | } | ||
1515 | |||
1516 | void sspm_write_multipart_part(struct sspm_buffer *buf, | ||
1517 | struct sspm_part *parts, | ||
1518 | int* part_num) | ||
1519 | { | ||
1520 | |||
1521 | int parent_level, level; | ||
1522 | struct sspm_header *header = &(parts[*part_num].header); | ||
1523 | /* Write the header for the multipart part */ | ||
1524 | sspm_write_header(buf,header); | ||
1525 | |||
1526 | parent_level = parts[*part_num].level; | ||
1527 | |||
1528 | (*part_num)++; | ||
1529 | |||
1530 | level = parts[*part_num].level; | ||
1531 | |||
1532 | while(parts[*part_num].header.major != SSPM_NO_MAJOR_TYPE && | ||
1533 | level == parent_level+1){ | ||
1534 | |||
1535 | assert(header->boundary); | ||
1536 | sspm_append_string(buf,header->boundary); | ||
1537 | sspm_append_char(buf,'\n'); | ||
1538 | |||
1539 | if (parts[*part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){ | ||
1540 | sspm_write_multipart_part(buf,parts,part_num); | ||
1541 | } else { | ||
1542 | sspm_write_part(buf, &(parts[*part_num]), part_num); | ||
1543 | } | ||
1544 | |||
1545 | (*part_num)++; | ||
1546 | level = parts[*part_num].level; | ||
1547 | } | ||
1548 | |||
1549 | sspm_append_string(buf,"\n\n--"); | ||
1550 | sspm_append_string(buf,header->boundary); | ||
1551 | sspm_append_string(buf,"\n"); | ||
1552 | |||
1553 | (*part_num)--; /* undo last, spurious, increment */ | ||
1554 | } | ||
1555 | |||
1556 | void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part,int *part_num) | ||
1557 | { | ||
1558 | |||
1559 | /* Write header */ | ||
1560 | sspm_write_header(buf,&(part->header)); | ||
1561 | |||
1562 | /* Write part data */ | ||
1563 | |||
1564 | if(part->data == 0){ | ||
1565 | return; | ||
1566 | } | ||
1567 | |||
1568 | if(part->header.encoding == SSPM_BASE64_ENCODING) { | ||
1569 | assert(part->data_size != 0); | ||
1570 | sspm_encode_base64(buf,part->data,part->data_size); | ||
1571 | } else if(part->header.encoding == SSPM_QUOTED_PRINTABLE_ENCODING) { | ||
1572 | sspm_encode_quoted_printable(buf,part->data); | ||
1573 | } else { | ||
1574 | sspm_append_string(buf,part->data); | ||
1575 | } | ||
1576 | |||
1577 | sspm_append_string(buf,"\n\n"); | ||
1578 | } | ||
1579 | |||
1580 | int sspm_write_mime(struct sspm_part *parts,size_t num_parts, | ||
1581 | char **output_string, char* header) | ||
1582 | { | ||
1583 | struct sspm_buffer buf; | ||
1584 | int part_num =0; | ||
1585 | |||
1586 | buf.buffer = malloc(4096); | ||
1587 | buf.pos = buf.buffer; | ||
1588 | buf.buf_size = 10; | ||
1589 | buf.line_pos = 0; | ||
1590 | |||
1591 | /* write caller's header */ | ||
1592 | if(header != 0){ | ||
1593 | sspm_append_string(&buf,header); | ||
1594 | } | ||
1595 | |||
1596 | if(buf.buffer[strlen(buf.buffer)-1] != '\n'){ | ||
1597 | sspm_append_char(&buf,'\n'); | ||
1598 | } | ||
1599 | |||
1600 | /* write mime-version header */ | ||
1601 | sspm_append_string(&buf,"Mime-Version: 1.0\n"); | ||
1602 | |||
1603 | /* End of header */ | ||
1604 | |||
1605 | /* Write body parts */ | ||
1606 | while(parts[part_num].header.major != SSPM_NO_MAJOR_TYPE){ | ||
1607 | if (parts[part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){ | ||
1608 | sspm_write_multipart_part(&buf,parts,&part_num); | ||
1609 | } else { | ||
1610 | sspm_write_part(&buf, &(parts[part_num]), &part_num); | ||
1611 | } | ||
1612 | |||
1613 | part_num++; | ||
1614 | } | ||
1615 | |||
1616 | |||
1617 | *output_string = buf.buffer; | ||
1618 | |||
1619 | return 0; | ||
1620 | } | ||
1621 | |||