src/etpan_mime.c
author Volker Birk <vb@pep-project.org>
Tue, 07 Apr 2020 10:07:46 +0200
branchsync
changeset 4534 96dc81d05fcb
parent 4502 4c82c412eb19
child 4537 0f441a3e8e36
permissions -rw-r--r--
don't generate fields for empty strings or empty lists
     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 = "text";
   902                 break;
   903             case MAILMIME_DISCRETE_TYPE_IMAGE:
   904                 _main_type = "image";
   905                 break;
   906             case MAILMIME_DISCRETE_TYPE_AUDIO:
   907                 _main_type = "audio";
   908                 break;
   909             case MAILMIME_DISCRETE_TYPE_VIDEO:
   910                 _main_type = "video";
   911                 break;
   912             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   913                 _main_type = "application";
   914                 break;
   915             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   916                 _main_type = "extension";
   917                 break;
   918             default:
   919                 return EINVAL;
   920         }
   921 
   922         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   923         _type = calloc(1, len);
   924         assert(_type);
   925         if (_type == NULL)
   926             return ENOMEM;
   927 
   928         strncpy(_type, _main_type, len);
   929         len -= strlen(_main_type);
   930         strncat(_type, "/", len--);
   931         strncat(_type, content->ct_subtype, len);
   932 
   933         if (content->ct_parameters) {
   934             clistiter *cur;
   935             for (cur = clist_begin(content->ct_parameters); cur; cur =
   936                     clist_next(cur)) {
   937                 struct mailmime_parameter * param = clist_content(cur);
   938                 if (param && param->pa_name && strcasecmp(param->pa_name,
   939                             "charset") == 0) {
   940                     _charset = param->pa_value;
   941                     break;
   942                 }
   943             }
   944             if (_charset)
   945                 *charset = strdup(_charset);
   946         }
   947 
   948         *type = _type;
   949         return 0;
   950     }
   951 
   952     return EINVAL;
   953 }
   954 
   955 // Only for null-terminated field strings.
   956 // can this field be transported as is without modification?)
   957 // (See rfc2822, section 2.2.3 - libetpan's handling isn't quite what
   958 // we need here.)
   959 bool must_field_value_be_encoded(const char* field_value) {
   960     if (!field_value)
   961         return false;
   962         
   963     return must_chunk_be_encoded((const void*)field_value, strlen(field_value), false);    
   964 }
   965 
   966 bool must_chunk_be_encoded(const void* value, size_t size, bool ignore_fws) {
   967 
   968     const char* begin_ptr = (const char*)value;    
   969 
   970     const char* end_ptr = begin_ptr + size;
   971 
   972     const char* cur_char_ptr = begin_ptr;
   973     while (cur_char_ptr < end_ptr) {
   974         char cur_char = *cur_char_ptr;
   975         if (cur_char > 127 || cur_char < 0)
   976             return true;
   977         // FIXME - do we need to deal with CRCRLF here?
   978         //         I guess in the worst case, it gets encoded, which
   979         //         is *supposed* to be harmless...
   980         if (!ignore_fws) {
   981             if (cur_char == '\r') {
   982                 const char* next = cur_char_ptr + 1;
   983                 const char* nextnext = next + 1;
   984                 if (next >= end_ptr || nextnext >= end_ptr
   985                     || *next != '\n'
   986                     || (*nextnext != ' ' && *nextnext != '\t')) {
   987                     return true;
   988                 }            
   989             }
   990             else if (cur_char == '\n') {
   991                 const char* prev = cur_char_ptr - 1;
   992                 if (prev == begin_ptr || *prev != '\r')
   993                     return true;
   994             }
   995         }
   996         cur_char_ptr++;
   997     }    
   998     return false;
   999 }
  1000 
  1001 #define TMP_TEMPLATE "pEp.XXXXXXXXXXXXXXXXXXXX"
  1002 #ifdef _WIN32
  1003 #define PATH_SEP '\\'
  1004 #else
  1005 #define PATH_SEP '/'
  1006 #endif
  1007 
  1008 static PEP_STATUS interpret_MIME(struct mailmime *mime,
  1009                                  message *msg,
  1010                                  bool* raise_msg_attachment);
  1011 
  1012 // This function was rewritten to use in-memory buffers instead of
  1013 // temporary files when the pgp/mime support was implemented for
  1014 // outlook, as the existing code did not work well on windows.
  1015 
  1016 static PEP_STATUS render_mime(struct mailmime *mime, char **mimetext)
  1017 {
  1018     PEP_STATUS status = PEP_STATUS_OK;
  1019     int col;
  1020     int r;
  1021 	size_t len;
  1022 	char* buf = NULL;
  1023 
  1024 	MMAPString* buffer;
  1025 
  1026 	buffer = mmap_string_new(NULL);
  1027 	if (buffer == NULL)
  1028 		goto enomem;
  1029 	
  1030 	col = 0;
  1031 	r = mailmime_write_mem(buffer, &col, mime);
  1032 	assert(r == MAILIMF_NO_ERROR);
  1033 	if (r == MAILIMF_ERROR_MEMORY)
  1034 		goto enomem;
  1035 	else if (r != MAILIMF_NO_ERROR)
  1036 		goto err_file;
  1037 
  1038 	// we overallocate by 1 byte, so we have a terminating 0.
  1039 	len = buffer->len;
  1040 	buf = calloc(len + 1, 1);
  1041 	if (buf == NULL)
  1042 		goto enomem;
  1043 
  1044 	memcpy(buf, buffer->str, len);
  1045 	mmap_string_free(buffer);
  1046 
  1047     *mimetext = buf;
  1048     return PEP_STATUS_OK;
  1049 
  1050 err_file:
  1051     status = PEP_CANNOT_CREATE_TEMP_FILE;
  1052     goto pEp_error;
  1053 
  1054 enomem:
  1055     status = PEP_OUT_OF_MEMORY;
  1056 
  1057 pEp_error:
  1058 	if (buffer)
  1059 		mmap_string_free(buffer);
  1060 	if (buf)
  1061 		free(buf);
  1062     return status;
  1063 }
  1064 
  1065 static PEP_STATUS mime_attachment(
  1066         bloblist_t *blob,
  1067         struct mailmime **result,
  1068         bool transport_encode,
  1069         bool set_attachment_forward_comment
  1070     )
  1071 {
  1072     PEP_STATUS status = PEP_STATUS_OK;
  1073     struct mailmime * mime = NULL;
  1074     char * mime_type;
  1075     assert(blob);
  1076     assert(result);
  1077 
  1078     *result = NULL;
  1079 
  1080 // TODO: It seems the pEp COM server adapter sends an empty string here,
  1081 // which leads to a crash later. Thus, we workaround here by treating an
  1082 // empty string as NULL. We need to check whether the bug really is here,
  1083 // or the pEp COM server adapter needs to be changed.
  1084     if (blob->mime_type == NULL || blob->mime_type[0] == '\0')
  1085         mime_type = "application/octet-stream";
  1086     else
  1087         mime_type = blob->mime_type;
  1088 
  1089     pEp_rid_list_t* resource = parse_uri(blob->filename);
  1090 
  1091     bool already_ascii = !(must_chunk_be_encoded(blob->value, blob->size, true));
  1092 
  1093     mime = get_file_part(resource, mime_type, blob->value, blob->size, 
  1094                           (already_ascii ? false : transport_encode),
  1095                           set_attachment_forward_comment);
  1096     free_rid_list(resource);
  1097     
  1098     assert(mime);
  1099     if (mime == NULL)
  1100         goto enomem;
  1101 
  1102     *result = mime;
  1103     return PEP_STATUS_OK;
  1104 
  1105 enomem:
  1106     status = PEP_OUT_OF_MEMORY;
  1107 
  1108     if (mime)
  1109         mailmime_free(mime);
  1110 
  1111     return status;
  1112 }
  1113 
  1114 static PEP_STATUS mime_html_text(
  1115         const char *plaintext,
  1116         const char *htmltext,
  1117         bloblist_t *attachments,
  1118         struct mailmime **result,
  1119         bool transport_encode
  1120     )
  1121 {
  1122     PEP_STATUS status = PEP_STATUS_OK;
  1123     struct mailmime * top_level_html_mime = NULL;
  1124     struct mailmime * mime = NULL;
  1125     struct mailmime * submime = NULL;
  1126     int r;
  1127 
  1128     assert(plaintext);
  1129     assert(htmltext);
  1130     assert(result);
  1131 
  1132     *result = NULL;
  1133 
  1134     mime = part_multiple_new("multipart/alternative");
  1135     assert(mime);
  1136     if (mime == NULL)
  1137         goto enomem;
  1138 
  1139     pEp_rid_list_t* resource = NULL;
  1140     
  1141     int encoding_type = (transport_encode ? MAILMIME_MECHANISM_QUOTED_PRINTABLE : 0);
  1142     submime = get_text_part(NULL, "text/plain", plaintext, strlen(plaintext),
  1143             encoding_type);
  1144     free_rid_list(resource);
  1145     resource = NULL;
  1146     
  1147     assert(submime);
  1148     if (submime == NULL)
  1149         goto enomem;
  1150 
  1151     r = mailmime_smart_add_part(mime, submime);
  1152     assert(r == MAILIMF_NO_ERROR);
  1153     if (r == MAILIMF_ERROR_MEMORY) {
  1154         goto enomem;
  1155     }
  1156     else {
  1157         // mailmime_smart_add_part() takes ownership of submime
  1158         submime = NULL;
  1159     }
  1160 
  1161     bool inlined_attachments = false;
  1162     
  1163     bloblist_t* traversal_ptr = attachments;
  1164     
  1165     while (traversal_ptr) {
  1166         if (traversal_ptr->disposition == PEP_CONTENT_DISP_INLINE) {
  1167             inlined_attachments = true;
  1168             break;
  1169         }
  1170         traversal_ptr = traversal_ptr->next;
  1171     }
  1172 
  1173     if (inlined_attachments) {
  1174         /* Noooooo... dirk, why do you do this to me? */
  1175         submime = part_multiple_new("multipart/related");
  1176         assert(submime);
  1177         if (submime == NULL)
  1178             goto enomem;
  1179 
  1180         top_level_html_mime = submime;
  1181         
  1182         r = mailmime_smart_add_part(mime, top_level_html_mime);
  1183         assert(r == MAILIMF_NO_ERROR);
  1184         if (r == MAILIMF_ERROR_MEMORY) {
  1185             goto enomem;
  1186         }
  1187         else {
  1188             // mailmime_smart_add_part() takes ownership of submime
  1189             submime = NULL;
  1190         }
  1191     }
  1192     else {
  1193         top_level_html_mime = mime;
  1194     }
  1195 
  1196 //    resource = new_rid_node(PEP_RID_FILENAME, "msg.html");
  1197     submime = get_text_part(NULL, "text/html", htmltext, strlen(htmltext),
  1198             encoding_type);
  1199     free_rid_list(resource);
  1200     resource = NULL;
  1201     
  1202     assert(submime);
  1203     if (submime == NULL)
  1204         goto enomem;
  1205 
  1206     r = mailmime_smart_add_part(top_level_html_mime, submime);
  1207     assert(r == MAILIMF_NO_ERROR);
  1208     if (r == MAILIMF_ERROR_MEMORY)
  1209         goto enomem;
  1210     else {
  1211         // mailmime_smart_add_part() takes ownership of submime
  1212         submime = NULL;
  1213     }
  1214 
  1215     bloblist_t *_a;
  1216     for (_a = attachments; _a != NULL; _a = _a->next) {
  1217         if (_a->disposition != PEP_CONTENT_DISP_INLINE)
  1218             continue;
  1219         status = mime_attachment(_a, &submime, transport_encode, false);
  1220         if (status != PEP_STATUS_OK)
  1221             return PEP_UNKNOWN_ERROR; // FIXME
  1222 
  1223         r = mailmime_smart_add_part(top_level_html_mime, submime);
  1224         assert(r == MAILIMF_NO_ERROR);
  1225         if (r == MAILIMF_ERROR_MEMORY) {
  1226             goto enomem;
  1227         }
  1228         else {
  1229             // mailmime_smart_add_part() takes ownership of submime
  1230             submime = NULL;
  1231         }
  1232     }
  1233 
  1234     *result = mime;
  1235     return PEP_STATUS_OK;
  1236 
  1237 enomem:
  1238     status = PEP_OUT_OF_MEMORY;
  1239 
  1240     if (mime)
  1241         mailmime_free(mime);
  1242 
  1243     if (submime)
  1244         mailmime_free(submime);
  1245 
  1246     return status;
  1247 }
  1248 
  1249 
  1250 // FIXME: maybe need to add transport_encode field here
  1251 static struct mailimf_mailbox * identity_to_mailbox(const pEp_identity *ident)
  1252 {
  1253     char *_username = NULL;
  1254     struct mailimf_mailbox *mb;
  1255 
  1256     if (!ident->username)
  1257         _username = strdup("");
  1258     else
  1259         _username = must_field_value_be_encoded(ident->username) ?
  1260                     mailmime_encode_subject_header("utf-8", ident->username, 0) : 
  1261                     strdup(ident->username);
  1262                   
  1263     assert(_username);
  1264     if (_username == NULL)
  1265         goto enomem;
  1266 
  1267     mb = mailbox_from_string(_username, ident->address);
  1268     if (mb == NULL)
  1269         goto enomem;
  1270 
  1271     free(_username);
  1272     _username = NULL;
  1273 
  1274     return mb;
  1275 
  1276 enomem:
  1277     free(_username);
  1278     return NULL;
  1279 }
  1280 
  1281 static struct mailimf_mailbox_list * identity_to_mbl(
  1282         const pEp_identity *ident)
  1283 {
  1284     struct mailimf_mailbox_list *mbl = NULL;
  1285     struct mailimf_mailbox *mb = NULL;
  1286     clist *list = NULL;
  1287     int r;
  1288 
  1289     assert(ident);
  1290 
  1291     list = clist_new();
  1292     if (list == NULL)
  1293         goto enomem;
  1294 
  1295     mb = identity_to_mailbox(ident);
  1296     if (mb == NULL)
  1297         goto enomem;
  1298 
  1299     r = clist_append(list, mb);
  1300     if (r)
  1301         goto enomem;
  1302 
  1303     mbl = mailimf_mailbox_list_new(list);
  1304     if (mbl == NULL)
  1305         goto enomem;
  1306 
  1307     return mbl;
  1308 
  1309 enomem:
  1310     if (mb)
  1311         mailimf_mailbox_free(mb);
  1312 
  1313     if (list)
  1314         clist_free(list);
  1315 
  1316     return NULL;
  1317 }
  1318 
  1319 static struct mailimf_address_list * identity_list_to_mal(identity_list *il)
  1320 {
  1321     struct mailimf_address_list *mal = NULL;
  1322     struct mailimf_mailbox *mb = NULL;
  1323     struct mailimf_address * addr = NULL;
  1324     clist *list = NULL;
  1325     int r;
  1326 
  1327     assert(il);
  1328 
  1329     list = clist_new();
  1330     if (list == NULL)
  1331         goto enomem;
  1332 
  1333     identity_list *_il;
  1334     for (_il = il; _il && _il->ident; _il = _il->next) {
  1335         mb = identity_to_mailbox(_il->ident);
  1336         if (mb == NULL)
  1337             goto enomem;
  1338 
  1339         addr = mailimf_address_new(MAILIMF_ADDRESS_MAILBOX, mb, NULL);
  1340         if (addr == NULL)
  1341             goto enomem;
  1342         mb = NULL;
  1343 
  1344         r = clist_append(list, addr);
  1345         if (r)
  1346             goto enomem;
  1347         addr = NULL;
  1348     }
  1349     mal = mailimf_address_list_new(list);
  1350     if (mal == NULL)
  1351         goto enomem;
  1352 
  1353     return mal;
  1354 
  1355 enomem:
  1356     if (mb)
  1357         mailimf_mailbox_free(mb);
  1358 
  1359     if (addr)
  1360         mailimf_address_free(addr);
  1361 
  1362     if (list)
  1363         clist_free(list);
  1364 
  1365     return NULL;
  1366 }
  1367 
  1368 static clist * stringlist_to_clist(stringlist_t *sl, bool transport_encode)
  1369 {
  1370     clist * cl = clist_new();
  1371     assert(cl);
  1372     if (cl == NULL)
  1373         return NULL;
  1374 
  1375     if (!sl || ((!sl->value || sl->value[0] == '\0') && sl->next == NULL))
  1376         return cl;
  1377         
  1378     stringlist_t *_sl;
  1379     for (_sl = sl; _sl; _sl = _sl->next) {
  1380         int r;
  1381         char * value = ((transport_encode && must_field_value_be_encoded(_sl->value)) ?
  1382                         mailmime_encode_subject_header("utf-8", _sl->value, 0) :
  1383                         strdup(_sl->value));
  1384         assert(value);
  1385         if (value == NULL) {
  1386             clist_free(cl);
  1387             return NULL;
  1388         }
  1389         r = clist_append(cl, value);
  1390         assert(r == 0);
  1391         if (r) {
  1392             free(value);
  1393             clist_free(cl);
  1394             return NULL;
  1395         }
  1396     }
  1397 
  1398     return cl;
  1399 }
  1400 
  1401 static PEP_STATUS build_fields(const message *msg, struct mailimf_fields **result)
  1402 {
  1403     PEP_STATUS status = PEP_STATUS_OK;
  1404     struct mailimf_fields * fields = NULL;
  1405     int r;
  1406     clist * fields_list = NULL;
  1407     unsigned char pEpstr[] = PEP_SUBJ_STRING; // unsigned due to UTF-8 byte fun
  1408 #ifdef WIN32
  1409     char* altstr = "pEp";
  1410 #else
  1411     char* altstr = (char*)pEpstr;
  1412 #endif        
  1413     char *subject = msg->shortmsg && msg->shortmsg[0] ? msg->shortmsg : altstr;
  1414 
  1415     assert(msg);
  1416     assert(result);
  1417 
  1418     *result = NULL;
  1419 
  1420     fields_list = clist_new();
  1421     assert(fields_list);
  1422     if (fields_list == NULL)
  1423         goto enomem;
  1424 
  1425     if (msg->id && msg->id[0]) {
  1426         char *_msgid = strdup(msg->id);
  1427         assert(_msgid);
  1428         if (_msgid == NULL)
  1429             goto enomem;
  1430 
  1431         r = _append_field(fields_list, MAILIMF_FIELD_MESSAGE_ID,
  1432                 (_new_func_t) mailimf_message_id_new, _msgid);
  1433         if (r) {
  1434             free(_msgid);
  1435             goto enomem;
  1436         }
  1437     }
  1438 
  1439     if (msg->sent) {
  1440         struct mailimf_date_time * dt = timestamp_to_etpantime(msg->sent);
  1441         if (dt == NULL)
  1442             goto enomem;
  1443 
  1444         r = _append_field(fields_list, MAILIMF_FIELD_ORIG_DATE,
  1445                 (_new_func_t) mailimf_orig_date_new, dt);
  1446         if (r) {
  1447             mailimf_date_time_free(dt);
  1448             goto enomem;
  1449         }
  1450         dt = NULL;
  1451     }
  1452 
  1453      if (msg->from) {
  1454         struct mailimf_mailbox_list *from = identity_to_mbl(msg->from);
  1455         if (from == NULL)
  1456             goto enomem;
  1457 
  1458         r = _append_field(fields_list, MAILIMF_FIELD_FROM,
  1459                 (_new_func_t) mailimf_from_new, from);
  1460         if (r) {
  1461             mailimf_mailbox_list_free(from);
  1462             goto enomem;
  1463         }
  1464     }
  1465 
  1466     if (msg->to && msg->to->ident) {
  1467         struct mailimf_address_list *to = identity_list_to_mal(msg->to);
  1468         if (to == NULL)
  1469             goto enomem;
  1470 
  1471         r = _append_field(fields_list, MAILIMF_FIELD_TO,
  1472                 (_new_func_t) mailimf_to_new, to);
  1473         if (r) {
  1474             mailimf_address_list_free(to);
  1475             goto enomem;
  1476         }
  1477     }
  1478 
  1479     char* _subject = NULL;
  1480     if (!must_field_value_be_encoded(subject)) {
  1481         _subject = strdup(subject);
  1482         assert(_subject);
  1483     }
  1484     else {
  1485         _subject = mailmime_encode_subject_header("utf-8", subject, 1);
  1486     }
  1487     if (_subject == NULL)
  1488         goto enomem;
  1489 
  1490     r = _append_field(fields_list, MAILIMF_FIELD_SUBJECT,
  1491             (_new_func_t) mailimf_subject_new, _subject);
  1492     if (r) {
  1493         free(_subject);
  1494         goto enomem;
  1495     }
  1496 
  1497     if (msg->cc && msg->cc->ident) {
  1498         struct mailimf_address_list *cc = identity_list_to_mal(msg->cc);
  1499         if (cc == NULL)
  1500             goto enomem;
  1501 
  1502         r = _append_field(fields_list, MAILIMF_FIELD_CC,
  1503                 (_new_func_t) mailimf_cc_new, cc);
  1504         if (r) {
  1505             mailimf_address_list_free(cc);
  1506             goto enomem;
  1507         }
  1508     }
  1509     
  1510     if (msg->bcc && msg->bcc->ident) {
  1511         struct mailimf_address_list *bcc = identity_list_to_mal(msg->bcc);
  1512         if (bcc == NULL)
  1513             goto enomem;
  1514 
  1515         r = _append_field(fields_list, MAILIMF_FIELD_BCC,
  1516                 (_new_func_t) mailimf_bcc_new, bcc);
  1517         if (r) {
  1518             mailimf_address_list_free(bcc);
  1519             goto enomem;
  1520         }
  1521     }
  1522     
  1523     if (msg->reply_to && msg->reply_to->ident) {
  1524         struct mailimf_address_list *reply_to = identity_list_to_mal(msg->reply_to);
  1525         if (reply_to == NULL)
  1526             goto enomem;
  1527 
  1528         r = _append_field(fields_list, MAILIMF_FIELD_REPLY_TO,
  1529                 (_new_func_t) mailimf_reply_to_new, reply_to);
  1530         if (r) {
  1531             mailimf_address_list_free(reply_to);
  1532             goto enomem;
  1533         }
  1534     }
  1535 
  1536     if (msg->in_reply_to && msg->in_reply_to->value) {
  1537         clist *in_reply_to = stringlist_to_clist(msg->in_reply_to, true);
  1538         if (in_reply_to == NULL)
  1539             goto enomem;
  1540 
  1541         r = _append_field(fields_list, MAILIMF_FIELD_IN_REPLY_TO,
  1542                 (_new_func_t) mailimf_in_reply_to_new, in_reply_to);
  1543         if (r) {
  1544             clist_free(in_reply_to);
  1545             goto enomem;
  1546         }
  1547     }
  1548 
  1549     if (msg->references && msg->references->value) {
  1550         clist *references = stringlist_to_clist(msg->references, true);
  1551         if (references == NULL)
  1552             goto enomem;
  1553 
  1554         r = _append_field(fields_list, MAILIMF_FIELD_REFERENCES,
  1555                 (_new_func_t) mailimf_references_new, references);
  1556         if (r) {
  1557             clist_free(references);
  1558             goto enomem;
  1559         }
  1560     }
  1561 
  1562     if (msg->keywords && msg->keywords->value) {
  1563         clist *keywords = stringlist_to_clist(msg->keywords, true);
  1564         if (keywords == NULL)
  1565             goto enomem;
  1566 
  1567         r = _append_field(fields_list, MAILIMF_FIELD_KEYWORDS,
  1568                 (_new_func_t) mailimf_keywords_new, keywords);
  1569         if (r) {
  1570             clist_free(keywords);
  1571             goto enomem;
  1572         }
  1573     }
  1574 
  1575     if (msg->comments && msg->comments[0]) {
  1576         char *comments = NULL;
  1577         if (!must_field_value_be_encoded(msg->comments)) {
  1578             comments = strdup(msg->comments);
  1579             assert(comments);
  1580         }
  1581         else  {
  1582             comments = mailmime_encode_subject_header("utf-8", msg->comments, 0);
  1583         }
  1584         if (comments == NULL)
  1585             goto enomem;
  1586 
  1587         r = _append_field(fields_list, MAILIMF_FIELD_COMMENTS,
  1588                 (_new_func_t) mailimf_comments_new, comments);
  1589         if (r) {
  1590             free(comments);
  1591             goto enomem;
  1592         }
  1593     }
  1594 
  1595     if (msg->opt_fields && msg->opt_fields->value) {
  1596         stringpair_list_t *_l;
  1597         for (_l = msg->opt_fields; _l && _l->value; _l = _l->next) {
  1598             char *key = _l->value->key;
  1599             char *value = _l->value->value;
  1600             if (key && value) {
  1601                 r = _append_optional_field(fields_list, key, value);
  1602 
  1603                 if (r)
  1604                     goto enomem;
  1605             }
  1606         }
  1607     }
  1608 
  1609     fields = mailimf_fields_new(fields_list);
  1610     assert(fields);
  1611     if (fields == NULL)
  1612         goto enomem;
  1613 
  1614     *result = fields;
  1615 
  1616     return PEP_STATUS_OK;
  1617 
  1618 enomem:
  1619     status = PEP_OUT_OF_MEMORY;
  1620 
  1621     if (fields_list)
  1622         clist_free(fields_list);
  1623 
  1624     if (fields)
  1625         mailimf_fields_free(fields);
  1626 
  1627     return status;
  1628 }
  1629 
  1630 static bool has_exceptional_extension(char* filename) {
  1631     if (!filename)
  1632         return false;
  1633     int len = strlen(filename);
  1634     if (len < 4)
  1635         return false;
  1636     char* ext_start = filename + (len - 4);
  1637     if (strcmp(ext_start, ".pgp") == 0 || strcmp(ext_start, ".gpg") == 0 ||
  1638         strcmp(ext_start, ".asc") == 0 || strcmp(ext_start, ".pEp") == 0)
  1639         return true;
  1640     return false;
  1641 }
  1642 
  1643 static pEp_rid_list_t* choose_resource_id(pEp_rid_list_t* rid_list) {
  1644     pEp_rid_list_t* retval = rid_list;
  1645     
  1646     /* multiple elements - least common case */
  1647     if (rid_list && rid_list->next) {
  1648         pEp_rid_list_t* rid_list_curr = rid_list;
  1649         retval = rid_list; 
  1650         
  1651         while (rid_list_curr) {
  1652             pEp_resource_id_type rid_type = rid_list_curr->rid_type;
  1653             if (rid_type == PEP_RID_CID)
  1654                 retval = rid_list_curr;
  1655             else if (rid_type == PEP_RID_FILENAME && has_exceptional_extension(rid_list_curr->rid))
  1656                 return rid_list_curr;
  1657             rid_list_curr = rid_list_curr->next;
  1658         }
  1659     } 
  1660     return retval;
  1661 }
  1662 
  1663 // static void split_inlined_and_attached(bloblist_t** inlined, bloblist_t** attached) {
  1664 //     bloblist_t** curr_pp = attached;
  1665 //     bloblist_t* curr = *curr_pp;
  1666 //     
  1667 //     bloblist_t* inline_ret = NULL;
  1668 //     bloblist_t** inline_curr_pp = &inline_ret;
  1669 //     
  1670 //     bloblist_t* att_ret = NULL;
  1671 //     bloblist_t** att_curr_pp = &att_ret;
  1672 //     
  1673 //     while (curr) {
  1674 //         if (curr->disposition == PEP_CONTENT_DISP_INLINE) {
  1675 //             *inline_curr_pp = curr;
  1676 //             inline_curr_pp = &(curr->next);
  1677 //         }
  1678 //         else {
  1679 //             *att_curr_pp = curr;
  1680 //             att_curr_pp = &(curr->next);            
  1681 //         }
  1682 //         *curr_pp = curr->next;
  1683 //         curr->next = NULL;
  1684 //         curr = *curr_pp;
  1685 //     }
  1686 //     
  1687 //     *inlined = inline_ret;
  1688 //     *attached = att_ret;
  1689 // }
  1690 
  1691 
  1692 static PEP_STATUS mime_encode_message_plain(
  1693         const message *msg,
  1694         bool omit_fields,
  1695         struct mailmime **result,
  1696         bool transport_encode,
  1697         bool set_attachment_forward_comment
  1698     )
  1699 {
  1700     struct mailmime * mime = NULL;
  1701     struct mailmime * submime = NULL;
  1702     int r;
  1703     PEP_STATUS status;
  1704     //char *subject;
  1705     char *plaintext;
  1706     char *htmltext;
  1707 
  1708     assert(msg);
  1709     assert(result);
  1710     
  1711     //subject = (msg->shortmsg) ? msg->shortmsg : "pEp";  // not used, yet.
  1712     plaintext = (msg->longmsg) ? msg->longmsg : "";
  1713     htmltext = msg->longmsg_formatted;
  1714 
  1715     if (htmltext && (htmltext[0] != '\0')) {
  1716         /* first, we need to strip out the inlined attachments to ensure this
  1717            gets set up correctly */
  1718            
  1719         status = mime_html_text(plaintext, htmltext, msg->attachments, &mime,
  1720                                 transport_encode);
  1721                 
  1722         if (status != PEP_STATUS_OK)
  1723             goto pEp_error;
  1724     }
  1725     else {
  1726         pEp_rid_list_t* resource = NULL;
  1727         if (is_PGP_message_text(plaintext)) {
  1728             resource = NULL;
  1729             int encoding_type = (transport_encode ? MAILMIME_MECHANISM_7BIT : 0);
  1730             mime = get_text_part(resource, "application/octet-stream", plaintext,
  1731                     strlen(plaintext), encoding_type);
  1732         }
  1733         else {
  1734             resource = NULL;
  1735             int encoding_type = (transport_encode ? MAILMIME_MECHANISM_QUOTED_PRINTABLE : 0);
  1736             mime = get_text_part(resource, "text/plain", plaintext, strlen(plaintext),
  1737                     encoding_type);
  1738         }
  1739         free_rid_list(resource);
  1740         
  1741         assert(mime);
  1742         if (mime == NULL)
  1743             goto enomem;
  1744     }
  1745 
  1746     bool normal_attachments = false;
  1747     
  1748     bloblist_t* traversal_ptr = msg->attachments;
  1749     
  1750     while (traversal_ptr) {
  1751         if (traversal_ptr->disposition != PEP_CONTENT_DISP_INLINE) {
  1752             normal_attachments = true;
  1753             break;
  1754         }
  1755         traversal_ptr = traversal_ptr->next;
  1756     }
  1757 
  1758     if (normal_attachments) {
  1759         submime = mime;
  1760         mime = part_multiple_new("multipart/mixed");
  1761         assert(mime);
  1762         if (mime == NULL)
  1763             goto enomem;
  1764 
  1765         r = mailmime_smart_add_part(mime, submime);
  1766         assert(r == MAILIMF_NO_ERROR);
  1767         if (r == MAILIMF_ERROR_MEMORY) {
  1768             goto enomem;
  1769         }
  1770         else {
  1771             // mailmime_smart_add_part() takes ownership of submime
  1772             submime = NULL;
  1773         }
  1774 
  1775         bloblist_t *_a;
  1776         bool first_one = true;
  1777         
  1778         for (_a = msg->attachments; _a != NULL; _a = _a->next) {
  1779 
  1780             if (_a->disposition == PEP_CONTENT_DISP_INLINE)
  1781                 continue;
  1782 
  1783             status = mime_attachment(_a, &submime, transport_encode,
  1784                                      (first_one && set_attachment_forward_comment));                         
  1785             if (status != PEP_STATUS_OK)
  1786                 goto pEp_error;
  1787             
  1788             first_one = false;    
  1789 
  1790             r = mailmime_smart_add_part(mime, submime);
  1791             assert(r == MAILIMF_NO_ERROR);
  1792             if (r == MAILIMF_ERROR_MEMORY) {
  1793                 goto enomem;
  1794             }
  1795             else {
  1796                 // mailmime_smart_add_part() takes ownership of submime
  1797                 submime = NULL;
  1798             }
  1799         }
  1800     }
  1801 
  1802     *result = mime;
  1803     return PEP_STATUS_OK;
  1804 
  1805 enomem:
  1806     status = PEP_OUT_OF_MEMORY;
  1807 
  1808 pEp_error:
  1809     if (mime)
  1810         mailmime_free(mime);
  1811 
  1812     if (submime)
  1813         mailmime_free(submime);
  1814 
  1815     return status;
  1816 }
  1817 
  1818 static PEP_STATUS mime_encode_message_PGP_MIME(
  1819         const message * msg,
  1820         bool omit_fields,
  1821         struct mailmime **result
  1822     )
  1823 {
  1824     struct mailmime * mime = NULL;
  1825     struct mailmime * submime = NULL;
  1826 	struct mailmime_parameter * param;
  1827     int r;
  1828     PEP_STATUS status;
  1829     char *plaintext;
  1830     size_t plaintext_size;
  1831 
  1832     assert(msg->attachments && msg->attachments->next &&
  1833             msg->attachments->next->value);
  1834 
  1835     plaintext = msg->attachments->next->value;
  1836     plaintext_size = msg->attachments->next->size;
  1837 
  1838     mime = part_multiple_new("multipart/encrypted");
  1839     assert(mime);
  1840     if (mime == NULL)
  1841         goto enomem;
  1842 
  1843     param = mailmime_param_new_with_data("protocol", "application/pgp-encrypted");
  1844     clist_append(mime->mm_content_type->ct_parameters, param);
  1845 
  1846     submime = get_pgp_encrypted_part();
  1847     assert(submime);
  1848     if (submime == NULL)
  1849         goto enomem;
  1850 
  1851     r = mailmime_smart_add_part(mime, submime);
  1852     assert(r == MAILIMF_NO_ERROR);
  1853     if (r == MAILIMF_ERROR_MEMORY) {
  1854         goto enomem;
  1855     }
  1856     else {
  1857         // mailmime_smart_add_part() takes ownership of submime
  1858         submime = NULL;
  1859     }
  1860 
  1861     pEp_rid_list_t* resource = new_rid_node(PEP_RID_FILENAME, "msg.asc");
  1862     submime = get_text_part(resource, "application/octet-stream", plaintext,
  1863             plaintext_size, MAILMIME_MECHANISM_7BIT);
  1864             
  1865     free_rid_list(resource);
  1866     
  1867     assert(submime);
  1868     if (submime == NULL)
  1869         goto enomem;
  1870 
  1871     r = mailmime_smart_add_part(mime, submime);
  1872     assert(r == MAILIMF_NO_ERROR);
  1873     if (r == MAILIMF_ERROR_MEMORY) {
  1874         goto enomem;
  1875     }
  1876     else {
  1877         // mailmime_smart_add_part() takes ownership of submime
  1878         submime = NULL;
  1879     }
  1880 
  1881     *result = mime;
  1882     return PEP_STATUS_OK;
  1883 
  1884 enomem:
  1885     status = PEP_OUT_OF_MEMORY;
  1886 
  1887     if (mime)
  1888         mailmime_free(mime);
  1889 
  1890     if (submime)
  1891         mailmime_free(submime);
  1892 
  1893     return status;
  1894 }
  1895 
  1896 PEP_STATUS _mime_encode_message_internal(
  1897         const message * msg,
  1898         bool omit_fields,
  1899         char **mimetext,
  1900         bool transport_encode,
  1901         bool set_attachment_forward_comment
  1902     )
  1903 {
  1904     PEP_STATUS status = PEP_STATUS_OK;
  1905     struct mailmime * msg_mime = NULL;
  1906     struct mailmime * mime = NULL;
  1907     struct mailimf_fields * fields = NULL;
  1908     char *buf = NULL;
  1909     int r;
  1910 
  1911     assert(msg);
  1912     assert(mimetext);
  1913 
  1914     if (!(msg && mimetext))
  1915         return PEP_ILLEGAL_VALUE;
  1916 
  1917     *mimetext = NULL;
  1918 
  1919     switch (msg->enc_format) {
  1920         case PEP_enc_none:
  1921             status = mime_encode_message_plain(msg, omit_fields, &mime, transport_encode, set_attachment_forward_comment);
  1922             break;
  1923 
  1924         // I'm presuming we should hardcore ignoring set_attachment_forward_comment here...
  1925         case PEP_enc_inline:
  1926             status = mime_encode_message_plain(msg, omit_fields, &mime, transport_encode, false);
  1927             break;
  1928 
  1929         case PEP_enc_S_MIME:
  1930             NOT_IMPLEMENTED
  1931                 
  1932         case PEP_enc_PGP_MIME:
  1933             status = mime_encode_message_PGP_MIME(msg, omit_fields, &mime);
  1934             break;
  1935 
  1936         case PEP_enc_PEP:
  1937             // today's pEp message format is PGP/MIME from the outside
  1938             status = mime_encode_message_PGP_MIME(msg, omit_fields, &mime);
  1939             break;
  1940 
  1941         default:
  1942             NOT_IMPLEMENTED
  1943     }
  1944 
  1945     if (status != PEP_STATUS_OK)
  1946         goto pEp_error;
  1947 
  1948     msg_mime = mailmime_new_message_data(NULL);
  1949     assert(msg_mime);
  1950     if (msg_mime == NULL)
  1951         goto enomem;
  1952 
  1953     r = mailmime_add_part(msg_mime, mime);
  1954     if (r) {
  1955         mailmime_free(mime);
  1956         goto enomem;
  1957     }
  1958     mime = NULL;
  1959 
  1960     if (!omit_fields) {
  1961         status = build_fields(msg, &fields);
  1962         if (status != PEP_STATUS_OK)
  1963             goto pEp_error;
  1964 
  1965         mailmime_set_imf_fields(msg_mime, fields);
  1966     }
  1967 
  1968     status = render_mime(msg_mime, &buf);
  1969     if (status != PEP_STATUS_OK)
  1970         goto pEp_error;
  1971 
  1972     mailmime_free(msg_mime);
  1973     *mimetext = buf;
  1974 
  1975     return PEP_STATUS_OK;
  1976 
  1977 enomem:
  1978     status = PEP_OUT_OF_MEMORY;
  1979 
  1980 pEp_error:
  1981     if (msg_mime)
  1982         mailmime_free(msg_mime);
  1983     else
  1984         if (mime)
  1985             mailmime_free(mime);
  1986 
  1987     return status;
  1988 }
  1989 
  1990 static pEp_identity *mailbox_to_identity(const struct mailimf_mailbox * mb)
  1991 {
  1992     char *username = NULL;
  1993 
  1994     assert(mb);
  1995     assert(mb->mb_addr_spec);
  1996 
  1997     if (mb->mb_addr_spec == NULL)
  1998         return NULL;
  1999 
  2000     if (mb->mb_display_name) {
  2001         size_t index = 0;
  2002         const int r = mailmime_encoded_phrase_parse("utf-8", mb->mb_display_name,
  2003                 strlen(mb->mb_display_name), &index, "utf-8", &username);
  2004         if (r)
  2005             goto enomem;
  2006     }
  2007 
  2008     pEp_identity *ident = new_identity(mb->mb_addr_spec, NULL, NULL, username);
  2009     if (ident == NULL)
  2010         goto enomem;
  2011     free(username);
  2012 
  2013     return ident;
  2014 
  2015 enomem:
  2016     free(username);
  2017     return NULL;
  2018 }
  2019 
  2020 static pEp_identity * mbl_to_identity(const struct mailimf_mailbox_list * mbl)
  2021 {
  2022     struct mailimf_mailbox * mb = clist_content(clist_begin(mbl->mb_list));
  2023     return mailbox_to_identity(mb);
  2024 }
  2025 
  2026 static identity_list * mal_to_identity_list(
  2027         const struct mailimf_address_list *mal
  2028     )
  2029 {
  2030     assert(mal);
  2031     clist *list = mal->ad_list;
  2032 
  2033     identity_list *il = new_identity_list(NULL);
  2034     if (il == NULL)
  2035         goto enomem;
  2036 
  2037     identity_list *_il = il;
  2038     for (clistiter *cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
  2039         pEp_identity *ident;
  2040 
  2041         struct mailimf_address *addr = clist_content(cur);
  2042         switch(addr->ad_type) {
  2043             case MAILIMF_ADDRESS_MAILBOX:
  2044                 ident = mailbox_to_identity(addr->ad_data.ad_mailbox);
  2045                 if (ident == NULL)
  2046                     goto enomem;
  2047                 _il = identity_list_add(_il, ident);
  2048                 if (_il == NULL)
  2049                     goto enomem;
  2050                 break;
  2051 
  2052             case MAILIMF_ADDRESS_GROUP:
  2053                 {
  2054                     struct mailimf_mailbox_list * mbl =
  2055                             addr->ad_data.ad_group->grp_mb_list;
  2056                     for (clistiter *cur2 = clist_begin(mbl->mb_list); cur2 != NULL;
  2057                             cur2 = clist_next(cur2)) {
  2058                         ident = mailbox_to_identity(clist_content(cur));
  2059                         if (ident == NULL)
  2060                             goto enomem;
  2061                         _il = identity_list_add(_il, ident);
  2062                         if (_il == NULL)
  2063                             goto enomem;
  2064                     }
  2065                 }
  2066                 break;
  2067 
  2068             default:
  2069                 assert(0);
  2070                 goto enomem;
  2071         }
  2072     }
  2073 
  2074     return il;
  2075 
  2076 enomem:
  2077     free_identity_list(il);
  2078     return NULL;
  2079 }
  2080 
  2081 static stringlist_t * clist_to_stringlist(const clist *list)
  2082 {
  2083     char *text = NULL;;
  2084     stringlist_t * sl = new_stringlist(NULL);
  2085     if (sl == NULL)
  2086         return NULL;
  2087 
  2088     stringlist_t *_sl = sl;
  2089     for (clistiter *cur = clist_begin(list); cur != NULL; cur = clist_next(cur)) {
  2090         char *phrase = clist_content(cur);
  2091         size_t index = 0;
  2092         
  2093         const int r = mailmime_encoded_phrase_parse("utf-8", phrase, strlen(phrase),
  2094                 &index, "utf-8", &text);
  2095         if (r)
  2096             goto enomem;
  2097 
  2098         _sl = stringlist_add(_sl, text);
  2099         if (_sl == NULL)
  2100             goto enomem;
  2101 
  2102         free(text);
  2103         text = NULL;
  2104     }
  2105 
  2106     return sl;
  2107 
  2108 enomem:
  2109     free_stringlist(sl);
  2110     free(text);
  2111 
  2112     return NULL;
  2113 }
  2114 
  2115 static PEP_STATUS read_fields(message *msg, clist *fieldlist)
  2116 {
  2117     PEP_STATUS status = PEP_STATUS_OK;
  2118     struct mailimf_field * _field;
  2119     clistiter *cur;
  2120     size_t index;
  2121     int r;
  2122     
  2123     stringpair_list_t *opt = msg->opt_fields;
  2124 
  2125     if (!fieldlist)
  2126         return PEP_STATUS_OK;
  2127         
  2128     for (cur = clist_begin(fieldlist); cur != NULL; cur = clist_next(cur)) {
  2129         _field = clist_content(cur);
  2130 
  2131         switch (_field->fld_type) {
  2132             case MAILIMF_FIELD_MESSAGE_ID:
  2133                 {
  2134                     char * text = _field->fld_data.fld_message_id->mid_value;
  2135 
  2136                     free(msg->id);
  2137                     index = 0;
  2138                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2139                             strlen(text), &index, "utf-8", &msg->id);
  2140                     if (r)
  2141                         goto enomem;
  2142                 }
  2143                 break;
  2144 
  2145             case MAILIMF_FIELD_SUBJECT:
  2146                 {
  2147                     char * text = _field->fld_data.fld_subject->sbj_value;
  2148 
  2149                     free(msg->shortmsg);
  2150                     index = 0;
  2151                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2152                             strlen(text), &index, "utf-8", &msg->shortmsg);
  2153                     if (r)
  2154                         goto enomem;
  2155                 }
  2156                 break;
  2157 
  2158             case MAILIMF_FIELD_ORIG_DATE:
  2159                 {
  2160                     struct mailimf_date_time *date =
  2161                         _field->fld_data.fld_orig_date->dt_date_time;
  2162 
  2163                     free_timestamp(msg->sent);
  2164                     msg->sent = etpantime_to_timestamp(date);
  2165                     if (msg->sent == NULL)
  2166                         goto enomem;
  2167                 }
  2168                 break;
  2169 
  2170             case MAILIMF_FIELD_FROM:
  2171                 {
  2172                     struct mailimf_mailbox_list *mbl =
  2173                             _field->fld_data.fld_from->frm_mb_list;
  2174                     pEp_identity *ident;
  2175 
  2176                     ident = mbl_to_identity(mbl);
  2177                     if (ident == NULL)
  2178                         goto pEp_error;
  2179 
  2180                     free_identity(msg->from);
  2181                     msg->from = ident;
  2182                 }
  2183                 break;
  2184 
  2185             case MAILIMF_FIELD_TO:
  2186                 {
  2187                     struct mailimf_address_list *mal =
  2188                             _field->fld_data.fld_to->to_addr_list;
  2189                     identity_list *il = mal_to_identity_list(mal);
  2190                     if (il == NULL)
  2191                         goto enomem;
  2192 
  2193                     free_identity_list(msg->to);
  2194                     msg->to = il;
  2195                 }
  2196                 break;
  2197 
  2198             case MAILIMF_FIELD_CC:
  2199                 {
  2200                     struct mailimf_address_list *mal =
  2201                             _field->fld_data.fld_cc->cc_addr_list;
  2202                     identity_list *il = mal_to_identity_list(mal);
  2203                     if (il == NULL)
  2204                         goto enomem;
  2205 
  2206                     free_identity_list(msg->cc);
  2207                     msg->cc = il;
  2208                 }
  2209                 break;
  2210 
  2211             case MAILIMF_FIELD_BCC:
  2212                 {
  2213                     struct mailimf_address_list *mal =
  2214                             _field->fld_data.fld_bcc->bcc_addr_list;
  2215                     identity_list *il = mal_to_identity_list(mal);
  2216                     if (il == NULL)
  2217                         goto enomem;
  2218 
  2219                     free_identity_list(msg->bcc);
  2220                     msg->bcc = il;
  2221                 }
  2222                 break;
  2223 
  2224             case MAILIMF_FIELD_REPLY_TO:
  2225                 {
  2226                     struct mailimf_address_list *mal =
  2227                             _field->fld_data.fld_reply_to->rt_addr_list;
  2228                     identity_list *il = mal_to_identity_list(mal);
  2229                     if (il == NULL)
  2230                         goto enomem;
  2231 
  2232                     free_identity_list(msg->reply_to);
  2233                     msg->reply_to = il;
  2234                 }
  2235                 break;
  2236 
  2237             case MAILIMF_FIELD_IN_REPLY_TO:
  2238                 {
  2239                     clist *list = _field->fld_data.fld_in_reply_to->mid_list;
  2240                     stringlist_t *sl = clist_to_stringlist(list);
  2241                     if (sl == NULL)
  2242                         goto enomem;
  2243 
  2244                     free_stringlist(msg->in_reply_to);
  2245                     msg->in_reply_to = sl;
  2246                 }
  2247                 break;
  2248 
  2249             case MAILIMF_FIELD_REFERENCES:
  2250                 {
  2251                     clist *list = _field->fld_data.fld_references->mid_list;
  2252                     stringlist_t *sl = clist_to_stringlist(list);
  2253                     if (sl == NULL)
  2254                         goto enomem;
  2255 
  2256                     free_stringlist(msg->references);
  2257                     msg->references = sl;
  2258                 }
  2259                 break;
  2260 
  2261             case MAILIMF_FIELD_KEYWORDS:
  2262                 {
  2263                     clist *list = _field->fld_data.fld_keywords->kw_list;
  2264                     stringlist_t *sl = clist_to_stringlist(list);
  2265                     if (sl == NULL)
  2266                         goto enomem;
  2267 
  2268                     free_stringlist(msg->keywords);
  2269                     msg->keywords = sl;
  2270                 }
  2271                 break;
  2272 
  2273             case MAILIMF_FIELD_COMMENTS:
  2274                 {
  2275                     char * text = _field->fld_data.fld_comments->cm_value;
  2276 
  2277                     free(msg->comments);
  2278                     index = 0;
  2279                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2280                             strlen(text), &index, "utf-8", &msg->comments);
  2281                     if (r)
  2282                         goto enomem;
  2283                 }
  2284                 break;
  2285 
  2286             case MAILIMF_FIELD_OPTIONAL_FIELD:
  2287                 {
  2288                     char * name =
  2289                             _field->fld_data.fld_optional_field->fld_name;
  2290                     char * value =
  2291                             _field->fld_data.fld_optional_field->fld_value;
  2292                     char *_value;
  2293 
  2294                     index = 0;
  2295                     r = mailmime_encoded_phrase_parse("utf-8", value,
  2296                             strlen(value), &index, "utf-8", &_value);
  2297                     if (r)
  2298                         goto enomem;
  2299 
  2300                     stringpair_t *pair = new_stringpair(name, _value);
  2301                     if (pair == NULL)
  2302                         goto enomem;
  2303 
  2304                     opt = stringpair_list_add(opt, pair);
  2305                     free(_value);
  2306                     if (opt == NULL)
  2307                         goto enomem;
  2308 
  2309                     if (msg->opt_fields == NULL)
  2310                         msg->opt_fields = opt;
  2311                 }
  2312                 break;
  2313         }
  2314     }
  2315     
  2316     return PEP_STATUS_OK;
  2317 
  2318 enomem:
  2319     status = PEP_OUT_OF_MEMORY;
  2320 
  2321 pEp_error:
  2322     return status;
  2323 }
  2324 
  2325 static PEP_STATUS interpret_body(struct mailmime *part, char **longmsg, size_t *size)
  2326 {
  2327     const char *text;
  2328     char *_longmsg;
  2329     size_t length;
  2330     size_t _size;
  2331     size_t index;
  2332     char *type = NULL;
  2333     char *charset = NULL;
  2334 
  2335     assert(part);
  2336     assert(longmsg);
  2337 
  2338     *longmsg = NULL;
  2339     if (size)
  2340         *size = 0;
  2341 
  2342     if (part->mm_body == NULL)
  2343         return PEP_ILLEGAL_VALUE;
  2344 
  2345     text = part->mm_body-> dt_data.dt_text.dt_data;
  2346     if (text == NULL)
  2347         return PEP_ILLEGAL_VALUE;
  2348 
  2349     length = part->mm_body->dt_data.dt_text.dt_length;
  2350 
  2351     if (part->mm_body->dt_encoded) {
  2352         int code = part->mm_body->dt_encoding;
  2353         index = 0;
  2354         int r = mailmime_part_parse(text, length, &index, code, &_longmsg, &_size);
  2355         switch (r) {
  2356             case MAILIMF_NO_ERROR:
  2357                 break;
  2358             case MAILIMF_ERROR_MEMORY:
  2359                 return PEP_OUT_OF_MEMORY;
  2360             default:
  2361                 return PEP_ILLEGAL_VALUE;
  2362         }
  2363     }
  2364     else {
  2365         _size = length + 1;
  2366         _longmsg = strndup(text, length);
  2367         if (_longmsg == NULL)
  2368             return PEP_OUT_OF_MEMORY;
  2369     }
  2370 
  2371     if (part->mm_content_type) {
  2372         if (_get_content_type(part->mm_content_type, &type, &charset) == 0) {
  2373             if (charset && strncasecmp(charset, "utf-8", 5) != 0) {
  2374                 char * _text;
  2375                 int r = charconv("utf-8", charset, _longmsg, _size, &_text);
  2376                 switch (r) {
  2377                     case MAILIMF_NO_ERROR:
  2378                         break;
  2379                     case MAILIMF_ERROR_MEMORY:
  2380                         return PEP_OUT_OF_MEMORY;
  2381                     default:
  2382                         return PEP_ILLEGAL_VALUE;
  2383                 }
  2384                 free(_longmsg);
  2385                 _longmsg = _text;
  2386             }
  2387         }
  2388     }
  2389     // FIXME: KG - we now have the text we want.
  2390     // Now we need to strip sigs and process them if they are there..
  2391     
  2392 
  2393     *longmsg = _longmsg;
  2394     if (size)
  2395         *size = _size;
  2396 
  2397     return PEP_STATUS_OK;
  2398 }
  2399 
  2400 // THIS IS A BEST-EFFORT ONLY FUNCTION, AND WE ARE NOT DOING MORE THAN THE
  2401 // SUBJECT FOR NOW.
  2402 static PEP_STATUS interpret_protected_headers(
  2403         struct mailmime* mime, 
  2404         message* msg
  2405     )
  2406 {
  2407     // N.B. this is *very much* enigmail output specific, and right now,
  2408     // we only care about subject replacement.
  2409     const char* header_string = "Content-Type: text/rfc822-headers; protected-headers=\"v1\"\nContent-Disposition: inline\n\n";
  2410     size_t content_length = mime->mm_length;
  2411     size_t header_strlen = strlen(header_string);
  2412     if (header_strlen < content_length) {
  2413         const char* headerblock = mime->mm_mime_start;
  2414         size_t subject_len = 0;
  2415         headerblock = strstr(headerblock, header_string);
  2416         if (headerblock) {
  2417             const char* subj_start = "Subject: ";
  2418             headerblock = strstr(headerblock, subj_start);
  2419             if (headerblock) {
  2420                 size_t subj_len = strlen(subj_start);
  2421                 headerblock += subj_len;
  2422                 char* end_pt = strstr(headerblock, "\n");
  2423                 if (end_pt) {
  2424                     if (end_pt != mime->mm_mime_start && *(end_pt - 1) == '\r')
  2425                         end_pt--;
  2426                     subject_len = end_pt - headerblock;
  2427                     char* new_subj = (char*)calloc(subject_len + 1, 1);
  2428                     if (new_subj) {
  2429                         strlcpy(new_subj, headerblock, subject_len + 1);
  2430                         free(msg->shortmsg);
  2431                         msg->shortmsg = new_subj;
  2432                     }    
  2433                 } // if there's no endpoint, there's something wrong here so we ignore all
  2434                   // This is best effort.
  2435             }
  2436         }
  2437     }
  2438     return PEP_STATUS_OK;
  2439 }
  2440 
  2441 // ONLY for main part!!!
  2442 static PEP_STATUS process_multipart_related(struct mailmime *mime,
  2443                                             message *msg) {
  2444     PEP_STATUS status = PEP_STATUS_OK;
  2445 
  2446     assert(mime);
  2447     assert(msg);
  2448 
  2449     clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;                                                
  2450 
  2451     if (partlist == NULL)
  2452         return PEP_ILLEGAL_VALUE;
  2453 
  2454     clistiter *cur = clist_begin(partlist);
  2455     struct mailmime *part = clist_content(cur);
  2456     
  2457     if (part == NULL)
  2458         return PEP_ILLEGAL_VALUE;
  2459 
  2460     struct mailmime_content *content = part->mm_content_type;    
  2461     assert(content);
  2462     
  2463     if (content == NULL)
  2464         return PEP_ILLEGAL_VALUE;
  2465 
  2466     if (_is_text_part(content, "html")) {
  2467         status = interpret_body(part, &msg->longmsg_formatted,
  2468                 NULL);
  2469         if (status)
  2470             return status;
  2471     }
  2472     else {
  2473         // ???
  2474         // This is what we would have done before, so... no
  2475         // worse than the status quo. But FIXME!
  2476         status = interpret_MIME(part, msg, NULL);
  2477         if (status)
  2478             return status;
  2479     }
  2480     
  2481     for (cur = clist_next(cur); cur; cur = clist_next(cur)) {
  2482         part = clist_content(cur);
  2483         if (part == NULL)
  2484             return PEP_ILLEGAL_VALUE;
  2485 
  2486         content = part->mm_content_type;
  2487         assert(content);
  2488         if (content == NULL)
  2489             return PEP_ILLEGAL_VALUE;
  2490 
  2491         status = interpret_MIME(part, msg, NULL);
  2492         if (status)
  2493             return status;
  2494     }
  2495     return status;
  2496 }
  2497 
  2498 static PEP_STATUS interpret_MIME(
  2499         struct mailmime *mime,
  2500         message *msg,
  2501         bool* raise_msg_attachment
  2502     )
  2503 {
  2504     PEP_STATUS status = PEP_STATUS_OK;
  2505 
  2506     assert(mime);
  2507     assert(msg);
  2508 
  2509     struct mailmime_content *content = mime->mm_content_type;
  2510     if (content) {
  2511         if (_is_multipart(content, "alternative")) {
  2512             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2513             if (partlist == NULL)
  2514                 return PEP_ILLEGAL_VALUE;
  2515 
  2516             clistiter *cur;
  2517             for (cur = clist_begin(partlist); cur; cur = clist_next(cur)) {
  2518                 struct mailmime *part = clist_content(cur);
  2519                 if (part == NULL)
  2520                     return PEP_ILLEGAL_VALUE;
  2521 
  2522                 content = part->mm_content_type;
  2523                 assert(content);
  2524                 if (content == NULL)
  2525                     return PEP_ILLEGAL_VALUE;
  2526 
  2527                 if (_is_text_part(content, "plain") && msg->longmsg == NULL) {
  2528                     status = interpret_body(part, &msg->longmsg, NULL);
  2529                     if (status)
  2530                         return status;
  2531                 }
  2532                 else if (_is_text_part(content, "rfc822-headers")) {
  2533                     status = interpret_protected_headers(part, msg);
  2534                     if (status)
  2535                         return status;
  2536                 }
  2537                 else if (_is_text_part(content, "html") &&
  2538                         msg->longmsg_formatted == NULL) {
  2539                     status = interpret_body(part, &msg->longmsg_formatted,
  2540                             NULL);
  2541                     if (status)
  2542                         return status;
  2543                 }
  2544                 else if (_is_multipart(content, "related") && 
  2545                     msg->longmsg_formatted == NULL) {
  2546                     status = process_multipart_related(part, msg);
  2547                     if (status)
  2548                         return status;
  2549                 }
  2550                 else /* add as attachment */ {
  2551                     status = interpret_MIME(part, msg, NULL);
  2552                     if (status)
  2553                         return status;
  2554                 }
  2555             }
  2556         }
  2557         else if (_is_multipart(content, "encrypted")) {
  2558             if (msg->longmsg == NULL)
  2559                 msg->longmsg = strdup("");
  2560             assert(msg->longmsg);
  2561             if (!msg->longmsg)
  2562                 return PEP_OUT_OF_MEMORY;
  2563 
  2564             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2565             if (partlist == NULL)
  2566                 return PEP_ILLEGAL_VALUE;
  2567 
  2568             clistiter *cur;
  2569             for (cur = clist_begin(partlist); cur; cur = clist_next(cur)) {
  2570                 struct mailmime *part= clist_content(cur);
  2571                 if (part == NULL)
  2572                     return PEP_ILLEGAL_VALUE;
  2573 
  2574                 status = interpret_MIME(part, msg, NULL);
  2575                 if (status != PEP_STATUS_OK)
  2576                     return status;
  2577             }
  2578         }
  2579         else if (_is_multipart(content, NULL)) {
  2580             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2581             if (partlist == NULL)
  2582                 return PEP_ILLEGAL_VALUE;
  2583 
  2584             clistiter *cur;
  2585             // only add raise_msg_attachment on 2nd part!
  2586             int _att_count = 0;
  2587             for (cur = clist_begin(partlist); cur; cur = clist_next(cur), _att_count++) {
  2588                 struct mailmime *part= clist_content(cur);
  2589                 if (part == NULL)
  2590                     return PEP_ILLEGAL_VALUE;
  2591                 status = interpret_MIME(part, msg, _att_count == 1 ? raise_msg_attachment : NULL);
  2592                 if (status != PEP_STATUS_OK)
  2593                     return status;
  2594             }
  2595         }
  2596         else {
  2597             if (_is_text_part(content, "html") &&
  2598                 msg->longmsg_formatted == NULL &&
  2599                 msg->longmsg == NULL) {
  2600                 status = interpret_body(mime, &msg->longmsg_formatted,
  2601                                         NULL);
  2602                 if (status)
  2603                     return status;
  2604             }
  2605             else if (_is_text_part(content, "rfc822-headers")) {
  2606                 status = interpret_protected_headers(mime, msg);
  2607                 if (status)
  2608                     return status;
  2609             }
  2610             else if (_is_text_part(content, "plain") && 
  2611                      msg->longmsg == NULL && msg->longmsg_formatted == NULL) {
  2612                 status = interpret_body(mime, &msg->longmsg, NULL);
  2613                 if (status)
  2614                     return status;
  2615             }            
  2616             else if (_is_text_part(content, NULL) && 
  2617                      !_is_text_part(content, "plain") &&
  2618                      msg->longmsg == NULL) {
  2619                 status = interpret_body(mime, &msg->longmsg, NULL);
  2620                 if (status)
  2621                     return status;
  2622             }
  2623             else {
  2624                 // Fixme - we need a control on recursion level here - KG: maybe NOT. We only go to depth 1.
  2625                 if (raise_msg_attachment != NULL) {
  2626                     bool is_msg = (_is_message_part(content, "rfc822") || _is_text_part(content, "rfc822"));
  2627                     if (is_msg) {
  2628                         if (content->ct_parameters) {
  2629                             clistiter *cur;
  2630                             for (cur = clist_begin(content->ct_parameters); cur; cur =
  2631                                  clist_next(cur)) {
  2632                                 struct mailmime_parameter * param = clist_content(cur);
  2633                                 if (param && param->pa_name && strcasecmp(param->pa_name, "forwarded") == 0) {
  2634                                     if (param->pa_value && strcasecmp(param->pa_value, "no") == 0) {
  2635                                         *raise_msg_attachment = true;
  2636                                         break;
  2637                                     }
  2638                                 }
  2639                             }
  2640                         }
  2641                     }
  2642                 }
  2643                 char *data = NULL;
  2644                 size_t size = 0;
  2645                 char * mime_type;
  2646                 char * charset;
  2647                 char * filename;
  2648                 int r;
  2649 
  2650                 r = _get_content_type(content, &mime_type, &charset);
  2651                 switch (r) {
  2652                     case 0:
  2653                         break;
  2654                     case EINVAL:
  2655                         return PEP_ILLEGAL_VALUE;
  2656                     case ENOMEM:
  2657                         return PEP_OUT_OF_MEMORY;
  2658                     default:
  2659                         return PEP_UNKNOWN_ERROR;
  2660                 }
  2661 
  2662                 assert(mime_type);
  2663 
  2664                 status = interpret_body(mime, &data, &size);
  2665                 if (status)
  2666                     return status;
  2667 
  2668                 pEp_rid_list_t* resource_id_list = _get_resource_id_list(mime);
  2669                 pEp_rid_list_t* chosen_resource_id = choose_resource_id(resource_id_list);
  2670                 
  2671                 //filename = _get_filename_or_cid(mime);
  2672                 char *_filename = NULL;
  2673                 
  2674                 if (chosen_resource_id) {
  2675                     filename = chosen_resource_id->rid;
  2676                     size_t index = 0;
  2677                     /* NOTA BENE */
  2678                     /* The prefix we just added shouldn't be a problem - this is about decoding %XX (RFC 2392) */
  2679                     /* If it becomes one, we have some MESSY fixing to do. :(                                  */
  2680                     r = mailmime_encoded_phrase_parse("utf-8", filename,
  2681                             strlen(filename), &index, "utf-8", &_filename);
  2682                     if (r) {
  2683                         goto enomem;
  2684                     }
  2685                     char* file_prefix = NULL;
  2686                     
  2687                     /* in case there are others later */
  2688                     switch (chosen_resource_id->rid_type) {
  2689                         case PEP_RID_CID:
  2690                             file_prefix = "cid";
  2691                             break;
  2692                         case PEP_RID_FILENAME:
  2693                             file_prefix = "file";
  2694                             break;
  2695                         default:
  2696                             break;
  2697                     }
  2698 
  2699                     
  2700                     if (file_prefix) {
  2701                         filename = build_uri(file_prefix, _filename);
  2702                         free(_filename);
  2703                         _filename = filename;
  2704                     }
  2705                 }
  2706 
  2707                 bloblist_t *_a = bloblist_add(msg->attachments, data, size,
  2708                         mime_type, _filename);
  2709                 free(_filename);
  2710                 free_rid_list(resource_id_list);
  2711                 resource_id_list = NULL;
  2712                 if (_a == NULL)
  2713                     return PEP_OUT_OF_MEMORY;
  2714                 if (msg->attachments == NULL)
  2715                     msg->attachments = _a;
  2716             }
  2717         }
  2718     }
  2719 
  2720     return PEP_STATUS_OK;
  2721 
  2722 enomem:
  2723     return PEP_OUT_OF_MEMORY;
  2724 }
  2725 
  2726 DYNAMIC_API PEP_STATUS mime_decode_message(
  2727         const char *mimetext,
  2728         size_t size,
  2729         message **msg
  2730     )
  2731 {
  2732     return _mime_decode_message_internal(mimetext, size, msg, NULL);
  2733 }        
  2734 
  2735 
  2736 PEP_STATUS _mime_decode_message_internal(
  2737         const char *mimetext,
  2738         size_t size,
  2739         message **msg,
  2740         bool* raise_msg_attachment
  2741     )
  2742 {
  2743     PEP_STATUS status = PEP_STATUS_OK;
  2744     struct mailmime * mime = NULL;
  2745     int r;
  2746     message *_msg = NULL;
  2747     size_t index;
  2748 
  2749     assert(mimetext);
  2750     assert(msg);
  2751 
  2752     if (!(mimetext && msg))
  2753         return PEP_ILLEGAL_VALUE;
  2754 
  2755     *msg = NULL;
  2756 
  2757     index = 0;
  2758     r = mailmime_parse(mimetext, size, &index, &mime);
  2759     assert(r == 0);
  2760     assert(mime);
  2761     if (r) {
  2762         if (r == MAILIMF_ERROR_MEMORY)
  2763             goto enomem;
  2764         else
  2765             goto err_mime;
  2766     }
  2767 
  2768     _msg = calloc(1, sizeof(message));
  2769     assert(_msg);
  2770     if (_msg == NULL)
  2771         goto enomem;
  2772 
  2773     clist * _fieldlist = _get_fields(mime);
  2774     if (_fieldlist) {
  2775         status = read_fields(_msg, _fieldlist);
  2776         if (status != PEP_STATUS_OK)
  2777             goto pEp_error;
  2778     }
  2779 
  2780     struct mailmime_content *content = _get_content(mime);
  2781 
  2782     if (content) {
  2783         status = interpret_MIME(mime->mm_data.mm_message.mm_msg_mime,
  2784                 _msg, raise_msg_attachment);
  2785         if (status != PEP_STATUS_OK)
  2786             goto pEp_error;
  2787     }
  2788 
  2789     mailmime_free(mime);
  2790     *msg = _msg;
  2791 
  2792     return status;
  2793 
  2794 err_mime:
  2795     status = PEP_ILLEGAL_VALUE;
  2796     goto pEp_error;
  2797 
  2798 enomem:
  2799     status = PEP_OUT_OF_MEMORY;
  2800 
  2801 pEp_error:
  2802     free_message(_msg);
  2803 
  2804     if (mime)
  2805         mailmime_free(mime);
  2806 
  2807     return status;
  2808 }