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