2 ======================================================================
3 FILE: sspm.c Parse Mime
4 CREATOR: eric 25 June 2000
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, http://www.softwarestudio.org
34 ======================================================================*/
40 #include <ctype.h> /* for tolower */
41 #include <stdlib.h> /* for malloc, free */
42 #include <string.h> /* for strcasecmp */
48 #define TMP_BUF_SIZE 1024
63 struct sspm_part *parts;
67 struct sspm_action_map *actions;
68 char* (*get_string)(char *s, size_t size, void* data);
69 void* get_string_data;
70 char temp[TMP_BUF_SIZE];
71 enum mime_state state;
74 void sspm_free_header(struct sspm_header *header);
75 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header);
76 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header);
78 char* sspm_strdup(char* str){
88 struct major_content_type_map
90 enum sspm_major_type type;
93 } major_content_type_map[] =
95 {SSPM_MULTIPART_MAJOR_TYPE,"multipart" },
96 {SSPM_TEXT_MAJOR_TYPE,"text" },
97 {SSPM_TEXT_MAJOR_TYPE,"text" },
98 {SSPM_IMAGE_MAJOR_TYPE,"image" },
99 {SSPM_AUDIO_MAJOR_TYPE,"audio" },
100 {SSPM_VIDEO_MAJOR_TYPE,"video" },
101 {SSPM_APPLICATION_MAJOR_TYPE,"application" },
102 {SSPM_MULTIPART_MAJOR_TYPE,"multipart" },
103 {SSPM_MESSAGE_MAJOR_TYPE,"message" },
104 {SSPM_UNKNOWN_MAJOR_TYPE,"" },
107 struct minor_content_type_map
109 enum sspm_minor_type type;
112 } minor_content_type_map[] =
114 {SSPM_ANY_MINOR_TYPE,"*" },
115 {SSPM_PLAIN_MINOR_TYPE,"plain" },
116 {SSPM_RFC822_MINOR_TYPE,"rfc822" },
117 {SSPM_DIGEST_MINOR_TYPE,"digest" },
118 {SSPM_CALENDAR_MINOR_TYPE,"calendar" },
119 {SSPM_MIXED_MINOR_TYPE,"mixed" },
120 {SSPM_RELATED_MINOR_TYPE,"related" },
121 {SSPM_ALTERNATIVE_MINOR_TYPE,"alternative" },
122 {SSPM_PARALLEL_MINOR_TYPE, "parallel" },
123 {SSPM_UNKNOWN_MINOR_TYPE,"" }
128 struct encoding_map {
129 enum sspm_encoding encoding;
131 } sspm_encoding_map[] =
133 {SSPM_NO_ENCODING,""},
134 {SSPM_QUOTED_PRINTABLE_ENCODING,"quoted-printable"},
135 {SSPM_8BIT_ENCODING,"8bit"},
136 {SSPM_7BIT_ENCODING,"7bit"},
137 {SSPM_BINARY_ENCODING,"binary"},
138 {SSPM_BASE64_ENCODING,"base64"},
139 {SSPM_UNKNOWN_ENCODING,""}
144 char* sspm_get_parameter(char* line, char* parameter)
147 static char name[1024];
149 /* Find where the parameter name is in the line */
150 p = strstr(line,parameter);
156 /* skip over the parameter name, the '=' and any blank spaces */
158 p+=strlen(parameter);
160 while(*p==' ' || *p == '='){
164 /*now find the next semicolon*/
168 /* Strip of leading quote */
176 strncpy(name,p,(size_t)s-(size_t)p);
181 /* Strip off trailing quote, if it exists */
183 q = strrchr(name,'\"');
192 char* sspm_property_name(char* line)
194 static char name[1024];
195 char *c = strchr(line,':');
198 strncpy(name,line,(size_t)c-(size_t)line);
199 name[(size_t)c-(size_t)line] = '\0';
206 char* sspm_value(char* line)
208 static char value[1024];
212 /* Find the first colon and the next semicolon */
214 c = strchr(line,':');
224 for(p=value; c != s; c++){
225 if(*c!=' ' && *c!='\n'){
236 char *mime_headers[] = {
238 "Content-Transfer-Encoding",
239 "Content-Disposition",
246 void* sspm_default_new_part()
250 void sspm_default_add_line(void *part, struct sspm_header *header,
251 char* line, size_t size)
255 void* sspm_default_end_part(void* part)
260 void sspm_default_free_part(void *part)
266 struct sspm_action_map sspm_action_map[] =
268 {SSPM_UNKNOWN_MAJOR_TYPE,SSPM_UNKNOWN_MINOR_TYPE,sspm_default_new_part,sspm_default_add_line,sspm_default_end_part,sspm_default_free_part},
271 int sspm_is_mime_header(char *line)
273 char *name = sspm_property_name(line);
280 for(i = 0; mime_headers[i] != 0; i++){
281 if(strcasecmp(name, mime_headers[i]) == 0)
288 int sspm_is_mail_header(char* line)
290 char *name = sspm_property_name(line);
300 int sspm_is_blank(char* line)
305 for(p=line; *p!=0; p++){
306 if( ! (*p == ' '|| *p == '\t' || *p=='\n') ){
319 int sspm_is_continuation_line(char* line)
321 if (line[0] == ' '|| line[0] == '\t' ) {
328 int sspm_is_mime_boundary(char *line)
330 if( line[0] == '-' && line[1] == '-') {
337 int sspm_is_mime_terminating_boundary(char *line)
341 if (sspm_is_mime_boundary(line) &&
342 strstr(line,"--\n")){
356 TERMINATING_BOUNDARY,
361 enum line_type get_line_type(char* line){
365 } else if(sspm_is_blank(line)){
367 } else if (sspm_is_mime_header(line)){
369 } else if (sspm_is_mail_header(line)){
371 } else if (sspm_is_continuation_line(line)){
372 return HEADER_CONTINUATION;
373 } else if (sspm_is_mime_terminating_boundary(line)){
374 return TERMINATING_BOUNDARY;
375 } else if (sspm_is_mime_boundary(line)) {
385 struct sspm_action_map get_action(struct mime_impl *impl,
386 enum sspm_major_type major,
387 enum sspm_minor_type minor)
391 /* Read caller suppled action map */
393 if (impl->actions != 0){
394 for(i=0; impl->actions[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
395 if((major == impl->actions[i].major &&
396 minor == impl->actions[i].minor) ||
397 (major == impl->actions[i].major &&
398 minor == SSPM_ANY_MINOR_TYPE)){
399 return impl->actions[i];
404 /* Else, read default action map */
406 for(i=0; sspm_action_map[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
407 if((major == sspm_action_map[i].major &&
408 minor == sspm_action_map[i].minor) ||
409 (major == sspm_action_map[i].major &&
410 minor == SSPM_ANY_MINOR_TYPE)){
415 return sspm_action_map[i];
419 char* sspm_lowercase(char* str)
422 char* new = sspm_strdup(str);
428 for(p = new; *p!=0; p++){
435 enum sspm_major_type sspm_find_major_content_type(char* type)
439 char* ltype = sspm_lowercase(type);
441 for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MAJOR_TYPE; i++){
442 if(strncmp(ltype, major_content_type_map[i].str,
443 strlen(major_content_type_map[i].str))==0){
445 return major_content_type_map[i].type;
449 return major_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
452 enum sspm_minor_type sspm_find_minor_content_type(char* type)
455 char* ltype = sspm_lowercase(type);
457 char *p = strchr(ltype,'/');
460 return SSPM_UNKNOWN_MINOR_TYPE;
463 p++; /* Skip the '/' */
465 for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; i++){
466 if(strncmp(p, minor_content_type_map[i].str,
467 strlen(minor_content_type_map[i].str))==0){
469 return minor_content_type_map[i].type;
474 return minor_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
477 char* sspm_major_type_string(enum sspm_major_type type)
481 for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MAJOR_TYPE;
484 if(type == major_content_type_map[i].type){
485 return major_content_type_map[i].str;
489 return major_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
492 char* sspm_minor_type_string(enum sspm_minor_type type)
495 for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE;
497 if(type == minor_content_type_map[i].type){
498 return minor_content_type_map[i].str;
502 return minor_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
506 char* sspm_encoding_string(enum sspm_encoding type)
509 for (i=0; sspm_encoding_map[i].encoding != SSPM_UNKNOWN_ENCODING;
511 if(type == sspm_encoding_map[i].encoding){
512 return sspm_encoding_map[i].str;
516 return sspm_encoding_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
519 /* Interpret a header line and add its data to the header
521 void sspm_build_header(struct sspm_header *header, char* line)
526 val = sspm_strdup(sspm_value(line));
527 prop = sspm_strdup(sspm_property_name(line));
529 if(strcmp(prop,"Content-Type") == 0){
531 /* Create a new mime_header, fill in content-type
532 and possibly boundary */
534 char* boundary= sspm_get_parameter(line,"boundary");
537 header->major = sspm_find_major_content_type(val);
538 header->minor = sspm_find_minor_content_type(val);
540 if(header->minor == SSPM_UNKNOWN_MINOR_TYPE){
541 char *p = strchr(val,'/');
544 p++; /* Skip the '/' */
546 header->minor_text = sspm_strdup(p);
548 /* Error, malformed content type */
549 header->minor_text = sspm_strdup("unknown");
553 header->boundary = sspm_strdup(boundary);
556 } else if(strcmp(prop,"Content-Transfer-Encoding")==0){
557 char* encoding = sspm_value(line);
558 char* lencoding = sspm_lowercase(encoding);
560 if(strcmp(lencoding,"base64")==0){
561 header->encoding = SSPM_BASE64_ENCODING;
562 } else if(strcmp(lencoding,"quoted-printable")==0){
563 header->encoding = SSPM_QUOTED_PRINTABLE_ENCODING;
564 } else if(strcmp(lencoding,"binary")==0){
565 header->encoding = SSPM_BINARY_ENCODING;
566 } else if(strcmp(lencoding,"7bit")==0){
567 header->encoding = SSPM_7BIT_ENCODING;
568 } else if(strcmp(lencoding,"8bit")==0){
569 header->encoding = SSPM_8BIT_ENCODING;
571 header->encoding = SSPM_UNKNOWN_ENCODING;
579 } else if(strcmp(prop,"Content-Id")==0){
580 char* cid = sspm_value(line);
581 header->content_id = sspm_strdup(cid);
589 char* sspm_get_next_line(struct mime_impl *impl)
592 s = impl->get_string(impl->temp,TMP_BUF_SIZE,impl->get_string_data);
595 impl->state = END_OF_INPUT;
601 void sspm_store_part(struct mime_impl *impl, struct sspm_header header,
602 int level, void *part, size_t size)
605 impl->parts[impl->part_no].header = header;
606 impl->parts[impl->part_no].level = level;
607 impl->parts[impl->part_no].data = part;
608 impl->parts[impl->part_no].data_size = size;
612 void sspm_set_error(struct sspm_header* header, enum sspm_error error,
615 header->error = error;
617 if(header->error_text!=0){
618 free(header->error_text);
624 header->error_text = sspm_strdup(message);
626 header->error_text = 0;
631 void* sspm_make_part(struct mime_impl *impl,
632 struct sspm_header *header,
633 struct sspm_header *parent_header,
638 /* For a single part type, read to the boundary, if there is a
639 boundary. Otherwise, read until the end of input. This routine
640 assumes that the caller has read the header and has left the input
641 at the first blank line */
647 struct sspm_action_map action = get_action(
653 part =action.new_part();
655 impl->state = IN_BODY;
657 while(end == 0 && (line = sspm_get_next_line(impl)) != 0){
659 if(sspm_is_mime_boundary(line)){
661 /* If there is a boundary, then this must be a multipart
662 part, so there must be a parent_header. */
663 if(parent_header == 0){
668 sspm_set_error(header,SSPM_UNEXPECTED_BOUNDARY_ERROR,line);
670 /* Read until the paired terminating boundary */
671 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
672 fprintf(stderr,"Out of memory");
675 strcpy(boundary,line);
676 strcat(boundary,"--");
677 while((line = sspm_get_next_line(impl)) != 0){
678 /*printf("Error: %s\n",line);*/
679 if(strcmp(boundary,line)==0){
688 if(strcmp((line+2),parent_header->boundary) == 0){
689 *end_part = action.end_part(part);
691 if(sspm_is_mime_boundary(line)){
692 impl->state = END_OF_PART;
693 } else if ( sspm_is_mime_terminating_boundary(line)){
694 impl->state = TERMINAL_END_OF_PART;
698 /* Error, this is not the correct terminating boundary*/
700 /* read and discard until we get the right boundary. */
705 "Expected: %s--. Got: %s",
706 parent_header->boundary,line);
708 sspm_set_error(parent_header,
709 SSPM_WRONG_BOUNDARY_ERROR,msg);
711 /* Read until the paired terminating boundary */
712 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
713 fprintf(stderr,"Out of memory");
716 strcpy(boundary,line);
717 strcat(boundary,"--");
718 while((line = sspm_get_next_line(impl)) != 0){
719 if(strcmp(boundary,line)==0){
729 *size = strlen(line);
731 data = (char*)malloc(*size+2);
733 if (header->encoding == SSPM_BASE64_ENCODING){
734 rtrn = decode_base64(data,line,size);
735 } else if(header->encoding == SSPM_QUOTED_PRINTABLE_ENCODING){
736 rtrn = decode_quoted_printable(data,line,size);
743 /* add a end-of-string after the data, just in case binary
744 data from decode64 gets passed to a tring handling
745 routine in add_line */
748 action.add_line(part,header,data,*size);
755 /* End the part if the input is exhausted */
756 *end_part = action.end_part(part);
763 void* sspm_make_multipart_subpart(struct mime_impl *impl,
764 struct sspm_header *parent_header)
766 struct sspm_header header;
771 if(parent_header->boundary == 0){
772 /* Error. Multipart headers must have a boundary*/
774 sspm_set_error(parent_header,SSPM_NO_BOUNDARY_ERROR,0);
775 /* read all of the reamining lines */
776 while((line = sspm_get_next_line(impl)) != 0){
783 /* Step 1: Read the opening boundary */
785 if(get_line_type(impl->temp) != BOUNDARY){
786 while((line=sspm_get_next_line(impl)) != 0 ){
787 if(sspm_is_mime_boundary(line)){
789 assert(parent_header != 0);
791 /* Check if it is the right boundary */
792 if(!sspm_is_mime_terminating_boundary(line) &&
793 strcmp((line+2),parent_header->boundary) == 0){
794 /* The +2 in strncmp skips over the leading "--" */
798 /* Got the wrong boundary, so read and discard
799 until we get the right boundary. */
804 "Expected: %s. Got: %s",
805 parent_header->boundary,line);
807 sspm_set_error(parent_header,
808 SSPM_WRONG_BOUNDARY_ERROR,msg);
810 /* Read until the paired terminating boundary */
811 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
812 fprintf(stderr,"Out of memory");
815 strcpy(boundary,line);
816 strcat(boundary,"--");
817 while((line = sspm_get_next_line(impl)) != 0){
818 if(strcmp(boundary,line)==0){
830 /* Step 2: Get the part header */
831 sspm_read_header(impl,&header);
833 /* If the header is still listed as default, there was probably an
835 if(header.def == 1 && header.error != SSPM_NO_ERROR){
836 sspm_set_error(&header,SSPM_NO_HEADER_ERROR,0);
840 if(header.error!= SSPM_NO_ERROR){
841 sspm_store_part(impl,header,impl->level,0,0);
845 /* Step 3: read the body */
847 if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
848 struct sspm_header *child_header;
849 child_header = &(impl->parts[impl->part_no].header);
851 /* Store the multipart part */
852 sspm_store_part(impl,header,impl->level,0,0);
854 /* now get all of the sub-parts */
855 part = sspm_make_multipart_part(impl,child_header);
857 if(get_line_type(impl->temp) != TERMINATING_BOUNDARY){
859 sspm_set_error(child_header,SSPM_NO_BOUNDARY_ERROR,impl->temp);
863 sspm_get_next_line(impl); /* Step past the terminating boundary */
866 sspm_make_part(impl, &header,parent_header,&part,&size);
868 memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
870 sspm_store_part(impl,header,impl->level,part,size);
877 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header)
881 /* Now descend a level into each of the children of this part */
884 /* Now we are working on the CHILD */
885 memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
888 part = sspm_make_multipart_subpart(impl,header);
891 /* Clean up the part in progress */
892 impl->parts[impl->part_no].header.major
893 = SSPM_NO_MAJOR_TYPE;
894 impl->parts[impl->part_no].header.minor
895 = SSPM_NO_MINOR_TYPE;
900 } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY &&
901 impl->state != END_OF_INPUT);
909 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header)
911 #define BUF_SIZE 1024
912 #define MAX_HEADER_LINES 25
915 char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits */
916 int current_line = -1;
919 memset(header_lines,0,sizeof(header_lines));
920 memset(header,0,sizeof(struct sspm_header));
922 /* Set up default header */
924 header->major = SSPM_TEXT_MAJOR_TYPE;
925 header->minor = SSPM_PLAIN_MINOR_TYPE;
926 header->error = SSPM_NO_ERROR;
927 header->error_text = 0;
929 /* Read all of the lines into memory */
930 while(end==0&& (buf=sspm_get_next_line(impl)) != 0){
932 enum line_type line_type = get_line_type(buf);
937 impl->state = END_OF_HEADER;
943 impl->state = IN_HEADER;
946 assert(strlen(buf) < BUF_SIZE);
948 strcpy(header_lines[current_line],buf);
953 case HEADER_CONTINUATION: {
957 if(current_line < 0){
958 /* This is not really a continuation line, since
959 we have not see any header line yet */
960 sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
964 last_line = header_lines[current_line];
966 impl->state = IN_HEADER;
969 /* skip over the spaces in buf start, and remove the new
970 line at the end of the lat line */
971 if (last_line[strlen(last_line)-1] == '\n'){
972 last_line[strlen(last_line)-1] = '\0';
975 while(*buf_start == ' ' ||*buf_start == '\t' ){
979 assert( strlen(buf_start) + strlen(last_line) < BUF_SIZE);
981 strcat(last_line,buf_start);
987 sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
994 for(current_line = 0;
995 current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0;
998 sspm_build_header(header,header_lines[current_line]);
1004 /* Root routine for parsing mime entries*/
1005 int sspm_parse_mime(struct sspm_part *parts,
1007 struct sspm_action_map *actions,
1008 char* (*get_string)(char *s, size_t size, void* data),
1009 void *get_string_data,
1010 struct sspm_header *first_header
1013 struct mime_impl impl;
1014 struct sspm_header header;
1018 /* Initialize all of the data */
1019 memset(&impl,0,sizeof(struct mime_impl));
1020 memset(&header,0,sizeof(struct sspm_header));
1022 for(i = 0; i<(int)max_parts; i++){
1023 parts[i].header.major = SSPM_NO_MAJOR_TYPE;
1024 parts[i].header.minor = SSPM_NO_MINOR_TYPE;
1028 impl.max_parts = max_parts;
1030 impl.actions = actions;
1031 impl.get_string = get_string;
1032 impl.get_string_data = get_string_data;
1034 /* Read the header of the message. This will be the email header,
1035 unless first_header is specified. But ( HACK) that var is not
1036 currently being used */
1037 sspm_read_header(&impl,&header);
1039 if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
1040 struct sspm_header *child_header;
1041 child_header = &(impl.parts[impl.part_no].header);
1043 sspm_store_part(&impl,header,impl.level,0,0);
1045 part = sspm_make_multipart_part(&impl,child_header);
1050 sspm_make_part(&impl, &header, 0,&part,&size);
1052 memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part));
1054 sspm_store_part(&impl,header,impl.level,part,size);
1060 void sspm_free_parts(struct sspm_part *parts, size_t max_parts)
1064 for(i = 0; i<(int)max_parts && parts[i].header.major != SSPM_NO_MAJOR_TYPE;
1066 sspm_free_header(&(parts[i].header));
1070 void sspm_free_header(struct sspm_header *header)
1072 if(header->boundary!=0){
1073 free(header->boundary);
1075 if(header->minor_text!=0){
1076 free(header->minor_text);
1078 if(header->charset!=0){
1079 free(header->charset);
1081 if(header->filename!=0){
1082 free(header->filename);
1084 if(header->content_id!=0){
1085 free(header->content_id);
1087 if(header->error_text!=0){
1088 free(header->error_text);
1092 /***********************************************************************
1093 The remaining code is beased on code from the mimelite distribution,
1094 which has the following notice:
1097 | Copyright (c) 1994 Gisle Hannemyr.
1098 | Permission is granted to hack, make and distribute copies of this
1099 | program as long as this copyright notice is not removed.
1100 | Flames, bug reports, comments and improvements to:
1101 | snail: Gisle Hannemyr, Brageveien 3A, 0452 Oslo, Norway
1102 | email: Inet: gisle@oslonett.no
1104 The code is heavily modified by Eric Busboom.
1106 ***********************************************************************/
1108 char *decode_quoted_printable(char *dest,
1115 while (*src != 0 && i < *size) {
1123 /* remove soft line breaks*/
1124 if ((*src == '\n') || (*src == '\r')){
1126 if ((*src == '\n') || (*src == '\r')){
1132 cc = isdigit(*src) ? (*src - '0') : (*src - 55);
1138 cc += isdigit(*src) ? (*src - '0') : (*src - 55);
1157 char *decode_base64(char *dest,
1162 char buf[4] = {0,0,0,0};
1167 while (*src && p<(int)*size && (cc!= -1)) {
1169 /* convert a character into the Base64 alphabet */
1172 if ((cc >= 'A') && (cc <= 'Z')) cc = cc - 'A';
1173 else if ((cc >= 'a') && (cc <= 'z')) cc = cc - 'a' + 26;
1174 else if ((cc >= '0') && (cc <= '9')) cc = cc - '0' + 52;
1175 else if (cc == '/') cc = 63;
1176 else if (cc == '+') cc = 62;
1181 /* If we've reached the end, fill the remaining slots in
1182 the bucket and do a final conversion */
1184 if(valid_data == 0){
1199 /* When we have 4 base64 letters, convert them into three
1202 *dest++ =(buf[0]<< 2)|((buf[1] & 0x30) >> 4);
1203 *dest++ =((buf[1] & 0x0F) << 4)|((buf[2] & 0x3C) >> 2);
1204 *dest++ =((buf[2] & 0x03) << 6)|(buf[3] & 0x3F);
1212 /* Calculate the size of the converted data*/
1213 *size = ((int)(size_out/4))*3;
1214 if(size_out%4 == 2) *size+=1;
1215 if(size_out%4 == 3) *size+=2;
1221 /***********************************************************************
1223 Routines to output MIME
1225 **********************************************************************/
1228 struct sspm_buffer {
1235 void sspm_append_string(struct sspm_buffer* buf, char* string);
1236 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part, int *part_num);
1238 void sspm_append_hex(struct sspm_buffer* buf, char ch)
1242 sprintf(tmp,"=%02X",ch);
1244 sspm_append_string(buf,tmp);
1247 /* a copy of icalmemory_append_char */
1248 void sspm_append_char(struct sspm_buffer* buf, char ch)
1253 size_t data_length, final_length;
1255 data_length = (size_t)buf->pos - (size_t)buf->buffer;
1257 final_length = data_length + 2;
1259 if ( final_length > (size_t) buf->buf_size ) {
1261 buf->buf_size = (buf->buf_size) * 2 + final_length +1;
1263 new_buf = realloc(buf->buffer,buf->buf_size);
1265 new_pos = (void*)((size_t)new_buf + data_length);
1268 buf->buffer = new_buf;
1275 /* A copy of icalmemory_append_string */
1276 void sspm_append_string(struct sspm_buffer* buf, char* string)
1281 size_t data_length, final_length, string_length;
1283 string_length = strlen(string);
1284 data_length = (size_t)buf->pos - (size_t)buf->buffer;
1285 final_length = data_length + string_length;
1287 if ( final_length >= (size_t) buf->buf_size) {
1290 buf->buf_size = (buf->buf_size) * 2 + final_length;
1292 new_buf = realloc(buf->buffer,buf->buf_size);
1294 new_pos = (void*)((size_t)new_buf + data_length);
1297 buf->buffer = new_buf;
1300 strcpy(buf->pos, string);
1302 buf->pos += string_length;
1307 static int sspm_is_printable(char c)
1309 return (c >= 33) && (c <= 126) && (c != '=');
1314 void sspm_encode_quoted_printable(struct sspm_buffer *buf, char* data)
1319 for(p = data; *p != 0; p++){
1321 if(sspm_is_printable(*p)){
1322 /* plain characters can represent themselves */
1323 /* RFC2045 Rule #2 */
1324 sspm_append_char(buf,*p);
1326 } else if ( *p == '\t' || *p == ' ' ) {
1328 /* For tabs and spaces, only encode if they appear at the
1330 /* RFC2045 Rule #3 */
1334 if( n == '\n' || n == '\r'){
1335 sspm_append_hex(buf,*p);
1338 sspm_append_char(buf,*p);
1342 } else if( *p == '\n' || *p == '\r'){
1343 sspm_append_char(buf,*p);
1348 /* All others need to be encoded */
1349 sspm_append_hex(buf,*p);
1354 /* Add line breaks */
1357 sspm_append_string(buf,"=\n");
1362 static char BaseTable[64] = {
1363 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
1364 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
1365 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
1366 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
1369 void sspm_write_base64(struct sspm_buffer *buf, char* inbuf,int size )
1375 outbuf[0] = outbuf[1] = outbuf[2] = outbuf[3] = 65;
1380 outbuf[3] = inbuf[2] & 0x3F;
1383 outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6);
1386 outbuf[0] = (inbuf[0] & 0xFC) >> 2;
1387 outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4);
1394 for(i = 0; i < 4; i++){
1396 if(outbuf[i] == 65){
1397 sspm_append_char(buf,'=');
1399 sspm_append_char(buf,BaseTable[(int)outbuf[i]]);
1404 void sspm_encode_base64(struct sspm_buffer *buf, char* data, size_t size)
1413 inbuf[0] = inbuf[1] = inbuf[2] = 0;
1415 for (p = data; *p !=0; p++){
1417 if (i%3 == 0 && first == 0){
1419 sspm_write_base64(buf, inbuf, 4);
1422 inbuf[0] = inbuf[1] = inbuf[2] = 0;
1425 assert(lpos%4 == 0);
1428 sspm_append_string(buf,"\n");
1440 /* If the inbuf was not exactly filled on the last byte, we need
1441 to spit out the odd bytes that did get in -- either one or
1442 two. This will result in an output of two bytes and '==' or
1443 three bytes and '=', respectively */
1445 if (i%3 == 1 && first == 0){
1446 sspm_write_base64(buf, inbuf, 2);
1447 } else if (i%3 == 2 && first == 0){
1448 sspm_write_base64(buf, inbuf, 3);
1453 void sspm_write_header(struct sspm_buffer *buf,struct sspm_header *header)
1457 char temp[TMP_BUF_SIZE];
1463 major = sspm_major_type_string(header->major);
1464 minor = sspm_minor_type_string(header->minor);
1466 if(header->minor == SSPM_UNKNOWN_MINOR_TYPE ){
1467 assert(header->minor_text !=0);
1468 minor = header->minor_text;
1471 sprintf(temp,"Content-Type: %s/%s",major,minor);
1473 sspm_append_string(buf,temp);
1475 if(header->boundary != 0){
1476 sprintf(temp,";boundary=\"%s\"",header->boundary);
1477 sspm_append_string(buf,temp);
1480 /* Append any content type parameters */
1481 if(header->content_type_params != 0){
1482 for(i=0; *(header->content_type_params[i])!= 0;i++){
1483 sprintf(temp,"%s",header->content_type_params[i]);
1484 sspm_append_char(buf,';');
1485 sspm_append_string(buf,temp);
1489 sspm_append_char(buf,'\n');
1491 /*Content-Transfer-Encoding */
1493 if(header->encoding != SSPM_UNKNOWN_ENCODING &&
1494 header->encoding != SSPM_NO_ENCODING){
1495 sprintf(temp,"Content-Transfer-Encoding: %s\n",
1496 sspm_encoding_string(header->encoding));
1499 sspm_append_char(buf,'\n');
1503 void sspm_write_multipart_part(struct sspm_buffer *buf,
1504 struct sspm_part *parts,
1508 int parent_level, level;
1509 struct sspm_header *header = &(parts[*part_num].header);
1510 /* Write the header for the multipart part */
1511 sspm_write_header(buf,header);
1513 parent_level = parts[*part_num].level;
1517 level = parts[*part_num].level;
1519 while(parts[*part_num].header.major != SSPM_NO_MAJOR_TYPE &&
1520 level == parent_level+1){
1522 assert(header->boundary);
1523 sspm_append_string(buf,header->boundary);
1524 sspm_append_char(buf,'\n');
1526 if (parts[*part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
1527 sspm_write_multipart_part(buf,parts,part_num);
1529 sspm_write_part(buf, &(parts[*part_num]), part_num);
1533 level = parts[*part_num].level;
1536 sspm_append_string(buf,"\n\n--");
1537 sspm_append_string(buf,header->boundary);
1538 sspm_append_string(buf,"\n");
1540 (*part_num)--; /* undo last, spurious, increment */
1543 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part,int *part_num)
1547 sspm_write_header(buf,&(part->header));
1549 /* Write part data */
1551 if(part->data == 0){
1555 if(part->header.encoding == SSPM_BASE64_ENCODING) {
1556 assert(part->data_size != 0);
1557 sspm_encode_base64(buf,part->data,part->data_size);
1558 } else if(part->header.encoding == SSPM_QUOTED_PRINTABLE_ENCODING) {
1559 sspm_encode_quoted_printable(buf,part->data);
1561 sspm_append_string(buf,part->data);
1564 sspm_append_string(buf,"\n\n");
1567 int sspm_write_mime(struct sspm_part *parts,size_t num_parts,
1568 char **output_string, char* header)
1570 struct sspm_buffer buf;
1573 buf.buffer = malloc(4096);
1574 buf.pos = buf.buffer;
1578 /* write caller's header */
1580 sspm_append_string(&buf,header);
1583 if(buf.buffer[strlen(buf.buffer)-1] != '\n'){
1584 sspm_append_char(&buf,'\n');
1587 /* write mime-version header */
1588 sspm_append_string(&buf,"MIME-Version: 1.0\n");
1592 /* Write body parts */
1593 while(parts[part_num].header.major != SSPM_NO_MAJOR_TYPE){
1594 if (parts[part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
1595 sspm_write_multipart_part(&buf,parts,&part_num);
1597 sspm_write_part(&buf, &(parts[part_num]), &part_num);
1604 *output_string = buf.buffer;