Back to index

lightning-sunbird  0.9+nobinonly
sspm.c
Go to the documentation of this file.
00001 /* -*- Mode: C -*-
00002   ======================================================================
00003   FILE: sspm.c Parse Mime
00004   CREATOR: eric 25 June 2000
00005   
00006   $Id: sspm.c,v 1.9 2005/01/24 13:15:19 acampi Exp $
00007   $Locker:  $
00008     
00009  The contents of this file are subject to the Mozilla Public License
00010  Version 1.0 (the "License"); you may not use this file except in
00011  compliance with the License. You may obtain a copy of the License at
00012  http://www.mozilla.org/MPL/
00013  
00014  Software distributed under the License is distributed on an "AS IS"
00015  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
00016  the License for the specific language governing rights and
00017  limitations under the License.
00018  
00019 
00020  This program is free software; you can redistribute it and/or modify
00021  it under the terms of either: 
00022 
00023     The LGPL as published by the Free Software Foundation, version
00024     2.1, available at: http://www.fsf.org/copyleft/lesser.html
00025 
00026   Or:
00027 
00028     The Mozilla Public License Version 1.0. You may obtain a copy of
00029     the License at http://www.mozilla.org/MPL/
00030 
00031   The Initial Developer of the Original Code is Eric Busboom
00032 
00033  (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
00034  ======================================================================*/
00035 
00036 #include <stdio.h>
00037 #include <string.h>
00038 #include "sspm.h"
00039 #include <assert.h>
00040 #include <ctype.h> /* for tolower */
00041 #include <stdlib.h>   /* for malloc, free */
00042 #include <string.h> /* for strcasecmp */
00043 
00044 #ifdef DMALLOC
00045 #include "dmalloc.h"
00046 #endif
00047 
00048 #ifdef WIN32
00049 #define snprintf      _snprintf
00050 #define strcasecmp    stricmp
00051 #endif
00052 
00053 #define TMP_BUF_SIZE 1024
00054 
00055 
00056 enum mime_state {
00057     UNKNOWN_STATE,
00058     IN_HEADER,
00059     END_OF_HEADER,
00060     IN_BODY,
00061     OPENING_PART,
00062     END_OF_PART,
00063     TERMINAL_END_OF_PART,
00064     END_OF_INPUT
00065 };
00066 
00067 struct mime_impl{
00068        struct sspm_part *parts; 
00069        size_t max_parts;
00070        int part_no;
00071        int level;
00072        struct sspm_action_map *actions;
00073        char* (*get_string)(char *s, size_t size, void* data);
00074        void* get_string_data;
00075        char temp[TMP_BUF_SIZE];
00076        enum mime_state state;
00077 };
00078 
00079 void sspm_free_header(struct sspm_header *header);
00080 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header);
00081 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header);
00082 
00083 char* sspm_strdup(char* str){
00084 
00085     char* s;
00086 
00087     s = strdup(str);
00088 
00089     return s;
00090 }
00091 
00092 
00093 static struct  major_content_type_map 
00094 {
00095        enum sspm_major_type type;
00096        char* str;
00097        
00098 } major_content_type_map[]  = 
00099 {
00100     {SSPM_MULTIPART_MAJOR_TYPE,"multipart" },
00101     {SSPM_TEXT_MAJOR_TYPE,"text" },
00102     {SSPM_TEXT_MAJOR_TYPE,"text" },
00103     {SSPM_IMAGE_MAJOR_TYPE,"image" },
00104     {SSPM_AUDIO_MAJOR_TYPE,"audio" },
00105     {SSPM_VIDEO_MAJOR_TYPE,"video" },
00106     {SSPM_APPLICATION_MAJOR_TYPE,"application" },
00107     {SSPM_MULTIPART_MAJOR_TYPE,"multipart" },
00108     {SSPM_MESSAGE_MAJOR_TYPE,"message" },
00109     {SSPM_UNKNOWN_MAJOR_TYPE,"" },
00110 };
00111 
00112 static struct  minor_content_type_map 
00113 {
00114        enum sspm_minor_type type;
00115        char* str;
00116 
00117 } minor_content_type_map[]  = 
00118 {
00119     {SSPM_ANY_MINOR_TYPE,"*" },
00120     {SSPM_PLAIN_MINOR_TYPE,"plain" },
00121     {SSPM_RFC822_MINOR_TYPE,"rfc822" },
00122     {SSPM_DIGEST_MINOR_TYPE,"digest" },
00123     {SSPM_CALENDAR_MINOR_TYPE,"calendar" },
00124     {SSPM_MIXED_MINOR_TYPE,"mixed" },
00125     {SSPM_RELATED_MINOR_TYPE,"related" },
00126     {SSPM_ALTERNATIVE_MINOR_TYPE,"alternative" },
00127     {SSPM_PARALLEL_MINOR_TYPE,  "parallel" },  
00128     {SSPM_UNKNOWN_MINOR_TYPE,"" } 
00129 };
00130 
00131 
00132 
00133 struct encoding_map {
00134        enum sspm_encoding encoding;
00135        char* str;
00136 } sspm_encoding_map[] = 
00137 {
00138     {SSPM_NO_ENCODING,""},
00139     {SSPM_QUOTED_PRINTABLE_ENCODING,"quoted-printable"},
00140     {SSPM_8BIT_ENCODING,"8bit"},
00141     {SSPM_7BIT_ENCODING,"7bit"},
00142     {SSPM_BINARY_ENCODING,"binary"},
00143     {SSPM_BASE64_ENCODING,"base64"},
00144     {SSPM_UNKNOWN_ENCODING,""}
00145 
00146 };
00147 
00148 
00149 char* sspm_get_parameter(char* line, char* parameter)
00150 {
00151     char *p,*s,*q;
00152     static char name[1024];
00153     
00154     /* Find where the parameter name is in the line */
00155     p = strstr(line,parameter);
00156 
00157     if( p == 0){
00158        return 0;
00159     }
00160 
00161     /* skip over the parameter name, the '=' and any blank spaces */
00162 
00163     p+=strlen(parameter);
00164 
00165     while(*p==' ' || *p == '='){
00166        p++;
00167     }
00168 
00169     /*now find the next semicolon*/
00170 
00171     s = strchr(p,';');
00172 
00173     /* Strip of leading quote */
00174     q = strchr(p,'\"');
00175 
00176     if(q !=0){
00177        p = q+1;
00178     }
00179 
00180     if(s != 0){
00181        strncpy(name,p,(size_t)s-(size_t)p);
00182     } else {
00183        strcpy(name,p);
00184     }
00185 
00186     /* Strip off trailing quote, if it exists */
00187 
00188     q = strrchr(name,'\"');
00189 
00190     if (q != 0){
00191        *q='\0';
00192     }
00193     
00194     return name;
00195 }
00196 
00197 char* sspm_property_name(char* line)
00198 {
00199     static char name[1024];
00200     char *c = strchr(line,':');
00201 
00202     if(c != 0){
00203        strncpy(name,line,(size_t)c-(size_t)line);
00204        name[(size_t)c-(size_t)line] = '\0';
00205        return name;
00206     } else {
00207        return 0;
00208     }
00209 }
00210 
00211 char* sspm_value(char* line)
00212 {
00213     static char value[1024];
00214 
00215     char *c,*s, *p;
00216 
00217     /* Find the first colon and the next semicolon */
00218 
00219     c = strchr(line,':');
00220     s = strchr(c,';');
00221 
00222     /* Skip the colon */
00223     c++;
00224 
00225     if (s == 0){
00226        s = c+strlen(line);
00227     }
00228 
00229     for(p=value; c != s; c++){
00230        if(*c!=' ' && *c!='\n'){
00231            *(p++) = *c;
00232        }
00233     }
00234 
00235     *p='\0';
00236 
00237     return value;
00238 
00239 }
00240 
00241 static char *mime_headers[] = {
00242     "Content-Type",
00243     "Content-Transfer-Encoding",
00244     "Content-Disposition",
00245     "Content-Id",
00246     "Mime-Version",
00247     0 
00248 };
00249 
00250 
00251 void* sspm_default_new_part()
00252 {
00253     return 0;
00254 }
00255 void sspm_default_add_line(void *part, struct sspm_header *header, 
00256                         char* line, size_t size)
00257 {
00258 }
00259 
00260 void* sspm_default_end_part(void* part)
00261 {
00262     return 0;
00263 }
00264 
00265 void sspm_default_free_part(void *part)
00266 {
00267 }
00268 
00269 
00270 
00271 struct sspm_action_map sspm_action_map[] = 
00272 {
00273     {SSPM_UNKNOWN_MAJOR_TYPE,SSPM_UNKNOWN_MINOR_TYPE,sspm_default_new_part,sspm_default_add_line,sspm_default_end_part,sspm_default_free_part},
00274 };
00275 
00276 int sspm_is_mime_header(char *line)
00277 {
00278     char *name = sspm_property_name(line);
00279     int i;
00280 
00281     if(name == 0){ 
00282        return 0;
00283     }
00284 
00285     for(i = 0; mime_headers[i] != 0; i++){
00286        if(strcasecmp(name, mime_headers[i]) == 0)
00287            return 1;
00288     }
00289     
00290     return 0;
00291 }
00292 
00293 int sspm_is_mail_header(char* line)
00294 {
00295     char *name = sspm_property_name(line);
00296 
00297     if (name != 0){
00298        return 1;
00299     }
00300 
00301     return 0;
00302 
00303 }
00304 
00305 int sspm_is_blank(char* line)
00306 {
00307     char *p;
00308     char c =0;
00309 
00310     for(p=line; *p!=0; p++){
00311        if( ! (*p == ' '|| *p == '\t' || *p=='\n') ){
00312            c++;
00313        }
00314     }
00315 
00316     if (c==0){
00317        return 1;
00318     }
00319 
00320     return 0;
00321     
00322 }
00323 
00324 int sspm_is_continuation_line(char* line)
00325 {
00326     if (line[0] == ' '|| line[0] == '\t' ) {
00327        return 1;
00328     }
00329 
00330     return 0;
00331 }
00332 
00333 int sspm_is_mime_boundary(char *line)
00334 {
00335     if( line[0] == '-' && line[1] == '-') {
00336        return 1;
00337     } 
00338 
00339     return 0;
00340 }
00341 
00342 int sspm_is_mime_terminating_boundary(char *line)
00343 {
00344 
00345 
00346     if (sspm_is_mime_boundary(line) &&
00347        strstr(line,"--\n")){
00348        return 1;
00349     } 
00350 
00351     return 0;
00352 }
00353 
00354 enum line_type {
00355     EMPTY,
00356     BLANK,
00357     MIME_HEADER,
00358     MAIL_HEADER,
00359     HEADER_CONTINUATION,
00360     BOUNDARY,
00361     TERMINATING_BOUNDARY,
00362     UNKNOWN_TYPE
00363 };
00364 
00365 
00366 static enum line_type get_line_type(char* line){
00367 
00368     if (line == 0){
00369        return EMPTY;
00370     } else if(sspm_is_blank(line)){
00371        return BLANK;
00372     } else if (sspm_is_mime_header(line)){
00373        return MIME_HEADER;
00374     } else if (sspm_is_mail_header(line)){
00375        return MAIL_HEADER;
00376     } else if (sspm_is_continuation_line(line)){
00377        return HEADER_CONTINUATION;
00378     } else if (sspm_is_mime_terminating_boundary(line)){
00379        return TERMINATING_BOUNDARY;
00380     } else if (sspm_is_mime_boundary(line)) {
00381        return BOUNDARY;
00382     } else {
00383        return UNKNOWN_TYPE;
00384     }
00385 
00386 
00387 }
00388 
00389 
00390 static struct sspm_action_map get_action(struct mime_impl *impl,
00391                               enum sspm_major_type major,
00392                               enum sspm_minor_type minor)
00393 {
00394     int i;
00395 
00396     /* Read caller suppled action map */
00397 
00398     if (impl->actions != 0){
00399        for(i=0; impl->actions[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
00400            if((major == impl->actions[i].major &&
00401               minor == impl->actions[i].minor) ||
00402               (major == impl->actions[i].major &&
00403               minor == SSPM_ANY_MINOR_TYPE)){
00404               return impl->actions[i];
00405            }
00406        }
00407     }
00408 
00409     /* Else, read default action map */
00410 
00411     for(i=0; sspm_action_map[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
00412            if((major == sspm_action_map[i].major &&
00413               minor == sspm_action_map[i].minor) ||
00414               (major == sspm_action_map[i].major &&
00415               minor == SSPM_ANY_MINOR_TYPE)){
00416            break;
00417        }
00418     }
00419     
00420     return sspm_action_map[i];
00421 }
00422 
00423 
00424 char* sspm_lowercase(char* str)
00425 {
00426     char* p = 0;
00427     char* ret;
00428 
00429     if(str ==0){
00430        return 0;
00431     }
00432 
00433     ret = sspm_strdup(str);
00434 
00435     for(p = ret; *p!=0; p++){
00436        *p = tolower(*p);
00437     }
00438 
00439     return ret;
00440 }
00441 
00442 enum sspm_major_type sspm_find_major_content_type(char* type)
00443 {
00444     int i;
00445 
00446     char* ltype = sspm_lowercase(type);
00447 
00448     for (i=0; major_content_type_map[i].type !=  SSPM_UNKNOWN_MINOR_TYPE; i++){
00449        if(strncmp(ltype, major_content_type_map[i].str,
00450                  strlen(major_content_type_map[i].str))==0){
00451            free(ltype);
00452            return major_content_type_map[i].type;
00453        }
00454     }
00455     free(ltype);
00456     return major_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
00457 }
00458 
00459 enum sspm_minor_type sspm_find_minor_content_type(char* type)
00460 {
00461     int i;
00462     char* ltype = sspm_lowercase(type);
00463 
00464     char *p = strchr(ltype,'/');
00465 
00466     if (p==0){
00467        return SSPM_UNKNOWN_MINOR_TYPE; 
00468     }
00469 
00470     p++; /* Skip the '/' */
00471 
00472     for (i=0; minor_content_type_map[i].type !=  SSPM_UNKNOWN_MINOR_TYPE; i++){
00473        if(strncmp(p, minor_content_type_map[i].str,
00474                  strlen(minor_content_type_map[i].str))==0){
00475            free(ltype);
00476            return minor_content_type_map[i].type;
00477        }
00478     }
00479     
00480     free(ltype);
00481     return minor_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
00482 }
00483 
00484 char* sspm_major_type_string(enum sspm_major_type type)
00485 {
00486     int i;
00487 
00488     for (i=0; major_content_type_map[i].type !=  SSPM_UNKNOWN_MINOR_TYPE; 
00489         i++){
00490 
00491        if(type == major_content_type_map[i].type){
00492            return major_content_type_map[i].str;
00493        }
00494     }
00495     
00496     return major_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
00497 }
00498 
00499 char* sspm_minor_type_string(enum sspm_minor_type type)
00500 {
00501     int i;
00502     for (i=0; minor_content_type_map[i].type !=  SSPM_UNKNOWN_MINOR_TYPE; 
00503         i++){
00504        if(type == minor_content_type_map[i].type){
00505            return minor_content_type_map[i].str;
00506        }
00507     }
00508     
00509     return minor_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
00510 }
00511 
00512 
00513 char* sspm_encoding_string(enum sspm_encoding type)
00514 {
00515     int i;
00516     for (i=0; sspm_encoding_map[i].encoding !=  SSPM_UNKNOWN_ENCODING; 
00517         i++){
00518        if(type == sspm_encoding_map[i].encoding){
00519            return sspm_encoding_map[i].str;
00520        }
00521     }
00522     
00523     return sspm_encoding_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
00524 }
00525 
00526 /* Interpret a header line and add its data to the header
00527    structure. */
00528 void sspm_build_header(struct sspm_header *header, char* line)
00529 {
00530     char *prop;
00531     char *val;
00532     
00533     val = sspm_strdup(sspm_value(line));
00534     prop = sspm_strdup(sspm_property_name(line));
00535 
00536     if(strcmp(prop,"Content-Type") == 0){
00537        
00538        /* Create a new mime_header, fill in content-type
00539           and possibly boundary */
00540        
00541        char* boundary= sspm_get_parameter(line,"boundary");
00542        
00543        header->def = 0;
00544        header->major = sspm_find_major_content_type(val);
00545        header->minor = sspm_find_minor_content_type(val);
00546        
00547        if(header->minor == SSPM_UNKNOWN_MINOR_TYPE){
00548            char *p = strchr(val,'/');
00549            
00550            if (p != 0){
00551               p++; /* Skip the '/' */
00552               
00553               header->minor_text = sspm_strdup(p);
00554            } else {
00555               /* Error, malformed content type */
00556               header->minor_text = sspm_strdup("unknown");
00557            }
00558        }
00559        if (boundary != 0){
00560            header->boundary = sspm_strdup(boundary);
00561        }
00562        
00563     } else if(strcmp(prop,"Content-Transfer-Encoding")==0){
00564        char* encoding = sspm_value(line);
00565        char* lencoding = sspm_lowercase(encoding);
00566 
00567        if(strcmp(lencoding,"base64")==0){
00568            header->encoding = SSPM_BASE64_ENCODING;
00569        } else        if(strcmp(lencoding,"quoted-printable")==0){
00570            header->encoding = SSPM_QUOTED_PRINTABLE_ENCODING;
00571        } else        if(strcmp(lencoding,"binary")==0){
00572            header->encoding = SSPM_BINARY_ENCODING;
00573        } else        if(strcmp(lencoding,"7bit")==0){
00574            header->encoding = SSPM_7BIT_ENCODING;
00575        } else        if(strcmp(lencoding,"8bit")==0){
00576            header->encoding = SSPM_8BIT_ENCODING;
00577        } else {
00578            header->encoding = SSPM_UNKNOWN_ENCODING;
00579        }
00580 
00581 
00582        free(lencoding);
00583 
00584        header->def = 0;
00585        
00586     } else if(strcmp(prop,"Content-Id")==0){
00587        char* cid = sspm_value(line);
00588        header->content_id = sspm_strdup(cid);
00589        header->def = 0;
00590        
00591     }
00592     free(val);
00593     free(prop);
00594 }
00595 
00596 char* sspm_get_next_line(struct mime_impl *impl)
00597 {
00598     char* s;
00599     s = impl->get_string(impl->temp,TMP_BUF_SIZE,impl->get_string_data);
00600     
00601     if(s == 0){
00602        impl->state = END_OF_INPUT;
00603     }
00604     return s;
00605 }
00606 
00607 
00608 void sspm_store_part(struct mime_impl *impl, struct sspm_header header,
00609                     int level, void *part, size_t size)
00610 {
00611     
00612     impl->parts[impl->part_no].header = header;
00613     impl->parts[impl->part_no].level = level;
00614     impl->parts[impl->part_no].data = part;  
00615     impl->parts[impl->part_no].data_size = size;  
00616     impl->part_no++;
00617 }
00618 
00619 void sspm_set_error(struct sspm_header* header, enum sspm_error error,
00620                   char* message)
00621 {
00622     header->error = error;
00623 
00624     if(header->error_text!=0){
00625        free(header->error_text);
00626     }
00627 
00628     header->def = 0;
00629 
00630     if(message != 0){
00631        header->error_text = sspm_strdup(message);  
00632     } else {
00633        header->error_text = 0;
00634     }
00635 
00636 }
00637 
00638 void* sspm_make_part(struct mime_impl *impl,
00639                    struct sspm_header *header, 
00640                    struct sspm_header *parent_header,
00641                    void **end_part,
00642                    size_t *size)
00643 {
00644 
00645     /* For a single part type, read to the boundary, if there is a
00646    boundary. Otherwise, read until the end of input.  This routine
00647    assumes that the caller has read the header and has left the input
00648    at the first blank line */
00649 
00650     char *line;
00651     void *part;
00652     int end = 0;
00653 
00654     struct sspm_action_map action = get_action(
00655        impl,
00656        header->major,
00657        header->minor);
00658 
00659     *size = 0;
00660     part =action.new_part();
00661 
00662     impl->state = IN_BODY;
00663 
00664     while(end == 0 && (line = sspm_get_next_line(impl)) != 0){
00665        
00666        if(sspm_is_mime_boundary(line)){
00667            
00668            /* If there is a boundary, then this must be a multipart
00669                part, so there must be a parent_header. */
00670            if(parent_header == 0){
00671               char* boundary;
00672               end = 1;
00673               *end_part = 0;
00674 
00675               sspm_set_error(header,SSPM_UNEXPECTED_BOUNDARY_ERROR,line);
00676 
00677               /* Read until the paired terminating boundary */
00678               if((boundary = (char*)malloc(strlen(line)+5)) == 0){
00679                   fprintf(stderr,"Out of memory");
00680                   abort();
00681               }
00682               strcpy(boundary,line);
00683               strcat(boundary,"--");
00684               while((line = sspm_get_next_line(impl)) != 0){
00685                   /*printf("Error: %s\n",line);*/
00686                   if(strcmp(boundary,line)==0){
00687                      break;
00688                   }
00689               }
00690               free(boundary);
00691 
00692               break;
00693            }
00694            
00695            if(strncmp((line+2),parent_header->boundary,
00696                      sizeof(parent_header->boundary)) == 0){
00697               *end_part = action.end_part(part);
00698 
00699               if(sspm_is_mime_boundary(line)){
00700                   impl->state = END_OF_PART;
00701               } else if ( sspm_is_mime_terminating_boundary(line)){
00702                   impl->state = TERMINAL_END_OF_PART;
00703               }
00704               end = 1;
00705            } else {
00706               /* Error, this is not the correct terminating boundary*/
00707 
00708               /* read and discard until we get the right boundary.  */
00709                   char* boundary;
00710                   char msg[256];
00711 
00712                   snprintf(msg,256,
00713                           "Expected: %s--. Got: %s",
00714                           parent_header->boundary,line);
00715 
00716                   sspm_set_error(parent_header,
00717                     SSPM_WRONG_BOUNDARY_ERROR,msg);
00718 
00719                   /* Read until the paired terminating boundary */
00720                   if((boundary = (char*)malloc(strlen(line)+5)) == 0){
00721                      fprintf(stderr,"Out of memory");
00722                      abort();
00723                   }          
00724                   strcpy(boundary,line);
00725                   strcat(boundary,"--");
00726                   while((line = sspm_get_next_line(impl)) != 0){
00727                      if(strcmp(boundary,line)==0){
00728                          break;
00729                      }
00730                   }
00731                   free(boundary);
00732 
00733            }  
00734        } else {
00735            char* data=0;
00736            char* rtrn=0;
00737            *size = strlen(line);
00738 
00739            data = (char*)malloc(*size+2);
00740            assert(data != 0);
00741            if (header->encoding == SSPM_BASE64_ENCODING){
00742               rtrn = decode_base64(data,line,size); 
00743            } else if(header->encoding == SSPM_QUOTED_PRINTABLE_ENCODING){
00744               rtrn = decode_quoted_printable(data,line,size); 
00745            } 
00746 
00747            if(rtrn == 0){
00748               strcpy(data,line);
00749            }
00750 
00751            /* add a end-of-string after the data, just in case binary
00752                data from decode64 gets passed to a tring handling
00753                routine in add_line  */
00754            data[*size+1]='\0';
00755 
00756            action.add_line(part,header,data,*size);
00757 
00758            free(data);
00759        }
00760     }
00761 
00762     if (end == 0){
00763        /* End the part if the input is exhausted */
00764        *end_part = action.end_part(part);
00765     }
00766 
00767     return end_part;
00768 }
00769 
00770 
00771 void* sspm_make_multipart_subpart(struct mime_impl *impl,
00772                          struct sspm_header *parent_header)
00773 {
00774     struct sspm_header header;
00775     char *line;
00776     void* part;
00777     size_t size;
00778 
00779     if(parent_header->boundary == 0){
00780        /* Error. Multipart headers must have a boundary*/
00781        
00782        sspm_set_error(parent_header,SSPM_NO_BOUNDARY_ERROR,0);
00783        /* read all of the reamining lines */
00784        while((line = sspm_get_next_line(impl)) != 0){
00785        }  
00786 
00787        return 0;
00788     }
00789 
00790 
00791     /* Step 1: Read the opening boundary */
00792 
00793     if(get_line_type(impl->temp) != BOUNDARY){
00794        while((line=sspm_get_next_line(impl)) != 0 ){
00795            if(sspm_is_mime_boundary(line)){
00796 
00797               assert(parent_header != 0);
00798 
00799               /* Check if it is the right boundary */
00800               if(!sspm_is_mime_terminating_boundary(line) &&
00801                  strncmp((line+2),parent_header->boundary, 
00802                         sizeof(parent_header->boundary)) 
00803                  == 0){
00804                   /* The +2 in strncmp skips over the leading "--" */
00805                   
00806                   break;
00807               } else {
00808                   /* Got the wrong boundary, so read and discard
00809                        until we get the right boundary.  */
00810                   char* boundary;
00811                   char msg[256];
00812                   
00813                   snprintf(msg,256,
00814                           "Expected: %s. Got: %s",
00815                           parent_header->boundary,line);
00816 
00817                   sspm_set_error(parent_header,
00818                                SSPM_WRONG_BOUNDARY_ERROR,msg);
00819 
00820                   /* Read until the paired terminating boundary */
00821                   if((boundary = (char*)malloc(strlen(line)+5)) == 0){
00822                      fprintf(stderr,"Out of memory");
00823                      abort();
00824                   }
00825                   strcpy(boundary,line);
00826                   strcat(boundary,"--");
00827                   while((line = sspm_get_next_line(impl)) != 0){
00828                      if(strcmp(boundary,line)==0){
00829                          break;
00830                      }
00831                   }
00832                   free(boundary);
00833                   
00834                   return 0;
00835               }
00836            }
00837        }
00838     }
00839 
00840     /* Step 2: Get the part header */
00841     sspm_read_header(impl,&header);
00842 
00843     /* If the header is still listed as default, there was probably an
00844        error */
00845     if(header.def == 1 && header.error != SSPM_NO_ERROR){
00846        sspm_set_error(&header,SSPM_NO_HEADER_ERROR,0);
00847        return 0;
00848     }
00849 
00850     if(header.error!= SSPM_NO_ERROR){
00851        sspm_store_part(impl,header,impl->level,0,0);
00852        return 0;
00853     }  
00854 
00855     /* Step 3: read the body */
00856     
00857     if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
00858        struct sspm_header *child_header;
00859        child_header = &(impl->parts[impl->part_no].header);
00860 
00861        /* Store the multipart part */
00862        sspm_store_part(impl,header,impl->level,0,0);
00863 
00864        /* now get all of the sub-parts */
00865        part = sspm_make_multipart_part(impl,child_header);
00866 
00867        if(get_line_type(impl->temp) != TERMINATING_BOUNDARY){
00868 
00869            sspm_set_error(child_header,SSPM_NO_BOUNDARY_ERROR,impl->temp);
00870            return 0;
00871        }
00872        
00873        sspm_get_next_line(impl); /* Step past the terminating boundary */
00874 
00875     } else {
00876        sspm_make_part(impl, &header,parent_header,&part,&size);
00877 
00878        memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
00879 
00880        sspm_store_part(impl,header,impl->level,part,size);
00881 
00882     }
00883 
00884     return part;
00885 }
00886 
00887 void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header)
00888 {
00889     void *part=0;
00890 
00891     /* Now descend a level into each of the children of this part */
00892     impl->level++;
00893 
00894     /* Now we are working on the CHILD */
00895     memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
00896 
00897     do{
00898        part = sspm_make_multipart_subpart(impl,header);
00899 
00900        if (part==0){
00901            /* Clean up the part in progress */
00902            impl->parts[impl->part_no].header.major 
00903               = SSPM_NO_MAJOR_TYPE;
00904            impl->parts[impl->part_no].header.minor 
00905               = SSPM_NO_MINOR_TYPE;
00906 
00907        }
00908        
00909 
00910     } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY &&
00911        impl->state != END_OF_INPUT);
00912 
00913     impl->level--;
00914 
00915     return 0;
00916 }
00917 
00918 
00919 void sspm_read_header(struct mime_impl *impl,struct sspm_header *header)
00920 {
00921 #define BUF_SIZE 1024
00922 #define MAX_HEADER_LINES 25
00923 
00924     char *buf;
00925     char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits */
00926     int current_line = -1;
00927     int end = 0;
00928 
00929     memset(header_lines,0,sizeof(header_lines));
00930     memset(header,0,sizeof(struct sspm_header));
00931 
00932     /* Set up default header */
00933     header->def = 1;
00934     header->major = SSPM_TEXT_MAJOR_TYPE;
00935     header->minor = SSPM_PLAIN_MINOR_TYPE;
00936     header->error = SSPM_NO_ERROR;
00937     header->error_text = 0;
00938 
00939     /* Read all of the lines into memory */
00940     while(end==0&& (buf=sspm_get_next_line(impl)) != 0){
00941 
00942        enum line_type line_type = get_line_type(buf);
00943        
00944        switch(line_type){
00945            case BLANK: {
00946               end = 1;
00947               impl->state = END_OF_HEADER;
00948               break;
00949            }
00950 
00951            case MAIL_HEADER:
00952            case MIME_HEADER: {         
00953               impl->state = IN_HEADER;
00954               current_line++;
00955               
00956               assert(strlen(buf) < BUF_SIZE);
00957               
00958               strncpy(header_lines[current_line],buf,BUF_SIZE-1);
00959               header_lines[current_line][BUF_SIZE-1] = '\0';
00960               
00961               break;
00962            }
00963            
00964            case HEADER_CONTINUATION: {
00965               char* last_line, *end;
00966               char *buf_start;
00967 
00968               if(current_line < 0){
00969                   /* This is not really a continuation line, since
00970                        we have not see any header line yet */
00971                   sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
00972                   return;
00973               }
00974 
00975               last_line = header_lines[current_line];
00976               end = (char*) ( (size_t)strlen(last_line)+
00977                                   (size_t)last_line);
00978               
00979               impl->state = IN_HEADER;
00980 
00981               
00982               /* skip over the spaces in buf start, and remove the new
00983                  line at the end of the lat line */
00984               if (last_line[strlen(last_line)-1] == '\n'){
00985                   last_line[strlen(last_line)-1] = '\0';
00986               }
00987               buf_start = buf;
00988               while(*buf_start == ' ' ||*buf_start == '\t' ){
00989                   buf_start++;
00990               }
00991               
00992               assert( strlen(buf_start) + strlen(last_line) < BUF_SIZE);
00993               
00994               strncat(last_line,buf_start,BUF_SIZE-strlen(last_line)-1);
00995               
00996               break;
00997            }
00998            
00999            default: {
01000               sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
01001               return;
01002            }
01003        }
01004     }
01005        
01006 
01007     for(current_line = 0;
01008        current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0;
01009        current_line++){
01010        
01011        sspm_build_header(header,header_lines[current_line]);
01012     }
01013 
01014 
01015 }
01016 
01017 /* Root routine for parsing mime entries*/
01018 int sspm_parse_mime(struct sspm_part *parts, 
01019                   size_t max_parts,
01020                   struct sspm_action_map *actions,
01021                   char* (*get_string)(char *s, size_t size, void* data),
01022                   void *get_string_data,
01023                   struct sspm_header *first_header
01024     )
01025 {
01026     struct mime_impl impl;
01027     struct sspm_header header;
01028     void *part;
01029     int i;
01030 
01031     /* Initialize all of the data */
01032     memset(&impl,0,sizeof(struct mime_impl));
01033     memset(&header,0,sizeof(struct sspm_header));
01034 
01035     for(i = 0; i<(int)max_parts; i++){
01036        parts[i].header.major = SSPM_NO_MAJOR_TYPE;
01037        parts[i].header.minor = SSPM_NO_MINOR_TYPE;
01038     }
01039        
01040     impl.parts = parts;
01041     impl.max_parts = max_parts;
01042     impl.part_no = 0;
01043     impl.actions = actions;
01044     impl.get_string = get_string;
01045     impl.get_string_data = get_string_data;
01046 
01047     /* Read the header of the message. This will be the email header,
01048        unless first_header is specified. But ( HACK) that var is not
01049        currently being used */
01050     sspm_read_header(&impl,&header);
01051 
01052     if(header.major == SSPM_MULTIPART_MAJOR_TYPE){
01053        struct sspm_header *child_header;
01054        child_header = &(impl.parts[impl.part_no].header);
01055        
01056        sspm_store_part(&impl,header,impl.level,0,0);
01057 
01058        part = sspm_make_multipart_part(&impl,child_header);
01059 
01060     } else {
01061        void *part;
01062        size_t size;
01063        sspm_make_part(&impl, &header, 0,&part,&size);
01064 
01065        memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part));
01066        
01067        sspm_store_part(&impl,header,impl.level,part,size);
01068     }
01069 
01070     return 0;
01071 }
01072 
01073 void sspm_free_parts(struct sspm_part *parts, size_t max_parts)
01074 {
01075      int i;
01076     
01077     for(i = 0; i<(int)max_parts && parts[i].header.major != SSPM_NO_MAJOR_TYPE;
01078        i++){
01079        sspm_free_header(&(parts[i].header));
01080     }
01081 }
01082 
01083 void sspm_free_header(struct sspm_header *header)
01084 {
01085     if(header->boundary!=0){
01086        free(header->boundary);
01087     }
01088     if(header->minor_text!=0){
01089        free(header->minor_text);
01090     }
01091     if(header->charset!=0){
01092        free(header->charset);
01093     }
01094     if(header->filename!=0){
01095        free(header->filename);
01096     }
01097     if(header->content_id!=0){
01098        free(header->content_id);
01099     }
01100     if(header->error_text!=0){
01101        free(header->error_text);
01102     }
01103 }
01104 
01105 /***********************************************************************
01106 The remaining code is beased on code from the mimelite distribution,
01107 which has the following notice:
01108 
01109 | Authorship:
01110 |    Copyright (c) 1994 Gisle Hannemyr.
01111 |    Permission is granted to hack, make and distribute copies of this
01112 |    program as long as this copyright notice is not removed.
01113 |    Flames, bug reports, comments and improvements to:
01114 |       snail: Gisle Hannemyr, Brageveien 3A, 0452 Oslo, Norway
01115 |       email: Inet: gisle@oslonett.no
01116 
01117 The code is heavily modified by Eric Busboom. 
01118 
01119 ***********************************************************************/
01120 
01121 char *decode_quoted_printable(char *dest, 
01122                                    char *src,
01123                                    size_t *size)
01124 {
01125     int cc;
01126     size_t i=0;
01127 
01128     while (*src != 0 && i < *size) {
01129        if (*src == '=') {
01130 
01131            src++; 
01132            if (!*src) {
01133               break;
01134            }
01135 
01136            /* remove soft line breaks*/
01137            if ((*src == '\n') || (*src == '\r')){
01138               src++;
01139               if ((*src == '\n') || (*src == '\r')){
01140                   src++;
01141               }
01142               continue;
01143            }
01144 
01145            cc  = isdigit(*src) ? (*src - '0') : (*src - 55);
01146            cc *= 0x10;
01147            src++; 
01148            if (!*src) {
01149               break;
01150            }
01151            cc += isdigit(*src) ? (*src - '0') : (*src - 55);
01152 
01153            *dest = cc;
01154 
01155        } else {
01156            *dest = *src;
01157        }
01158        
01159        dest++;
01160        src++;
01161        i++;
01162     }
01163     
01164     *dest = '\0';
01165     
01166     *size = i;
01167     return(dest);
01168 }
01169 
01170 char *decode_base64(char *dest, 
01171                           char *src,
01172                           size_t *size)
01173 {
01174     int cc = 0;
01175     char buf[4] = {0,0,0,0};  
01176     int p = 0;
01177     int valid_data = 0;
01178     size_t size_out=0;
01179     
01180     while (*src && p<(int)*size && (cc!=  -1)) {
01181        
01182        /* convert a character into the Base64 alphabet */
01183        cc = *src++;
01184        
01185        if     ((cc >= 'A') && (cc <= 'Z')) cc = cc - 'A';
01186        else if ((cc >= 'a') && (cc <= 'z')) cc = cc - 'a' + 26;
01187        else if ((cc >= '0') && (cc <= '9')) cc = cc - '0' + 52;
01188        else if  (cc == '/')             cc = 63;
01189        else if  (cc == '+')             cc = 62;
01190        else                                 cc = -1;
01191        
01192        assert(cc<64);
01193 
01194        /* If we've reached the end, fill the remaining slots in
01195           the bucket and do a final conversion */
01196        if(cc== -1){
01197            if(valid_data == 0){
01198               return 0;
01199            }
01200 
01201            while(p%4!=3){
01202               p++;
01203               buf[p%4] = 0;
01204            }
01205        } else {
01206            buf[p%4] = cc;
01207            size_out++;
01208            valid_data = 1;
01209        }
01210 
01211        
01212        /* When we have 4 base64 letters, convert them into three
01213           bytes */
01214        if (p%4 == 3) {
01215            *dest++ =(buf[0]<< 2)|((buf[1] & 0x30) >> 4);
01216            *dest++ =((buf[1] & 0x0F) << 4)|((buf[2] & 0x3C) >> 2);
01217            *dest++ =((buf[2] & 0x03) << 6)|(buf[3] & 0x3F);
01218 
01219            memset(buf,0,4);
01220        }
01221 
01222        p++;
01223 
01224     }
01225     /* Calculate the size of the converted data*/
01226    *size = ((int)(size_out/4))*3;
01227     if(size_out%4 == 2) *size+=1;
01228     if(size_out%4 == 3) *size+=2;
01229 
01230     return(dest);
01231 }
01232 
01233 
01234 /***********************************************************************
01235                                                                
01236  Routines to output MIME
01237 
01238 **********************************************************************/
01239 
01240 
01241 struct sspm_buffer {
01242        char* buffer;
01243        char* pos;
01244        size_t buf_size;
01245        int line_pos;
01246 };
01247 
01248 void sspm_append_string(struct sspm_buffer* buf, char* string);
01249 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part, int *part_num);
01250 
01251 void sspm_append_hex(struct sspm_buffer* buf, char ch)
01252 {
01253     char tmp[4];
01254 
01255     snprintf(tmp,sizeof(tmp),"=%02X",ch);
01256 
01257     sspm_append_string(buf,tmp);
01258 }
01259 
01260 /* a copy of icalmemory_append_char */
01261 void sspm_append_char(struct sspm_buffer* buf, char ch)
01262 {
01263     char *new_buf;
01264     char *new_pos;
01265 
01266     size_t data_length, final_length;
01267 
01268     data_length = (size_t)buf->pos - (size_t)buf->buffer;
01269 
01270     final_length = data_length + 2; 
01271 
01272     if ( final_length > (size_t) buf->buf_size ) {
01273        
01274        buf->buf_size  = (buf->buf_size) * 2  + final_length +1;
01275 
01276        new_buf = realloc(buf->buffer,buf->buf_size);
01277 
01278        new_pos = (void*)((size_t)new_buf + data_length);
01279        
01280        buf->pos = new_pos;
01281        buf->buffer = new_buf;
01282     }
01283 
01284     *(buf->pos) = ch;
01285     buf->pos += 1;
01286     *(buf->pos) = 0;
01287 }
01288 /* A copy of icalmemory_append_string */
01289 void sspm_append_string(struct sspm_buffer* buf, char* string)
01290 {
01291     char *new_buf;
01292     char *new_pos;
01293 
01294     size_t data_length, final_length, string_length;
01295 
01296     string_length = strlen(string);
01297     data_length = (size_t)buf->pos - (size_t)buf->buffer;    
01298     final_length = data_length + string_length; 
01299 
01300     if ( final_length >= (size_t) buf->buf_size) {
01301 
01302        
01303        buf->buf_size  = (buf->buf_size) * 2  + final_length;
01304 
01305        new_buf = realloc(buf->buffer,buf->buf_size);
01306 
01307        new_pos = (void*)((size_t)new_buf + data_length);
01308        
01309        buf->pos = new_pos;
01310        buf->buffer = new_buf;
01311     }
01312     
01313     strcpy(buf->pos, string);
01314 
01315     buf->pos += string_length;
01316 }
01317 
01318 
01319 
01320 static int sspm_is_printable(char c)
01321 {
01322     return (c >= 33) && (c <= 126) && (c != '=');
01323 
01324 } 
01325                      
01326 
01327 void sspm_encode_quoted_printable(struct sspm_buffer *buf, char* data)
01328 {
01329     char *p;
01330     int lpos = 0;
01331 
01332     for(p = data; *p != 0; p++){
01333 
01334        if(sspm_is_printable(*p)){
01335            /* plain characters can represent themselves */
01336            /* RFC2045 Rule #2 */
01337               sspm_append_char(buf,*p);
01338               lpos++;
01339        } else if ( *p == '\t' || *p == ' ' ) {
01340 
01341            /* For tabs and spaces, only encode if they appear at the
01342                end of the line */
01343            /* RFC2045 Rule #3 */
01344 
01345           char n = *(p+1);
01346 
01347           if( n == '\n' || n == '\r'){
01348               sspm_append_hex(buf,*p);
01349               lpos += 3;
01350           } else {
01351               sspm_append_char(buf,*p);
01352               lpos++;
01353           }
01354 
01355        } else if( *p == '\n' || *p == '\r'){
01356            sspm_append_char(buf,*p);
01357 
01358            lpos=0;
01359 
01360        } else {
01361            /* All others need to be encoded */
01362            sspm_append_hex(buf,*p);
01363            lpos+=3;
01364        }
01365 
01366 
01367        /* Add line breaks */
01368        if (lpos > 72){
01369            lpos = 0;
01370            sspm_append_string(buf,"=\n");
01371        }
01372     }
01373 }
01374 
01375 static char BaseTable[64] = {
01376     'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
01377     'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
01378     'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
01379     'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
01380 };
01381     
01382 void sspm_write_base64(struct sspm_buffer *buf, char* inbuf,int size )
01383 {
01384     
01385     char outbuf[4];
01386     int i;
01387 
01388     outbuf[0] = outbuf[1] = outbuf[2] = outbuf[3] = 65;
01389 
01390     switch(size){
01391        
01392        case 4:
01393            outbuf[3] =   inbuf[2] & 0x3F;
01394 
01395        case 3:
01396            outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6);
01397 
01398        case 2:       
01399            outbuf[0] =  (inbuf[0] & 0xFC) >> 2;
01400            outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4);
01401            break;
01402 
01403        default:
01404            assert(0);
01405     }
01406 
01407     for(i = 0; i < 4; i++){
01408 
01409        if(outbuf[i] == 65){
01410            sspm_append_char(buf,'=');
01411        } else {
01412            sspm_append_char(buf,BaseTable[(int)outbuf[i]]);
01413        }
01414     }
01415 }
01416              
01417 void sspm_encode_base64(struct sspm_buffer *buf, char* data, size_t size)
01418 {
01419 
01420     char *p;
01421     char inbuf[3];
01422     int i = 0;
01423     int first = 1;
01424     int lpos = 0;
01425 
01426     inbuf[0] = inbuf[1] = inbuf[2]  = 0;
01427 
01428     for (p = data; *p !=0; p++){
01429                          
01430        if (i%3 == 0 && first == 0){
01431 
01432            sspm_write_base64(buf, inbuf, 4);
01433            lpos+=4;
01434 
01435            inbuf[0] = inbuf[1] = inbuf[2] = 0;
01436        }
01437 
01438        assert(lpos%4 == 0);
01439 
01440        if (lpos == 72){
01441            sspm_append_string(buf,"\n");
01442            lpos = 0;
01443        }
01444 
01445        inbuf[i%3] = *p;
01446 
01447        i++;
01448        first = 0;
01449 
01450     }
01451 
01452     
01453     /* If the inbuf was not exactly filled on the last byte, we need
01454        to spit out the odd bytes that did get in -- either one or
01455        two. This will result in an output of two bytes and '==' or
01456        three bytes and '=', respectively */
01457     
01458     if (i%3 == 1 && first == 0){
01459            sspm_write_base64(buf, inbuf, 2);
01460     } else if (i%3 == 2 && first == 0){
01461            sspm_write_base64(buf, inbuf, 3);
01462     }
01463 
01464 }
01465 
01466 void sspm_write_header(struct sspm_buffer *buf,struct sspm_header *header)
01467 {
01468     
01469     int i;
01470     char temp[TMP_BUF_SIZE];                            
01471     char* major; 
01472     char* minor; 
01473     
01474     /* Content-type */
01475 
01476     major = sspm_major_type_string(header->major);
01477     minor = sspm_minor_type_string(header->minor);
01478 
01479     if(header->minor == SSPM_UNKNOWN_MINOR_TYPE ){
01480        assert(header->minor_text !=0);
01481        minor = header->minor_text;
01482     }
01483     
01484     snprintf(temp,sizeof(temp),"Content-Type: %s/%s",major,minor);
01485 
01486     sspm_append_string(buf,temp);
01487 
01488     if(header->boundary != 0){
01489        snprintf(temp,sizeof(temp),";boundary=\"%s\"",header->boundary);
01490        sspm_append_string(buf,temp);
01491     }
01492     
01493     /* Append any content type parameters */    
01494     if(header->content_type_params != 0){
01495        for(i=0; *(header->content_type_params[i])!= 0;i++){
01496            snprintf(temp,sizeof(temp),header->content_type_params[i]);
01497            sspm_append_char(buf,';');
01498            sspm_append_string(buf,temp);
01499        }
01500     }
01501     
01502     sspm_append_char(buf,'\n');
01503 
01504     /*Content-Transfer-Encoding */
01505 
01506     if(header->encoding != SSPM_UNKNOWN_ENCODING &&
01507        header->encoding != SSPM_NO_ENCODING){
01508        snprintf(temp,sizeof(temp),"Content-Transfer-Encoding: %s\n",
01509               sspm_encoding_string(header->encoding));
01510     }
01511 
01512     sspm_append_char(buf,'\n');
01513 
01514 }
01515 
01516 void sspm_write_multipart_part(struct sspm_buffer *buf,
01517                             struct sspm_part *parts,
01518                             int* part_num)
01519 {
01520 
01521     int parent_level, level;
01522     struct sspm_header *header = &(parts[*part_num].header);
01523     /* Write the header for the multipart part */
01524     sspm_write_header(buf,header);
01525 
01526     parent_level = parts[*part_num].level;
01527 
01528     (*part_num)++;
01529 
01530     level = parts[*part_num].level;
01531 
01532     while(parts[*part_num].header.major != SSPM_NO_MAJOR_TYPE &&
01533          level == parent_level+1){
01534 
01535        assert(header->boundary);
01536        sspm_append_string(buf,header->boundary);
01537        sspm_append_char(buf,'\n');
01538        
01539        if (parts[*part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
01540            sspm_write_multipart_part(buf,parts,part_num);
01541        } else {
01542            sspm_write_part(buf, &(parts[*part_num]), part_num);
01543        }      
01544 
01545        (*part_num)++;
01546        level =  parts[*part_num].level;
01547     }
01548    
01549     sspm_append_string(buf,"\n\n--");
01550     sspm_append_string(buf,header->boundary);
01551     sspm_append_string(buf,"\n");
01552 
01553     (*part_num)--; /* undo last, spurious, increment */
01554 }
01555 
01556 void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part,int *part_num)
01557 {
01558 
01559     /* Write header */
01560     sspm_write_header(buf,&(part->header));
01561 
01562     /* Write part data */
01563 
01564     if(part->data == 0){
01565        return;
01566     }
01567 
01568     if(part->header.encoding == SSPM_BASE64_ENCODING) {
01569        assert(part->data_size != 0);
01570        sspm_encode_base64(buf,part->data,part->data_size);
01571     } else if(part->header.encoding == SSPM_QUOTED_PRINTABLE_ENCODING) {
01572        sspm_encode_quoted_printable(buf,part->data);
01573     } else {
01574        sspm_append_string(buf,part->data);
01575     }
01576 
01577     sspm_append_string(buf,"\n\n");
01578 }
01579 
01580 int sspm_write_mime(struct sspm_part *parts,size_t num_parts,
01581                   char **output_string, char* header)
01582 {
01583     struct sspm_buffer buf;
01584     int part_num =0;
01585 
01586     buf.buffer = malloc(4096);
01587     buf.pos = buf.buffer;
01588     buf.buf_size = 10;
01589     buf.line_pos = 0;
01590 
01591     /* write caller's header */
01592     if(header != 0){
01593        sspm_append_string(&buf,header);
01594     }
01595 
01596     if(buf.buffer[strlen(buf.buffer)-1] != '\n'){
01597        sspm_append_char(&buf,'\n');
01598     }
01599 
01600     /* write mime-version header */
01601     sspm_append_string(&buf,"Mime-Version: 1.0\n");
01602 
01603     /* End of header */
01604 
01605     /* Write body parts */
01606     while(parts[part_num].header.major != SSPM_NO_MAJOR_TYPE){
01607        if (parts[part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){
01608            sspm_write_multipart_part(&buf,parts,&part_num);
01609        } else {
01610            sspm_write_part(&buf, &(parts[part_num]), &part_num);
01611        }      
01612 
01613        part_num++;
01614     }
01615 
01616 
01617     *output_string = buf.buffer;
01618 
01619     return 0;
01620 }
01621