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