src/etpan_mime.c
author Krista Bennett <krista@pep-project.org>
Tue, 14 Aug 2018 13:45:34 +0200
branchENGINE-451
changeset 2826 07c080d73a63
parent 2688 a34d253c9292
child 3439 3333c94c7827
permissions -rw-r--r--
ENGINE-451: abandoning branch - config files to be dealt with using gpgme (and hotfix will be external to engine
     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 "platform.h"
    10 
    11 #include <string.h>
    12 #include <stdlib.h>
    13 #include <assert.h>
    14 #include <errno.h>
    15 
    16 #define MAX_MESSAGE_ID 128
    17 
    18 static char * generate_boundary(void)
    19 {
    20     char id[MAX_MESSAGE_ID];
    21 
    22     // no cryptographically strong random needed here
    23     const long value1 = random();
    24     const long value2 = random();
    25     const long value3 = random();
    26     const long value4 = random();
    27 
    28     snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
    29             value3, value4);
    30     
    31     return strdup(id);
    32 }
    33 
    34 struct mailmime * part_new_empty(
    35         struct mailmime_content * content,
    36         struct mailmime_fields * mime_fields,
    37         int force_single
    38     )
    39 {
    40     struct mailmime * build_info;
    41     clist * list = NULL;
    42     int r;
    43     int mime_type;
    44     char * attr_name = NULL;
    45     char * attr_value = NULL;
    46     struct mailmime_parameter * param = NULL;
    47     clist * parameters = NULL;
    48     char *boundary = NULL;
    49 
    50     list = NULL;
    51 
    52     if (force_single) {
    53         mime_type = MAILMIME_SINGLE;
    54     }
    55     else {
    56         switch (content->ct_type->tp_type) {
    57             case MAILMIME_TYPE_DISCRETE_TYPE:
    58                 mime_type = MAILMIME_SINGLE;
    59                 break;
    60 
    61             case MAILMIME_TYPE_COMPOSITE_TYPE:
    62                 switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
    63                     case MAILMIME_COMPOSITE_TYPE_MULTIPART:
    64                         mime_type = MAILMIME_MULTIPLE;
    65                         break;
    66 
    67                     case MAILMIME_COMPOSITE_TYPE_MESSAGE:
    68                         if (strcasecmp(content->ct_subtype, "rfc822") == 0)
    69                             mime_type = MAILMIME_MESSAGE;
    70                         else
    71                             mime_type = MAILMIME_SINGLE;
    72                         break;
    73 
    74                     default:
    75                         goto enomem;
    76                 }
    77                 break;
    78 
    79             default:
    80                 goto enomem;
    81         }
    82     }
    83 
    84     if (mime_type == MAILMIME_MULTIPLE) {
    85         list = clist_new();
    86         assert(list);
    87         if (list == NULL)
    88             goto enomem;
    89 
    90         attr_name = strdup("boundary");
    91         assert(attr_name);
    92         if (attr_name == NULL)
    93             goto enomem;
    94 
    95         boundary = generate_boundary();
    96         assert(boundary);
    97         attr_value = boundary;
    98         if (attr_value == NULL)
    99             goto enomem;
   100 
   101         param = mailmime_parameter_new(attr_name, attr_value);
   102         assert(param);
   103         if (param == NULL)
   104             goto enomem;
   105         attr_name = NULL;
   106         attr_value = NULL;
   107 
   108         if (content->ct_parameters == NULL) {
   109             parameters = clist_new();
   110             assert(parameters);
   111             if (parameters == NULL)
   112                 goto enomem;
   113         }
   114         else {
   115             parameters = content->ct_parameters;
   116         }
   117 
   118         r = clist_append(parameters, param);
   119         if (r)
   120             goto enomem;
   121         param = NULL;
   122 
   123         if (content->ct_parameters == NULL)
   124             content->ct_parameters = parameters;
   125     }
   126 
   127     build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
   128             NULL, NULL, list, NULL, NULL);
   129     if (build_info == NULL)
   130         goto enomem;
   131 
   132     return build_info;
   133 
   134 enomem:
   135     if (list)
   136         clist_free(list);
   137     free(attr_name);
   138     free(attr_value);
   139     if (content->ct_parameters == NULL)
   140         if (parameters)
   141             clist_free(parameters);
   142     if (param)
   143         mailmime_parameter_free(param);
   144     return NULL;
   145 }
   146 
   147 struct mailmime * get_pgp_encrypted_part(void)
   148 {
   149     struct mailmime * mime = NULL;
   150     struct mailmime_fields * mime_fields = NULL;
   151     struct mailmime_content * content = NULL;
   152     int r;
   153 
   154     content = mailmime_content_new_with_str("application/pgp-encrypted");
   155     if (content == NULL)
   156         goto enomem;
   157 
   158     mime_fields = mailmime_fields_new_empty();
   159     if (mime_fields == NULL)
   160         goto enomem;
   161 
   162     mime = part_new_empty(content, mime_fields, 1);
   163     if (mime == NULL)
   164         goto enomem;
   165     mime_fields = NULL;
   166     content = NULL;
   167 
   168     r = mailmime_set_body_text(mime, "Version: 1\n", 10);
   169     if (r != 0)
   170         goto enomem;
   171 
   172     return mime;
   173 
   174 enomem:
   175     if (content)
   176         mailmime_content_free(content);
   177     if (mime_fields)
   178         mailmime_fields_free(mime_fields);
   179     if (mime)
   180         mailmime_free(mime);
   181 
   182     return NULL;
   183 }
   184 
   185 struct mailmime * get_text_part(
   186         pEp_rid_list_t* resource,
   187         const char * mime_type,
   188         const char * text,
   189         size_t length,
   190         int encoding_type
   191     )
   192 {
   193     char * disposition_name = NULL;
   194     struct mailmime_fields * mime_fields = NULL;
   195     struct mailmime * mime = NULL;
   196     struct mailmime_content * content = NULL;
   197     struct mailmime_parameter * param = NULL;
   198     struct mailmime_disposition * disposition = NULL;
   199     struct mailmime_mechanism * encoding = NULL;
   200     char* content_id = NULL;
   201     int r;
   202                 
   203     if (resource != NULL && resource->rid != NULL) {
   204         switch (resource->rid_type) {
   205             case PEP_RID_CID:
   206                 content_id = strdup(resource->rid);
   207                 break;
   208             case PEP_RID_FILENAME:
   209             default:
   210                 disposition_name = strdup(resource->rid);
   211                 if (disposition_name == NULL)
   212                     goto enomem;
   213                     
   214                 disposition =
   215                         mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   216                                 disposition_name, NULL, NULL, NULL, (size_t) -1);
   217 
   218                 if (disposition == NULL)
   219                     goto enomem;
   220 
   221                 disposition_name = NULL;                
   222                 break;
   223         }    
   224     }
   225     
   226     if (encoding_type) {
   227         encoding = mailmime_mechanism_new(encoding_type, NULL);
   228         if (encoding == NULL)
   229             goto enomem;
   230     }
   231 
   232     mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
   233             disposition, NULL);
   234     if (mime_fields == NULL)
   235         goto enomem;
   236     encoding = NULL;
   237     disposition = NULL;
   238     content_id = NULL;
   239 
   240     content = mailmime_content_new_with_str(mime_type);
   241     if (content == NULL)
   242         goto enomem;
   243     
   244     if (encoding_type != MAILMIME_MECHANISM_7BIT) {
   245         param = mailmime_param_new_with_data("charset", "utf-8");
   246         r = clist_append(content->ct_parameters, param);
   247         if (r != 0)
   248             goto enomem;
   249     }
   250 
   251     mime = part_new_empty(content, mime_fields, 1);
   252     if (mime == NULL)
   253         goto enomem;
   254     content = NULL;
   255     mime_fields = NULL;
   256 
   257     if (text) {
   258         r = mailmime_set_body_text(mime, (char *) text, length);
   259         if (r != 0)
   260             goto enomem;
   261     }
   262     
   263     return mime;
   264 
   265 enomem:
   266     free(disposition_name);
   267     if (mime_fields)
   268         mailmime_fields_free(mime_fields);
   269     if (mime)
   270         mailmime_free(mime);
   271     if (content)
   272         mailmime_content_free(content);
   273     if (param)
   274         mailmime_parameter_free(param);
   275     if (disposition)
   276         mailmime_disposition_free(disposition);
   277     if (encoding)
   278         mailmime_mechanism_free(encoding);
   279 
   280     return NULL;
   281 }
   282 
   283 struct mailmime * get_file_part(
   284         pEp_rid_list_t* resource,
   285         const char * mime_type,
   286         char * data,
   287         size_t length,
   288         bool transport_encode
   289     )
   290 {
   291     char * disposition_name = NULL;
   292     int encoding_type;
   293     struct mailmime_disposition * disposition = NULL;
   294     struct mailmime_mechanism * encoding = NULL;
   295     struct mailmime_content * content = NULL;
   296     struct mailmime * mime = NULL;
   297     struct mailmime_fields * mime_fields = NULL;
   298     char* content_id = NULL;
   299     int r;
   300                 
   301     if (resource != NULL && resource->rid != NULL) {
   302         switch (resource->rid_type) {
   303             case PEP_RID_CID:
   304                 content_id = strdup(resource->rid);
   305                 disposition =
   306                     mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   307                                                        NULL, NULL, NULL, NULL, (size_t) -1);
   308                     if (disposition == NULL)
   309                         goto enomem;
   310                 break;
   311             case PEP_RID_FILENAME:
   312             default:
   313                 disposition_name = strdup(resource->rid);
   314                 if (disposition_name == NULL)
   315                     goto enomem;
   316                     
   317                 disposition =
   318                         mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   319                                 disposition_name, NULL, NULL, NULL, (size_t) -1);
   320                                 
   321                 if (disposition == NULL)
   322                     goto enomem;
   323                 disposition_name = NULL;
   324                 
   325                 break;
   326         }    
   327     }
   328     
   329 
   330     content = mailmime_content_new_with_str(mime_type);
   331     if (content == NULL)
   332         goto enomem;
   333 
   334     encoding = NULL;
   335 
   336     if (transport_encode) {
   337         encoding_type = MAILMIME_MECHANISM_BASE64;
   338         encoding = mailmime_mechanism_new(encoding_type, NULL);
   339         if (encoding == NULL)
   340             goto enomem;
   341     }
   342 
   343     mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
   344             disposition, NULL);
   345     if (mime_fields == NULL)
   346         goto enomem;
   347     encoding = NULL;
   348     disposition = NULL;
   349 
   350     mime = part_new_empty(content, mime_fields, 1);
   351     if (mime == NULL)
   352         goto enomem;
   353     content = NULL;
   354     mime_fields = NULL;
   355 
   356     if(length > 0)
   357     {
   358         r = mailmime_set_body_text(mime, data, length);
   359         if (r != 0)
   360             goto enomem;
   361     }
   362 
   363     return mime;
   364 
   365 enomem:
   366     free(disposition_name);
   367     if (disposition)
   368         mailmime_disposition_free(disposition);
   369     if (encoding)
   370         mailmime_mechanism_free(encoding);
   371     if (content)
   372         mailmime_content_free(content);
   373     if (mime_fields)
   374         mailmime_fields_free(mime_fields);
   375     if (mime)
   376         mailmime_free(mime);
   377     
   378     return NULL;
   379 }
   380 
   381 struct mailmime * part_multiple_new(const char *type)
   382 {
   383     struct mailmime_fields *mime_fields = NULL;
   384     struct mailmime_content *content = NULL;
   385     struct mailmime *mp = NULL;
   386     
   387     mime_fields = mailmime_fields_new_empty();
   388     if (mime_fields == NULL)
   389         goto enomem;
   390     
   391     content = mailmime_content_new_with_str(type);
   392     if (content == NULL)
   393         goto enomem;
   394     
   395     mp = part_new_empty(content, mime_fields, 0);
   396     if (mp == NULL)
   397         goto enomem;
   398     
   399     return mp;
   400     
   401 enomem:
   402     if (content)
   403         mailmime_content_free(content);
   404     if (mime_fields)
   405         mailmime_fields_free(mime_fields);
   406 
   407     return NULL;
   408 }
   409 
   410 struct mailimf_field * _new_field(
   411         int type,
   412         _new_func_t new_func,
   413         void *value
   414     )
   415 {
   416     void *data = new_func(value);
   417     assert(data);
   418     if (data == NULL)
   419         return NULL;
   420 
   421     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   422     assert(result);
   423     if (result == NULL) {
   424         free(data);
   425         return NULL;
   426     }
   427 
   428     result->fld_type = type;
   429     result->fld_data.fld_return_path = data;
   430 
   431     return result;
   432 }
   433 
   434 void _free_field(struct mailimf_field *field)
   435 {
   436     if (field)
   437         free(field->fld_data.fld_return_path);
   438     free(field);
   439 }
   440 
   441 int _append_field(
   442         clist *list,
   443         int type,
   444         _new_func_t new_func,
   445         void *value
   446     )
   447 {
   448     int r;
   449     struct mailimf_field * field;
   450 
   451     assert(list);
   452     assert(new_func);
   453     assert(value);
   454 
   455     field = _new_field(type, new_func, value);
   456     if (field == NULL)
   457         return -1;
   458 
   459     r = clist_append(list, field);
   460     if (r)
   461         _free_field(field);
   462 
   463     return r;
   464 }
   465 
   466 // http://media2.giga.de/2014/02/Image-28.jpg
   467 
   468 struct mailimf_date_time * timestamp_to_etpantime(const struct tm *ts)
   469 {
   470     struct mailimf_date_time * result = calloc(1,
   471             sizeof(struct mailimf_date_time));
   472     assert(result);
   473     if (result == NULL)
   474         return NULL;
   475 
   476     assert(ts);
   477 
   478     result->dt_sec = ts->tm_sec;
   479     result->dt_min = ts->tm_min;
   480     result->dt_hour = ts->tm_hour;
   481     result->dt_day = ts->tm_mday;
   482     result->dt_month = ts->tm_mon + 1;
   483     result->dt_year = ts->tm_year + 1900;
   484 #ifndef WIN32
   485     result->dt_zone = (int) (ts->tm_gmtoff / 36L);
   486 #endif
   487     return result;
   488 }
   489 
   490 struct tm * etpantime_to_timestamp(const struct mailimf_date_time *et)
   491 {
   492     struct tm * result = calloc(1, sizeof(struct tm));
   493     assert(result);
   494     if (result == NULL)
   495         return NULL;
   496 
   497     assert(et);
   498 
   499     result->tm_sec = et->dt_sec;
   500     result->tm_min = et->dt_min;
   501     result->tm_hour = et->dt_hour;
   502     result->tm_mday = et->dt_day;
   503     result->tm_mon = et->dt_month - 1;
   504     result->tm_year = et->dt_year - 1900;
   505 #ifndef WIN32
   506     result->tm_gmtoff = 36L * (long) et->dt_zone;
   507 #endif
   508     return result;
   509 }
   510 
   511 struct mailimf_mailbox * mailbox_from_string(
   512         const char *name,
   513         const char *address
   514     )
   515 {
   516     struct mailimf_mailbox *mb = NULL;
   517     char *_name = NULL;
   518     char *_address = NULL;
   519 
   520     assert(address);
   521 
   522     _name = name ? strdup(name) : strdup("");
   523     if (_name == NULL)
   524         goto enomem;
   525 
   526     _address = strdup(address);
   527     if (_address == NULL)
   528         goto enomem;
   529 
   530     mb = mailimf_mailbox_new(_name, _address);
   531     assert(mb);
   532     if (mb == NULL)
   533         goto enomem;
   534 
   535     return mb;
   536 
   537 enomem:
   538     free(_name);
   539     free(_address);
   540 
   541     return NULL;
   542 }
   543 
   544 
   545 struct mailimf_field * create_optional_field(
   546         const char *field,
   547         const char *value
   548     )
   549 {
   550     char *_field = NULL;
   551     char *_value = NULL;
   552     struct mailimf_optional_field *optional_field = NULL;
   553 
   554     _field = strdup(field);
   555     if (_field == NULL)
   556         goto enomem;
   557 
   558     if (!must_field_value_be_encoded(value))
   559         _value = strdup(value);
   560     else    
   561         _value = mailmime_encode_subject_header("utf-8", value, 0);
   562     if (_value == NULL)
   563         goto enomem;
   564 
   565     optional_field = mailimf_optional_field_new(_field, _value);
   566     if (optional_field == NULL)
   567         goto enomem;
   568 
   569     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   570     assert(result);
   571     if (result == NULL)
   572         goto enomem;
   573 
   574     result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
   575     result->fld_data.fld_optional_field = optional_field;
   576 
   577     return result;
   578 
   579 enomem:
   580     if (optional_field) {
   581         mailimf_optional_field_free(optional_field);
   582     }
   583     else {
   584         free(_field);
   585         free(_value);
   586     }
   587 
   588     return NULL;
   589 }
   590 
   591 int _append_optional_field(
   592         clist *list,
   593         const char *field,
   594         const char *value
   595     )
   596 {
   597     int r;
   598     struct mailimf_field * optional_field =
   599             create_optional_field(field, value);
   600 
   601     if (optional_field == NULL)
   602         return -1;
   603 
   604     r = clist_append(list, optional_field);
   605     if (r)
   606         mailimf_field_free(optional_field);
   607 
   608     return r;
   609 }
   610 
   611 clist * _get_fields(struct mailmime * mime)
   612 {
   613     clist * _fieldlist = NULL;
   614 
   615     assert(mime);
   616 
   617     if (mime->mm_data.mm_message.mm_fields &&
   618             mime->mm_data.mm_message.mm_fields->fld_list) {
   619         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   620     }
   621 
   622     return _fieldlist;
   623 }
   624 
   625 struct mailmime_content * _get_content(struct mailmime * mime)
   626 {
   627     struct mailmime_content * content = NULL;
   628 
   629     assert(mime);
   630 
   631     if (mime->mm_data.mm_message.mm_msg_mime)
   632         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   633 
   634     return content;
   635 }
   636 
   637 
   638 /* Return a list of identifier_type and resource id (filename, cid, etc) */
   639 pEp_rid_list_t* _get_resource_id_list(struct mailmime *mime)
   640 {
   641     clist * _fieldlist = NULL;
   642 
   643     assert(mime);
   644 
   645     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   646         _fieldlist = mime->mm_mime_fields->fld_list;
   647     else
   648         return NULL;
   649 
   650     clistiter *cur;
   651 
   652     pEp_rid_list_t* rid_list = NULL; 
   653     pEp_rid_list_t** rid_list_curr_p = &rid_list; 
   654         
   655     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   656         struct mailmime_field * _field = clist_content(cur);
   657         /* content_id */
   658         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   659             pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
   660             new_rid->rid_type = PEP_RID_CID;
   661             new_rid->rid = strdup(_field->fld_data.fld_id);
   662             *rid_list_curr_p = new_rid;
   663             rid_list_curr_p = &new_rid->next;
   664         }
   665         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   666             /* filename */
   667             if (_field->fld_data.fld_disposition &&
   668                     _field->fld_data.fld_disposition->dsp_parms) {
   669                 clist * _parmlist =
   670                         _field->fld_data.fld_disposition->dsp_parms;
   671                 clistiter *cur2;
   672                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   673                         clist_next(cur2)) {
   674                     struct mailmime_disposition_parm * param =
   675                             clist_content(cur2);
   676                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   677                         pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
   678                         new_rid->rid_type = PEP_RID_FILENAME;
   679                         new_rid->rid = strdup(param->pa_data.pa_filename);
   680                         *rid_list_curr_p = new_rid;
   681                         rid_list_curr_p = &new_rid->next;
   682                     }                
   683                 }
   684             }
   685         }
   686     }
   687     /* Will almost certainly usually be a singleton, but we need to be able to decide */
   688     return rid_list;
   689 }
   690 
   691 
   692 /* FIXME: about to be obsoleted? */
   693 char * _get_filename_or_cid(struct mailmime *mime)
   694 {
   695     clist * _fieldlist = NULL;
   696 
   697     assert(mime);
   698 
   699     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   700         _fieldlist = mime->mm_mime_fields->fld_list;
   701     else
   702         return NULL;
   703 
   704     clistiter *cur;
   705     
   706     char* _temp_filename_ptr = NULL;
   707     
   708     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   709         struct mailmime_field * _field = clist_content(cur);
   710         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   711             /* We prefer CIDs to filenames when both are present */
   712             free(_temp_filename_ptr); /* can be null, it's ok */
   713             return build_uri("cid", _field->fld_data.fld_id); 
   714         }
   715         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   716             if (_field->fld_data.fld_disposition &&
   717                     _field->fld_data.fld_disposition->dsp_parms &&
   718                     !_temp_filename_ptr) {
   719                 clist * _parmlist =
   720                         _field->fld_data.fld_disposition->dsp_parms;
   721                 clistiter *cur2;
   722                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   723                         clist_next(cur2)) {
   724                     struct mailmime_disposition_parm * param =
   725                             clist_content(cur2);
   726                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   727                         _temp_filename_ptr = build_uri("file", param->pa_data.pa_filename);
   728                         break;
   729                     }                
   730                 }
   731             }
   732         }
   733     }
   734     /* Ok, it wasn't a CID */
   735     return _temp_filename_ptr;
   736 }
   737 
   738 static bool parameter_has_value(
   739         struct mailmime_content *content,       
   740         const char *name,
   741         const char *value
   742     )
   743 {
   744     clistiter *cur;
   745 
   746     assert(name);
   747     assert(value);
   748 
   749     clist * list = content->ct_parameters;
   750     if (list == NULL)
   751         return false;
   752 
   753     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   754         struct mailmime_parameter * param = clist_content(cur);
   755         if (param &&
   756                 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
   757                 param->pa_value && strcasecmp(value, param->pa_value) == 0)
   758             return true;
   759     }
   760 
   761     return false;
   762 }
   763 
   764 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   765 {
   766     assert(content);
   767 
   768     if (content->ct_type && content->ct_type->tp_type ==
   769             MAILMIME_TYPE_COMPOSITE_TYPE &&
   770             content->ct_type->tp_data.tp_composite_type &&
   771             content->ct_type->tp_data.tp_composite_type->ct_type ==
   772             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   773         if (subtype)
   774             return content->ct_subtype &&
   775                     strcasecmp(content->ct_subtype, subtype) == 0;
   776         else
   777             return true;
   778     }
   779 
   780     return false;
   781 }
   782 
   783 bool _is_PGP_MIME(struct mailmime_content *content)
   784 {
   785     assert(content);
   786 
   787     if (_is_multipart(content, "encrypted") &&
   788             parameter_has_value(content, "protocol",
   789                     "application/pgp-encrypted"))
   790         return true;
   791 
   792     return false;
   793 }
   794 
   795 bool _is_text_part(struct mailmime_content *content, const char *subtype)
   796 {
   797     assert(content);
   798 
   799     if (content->ct_type && content->ct_type->tp_type ==
   800             MAILMIME_TYPE_DISCRETE_TYPE &&
   801             content->ct_type->tp_data.tp_discrete_type &&
   802             content->ct_type->tp_data.tp_discrete_type->dt_type ==
   803             MAILMIME_DISCRETE_TYPE_TEXT) {
   804         if (subtype)
   805             return content->ct_subtype &&
   806                     strcasecmp(content->ct_subtype, subtype) == 0;
   807         else
   808             return true;
   809     }
   810 
   811     return false;
   812 }
   813 
   814 int _get_content_type(
   815         const struct mailmime_content *content,
   816         char **type,
   817         char **charset
   818     )
   819 {
   820     char *_type = NULL;
   821     char *_charset = NULL;
   822 
   823     assert(content);
   824     assert(type);
   825     assert(charset);
   826 
   827     *type = NULL;
   828     *charset = NULL;
   829 
   830     if (content->ct_subtype == NULL)
   831         return EINVAL;
   832 
   833     if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
   834         size_t len;
   835         const char *_main_type;
   836 
   837         switch  (content->ct_type->tp_data.tp_discrete_type->dt_type) {
   838             case MAILMIME_DISCRETE_TYPE_TEXT:
   839                 _main_type = "text";
   840                 break;
   841             case MAILMIME_DISCRETE_TYPE_IMAGE:
   842                 _main_type = "image";
   843                 break;
   844             case MAILMIME_DISCRETE_TYPE_AUDIO:
   845                 _main_type = "audio";
   846                 break;
   847             case MAILMIME_DISCRETE_TYPE_VIDEO:
   848                 _main_type = "video";
   849                 break;
   850             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   851                 _main_type = "application";
   852                 break;
   853             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   854                 _main_type = "extension";
   855                 break;
   856             default:
   857                 return EINVAL;
   858         }
   859 
   860         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   861         _type = calloc(1, len);
   862         assert(_type);
   863         if (_type == NULL)
   864             return ENOMEM;
   865 
   866         strncpy(_type, _main_type, len);
   867         len -= strlen(_main_type);
   868         strncat(_type, "/", len--);
   869         strncat(_type, content->ct_subtype, len);
   870 
   871         if (content->ct_parameters) {
   872             clistiter *cur;
   873             for (cur = clist_begin(content->ct_parameters); cur; cur =
   874                     clist_next(cur)) {
   875                 struct mailmime_parameter * param = clist_content(cur);
   876                 if (param && param->pa_name && strcasecmp(param->pa_name,
   877                             "charset") == 0) {
   878                     _charset = param->pa_value;
   879                     break;
   880                 }
   881             }
   882             if (_charset)
   883                 *charset = strdup(_charset);
   884         }
   885 
   886         *type = _type;
   887         return 0;
   888     }
   889 
   890     return EINVAL;
   891 }
   892 
   893 // Only for null-terminated field strings.
   894 // can this field be transported as is without modification?)
   895 // (See rfc2822, section 2.2.3 - libetpan's handling isn't quite what
   896 // we need here.)
   897 bool must_field_value_be_encoded(const char* field_value) {
   898     if (!field_value)
   899         return false;
   900         
   901     return must_chunk_be_encoded((const void*)field_value, strlen(field_value), false);    
   902 }
   903 
   904 bool must_chunk_be_encoded(const void* value, size_t size, bool ignore_fws) {
   905 
   906     const char* begin_ptr = (const char*)value;    
   907 
   908     const char* end_ptr = begin_ptr + size;
   909 
   910     const char* cur_char_ptr = begin_ptr;
   911     while (cur_char_ptr < end_ptr) {
   912         char cur_char = *cur_char_ptr;
   913         if (cur_char > 127 || cur_char < 0)
   914             return true;
   915         // FIXME - do we need to deal with CRCRLF here?
   916         //         I guess in the worst case, it gets encoded, which
   917         //         is *supposed* to be harmless...
   918         if (!ignore_fws) {
   919             if (cur_char == '\r') {
   920                 const char* next = cur_char_ptr + 1;
   921                 const char* nextnext = next + 1;
   922                 if (next >= end_ptr || nextnext >= end_ptr
   923                     || *next != '\n'
   924                     || (*nextnext != ' ' && *nextnext != '\t')) {
   925                     return true;
   926                 }            
   927             }
   928             else if (cur_char == '\n') {
   929                 const char* prev = cur_char_ptr - 1;
   930                 if (prev == begin_ptr || *prev != '\r')
   931                     return true;
   932             }
   933         }
   934         cur_char_ptr++;
   935     }    
   936     return false;
   937 }