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(strncmp((line+2),parent_header->boundary,
689 sizeof(parent_header->boundary)) == 0){
690 *end_part = action.end_part(part);
692 if(sspm_is_mime_boundary(line)){
693 impl->state = END_OF_PART;
694 } else if ( sspm_is_mime_terminating_boundary(line)){
695 impl->state = TERMINAL_END_OF_PART;
699 /* Error, this is not the correct terminating boundary*/
701 /* read and discard until we get the right boundary. */
706 "Expected: %s--. Got: %s",
707 parent_header->boundary,line);
709 sspm_set_error(parent_header,
710 SSPM_WRONG_BOUNDARY_ERROR,msg);
712 /* Read until the paired terminating boundary */
713 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
714 fprintf(stderr,"Out of memory");
717 strcpy(boundary,line);
718 strcat(boundary,"--");
719 while((line = sspm_get_next_line(impl)) != 0){
720 if(strcmp(boundary,line)==0){
730 *size = strlen(line);
732 data = (char*)malloc(*size+2);
734 if (header->encoding == SSPM_BASE64_ENCODING){
735 rtrn = decode_base64(data,line,size);
736 } else if(header->encoding == SSPM_QUOTED_PRINTABLE_ENCODING){
737 rtrn = decode_quoted_printable(data,line,size);
744 /* add a end-of-string after the data, just in case binary
745 data from decode64 gets passed to a tring handling
746 routine in add_line */
749 action.add_line(part,header,data,*size);
756 /* End the part if the input is exhausted */
757 *end_part = action.end_part(part);
764 void* sspm_make_multipart_subpart(struct mime_impl *impl,
765 struct sspm_header *parent_header)
767 struct sspm_header header;
772 if(parent_header->boundary == 0){
773 /* Error. Multipart headers must have a boundary*/
775 sspm_set_error(parent_header,SSPM_NO_BOUNDARY_ERROR,0);
776 /* read all of the reamining lines */
777 while((line = sspm_get_next_line(impl)) != 0){
784 /* Step 1: Read the opening boundary */
786 if(get_line_type(impl->temp) != BOUNDARY){
787 while((line=sspm_get_next_line(impl)) != 0 ){
788 if(sspm_is_mime_boundary(line)){
790 assert(parent_header != 0);
792 /* Check if it is the right boundary */
793 if(!sspm_is_mime_terminating_boundary(line) &&
794 strncmp((line+2),parent_header->boundary,
795 sizeof(parent_header->boundary))
797 /* The +2 in strncmp skips over the leading "--" */
801 /* Got the wrong boundary, so read and discard
802 until we get the right boundary. */
807 "Expected: %s. Got: %s",
808 parent_header->boundary,line);
810 sspm_set_error(parent_header,
811 SSPM_WRONG_BOUNDARY_ERROR,msg);
813 /* Read until the paired terminating boundary */
814 if((boundary = (char*)malloc(strlen(line)+5)) == 0){
815 fprintf(stderr,"Out of memory");
818 strcpy(boundary,line);
819 strcat(boundary,"--");
820 while((line = sspm_get_next_line(impl)) != 0){
821 if(strcmp(boundary,line)==0){
833 /* Step 2: Get the part header */
834 sspm_read_header(impl,&header);
836 /* If the header is still listed as default, there was probably an
838 if(header.def == 1 && header.error != SSPM_NO_ERROR){
839 sspm_set_error(&header,SSPM_NO_HEADER_ERROR,0);
843 if(header.error!= SSPM_NO_ERROR){
844 sspm_store_part(impl,header,impl->level,0,0);
848 /* Step 3: read the body */
850 if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
851 struct sspm_header *child_header;
852 child_header = &(impl->parts[impl->part_no].header);
854 /* Store the multipart part */
855 sspm_store_part(impl,header,impl->level,0,0);
857 /* now get all of the sub-parts */
858 part = sspm_make_multipart_part(impl,child_header);
860 if(get_line_type(impl->temp) != TERMINATING_BOUNDARY){
862 sspm_set_error(child_header,SSPM_NO_BOUNDARY_ERROR,impl->temp);
866 sspm_get_next_line(impl); /* Step past the terminating boundary */
869 sspm_make_part(impl, &header,parent_header,&part,&size);
871 memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
873 sspm_store_part(impl,header,impl->level,part,size);
880 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header)
884 /* Now descend a level into each of the children of this part */
887 /* Now we are working on the CHILD */
888 memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
891 part = sspm_make_multipart_subpart(impl,header);
894 /* Clean up the part in progress */
895 impl->parts[impl->part_no].header.major
896 = SSPM_NO_MAJOR_TYPE;
897 impl->parts[impl->part_no].header.minor
898 = SSPM_NO_MINOR_TYPE;
903 } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY &&
904 impl->state != END_OF_INPUT);
912 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header)
914 #define BUF_SIZE 1024
915 #define MAX_HEADER_LINES 25
918 char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits */
919 int current_line = -1;
922 memset(header_lines,0,sizeof(header_lines));
923 memset(header,0,sizeof(struct sspm_header));
925 /* Set up default header */
927 header->major = SSPM_TEXT_MAJOR_TYPE;
928 header->minor = SSPM_PLAIN_MINOR_TYPE;
929 header->error = SSPM_NO_ERROR;
930 header->error_text = 0;
932 /* Read all of the lines into memory */
933 while(end==0&& (buf=sspm_get_next_line(impl)) != 0){
935 enum line_type line_type = get_line_type(buf);
940 impl->state = END_OF_HEADER;
946 impl->state = IN_HEADER;
949 assert(strlen(buf) < BUF_SIZE);
951 strcpy(header_lines[current_line],buf);
956 case HEADER_CONTINUATION: {
957 char* last_line, *end;
960 if(current_line < 0){
961 /* This is not really a continuation line, since
962 we have not see any header line yet */
963 sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
967 last_line = header_lines[current_line];
968 end = (char*) ( (size_t)strlen(last_line)+
971 impl->state = IN_HEADER;
974 /* skip over the spaces in buf start, and remove the new
975 line at the end of the lat line */
976 if (last_line[strlen(last_line)-1] == '\n'){
977 last_line[strlen(last_line)-1] = '\0';
980 while(*buf_start == ' ' ||*buf_start == '\t' ){
984 assert( strlen(buf_start) + strlen(last_line) < BUF_SIZE);
986 strcat(last_line,buf_start);
992 sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
999 for(current_line = 0;
1000 current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0;
1003 sspm_build_header(header,header_lines[current_line]);
1009 /* Root routine for parsing mime entries*/
1010 int sspm_parse_mime(struct sspm_part *parts,
1012 struct sspm_action_map *actions,
1013 char* (*get_string)(char *s, size_t size, void* data),
1014 void *get_string_data,
1015 struct sspm_header *first_header
1018 struct mime_impl impl;
1019 struct sspm_header header;
1023 /* Initialize all of the data */
1024 memset(&impl,0,sizeof(struct mime_impl));
1025 memset(&header,0,sizeof(struct sspm_header));
1027 for(i = 0; i<(int)max_parts; i++){
1028 parts[i].header.major = SSPM_NO_MAJOR_TYPE;
1029 parts[i].header.minor = SSPM_NO_MINOR_TYPE;
1033 impl.max_parts = max_parts;
1035 impl.actions = actions;
1036 impl.get_string = get_string;
1037 impl.get_string_data = get_string_data;
1039 /* Read the header of the message. This will be the email header,
1040 unless first_header is specified. But ( HACK) that var is not
1041 currently being used */
1042 sspm_read_header(&impl,&header);
1044 if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
1045 struct sspm_header *child_header;
1046 child_header = &(impl.parts[impl.part_no].header);
1048 sspm_store_part(&impl,header,impl.level,0,0);
1050 part = sspm_make_multipart_part(&impl,child_header);
1055 sspm_make_part(&impl, &header, 0,&part,&size);
1057 memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part));
1059 sspm_store_part(&impl,header,impl.level,part,size);
1065 void sspm_free_parts(struct sspm_part *parts, size_t max_parts)
1069 for(i = 0; i<(int)max_parts && parts[i].header.major != SSPM_NO_MAJOR_TYPE;
1071 sspm_free_header(&(parts[i].header));
1075 void sspm_free_header(struct sspm_header *header)
1077 if(header->boundary!=0){
1078 free(header->boundary);
1080 if(header->minor_text!=0){
1081 free(header->minor_text);
1083 if(header->charset!=0){
1084 free(header->charset);
1086 if(header->filename!=0){
1087 free(header->filename);
1089 if(header->content_id!=0){
1090 free(header->content_id);
1092 if(header->error_text!=0){
1093 free(header->error_text);
1097 /***********************************************************************
1098 The remaining code is beased on code from the mimelite distribution,
1099 which has the following notice:
1102 | Copyright (c) 1994 Gisle Hannemyr.
1103 | Permission is granted to hack, make and distribute copies of this
1104 | program as long as this copyright notice is not removed.
1105 | Flames, bug reports, comments and improvements to:
1106 | snail: Gisle Hannemyr, Brageveien 3A, 0452 Oslo, Norway
1107 | email: Inet: gisle@oslonett.no
1109 The code is heavily modified by Eric Busboom.
1111 ***********************************************************************/
1113 char *decode_quoted_printable(char *dest,
1120 while (*src != 0 && i < *size) {
1128 /* remove soft line breaks*/
1129 if ((*src == '\n') || (*src == '\r')){
1131 if ((*src == '\n') || (*src == '\r')){
1137 cc = isdigit(*src) ? (*src - '0') : (*src - 55);
1143 cc += isdigit(*src) ? (*src - '0') : (*src - 55);
1162 char *decode_base64(char *dest,
1167 char buf[4] = {0,0,0,0};
1172 while (*src && p<(int)*size && (cc!= -1)) {
1174 /* convert a character into the Base64 alphabet */
1177 if ((cc >= 'A') && (cc <= 'Z')) cc = cc - 'A';
1178 else if ((cc >= 'a') && (cc <= 'z')) cc = cc - 'a' + 26;
1179 else if ((cc >= '0') && (cc <= '9')) cc = cc - '0' + 52;
1180 else if (cc == '/') cc = 63;
1181 else if (cc == '+') cc = 62;
1186 /* If we've reached the end, fill the remaining slots in
1187 the bucket and do a final conversion */
1189 if(valid_data == 0){
1204 /* When we have 4 base64 letters, convert them into three
1207 *dest++ =(buf[0]<< 2)|((buf[1] & 0x30) >> 4);
1208 *dest++ =((buf[1] & 0x0F) << 4)|((buf[2] & 0x3C) >> 2);
1209 *dest++ =((buf[2] & 0x03) << 6)|(buf[3] & 0x3F);
1217 /* Calculate the size of the converted data*/
1218 *size = ((int)(size_out/4))*3;
1219 if(size_out%4 == 2) *size+=1;
1220 if(size_out%4 == 3) *size+=2;
1226 /***********************************************************************
1228 Routines to output MIME
1230 **********************************************************************/
1233 struct sspm_buffer {
1240 void sspm_append_string(struct sspm_buffer* buf, char* string);
1241 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part, int *part_num);
1243 void sspm_append_hex(struct sspm_buffer* buf, char ch)
1247 sprintf(tmp,"=%02X",ch);
1249 sspm_append_string(buf,tmp);
1252 /* a copy of icalmemory_append_char */
1253 void sspm_append_char(struct sspm_buffer* buf, char ch)
1258 size_t data_length, final_length;
1260 data_length = (size_t)buf->pos - (size_t)buf->buffer;
1262 final_length = data_length + 2;
1264 if ( final_length > (size_t) buf->buf_size ) {
1266 buf->buf_size = (buf->buf_size) * 2 + final_length +1;
1268 new_buf = realloc(buf->buffer,buf->buf_size);
1270 new_pos = (void*)((size_t)new_buf + data_length);
1273 buf->buffer = new_buf;
1280 /* A copy of icalmemory_append_string */
1281 void sspm_append_string(struct sspm_buffer* buf, char* string)
1286 size_t data_length, final_length, string_length;
1288 string_length = strlen(string);
1289 data_length = (size_t)buf->pos - (size_t)buf->buffer;
1290 final_length = data_length + string_length;
1292 if ( final_length >= (size_t) buf->buf_size) {
1295 buf->buf_size = (buf->buf_size) * 2 + final_length;
1297 new_buf = realloc(buf->buffer,buf->buf_size);
1299 new_pos = (void*)((size_t)new_buf + data_length);
1302 buf->buffer = new_buf;
1305 strcpy(buf->pos, string);
1307 buf->pos += string_length;
1312 static int sspm_is_printable(char c)
1314 return (c >= 33) && (c <= 126) && (c != '=');
1319 void sspm_encode_quoted_printable(struct sspm_buffer *buf, char* data)
1324 for(p = data; *p != 0; p++){
1326 if(sspm_is_printable(*p)){
1327 /* plain characters can represent themselves */
1328 /* RFC2045 Rule #2 */
1329 sspm_append_char(buf,*p);
1331 } else if ( *p == '\t' || *p == ' ' ) {
1333 /* For tabs and spaces, only encode if they appear at the
1335 /* RFC2045 Rule #3 */
1339 if( n == '\n' || n == '\r'){
1340 sspm_append_hex(buf,*p);
1343 sspm_append_char(buf,*p);
1347 } else if( *p == '\n' || *p == '\r'){
1348 sspm_append_char(buf,*p);
1353 /* All others need to be encoded */
1354 sspm_append_hex(buf,*p);
1359 /* Add line breaks */
1362 sspm_append_string(buf,"=\n");
1367 static char BaseTable[64] = {
1368 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
1369 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
1370 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
1371 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
1374 void sspm_write_base64(struct sspm_buffer *buf, char* inbuf,int size )
1380 outbuf[0] = outbuf[1] = outbuf[2] = outbuf[3] = 65;
1385 outbuf[3] = inbuf[2] & 0x3F;
1388 outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6);
1391 outbuf[0] = (inbuf[0] & 0xFC) >> 2;
1392 outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4);
1399 for(i = 0; i < 4; i++){
1401 if(outbuf[i] == 65){
1402 sspm_append_char(buf,'=');
1404 sspm_append_char(buf,BaseTable[(int)outbuf[i]]);
1409 void sspm_encode_base64(struct sspm_buffer *buf, char* data, size_t size)
1418 inbuf[0] = inbuf[1] = inbuf[2] = 0;
1420 for (p = data; *p !=0; p++){
1422 if (i%3 == 0 && first == 0){
1424 sspm_write_base64(buf, inbuf, 4);
1427 inbuf[0] = inbuf[1] = inbuf[2] = 0;
1430 assert(lpos%4 == 0);
1433 sspm_append_string(buf,"\n");
1445 /* If the inbuf was not exactly filled on the last byte, we need
1446 to spit out the odd bytes that did get in -- either one or
1447 two. This will result in an output of two bytes and '==' or
1448 three bytes and '=', respectively */
1450 if (i%3 == 1 && first == 0){
1451 sspm_write_base64(buf, inbuf, 2);
1452 } else if (i%3 == 2 && first == 0){
1453 sspm_write_base64(buf, inbuf, 3);
1458 void sspm_write_header(struct sspm_buffer *buf,struct sspm_header *header)
1462 char temp[TMP_BUF_SIZE];
1468 major = sspm_major_type_string(header->major);
1469 minor = sspm_minor_type_string(header->minor);
1471 if(header->minor == SSPM_UNKNOWN_MINOR_TYPE ){
1472 assert(header->minor_text !=0);
1473 minor = header->minor_text;
1476 sprintf(temp,"Content-Type: %s/%s",major,minor);
1478 sspm_append_string(buf,temp);
1480 if(header->boundary != 0){
1481 sprintf(temp,";boundary=\"%s\"",header->boundary);
1482 sspm_append_string(buf,temp);
1485 /* Append any content type parameters */
1486 if(header->content_type_params != 0){
1487 for(i=0; *(header->content_type_params[i])!= 0;i++){
1488 sprintf(temp,"%s",header->content_type_params[i]);
1489 sspm_append_char(buf,';');
1490 sspm_append_string(buf,temp);
1494 sspm_append_char(buf,'\n');
1496 /*Content-Transfer-Encoding */
1498 if(header->encoding != SSPM_UNKNOWN_ENCODING &&
1499 header->encoding != SSPM_NO_ENCODING){
1500 sprintf(temp,"Content-Transfer-Encoding: %s\n",
1501 sspm_encoding_string(header->encoding));
1504 sspm_append_char(buf,'\n');
1508 void sspm_write_multipart_part(struct sspm_buffer *buf,
1509 struct sspm_part *parts,
1513 int parent_level, level;
1514 struct sspm_header *header = &(parts[*part_num].header);
1515 /* Write the header for the multipart part */
1516 sspm_write_header(buf,header);
1518 parent_level = parts[*part_num].level;
1522 level = parts[*part_num].level;
1524 while(parts[*part_num].header.major != SSPM_NO_MAJOR_TYPE &&
1525 level == parent_level+1){
1527 assert(header->boundary);
1528 sspm_append_string(buf,header->boundary);
1529 sspm_append_char(buf,'\n');
1531 if (parts[*part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
1532 sspm_write_multipart_part(buf,parts,part_num);
1534 sspm_write_part(buf, &(parts[*part_num]), part_num);
1538 level = parts[*part_num].level;
1541 sspm_append_string(buf,"\n\n--");
1542 sspm_append_string(buf,header->boundary);
1543 sspm_append_string(buf,"\n");
1545 (*part_num)--; /* undo last, spurious, increment */
1548 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part,int *part_num)
1552 sspm_write_header(buf,&(part->header));
1554 /* Write part data */
1556 if(part->data == 0){
1560 if(part->header.encoding == SSPM_BASE64_ENCODING) {
1561 assert(part->data_size != 0);
1562 sspm_encode_base64(buf,part->data,part->data_size);
1563 } else if(part->header.encoding == SSPM_QUOTED_PRINTABLE_ENCODING) {
1564 sspm_encode_quoted_printable(buf,part->data);
1566 sspm_append_string(buf,part->data);
1569 sspm_append_string(buf,"\n\n");
1572 int sspm_write_mime(struct sspm_part *parts,size_t num_parts,
1573 char **output_string, char* header)
1575 struct sspm_buffer buf;
1578 buf.buffer = malloc(4096);
1579 buf.pos = buf.buffer;
1583 /* write caller's header */
1585 sspm_append_string(&buf,header);
1588 if(buf.buffer[strlen(buf.buffer)-1] != '\n'){
1589 sspm_append_char(&buf,'\n');
1592 /* write mime-version header */
1593 sspm_append_string(&buf,"MIME-Version: 1.0\n");
1597 /* Write body parts */
1598 while(parts[part_num].header.major != SSPM_NO_MAJOR_TYPE){
1599 if (parts[part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
1600 sspm_write_multipart_part(&buf,parts,&part_num);
1602 sspm_write_part(&buf, &(parts[part_num]), &part_num);
1609 *output_string = buf.buffer;