src/etpan_mime.c
author Krista 'DarthMama' Bennett <krista@pep.foundation>
Thu, 16 May 2019 16:56:09 +0200
branchENGINE-553
changeset 3710 d7c1395f473c
parent 3678 07aceb41d96e
child 3729 1c6553767712
permissions -rw-r--r--
ENGINE-553: Message 2.1 - addition of forward=no is there, and is detected. Now: to act on it. (Yes, Roker, I will file you a task against libpEpMIME once I have this impl sorted :)
     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("forward", "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 = new_rid_node(PEP_RID_FILENAME, "msg.txt");
  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 : 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) {
  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) {
  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) {
  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) {
  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) {
  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) {
  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) {
  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) {
  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) {
  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) {
  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 = new_rid_node(PEP_RID_FILENAME, "msg.asc");
  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 = new_rid_node(PEP_RID_FILENAME, "msg.txt");
  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             NOT_IMPLEMENTED
  1938 
  1939         default:
  1940             NOT_IMPLEMENTED
  1941     }
  1942 
  1943     if (status != PEP_STATUS_OK)
  1944         goto pEp_error;
  1945 
  1946     msg_mime = mailmime_new_message_data(NULL);
  1947     assert(msg_mime);
  1948     if (msg_mime == NULL)
  1949         goto enomem;
  1950 
  1951     r = mailmime_add_part(msg_mime, mime);
  1952     if (r) {
  1953         mailmime_free(mime);
  1954         goto enomem;
  1955     }
  1956     mime = NULL;
  1957 
  1958     if (!omit_fields) {
  1959         status = build_fields(msg, &fields);
  1960         if (status != PEP_STATUS_OK)
  1961             goto pEp_error;
  1962 
  1963         mailmime_set_imf_fields(msg_mime, fields);
  1964     }
  1965 
  1966     status = render_mime(msg_mime, &buf);
  1967     if (status != PEP_STATUS_OK)
  1968         goto pEp_error;
  1969 
  1970     mailmime_free(msg_mime);
  1971     *mimetext = buf;
  1972 
  1973     return PEP_STATUS_OK;
  1974 
  1975 enomem:
  1976     status = PEP_OUT_OF_MEMORY;
  1977 
  1978 pEp_error:
  1979     if (msg_mime)
  1980         mailmime_free(msg_mime);
  1981     else
  1982         if (mime)
  1983             mailmime_free(mime);
  1984 
  1985     return status;
  1986 }
  1987 
  1988 static pEp_identity *mailbox_to_identity(const struct mailimf_mailbox * mb)
  1989 {
  1990     char *username = NULL;
  1991 
  1992     assert(mb);
  1993     assert(mb->mb_addr_spec);
  1994 
  1995     if (mb->mb_addr_spec == NULL)
  1996         return NULL;
  1997 
  1998     if (mb->mb_display_name) {
  1999         size_t index = 0;
  2000         const int r = mailmime_encoded_phrase_parse("utf-8", mb->mb_display_name,
  2001                 strlen(mb->mb_display_name), &index, "utf-8", &username);
  2002         if (r)
  2003             goto enomem;
  2004     }
  2005 
  2006     pEp_identity *ident = new_identity(mb->mb_addr_spec, NULL, NULL, username);
  2007     if (ident == NULL)
  2008         goto enomem;
  2009     free(username);
  2010 
  2011     return ident;
  2012 
  2013 enomem:
  2014     free(username);
  2015     return NULL;
  2016 }
  2017 
  2018 static pEp_identity * mbl_to_identity(const struct mailimf_mailbox_list * mbl)
  2019 {
  2020     struct mailimf_mailbox * mb = clist_content(clist_begin(mbl->mb_list));
  2021     return mailbox_to_identity(mb);
  2022 }
  2023 
  2024 static identity_list * mal_to_identity_list(
  2025         const struct mailimf_address_list *mal
  2026     )
  2027 {
  2028     assert(mal);
  2029     clist *list = mal->ad_list;
  2030 
  2031     identity_list *il = new_identity_list(NULL);
  2032     if (il == NULL)
  2033         goto enomem;
  2034 
  2035     identity_list *_il = il;
  2036     for (clistiter *cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
  2037         pEp_identity *ident;
  2038 
  2039         struct mailimf_address *addr = clist_content(cur);
  2040         switch(addr->ad_type) {
  2041             case MAILIMF_ADDRESS_MAILBOX:
  2042                 ident = mailbox_to_identity(addr->ad_data.ad_mailbox);
  2043                 if (ident == NULL)
  2044                     goto enomem;
  2045                 _il = identity_list_add(_il, ident);
  2046                 if (_il == NULL)
  2047                     goto enomem;
  2048                 break;
  2049 
  2050             case MAILIMF_ADDRESS_GROUP:
  2051                 {
  2052                     struct mailimf_mailbox_list * mbl =
  2053                             addr->ad_data.ad_group->grp_mb_list;
  2054                     for (clistiter *cur2 = clist_begin(mbl->mb_list); cur2 != NULL;
  2055                             cur2 = clist_next(cur2)) {
  2056                         ident = mailbox_to_identity(clist_content(cur));
  2057                         if (ident == NULL)
  2058                             goto enomem;
  2059                         _il = identity_list_add(_il, ident);
  2060                         if (_il == NULL)
  2061                             goto enomem;
  2062                     }
  2063                 }
  2064                 break;
  2065 
  2066             default:
  2067                 assert(0);
  2068                 goto enomem;
  2069         }
  2070     }
  2071 
  2072     return il;
  2073 
  2074 enomem:
  2075     free_identity_list(il);
  2076     return NULL;
  2077 }
  2078 
  2079 static stringlist_t * clist_to_stringlist(const clist *list)
  2080 {
  2081     char *text = NULL;;
  2082     stringlist_t * sl = new_stringlist(NULL);
  2083     if (sl == NULL)
  2084         return NULL;
  2085 
  2086     stringlist_t *_sl = sl;
  2087     for (clistiter *cur = clist_begin(list); cur != NULL; cur = clist_next(cur)) {
  2088         char *phrase = clist_content(cur);
  2089         size_t index = 0;
  2090         
  2091         const int r = mailmime_encoded_phrase_parse("utf-8", phrase, strlen(phrase),
  2092                 &index, "utf-8", &text);
  2093         if (r)
  2094             goto enomem;
  2095 
  2096         _sl = stringlist_add(_sl, text);
  2097         if (_sl == NULL)
  2098             goto enomem;
  2099 
  2100         free(text);
  2101         text = NULL;
  2102     }
  2103 
  2104     return sl;
  2105 
  2106 enomem:
  2107     free_stringlist(sl);
  2108     free(text);
  2109 
  2110     return NULL;
  2111 }
  2112 
  2113 static PEP_STATUS read_fields(message *msg, clist *fieldlist)
  2114 {
  2115     PEP_STATUS status = PEP_STATUS_OK;
  2116     struct mailimf_field * _field;
  2117     clistiter *cur;
  2118     size_t index;
  2119     int r;
  2120     
  2121     stringpair_list_t *opt = msg->opt_fields;
  2122 
  2123     if (!fieldlist)
  2124         return PEP_STATUS_OK;
  2125         
  2126     for (cur = clist_begin(fieldlist); cur != NULL; cur = clist_next(cur)) {
  2127         _field = clist_content(cur);
  2128 
  2129         switch (_field->fld_type) {
  2130             case MAILIMF_FIELD_MESSAGE_ID:
  2131                 {
  2132                     char * text = _field->fld_data.fld_message_id->mid_value;
  2133 
  2134                     free(msg->id);
  2135                     index = 0;
  2136                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2137                             strlen(text), &index, "utf-8", &msg->id);
  2138                     if (r)
  2139                         goto enomem;
  2140                 }
  2141                 break;
  2142 
  2143             case MAILIMF_FIELD_SUBJECT:
  2144                 {
  2145                     char * text = _field->fld_data.fld_subject->sbj_value;
  2146 
  2147                     free(msg->shortmsg);
  2148                     index = 0;
  2149                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2150                             strlen(text), &index, "utf-8", &msg->shortmsg);
  2151                     if (r)
  2152                         goto enomem;
  2153                 }
  2154                 break;
  2155 
  2156             case MAILIMF_FIELD_ORIG_DATE:
  2157                 {
  2158                     struct mailimf_date_time *date =
  2159                         _field->fld_data.fld_orig_date->dt_date_time;
  2160 
  2161                     free_timestamp(msg->sent);
  2162                     msg->sent = etpantime_to_timestamp(date);
  2163                     if (msg->sent == NULL)
  2164                         goto enomem;
  2165                 }
  2166                 break;
  2167 
  2168             case MAILIMF_FIELD_FROM:
  2169                 {
  2170                     struct mailimf_mailbox_list *mbl =
  2171                             _field->fld_data.fld_from->frm_mb_list;
  2172                     pEp_identity *ident;
  2173 
  2174                     ident = mbl_to_identity(mbl);
  2175                     if (ident == NULL)
  2176                         goto pEp_error;
  2177 
  2178                     free_identity(msg->from);
  2179                     msg->from = ident;
  2180                 }
  2181                 break;
  2182 
  2183             case MAILIMF_FIELD_TO:
  2184                 {
  2185                     struct mailimf_address_list *mal =
  2186                             _field->fld_data.fld_to->to_addr_list;
  2187                     identity_list *il = mal_to_identity_list(mal);
  2188                     if (il == NULL)
  2189                         goto enomem;
  2190 
  2191                     free_identity_list(msg->to);
  2192                     msg->to = il;
  2193                 }
  2194                 break;
  2195 
  2196             case MAILIMF_FIELD_CC:
  2197                 {
  2198                     struct mailimf_address_list *mal =
  2199                             _field->fld_data.fld_cc->cc_addr_list;
  2200                     identity_list *il = mal_to_identity_list(mal);
  2201                     if (il == NULL)
  2202                         goto enomem;
  2203 
  2204                     free_identity_list(msg->cc);
  2205                     msg->cc = il;
  2206                 }
  2207                 break;
  2208 
  2209             case MAILIMF_FIELD_BCC:
  2210                 {
  2211                     struct mailimf_address_list *mal =
  2212                             _field->fld_data.fld_bcc->bcc_addr_list;
  2213                     identity_list *il = mal_to_identity_list(mal);
  2214                     if (il == NULL)
  2215                         goto enomem;
  2216 
  2217                     free_identity_list(msg->bcc);
  2218                     msg->bcc = il;
  2219                 }
  2220                 break;
  2221 
  2222             case MAILIMF_FIELD_REPLY_TO:
  2223                 {
  2224                     struct mailimf_address_list *mal =
  2225                             _field->fld_data.fld_reply_to->rt_addr_list;
  2226                     identity_list *il = mal_to_identity_list(mal);
  2227                     if (il == NULL)
  2228                         goto enomem;
  2229 
  2230                     free_identity_list(msg->reply_to);
  2231                     msg->reply_to = il;
  2232                 }
  2233                 break;
  2234 
  2235             case MAILIMF_FIELD_IN_REPLY_TO:
  2236                 {
  2237                     clist *list = _field->fld_data.fld_in_reply_to->mid_list;
  2238                     stringlist_t *sl = clist_to_stringlist(list);
  2239                     if (sl == NULL)
  2240                         goto enomem;
  2241 
  2242                     free_stringlist(msg->in_reply_to);
  2243                     msg->in_reply_to = sl;
  2244                 }
  2245                 break;
  2246 
  2247             case MAILIMF_FIELD_REFERENCES:
  2248                 {
  2249                     clist *list = _field->fld_data.fld_references->mid_list;
  2250                     stringlist_t *sl = clist_to_stringlist(list);
  2251                     if (sl == NULL)
  2252                         goto enomem;
  2253 
  2254                     free_stringlist(msg->references);
  2255                     msg->references = sl;
  2256                 }
  2257                 break;
  2258 
  2259             case MAILIMF_FIELD_KEYWORDS:
  2260                 {
  2261                     clist *list = _field->fld_data.fld_keywords->kw_list;
  2262                     stringlist_t *sl = clist_to_stringlist(list);
  2263                     if (sl == NULL)
  2264                         goto enomem;
  2265 
  2266                     free_stringlist(msg->keywords);
  2267                     msg->keywords = sl;
  2268                 }
  2269                 break;
  2270 
  2271             case MAILIMF_FIELD_COMMENTS:
  2272                 {
  2273                     char * text = _field->fld_data.fld_comments->cm_value;
  2274 
  2275                     free(msg->comments);
  2276                     index = 0;
  2277                     r = mailmime_encoded_phrase_parse("utf-8", text,
  2278                             strlen(text), &index, "utf-8", &msg->comments);
  2279                     if (r)
  2280                         goto enomem;
  2281                 }
  2282                 break;
  2283 
  2284             case MAILIMF_FIELD_OPTIONAL_FIELD:
  2285                 {
  2286                     char * name =
  2287                             _field->fld_data.fld_optional_field->fld_name;
  2288                     char * value =
  2289                             _field->fld_data.fld_optional_field->fld_value;
  2290                     char *_value;
  2291 
  2292                     index = 0;
  2293                     r = mailmime_encoded_phrase_parse("utf-8", value,
  2294                             strlen(value), &index, "utf-8", &_value);
  2295                     if (r)
  2296                         goto enomem;
  2297 
  2298                     stringpair_t *pair = new_stringpair(name, _value);
  2299                     if (pair == NULL)
  2300                         goto enomem;
  2301 
  2302                     opt = stringpair_list_add(opt, pair);
  2303                     free(_value);
  2304                     if (opt == NULL)
  2305                         goto enomem;
  2306 
  2307                     if (msg->opt_fields == NULL)
  2308                         msg->opt_fields = opt;
  2309                 }
  2310                 break;
  2311         }
  2312     }
  2313     
  2314     return PEP_STATUS_OK;
  2315 
  2316 enomem:
  2317     status = PEP_OUT_OF_MEMORY;
  2318 
  2319 pEp_error:
  2320     return status;
  2321 }
  2322 
  2323 static PEP_STATUS interpret_body(struct mailmime *part, char **longmsg, size_t *size)
  2324 {
  2325     const char *text;
  2326     char *_longmsg;
  2327     size_t length;
  2328     size_t _size;
  2329     size_t index;
  2330     char *type = NULL;
  2331     char *charset = NULL;
  2332 
  2333     assert(part);
  2334     assert(longmsg);
  2335 
  2336     *longmsg = NULL;
  2337     if (size)
  2338         *size = 0;
  2339 
  2340     if (part->mm_body == NULL)
  2341         return PEP_ILLEGAL_VALUE;
  2342 
  2343     text = part->mm_body-> dt_data.dt_text.dt_data;
  2344     if (text == NULL)
  2345         return PEP_ILLEGAL_VALUE;
  2346 
  2347     length = part->mm_body->dt_data.dt_text.dt_length;
  2348 
  2349     if (part->mm_body->dt_encoded) {
  2350         int code = part->mm_body->dt_encoding;
  2351         index = 0;
  2352         int r = mailmime_part_parse(text, length, &index, code, &_longmsg, &_size);
  2353         switch (r) {
  2354             case MAILIMF_NO_ERROR:
  2355                 break;
  2356             case MAILIMF_ERROR_MEMORY:
  2357                 return PEP_OUT_OF_MEMORY;
  2358             default:
  2359                 return PEP_ILLEGAL_VALUE;
  2360         }
  2361     }
  2362     else {
  2363         _size = length + 1;
  2364         _longmsg = strndup(text, length);
  2365         if (_longmsg == NULL)
  2366             return PEP_OUT_OF_MEMORY;
  2367     }
  2368 
  2369     if (part->mm_content_type) {
  2370         if (_get_content_type(part->mm_content_type, &type, &charset) == 0) {
  2371             if (charset && strncasecmp(charset, "utf-8", 5) != 0) {
  2372                 char * _text;
  2373                 int r = charconv("utf-8", charset, _longmsg, _size, &_text);
  2374                 switch (r) {
  2375                     case MAILIMF_NO_ERROR:
  2376                         break;
  2377                     case MAILIMF_ERROR_MEMORY:
  2378                         return PEP_OUT_OF_MEMORY;
  2379                     default:
  2380                         return PEP_ILLEGAL_VALUE;
  2381                 }
  2382                 free(_longmsg);
  2383                 _longmsg = _text;
  2384             }
  2385         }
  2386     }
  2387     // FIXME: KG - we now have the text we want.
  2388     // Now we need to strip sigs and process them if they are there..
  2389     
  2390 
  2391     *longmsg = _longmsg;
  2392     if (size)
  2393         *size = _size;
  2394 
  2395     return PEP_STATUS_OK;
  2396 }
  2397 
  2398 // THIS IS A BEST-EFFORT ONLY FUNCTION, AND WE ARE NOT DOING MORE THAN THE
  2399 // SUBJECT FOR NOW.
  2400 static PEP_STATUS interpret_protected_headers(
  2401         struct mailmime* mime, 
  2402         message* msg
  2403     )
  2404 {
  2405     // N.B. this is *very much* enigmail output specific, and right now,
  2406     // we only care about subject replacement.
  2407     const char* header_string = "Content-Type: text/rfc822-headers; protected-headers=\"v1\"\nContent-Disposition: inline\n\n";
  2408     size_t content_length = mime->mm_length;
  2409     size_t header_strlen = strlen(header_string);
  2410     if (header_strlen < content_length) {
  2411         const char* headerblock = mime->mm_mime_start;
  2412         size_t subject_len = 0;
  2413         headerblock = strstr(headerblock, header_string);
  2414         if (headerblock) {
  2415             const char* subj_start = "Subject: ";
  2416             headerblock = strstr(headerblock, subj_start);
  2417             if (headerblock) {
  2418                 size_t subj_len = strlen(subj_start);
  2419                 headerblock += subj_len;
  2420                 char* end_pt = strstr(headerblock, "\n");
  2421                 if (end_pt) {
  2422                     if (end_pt != mime->mm_mime_start && *(end_pt - 1) == '\r')
  2423                         end_pt--;
  2424                     subject_len = end_pt - headerblock;
  2425                     char* new_subj = (char*)calloc(subject_len + 1, 1);
  2426                     if (new_subj) {
  2427                         strlcpy(new_subj, headerblock, subject_len + 1);
  2428                         free(msg->shortmsg);
  2429                         msg->shortmsg = new_subj;
  2430                     }    
  2431                 } // if there's no endpoint, there's something wrong here so we ignore all
  2432                   // This is best effort.
  2433             }
  2434         }
  2435     }
  2436     return PEP_STATUS_OK;
  2437 }
  2438 
  2439 // ONLY for main part!!!
  2440 static PEP_STATUS process_multipart_related(struct mailmime *mime,
  2441                                             message *msg) {
  2442     PEP_STATUS status = PEP_STATUS_OK;
  2443 
  2444     assert(mime);
  2445     assert(msg);
  2446 
  2447     clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;                                                
  2448 
  2449     if (partlist == NULL)
  2450         return PEP_ILLEGAL_VALUE;
  2451 
  2452     clistiter *cur = clist_begin(partlist);
  2453     struct mailmime *part = clist_content(cur);
  2454     
  2455     if (part == NULL)
  2456         return PEP_ILLEGAL_VALUE;
  2457 
  2458     struct mailmime_content *content = part->mm_content_type;    
  2459     assert(content);
  2460     
  2461     if (content == NULL)
  2462         return PEP_ILLEGAL_VALUE;
  2463 
  2464     if (_is_text_part(content, "html")) {
  2465         status = interpret_body(part, &msg->longmsg_formatted,
  2466                 NULL);
  2467         if (status)
  2468             return status;
  2469     }
  2470     else {
  2471         // ???
  2472         // This is what we would have done before, so... no
  2473         // worse than the status quo. But FIXME!
  2474         status = interpret_MIME(part, msg, NULL);
  2475         if (status)
  2476             return status;
  2477     }
  2478     
  2479     for (cur = clist_next(cur); cur; cur = clist_next(cur)) {
  2480         part = clist_content(cur);
  2481         if (part == NULL)
  2482             return PEP_ILLEGAL_VALUE;
  2483 
  2484         content = part->mm_content_type;
  2485         assert(content);
  2486         if (content == NULL)
  2487             return PEP_ILLEGAL_VALUE;
  2488 
  2489         status = interpret_MIME(part, msg, NULL);
  2490         if (status)
  2491             return status;
  2492     }
  2493     return status;
  2494 }
  2495 
  2496 static PEP_STATUS interpret_MIME(
  2497         struct mailmime *mime,
  2498         message *msg,
  2499         bool* raise_msg_attachment
  2500     )
  2501 {
  2502     PEP_STATUS status = PEP_STATUS_OK;
  2503 
  2504     assert(mime);
  2505     assert(msg);
  2506 
  2507     struct mailmime_content *content = mime->mm_content_type;
  2508     if (content) {
  2509         if (_is_multipart(content, "alternative")) {
  2510             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2511             if (partlist == NULL)
  2512                 return PEP_ILLEGAL_VALUE;
  2513 
  2514             clistiter *cur;
  2515             for (cur = clist_begin(partlist); cur; cur = clist_next(cur)) {
  2516                 struct mailmime *part = clist_content(cur);
  2517                 if (part == NULL)
  2518                     return PEP_ILLEGAL_VALUE;
  2519 
  2520                 content = part->mm_content_type;
  2521                 assert(content);
  2522                 if (content == NULL)
  2523                     return PEP_ILLEGAL_VALUE;
  2524 
  2525                 if (_is_text_part(content, "plain") && msg->longmsg == NULL) {
  2526                     status = interpret_body(part, &msg->longmsg, NULL);
  2527                     if (status)
  2528                         return status;
  2529                 }
  2530                 else if (_is_text_part(content, "rfc822-headers")) {
  2531                     status = interpret_protected_headers(part, msg);
  2532                     if (status)
  2533                         return status;
  2534                 }
  2535                 else if (_is_text_part(content, "html") &&
  2536                         msg->longmsg_formatted == NULL) {
  2537                     status = interpret_body(part, &msg->longmsg_formatted,
  2538                             NULL);
  2539                     if (status)
  2540                         return status;
  2541                 }
  2542                 else if (_is_multipart(content, "related") && 
  2543                     msg->longmsg_formatted == NULL) {
  2544                     status = process_multipart_related(part, msg);
  2545                     if (status)
  2546                         return status;
  2547                 }
  2548                 else /* add as attachment */ {
  2549                     status = interpret_MIME(part, msg, NULL);
  2550                     if (status)
  2551                         return status;
  2552                 }
  2553             }
  2554         }
  2555         else if (_is_multipart(content, "encrypted")) {
  2556             if (msg->longmsg == NULL)
  2557                 msg->longmsg = strdup("");
  2558             assert(msg->longmsg);
  2559             if (!msg->longmsg)
  2560                 return PEP_OUT_OF_MEMORY;
  2561 
  2562             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2563             if (partlist == NULL)
  2564                 return PEP_ILLEGAL_VALUE;
  2565 
  2566             clistiter *cur;
  2567             for (cur = clist_begin(partlist); cur; cur = clist_next(cur)) {
  2568                 struct mailmime *part= clist_content(cur);
  2569                 if (part == NULL)
  2570                     return PEP_ILLEGAL_VALUE;
  2571 
  2572                 status = interpret_MIME(part, msg, NULL);
  2573                 if (status != PEP_STATUS_OK)
  2574                     return status;
  2575             }
  2576         }
  2577         else if (_is_multipart(content, NULL)) {
  2578             clist *partlist = mime->mm_data.mm_multipart.mm_mp_list;
  2579             if (partlist == NULL)
  2580                 return PEP_ILLEGAL_VALUE;
  2581 
  2582             clistiter *cur;
  2583             // only add raise_msg_attachment on 2nd part!
  2584             int _att_count = 0;
  2585             for (cur = clist_begin(partlist); cur; cur = clist_next(cur), _att_count++) {
  2586                 struct mailmime *part= clist_content(cur);
  2587                 if (part == NULL)
  2588                     return PEP_ILLEGAL_VALUE;
  2589                 status = interpret_MIME(part, msg, _att_count == 1 ? raise_msg_attachment : NULL);
  2590                 if (status != PEP_STATUS_OK)
  2591                     return status;
  2592             }
  2593         }
  2594         else {
  2595             if (_is_text_part(content, "html") &&
  2596                 msg->longmsg_formatted == NULL &&
  2597                 msg->longmsg == NULL) {
  2598                 status = interpret_body(mime, &msg->longmsg_formatted,
  2599                                         NULL);
  2600                 if (status)
  2601                     return status;
  2602             }
  2603             else if (_is_text_part(content, "rfc822-headers")) {
  2604                 status = interpret_protected_headers(mime, msg);
  2605                 if (status)
  2606                     return status;
  2607             }
  2608             else if (_is_text_part(content, "plain") && 
  2609                      msg->longmsg == NULL && msg->longmsg_formatted == NULL) {
  2610                 status = interpret_body(mime, &msg->longmsg, NULL);
  2611                 if (status)
  2612                     return status;
  2613             }            
  2614             else if (_is_text_part(content, NULL) && 
  2615                      !_is_text_part(content, "plain") &&
  2616                      msg->longmsg == NULL) {
  2617                 status = interpret_body(mime, &msg->longmsg, NULL);
  2618                 if (status)
  2619                     return status;
  2620             }
  2621             else {
  2622                 // Fixme - we need a control on recursion level here - KG: maybe NOT. We only go to depth 1.
  2623                 if (raise_msg_attachment != NULL) {
  2624                     bool is_msg = (_is_message_part(content, "rfc822") || _is_text_part(content, "rfc822"));
  2625                     if (is_msg) {
  2626                         if (content->ct_parameters) {
  2627                             clistiter *cur;
  2628                             for (cur = clist_begin(content->ct_parameters); cur; cur =
  2629                                  clist_next(cur)) {
  2630                                 struct mailmime_parameter * param = clist_content(cur);
  2631                                 if (param && param->pa_name && strcasecmp(param->pa_name, "forward") == 0) {
  2632                                     if (param->pa_value && strcasecmp(param->pa_value, "no") == 0) {
  2633                                         *raise_msg_attachment = true;
  2634                                         break;
  2635                                     }
  2636                                 }
  2637                             }
  2638                         }
  2639                     }
  2640                 }
  2641                 char *data = NULL;
  2642                 size_t size = 0;
  2643                 char * mime_type;
  2644                 char * charset;
  2645                 char * filename;
  2646                 int r;
  2647 
  2648                 r = _get_content_type(content, &mime_type, &charset);
  2649                 switch (r) {
  2650                     case 0:
  2651                         break;
  2652                     case EINVAL:
  2653                         return PEP_ILLEGAL_VALUE;
  2654                     case ENOMEM:
  2655                         return PEP_OUT_OF_MEMORY;
  2656                     default:
  2657                         return PEP_UNKNOWN_ERROR;
  2658                 }
  2659 
  2660                 assert(mime_type);
  2661 
  2662                 status = interpret_body(mime, &data, &size);
  2663                 if (status)
  2664                     return status;
  2665 
  2666                 pEp_rid_list_t* resource_id_list = _get_resource_id_list(mime);
  2667                 pEp_rid_list_t* chosen_resource_id = choose_resource_id(resource_id_list);
  2668                 
  2669                 //filename = _get_filename_or_cid(mime);
  2670                 char *_filename = NULL;
  2671                 
  2672                 if (chosen_resource_id) {
  2673                     filename = chosen_resource_id->rid;
  2674                     size_t index = 0;
  2675                     /* NOTA BENE */
  2676                     /* The prefix we just added shouldn't be a problem - this is about decoding %XX (RFC 2392) */
  2677                     /* If it becomes one, we have some MESSY fixing to do. :(                                  */
  2678                     r = mailmime_encoded_phrase_parse("utf-8", filename,
  2679                             strlen(filename), &index, "utf-8", &_filename);
  2680                     if (r) {
  2681                         goto enomem;
  2682                     }
  2683                     char* file_prefix = NULL;
  2684                     
  2685                     /* in case there are others later */
  2686                     switch (chosen_resource_id->rid_type) {
  2687                         case PEP_RID_CID:
  2688                             file_prefix = "cid";
  2689                             break;
  2690                         case PEP_RID_FILENAME:
  2691                             file_prefix = "file";
  2692                             break;
  2693                         default:
  2694                             break;
  2695                     }
  2696 
  2697                     
  2698                     if (file_prefix) {
  2699                         filename = build_uri(file_prefix, _filename);
  2700                         free(_filename);
  2701                         _filename = filename;
  2702                     }
  2703                 }
  2704 
  2705                 bloblist_t *_a = bloblist_add(msg->attachments, data, size,
  2706                         mime_type, _filename);
  2707                 free(_filename);
  2708                 free_rid_list(resource_id_list);
  2709                 resource_id_list = NULL;
  2710                 if (_a == NULL)
  2711                     return PEP_OUT_OF_MEMORY;
  2712                 if (msg->attachments == NULL)
  2713                     msg->attachments = _a;
  2714             }
  2715         }
  2716     }
  2717 
  2718     return PEP_STATUS_OK;
  2719 
  2720 enomem:
  2721     return PEP_OUT_OF_MEMORY;
  2722 }
  2723 
  2724 DYNAMIC_API PEP_STATUS mime_decode_message(
  2725         const char *mimetext,
  2726         size_t size,
  2727         message **msg,
  2728         bool* raise_msg_attachment
  2729     )
  2730 {
  2731     PEP_STATUS status = PEP_STATUS_OK;
  2732     struct mailmime * mime = NULL;
  2733     int r;
  2734     message *_msg = NULL;
  2735     size_t index;
  2736 
  2737     assert(mimetext);
  2738     assert(msg);
  2739 
  2740     if (!(mimetext && msg))
  2741         return PEP_ILLEGAL_VALUE;
  2742 
  2743     *msg = NULL;
  2744 
  2745     index = 0;
  2746     r = mailmime_parse(mimetext, size, &index, &mime);
  2747     assert(r == 0);
  2748     assert(mime);
  2749     if (r) {
  2750         if (r == MAILIMF_ERROR_MEMORY)
  2751             goto enomem;
  2752         else
  2753             goto err_mime;
  2754     }
  2755 
  2756     _msg = calloc(1, sizeof(message));
  2757     assert(_msg);
  2758     if (_msg == NULL)
  2759         goto enomem;
  2760 
  2761     clist * _fieldlist = _get_fields(mime);
  2762     if (_fieldlist) {
  2763         status = read_fields(_msg, _fieldlist);
  2764         if (status != PEP_STATUS_OK)
  2765             goto pEp_error;
  2766     }
  2767 
  2768     struct mailmime_content *content = _get_content(mime);
  2769 
  2770     if (content) {
  2771         status = interpret_MIME(mime->mm_data.mm_message.mm_msg_mime,
  2772                 _msg, raise_msg_attachment);
  2773         if (status != PEP_STATUS_OK)
  2774             goto pEp_error;
  2775     }
  2776 
  2777     mailmime_free(mime);
  2778     *msg = _msg;
  2779 
  2780     return status;
  2781 
  2782 err_mime:
  2783     status = PEP_ILLEGAL_VALUE;
  2784     goto pEp_error;
  2785 
  2786 enomem:
  2787     status = PEP_OUT_OF_MEMORY;
  2788 
  2789 pEp_error:
  2790     free_message(_msg);
  2791 
  2792     if (mime)
  2793         mailmime_free(mime);
  2794 
  2795     return status;
  2796 }