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