7606521940069d75bc250facd4aaa4124018a385
[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_MAJOR_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_MAJOR_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(strcmp((line+2),parent_header->boundary) == 0){
689                 *end_part = action.end_part(part);
690
691                 if(sspm_is_mime_boundary(line)){
692                     impl->state = END_OF_PART;
693                 } else if ( sspm_is_mime_terminating_boundary(line)){
694                     impl->state = TERMINAL_END_OF_PART;
695                 }
696                 end = 1;
697             } else {
698                 /* Error, this is not the correct terminating boundary*/
699
700                 /* read and discard until we get the right boundary.  */
701                     char* boundary;
702                     char msg[256];
703
704                     snprintf(msg,256,
705                              "Expected: %s--. Got: %s",
706                              parent_header->boundary,line);
707
708                     sspm_set_error(parent_header,
709                       SSPM_WRONG_BOUNDARY_ERROR,msg);
710
711                     /* Read until the paired terminating boundary */
712                     if((boundary = (char*)malloc(strlen(line)+5)) == 0){
713                         fprintf(stderr,"Out of memory");
714                         abort();
715                     }            
716                     strcpy(boundary,line);
717                     strcat(boundary,"--");
718                     while((line = sspm_get_next_line(impl)) != 0){
719                         if(strcmp(boundary,line)==0){
720                             break;
721                         }
722                     }
723                     free(boundary);
724
725             }   
726         } else {
727             char* data=0;
728             char* rtrn=0;
729             *size = strlen(line);
730
731             data = (char*)malloc(*size+2);
732             assert(data != 0);
733             if (header->encoding == SSPM_BASE64_ENCODING){
734                 rtrn = decode_base64(data,line,size); 
735             } else if(header->encoding == SSPM_QUOTED_PRINTABLE_ENCODING){
736                 rtrn = decode_quoted_printable(data,line,size); 
737             } 
738
739             if(rtrn == 0){
740                 strcpy(data,line);
741             }
742
743             /* add a end-of-string after the data, just in case binary
744                data from decode64 gets passed to a tring handling
745                routine in add_line  */
746             data[*size+1]='\0';
747
748             action.add_line(part,header,data,*size);
749
750             free(data);
751         }
752     }
753
754     if (end == 0){
755         /* End the part if the input is exhausted */
756         *end_part = action.end_part(part);
757     }
758
759     return end_part;
760 }
761
762
763 void* sspm_make_multipart_subpart(struct mime_impl *impl,
764                             struct sspm_header *parent_header)
765 {
766     struct sspm_header header;
767     char *line;
768     void* part;
769     size_t size;
770
771     if(parent_header->boundary == 0){
772         /* Error. Multipart headers must have a boundary*/
773         
774         sspm_set_error(parent_header,SSPM_NO_BOUNDARY_ERROR,0);
775         /* read all of the reamining lines */
776         while((line = sspm_get_next_line(impl)) != 0){
777         }  
778
779         return 0;
780     }
781
782
783     /* Step 1: Read the opening boundary */
784
785     if(get_line_type(impl->temp) != BOUNDARY){
786         while((line=sspm_get_next_line(impl)) != 0 ){
787             if(sspm_is_mime_boundary(line)){
788
789                 assert(parent_header != 0);
790
791                 /* Check if it is the right boundary */
792                 if(!sspm_is_mime_terminating_boundary(line) &&
793                    strcmp((line+2),parent_header->boundary) == 0){
794                     /* The +2 in strncmp skips over the leading "--" */
795                     
796                     break;
797                 } else {
798                     /* Got the wrong boundary, so read and discard
799                        until we get the right boundary.  */
800                     char* boundary;
801                     char msg[256];
802                     
803                     snprintf(msg,256,
804                              "Expected: %s. Got: %s",
805                              parent_header->boundary,line);
806
807                     sspm_set_error(parent_header,
808                                    SSPM_WRONG_BOUNDARY_ERROR,msg);
809
810                     /* Read until the paired terminating boundary */
811                     if((boundary = (char*)malloc(strlen(line)+5)) == 0){
812                         fprintf(stderr,"Out of memory");
813                         abort();
814                     }
815                     strcpy(boundary,line);
816                     strcat(boundary,"--");
817                     while((line = sspm_get_next_line(impl)) != 0){
818                         if(strcmp(boundary,line)==0){
819                             break;
820                         }
821                     }
822                     free(boundary);
823                     
824                     return 0;
825                 }
826             }
827         }
828     }
829
830     /* Step 2: Get the part header */
831     sspm_read_header(impl,&header);
832
833     /* If the header is still listed as default, there was probably an
834        error */
835     if(header.def == 1 && header.error != SSPM_NO_ERROR){
836         sspm_set_error(&header,SSPM_NO_HEADER_ERROR,0);
837         return 0;
838     }
839
840     if(header.error!= SSPM_NO_ERROR){
841         sspm_store_part(impl,header,impl->level,0,0);
842         return 0;
843     }   
844
845     /* Step 3: read the body */
846     
847     if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
848         struct sspm_header *child_header;
849         child_header = &(impl->parts[impl->part_no].header);
850
851         /* Store the multipart part */
852         sspm_store_part(impl,header,impl->level,0,0);
853
854         /* now get all of the sub-parts */
855         part = sspm_make_multipart_part(impl,child_header);
856
857         if(get_line_type(impl->temp) != TERMINATING_BOUNDARY){
858
859             sspm_set_error(child_header,SSPM_NO_BOUNDARY_ERROR,impl->temp);
860             return 0;
861         }
862         
863         sspm_get_next_line(impl); /* Step past the terminating boundary */
864
865     } else {
866         sspm_make_part(impl, &header,parent_header,&part,&size);
867
868         memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
869
870         sspm_store_part(impl,header,impl->level,part,size);
871
872     }
873
874     return part;
875 }
876
877 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header)
878 {
879     void *part=0;
880
881     /* Now descend a level into each of the children of this part */
882     impl->level++;
883
884     /* Now we are working on the CHILD */
885     memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
886
887     do{
888         part = sspm_make_multipart_subpart(impl,header);
889
890         if (part==0){
891             /* Clean up the part in progress */
892             impl->parts[impl->part_no].header.major 
893                 = SSPM_NO_MAJOR_TYPE;
894             impl->parts[impl->part_no].header.minor 
895                 = SSPM_NO_MINOR_TYPE;
896
897         }
898         
899
900     } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY &&
901         impl->state != END_OF_INPUT);
902
903     impl->level--;
904
905     return 0;
906 }
907
908
909 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header)
910 {
911 #define BUF_SIZE 1024
912 #define MAX_HEADER_LINES 25
913
914     char *buf;
915     char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits */
916     int current_line = -1;
917     int end = 0;
918
919     memset(header_lines,0,sizeof(header_lines));
920     memset(header,0,sizeof(struct sspm_header));
921
922     /* Set up default header */
923     header->def = 1;
924     header->major = SSPM_TEXT_MAJOR_TYPE;
925     header->minor = SSPM_PLAIN_MINOR_TYPE;
926     header->error = SSPM_NO_ERROR;
927     header->error_text = 0;
928
929     /* Read all of the lines into memory */
930     while(end==0&& (buf=sspm_get_next_line(impl)) != 0){
931
932         enum line_type line_type = get_line_type(buf);
933         
934         switch(line_type){
935             case BLANK: {
936                 end = 1;
937                 impl->state = END_OF_HEADER;
938                 break;
939             }
940
941             case MAIL_HEADER:
942             case MIME_HEADER: {     
943                 impl->state = IN_HEADER;
944                 current_line++;
945                 
946                 assert(strlen(buf) < BUF_SIZE);
947                 
948                 strcpy(header_lines[current_line],buf);
949                 
950                 break;
951             }
952             
953             case HEADER_CONTINUATION: {
954                 char* last_line;
955                 char *buf_start;
956
957                 if(current_line < 0){
958                     /* This is not really a continuation line, since
959                        we have not see any header line yet */
960                     sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
961                     return;
962                 }
963
964                 last_line = header_lines[current_line];
965                 
966                 impl->state = IN_HEADER;
967
968                 
969                 /* skip over the spaces in buf start, and remove the new
970                    line at the end of the lat line */
971                 if (last_line[strlen(last_line)-1] == '\n'){
972                     last_line[strlen(last_line)-1] = '\0';
973                 }
974                 buf_start = buf;
975                 while(*buf_start == ' ' ||*buf_start == '\t' ){
976                     buf_start++;
977                 }
978                 
979                 assert( strlen(buf_start) + strlen(last_line) < BUF_SIZE);
980                 
981                 strcat(last_line,buf_start);
982                 
983                 break;
984             }
985             
986             default: {
987                 sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
988                 return;
989             }
990         }
991     }
992         
993
994     for(current_line = 0;
995         current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0;
996         current_line++){
997         
998         sspm_build_header(header,header_lines[current_line]);
999     }
1000
1001
1002 }
1003
1004 /* Root routine for parsing mime entries*/
1005 int sspm_parse_mime(struct sspm_part *parts, 
1006                     size_t max_parts,
1007                     struct sspm_action_map *actions,
1008                     char* (*get_string)(char *s, size_t size, void* data),
1009                     void *get_string_data,
1010                     struct sspm_header *first_header
1011     )
1012 {
1013     struct mime_impl impl;
1014     struct sspm_header header;
1015     void *part;
1016     int i;
1017
1018     /* Initialize all of the data */
1019     memset(&impl,0,sizeof(struct mime_impl));
1020     memset(&header,0,sizeof(struct sspm_header));
1021
1022     for(i = 0; i<(int)max_parts; i++){
1023         parts[i].header.major = SSPM_NO_MAJOR_TYPE;
1024         parts[i].header.minor = SSPM_NO_MINOR_TYPE;
1025     }
1026         
1027     impl.parts = parts;
1028     impl.max_parts = max_parts;
1029     impl.part_no = 0;
1030     impl.actions = actions;
1031     impl.get_string = get_string;
1032     impl.get_string_data = get_string_data;
1033
1034     /* Read the header of the message. This will be the email header,
1035        unless first_header is specified. But ( HACK) that var is not
1036        currently being used */
1037     sspm_read_header(&impl,&header);
1038
1039     if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
1040         struct sspm_header *child_header;
1041         child_header = &(impl.parts[impl.part_no].header);
1042         
1043         sspm_store_part(&impl,header,impl.level,0,0);
1044
1045         part = sspm_make_multipart_part(&impl,child_header);
1046
1047     } else {
1048         void *part;
1049         size_t size;
1050         sspm_make_part(&impl, &header, 0,&part,&size);
1051
1052         memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part));
1053         
1054         sspm_store_part(&impl,header,impl.level,part,size);
1055     }
1056
1057     return 0;
1058 }
1059
1060 void sspm_free_parts(struct sspm_part *parts, size_t max_parts)
1061 {
1062      int i;
1063     
1064     for(i = 0; i<(int)max_parts && parts[i].header.major != SSPM_NO_MAJOR_TYPE;
1065         i++){
1066         sspm_free_header(&(parts[i].header));
1067     }
1068 }
1069
1070 void sspm_free_header(struct sspm_header *header)
1071 {
1072     if(header->boundary!=0){
1073         free(header->boundary);
1074     }
1075     if(header->minor_text!=0){
1076         free(header->minor_text);
1077     }
1078     if(header->charset!=0){
1079         free(header->charset);
1080     }
1081     if(header->filename!=0){
1082         free(header->filename);
1083     }
1084     if(header->content_id!=0){
1085         free(header->content_id);
1086     }
1087     if(header->error_text!=0){
1088         free(header->error_text);
1089     }
1090 }
1091
1092 /***********************************************************************
1093 The remaining code is beased on code from the mimelite distribution,
1094 which has the following notice:
1095
1096 | Authorship:
1097 |    Copyright (c) 1994 Gisle Hannemyr.
1098 |    Permission is granted to hack, make and distribute copies of this
1099 |    program as long as this copyright notice is not removed.
1100 |    Flames, bug reports, comments and improvements to:
1101 |       snail: Gisle Hannemyr, Brageveien 3A, 0452 Oslo, Norway
1102 |       email: Inet: gisle@oslonett.no
1103
1104 The code is heavily modified by Eric Busboom. 
1105
1106 ***********************************************************************/
1107
1108 char *decode_quoted_printable(char *dest, 
1109                                        char *src,
1110                                        size_t *size)
1111 {
1112     int cc;
1113     size_t i=0;
1114
1115     while (*src != 0 && i < *size) {
1116         if (*src == '=') {
1117
1118             src++; 
1119             if (!*src) {
1120                 break;
1121             }
1122
1123             /* remove soft line breaks*/
1124             if ((*src == '\n') || (*src == '\r')){
1125                 src++;
1126                 if ((*src == '\n') || (*src == '\r')){
1127                     src++;
1128                 }
1129                 continue;
1130             }
1131
1132             cc  = isdigit(*src) ? (*src - '0') : (*src - 55);
1133             cc *= 0x10;
1134             src++; 
1135             if (!*src) {
1136                 break;
1137             }
1138             cc += isdigit(*src) ? (*src - '0') : (*src - 55);
1139
1140             *dest = cc;
1141
1142         } else {
1143             *dest = *src;
1144         }
1145         
1146         dest++;
1147         src++;
1148         i++;
1149     }
1150     
1151     *dest = '\0';
1152     
1153     *size = i;
1154     return(dest);
1155 }
1156
1157 char *decode_base64(char *dest, 
1158                              char *src,
1159                              size_t *size)
1160 {
1161     int cc = 0;
1162     char buf[4] = {0,0,0,0};  
1163     int p = 0;
1164     int valid_data = 0;
1165     size_t size_out=0;
1166     
1167     while (*src && p<(int)*size && (cc!=  -1)) {
1168         
1169         /* convert a character into the Base64 alphabet */
1170         cc = *src++;
1171         
1172         if      ((cc >= 'A') && (cc <= 'Z')) cc = cc - 'A';
1173         else if ((cc >= 'a') && (cc <= 'z')) cc = cc - 'a' + 26;
1174         else if ((cc >= '0') && (cc <= '9')) cc = cc - '0' + 52;
1175         else if  (cc == '/')                 cc = 63;
1176         else if  (cc == '+')                 cc = 62;
1177         else                                 cc = -1;
1178         
1179         assert(cc<64);
1180
1181         /* If we've reached the end, fill the remaining slots in
1182            the bucket and do a final conversion */
1183         if(cc== -1){
1184             if(valid_data == 0){
1185                 return 0;
1186             }
1187
1188             while(p%4!=3){
1189                 p++;
1190                 buf[p%4] = 0;
1191             }
1192         } else {
1193             buf[p%4] = cc;
1194             size_out++;
1195             valid_data = 1;
1196         }
1197
1198         
1199         /* When we have 4 base64 letters, convert them into three
1200            bytes */
1201         if (p%4 == 3) {
1202             *dest++ =(buf[0]<< 2)|((buf[1] & 0x30) >> 4);
1203             *dest++ =((buf[1] & 0x0F) << 4)|((buf[2] & 0x3C) >> 2);
1204             *dest++ =((buf[2] & 0x03) << 6)|(buf[3] & 0x3F);
1205
1206             memset(buf,0,4);
1207         }
1208
1209         p++;
1210
1211     }
1212     /* Calculate the size of the converted data*/
1213    *size = ((int)(size_out/4))*3;
1214     if(size_out%4 == 2) *size+=1;
1215     if(size_out%4 == 3) *size+=2;
1216
1217     return(dest);
1218 }
1219
1220
1221 /***********************************************************************
1222                                                                        
1223  Routines to output MIME
1224
1225 **********************************************************************/
1226
1227
1228 struct sspm_buffer {
1229         char* buffer;
1230         char* pos;
1231         size_t buf_size;
1232         int line_pos;
1233 };
1234
1235 void sspm_append_string(struct sspm_buffer* buf, char* string);
1236 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part, int *part_num);
1237
1238 void sspm_append_hex(struct sspm_buffer* buf, char ch)
1239 {
1240     char tmp[3];
1241
1242     sprintf(tmp,"=%02X",ch);
1243
1244     sspm_append_string(buf,tmp);
1245 }
1246
1247 /* a copy of icalmemory_append_char */
1248 void sspm_append_char(struct sspm_buffer* buf, char ch)
1249 {
1250     char *new_buf;
1251     char *new_pos;
1252
1253     size_t data_length, final_length;
1254
1255     data_length = (size_t)buf->pos - (size_t)buf->buffer;
1256
1257     final_length = data_length + 2; 
1258
1259     if ( final_length > (size_t) buf->buf_size ) {
1260         
1261         buf->buf_size  = (buf->buf_size) * 2  + final_length +1;
1262
1263         new_buf = realloc(buf->buffer,buf->buf_size);
1264
1265         new_pos = (void*)((size_t)new_buf + data_length);
1266         
1267         buf->pos = new_pos;
1268         buf->buffer = new_buf;
1269     }
1270
1271     *(buf->pos) = ch;
1272     buf->pos += 1;
1273     *(buf->pos) = 0;
1274 }
1275 /* A copy of icalmemory_append_string */
1276 void sspm_append_string(struct sspm_buffer* buf, char* string)
1277 {
1278     char *new_buf;
1279     char *new_pos;
1280
1281     size_t data_length, final_length, string_length;
1282
1283     string_length = strlen(string);
1284     data_length = (size_t)buf->pos - (size_t)buf->buffer;    
1285     final_length = data_length + string_length; 
1286
1287     if ( final_length >= (size_t) buf->buf_size) {
1288
1289         
1290         buf->buf_size  = (buf->buf_size) * 2  + final_length;
1291
1292         new_buf = realloc(buf->buffer,buf->buf_size);
1293
1294         new_pos = (void*)((size_t)new_buf + data_length);
1295         
1296         buf->pos = new_pos;
1297         buf->buffer = new_buf;
1298     }
1299     
1300     strcpy(buf->pos, string);
1301
1302     buf->pos += string_length;
1303 }
1304
1305
1306
1307 static int sspm_is_printable(char c)
1308 {
1309     return (c >= 33) && (c <= 126) && (c != '=');
1310
1311
1312                      
1313
1314 void sspm_encode_quoted_printable(struct sspm_buffer *buf, char* data)
1315 {
1316     char *p;
1317     int lpos = 0;
1318
1319     for(p = data; *p != 0; p++){
1320
1321         if(sspm_is_printable(*p)){
1322             /* plain characters can represent themselves */
1323             /* RFC2045 Rule #2 */
1324                sspm_append_char(buf,*p);
1325                lpos++;
1326         } else if ( *p == '\t' || *p == ' ' ) {
1327
1328             /* For tabs and spaces, only encode if they appear at the
1329                end of the line */
1330             /* RFC2045 Rule #3 */
1331
1332            char n = *(p+1);
1333
1334            if( n == '\n' || n == '\r'){
1335                sspm_append_hex(buf,*p);
1336                lpos += 3;
1337            } else {
1338                sspm_append_char(buf,*p);
1339                lpos++;
1340            }
1341
1342         } else if( *p == '\n' || *p == '\r'){
1343             sspm_append_char(buf,*p);
1344
1345             lpos=0;
1346
1347         } else {
1348             /* All others need to be encoded */
1349             sspm_append_hex(buf,*p);
1350             lpos+=3;
1351         }
1352
1353
1354         /* Add line breaks */
1355         if (lpos > 72){
1356             lpos = 0;
1357             sspm_append_string(buf,"=\n");
1358         }
1359     }
1360 }
1361
1362 static char BaseTable[64] = {
1363     'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
1364     'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
1365     'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
1366     'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
1367 };
1368     
1369 void sspm_write_base64(struct sspm_buffer *buf, char* inbuf,int size )
1370 {
1371     
1372     char outbuf[4];
1373     int i;
1374
1375     outbuf[0] = outbuf[1] = outbuf[2] = outbuf[3] = 65;
1376
1377     switch(size){
1378         
1379         case 4:
1380             outbuf[3] =   inbuf[2] & 0x3F;
1381
1382         case 3:
1383             outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6);
1384
1385         case 2: 
1386             outbuf[0] =  (inbuf[0] & 0xFC) >> 2;
1387             outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4);
1388             break;
1389
1390         default:
1391             assert(0);
1392     }
1393
1394     for(i = 0; i < 4; i++){
1395
1396         if(outbuf[i] == 65){
1397             sspm_append_char(buf,'=');
1398         } else {
1399             sspm_append_char(buf,BaseTable[(int)outbuf[i]]);
1400         }
1401     }
1402 }
1403              
1404 void sspm_encode_base64(struct sspm_buffer *buf, char* data, size_t size)
1405 {
1406
1407     char *p;
1408     char inbuf[3];
1409     int i = 0;
1410     int first = 1;
1411     int lpos = 0;
1412
1413     inbuf[0] = inbuf[1] = inbuf[2]  = 0;
1414
1415     for (p = data; *p !=0; p++){
1416                          
1417         if (i%3 == 0 && first == 0){
1418
1419             sspm_write_base64(buf, inbuf, 4);
1420             lpos+=4;
1421
1422             inbuf[0] = inbuf[1] = inbuf[2] = 0;
1423         }
1424
1425         assert(lpos%4 == 0);
1426
1427         if (lpos == 72){
1428             sspm_append_string(buf,"\n");
1429             lpos = 0;
1430         }
1431
1432         inbuf[i%3] = *p;
1433
1434         i++;
1435         first = 0;
1436
1437     }
1438
1439     
1440     /* If the inbuf was not exactly filled on the last byte, we need
1441        to spit out the odd bytes that did get in -- either one or
1442        two. This will result in an output of two bytes and '==' or
1443        three bytes and '=', respectively */
1444     
1445     if (i%3 == 1 && first == 0){
1446             sspm_write_base64(buf, inbuf, 2);
1447     } else if (i%3 == 2 && first == 0){
1448             sspm_write_base64(buf, inbuf, 3);
1449     }
1450
1451 }
1452
1453 void sspm_write_header(struct sspm_buffer *buf,struct sspm_header *header)
1454 {
1455     
1456     int i;
1457     char temp[TMP_BUF_SIZE];                           
1458     char* major; 
1459     char* minor; 
1460     
1461     /* Content-type */
1462
1463     major = sspm_major_type_string(header->major);
1464     minor = sspm_minor_type_string(header->minor);
1465
1466     if(header->minor == SSPM_UNKNOWN_MINOR_TYPE ){
1467         assert(header->minor_text !=0);
1468         minor = header->minor_text;
1469     }
1470     
1471     sprintf(temp,"Content-Type: %s/%s",major,minor);
1472
1473     sspm_append_string(buf,temp);
1474
1475     if(header->boundary != 0){
1476         sprintf(temp,";boundary=\"%s\"",header->boundary);
1477         sspm_append_string(buf,temp);
1478     }
1479     
1480     /* Append any content type parameters */    
1481     if(header->content_type_params != 0){
1482         for(i=0; *(header->content_type_params[i])!= 0;i++){
1483             sprintf(temp,"%s",header->content_type_params[i]);
1484             sspm_append_char(buf,';');
1485             sspm_append_string(buf,temp);
1486         }
1487     }
1488     
1489     sspm_append_char(buf,'\n');
1490
1491     /*Content-Transfer-Encoding */
1492
1493     if(header->encoding != SSPM_UNKNOWN_ENCODING &&
1494         header->encoding != SSPM_NO_ENCODING){
1495         sprintf(temp,"Content-Transfer-Encoding: %s\n",
1496                 sspm_encoding_string(header->encoding));
1497     }
1498
1499     sspm_append_char(buf,'\n');
1500
1501 }
1502
1503 void sspm_write_multipart_part(struct sspm_buffer *buf,
1504                                struct sspm_part *parts,
1505                                int* part_num)
1506 {
1507
1508     int parent_level, level;
1509     struct sspm_header *header = &(parts[*part_num].header);
1510     /* Write the header for the multipart part */
1511     sspm_write_header(buf,header);
1512
1513     parent_level = parts[*part_num].level;
1514
1515     (*part_num)++;
1516
1517     level = parts[*part_num].level;
1518
1519     while(parts[*part_num].header.major != SSPM_NO_MAJOR_TYPE &&
1520           level == parent_level+1){
1521
1522         assert(header->boundary);
1523         sspm_append_string(buf,header->boundary);
1524         sspm_append_char(buf,'\n');
1525         
1526         if (parts[*part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
1527             sspm_write_multipart_part(buf,parts,part_num);
1528         } else {
1529             sspm_write_part(buf, &(parts[*part_num]), part_num);
1530         }       
1531
1532         (*part_num)++;
1533         level =  parts[*part_num].level;
1534     }
1535    
1536     sspm_append_string(buf,"\n\n--");
1537     sspm_append_string(buf,header->boundary);
1538     sspm_append_string(buf,"\n");
1539
1540     (*part_num)--; /* undo last, spurious, increment */
1541 }
1542
1543 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part,int *part_num)
1544 {
1545
1546     /* Write header */
1547     sspm_write_header(buf,&(part->header));
1548
1549     /* Write part data */
1550
1551     if(part->data == 0){
1552         return;
1553     }
1554
1555     if(part->header.encoding == SSPM_BASE64_ENCODING) {
1556         assert(part->data_size != 0);
1557         sspm_encode_base64(buf,part->data,part->data_size);
1558     } else if(part->header.encoding == SSPM_QUOTED_PRINTABLE_ENCODING) {
1559         sspm_encode_quoted_printable(buf,part->data);
1560     } else {
1561         sspm_append_string(buf,part->data);
1562     }
1563
1564     sspm_append_string(buf,"\n\n");
1565 }
1566
1567 int sspm_write_mime(struct sspm_part *parts,size_t num_parts,
1568                     char **output_string, char* header)
1569 {
1570     struct sspm_buffer buf;
1571     int part_num =0;
1572
1573     buf.buffer = malloc(4096);
1574     buf.pos = buf.buffer;
1575     buf.buf_size = 10;
1576     buf.line_pos = 0;
1577
1578     /* write caller's header */
1579     if(header != 0){
1580         sspm_append_string(&buf,header);
1581     }
1582
1583     if(buf.buffer[strlen(buf.buffer)-1] != '\n'){
1584         sspm_append_char(&buf,'\n');
1585     }
1586
1587     /* write mime-version header */
1588     sspm_append_string(&buf,"MIME-Version: 1.0\n");
1589
1590     /* End of header */
1591
1592     /* Write body parts */
1593     while(parts[part_num].header.major != SSPM_NO_MAJOR_TYPE){
1594         if (parts[part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
1595             sspm_write_multipart_part(&buf,parts,&part_num);
1596         } else {
1597             sspm_write_part(&buf, &(parts[part_num]), &part_num);
1598         }       
1599
1600         part_num++;
1601     }
1602
1603
1604     *output_string = buf.buffer;
1605
1606     return 0;
1607 }
1608