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