src/etpan_mime.c
author Krista Bennett <krista@pep-project.org>
Wed, 25 Oct 2017 10:29:16 +0200
branchENGINE-298
changeset 2200 644d22d740f0
parent 2011 86bbefb0e806
child 2562 a381a9bd101c
permissions -rw-r--r--
ENGINE-298: transport encoding should only take place when the message is at the last stage before outbound return. Added a transport_encode flag to several of the MIME-tree building functions, as libetpan does not distinguish between building the MIME tree and encoding its parts.
     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 struct mailimf_field * create_optional_field(
   545         const char *field,
   546         const char *value
   547     )
   548 {
   549     char *_field = NULL;
   550     char *_value = NULL;
   551     struct mailimf_optional_field *optional_field = NULL;
   552 
   553     _field = strdup(field);
   554     if (_field == NULL)
   555         goto enomem;
   556 
   557     _value = mailmime_encode_subject_header("utf-8", value, 0);
   558     if (_value == NULL)
   559         goto enomem;
   560 
   561     optional_field = mailimf_optional_field_new(_field, _value);
   562     if (optional_field == NULL)
   563         goto enomem;
   564 
   565     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   566     assert(result);
   567     if (result == NULL)
   568         goto enomem;
   569 
   570     result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
   571     result->fld_data.fld_optional_field = optional_field;
   572 
   573     return result;
   574 
   575 enomem:
   576     if (optional_field) {
   577         mailimf_optional_field_free(optional_field);
   578     }
   579     else {
   580         free(_field);
   581         free(_value);
   582     }
   583 
   584     return NULL;
   585 }
   586 
   587 int _append_optional_field(
   588         clist *list,
   589         const char *field,
   590         const char *value
   591     )
   592 {
   593     int r;
   594     struct mailimf_field * optional_field =
   595             create_optional_field(field, value);
   596 
   597     if (optional_field == NULL)
   598         return -1;
   599 
   600     r = clist_append(list, optional_field);
   601     if (r)
   602         mailimf_field_free(optional_field);
   603 
   604     return r;
   605 }
   606 
   607 clist * _get_fields(struct mailmime * mime)
   608 {
   609     clist * _fieldlist = NULL;
   610 
   611     assert(mime);
   612 
   613     if (mime->mm_data.mm_message.mm_fields &&
   614             mime->mm_data.mm_message.mm_fields->fld_list) {
   615         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   616     }
   617 
   618     return _fieldlist;
   619 }
   620 
   621 struct mailmime_content * _get_content(struct mailmime * mime)
   622 {
   623     struct mailmime_content * content = NULL;
   624 
   625     assert(mime);
   626 
   627     if (mime->mm_data.mm_message.mm_msg_mime)
   628         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   629 
   630     return content;
   631 }
   632 
   633 
   634 /* Return a list of identifier_type and resource id (filename, cid, etc) */
   635 pEp_rid_list_t* _get_resource_id_list(struct mailmime *mime)
   636 {
   637     clist * _fieldlist = NULL;
   638 
   639     assert(mime);
   640 
   641     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   642         _fieldlist = mime->mm_mime_fields->fld_list;
   643     else
   644         return NULL;
   645 
   646     clistiter *cur;
   647 
   648     pEp_rid_list_t* rid_list = NULL; 
   649     pEp_rid_list_t** rid_list_curr_p = &rid_list; 
   650         
   651     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   652         struct mailmime_field * _field = clist_content(cur);
   653         /* content_id */
   654         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   655             pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
   656             new_rid->rid_type = PEP_RID_CID;
   657             new_rid->rid = strdup(_field->fld_data.fld_id);
   658             *rid_list_curr_p = new_rid;
   659             rid_list_curr_p = &new_rid->next;
   660         }
   661         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   662             /* filename */
   663             if (_field->fld_data.fld_disposition &&
   664                     _field->fld_data.fld_disposition->dsp_parms) {
   665                 clist * _parmlist =
   666                         _field->fld_data.fld_disposition->dsp_parms;
   667                 clistiter *cur2;
   668                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   669                         clist_next(cur2)) {
   670                     struct mailmime_disposition_parm * param =
   671                             clist_content(cur2);
   672                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   673                         pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
   674                         new_rid->rid_type = PEP_RID_FILENAME;
   675                         new_rid->rid = strdup(param->pa_data.pa_filename);
   676                         *rid_list_curr_p = new_rid;
   677                         rid_list_curr_p = &new_rid->next;
   678                     }                
   679                 }
   680             }
   681         }
   682     }
   683     /* Will almost certainly usually be a singleton, but we need to be able to decide */
   684     return rid_list;
   685 }
   686 
   687 
   688 /* FIXME: about to be obsoleted? */
   689 char * _get_filename_or_cid(struct mailmime *mime)
   690 {
   691     clist * _fieldlist = NULL;
   692 
   693     assert(mime);
   694 
   695     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   696         _fieldlist = mime->mm_mime_fields->fld_list;
   697     else
   698         return NULL;
   699 
   700     clistiter *cur;
   701     
   702     char* _temp_filename_ptr = NULL;
   703     
   704     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   705         struct mailmime_field * _field = clist_content(cur);
   706         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   707             /* We prefer CIDs to filenames when both are present */
   708             free(_temp_filename_ptr); /* can be null, it's ok */
   709             return build_uri("cid", _field->fld_data.fld_id); 
   710         }
   711         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   712             if (_field->fld_data.fld_disposition &&
   713                     _field->fld_data.fld_disposition->dsp_parms &&
   714                     !_temp_filename_ptr) {
   715                 clist * _parmlist =
   716                         _field->fld_data.fld_disposition->dsp_parms;
   717                 clistiter *cur2;
   718                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   719                         clist_next(cur2)) {
   720                     struct mailmime_disposition_parm * param =
   721                             clist_content(cur2);
   722                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   723                         _temp_filename_ptr = build_uri("file", param->pa_data.pa_filename);
   724                         break;
   725                     }                
   726                 }
   727             }
   728         }
   729     }
   730     /* Ok, it wasn't a CID */
   731     return _temp_filename_ptr;
   732 }
   733 
   734 static bool parameter_has_value(
   735         struct mailmime_content *content,       
   736         const char *name,
   737         const char *value
   738     )
   739 {
   740     clistiter *cur;
   741 
   742     assert(name);
   743     assert(value);
   744 
   745     clist * list = content->ct_parameters;
   746     if (list == NULL)
   747         return false;
   748 
   749     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   750         struct mailmime_parameter * param = clist_content(cur);
   751         if (param &&
   752                 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
   753                 param->pa_value && strcasecmp(value, param->pa_value) == 0)
   754             return true;
   755     }
   756 
   757     return false;
   758 }
   759 
   760 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   761 {
   762     assert(content);
   763 
   764     if (content->ct_type && content->ct_type->tp_type ==
   765             MAILMIME_TYPE_COMPOSITE_TYPE &&
   766             content->ct_type->tp_data.tp_composite_type &&
   767             content->ct_type->tp_data.tp_composite_type->ct_type ==
   768             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   769         if (subtype)
   770             return content->ct_subtype &&
   771                     strcasecmp(content->ct_subtype, subtype) == 0;
   772         else
   773             return true;
   774     }
   775 
   776     return false;
   777 }
   778 
   779 bool _is_PGP_MIME(struct mailmime_content *content)
   780 {
   781     assert(content);
   782 
   783     if (_is_multipart(content, "encrypted") &&
   784             parameter_has_value(content, "protocol",
   785                     "application/pgp-encrypted"))
   786         return true;
   787 
   788     return false;
   789 }
   790 
   791 bool _is_text_part(struct mailmime_content *content, const char *subtype)
   792 {
   793     assert(content);
   794 
   795     if (content->ct_type && content->ct_type->tp_type ==
   796             MAILMIME_TYPE_DISCRETE_TYPE &&
   797             content->ct_type->tp_data.tp_discrete_type &&
   798             content->ct_type->tp_data.tp_discrete_type->dt_type ==
   799             MAILMIME_DISCRETE_TYPE_TEXT) {
   800         if (subtype)
   801             return content->ct_subtype &&
   802                     strcasecmp(content->ct_subtype, subtype) == 0;
   803         else
   804             return true;
   805     }
   806 
   807     return false;
   808 }
   809 
   810 int _get_content_type(
   811         const struct mailmime_content *content,
   812         char **type,
   813         char **charset
   814     )
   815 {
   816     char *_type = NULL;
   817     char *_charset = NULL;
   818 
   819     assert(content);
   820     assert(type);
   821     assert(charset);
   822 
   823     *type = NULL;
   824     *charset = NULL;
   825 
   826     if (content->ct_subtype == NULL)
   827         return EINVAL;
   828 
   829     if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
   830         size_t len;
   831         const char *_main_type;
   832 
   833         switch  (content->ct_type->tp_data.tp_discrete_type->dt_type) {
   834             case MAILMIME_DISCRETE_TYPE_TEXT:
   835                 _main_type = "text";
   836                 break;
   837             case MAILMIME_DISCRETE_TYPE_IMAGE:
   838                 _main_type = "image";
   839                 break;
   840             case MAILMIME_DISCRETE_TYPE_AUDIO:
   841                 _main_type = "audio";
   842                 break;
   843             case MAILMIME_DISCRETE_TYPE_VIDEO:
   844                 _main_type = "video";
   845                 break;
   846             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   847                 _main_type = "application";
   848                 break;
   849             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   850                 _main_type = "extension";
   851                 break;
   852             default:
   853                 return EINVAL;
   854         }
   855 
   856         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   857         _type = calloc(1, len);
   858         assert(_type);
   859         if (_type == NULL)
   860             return ENOMEM;
   861 
   862         strncpy(_type, _main_type, len);
   863         len -= strlen(_main_type);
   864         strncat(_type, "/", len--);
   865         strncat(_type, content->ct_subtype, len);
   866 
   867         if (content->ct_parameters) {
   868             clistiter *cur;
   869             for (cur = clist_begin(content->ct_parameters); cur; cur =
   870                     clist_next(cur)) {
   871                 struct mailmime_parameter * param = clist_content(cur);
   872                 if (param && param->pa_name && strcasecmp(param->pa_name,
   873                             "charset") == 0) {
   874                     _charset = param->pa_value;
   875                     break;
   876                 }
   877             }
   878             if (_charset)
   879                 *charset = strdup(_charset);
   880         }
   881 
   882         *type = _type;
   883         return 0;
   884     }
   885 
   886     return EINVAL;
   887 }