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 #ifdef WIN32
50 #if defined(_MSC_VER) && (_MSC_VER < 1900)
51 #define snprintf _snprintf
52 #endif
53 #define strcasecmp    stricmp
54 #endif
55 
56 #define TMP_BUF_SIZE 1024
57 
58 
59 enum mime_state {
60     UNKNOWN_STATE,
61     IN_HEADER,
62     END_OF_HEADER,
63     IN_BODY,
64     OPENING_PART,
65     END_OF_PART,
66     TERMINAL_END_OF_PART,
67     END_OF_INPUT
68 };
69 
70 struct mime_impl{
71 	struct sspm_part *parts;
72 	size_t max_parts;
73 	int part_no;
74 	int level;
75 	const struct sspm_action_map *actions;
76 	char* (*get_string)(char *s, size_t size, void* data);
77 	void* get_string_data;
78 	char temp[TMP_BUF_SIZE];
79 	enum mime_state state;
80 };
81 
82 void sspm_free_header(struct sspm_header *header);
83 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header);
84 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header);
85 
sspm_strdup(const char * str)86 char* sspm_strdup(const char* str){
87 
88     char* s;
89 
90     s = strdup(str);
91 
92     return s;
93 }
94 
95 
96 static struct  major_content_type_map
97 {
98     enum sspm_major_type type;
99     const char* str;
100 
101 } major_content_type_map[]  =
102 {
103     {SSPM_MULTIPART_MAJOR_TYPE,"multipart" },
104     {SSPM_TEXT_MAJOR_TYPE,"text" },
105     {SSPM_TEXT_MAJOR_TYPE,"text" },
106     {SSPM_IMAGE_MAJOR_TYPE,"image" },
107     {SSPM_AUDIO_MAJOR_TYPE,"audio" },
108     {SSPM_VIDEO_MAJOR_TYPE,"video" },
109     {SSPM_APPLICATION_MAJOR_TYPE,"application" },
110     {SSPM_MULTIPART_MAJOR_TYPE,"multipart" },
111     {SSPM_MESSAGE_MAJOR_TYPE,"message" },
112     {SSPM_UNKNOWN_MAJOR_TYPE,"" },
113 };
114 
115 static struct  minor_content_type_map
116 {
117     enum sspm_minor_type type;
118     const char* str;
119 
120 } minor_content_type_map[]  =
121 {
122     {SSPM_ANY_MINOR_TYPE,"*" },
123     {SSPM_PLAIN_MINOR_TYPE,"plain" },
124     {SSPM_RFC822_MINOR_TYPE,"rfc822" },
125     {SSPM_DIGEST_MINOR_TYPE,"digest" },
126     {SSPM_CALENDAR_MINOR_TYPE,"calendar" },
127     {SSPM_MIXED_MINOR_TYPE,"mixed" },
128     {SSPM_RELATED_MINOR_TYPE,"related" },
129     {SSPM_ALTERNATIVE_MINOR_TYPE,"alternative" },
130     {SSPM_PARALLEL_MINOR_TYPE,  "parallel" },
131     {SSPM_UNKNOWN_MINOR_TYPE,"" }
132 };
133 
134 
135 
136 struct encoding_map {
137     enum sspm_encoding encoding;
138     const char* str;
139 } sspm_encoding_map[] =
140 {
141     {SSPM_NO_ENCODING,""},
142     {SSPM_QUOTED_PRINTABLE_ENCODING,"quoted-printable"},
143     {SSPM_8BIT_ENCODING,"8bit"},
144     {SSPM_7BIT_ENCODING,"7bit"},
145     {SSPM_BINARY_ENCODING,"binary"},
146     {SSPM_BASE64_ENCODING,"base64"},
147     {SSPM_UNKNOWN_ENCODING,""}
148 
149 };
150 
151 
sspm_get_parameter(const char * line,const char * parameter)152 char* sspm_get_parameter(const char* line, const char* parameter)
153 {
154     char *p,*s,*q;
155     static char name[1024];
156 
157     /* Find where the parameter name is in the line */
158     p = strstr(line,parameter);
159 
160     if( p == 0){
161 	return 0;
162     }
163 
164     /* skip over the parameter name, the '=' and any blank spaces */
165 
166     p+=strlen(parameter);
167 
168     while(*p==' ' || *p == '='){
169 	p++;
170     }
171 
172     /*now find the next semicolon*/
173 
174     s = strchr(p,';');
175 
176     /* Strip of leading quote */
177     q = strchr(p,'\"');
178 
179     if(q !=0){
180 	p = q+1;
181     }
182 
183     if(s != 0){
184 	strncpy(name,p,(size_t)s-(size_t)p);
185     } else {
186 	strncpy(name,p,sizeof(name)-1);
187 	name[sizeof(name)-1]='\0';
188     }
189 
190     /* Strip off trailing quote, if it exists */
191 
192     q = strrchr(name,'\"');
193 
194     if (q != 0){
195 	*q='\0';
196     }
197 
198     return name;
199 }
200 
sspm_property_name(const char * line)201 char* sspm_property_name(const char* line)
202 {
203     static char name[1024];
204     char *c = strchr(line,':');
205 
206     if(c != 0){
207 	strncpy(name,line,(size_t)c-(size_t)line);
208 	name[(size_t)c-(size_t)line] = '\0';
209 	return name;
210     } else {
211 	return 0;
212     }
213 }
214 
sspm_value(char * line)215 char* sspm_value(char* line)
216 {
217     static char value[1024];
218 
219     char *c,*s, *p;
220 
221     /* Find the first colon and the next semicolon */
222 
223     value[0] = 0;
224     c = strchr(line,':');
225     if (!c)
226       return value;
227     s = strchr(c,';');
228 
229     /* Skip the colon */
230     c++;
231 
232     if (s == 0){
233 	s = c+strlen(line);
234     }
235 
236     for(p=value; c != s; c++){
237 	if(*c!=' ' && *c!='\n'){
238 	    *(p++) = *c;
239 	}
240     }
241 
242     *p='\0';
243 
244     return value;
245 
246 }
247 
248 static const char *mime_headers[] = {
249     "Content-Type",
250     "Content-Transfer-Encoding",
251     "Content-Disposition",
252     "Content-Id",
253     "Mime-Version",
254     0
255 };
256 
257 
sspm_default_new_part(void)258 void* sspm_default_new_part(void)
259 {
260     return 0;
261 }
sspm_default_add_line(void * part,struct sspm_header * header,const char * line,size_t size)262 void sspm_default_add_line(void *part, struct sspm_header *header,
263 			   const char* line, size_t size)
264 {
265     (void)part;
266     (void)header;
267     (void)line;
268     (void)size;
269 }
270 
sspm_default_end_part(void * part)271 void* sspm_default_end_part(void* part)
272 {
273     (void)part;
274     return 0;
275 }
276 
sspm_default_free_part(void * part)277 void sspm_default_free_part(void *part)
278 {
279     (void)part;
280 }
281 
282 
283 
284 struct sspm_action_map sspm_action_map[] =
285 {
286     {SSPM_UNKNOWN_MAJOR_TYPE,SSPM_UNKNOWN_MINOR_TYPE,sspm_default_new_part,sspm_default_add_line,sspm_default_end_part,sspm_default_free_part},
287 };
288 
sspm_is_mime_header(char * line)289 int sspm_is_mime_header(char *line)
290 {
291     char *name = sspm_property_name(line);
292     int i;
293 
294     if(name == 0){
295 	return 0;
296     }
297 
298     for(i = 0; mime_headers[i] != 0; i++){
299 	if(strcasecmp(name, mime_headers[i]) == 0)
300 	    return 1;
301     }
302 
303     return 0;
304 }
305 
sspm_is_mail_header(char * line)306 int sspm_is_mail_header(char* line)
307 {
308     char *name = sspm_property_name(line);
309 
310     if (name != 0){
311 	return 1;
312     }
313 
314     return 0;
315 
316 }
317 
sspm_is_blank(char * line)318 int sspm_is_blank(char* line)
319 {
320     char *p;
321     char c =0;
322 
323     for(p=line; *p!=0; p++){
324 	if( ! (*p == ' '|| *p == '\t' || *p=='\n') ){
325 	    c++;
326 	}
327     }
328 
329     if (c==0){
330 	return 1;
331     }
332 
333     return 0;
334 
335 }
336 
sspm_is_continuation_line(char * line)337 int sspm_is_continuation_line(char* line)
338 {
339     if (line[0] == ' '|| line[0] == '\t' ) {
340 	return 1;
341     }
342 
343     return 0;
344 }
345 
sspm_is_mime_boundary(char * line)346 int sspm_is_mime_boundary(char *line)
347 {
348     if( line[0] == '-' && line[1] == '-') {
349 	return 1;
350     }
351 
352     return 0;
353 }
354 
sspm_is_mime_terminating_boundary(char * line)355 int sspm_is_mime_terminating_boundary(char *line)
356 {
357 
358 
359     if (sspm_is_mime_boundary(line) &&
360 	strstr(line,"--\n")){
361 	return 1;
362     }
363 
364     return 0;
365 }
366 
367 enum line_type {
368     EMPTY,
369     BLANK,
370     MIME_HEADER,
371     MAIL_HEADER,
372     HEADER_CONTINUATION,
373     BOUNDARY,
374     TERMINATING_BOUNDARY,
375     UNKNOWN_TYPE
376 };
377 
378 
get_line_type(char * line)379 static enum line_type get_line_type(char* line){
380 
381     if (line == 0){
382 	return EMPTY;
383     } else if(sspm_is_blank(line)){
384 	return BLANK;
385     } else if (sspm_is_mime_header(line)){
386 	return MIME_HEADER;
387     } else if (sspm_is_mail_header(line)){
388 	return MAIL_HEADER;
389     } else if (sspm_is_continuation_line(line)){
390 	return HEADER_CONTINUATION;
391     } else if (sspm_is_mime_terminating_boundary(line)){
392 	return TERMINATING_BOUNDARY;
393     } else if (sspm_is_mime_boundary(line)) {
394 	return BOUNDARY;
395     } else {
396 	return UNKNOWN_TYPE;
397     }
398 
399 
400 }
401 
402 
get_action(struct mime_impl * impl,enum sspm_major_type major,enum sspm_minor_type minor)403 static struct sspm_action_map get_action(struct mime_impl *impl,
404 				  enum sspm_major_type major,
405 				  enum sspm_minor_type minor)
406 {
407     int i;
408 
409     /* Read caller suppled action map */
410 
411     if (impl->actions != 0){
412 	for(i=0; impl->actions[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
413 	    if((major == impl->actions[i].major &&
414 	       minor == impl->actions[i].minor) ||
415 	       (major == impl->actions[i].major &&
416 		minor == SSPM_ANY_MINOR_TYPE)){
417 		return impl->actions[i];
418 	    }
419 	}
420     }
421 
422     /* Else, read default action map */
423 
424     for(i=0; sspm_action_map[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
425 	    if((major == sspm_action_map[i].major &&
426 	       minor == sspm_action_map[i].minor) ||
427 	       (major == sspm_action_map[i].major &&
428 		minor == SSPM_ANY_MINOR_TYPE)){
429 	    break;
430 	}
431     }
432 
433     return sspm_action_map[i];
434 }
435 
436 
sspm_lowercase(char * str)437 char* sspm_lowercase(char* str)
438 {
439     char* p = 0;
440     char* new;
441 
442     if(str ==0){
443 	return 0;
444     }
445     new = sspm_strdup(str);
446     for(p = new; *p!=0; p++){
447 	*p = tolower(*p);
448     }
449 
450     return new;
451 }
452 
sspm_find_major_content_type(char * type)453 enum sspm_major_type sspm_find_major_content_type(char* type)
454 {
455     int i;
456 
457     char* ltype = sspm_lowercase(type);
458 
459     for (i=0; major_content_type_map[i].type !=  SSPM_UNKNOWN_MAJOR_TYPE; i++){
460 	if(strncmp(ltype, major_content_type_map[i].str,
461 		   strlen(major_content_type_map[i].str))==0){
462 	    free(ltype);
463 	    return major_content_type_map[i].type;
464 	}
465     }
466     free(ltype);
467     return major_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
468 }
469 
sspm_find_minor_content_type(char * type)470 enum sspm_minor_type sspm_find_minor_content_type(char* type)
471 {
472     int i;
473     char* ltype = sspm_lowercase(type);
474 
475     char *p = strchr(ltype,'/');
476 
477     if (p==0){
478         free(ltype);
479 	return SSPM_UNKNOWN_MINOR_TYPE;
480     }
481 
482     p++; /* Skip the '/' */
483 
484     for (i=0; minor_content_type_map[i].type !=  SSPM_UNKNOWN_MINOR_TYPE; i++){
485 	if(strncmp(p, minor_content_type_map[i].str,
486 		   strlen(minor_content_type_map[i].str))==0){
487 	    free(ltype);
488 	    return minor_content_type_map[i].type;
489 	}
490     }
491 
492     free(ltype);
493     return minor_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
494 }
495 
sspm_major_type_string(enum sspm_major_type type)496 const char* sspm_major_type_string(enum sspm_major_type type)
497 {
498     int i;
499 
500     for (i=0; major_content_type_map[i].type !=  SSPM_UNKNOWN_MAJOR_TYPE;
501 	 i++){
502 
503 	if(type == major_content_type_map[i].type){
504 	    return major_content_type_map[i].str;
505 	}
506     }
507 
508     return major_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
509 }
510 
sspm_minor_type_string(enum sspm_minor_type type)511 const char* sspm_minor_type_string(enum sspm_minor_type type)
512 {
513     int i;
514     for (i=0; minor_content_type_map[i].type !=  SSPM_UNKNOWN_MINOR_TYPE;
515 	 i++){
516 	if(type == minor_content_type_map[i].type){
517 	    return minor_content_type_map[i].str;
518 	}
519     }
520 
521     return minor_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
522 }
523 
524 
sspm_encoding_string(enum sspm_encoding type)525 const char* sspm_encoding_string(enum sspm_encoding type)
526 {
527     int i;
528     for (i=0; sspm_encoding_map[i].encoding !=  SSPM_UNKNOWN_ENCODING;
529 	 i++){
530 	if(type == sspm_encoding_map[i].encoding){
531 	    return sspm_encoding_map[i].str;
532 	}
533     }
534 
535     return sspm_encoding_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
536 }
537 
538 /* Interpret a header line and add its data to the header
539    structure. */
sspm_build_header(struct sspm_header * header,char * line)540 void sspm_build_header(struct sspm_header *header, char* line)
541 {
542     char *prop;
543     char *val;
544 
545     val = sspm_strdup(sspm_value(line));
546     prop = sspm_strdup(sspm_property_name(line));
547 
548     if(strcasecmp(prop,"Content-Type") == 0){
549 
550 	/* Create a new mime_header, fill in content-type
551 	   and possibly boundary */
552 
553 	char* boundary= sspm_get_parameter(line,"boundary");
554 
555 	header->def = 0;
556 	header->major = sspm_find_major_content_type(val);
557 	header->minor = sspm_find_minor_content_type(val);
558 
559 	if(header->minor == SSPM_UNKNOWN_MINOR_TYPE){
560 	    char *p = strchr(val,'/');
561 
562 	    if (p != 0){
563 		p++; /* Skip the '/' */
564 
565 		header->minor_text = sspm_strdup(p);
566 	    } else {
567 		/* Error, malformed content type */
568 		header->minor_text = sspm_strdup("unknown");
569 	    }
570 	}
571 	if (boundary != 0){
572 	    header->boundary = sspm_strdup(boundary);
573 	}
574 
575     } else if(strcasecmp(prop,"Content-Transfer-Encoding")==0){
576 	char* encoding = sspm_value(line);
577 	char* lencoding = sspm_lowercase(encoding);
578 
579 	if(strcasecmp(lencoding,"base64")==0){
580 	    header->encoding = SSPM_BASE64_ENCODING;
581 	} else 	if(strcasecmp(lencoding,"quoted-printable")==0){
582 	    header->encoding = SSPM_QUOTED_PRINTABLE_ENCODING;
583 	} else 	if(strcasecmp(lencoding,"binary")==0){
584 	    header->encoding = SSPM_BINARY_ENCODING;
585 	} else 	if(strcasecmp(lencoding,"7bit")==0){
586 	    header->encoding = SSPM_7BIT_ENCODING;
587 	} else 	if(strcasecmp(lencoding,"8bit")==0){
588 	    header->encoding = SSPM_8BIT_ENCODING;
589 	} else {
590 	    header->encoding = SSPM_UNKNOWN_ENCODING;
591 	}
592 
593 
594 	free(lencoding);
595 
596 	header->def = 0;
597 
598     } else if(strcasecmp(prop,"Content-Id")==0){
599 	char* cid = sspm_value(line);
600 	header->content_id = sspm_strdup(cid);
601 	header->def = 0;
602 
603     }
604     free(val);
605     free(prop);
606 }
607 
sspm_get_next_line(struct mime_impl * impl)608 char* sspm_get_next_line(struct mime_impl *impl)
609 {
610     char* s;
611     s = impl->get_string(impl->temp,TMP_BUF_SIZE,impl->get_string_data);
612 
613     if(s == 0){
614 	impl->state = END_OF_INPUT;
615     }
616     return s;
617 }
618 
619 
sspm_store_part(struct mime_impl * impl,struct sspm_header header,int level,void * part,size_t size)620 void sspm_store_part(struct mime_impl *impl, struct sspm_header header,
621 		      int level, void *part, size_t size)
622 {
623 
624     impl->parts[impl->part_no].header = header;
625     impl->parts[impl->part_no].level = level;
626     impl->parts[impl->part_no].data = part;
627     impl->parts[impl->part_no].data_size = size;
628     impl->part_no++;
629 }
630 
sspm_set_error(struct sspm_header * header,enum sspm_error error,char * message)631 void sspm_set_error(struct sspm_header* header, enum sspm_error error,
632 		    char* message)
633 {
634     header->error = error;
635 
636     if(header->error_text!=0){
637 	free(header->error_text);
638     }
639 
640     header->def = 0;
641 
642     if(message != 0){
643 	header->error_text = sspm_strdup(message);
644     } else {
645 	header->error_text = 0;
646     }
647 
648 }
649 
sspm_make_part(struct mime_impl * impl,struct sspm_header * header,struct sspm_header * parent_header,void ** end_part,size_t * size)650 void* sspm_make_part(struct mime_impl *impl,
651 		     struct sspm_header *header,
652 		     struct sspm_header *parent_header,
653 		     void **end_part,
654 		     size_t *size)
655 {
656 
657     /* For a single part type, read to the boundary, if there is a
658    boundary. Otherwise, read until the end of input.  This routine
659    assumes that the caller has read the header and has left the input
660    at the first blank line */
661 
662     char *line;
663     void *part;
664     int end = 0;
665 
666     struct sspm_action_map action = get_action(
667 	impl,
668 	header->major,
669 	header->minor);
670 
671     *size = 0;
672     part =action.new_part();
673 
674     impl->state = IN_BODY;
675 
676     while(end == 0 && (line = sspm_get_next_line(impl)) != 0){
677 
678 	if(sspm_is_mime_boundary(line)){
679 
680 	    /* If there is a boundary, then this must be a multipart
681                part, so there must be a parent_header. */
682 	    if(parent_header == 0){
683 		char* boundary;
684 		end = 1;
685 		*end_part = 0;
686 
687 		sspm_set_error(header,SSPM_UNEXPECTED_BOUNDARY_ERROR,line);
688 
689 		/* Read until the paired terminating boundary */
690 		if((boundary = (char*)malloc(strlen(line)+5)) == 0){
691 		    fprintf(stderr,"Out of memory");
692 		    abort();
693 		}
694 		strcpy(boundary,line);
695 		strcat(boundary,"--");
696 		while((line = sspm_get_next_line(impl)) != 0){
697 		    /*printf("Error: %s\n",line);*/
698 		    if(strcmp(boundary,line)==0){
699 			break;
700 		    }
701 		}
702 		free(boundary);
703 
704 		break;
705 	    }
706 
707 	    if(strcmp((line+2),parent_header->boundary) == 0) {
708 		*end_part = action.end_part(part);
709 
710 		if(sspm_is_mime_boundary(line)){
711 		    impl->state = END_OF_PART;
712 		} else if ( sspm_is_mime_terminating_boundary(line)){
713 		    impl->state = TERMINAL_END_OF_PART;
714 		}
715 		end = 1;
716 	    } else {
717 		/* Error, this is not the correct terminating boundary*/
718 
719 		/* read and discard until we get the right boundary.  */
720 		    char* boundary;
721 		    char msg[256];
722 
723 		    snprintf(msg,256,
724 			     "Expected: %s--. Got: %s",
725 			     parent_header->boundary,line);
726 
727 		    sspm_set_error(parent_header,
728 		      SSPM_WRONG_BOUNDARY_ERROR,msg);
729 
730 		    /* Read until the paired terminating boundary */
731 		    if((boundary = (char*)malloc(strlen(line)+5)) == 0){
732 			fprintf(stderr,"Out of memory");
733 			abort();
734 		    }
735 		    strcpy(boundary,line);
736 		    strcat(boundary,"--");
737 		    while((line = sspm_get_next_line(impl)) != 0){
738 			if(strcmp(boundary,line)==0){
739 			    break;
740 			}
741 		    }
742 		    free(boundary);
743 
744 	    }
745 	} else {
746 	    char* data=0;
747 	    char* rtrn=0;
748 	    *size = strlen(line);
749 
750 	    data = (char*)malloc(*size+2);
751 	    assert(data != 0);
752 	    if (header->encoding == SSPM_BASE64_ENCODING){
753 		rtrn = decode_base64(data,line,size);
754 	    } else if(header->encoding == SSPM_QUOTED_PRINTABLE_ENCODING){
755 		rtrn = decode_quoted_printable(data,line,size);
756 	    }
757 
758 	    if(rtrn == 0){
759 		strcpy(data,line);
760 	    }
761 
762 	    /* add a end-of-string after the data, just in case binary
763                data from decode64 gets passed to a tring handling
764                routine in add_line  */
765 	    data[*size+1]='\0';
766 
767 	    action.add_line(part,header,data,*size);
768 
769 	    free(data);
770 	}
771     }
772 
773     if (end == 0){
774 	/* End the part if the input is exhausted */
775 	*end_part = action.end_part(part);
776     }
777 
778     return end_part;
779 }
780 
781 
sspm_make_multipart_subpart(struct mime_impl * impl,struct sspm_header * parent_header)782 void* sspm_make_multipart_subpart(struct mime_impl *impl,
783 			    struct sspm_header *parent_header)
784 {
785     struct sspm_header header;
786     char *line;
787     void* part;
788     size_t size;
789 
790     if(parent_header->boundary == 0){
791 	/* Error. Multipart headers must have a boundary*/
792 
793 	sspm_set_error(parent_header,SSPM_NO_BOUNDARY_ERROR,0);
794 	/* read all of the reamining lines */
795 	while((line = sspm_get_next_line(impl)) != 0){
796 	}
797 
798 	return 0;
799     }
800 
801 
802     /* Step 1: Read the opening boundary */
803 
804     if(get_line_type(impl->temp) != BOUNDARY){
805 	while((line=sspm_get_next_line(impl)) != 0 ){
806 	    if(sspm_is_mime_boundary(line)){
807 
808 		assert(parent_header != 0);
809 
810 		/* Check if it is the right boundary */
811 		if(!sspm_is_mime_terminating_boundary(line) &&
812 		   strcmp((line+2),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 
sspm_make_multipart_part(struct mime_impl * impl,struct sspm_header * header)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 
sspm_read_header(struct mime_impl * impl,struct sspm_header * header)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*/
sspm_parse_mime(struct sspm_part * parts,size_t max_parts,const struct sspm_action_map * actions,char * (* get_string)(char * s,size_t size,void * data),void * get_string_data,struct sspm_header * first_header)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 
sspm_free_parts(struct sspm_part * parts,size_t max_parts)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 
sspm_free_header(struct sspm_header * header)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 
decode_quoted_printable(char * dest,char * src,size_t * size)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 
decode_base64(char * dest,char * src,size_t * size)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 
sspm_append_hex(struct sspm_buffer * buf,char ch)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 */
sspm_append_char(struct sspm_buffer * buf,char ch)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 */
sspm_append_string(struct sspm_buffer * buf,const char * 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 
sspm_is_printable(char c)1333 static int sspm_is_printable(char c)
1334 {
1335     return (c >= 33) && (c <= 126) && (c != '=');
1336 
1337 }
1338 
1339 
sspm_encode_quoted_printable(struct sspm_buffer * buf,char * data)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 
sspm_write_base64(struct sspm_buffer * buf,char * inbuf,int size)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 
sspm_encode_base64(struct sspm_buffer * buf,char * data,size_t size)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 
sspm_write_header(struct sspm_buffer * buf,struct sspm_header * header)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 
sspm_write_multipart_part(struct sspm_buffer * buf,struct sspm_part * parts,int * part_num)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 
sspm_write_part(struct sspm_buffer * buf,struct sspm_part * part,int * part_num)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 
sspm_write_mime(struct sspm_part * parts,size_t num_parts,char ** output_string,const char * header)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     int 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 
1640