2 ======================================================================
3 FILE: sspm.c Parse Mime
4 CREATOR: eric 25 June 2000
6 $Id: sspm.c,v 1.13 2008-01-28 22:34:38 artcancro Exp $
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/
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.
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of either:
23 The LGPL as published by the Free Software Foundation, version
24 2.1, available at: http://www.fsf.org/copyleft/lesser.html
28 The Mozilla Public License Version 1.0. You may obtain a copy of
29 the License at http://www.mozilla.org/MPL/
31 The Initial Developer of the Original Code is Eric Busboom
33 (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
34 http://www.softwarestudio.org
35 ======================================================================*/
41 #include <ctype.h> /* for tolower */
42 #include <stdlib.h> /* for malloc, free */
43 #include <string.h> /* for strcasecmp */
50 #define snprintf _snprintf
51 #define strcasecmp stricmp
54 #define TMP_BUF_SIZE 1024
69 struct sspm_part *parts;
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;
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);
84 char* sspm_strdup(const char* str){
94 static struct major_content_type_map
96 enum sspm_major_type type;
99 } major_content_type_map[] =
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,"" },
113 static struct minor_content_type_map
115 enum sspm_minor_type type;
118 } minor_content_type_map[] =
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,"" }
134 struct encoding_map {
135 enum sspm_encoding encoding;
137 } sspm_encoding_map[] =
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,""}
150 char* sspm_get_parameter(const char* line, const char* parameter)
153 static char name[1024];
155 /* Find where the parameter name is in the line */
156 p = strstr(line,parameter);
162 /* skip over the parameter name, the '=' and any blank spaces */
164 p+=strlen(parameter);
166 while(*p==' ' || *p == '='){
170 /*now find the next semicolon*/
174 /* Strip of leading quote */
182 strncpy(name,p,(size_t)s-(size_t)p);
184 strncpy(name,p,sizeof(name)-1);
185 name[sizeof(name)-1]='\0';
188 /* Strip off trailing quote, if it exists */
190 q = strrchr(name,'\"');
199 char* sspm_property_name(const char* line)
201 static char name[1024];
202 char *c = strchr(line,':');
205 strncpy(name,line,(size_t)c-(size_t)line);
206 name[(size_t)c-(size_t)line] = '\0';
213 char* sspm_value(char* line)
215 static char value[1024];
219 /* Find the first colon and the next semicolon */
222 c = strchr(line,':');
234 for(p=value; c != s; c++){
235 if(*c!=' ' && *c!='\n'){
246 static const char *mime_headers[] = {
248 "Content-Transfer-Encoding",
249 "Content-Disposition",
256 void* sspm_default_new_part(void)
260 void sspm_default_add_line(void *part, struct sspm_header *header,
261 const char* line, size_t size)
269 void* sspm_default_end_part(void* part)
275 void sspm_default_free_part(void *part)
282 struct sspm_action_map sspm_action_map[] =
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},
287 int sspm_is_mime_header(char *line)
289 char *name = sspm_property_name(line);
296 for(i = 0; mime_headers[i] != 0; i++){
297 if(strcasecmp(name, mime_headers[i]) == 0)
304 int sspm_is_mail_header(char* line)
306 char *name = sspm_property_name(line);
316 int sspm_is_blank(char* line)
321 for(p=line; *p!=0; p++){
322 if( ! (*p == ' '|| *p == '\t' || *p=='\n') ){
335 int sspm_is_continuation_line(char* line)
337 if (line[0] == ' '|| line[0] == '\t' ) {
344 int sspm_is_mime_boundary(char *line)
346 if( line[0] == '-' && line[1] == '-') {
353 int sspm_is_mime_terminating_boundary(char *line)
357 if (sspm_is_mime_boundary(line) &&
358 strstr(line,"--\n")){
372 TERMINATING_BOUNDARY,
377 static enum line_type get_line_type(char* line){
381 } else if(sspm_is_blank(line)){
383 } else if (sspm_is_mime_header(line)){
385 } else if (sspm_is_mail_header(line)){
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)) {
401 static struct sspm_action_map get_action(struct mime_impl *impl,
402 enum sspm_major_type major,
403 enum sspm_minor_type minor)
407 /* Read caller suppled action map */
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];
420 /* Else, read default action map */
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)){
431 return sspm_action_map[i];
435 char* sspm_lowercase(char* str)
443 new = sspm_strdup(str);
444 for(p = new; *p!=0; p++){
451 enum sspm_major_type sspm_find_major_content_type(char* type)
455 char* ltype = sspm_lowercase(type);
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){
461 return major_content_type_map[i].type;
465 return major_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
468 enum sspm_minor_type sspm_find_minor_content_type(char* type)
471 char* ltype = sspm_lowercase(type);
473 char *p = strchr(ltype,'/');
477 return SSPM_UNKNOWN_MINOR_TYPE;
480 p++; /* Skip the '/' */
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){
486 return minor_content_type_map[i].type;
491 return minor_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
494 const char* sspm_major_type_string(enum sspm_major_type type)
498 for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE;
501 if(type == major_content_type_map[i].type){
502 return major_content_type_map[i].str;
506 return major_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
509 const char* sspm_minor_type_string(enum sspm_minor_type type)
512 for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE;
514 if(type == minor_content_type_map[i].type){
515 return minor_content_type_map[i].str;
519 return minor_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
523 const char* sspm_encoding_string(enum sspm_encoding type)
526 for (i=0; sspm_encoding_map[i].encoding != SSPM_UNKNOWN_ENCODING;
528 if(type == sspm_encoding_map[i].encoding){
529 return sspm_encoding_map[i].str;
533 return sspm_encoding_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
536 /* Interpret a header line and add its data to the header
538 void sspm_build_header(struct sspm_header *header, char* line)
543 val = sspm_strdup(sspm_value(line));
544 prop = sspm_strdup(sspm_property_name(line));
546 if(strcasecmp(prop,"Content-Type") == 0){
548 /* Create a new mime_header, fill in content-type
549 and possibly boundary */
551 char* boundary= sspm_get_parameter(line,"boundary");
554 header->major = sspm_find_major_content_type(val);
555 header->minor = sspm_find_minor_content_type(val);
557 if(header->minor == SSPM_UNKNOWN_MINOR_TYPE){
558 char *p = strchr(val,'/');
561 p++; /* Skip the '/' */
563 header->minor_text = sspm_strdup(p);
565 /* Error, malformed content type */
566 header->minor_text = sspm_strdup("unknown");
570 header->boundary = sspm_strdup(boundary);
573 } else if(strcasecmp(prop,"Content-Transfer-Encoding")==0){
574 char* encoding = sspm_value(line);
575 char* lencoding = sspm_lowercase(encoding);
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;
588 header->encoding = SSPM_UNKNOWN_ENCODING;
596 } else if(strcasecmp(prop,"Content-Id")==0){
597 char* cid = sspm_value(line);
598 header->content_id = sspm_strdup(cid);
606 char* sspm_get_next_line(struct mime_impl *impl)
609 s = impl->get_string(impl->temp,TMP_BUF_SIZE,impl->get_string_data);
612 impl->state = END_OF_INPUT;
618 void sspm_store_part(struct mime_impl *impl, struct sspm_header header,
619 int level, void *part, size_t size)
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;
629 void sspm_set_error(struct sspm_header* header, enum sspm_error error,
632 header->error = error;
634 if(header->error_text!=0){
635 free(header->error_text);
641 header->error_text = sspm_strdup(message);
643 header->error_text = 0;
648 void* sspm_make_part(struct mime_impl *impl,
649 struct sspm_header *header,
650 struct sspm_header *parent_header,
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 */
664 struct sspm_action_map action = get_action(
670 part =action.new_part();
672 impl->state = IN_BODY;
674 while(end == 0 && (line = sspm_get_next_line(impl)) != 0){
676 if(sspm_is_mime_boundary(line)){
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){
685 sspm_set_error(header,SSPM_UNEXPECTED_BOUNDARY_ERROR,line);
687 /* Read until the paired terminating boundary */
688 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
689 fprintf(stderr,"Out of memory");
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){
705 if(strncmp((line+2),parent_header->boundary,
706 sizeof(parent_header->boundary)) == 0){
707 *end_part = action.end_part(part);
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;
716 /* Error, this is not the correct terminating boundary*/
718 /* read and discard until we get the right boundary. */
723 "Expected: %s--. Got: %s",
724 parent_header->boundary,line);
726 sspm_set_error(parent_header,
727 SSPM_WRONG_BOUNDARY_ERROR,msg);
729 /* Read until the paired terminating boundary */
730 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
731 fprintf(stderr,"Out of memory");
734 strcpy(boundary,line);
735 strcat(boundary,"--");
736 while((line = sspm_get_next_line(impl)) != 0){
737 if(strcmp(boundary,line)==0){
747 *size = strlen(line);
749 data = (char*)malloc(*size+2);
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);
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 */
766 action.add_line(part,header,data,*size);
773 /* End the part if the input is exhausted */
774 *end_part = action.end_part(part);
781 void* sspm_make_multipart_subpart(struct mime_impl *impl,
782 struct sspm_header *parent_header)
784 struct sspm_header header;
789 if(parent_header->boundary == 0){
790 /* Error. Multipart headers must have a boundary*/
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){
801 /* Step 1: Read the opening boundary */
803 if(get_line_type(impl->temp) != BOUNDARY){
804 while((line=sspm_get_next_line(impl)) != 0 ){
805 if(sspm_is_mime_boundary(line)){
807 assert(parent_header != 0);
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))
814 /* The +2 in strncmp skips over the leading "--" */
818 /* Got the wrong boundary, so read and discard
819 until we get the right boundary. */
824 "Expected: %s. Got: %s",
825 parent_header->boundary,line);
827 sspm_set_error(parent_header,
828 SSPM_WRONG_BOUNDARY_ERROR,msg);
830 /* Read until the paired terminating boundary */
831 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
832 fprintf(stderr,"Out of memory");
835 strcpy(boundary,line);
836 strcat(boundary,"--");
837 while((line = sspm_get_next_line(impl)) != 0){
838 if(strcmp(boundary,line)==0){
850 /* Step 2: Get the part header */
851 sspm_read_header(impl,&header);
853 /* If the header is still listed as default, there was probably an
855 if(header.def == 1 && header.error != SSPM_NO_ERROR){
856 sspm_set_error(&header,SSPM_NO_HEADER_ERROR,0);
860 if(header.error!= SSPM_NO_ERROR){
861 sspm_store_part(impl,header,impl->level,0,0);
865 /* Step 3: read the body */
867 if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
868 struct sspm_header *child_header;
869 child_header = &(impl->parts[impl->part_no].header);
871 /* Store the multipart part */
872 sspm_store_part(impl,header,impl->level,0,0);
874 /* now get all of the sub-parts */
875 part = sspm_make_multipart_part(impl,child_header);
877 if(get_line_type(impl->temp) != TERMINATING_BOUNDARY){
879 sspm_set_error(child_header,SSPM_NO_BOUNDARY_ERROR,impl->temp);
883 sspm_get_next_line(impl); /* Step past the terminating boundary */
886 sspm_make_part(impl, &header,parent_header,&part,&size);
888 memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
890 sspm_store_part(impl,header,impl->level,part,size);
897 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header)
901 /* Now descend a level into each of the children of this part */
904 /* Now we are working on the CHILD */
905 memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
908 part = sspm_make_multipart_subpart(impl,header);
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;
920 } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY &&
921 impl->state != END_OF_INPUT);
929 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header)
931 #define BUF_SIZE 1024
932 #define MAX_HEADER_LINES 25
935 char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits TODO*/
936 int current_line = -1;
939 memset(header_lines,0,sizeof(header_lines));
940 memset(header,0,sizeof(struct sspm_header));
942 /* Set up default header */
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;
949 /* Read all of the lines into memory */
950 while(current_line<(MAX_HEADER_LINES-2) &&
952 ((buf=sspm_get_next_line(impl)) != 0)){
954 enum line_type line_type = get_line_type(buf);
959 impl->state = END_OF_HEADER;
965 impl->state = IN_HEADER;
968 assert(strlen(buf) < BUF_SIZE);
970 strncpy(header_lines[current_line],buf,BUF_SIZE);
971 header_lines[current_line][BUF_SIZE-1] = '\0';
976 case HEADER_CONTINUATION: {
977 char* last_line, *end;
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);
987 last_line = header_lines[current_line];
988 end = (char*) ( (size_t)strlen(last_line)+
991 impl->state = IN_HEADER;
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';
1000 while(*buf_start == ' ' ||*buf_start == '\t' ){
1004 assert( strlen(buf_start) + strlen(last_line) < BUF_SIZE);
1006 strncat(last_line,buf_start, BUF_SIZE-strlen(last_line)-1);
1012 sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
1019 for(current_line = 0;
1020 current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0;
1023 sspm_build_header(header,header_lines[current_line]);
1029 /* Root routine for parsing mime entries*/
1030 int sspm_parse_mime(struct sspm_part *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
1038 struct mime_impl impl;
1039 struct sspm_header header;
1044 /* Initialize all of the data */
1045 memset(&impl,0,sizeof(struct mime_impl));
1046 memset(&header,0,sizeof(struct sspm_header));
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;
1054 impl.max_parts = max_parts;
1056 impl.actions = actions;
1057 impl.get_string = get_string;
1058 impl.get_string_data = get_string_data;
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);
1065 if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
1066 struct sspm_header *child_header;
1067 child_header = &(impl.parts[impl.part_no].header);
1069 sspm_store_part(&impl,header,impl.level,0,0);
1071 part = sspm_make_multipart_part(&impl,child_header);
1076 sspm_make_part(&impl, &header, 0,&part,&size);
1078 memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part));
1080 sspm_store_part(&impl,header,impl.level,part,size);
1086 void sspm_free_parts(struct sspm_part *parts, size_t max_parts)
1090 for(i = 0; i<(int)max_parts && parts[i].header.major != SSPM_NO_MAJOR_TYPE;
1092 sspm_free_header(&(parts[i].header));
1096 void sspm_free_header(struct sspm_header *header)
1098 if(header->boundary!=0){
1099 free(header->boundary);
1101 if(header->minor_text!=0){
1102 free(header->minor_text);
1104 if(header->charset!=0){
1105 free(header->charset);
1107 if(header->filename!=0){
1108 free(header->filename);
1110 if(header->content_id!=0){
1111 free(header->content_id);
1113 if(header->error_text!=0){
1114 free(header->error_text);
1118 /***********************************************************************
1119 The remaining code is beased on code from the mimelite distribution,
1120 which has the following notice:
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
1130 The code is heavily modified by Eric Busboom.
1132 ***********************************************************************/
1134 char *decode_quoted_printable(char *dest,
1141 while (*src != 0 && i < *size) {
1149 /* remove soft line breaks*/
1150 if ((*src == '\n') || (*src == '\r')){
1152 if ((*src == '\n') || (*src == '\r')){
1158 cc = isdigit(*src) ? (*src - '0') : (*src - 55);
1164 cc += isdigit(*src) ? (*src - '0') : (*src - 55);
1183 char *decode_base64(char *dest,
1188 char buf[4] = {0,0,0,0};
1193 while (*src && p<(int)*size && (cc!= -1)) {
1195 /* convert a character into the Base64 alphabet */
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;
1207 /* If we've reached the end, fill the remaining slots in
1208 the bucket and do a final conversion */
1210 if(valid_data == 0){
1225 /* When we have 4 base64 letters, convert them into three
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);
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;
1247 /***********************************************************************
1249 Routines to output MIME
1251 **********************************************************************/
1254 struct sspm_buffer {
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);
1264 void sspm_append_hex(struct sspm_buffer* buf, char ch)
1268 snprintf(tmp,sizeof(tmp),"=%02X",ch);
1270 sspm_append_string(buf,tmp);
1273 /* a copy of icalmemory_append_char */
1274 void sspm_append_char(struct sspm_buffer* buf, char ch)
1279 size_t data_length, final_length;
1281 data_length = (size_t)buf->pos - (size_t)buf->buffer;
1283 final_length = data_length + 2;
1285 if ( final_length > (size_t) buf->buf_size ) {
1287 buf->buf_size = (buf->buf_size) * 2 + final_length +1;
1289 new_buf = realloc(buf->buffer,buf->buf_size);
1291 new_pos = (void*)((size_t)new_buf + data_length);
1294 buf->buffer = new_buf;
1301 /* A copy of icalmemory_append_string */
1302 void sspm_append_string(struct sspm_buffer* buf, const char* string)
1307 size_t data_length, final_length, string_length;
1309 string_length = strlen(string);
1310 data_length = (size_t)buf->pos - (size_t)buf->buffer;
1311 final_length = data_length + string_length;
1313 if ( final_length >= (size_t) buf->buf_size) {
1316 buf->buf_size = (buf->buf_size) * 2 + final_length;
1318 new_buf = realloc(buf->buffer,buf->buf_size);
1320 new_pos = (void*)((size_t)new_buf + data_length);
1323 buf->buffer = new_buf;
1326 strcpy(buf->pos, string);
1328 buf->pos += string_length;
1333 static int sspm_is_printable(char c)
1335 return (c >= 33) && (c <= 126) && (c != '=');
1340 void sspm_encode_quoted_printable(struct sspm_buffer *buf, char* data)
1345 for(p = data; *p != 0; p++){
1347 if(sspm_is_printable(*p)){
1348 /* plain characters can represent themselves */
1349 /* RFC2045 Rule #2 */
1350 sspm_append_char(buf,*p);
1352 } else if ( *p == '\t' || *p == ' ' ) {
1354 /* For tabs and spaces, only encode if they appear at the
1356 /* RFC2045 Rule #3 */
1360 if( n == '\n' || n == '\r'){
1361 sspm_append_hex(buf,*p);
1364 sspm_append_char(buf,*p);
1368 } else if( *p == '\n' || *p == '\r'){
1369 sspm_append_char(buf,*p);
1374 /* All others need to be encoded */
1375 sspm_append_hex(buf,*p);
1380 /* Add line breaks */
1383 sspm_append_string(buf,"=\n");
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','+','/'
1395 void sspm_write_base64(struct sspm_buffer *buf, char* inbuf,int size )
1401 outbuf[0] = outbuf[1] = outbuf[2] = outbuf[3] = 65;
1406 outbuf[3] = inbuf[2] & 0x3F;
1409 outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6);
1412 outbuf[0] = (inbuf[0] & 0xFC) >> 2;
1413 outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4);
1420 for(i = 0; i < 4; i++){
1422 if(outbuf[i] == 65){
1423 sspm_append_char(buf,'=');
1425 sspm_append_char(buf,BaseTable[(int)outbuf[i]]);
1430 void sspm_encode_base64(struct sspm_buffer *buf, char* data, size_t size)
1439 inbuf[0] = inbuf[1] = inbuf[2] = 0;
1441 for (p = data; *p !=0; p++){
1443 if (i%3 == 0 && first == 0){
1445 sspm_write_base64(buf, inbuf, 4);
1448 inbuf[0] = inbuf[1] = inbuf[2] = 0;
1451 assert(lpos%4 == 0);
1454 sspm_append_string(buf,"\n");
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 */
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);
1479 void sspm_write_header(struct sspm_buffer *buf,struct sspm_header *header)
1483 char temp[TMP_BUF_SIZE];
1489 major = sspm_major_type_string(header->major);
1490 minor = sspm_minor_type_string(header->minor);
1492 if(header->minor == SSPM_UNKNOWN_MINOR_TYPE ){
1493 assert(header->minor_text !=0);
1494 minor = header->minor_text;
1497 snprintf(temp,sizeof(temp),"Content-Type: %s/%s",major,minor);
1499 sspm_append_string(buf,temp);
1501 if(header->boundary != 0){
1502 snprintf(temp,sizeof(temp),";boundary=\"%s\"",header->boundary);
1503 sspm_append_string(buf,temp);
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);
1515 sspm_append_char(buf,'\n');
1517 /*Content-Transfer-Encoding */
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));
1525 sspm_append_char(buf,'\n');
1529 void sspm_write_multipart_part(struct sspm_buffer *buf,
1530 struct sspm_part *parts,
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);
1539 parent_level = parts[*part_num].level;
1543 level = parts[*part_num].level;
1545 while(parts[*part_num].header.major != SSPM_NO_MAJOR_TYPE &&
1546 level == parent_level+1){
1548 assert(header->boundary);
1549 sspm_append_string(buf,header->boundary);
1550 sspm_append_char(buf,'\n');
1552 if (parts[*part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
1553 sspm_write_multipart_part(buf,parts,part_num);
1555 sspm_write_part(buf, &(parts[*part_num]), part_num);
1559 level = parts[*part_num].level;
1562 sspm_append_string(buf,"\n\n--");
1563 sspm_append_string(buf,header->boundary);
1564 sspm_append_string(buf,"\n");
1566 (*part_num)--; /* undo last, spurious, increment */
1569 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part,int *part_num)
1574 sspm_write_header(buf,&(part->header));
1576 /* Write part data */
1578 if(part->data == 0){
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);
1588 sspm_append_string(buf,part->data);
1591 sspm_append_string(buf,"\n\n");
1594 int sspm_write_mime(struct sspm_part *parts,size_t num_parts,
1595 char **output_string, const char* header)
1597 struct sspm_buffer buf;
1602 buf.buffer = malloc(4096);
1603 buf.buffer[0] = '\0';
1604 buf.pos = buf.buffer;
1608 /* write caller's header */
1610 sspm_append_string(&buf,header);
1613 slen = strlen(buf.buffer);
1614 if(slen > 0 && buf.buffer[slen-1] != '\n'){
1615 sspm_append_char(&buf,'\n');
1618 /* write mime-version header */
1619 sspm_append_string(&buf,"Mime-Version: 1.0\n");
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);
1628 sspm_write_part(&buf, &(parts[part_num]), &part_num);
1635 *output_string = buf.buffer;