src/etpan_mime.c
author Krista 'DarthMama' Bennett <krista@pep.foundation>
Wed, 08 Apr 2020 12:31:57 +0200
branchENGINE-514
changeset 4540 68bf5211fe05
parent 4537 0f441a3e8e36
child 4550 874a85ee3e59
permissions -rw-r--r--
ENGINE-514: fixing it on our side instead of etpan, as we neither need nor want recursive processing of the node
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include "etpan_mime.h"
     5 #ifndef mailmime_param_new_with_data
     6 #include <libetpan/mailprivacy_tools.h>
     7 #endif
     8 
     9 #include "pEp_internal.h"
    10 #include "platform.h"
    11 #include "mime.h"
    12 #include "wrappers.h"
    13 #include "resource_id.h"
    14 
    15 #include <string.h>
    16 #include <stdlib.h>
    17 #include <assert.h>
    18 #include <errno.h>
    19 
    20 #define MAX_MESSAGE_ID 128
    21 
    22 static char * generate_boundary(void)
    23 {
    24     char id[MAX_MESSAGE_ID];
    25 
    26     // no cryptographically strong random needed here
    27     const long value1 = random();
    28     const long value2 = random();
    29     const long value3 = random();
    30     const long value4 = random();
    31 
    32     snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
    33             value3, value4);
    34     
    35     return strdup(id);
    36 }
    37 
    38 struct mailmime * part_new_empty(
    39         struct mailmime_content * content,
    40         struct mailmime_fields * mime_fields,
    41         stringpair_list_t* param_keyvals,
    42         int force_single
    43     )
    44 {
    45     struct mailmime * build_info;
    46     clist * list = NULL;
    47     int r;
    48     int mime_type;
    49     char * attr_name = NULL;
    50     char * attr_value = NULL;
    51     struct mailmime_parameter * param = NULL;
    52     clist * parameters = NULL;
    53     char *boundary = NULL;
    54 
    55     list = NULL;
    56 
    57     if (force_single) {
    58         mime_type = MAILMIME_SINGLE;
    59     }
    60     else {
    61         switch (content->ct_type->tp_type) {
    62             case MAILMIME_TYPE_DISCRETE_TYPE:
    63                 mime_type = MAILMIME_SINGLE;
    64                 break;
    65 
    66             case MAILMIME_TYPE_COMPOSITE_TYPE:
    67                 switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
    68                     case MAILMIME_COMPOSITE_TYPE_MULTIPART:
    69                         mime_type = MAILMIME_MULTIPLE;
    70                         break;
    71 
    72                     case MAILMIME_COMPOSITE_TYPE_MESSAGE:
    73                         if (strcasecmp(content->ct_subtype, "rfc822") == 0)
    74                             mime_type = MAILMIME_MESSAGE;
    75                         else
    76                             mime_type = MAILMIME_SINGLE;
    77                         break;
    78 
    79                     default:
    80                         goto enomem;
    81                 }
    82                 break;
    83 
    84             default:
    85                 goto enomem;
    86         }
    87     }
    88 
    89     if (mime_type == MAILMIME_MULTIPLE) {
    90         list = clist_new();
    91         assert(list);
    92         if (list == NULL)
    93             goto enomem;
    94 
    95         attr_name = strdup("boundary");
    96         assert(attr_name);
    97         if (attr_name == NULL)
    98             goto enomem;
    99 
   100         boundary = generate_boundary();
   101         assert(boundary);
   102         attr_value = boundary;
   103         if (attr_value == NULL)
   104             goto enomem;
   105 
   106         param = mailmime_parameter_new(attr_name, attr_value);
   107         assert(param);
   108         if (param == NULL)
   109             goto enomem;
   110         attr_name = NULL;
   111         attr_value = NULL;
   112 
   113         if (content->ct_parameters == NULL) {
   114             parameters = clist_new();
   115             assert(parameters);
   116             if (parameters == NULL)
   117                 goto enomem;
   118         }
   119         else {
   120             parameters = content->ct_parameters;
   121         }
   122 
   123         r = clist_append(parameters, param);
   124         if (r)
   125             goto enomem;
   126         param = NULL;
   127 
   128         if (content->ct_parameters == NULL)
   129             content->ct_parameters = parameters;
   130     }
   131     
   132     if (param_keyvals) {
   133         stringpair_list_t* cur;
   134         for (cur = param_keyvals; cur; cur = cur->next) {
   135             attr_name = strdup(cur->value->key);
   136             attr_value = strdup(cur->value->value);
   137             
   138             param = mailmime_parameter_new(attr_name, attr_value);
   139             assert(param);
   140             if (param == NULL)
   141                 goto enomem;
   142                 
   143             attr_name = NULL;
   144             attr_value = NULL;
   145 
   146             if (content->ct_parameters == NULL) {
   147                 parameters = clist_new();
   148                 assert(parameters);
   149                 if (parameters == NULL)
   150                     goto enomem;
   151             }
   152             else {
   153                 parameters = content->ct_parameters;
   154             }
   155 
   156             r = clist_append(parameters, param);
   157             if (r)
   158                 goto enomem;
   159             param = NULL;
   160 
   161             if (content->ct_parameters == NULL)
   162                 content->ct_parameters = parameters;            
   163         }
   164     }
   165 
   166     build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
   167             NULL, NULL, list, NULL, NULL);
   168     if (build_info == NULL)
   169         goto enomem;
   170 
   171     return build_info;
   172 
   173 enomem:
   174     if (list)
   175         clist_free(list);
   176     free(attr_name);
   177     free(attr_value);
   178     if (content->ct_parameters == NULL)
   179         if (parameters)
   180             clist_free(parameters);
   181     if (param)
   182         mailmime_parameter_free(param);
   183     return NULL;
   184 }
   185 
   186 struct mailmime * get_pgp_encrypted_part(void)
   187 {
   188     struct mailmime * mime = NULL;
   189     struct mailmime_fields * mime_fields = NULL;
   190     struct mailmime_content * content = NULL;
   191     int r;
   192 
   193     content = mailmime_content_new_with_str("application/pgp-encrypted");
   194     if (content == NULL)
   195         goto enomem;
   196 
   197     mime_fields = mailmime_fields_new_empty();
   198     if (mime_fields == NULL)
   199         goto enomem;
   200 
   201     mime = part_new_empty(content, mime_fields, NULL, 1);
   202     if (mime == NULL)
   203         goto enomem;
   204     mime_fields = NULL;
   205     content = NULL;
   206 
   207     r = mailmime_set_body_text(mime, "Version: 1\n", 10);
   208     if (r != 0)
   209         goto enomem;
   210 
   211     return mime;
   212 
   213 enomem:
   214     if (content)
   215         mailmime_content_free(content);
   216     if (mime_fields)
   217         mailmime_fields_free(mime_fields);
   218     if (mime)
   219         mailmime_free(mime);
   220 
   221     return NULL;
   222 }
   223 
   224 struct mailmime * get_text_part(
   225         pEp_rid_list_t* resource,
   226         const char * mime_type,
   227         const char * text,
   228         size_t length,
   229         int encoding_type
   230     )
   231 {
   232     char * disposition_name = NULL;
   233     struct mailmime_fields * mime_fields = NULL;
   234     struct mailmime * mime = NULL;
   235     struct mailmime_content * content = NULL;
   236     struct mailmime_parameter * param = NULL;
   237     struct mailmime_disposition * disposition = NULL;
   238     struct mailmime_mechanism * encoding = NULL;
   239     char* content_id = NULL;
   240     int r;
   241                 
   242     if (resource != NULL && resource->rid != NULL) {
   243         switch (resource->rid_type) {
   244             case PEP_RID_CID:
   245                 content_id = strdup(resource->rid);
   246                 break;
   247             case PEP_RID_FILENAME:
   248             default:
   249                 disposition_name = strdup(resource->rid);
   250                 if (disposition_name == NULL)
   251                     goto enomem;
   252                     
   253                 disposition =
   254                         mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   255                                 disposition_name, NULL, NULL, NULL, (size_t) -1);
   256 
   257                 if (disposition == NULL)
   258                     goto enomem;
   259 
   260                 disposition_name = NULL;                
   261                 break;
   262         }    
   263     }
   264     
   265     if (encoding_type) {
   266         encoding = mailmime_mechanism_new(encoding_type, NULL);
   267         if (encoding == NULL)
   268             goto enomem;
   269     }
   270 
   271     mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
   272             disposition, NULL);
   273     if (mime_fields == NULL)
   274         goto enomem;
   275     encoding = NULL;
   276     disposition = NULL;
   277     content_id = NULL;
   278 
   279     content = mailmime_content_new_with_str(mime_type);
   280     if (content == NULL)
   281         goto enomem;
   282     
   283     if (encoding_type != MAILMIME_MECHANISM_7BIT) {
   284         param = mailmime_param_new_with_data("charset", "utf-8");
   285         r = clist_append(content->ct_parameters, param);
   286         if (r != 0)
   287             goto enomem;
   288     }
   289 
   290     mime = part_new_empty(content, mime_fields, NULL, 1);
   291     if (mime == NULL)
   292         goto enomem;
   293     content = NULL;
   294     mime_fields = NULL;
   295 
   296     if (text) {
   297         r = mailmime_set_body_text(mime, (char *) text, length);
   298         if (r != 0)
   299             goto enomem;
   300     }
   301     
   302     return mime;
   303 
   304 enomem:
   305     free(disposition_name);
   306     if (mime_fields)
   307         mailmime_fields_free(mime_fields);
   308     if (mime)
   309         mailmime_free(mime);
   310     if (content)
   311         mailmime_content_free(content);
   312     if (param)
   313         mailmime_parameter_free(param);
   314     if (disposition)
   315         mailmime_disposition_free(disposition);
   316     if (encoding)
   317         mailmime_mechanism_free(encoding);
   318 
   319     return NULL;
   320 }
   321 
   322 struct mailmime * get_file_part(
   323         pEp_rid_list_t* resource,
   324         const char * mime_type,
   325         char * data,
   326         size_t length,
   327         bool transport_encode,
   328         bool set_attachment_forward_comment
   329     )
   330 {
   331     char * disposition_name = NULL;
   332     int encoding_type;
   333     struct mailmime_disposition * disposition = NULL;
   334     struct mailmime_mechanism * encoding = NULL;
   335     struct mailmime_content * content = NULL;
   336     struct mailmime * mime = NULL;
   337     struct mailmime_fields * mime_fields = NULL;
   338     char* content_id = NULL;
   339     int r;
   340                 
   341     if (resource != NULL && resource->rid != NULL) {
   342         switch (resource->rid_type) {
   343             case PEP_RID_CID:
   344                 content_id = strdup(resource->rid);
   345                 disposition =
   346                     mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   347                                                        NULL, NULL, NULL, NULL, (size_t) -1);
   348                     if (disposition == NULL)
   349                         goto enomem;
   350                 break;
   351             case PEP_RID_FILENAME:
   352             default:
   353                 disposition_name = strdup(resource->rid);
   354                 if (disposition_name == NULL)
   355                     goto enomem;
   356                     
   357                 disposition =
   358                         mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   359                                 disposition_name, NULL, NULL, NULL, (size_t) -1);
   360                                 
   361                 if (disposition == NULL)
   362                     goto enomem;
   363                 disposition_name = NULL;
   364                 
   365                 break;
   366         }    
   367     }
   368     
   369 
   370     content = mailmime_content_new_with_str(mime_type);
   371     if (content == NULL)
   372         goto enomem;
   373 
   374     encoding = NULL;
   375 
   376     if (transport_encode) {
   377         encoding_type = MAILMIME_MECHANISM_BASE64;
   378         encoding = mailmime_mechanism_new(encoding_type, NULL);
   379         if (encoding == NULL)
   380             goto enomem;
   381     }
   382 
   383     mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
   384             disposition, NULL);
   385     if (mime_fields == NULL)
   386         goto enomem;
   387     encoding = NULL;
   388     disposition = NULL;
   389 
   390     stringpair_list_t* extra_params = NULL;
   391     
   392     if (set_attachment_forward_comment)
   393         extra_params = new_stringpair_list(new_stringpair("forwarded", "no"));
   394     
   395     mime = part_new_empty(content, mime_fields, extra_params, 1);
   396     free_stringpair_list(extra_params);
   397     if (mime == NULL)
   398         goto enomem;
   399     content = NULL;
   400     mime_fields = NULL;
   401 
   402     if(length > 0)
   403     {
   404         r = mailmime_set_body_text(mime, data, length);
   405         if (r != 0)
   406             goto enomem;
   407     }
   408 
   409     return mime;
   410 
   411 enomem:
   412     free(disposition_name);
   413     if (disposition)
   414         mailmime_disposition_free(disposition);
   415     if (encoding)
   416         mailmime_mechanism_free(encoding);
   417     if (content)
   418         mailmime_content_free(content);
   419     if (mime_fields)
   420         mailmime_fields_free(mime_fields);
   421     if (mime)
   422         mailmime_free(mime);
   423     
   424     return NULL;
   425 }
   426 
   427 struct mailmime * part_multiple_new(const char *type)
   428 {
   429     struct mailmime_fields *mime_fields = NULL;
   430     struct mailmime_content *content = NULL;
   431     struct mailmime *mp = NULL;
   432     
   433     mime_fields = mailmime_fields_new_empty();
   434     if (mime_fields == NULL)
   435         goto enomem;
   436     
   437     content = mailmime_content_new_with_str(type);
   438     if (content == NULL)
   439         goto enomem;
   440     
   441     mp = part_new_empty(content, mime_fields, NULL, 0);
   442     if (mp == NULL)
   443         goto enomem;
   444     
   445     return mp;
   446     
   447 enomem:
   448     if (content)
   449         mailmime_content_free(content);
   450     if (mime_fields)
   451         mailmime_fields_free(mime_fields);
   452 
   453     return NULL;
   454 }
   455 
   456 struct mailimf_field * _new_field(
   457         int type,
   458         _new_func_t new_func,
   459         void *value
   460     )
   461 {
   462     void *data = new_func(value);
   463     assert(data);
   464     if (data == NULL)
   465         return NULL;
   466 
   467     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   468     assert(result);
   469     if (result == NULL) {
   470         free(data);
   471         return NULL;
   472     }
   473 
   474     result->fld_type = type;
   475     result->fld_data.fld_return_path = data;
   476 
   477     return result;
   478 }
   479 
   480 void _free_field(struct mailimf_field *field)
   481 {
   482     if (field)
   483         free(field->fld_data.fld_return_path);
   484     free(field);
   485 }
   486 
   487 int _append_field(
   488         clist *list,
   489         int type,
   490         _new_func_t new_func,
   491         void *value
   492     )
   493 {
   494     int r;
   495     struct mailimf_field * field;
   496 
   497     assert(list);
   498     assert(new_func);
   499     assert(value);
   500 
   501     field = _new_field(type, new_func, value);
   502     if (field == NULL)
   503         return -1;
   504 
   505     r = clist_append(list, field);
   506     if (r)
   507         _free_field(field);
   508 
   509     return r;
   510 }
   511 
   512 // http://media2.giga.de/2014/02/Image-28.jpg
   513 
   514 struct mailimf_date_time * timestamp_to_etpantime(const struct tm *ts)
   515 {
   516     struct mailimf_date_time * result = calloc(1,
   517             sizeof(struct mailimf_date_time));
   518     assert(result);
   519     if (result == NULL)
   520         return NULL;
   521 
   522     assert(ts);
   523 
   524     result->dt_sec = ts->tm_sec;
   525     result->dt_min = ts->tm_min;
   526     result->dt_hour = ts->tm_hour;
   527     result->dt_day = ts->tm_mday;
   528     result->dt_month = ts->tm_mon + 1;
   529     result->dt_year = ts->tm_year + 1900;
   530 #ifndef WIN32
   531     result->dt_zone = (int) (ts->tm_gmtoff / 36L);
   532 #endif
   533     return result;
   534 }
   535 
   536 struct tm * etpantime_to_timestamp(const struct mailimf_date_time *et)
   537 {
   538     struct tm * result = calloc(1, sizeof(struct tm));
   539     assert(result);
   540     if (result == NULL)
   541         return NULL;
   542 
   543     assert(et);
   544 
   545     result->tm_sec = et->dt_sec;
   546     result->tm_min = et->dt_min;
   547     result->tm_hour = et->dt_hour;
   548     result->tm_mday = et->dt_day;
   549     result->tm_mon = et->dt_month - 1;
   550     result->tm_year = et->dt_year - 1900;
   551 #ifndef WIN32
   552     result->tm_gmtoff = 36L * (long) et->dt_zone;
   553 #endif
   554     return result;
   555 }
   556 
   557 struct mailimf_mailbox * mailbox_from_string(
   558         const char *name,
   559         const char *address
   560     )
   561 {
   562     struct mailimf_mailbox *mb = NULL;
   563     char *_name = NULL;
   564     char *_address = NULL;
   565 
   566     assert(address);
   567 
   568     _name = name ? strdup(name) : strdup("");
   569     if (_name == NULL)
   570         goto enomem;
   571 
   572     _address = strdup(address);
   573     if (_address == NULL)
   574         goto enomem;
   575 
   576     mb = mailimf_mailbox_new(_name, _address);
   577     assert(mb);
   578     if (mb == NULL)
   579         goto enomem;
   580 
   581     return mb;
   582 
   583 enomem:
   584     free(_name);
   585     free(_address);
   586 
   587     return NULL;
   588 }
   589 
   590 
   591 struct mailimf_field * create_optional_field(
   592         const char *field,
   593         const char *value
   594     )
   595 {
   596     char *_field = NULL;
   597     char *_value = NULL;
   598     struct mailimf_optional_field *optional_field = NULL;
   599 
   600     _field = strdup(field);
   601     if (_field == NULL)
   602         goto enomem;
   603 
   604     if (!must_field_value_be_encoded(value))
   605         _value = strdup(value);
   606     else    
   607         _value = mailmime_encode_subject_header("utf-8", value, 0);
   608     if (_value == NULL)
   609         goto enomem;
   610 
   611     optional_field = mailimf_optional_field_new(_field, _value);
   612     if (optional_field == NULL)
   613         goto enomem;
   614 
   615     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   616     assert(result);
   617     if (result == NULL)
   618         goto enomem;
   619 
   620     result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
   621     result->fld_data.fld_optional_field = optional_field;
   622 
   623     return result;
   624 
   625 enomem:
   626     if (optional_field) {
   627         mailimf_optional_field_free(optional_field);
   628     }
   629     else {
   630         free(_field);
   631         free(_value);
   632     }
   633 
   634     return NULL;
   635 }
   636 
   637 int _append_optional_field(
   638         clist *list,
   639         const char *field,
   640         const char *value
   641     )
   642 {
   643     int r;
   644     struct mailimf_field * optional_field =
   645             create_optional_field(field, value);
   646 
   647     if (optional_field == NULL)
   648         return -1;
   649 
   650     r = clist_append(list, optional_field);
   651     if (r)
   652         mailimf_field_free(optional_field);
   653 
   654     return r;
   655 }
   656 
   657 clist * _get_fields(struct mailmime * mime)
   658 {
   659     clist * _fieldlist = NULL;
   660 
   661     assert(mime);
   662 
   663     if (mime->mm_data.mm_message.mm_fields &&
   664             mime->mm_data.mm_message.mm_fields->fld_list) {
   665         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   666     }
   667 
   668     return _fieldlist;
   669 }
   670 
   671 struct mailmime_content * _get_content(struct mailmime * mime)
   672 {
   673     struct mailmime_content * content = NULL;
   674 
   675     assert(mime);
   676 
   677     if (mime->mm_data.mm_message.mm_msg_mime)
   678         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   679 
   680     return content;
   681 }
   682 
   683 
   684 /* Return a list of identifier_type and resource id (filename, cid, etc) */
   685 pEp_rid_list_t* _get_resource_id_list(struct mailmime *mime)
   686 {
   687     clist * _fieldlist = NULL;
   688 
   689     assert(mime);
   690 
   691     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   692         _fieldlist = mime->mm_mime_fields->fld_list;
   693     else
   694         return NULL;
   695 
   696     clistiter *cur;
   697 
   698     pEp_rid_list_t* rid_list = NULL; 
   699     pEp_rid_list_t** rid_list_curr_p = &rid_list; 
   700         
   701     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   702         struct mailmime_field * _field = clist_content(cur);
   703         /* content_id */
   704         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   705             pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
   706             new_rid->rid_type = PEP_RID_CID;
   707             new_rid->rid = strdup(_field->fld_data.fld_id);
   708             *rid_list_curr_p = new_rid;
   709             rid_list_curr_p = &new_rid->next;
   710         }
   711         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   712             /* filename */
   713             if (_field->fld_data.fld_disposition &&
   714                     _field->fld_data.fld_disposition->dsp_parms) {
   715                 clist * _parmlist =
   716                         _field->fld_data.fld_disposition->dsp_parms;
   717                 clistiter *cur2;
   718                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   719                         clist_next(cur2)) {
   720                     struct mailmime_disposition_parm * param =
   721                             clist_content(cur2);
   722                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   723                         pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
   724                         new_rid->rid_type = PEP_RID_FILENAME;
   725                         new_rid->rid = strdup(param->pa_data.pa_filename);
   726                         *rid_list_curr_p = new_rid;
   727                         rid_list_curr_p = &new_rid->next;
   728                     }                
   729                 }
   730             }
   731         }
   732     }
   733     /* Will almost certainly usually be a singleton, but we need to be able to decide */
   734     return rid_list;
   735 }
   736 
   737 
   738 /* FIXME: about to be obsoleted? */
   739 char * _get_filename_or_cid(struct mailmime *mime)
   740 {
   741     clist * _fieldlist = NULL;
   742 
   743     assert(mime);
   744 
   745     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   746         _fieldlist = mime->mm_mime_fields->fld_list;
   747     else
   748         return NULL;
   749 
   750     clistiter *cur;
   751     
   752     char* _temp_filename_ptr = NULL;
   753     
   754     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   755         struct mailmime_field * _field = clist_content(cur);
   756         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   757             /* We prefer CIDs to filenames when both are present */
   758             free(_temp_filename_ptr); /* can be null, it's ok */
   759             return build_uri("cid", _field->fld_data.fld_id); 
   760         }
   761         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   762             if (_field->fld_data.fld_disposition &&
   763                     _field->fld_data.fld_disposition->dsp_parms &&
   764                     !_temp_filename_ptr) {
   765                 clist * _parmlist =
   766                         _field->fld_data.fld_disposition->dsp_parms;
   767                 clistiter *cur2;
   768                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   769                         clist_next(cur2)) {
   770                     struct mailmime_disposition_parm * param =
   771                             clist_content(cur2);
   772                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   773                         _temp_filename_ptr = build_uri("file", param->pa_data.pa_filename);
   774                         break;
   775                     }                
   776                 }
   777             }
   778         }
   779     }
   780     /* Ok, it wasn't a CID */
   781     return _temp_filename_ptr;
   782 }
   783 
   784 static bool parameter_has_value(
   785         struct mailmime_content *content,       
   786         const char *name,
   787         const char *value
   788     )
   789 {
   790     clistiter *cur;
   791 
   792     assert(name);
   793     assert(value);
   794 
   795     clist * list = content->ct_parameters;
   796     if (list == NULL)
   797         return false;
   798 
   799     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   800         struct mailmime_parameter * param = clist_content(cur);
   801         if (param &&
   802                 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
   803                 param->pa_value && strcasecmp(value, param->pa_value) == 0)
   804             return true;
   805     }
   806 
   807     return false;
   808 }
   809 
   810 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   811 {
   812     assert(content);
   813 
   814     if (content->ct_type && content->ct_type->tp_type ==
   815             MAILMIME_TYPE_COMPOSITE_TYPE &&
   816             content->ct_type->tp_data.tp_composite_type &&
   817             content->ct_type->tp_data.tp_composite_type->ct_type ==
   818             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   819         if (subtype)
   820             return content->ct_subtype &&
   821                     strcasecmp(content->ct_subtype, subtype) == 0;
   822         else
   823             return true;
   824     }
   825 
   826     return false;
   827 }
   828 
   829 bool _is_PGP_MIME(struct mailmime_content *content)
   830 {
   831     assert(content);
   832 
   833     if (_is_multipart(content, "encrypted") &&
   834             parameter_has_value(content, "protocol",
   835                     "application/pgp-encrypted"))
   836         return true;
   837 
   838     return false;
   839 }
   840 
   841 bool _is_text_part(struct mailmime_content *content, const char *subtype)
   842 {
   843     assert(content);
   844 
   845     if (content->ct_type && content->ct_type->tp_type ==
   846             MAILMIME_TYPE_DISCRETE_TYPE &&
   847             content->ct_type->tp_data.tp_discrete_type &&
   848             content->ct_type->tp_data.tp_discrete_type->dt_type ==
   849             MAILMIME_DISCRETE_TYPE_TEXT) {
   850         if (subtype)
   851             return content->ct_subtype &&
   852                     strcasecmp(content->ct_subtype, subtype) == 0;
   853         else
   854             return true;
   855     }
   856 
   857     return false;
   858 }
   859 
   860 bool _is_message_part(struct mailmime_content *content, const char* subtype) {
   861     assert(content);
   862     if (content->ct_type && content->ct_type->tp_type == MAILMIME_TYPE_COMPOSITE_TYPE &&
   863             content->ct_type->tp_data.tp_composite_type &&
   864             content->ct_type->tp_data.tp_composite_type->ct_type ==
   865             MAILMIME_COMPOSITE_TYPE_MESSAGE) {
   866         if (subtype)
   867             return content->ct_subtype &&
   868                     strcasecmp(content->ct_subtype, subtype) == 0;
   869         else
   870             return true;                
   871     }
   872     
   873     return false;
   874 }
   875 
   876 int _get_content_type(
   877         const struct mailmime_content *content,
   878         char **type,
   879         char **charset
   880     )
   881 {
   882     char *_type = NULL;
   883     char *_charset = NULL;
   884 
   885     assert(content);
   886     assert(type);
   887     assert(charset);
   888 
   889     *type = NULL;
   890     *charset = NULL;
   891 
   892     if (content->ct_subtype == NULL)
   893         return EINVAL;
   894 
   895     if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
   896         size_t len;
   897         const char *_main_type;
   898 
   899         switch  (content->ct_type->tp_data.tp_discrete_type->dt_type) {
   900             case MAILMIME_DISCRETE_TYPE_TEXT:
   901                 _main_type = (content->ct_subtype && 
   902                               strcasecmp(content->ct_subtype, "rfc822") == 0 ?
   903                               "message" : "text");
   904                 break;
   905             case MAILMIME_DISCRETE_TYPE_IMAGE:
   906                 _main_type = "image";
   907                 break;
   908             case MAILMIME_DISCRETE_TYPE_AUDIO:
   909                 _main_type = "audio";
   910                 break;
   911             case MAILMIME_DISCRETE_TYPE_VIDEO:
   912                 _main_type = "video";
   913                 break;
   914             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   915                 _main_type = "application";
   916                 break;
   917             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   918                 _main_type = "extension";
   919                 break;
   920             default:
   921                 return EINVAL;
   922         }
   923 
   924         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   925         _type = calloc(1, len);
   926         assert(_type);
   927         if (_type == NULL)
   928             return ENOMEM;
   929 
   930         strncpy(_type, _main_type, len);
   931         len -= strlen(_main_type);
   932         strncat(_type, "/", len--);
   933         strncat(_type, content->ct_subtype, len);
   934 
   935         if (content->ct_parameters) {
   936             clistiter *cur;
   937             for (cur = clist_begin(content->ct_parameters); cur; cur =
   938                     clist_next(cur)) {
   939                 struct mailmime_parameter * param = clist_content(cur);
   940                 if (param && param->pa_name && strcasecmp(param->pa_name,
   941                             "charset") == 0) {
   942                     _charset = param->pa_value;
   943                     break;
   944                 }
   945             }
   946             if (_charset)
   947                 *charset = strdup(_charset);
   948         }
   949 
   950         *type = _type;
   951         return 0;
   952     }
   953 
   954     return EINVAL;
   955 }
   956 
   957 // Only for null-terminated field strings.
   958 // can this field be transported as is without modification?)
   959 // (See rfc2822, section 2.2.3 - libetpan's handling isn't quite what
   960 // we need here.)
   961 bool must_field_value_be_encoded(const char* field_value) {
   962     if (!field_value)
   963         return false;
   964         
   965     return must_chunk_be_encoded((const void*)field_value, strlen(field_value), false);    
   966 }
   967 
   968 bool must_chunk_be_encoded(const void* value, size_t size, bool ignore_fws) {
   969 
   970     const char* begin_ptr = (const char*)value;    
   971 
   972     const char* end_ptr = begin_ptr + size;
   973 
   974     const char* cur_char_ptr = begin_ptr;
   975     while (cur_char_ptr < end_ptr) {
   976         char cur_char = *cur_char_ptr;
   977         if (cur_char > 127 || cur_char < 0)
   978             return true;
   979         // FIXME - do we need to deal with CRCRLF here?
   980         //         I guess in the worst case, it gets encoded, which
   981         //         is *supposed* to be harmless...
   982         if (!ignore_fws) {
   983             if (cur_char == '\r') {
   984                 const char* next = cur_char_ptr + 1;
   985                 const char* nextnext = next + 1;
   986                 if (next >= end_ptr || nextnext >= end_ptr
   987                     || *next != '\n'
   988                     || (*nextnext != ' ' && *nextnext != '\t')) {
   989                     return true;
   990                 }            
   991             }
   992             else if (cur_char == '\n') {
   993                 const char* prev = cur_char_ptr - 1;
   994                 if (prev == begin_ptr || *prev != '\r')
   995                     return true;
   996             }
   997         }
   998         cur_char_ptr++;
   999     }    
  1000     return false;
  1001 }
  1002 
  1003 #define TMP_TEMPLATE "pEp.XXXXXXXXXXXXXXXXXXXX"
  1004 #ifdef _WIN32
  1005 #define PATH_SEP '\\'
  1006 #else
  1007 #define PATH_SEP '/'
  1008 #endif
  1009 
  1010 static PEP_STATUS interpret_MIME(struct mailmime *mime,
  1011                                  message *msg,
  1012                                  bool* raise_msg_attachment);
  1013 
  1014 // This function was rewritten to use in-memory buffers instead of
  1015 // temporary files when the pgp/mime support was implemented for
  1016 // outlook, as the existing code did not work well on windows.
  1017 
  1018 static PEP_STATUS render_mime(struct mailmime *mime, char **mimetext)
  1019 {
  1020     PEP_STATUS status = PEP_STATUS_OK;
  1021     int col;
  1022     int r;
  1023 	size_t len;
  1024 	char* buf = NULL;
  1025 
  1026 	MMAPString* buffer;
  1027 
  1028 	buffer = mmap_string_new(NULL);
  1029 	if (buffer == NULL)
  1030 		goto enomem;
  1031 	
  1032 	col = 0;
  1033 	r = mailmime_write_mem(buffer, &col, mime);
  1034 	assert(r == MAILIMF_NO_ERROR);
  1035 	if (r == MAILIMF_ERROR_MEMORY)
  1036 		goto enomem;
  1037 	else if (r != MAILIMF_NO_ERROR)
  1038 		goto err_file;
  1039 
  1040 	// we overallocate by 1 byte, so we have a terminating 0.
  1041 	len = buffer->len;
  1042 	buf = calloc(len + 1, 1);
  1043 	if (buf == NULL)
  1044 		goto enomem;
  1045 
  1046 	memcpy(buf, buffer->str, len);
  1047 	mmap_string_free(buffer);
  1048 
  1049     *mimetext = buf;
  1050     return PEP_STATUS_OK;
  1051 
  1052 err_file:
  1053     status = PEP_CANNOT_CREATE_TEMP_FILE;
  1054     goto pEp_error;
  1055 
  1056 enomem:
  1057     status = PEP_OUT_OF_MEMORY;
  1058 
  1059 pEp_error:
  1060 	if (buffer)
  1061 		mmap_string_free(buffer);
  1062 	if (buf)
  1063 		free(buf);
  1064     return status;
  1065 }
  1066 
  1067 static PEP_STATUS mime_attachment(
  1068         bloblist_t *blob,
  1069         struct mailmime **result,
  1070         bool transport_encode,
  1071         bool set_attachment_forward_comment
  1072     )
  1073 {
  1074     PEP_STATUS status = PEP_STATUS_OK;
  1075     struct mailmime * mime = NULL;
  1076     char * mime_type;
  1077     assert(blob);
  1078     assert(result);
  1079 
  1080     *result = NULL;
  1081 
  1082 // TODO: It seems the pEp COM server adapter sends an empty string here,
  1083 // which leads to a crash later. Thus, we workaround here by treating an
  1084 // empty string as NULL. We need to check whether the bug really is here,
  1085 // or the pEp COM server adapter needs to be changed.
  1086     if (blob->mime_type == NULL || blob->mime_type[0] == '\0')
  1087         mime_type = "application/octet-stream";
  1088     else
  1089         mime_type = blob->mime_type;
  1090 
  1091     pEp_rid_list_t* resource = parse_uri(blob->filename);
  1092 
  1093     bool already_ascii = !(must_chunk_be_encoded(blob->value, blob->size, true));
  1094 
  1095     mime = get_file_part(resource, mime_type, blob->value, blob->size, 
  1096                           (already_ascii ? false : transport_encode),
  1097                           set_attachment_forward_comment);
  1098     free_rid_list(resource);
  1099     
  1100     assert(mime);
  1101     if (mime == NULL)
  1102         goto enomem;
  1103 
  1104     *result = mime;
  1105     return PEP_STATUS_OK;
  1106 
  1107 enomem:
  1108     status = PEP_OUT_OF_MEMORY;
  1109 
  1110     if (mime)
  1111         mailmime_free(mime);
  1112 
  1113     return status;
  1114 }
  1115 
  1116 static PEP_STATUS mime_html_text(
  1117         const char *plaintext,
  1118         const char *htmltext,
  1119         bloblist_t *attachments,
  1120         struct mailmime **result,
  1121         bool transport_encode
  1122     )
  1123 {
  1124     PEP_STATUS status = PEP_STATUS_OK;
  1125     struct mailmime * top_level_html_mime = NULL;
  1126     struct mailmime * mime = NULL;
  1127     struct mailmime * submime = NULL;
  1128     int r;
  1129 
  1130     assert(plaintext);
  1131     assert(htmltext);
  1132     assert(result);
  1133 
  1134     *result = NULL;
  1135 
  1136     mime = part_multiple_new("multipart/alternative");
  1137     assert(mime);
  1138     if (mime == NULL)
  1139         goto enomem;
  1140 
  1141     pEp_rid_list_t* resource = NULL;
  1142     
  1143     int encoding_type = (transport_encode ? MAILMIME_MECHANISM_QUOTED_PRINTABLE : 0);
  1144     submime = get_text_part(NULL, "text/plain", plaintext, strlen(plaintext),
  1145             encoding_type);
  1146     free_rid_list(resource);
  1147     resource = NULL;
  1148     
  1149     assert(submime);
  1150     if (submime == NULL)
  1151         goto enomem;
  1152 
  1153     r = mailmime_smart_add_part(mime, submime);
  1154     assert(r == MAILIMF_NO_ERROR);
  1155     if (r == MAILIMF_ERROR_MEMORY) {
  1156         goto enomem;
  1157     }
  1158     else {
  1159         // mailmime_smart_add_part() takes ownership of submime
  1160         submime = NULL;
  1161     }
  1162 
  1163     bool inlined_attachments = false;
  1164     
  1165     bloblist_t* traversal_ptr = attachments;
  1166     
  1167     while (traversal_ptr) {
  1168         if (traversal_ptr->disposition == PEP_CONTENT_DISP_INLINE) {
  1169             inlined_attachments = true;
  1170             break;
  1171         }
  1172         traversal_ptr = traversal_ptr->next;
  1173     }
  1174 
  1175     if (inlined_attachments) {
  1176         /* Noooooo... dirk, why do you do this to me? */
  1177         submime = part_multiple_new("multipart/related");
  1178         assert(submime);
  1179         if (submime == NULL)
  1180             goto enomem;
  1181 
  1182         top_level_html_mime = submime;
  1183         
  1184         r = mailmime_smart_add_part(mime, top_level_html_mime);
  1185         assert(r == MAILIMF_NO_ERROR);
  1186         if (r == MAILIMF_ERROR_MEMORY) {
  1187             goto enomem;
  1188         }
  1189         else {
  1190             // mailmime_smart_add_part() takes ownership of submime
  1191             submime = NULL;
  1192         }
  1193     }
  1194     else {
  1195         top_level_html_mime = mime;
  1196     }
  1197 
  1198 //    resource = new_rid_node(PEP_RID_FILENAME, "msg.html");
  1199     submime = get_text_part(NULL, "text/html", htmltext, strlen(htmltext),
  1200             encoding_type);
  1201     free_rid_list(resource);
  1202     resource = NULL;
  1203     
  1204     assert(submime);
  1205     if (submime == NULL)
  1206         goto enomem;
  1207 
  1208     r = mailmime_smart_add_part(top_level_html_mime, submime);
  1209     assert(r == MAILIMF_NO_ERROR);
  1210     if (r == MAILIMF_ERROR_MEMORY)
  1211         goto enomem;
  1212     else {
  1213         // mailmime_smart_add_part() takes ownership of submime
  1214         submime = NULL;
  1215     }
  1216 
  1217     bloblist_t *_a;
  1218     for (_a = attachments; _a != NULL; _a = _a->next) {
  1219         if (_a->disposition != PEP_CONTENT_DISP_INLINE)
  1220             continue;
  1221         status = mime_attachment(_a, &submime, transport_encode, false);
  1222         if (status != PEP_STATUS_OK)
  1223             return PEP_UNKNOWN_ERROR; // FIXME
  1224 
  1225         r = mailmime_smart_add_part(top_level_html_mime, submime);
  1226         assert(r == MAILIMF_NO_ERROR);
  1227         if (r == MAILIMF_ERROR_MEMORY) {
  1228             goto enomem;
  1229         }
  1230         else {
  1231             // mailmime_smart_add_part() takes ownership of submime
  1232             submime = NULL;
  1233         }
  1234     }
  1235 
  1236     *result = mime;
  1237     return PEP_STATUS_OK;
  1238 
  1239 enomem:
  1240     status = PEP_OUT_OF_MEMORY;
  1241 
  1242     if (mime)
  1243         mailmime_free(mime);
  1244 
  1245     if (submime)
  1246         mailmime_free(submime);
  1247 
  1248     return status;
  1249 }
  1250 
  1251 
  1252 // FIXME: maybe need to add transport_encode field here
  1253 static struct mailimf_mailbox * identity_to_mailbox(const pEp_identity *ident)
  1254 {
  1255     char *_username = NULL;
  1256     struct mailimf_mailbox *mb;
  1257 
  1258     if (!ident->username)
  1259         _username = strdup("");
  1260     else
  1261         _username = must_field_value_be_encoded(ident->username) ?
  1262                     mailmime_encode_subject_header("utf-8", ident->username, 0) : 
  1263                     strdup(ident->username);
  1264                   
  1265     assert(_username);
  1266     if (_username == NULL)
  1267         goto enomem;
  1268 
  1269     mb = mailbox_from_string(_username, ident->address);
  1270     if (mb == NULL)
  1271         goto enomem;
  1272 
  1273     free(_username);
  1274     _username = NULL;
  1275 
  1276     return mb;
  1277 
  1278 enomem:
  1279     free(_username);
  1280     return NULL;
  1281 }
  1282 
  1283 static struct mailimf_mailbox_list * identity_to_mbl(
  1284         const pEp_identity *ident)
  1285 {
  1286     struct mailimf_mailbox_list *mbl = NULL;
  1287     struct mailimf_mailbox *mb = NULL;
  1288     clist *list = NULL;
  1289     int r;
  1290 
  1291     assert(ident);
  1292 
  1293     list = clist_new();
  1294     if (list == NULL)
  1295         goto enomem;
  1296 
  1297     mb = identity_to_mailbox(ident);
  1298     if (mb == NULL)
  1299         goto enomem;
  1300 
  1301     r = clist_append(list, mb);
  1302     if (r)
  1303         goto enomem;
  1304 
  1305     mbl = mailimf_mailbox_list_new(list);
  1306     if (mbl == NULL)
  1307         goto enomem;
  1308 
  1309     return mbl;
  1310 
  1311 enomem:
  1312     if (mb)
  1313         mailimf_mailbox_free(mb);
  1314 
  1315     if (list)
  1316         clist_free(list);
  1317 
  1318     return NULL;
  1319 }
  1320 
  1321 static struct mailimf_address_list * identity_list_to_mal(identity_list *il)
  1322 {
  1323     struct mailimf_address_list *mal = NULL;
  1324     struct mailimf_mailbox *mb = NULL;
  1325     struct mailimf_address * addr = NULL;
  1326     clist *list = NULL;
  1327     int r;
  1328 
  1329     assert(il);
  1330 
  1331     list = clist_new();
  1332     if (list == NULL)
  1333         goto enomem;
  1334 
  1335     identity_list *_il;
  1336     for (_il = il; _il && _il->ident; _il = _il->next) {
  1337         mb = identity_to_mailbox(_il->ident);
  1338         if (mb == NULL)
  1339             goto enomem;
  1340 
  1341         addr = mailimf_address_new(MAILIMF_ADDRESS_MAILBOX, mb, NULL);
  1342         if (addr == NULL)
  1343             goto enomem;
  1344         mb = NULL;
  1345 
  1346         r = clist_append(list, addr);
  1347         if (r)
  1348             goto enomem;
  1349         addr = NULL;
  1350     }
  1351     mal = mailimf_address_list_new(list);
  1352     if (mal == NULL)
  1353         goto enomem;
  1354 
  1355     return mal;
  1356 
  1357 enomem:
  1358     if (mb)
  1359         mailimf_mailbox_free(mb);
  1360 
  1361     if (addr)
  1362         mailimf_address_free(addr);
  1363 
  1364     if (list)
  1365         clist_free(list);
  1366 
  1367     return NULL;
  1368 }
  1369 
  1370 static clist * stringlist_to_clist(stringlist_t *sl, bool transport_encode)
  1371 {
  1372     clist * cl = clist_new();
  1373     assert(cl);
  1374     if (cl == NULL)
  1375         return NULL;
  1376 
  1377     if (!sl || ((!sl->value || sl->value[0] == '\0') && sl->next == NULL))
  1378         return cl;
  1379         
  1380     stringlist_t *_sl;
  1381     for (_sl = sl; _sl; _sl = _sl->next) {
  1382         int r;
  1383         char * value = ((transport_encode && must_field_value_be_encoded(_sl->value)) ?
  1384                         mailmime_encode_subject_header("utf-8", _sl->value, 0) :
  1385                         strdup(_sl->value));
  1386         assert(value);
  1387         if (value == NULL) {
  1388             clist_free(cl);
  1389             return NULL;
  1390         }
  1391         r = clist_append(cl, value);
  1392         assert(r == 0);
  1393         if (r) {
  1394             free(value);
  1395             clist_free(cl);
  1396             return NULL;
  1397         }
  1398     }
  1399 
  1400     return cl;
  1401 }
  1402 
  1403 static PEP_STATUS build_fields(const message *msg, struct mailimf_fields **result)
  1404 {
  1405     PEP_STATUS status = PEP_STATUS_OK;
  1406     struct mailimf_fields * fields = NULL;
  1407     int r;
  1408     clist * fields_list = NULL;
  1409     unsigned char pEpstr[] = PEP_SUBJ_STRING; // unsigned due to UTF-8 byte fun
  1410 #ifdef WIN32
  1411     char* altstr = "pEp";
  1412 #else
  1413     char* altstr = (char*)pEpstr;
  1414 #endif        
  1415     char *subject = msg->shortmsg && msg->shortmsg[0] ? msg->shortmsg : altstr;
  1416 
  1417     assert(msg);
  1418     assert(result);
  1419 
  1420     *result = NULL;
  1421 
  1422     fields_list = clist_new();
  1423     assert(fields_list);
  1424     if (fields_list == NULL)
  1425         goto enomem;
  1426 
  1427     if (msg->id && msg->id[0]) {
  1428         char *_msgid = strdup(msg->id);
  1429         assert(_msgid);
  1430         if (_msgid == NULL)
  1431             goto enomem;
  1432 
  1433         r = _append_field(fields_list, MAILIMF_FIELD_MESSAGE_ID,
  1434                 (_new_func_t) mailimf_message_id_new, _msgid);
  1435         if (r) {
  1436             free(_msgid);
  1437             goto enomem;
  1438         }
  1439     }
  1440 
  1441     if (msg->sent) {
  1442         struct mailimf_date_time * dt = timestamp_to_etpantime(msg->sent);
  1443         if (dt == NULL)
  1444             goto enomem;
  1445 
  1446         r = _append_field(fields_list, MAILIMF_FIELD_ORIG_DATE,
  1447                 (_new_func_t) mailimf_orig_date_new, dt);
  1448         if (r) {
  1449             mailimf_date_time_free(dt);
  1450             goto enomem;
  1451         }
  1452         dt = NULL;
  1453     }
  1454 
  1455      if (msg->from) {
  1456         struct mailimf_mailbox_list *from = identity_to_mbl(msg->from);
  1457         if (from == NULL)
  1458             goto enomem;
  1459 
  1460         r = _append_field(fields_list, MAILIMF_FIELD_FROM,
  1461                 (_new_func_t) mailimf_from_new, from);
  1462         if (r) {
  1463             mailimf_mailbox_list_free(from);
  1464             goto enomem;
  1465         }
  1466     }
  1467 
  1468     if (msg->to && msg->to->ident) {
  1469         struct mailimf_address_list *to = identity_list_to_mal(msg->to);
  1470         if (to == NULL)
  1471             goto enomem;
  1472 
  1473         r = _append_field(fields_list, MAILIMF_FIELD_TO,
  1474                 (_new_func_t) mailimf_to_new, to);
  1475         if (r) {
  1476             mailimf_address_list_free(to);
  1477             goto enomem;
  1478         }
  1479     }
  1480 
  1481     char* _subject = NULL;
  1482     if (!must_field_value_be_encoded(subject)) {
  1483         _subject = strdup(subject);
  1484         assert(_subject);
  1485     }
  1486     else {
  1487         _subject = mailmime_encode_subject_header("utf-8", subject, 1);
  1488     }
  1489     if (_subject == NULL)
  1490         goto enomem;
  1491 
  1492     r = _append_field(fields_list, MAILIMF_FIELD_SUBJECT,
  1493             (_new_func_t) mailimf_subject_new, _subject);
  1494     if (r) {
  1495         free(_subject);
  1496         goto enomem;
  1497     }
  1498 
  1499     if (msg->cc && msg->cc->ident) {
  1500         struct mailimf_address_list *cc = identity_list_to_mal(msg->cc);
  1501         if (cc == NULL)
  1502             goto enomem;
  1503 
  1504         r = _append_field(fields_list, MAILIMF_FIELD_CC,
  1505                 (_new_func_t) mailimf_cc_new, cc);
  1506         if (r) {
  1507             mailimf_address_list_free(cc);
  1508             goto enomem;
  1509         }
  1510     }
  1511     
  1512     if (msg->bcc && msg->bcc->ident) {
  1513         struct mailimf_address_list *bcc = identity_list_to_mal(msg->bcc);
  1514         if (bcc == NULL)
  1515             goto enomem;
  1516 
  1517         r = _append_field(fields_list, MAILIMF_FIELD_BCC,
  1518                 (_new_func_t) mailimf_bcc_new, bcc);
  1519         if (r) {
  1520             mailimf_address_list_free(bcc);
  1521             goto enomem;
  1522         }
  1523     }
  1524     
  1525     if (msg->reply_to && msg->reply_to->ident) {
  1526         struct mailimf_address_list *reply_to = identity_list_to_mal(msg->reply_to);
  1527         if (reply_to == NULL)
  1528             goto enomem;
  1529 
  1530         r = _append_field(fields_list, MAILIMF_FIELD_REPLY_TO,
  1531                 (_new_func_t) mailimf_reply_to_new, reply_to);
  1532         if (r) {
  1533             mailimf_address_list_free(reply_to);
  1534             goto enomem;
  1535         }
  1536     }
  1537 
  1538     if (msg->in_reply_to && msg->in_reply_to->value) {
  1539         clist *in_reply_to = stringlist_to_clist(msg->in_reply_to, true);
  1540         if (in_reply_to == NULL)
  1541             goto enomem;
  1542 
  1543         r = _append_field(fields_list, MAILIMF_FIELD_IN_REPLY_TO,
  1544                 (_new_func_t) mailimf_in_reply_to_new, in_reply_to);
  1545         if (r) {
  1546             clist_free(in_reply_to);
  1547             goto enomem;
  1548         }
  1549     }
  1550 
  1551     if (msg->references && msg->references->value) {
  1552         clist *references = stringlist_to_clist(msg->references, true);
  1553         if (references == NULL)
  1554             goto enomem;
  1555 
  1556         r = _append_field(fields_list, MAILIMF_FIELD_REFERENCES,
  1557                 (_new_func_t) mailimf_references_new, references);
  1558         if (r) {
  1559             clist_free(references);
  1560             goto enomem;
  1561         }
  1562     }
  1563 
  1564     if (msg->keywords && msg->keywords->value) {
  1565         clist *keywords = stringlist_to_clist(msg->keywords, true);
  1566         if (keywords == NULL)
  1567             goto enomem;
  1568 
  1569         r = _append_field(fields_list, MAILIMF_FIELD_KEYWORDS,
  1570                 (_new_func_t) mailimf_keywords_new, keywords);
  1571         if (r) {
  1572             clist_free(keywords);
  1573             goto enomem;
  1574         }
  1575     }
  1576 
  1577     if (msg->comments && msg->comments[0]) {
  1578         char *comments = NULL;
  1579         if (!must_field_value_be_encoded(msg->comments)) {
  1580             comments = strdup(msg->comments);
  1581             assert(comments);
  1582         }
  1583         else  {
  1584             comments = mailmime_encode_subject_header("utf-8", msg->comments, 0);
  1585         }
  1586         if (comments == NULL)
  1587             goto enomem;
  1588 
  1589         r = _append_field(fields_list, MAILIMF_FIELD_COMMENTS,
  1590                 (_new_func_t) mailimf_comments_new, comments);
  1591         if (r) {
  1592             free(comments);
  1593             goto enomem;
  1594         }
  1595     }
  1596 
  1597     if (msg->opt_fields && msg->opt_fields->value) {
  1598         stringpair_list_t *_l;
  1599         for (_l = msg->opt_fields; _l && _l->value; _l = _l->next) {
  1600             char *key = _l->value->key;
  1601             char *value = _l->value->value;
  1602             if (key && value) {
  1603                 r = _append_optional_field(fields_list, key, value);
  1604 
  1605                 if (r)
  1606                     goto enomem;
  1607             }
  1608         }
  1609     }
  1610 
  1611     fields = mailimf_fields_new(fields_list);
  1612     assert(fields);
  1613     if (fields == NULL)
  1614         goto enomem;
  1615 
  1616     *result = fields;
  1617 
  1618     return PEP_STATUS_OK;
  1619 
  1620 enomem:
  1621     status = PEP_OUT_OF_MEMORY;
  1622 
  1623     if (fields_list)
  1624         clist_free(fields_list);
  1625 
  1626     if (fields)
  1627         mailimf_fields_free(fields);
  1628 
  1629     return status;
  1630 }
  1631 
  1632 static bool has_exceptional_extension(char* filename) {
  1633     if (!filename)
  1634         return false;
  1635     int len = strlen(filename);
  1636     if (len < 4)
  1637         return false;
  1638     char* ext_start = filename + (len - 4);
  1639     if (strcmp(ext_start, ".pgp") == 0 || strcmp(ext_start, ".gpg") == 0 ||
  1640         strcmp(ext_start, ".asc") == 0 || strcmp(ext_start, ".pEp") == 0)
  1641         return true;
  1642     return false;
  1643 }
  1644 
  1645 static pEp_rid_list_t* choose_resource_id(pEp_rid_list_t* rid_list) {
  1646     pEp_rid_list_t* retval = rid_list;
  1647     
  1648     /* multiple elements - least common case */
  1649     if (rid_list && rid_list->next) {
  1650         pEp_rid_list_t* rid_list_curr = rid_list;
  1651         retval = rid_list; 
  1652         
  1653         while (rid_list_curr) {
  1654             pEp_resource_id_type rid_type = rid_list_curr->rid_type;
  1655             if (rid_type == PEP_RID_CID)
  1656                 retval = rid_list_curr;
  1657             else if (rid_type == PEP_RID_FILENAME && has_exceptional_extension(rid_list_curr->rid))
  1658                 return rid_list_curr;
  1659             rid_list_curr = rid_list_curr->next;
  1660         }
  1661     } 
  1662     return retval;
  1663 }
  1664 
  1665 // static void split_inlined_and_attached(bloblist_t** inlined, bloblist_t** attached) {
  1666 //     bloblist_t** curr_pp = attached;
  1667 //     bloblist_t* curr = *curr_pp;
  1668 //     
  1669 //     bloblist_t* inline_ret = NULL;
  1670 //     bloblist_t** inline_curr_pp = &inline_ret;
  1671 //     
  1672 //     bloblist_t* att_ret = NULL;
  1673 //     bloblist_t** att_curr_pp = &att_ret;
  1674 //     
  1675 //     while (curr) {
  1676 //         if (curr->disposition == PEP_CONTENT_DISP_INLINE) {
  1677 //             *inline_curr_pp = curr;
  1678 //             inline_curr_pp = &(curr->next);
  1679 //         }
  1680 //         else {
  1681 //             *att_curr_pp = curr;
  1682 //             att_curr_pp = &(curr->next);            
  1683 //         }
  1684 //         *curr_pp = curr->next;
  1685 //         curr->next = NULL;
  1686 //         curr = *curr_pp;
  1687 //     }
  1688 //     
  1689 //     *inlined = inline_ret;
  1690 //     *attached = att_ret;
  1691 // }
  1692 
  1693 
  1694 static PEP_STATUS mime_encode_message_plain(
  1695         const message *msg,
  1696         bool omit_fields,
  1697         struct mailmime **result,
  1698         bool transport_encode,
  1699         bool set_attachment_forward_comment
  1700     )
  1701 {
  1702     struct mailmime * mime = NULL;
  1703     struct mailmime * submime = NULL;
  1704     int r;
  1705     PEP_STATUS status;
  1706     //char *subject;
  1707     char *plaintext;
  1708     char *htmltext;
  1709 
  1710     assert(msg);
  1711     assert(result);
  1712     
  1713     //subject = (msg->shortmsg) ? msg->shortmsg : "pEp";  // not used, yet.
  1714     plaintext = (msg->longmsg) ? msg->longmsg : "";
  1715     htmltext = msg->longmsg_formatted;
  1716 
  1717     if (htmltext && (htmltext[0] != '\0')) {
  1718         /* first, we need to strip out the inlined attachments to ensure this
  1719            gets set up correctly */
  1720            
  1721         status = mime_html_text(plaintext, htmltext, msg->attachments, &mime,
  1722                                 transport_encode);
  1723                 
  1724         if (status != PEP_STATUS_OK)
  1725             goto pEp_error;
  1726     }
  1727     else {
  1728         pEp_rid_list_t* resource = NULL;
  1729         if (is_PGP_message_text(plaintext)) {
  1730             resource = NULL;
  1731             int encoding_type = (transport_encode ? MAILMIME_MECHANISM_7BIT : 0);
  1732             mime = get_text_part(resource, "application/octet-stream", plaintext,
  1733                     strlen(plaintext), encoding_type);
  1734         }
  1735         else {
  1736             resource = NULL;
  1737             int encoding_type = (transport_encode ? MAILMIME_MECHANISM_QUOTED_PRINTABLE : 0);
  1738             mime = get_text_part(resource, "text/plain", plaintext, strlen(plaintext),
  1739                     encoding_type);
  1740         }
  1741         free_rid_list(resource);
  1742         
  1743         assert(mime);
  1744         if (mime == NULL)
  1745             goto enomem;
  1746     }
  1747 
  1748     bool normal_attachments = false;
  1749     
  1750     bloblist_t* traversal_ptr = msg->attachments;
  1751     
  1752     while (traversal_ptr) {
  1753         if (traversal_ptr->disposition != PEP_CONTENT_DISP_INLINE) {
  1754             normal_attachments = true;
  1755             break;
  1756         }
  1757         traversal_ptr = traversal_ptr->next;
  1758     }
  1759 
  1760     if (normal_attachments) {
  1761         submime = mime;
  1762         mime = part_multiple_new("multipart/mixed");
  1763         assert(mime);
  1764         if (mime == NULL)
  1765             goto enomem;
  1766 
  1767         r = mailmime_smart_add_part(mime, submime);
  1768         assert(r == MAILIMF_NO_ERROR);
  1769         if (r == MAILIMF_ERROR_MEMORY) {
  1770             goto enomem;
  1771         }
  1772         else {
  1773             // mailmime_smart_add_part() takes ownership of submime
  1774             submime = NULL;
  1775         }
  1776 
  1777         bloblist_t *_a;
  1778         bool first_one = true;
  1779         
  1780         for (_a = msg->attachments; _a != NULL; _a = _a->next) {
  1781 
  1782             if (_a->disposition == PEP_CONTENT_DISP_INLINE)
  1783                 continue;
  1784 
  1785             status = mime_attachment(_a, &submime, transport_encode,
  1786                                      (first_one && set_attachment_forward_comment));                         
  1787             if (status != PEP_STATUS_OK)
  1788                 goto pEp_error;
  1789             
  1790             first_one = false;    
  1791 
  1792             r = mailmime_smart_add_part(mime, submime);
  1793             assert(r == MAILIMF_NO_ERROR);
  1794             if (r == MAILIMF_ERROR_MEMORY) {
  1795                 goto enomem;
  1796             }
  1797             else {
  1798                 // mailmime_smart_add_part() takes ownership of submime
  1799                 submime = NULL;
  1800             }
  1801         }
  1802     }
  1803 
  1804     *result = mime;
  1805     return PEP_STATUS_OK;
  1806 
  1807 enomem:
  1808     status = PEP_OUT_OF_MEMORY;
  1809 
  1810 pEp_error:
  1811     if (mime)
  1812         mailmime_free(mime);
  1813 
  1814     if (submime)
  1815         mailmime_free(submime);
  1816 
  1817     return status;
  1818 }
  1819 
  1820 static PEP_STATUS mime_encode_message_PGP_MIME(
  1821         const message * msg,
  1822         bool omit_fields,
  1823         struct mailmime **result
  1824     )
  1825 {
  1826     struct mailmime * mime = NULL;
  1827     struct mailmime * submime = NULL;
  1828 	struct mailmime_parameter * param;
  1829     int r;
  1830     PEP_STATUS status;
  1831     char *plaintext;
  1832     size_t plaintext_size;
  1833 
  1834     assert(msg->attachments && msg->attachments->next &&
  1835             msg->attachments->next->value);
  1836 
  1837     plaintext = msg->attachments->next->value;
  1838     plaintext_size = msg->attachments->next->size;
  1839 
  1840     mime = part_multiple_new("multipart/encrypted");
  1841     assert(mime);
  1842     if (mime == NULL)
  1843         goto enomem;
  1844 
  1845     param = mailmime_param_new_with_data("protocol", "application/pgp-encrypted");
  1846     clist_append(mime->mm_content_type->ct_parameters, param);
  1847 
  1848     submime = get_pgp_encrypted_part();
  1849     assert(submime);
  1850     if (submime == NULL)
  1851         goto enomem;
  1852 
  1853     r = mailmime_smart_add_part(mime, submime);
  1854     assert(r == MAILIMF_NO_ERROR);
  1855     if (r == MAILIMF_ERROR_MEMORY) {
  1856         goto enomem;
  1857     }
  1858     else {
  1859         // mailmime_smart_add_part() takes ownership of submime
  1860         submime = NULL;
  1861     }
  1862 
  1863     pEp_rid_list_t* resource = new_rid_node(PEP_RID_FILENAME, "msg.asc");
  1864     submime = get_text_part(resource, "application/octet-stream", plaintext,
  1865             plaintext_size, MAILMIME_MECHANISM_7BIT);
  1866             
  1867     free_rid_list(resource);
  1868     
  1869     assert(submime);
  1870     if (submime == NULL)
  1871         goto enomem;
  1872 
  1873     r = mailmime_smart_add_part(mime, submime);
  1874     assert(r == MAILIMF_NO_ERROR);
  1875     if (r == MAILIMF_ERROR_MEMORY) {
  1876         goto enomem;
  1877     }
  1878     else {
  1879         // mailmime_smart_add_part() takes ownership of submime
  1880         submime = NULL;
  1881     }
  1882 
  1883     *result = mime;
  1884     return PEP_STATUS_OK;
  1885 
  1886 enomem:
  1887     status = PEP_OUT_OF_MEMORY;
  1888 
  1889     if (mime)
  1890         mailmime_free(mime);
  1891 
  1892     if (submime)
  1893         mailmime_free(submime);
  1894 
  1895     return status;
  1896 }
  1897 
  1898 PEP_STATUS _mime_encode_message_internal(
  1899         const message * msg,
  1900         bool omit_fields,
  1901         char **mimetext,
  1902         bool transport_encode,
  1903         bool set_attachment_forward_comment
  1904     )
  1905 {
  1906     PEP_STATUS status = PEP_STATUS_OK;
  1907     struct mailmime * msg_mime = NULL;
  1908     struct mailmime * mime = NULL;
  1909     struct mailimf_fields * fields = NULL;
  1910     char *buf = NULL;
  1911     int r;
  1912 
  1913     assert(msg);
  1914     assert(mimetext);
  1915 
  1916     if (!(msg && mimetext))
  1917         return PEP_ILLEGAL_VALUE;
  1918 
  1919     *mimetext = NULL;
  1920 
  1921     switch (msg->enc_format) {
  1922         case PEP_enc_none:
  1923             status = mime_encode_message_plain(msg, omit_fields, &mime, transport_encode, set_attachment_forward_comment);
  1924             break;
  1925 
  1926         // I'm presuming we should hardcore ignoring set_attachment_forward_comment here...
  1927         case PEP_enc_inline:
  1928             status = mime_encode_message_plain(msg, omit_fields, &mime, transport_encode, false);
  1929             break;
  1930 
  1931         case PEP_enc_S_MIME:
  1932             NOT_IMPLEMENTED
  1933                 
  1934         case PEP_enc_PGP_MIME:
  1935             status = mime_encode_message_PGP_MIME(msg, omit_fields, &mime);
  1936             break;
  1937 
  1938         case PEP_enc_PEP:
  1939             // today's pEp message format is PGP/MIME from the outside
  1940             status = mime_encode_message_PGP_MIME(msg, omit_fields, &mime);
  1941             break;
  1942 
  1943         default:
  1944             NOT_IMPLEMENTED
  1945     }
  1946 
  1947     if (status != PEP_STATUS_OK)
  1948         goto pEp_error;
  1949 
  1950     msg_mime = mailmime_new_message_data(NULL);
  1951     assert(msg_mime);
  1952     if (msg_mime == NULL)
  1953         goto enomem;
  1954 
  1955     r = mailmime_add_part(msg_mime, mime);
  1956     if (r) {
  1957         mailmime_free(mime);
  1958         goto enomem;
  1959     }
  1960     mime = NULL;
  1961 
  1962     if (!omit_fields) {
  1963         status = build_fields(msg, &fields);
  1964         if (status != PEP_STATUS_OK)
  1965             goto pEp_error;
  1966 
  1967         mailmime_set_imf_fields(msg_mime, fields);
  1968     }
  1969 
  1970     status = render_mime(msg_mime, &buf);
  1971     if (status != PEP_STATUS_OK)
  1972         goto pEp_error;
  1973 
  1974     mailmime_free(msg_mime);
  1975     *mimetext = buf;
  1976 
  1977     return PEP_STATUS_OK;
  1978 
  1979 enomem:
  1980     status = PEP_OUT_OF_MEMORY;
  1981 
  1982 pEp_error:
  1983     if (msg_mime)
  1984         mailmime_free(msg_mime);
  1985     else
  1986         if (mime)
  1987             mailmime_free(mime);
  1988 
  1989     return status;
  1990 }
  1991 
  1992 static pEp_identity *mailbox_to_identity(const struct mailimf_mailbox * mb)
  1993 {
  1994     char *username = NULL;
  1995 
  1996     assert(mb);
  1997     assert(mb->mb_addr_spec);
  1998 
  1999     if (mb->mb_addr_spec == NULL)
  2000         return NULL;
  2001 
  2002     if (mb->mb_display_name) {
  2003         size_t index = 0;
  2004         const int r = mailmime_encoded_phrase_parse("utf-8", mb->mb_display_name,
  2005                 strlen(mb->mb_display_name), &index, "utf-8", &username);
  2006         if (r)
  2007             goto enomem;
  2008     }
  2009 
  2010     pEp_identity *ident = new_identity(mb->mb_addr_spec, NULL, NULL, username);
  2011     if (ident == NULL)
  2012         goto enomem;
  2013     free(username);
  2014 
  2015     return ident;
  2016 
  2017 enomem:
  2018     free(username);
  2019     return NULL;
  2020 }
  2021 
  2022 static pEp_identity * mbl_to_identity(const struct mailimf_mailbox_list * mbl)
  2023 {
  2024     struct mailimf_mailbox * mb = clist_content(clist_begin(mbl->mb_list));
  2025     return mailbox_to_identity(mb);
  2026 }
  2027 
  2028 static identity_list * mal_to_identity_list(
  2029         const struct mailimf_address_list *mal
  2030     )
  2031 {
  2032     identity_list *il = new_identity_list(NULL);
  2033     if (il == NULL)
  2034         goto enomem;
  2035 
  2036     // if we have nothing to translate then return an empty list
  2037     if (!mal)
  2038         return il;
  2039 
  2040     clist *list = mal->ad_list;
  2041 
  2042     identity_list *_il = il;
  2043     for (clistiter *cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
  2044         pEp_identity *ident;
  2045 
  2046         struct mailimf_address *addr = clist_content(cur);
  2047         switch(addr->ad_type) {
  2048             case MAILIMF_ADDRESS_MAILBOX:
  2049                 ident = mailbox_to_identity(addr->ad_data.ad_mailbox);
  2050                 if (ident == NULL)
  2051                     goto enomem;
  2052                 _il = identity_list_add(_il, ident);
  2053                 if (_il == NULL)
  2054                     goto enomem;
  2055                 break;
  2056 
  2057             case MAILIMF_ADDRESS_GROUP:
  2058                 {
  2059                     struct mailimf_mailbox_list * mbl =
  2060                             addr->ad_data.ad_group->grp_mb_list;
  2061                     for (clistiter *cur2 = clist_begin(mbl->mb_list); cur2 != NULL;
  2062                             cur2 = clist_next(cur2)) {
  2063                         ident = mailbox_to_identity(clist_content(cur));
  2064                         if (ident == NULL)
  2065                             goto enomem;
  2066                         _il = identity_list_add(_il, ident);
  2067                         if (_il == NULL)
  2068                             goto enomem;
  2069                     }
  2070                 }
  2071                 break;
  2072 
  2073             default:
  2074                 assert(0);
  2075                 goto enomem;
  2076         }
  2077     }
  2078 
  2079     return il;
  2080 
  2081 enomem:
  2082     free_identity_list(il);
  2083     return NULL;
  2084 }
  2085 
  2086 static stringlist_t * clist_to_stringlist(const clist *list)
  2087 {
  2088     char *text = NULL;;
  2089     stringlist_t * sl = new_stringlist(NULL);
  2090     if (sl == NULL)
  2091         return NULL;
  2092 
  2093     stringlist_t *_sl = sl;
  2094     for (clistiter *cur = clist_begin(list); cur != NULL; cur = clist_next(cur)) {
  2095         char *phrase = clist_content(cur);
  2096         size_t index = 0;
  2097         
  2098         const int r = mailmime_encoded_phrase_parse("utf-8", phrase, strlen(phrase),
  2099                 &index, "utf-8", &text);
  2100         if (r)
  2101             goto enomem;
  2102 
  2103         _sl = stringlist_add(_sl, text);
  2104         if (_sl == NULL)
  2105             goto enomem;
  2106 
  2107         free(text);
  2108         text = NULL;
  2109     }
  2110 
  2111     return sl;
  2112 
  2113 enomem:
  2114     free_stringlist(sl);
  2115     free(text);
  2116 
  2117     return NULL;
  2118 }
  2119 
  2120 static PEP_STATUS read_fields(message *msg, clist *fieldlist)
  2121 {
  2122     PEP_STATUS status = PEP_STATUS_OK;
  2123     struct mailimf_field * _field;
  2124     clistiter *cur;
  2125     size_t index;
  2126     int r;
  2127     
  2128     stringpair_list_t *opt = msg->opt_fields;
  2129 
  2130     if (!fieldlist)
  2131         return PEP_STATUS_OK;
  2132         
  2133     for (cur = clist_begin(fieldlist); cur != NULL; cur = clist_next(cur)) {
  2134         _field = clist_content(cur);
  2135 
  2136         switch (_field->fld_type) {
  2137             case MAILIMF_FIELD_MESSAGE_ID:
  2138                 {
  2139                     char * text = _field->fld_data.fld_message_id->mid_value;
  2140 
  2141                     free(msg->id);
  2142                     index = 0;
  2143                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2144                             strlen(text), &index, "utf-8", &msg->id);
  2145                     if (r)
  2146                         goto enomem;
  2147                 }
  2148                 break;
  2149 
  2150             case MAILIMF_FIELD_SUBJECT:
  2151                 {
  2152                     char * text = _field->fld_data.fld_subject->sbj_value;
  2153 
  2154                     free(msg->shortmsg);
  2155                     index = 0;
  2156                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2157                             strlen(text), &index, "utf-8", &msg->shortmsg);
  2158                     if (r)
  2159                         goto enomem;
  2160                 }
  2161                 break;
  2162 
  2163             case MAILIMF_FIELD_ORIG_DATE:
  2164                 {
  2165                     struct mailimf_date_time *date =
  2166                         _field->fld_data.fld_orig_date->dt_date_time;
  2167 
  2168                     free_timestamp(msg->sent);
  2169                     msg->sent = etpantime_to_timestamp(date);
  2170                     if (msg->sent == NULL)
  2171                         goto enomem;
  2172                 }
  2173                 break;
  2174 
  2175             case MAILIMF_FIELD_FROM:
  2176                 {
  2177                     struct mailimf_mailbox_list *mbl =
  2178                             _field->fld_data.fld_from->frm_mb_list;
  2179                     pEp_identity *ident;
  2180 
  2181                     ident = mbl_to_identity(mbl);
  2182                     if (ident == NULL)
  2183                         goto pEp_error;
  2184 
  2185                     free_identity(msg->from);
  2186                     msg->from = ident;
  2187                 }
  2188                 break;
  2189 
  2190             case MAILIMF_FIELD_TO:
  2191                 {
  2192                     struct mailimf_address_list *mal =
  2193                             _field->fld_data.fld_to->to_addr_list;
  2194                     identity_list *il = mal_to_identity_list(mal);
  2195                     if (il == NULL)
  2196                         goto enomem;
  2197 
  2198                     free_identity_list(msg->to);
  2199                     msg->to = il;
  2200                 }
  2201                 break;
  2202 
  2203             case MAILIMF_FIELD_CC:
  2204                 {
  2205                     struct mailimf_address_list *mal =
  2206                             _field->fld_data.fld_cc->cc_addr_list;
  2207                     identity_list *il = mal_to_identity_list(mal);
  2208                     if (il == NULL)
  2209                         goto enomem;
  2210 
  2211                     free_identity_list(msg->cc);
  2212                     msg->cc = il;
  2213                 }
  2214                 break;
  2215 
  2216             case MAILIMF_FIELD_BCC:
  2217                 {
  2218                     struct mailimf_address_list *mal =
  2219                             _field->fld_data.fld_bcc->bcc_addr_list;
  2220                     identity_list *il = mal_to_identity_list(mal);
  2221                     if (il == NULL)
  2222                         goto enomem;
  2223 
  2224                     free_identity_list(msg->bcc);
  2225                     msg->bcc = il;
  2226                 }
  2227                 break;
  2228 
  2229             case MAILIMF_FIELD_REPLY_TO:
  2230                 {
  2231                     struct mailimf_address_list *mal =
  2232                             _field->fld_data.fld_reply_to->rt_addr_list;
  2233                     identity_list *il = mal_to_identity_list(mal);
  2234                     if (il == NULL)
  2235                         goto enomem;
  2236 
  2237                     free_identity_list(msg->reply_to);
  2238                     msg->reply_to = il;
  2239                 }
  2240                 break;
  2241 
  2242             case MAILIMF_FIELD_IN_REPLY_TO:
  2243                 {
  2244                     clist *list = _field->fld_data.fld_in_reply_to->mid_list;
  2245                     stringlist_t *sl = clist_to_stringlist(list);
  2246                     if (sl == NULL)
  2247                         goto enomem;
  2248 
  2249                     free_stringlist(msg->in_reply_to);
  2250                     msg->in_reply_to = sl;
  2251                 }
  2252                 break;
  2253 
  2254             case MAILIMF_FIELD_REFERENCES:
  2255                 {
  2256                     clist *list = _field->fld_data.fld_references->mid_list;
  2257                     stringlist_t *sl = clist_to_stringlist(list);
  2258                     if (sl == NULL)
  2259                         goto enomem;
  2260 
  2261                     free_stringlist(msg->references);
  2262                     msg->references = sl;
  2263                 }
  2264                 break;
  2265 
  2266             case MAILIMF_FIELD_KEYWORDS:
  2267                 {
  2268                     clist *list = _field->fld_data.fld_keywords->kw_list;
  2269                     stringlist_t *sl = clist_to_stringlist(list);
  2270                     if (sl == NULL)
  2271                         goto enomem;
  2272 
  2273                     free_stringlist(msg->keywords);
  2274                     msg->keywords = sl;
  2275                 }
  2276                 break;
  2277 
  2278             case MAILIMF_FIELD_COMMENTS:
  2279                 {
  2280                     char * text = _field->fld_data.fld_comments->cm_value;
  2281 
  2282                     free(msg->comments);
  2283                     index = 0;
  2284                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2285                             strlen(text), &index, "utf-8", &msg->comments);
  2286                     if (r)
  2287                         goto enomem;
  2288                 }
  2289                 break;
  2290 
  2291             case MAILIMF_FIELD_OPTIONAL_FIELD:
  2292                 {
  2293                     char * name =
  2294                             _field->fld_data.fld_optional_field->fld_name;
  2295                     char * value =
  2296                             _field->fld_data.fld_optional_field->fld_value;
  2297                     char *_value;
  2298 
  2299                     index = 0;
  2300                     r = mailmime_encoded_phrase_parse("utf-8", value,
  2301                             strlen(value), &index, "utf-8", &_value);
  2302                     if (r)
  2303                         goto enomem;
  2304 
  2305                     stringpair_t *pair = new_stringpair(name, _value);
  2306                     if (pair == NULL)
  2307                         goto enomem;
  2308 
  2309                     opt = stringpair_list_add(opt, pair);
  2310                     free(_value);
  2311                     if (opt == NULL)
  2312                         goto enomem;
  2313 
  2314                     if (msg->opt_fields == NULL)
  2315                         msg->opt_fields = opt;
  2316                 }
  2317                 break;
  2318         }
  2319     }
  2320     
  2321     return PEP_STATUS_OK;
  2322 
  2323 enomem:
  2324     status = PEP_OUT_OF_MEMORY;
  2325 
  2326 pEp_error:
  2327     return status;
  2328 }
  2329 
  2330 static PEP_STATUS interpret_body(struct mailmime *part, char **longmsg, size_t *size)
  2331 {
  2332     const char *text;
  2333     char *_longmsg;
  2334     size_t length;
  2335     size_t _size;
  2336     size_t index;
  2337     char *type = NULL;
  2338     char *charset = NULL;
  2339 
  2340     assert(part);
  2341     assert(longmsg);
  2342 
  2343     *longmsg = NULL;
  2344     if (size)
  2345         *size = 0;
  2346 
  2347     if (part->mm_body == NULL)
  2348         return PEP_ILLEGAL_VALUE;
  2349 
  2350     text = part->mm_body-> dt_data.dt_text.dt_data;
  2351     if (text == NULL)
  2352         return PEP_ILLEGAL_VALUE;
  2353 
  2354     length = part->mm_body->dt_data.dt_text.dt_length;
  2355 
  2356     if (part->mm_body->dt_encoded) {
  2357         int code = part->mm_body->dt_encoding;
  2358         index = 0;
  2359         int r = mailmime_part_parse(text, length, &index, code, &_longmsg, &_size);
  2360         switch (r) {
  2361             case MAILIMF_NO_ERROR:
  2362                 break;
  2363             case MAILIMF_ERROR_MEMORY:
  2364                 return PEP_OUT_OF_MEMORY;
  2365             default:
  2366                 return PEP_ILLEGAL_VALUE;
  2367         }
  2368     }
  2369     else {
  2370         _size = length + 1;
  2371         _longmsg = strndup(text, length);
  2372         if (_longmsg == NULL)
  2373             return PEP_OUT_OF_MEMORY;
  2374     }
  2375 
  2376     if (part->mm_content_type) {
  2377         if (_get_content_type(part->mm_content_type, &type, &charset) == 0) {
  2378             if (charset && strncasecmp(charset, "utf-8", 5) != 0) {
  2379                 char * _text;
  2380                 int r = charconv("utf-8", charset, _longmsg, _size, &_text);
  2381                 switch (r) {
  2382                     case MAILIMF_NO_ERROR:
  2383                         break;
  2384                     case MAILIMF_ERROR_MEMORY:
  2385                         return PEP_OUT_OF_MEMORY;
  2386                     default:
  2387                         return PEP_ILLEGAL_VALUE;
  2388                 }
  2389                 free(_longmsg);
  2390                 _longmsg = _text;
  2391             }
  2392         }
  2393     }
  2394     // FIXME: KG - we now have the text we want.
  2395     // Now we need to strip sigs and process them if they are there..
  2396     
  2397 
  2398     *longmsg = _longmsg;
  2399     if (size)
  2400         *size = _size;
  2401 
  2402     return PEP_STATUS_OK;
  2403 }
  2404 
  2405 // THIS IS A BEST-EFFORT ONLY FUNCTION, AND WE ARE NOT DOING MORE THAN THE
  2406 // SUBJECT FOR NOW.
  2407 static PEP_STATUS interpret_protected_headers(
  2408         struct mailmime* mime, 
  2409         message* msg
  2410     )
  2411 {
  2412     // N.B. this is *very much* enigmail output specific, and right now,
  2413     // we only care about subject replacement.
  2414     const char* header_string = "Content-Type: text/rfc822-headers; protected-headers=\"v1\"\nContent-Disposition: inline\n\n";
  2415     size_t content_length = mime->mm_length;
  2416     size_t header_strlen = strlen(header_string);
  2417     if (header_strlen < content_length) {
  2418         const char* headerblock = mime->mm_mime_start;
  2419         size_t subject_len = 0;
  2420         headerblock = strstr(headerblock, header_string);
  2421         if (headerblock) {
  2422             const char* subj_start = "Subject: ";
  2423             headerblock = strstr(headerblock, subj_start);
  2424             if (headerblock) {
  2425                 size_t subj_len = strlen(subj_start);
  2426                 headerblock += subj_len;
  2427                 char* end_pt = strstr(headerblock, "\n");
  2428                 if (end_pt) {
  2429                     if (end_pt != mime->mm_mime_start && *(end_pt - 1) == '\r')
  2430                         end_pt--;
  2431                     subject_len = end_pt - headerblock;
  2432                     char* new_subj = (char*)calloc(subject_len + 1, 1);
  2433                     if (new_subj) {
  2434                         strlcpy(new_subj, headerblock, subject_len + 1);
  2435                         free(msg->shortmsg);
  2436                         msg->shortmsg = new_subj;
  2437                     }    
  2438                 } // if there's no endpoint, there's something wrong here so we ignore all
  2439                   // This is best effort.
  2440             }
  2441         }
  2442     }
  2443     return PEP_STATUS_OK;
  2444 }
  2445 
  2446 // ONLY for main part!!!
  2447 static PEP_STATUS process_multipart_related(struct mailmime *mime,
  2448                                             message *msg) {
  2449     PEP_STATUS status = PEP_STATUS_OK;
  2450 
  2451     assert(mime);
  2452     assert(msg);
  2453 
  2454     clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;                                                
  2455 
  2456     if (partlist == NULL)
  2457         return PEP_ILLEGAL_VALUE;
  2458 
  2459     clistiter *cur = clist_begin(partlist);
  2460     struct mailmime *part = clist_content(cur);
  2461     
  2462     if (part == NULL)
  2463         return PEP_ILLEGAL_VALUE;
  2464 
  2465     struct mailmime_content *content = part->mm_content_type;    
  2466     assert(content);
  2467     
  2468     if (content == NULL)
  2469         return PEP_ILLEGAL_VALUE;
  2470 
  2471     if (_is_text_part(content, "html")) {
  2472         status = interpret_body(part, &msg->longmsg_formatted,
  2473                 NULL);
  2474         if (status)
  2475             return status;
  2476     }
  2477     else {
  2478         // ???
  2479         // This is what we would have done before, so... no
  2480         // worse than the status quo. But FIXME!
  2481         status = interpret_MIME(part, msg, NULL);
  2482         if (status)
  2483             return status;
  2484     }
  2485     
  2486     for (cur = clist_next(cur); cur; cur = clist_next(cur)) {
  2487         part = clist_content(cur);
  2488         if (part == NULL)
  2489             return PEP_ILLEGAL_VALUE;
  2490 
  2491         content = part->mm_content_type;
  2492         assert(content);
  2493         if (content == NULL)
  2494             return PEP_ILLEGAL_VALUE;
  2495 
  2496         status = interpret_MIME(part, msg, NULL);
  2497         if (status)
  2498             return status;
  2499     }
  2500     return status;
  2501 }
  2502 
  2503 static PEP_STATUS interpret_MIME(
  2504         struct mailmime *mime,
  2505         message *msg,
  2506         bool* raise_msg_attachment
  2507     )
  2508 {
  2509     PEP_STATUS status = PEP_STATUS_OK;
  2510 
  2511     assert(mime);
  2512     assert(msg);
  2513 
  2514     struct mailmime_content *content = mime->mm_content_type;
  2515     if (content) {
  2516         if (_is_multipart(content, "alternative")) {
  2517             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2518             if (partlist == NULL)
  2519                 return PEP_ILLEGAL_VALUE;
  2520 
  2521             clistiter *cur;
  2522             for (cur = clist_begin(partlist); cur; cur = clist_next(cur)) {
  2523                 struct mailmime *part = clist_content(cur);
  2524                 if (part == NULL)
  2525                     return PEP_ILLEGAL_VALUE;
  2526 
  2527                 content = part->mm_content_type;
  2528                 assert(content);
  2529                 if (content == NULL)
  2530                     return PEP_ILLEGAL_VALUE;
  2531 
  2532                 if (_is_text_part(content, "plain") && msg->longmsg == NULL) {
  2533                     status = interpret_body(part, &msg->longmsg, NULL);
  2534                     if (status)
  2535                         return status;
  2536                 }
  2537                 else if (_is_text_part(content, "rfc822-headers")) {
  2538                     status = interpret_protected_headers(part, msg);
  2539                     if (status)
  2540                         return status;
  2541                 }
  2542                 else if (_is_text_part(content, "html") &&
  2543                         msg->longmsg_formatted == NULL) {
  2544                     status = interpret_body(part, &msg->longmsg_formatted,
  2545                             NULL);
  2546                     if (status)
  2547                         return status;
  2548                 }
  2549                 else if (_is_multipart(content, "related") && 
  2550                     msg->longmsg_formatted == NULL) {
  2551                     status = process_multipart_related(part, msg);
  2552                     if (status)
  2553                         return status;
  2554                 }
  2555                 else /* add as attachment */ {
  2556                     status = interpret_MIME(part, msg, NULL);
  2557                     if (status)
  2558                         return status;
  2559                 }
  2560             }
  2561         }
  2562         else if (_is_multipart(content, "encrypted")) {
  2563             if (msg->longmsg == NULL)
  2564                 msg->longmsg = strdup("");
  2565             assert(msg->longmsg);
  2566             if (!msg->longmsg)
  2567                 return PEP_OUT_OF_MEMORY;
  2568 
  2569             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2570             if (partlist == NULL)
  2571                 return PEP_ILLEGAL_VALUE;
  2572 
  2573             clistiter *cur;
  2574             for (cur = clist_begin(partlist); cur; cur = clist_next(cur)) {
  2575                 struct mailmime *part= clist_content(cur);
  2576                 if (part == NULL)
  2577                     return PEP_ILLEGAL_VALUE;
  2578 
  2579                 status = interpret_MIME(part, msg, NULL);
  2580                 if (status != PEP_STATUS_OK)
  2581                     return status;
  2582             }
  2583         }
  2584         else if (_is_multipart(content, NULL)) {
  2585             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2586             if (partlist == NULL)
  2587                 return PEP_ILLEGAL_VALUE;
  2588 
  2589             clistiter *cur;
  2590             // only add raise_msg_attachment on 2nd part!
  2591             int _att_count = 0;
  2592             for (cur = clist_begin(partlist); cur; cur = clist_next(cur), _att_count++) {
  2593                 struct mailmime *part= clist_content(cur);
  2594                 if (part == NULL)
  2595                     return PEP_ILLEGAL_VALUE;
  2596                 status = interpret_MIME(part, msg, _att_count == 1 ? raise_msg_attachment : NULL);
  2597                 if (status != PEP_STATUS_OK)
  2598                     return status;
  2599             }
  2600         }
  2601         else {
  2602             if (_is_text_part(content, "html") &&
  2603                 msg->longmsg_formatted == NULL &&
  2604                 msg->longmsg == NULL) {
  2605                 status = interpret_body(mime, &msg->longmsg_formatted,
  2606                                         NULL);
  2607                 if (status)
  2608                     return status;
  2609             }
  2610             else if (_is_text_part(content, "rfc822-headers")) {
  2611                 status = interpret_protected_headers(mime, msg);
  2612                 if (status)
  2613                     return status;
  2614             }
  2615             else if (_is_text_part(content, "plain") && 
  2616                      msg->longmsg == NULL && msg->longmsg_formatted == NULL) {
  2617                 status = interpret_body(mime, &msg->longmsg, NULL);
  2618                 if (status)
  2619                     return status;
  2620             }            
  2621             else if (_is_text_part(content, NULL) && 
  2622                      !_is_text_part(content, "plain") &&
  2623                      msg->longmsg == NULL) {
  2624                 status = interpret_body(mime, &msg->longmsg, NULL);
  2625                 if (status)
  2626                     return status;
  2627             }
  2628             else {
  2629                 // Fixme - we need a control on recursion level here - KG: maybe NOT. We only go to depth 1.
  2630                 if (raise_msg_attachment != NULL) {
  2631                     bool is_msg = (_is_message_part(content, "rfc822") || _is_text_part(content, "rfc822"));
  2632                     if (is_msg) {
  2633                         if (content->ct_parameters) {
  2634                             clistiter *cur;
  2635                             for (cur = clist_begin(content->ct_parameters); cur; cur =
  2636                                  clist_next(cur)) {
  2637                                 struct mailmime_parameter * param = clist_content(cur);
  2638                                 if (param && param->pa_name && strcasecmp(param->pa_name, "forwarded") == 0) {
  2639                                     if (param->pa_value && strcasecmp(param->pa_value, "no") == 0) {
  2640                                         *raise_msg_attachment = true;
  2641                                         break;
  2642                                     }
  2643                                 }
  2644                             }
  2645                         }
  2646                     }
  2647                 }
  2648                 char *data = NULL;
  2649                 size_t size = 0;
  2650                 char * mime_type;
  2651                 char * charset;
  2652                 char * filename;
  2653                 int r;
  2654 
  2655                 r = _get_content_type(content, &mime_type, &charset);
  2656                 switch (r) {
  2657                     case 0:
  2658                         break;
  2659                     case EINVAL:
  2660                         return PEP_ILLEGAL_VALUE;
  2661                     case ENOMEM:
  2662                         return PEP_OUT_OF_MEMORY;
  2663                     default:
  2664                         return PEP_UNKNOWN_ERROR;
  2665                 }
  2666 
  2667                 assert(mime_type);
  2668 
  2669                 status = interpret_body(mime, &data, &size);
  2670                 if (status)
  2671                     return status;
  2672 
  2673                 pEp_rid_list_t* resource_id_list = _get_resource_id_list(mime);
  2674                 pEp_rid_list_t* chosen_resource_id = choose_resource_id(resource_id_list);
  2675                 
  2676                 //filename = _get_filename_or_cid(mime);
  2677                 char *_filename = NULL;
  2678                 
  2679                 if (chosen_resource_id) {
  2680                     filename = chosen_resource_id->rid;
  2681                     size_t index = 0;
  2682                     /* NOTA BENE */
  2683                     /* The prefix we just added shouldn't be a problem - this is about decoding %XX (RFC 2392) */
  2684                     /* If it becomes one, we have some MESSY fixing to do. :(                                  */
  2685                     r = mailmime_encoded_phrase_parse("utf-8", filename,
  2686                             strlen(filename), &index, "utf-8", &_filename);
  2687                     if (r) {
  2688                         goto enomem;
  2689                     }
  2690                     char* file_prefix = NULL;
  2691                     
  2692                     /* in case there are others later */
  2693                     switch (chosen_resource_id->rid_type) {
  2694                         case PEP_RID_CID:
  2695                             file_prefix = "cid";
  2696                             break;
  2697                         case PEP_RID_FILENAME:
  2698                             file_prefix = "file";
  2699                             break;
  2700                         default:
  2701                             break;
  2702                     }
  2703 
  2704                     
  2705                     if (file_prefix) {
  2706                         filename = build_uri(file_prefix, _filename);
  2707                         free(_filename);
  2708                         _filename = filename;
  2709                     }
  2710                 }
  2711 
  2712                 bloblist_t *_a = bloblist_add(msg->attachments, data, size,
  2713                         mime_type, _filename);
  2714                 free(_filename);
  2715                 free_rid_list(resource_id_list);
  2716                 resource_id_list = NULL;
  2717                 if (_a == NULL)
  2718                     return PEP_OUT_OF_MEMORY;
  2719                 if (msg->attachments == NULL)
  2720                     msg->attachments = _a;
  2721             }
  2722         }
  2723     }
  2724 
  2725     return PEP_STATUS_OK;
  2726 
  2727 enomem:
  2728     return PEP_OUT_OF_MEMORY;
  2729 }
  2730 
  2731 DYNAMIC_API PEP_STATUS mime_decode_message(
  2732         const char *mimetext,
  2733         size_t size,
  2734         message **msg
  2735     )
  2736 {
  2737     return _mime_decode_message_internal(mimetext, size, msg, NULL);
  2738 }        
  2739 
  2740 
  2741 PEP_STATUS _mime_decode_message_internal(
  2742         const char *mimetext,
  2743         size_t size,
  2744         message **msg,
  2745         bool* raise_msg_attachment
  2746     )
  2747 {
  2748     PEP_STATUS status = PEP_STATUS_OK;
  2749     struct mailmime * mime = NULL;
  2750     int r;
  2751     message *_msg = NULL;
  2752     size_t index;
  2753 
  2754     assert(mimetext);
  2755     assert(msg);
  2756 
  2757     if (!(mimetext && msg))
  2758         return PEP_ILLEGAL_VALUE;
  2759 
  2760     *msg = NULL;
  2761 
  2762     index = 0;
  2763     r = mailmime_parse(mimetext, size, &index, &mime);
  2764     assert(r == 0);
  2765     assert(mime);
  2766     if (r) {
  2767         if (r == MAILIMF_ERROR_MEMORY)
  2768             goto enomem;
  2769         else
  2770             goto err_mime;
  2771     }
  2772 
  2773     _msg = calloc(1, sizeof(message));
  2774     assert(_msg);
  2775     if (_msg == NULL)
  2776         goto enomem;
  2777 
  2778     clist * _fieldlist = _get_fields(mime);
  2779     if (_fieldlist) {
  2780         status = read_fields(_msg, _fieldlist);
  2781         if (status != PEP_STATUS_OK)
  2782             goto pEp_error;
  2783     }
  2784 
  2785     struct mailmime_content *content = _get_content(mime);
  2786 
  2787     if (content) {
  2788         status = interpret_MIME(mime->mm_data.mm_message.mm_msg_mime,
  2789                 _msg, raise_msg_attachment);
  2790         if (status != PEP_STATUS_OK)
  2791             goto pEp_error;
  2792     }
  2793 
  2794     mailmime_free(mime);
  2795     *msg = _msg;
  2796 
  2797     return status;
  2798 
  2799 err_mime:
  2800     status = PEP_ILLEGAL_VALUE;
  2801     goto pEp_error;
  2802 
  2803 enomem:
  2804     status = PEP_OUT_OF_MEMORY;
  2805 
  2806 pEp_error:
  2807     free_message(_msg);
  2808 
  2809     if (mime)
  2810         mailmime_free(mime);
  2811 
  2812     return status;
  2813 }