summaryrefslogtreecommitdiffabout
path: root/libical/src/libical/sspm.c
Unidiff
Diffstat (limited to 'libical/src/libical/sspm.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libical/src/libical/sspm.c1621
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
50int 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
59enum 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
70struct 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
82void sspm_free_header(struct sspm_header *header);
83void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header);
84void sspm_read_header(struct mime_impl *impl,struct sspm_header *header);
85
86char* sspm_strdup(char* str){
87
88 char* s;
89
90 s = strdup(str);
91
92 return s;
93}
94
95
96struct 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
115struct 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
136struct 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
152char* 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
200char* 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
214char* 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
244char *mime_headers[] = {
245 "Content-Type",
246 "Content-Transfer-Encoding",
247 "Content-Disposition",
248 "Content-Id",
249 "Mime-Version",
250 0
251};
252
253
254void* sspm_default_new_part()
255{
256 return 0;
257}
258void sspm_default_add_line(void *part, struct sspm_header *header,
259 char* line, size_t size)
260{
261}
262
263void* sspm_default_end_part(void* part)
264{
265 return 0;
266}
267
268void sspm_default_free_part(void *part)
269{
270}
271
272
273
274struct 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
279int 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
296int 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
308int 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
327int sspm_is_continuation_line(char* line)
328{
329 if (line[0] == ' '|| line[0] == '\t' ) {
330 return 1;
331 }
332
333 return 0;
334}
335
336int sspm_is_mime_boundary(char *line)
337{
338 if( line[0] == '-' && line[1] == '-') {
339 return 1;
340 }
341
342 return 0;
343}
344
345int 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
357enum 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
369enum 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
393struct 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
427char* 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
443enum 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
460enum 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
485char* 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
500char* 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
514char* 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. */
529void 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
597char* 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
609void 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
620void 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
639void* 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
772void* 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
888void* 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
920void 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*/
1018int 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
1073void 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
1083void 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/***********************************************************************
1106The remaining code is beased on code from the mimelite distribution,
1107which 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
1117The code is heavily modified by Eric Busboom.
1118
1119***********************************************************************/
1120
1121char *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
1170char *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
1241struct sspm_buffer {
1242 char* buffer;
1243 char* pos;
1244 size_t buf_size;
1245 int line_pos;
1246};
1247
1248void sspm_append_string(struct sspm_buffer* buf, char* string);
1249void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part, int *part_num);
1250
1251void 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 */
1261void 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 */
1289void 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
1320static int sspm_is_printable(char c)
1321{
1322 return (c >= 33) && (c <= 126) && (c != '=');
1323
1324}
1325
1326
1327void 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
1375static 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
1382void 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
1417void 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
1466void 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
1516void 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
1556void 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
1580int 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