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