src/etpan_mime.c
author Krista Bennett <krista@pep-project.org>
Fri, 30 Jun 2017 05:51:53 +0200
branchENGINE-9
changeset 1900 0aa3e9e7c850
parent 1881 facb0b5b0bce
child 2011 86bbefb0e806
permissions -rw-r--r--
ENGINE=9: it helps to malloc the space for the data tructure, not just a pointer. DOH.
     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     )
   289 {
   290     char * disposition_name = NULL;
   291     int encoding_type;
   292     struct mailmime_disposition * disposition = NULL;
   293     struct mailmime_mechanism * encoding = NULL;
   294     struct mailmime_content * content = NULL;
   295     struct mailmime * mime = NULL;
   296     struct mailmime_fields * mime_fields = NULL;
   297     char* content_id = NULL;
   298     int r;
   299                 
   300     if (resource != NULL && resource->rid != NULL) {
   301         switch (resource->rid_type) {
   302             case PEP_RID_CID:
   303                 content_id = strdup(resource->rid);
   304                 break;
   305             case PEP_RID_FILENAME:
   306             default:
   307                 disposition_name = strdup(resource->rid);
   308                 if (disposition_name == NULL)
   309                     goto enomem;
   310                     
   311                 disposition =
   312                         mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   313                                 disposition_name, NULL, NULL, NULL, (size_t) -1);
   314                                 
   315                 if (disposition == NULL)
   316                     goto enomem;
   317                 disposition_name = NULL;
   318                 
   319                 break;
   320         }    
   321     }
   322     
   323 
   324     content = mailmime_content_new_with_str(mime_type);
   325     if (content == NULL)
   326         goto enomem;
   327 
   328     encoding_type = MAILMIME_MECHANISM_BASE64;
   329     encoding = mailmime_mechanism_new(encoding_type, NULL);
   330     if (encoding == NULL)
   331         goto enomem;
   332 
   333     mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
   334             disposition, NULL);
   335     if (mime_fields == NULL)
   336         goto enomem;
   337     encoding = NULL;
   338     disposition = NULL;
   339 
   340     mime = part_new_empty(content, mime_fields, 1);
   341     if (mime == NULL)
   342         goto enomem;
   343     content = NULL;
   344     mime_fields = NULL;
   345 
   346     if(length > 0)
   347     {
   348         r = mailmime_set_body_text(mime, data, length);
   349         if (r != 0)
   350             goto enomem;
   351     }
   352 
   353     return mime;
   354 
   355 enomem:
   356     free(disposition_name);
   357     if (disposition)
   358         mailmime_disposition_free(disposition);
   359     if (encoding)
   360         mailmime_mechanism_free(encoding);
   361     if (content)
   362         mailmime_content_free(content);
   363     if (mime_fields)
   364         mailmime_fields_free(mime_fields);
   365     if (mime)
   366         mailmime_free(mime);
   367     
   368     return NULL;
   369 }
   370 
   371 struct mailmime * part_multiple_new(const char *type)
   372 {
   373     struct mailmime_fields *mime_fields = NULL;
   374     struct mailmime_content *content = NULL;
   375     struct mailmime *mp = NULL;
   376     
   377     mime_fields = mailmime_fields_new_empty();
   378     if (mime_fields == NULL)
   379         goto enomem;
   380     
   381     content = mailmime_content_new_with_str(type);
   382     if (content == NULL)
   383         goto enomem;
   384     
   385     mp = part_new_empty(content, mime_fields, 0);
   386     if (mp == NULL)
   387         goto enomem;
   388     
   389     return mp;
   390     
   391 enomem:
   392     if (content)
   393         mailmime_content_free(content);
   394     if (mime_fields)
   395         mailmime_fields_free(mime_fields);
   396 
   397     return NULL;
   398 }
   399 
   400 struct mailimf_field * _new_field(
   401         int type,
   402         _new_func_t new_func,
   403         void *value
   404     )
   405 {
   406     void *data = new_func(value);
   407     assert(data);
   408     if (data == NULL)
   409         return NULL;
   410 
   411     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   412     assert(result);
   413     if (result == NULL) {
   414         free(data);
   415         return NULL;
   416     }
   417 
   418     result->fld_type = type;
   419     result->fld_data.fld_return_path = data;
   420 
   421     return result;
   422 }
   423 
   424 void _free_field(struct mailimf_field *field)
   425 {
   426     if (field)
   427         free(field->fld_data.fld_return_path);
   428     free(field);
   429 }
   430 
   431 int _append_field(
   432         clist *list,
   433         int type,
   434         _new_func_t new_func,
   435         void *value
   436     )
   437 {
   438     int r;
   439     struct mailimf_field * field;
   440 
   441     assert(list);
   442     assert(new_func);
   443     assert(value);
   444 
   445     field = _new_field(type, new_func, value);
   446     if (field == NULL)
   447         return -1;
   448 
   449     r = clist_append(list, field);
   450     if (r)
   451         _free_field(field);
   452 
   453     return r;
   454 }
   455 
   456 // http://media2.giga.de/2014/02/Image-28.jpg
   457 
   458 struct mailimf_date_time * timestamp_to_etpantime(const struct tm *ts)
   459 {
   460     struct mailimf_date_time * result = calloc(1,
   461             sizeof(struct mailimf_date_time));
   462     assert(result);
   463     if (result == NULL)
   464         return NULL;
   465 
   466     assert(ts);
   467 
   468     result->dt_sec = ts->tm_sec;
   469     result->dt_min = ts->tm_min;
   470     result->dt_hour = ts->tm_hour;
   471     result->dt_day = ts->tm_mday;
   472     result->dt_month = ts->tm_mon + 1;
   473     result->dt_year = ts->tm_year + 1900;
   474 #ifndef WIN32
   475     result->dt_zone = (int) (ts->tm_gmtoff / 36L);
   476 #endif
   477     return result;
   478 }
   479 
   480 struct tm * etpantime_to_timestamp(const struct mailimf_date_time *et)
   481 {
   482     struct tm * result = calloc(1, sizeof(struct tm));
   483     assert(result);
   484     if (result == NULL)
   485         return NULL;
   486 
   487     assert(et);
   488 
   489     result->tm_sec = et->dt_sec;
   490     result->tm_min = et->dt_min;
   491     result->tm_hour = et->dt_hour;
   492     result->tm_mday = et->dt_day;
   493     result->tm_mon = et->dt_month - 1;
   494     result->tm_year = et->dt_year - 1900;
   495 #ifndef WIN32
   496     result->tm_gmtoff = 36L * (long) et->dt_zone;
   497 #endif
   498     return result;
   499 }
   500 
   501 struct mailimf_mailbox * mailbox_from_string(
   502         const char *name,
   503         const char *address
   504     )
   505 {
   506     struct mailimf_mailbox *mb = NULL;
   507     char *_name = NULL;
   508     char *_address = NULL;
   509 
   510     assert(address);
   511 
   512     _name = name ? strdup(name) : strdup("");
   513     if (_name == NULL)
   514         goto enomem;
   515 
   516     _address = strdup(address);
   517     if (_address == NULL)
   518         goto enomem;
   519 
   520     mb = mailimf_mailbox_new(_name, _address);
   521     assert(mb);
   522     if (mb == NULL)
   523         goto enomem;
   524 
   525     return mb;
   526 
   527 enomem:
   528     free(_name);
   529     free(_address);
   530 
   531     return NULL;
   532 }
   533 
   534 struct mailimf_field * create_optional_field(
   535         const char *field,
   536         const char *value
   537     )
   538 {
   539     char *_field = NULL;
   540     char *_value = NULL;
   541     struct mailimf_optional_field *optional_field = NULL;
   542 
   543     _field = strdup(field);
   544     if (_field == NULL)
   545         goto enomem;
   546 
   547     _value = mailmime_encode_subject_header("utf-8", value, 0);
   548     if (_value == NULL)
   549         goto enomem;
   550 
   551     optional_field = mailimf_optional_field_new(_field, _value);
   552     if (optional_field == NULL)
   553         goto enomem;
   554 
   555     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   556     assert(result);
   557     if (result == NULL)
   558         goto enomem;
   559 
   560     result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
   561     result->fld_data.fld_optional_field = optional_field;
   562 
   563     return result;
   564 
   565 enomem:
   566     if (optional_field) {
   567         mailimf_optional_field_free(optional_field);
   568     }
   569     else {
   570         free(_field);
   571         free(_value);
   572     }
   573 
   574     return NULL;
   575 }
   576 
   577 int _append_optional_field(
   578         clist *list,
   579         const char *field,
   580         const char *value
   581     )
   582 {
   583     int r;
   584     struct mailimf_field * optional_field =
   585             create_optional_field(field, value);
   586 
   587     if (optional_field == NULL)
   588         return -1;
   589 
   590     r = clist_append(list, optional_field);
   591     if (r)
   592         mailimf_field_free(optional_field);
   593 
   594     return r;
   595 }
   596 
   597 clist * _get_fields(struct mailmime * mime)
   598 {
   599     clist * _fieldlist = NULL;
   600 
   601     assert(mime);
   602 
   603     if (mime->mm_data.mm_message.mm_fields &&
   604             mime->mm_data.mm_message.mm_fields->fld_list) {
   605         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   606     }
   607 
   608     return _fieldlist;
   609 }
   610 
   611 struct mailmime_content * _get_content(struct mailmime * mime)
   612 {
   613     struct mailmime_content * content = NULL;
   614 
   615     assert(mime);
   616 
   617     if (mime->mm_data.mm_message.mm_msg_mime)
   618         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   619 
   620     return content;
   621 }
   622 
   623 
   624 /* Return a list of identifier_type and resource id (filename, cid, etc) */
   625 pEp_rid_list_t* _get_resource_id_list(struct mailmime *mime)
   626 {
   627     clist * _fieldlist = NULL;
   628 
   629     assert(mime);
   630 
   631     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   632         _fieldlist = mime->mm_mime_fields->fld_list;
   633     else
   634         return NULL;
   635 
   636     clistiter *cur;
   637 
   638     pEp_rid_list_t* rid_list = NULL; 
   639     pEp_rid_list_t** rid_list_curr_p = &rid_list; 
   640         
   641     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   642         struct mailmime_field * _field = clist_content(cur);
   643         /* content_id */
   644         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   645             pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
   646             new_rid->rid_type = PEP_RID_CID;
   647             new_rid->rid = strdup(_field->fld_data.fld_id);
   648             *rid_list_curr_p = new_rid;
   649             rid_list_curr_p = &new_rid->next;
   650         }
   651         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   652             /* filename */
   653             if (_field->fld_data.fld_disposition &&
   654                     _field->fld_data.fld_disposition->dsp_parms) {
   655                 clist * _parmlist =
   656                         _field->fld_data.fld_disposition->dsp_parms;
   657                 clistiter *cur2;
   658                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   659                         clist_next(cur2)) {
   660                     struct mailmime_disposition_parm * param =
   661                             clist_content(cur2);
   662                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   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_FILENAME;
   665                         new_rid->rid = strdup(param->pa_data.pa_filename);
   666                         *rid_list_curr_p = new_rid;
   667                         rid_list_curr_p = &new_rid->next;
   668                     }                
   669                 }
   670             }
   671         }
   672     }
   673     /* Will almost certainly usually be a singleton, but we need to be able to decide */
   674     return rid_list;
   675 }
   676 
   677 
   678 /* FIXME: about to be obsoleted? */
   679 char * _get_filename_or_cid(struct mailmime *mime)
   680 {
   681     clist * _fieldlist = NULL;
   682 
   683     assert(mime);
   684 
   685     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   686         _fieldlist = mime->mm_mime_fields->fld_list;
   687     else
   688         return NULL;
   689 
   690     clistiter *cur;
   691     
   692     char* _temp_filename_ptr = NULL;
   693     
   694     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   695         struct mailmime_field * _field = clist_content(cur);
   696         if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
   697             /* We prefer CIDs to filenames when both are present */
   698             free(_temp_filename_ptr); /* can be null, it's ok */
   699             return build_uri("cid", _field->fld_data.fld_id); 
   700         }
   701         else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   702             if (_field->fld_data.fld_disposition &&
   703                     _field->fld_data.fld_disposition->dsp_parms &&
   704                     !_temp_filename_ptr) {
   705                 clist * _parmlist =
   706                         _field->fld_data.fld_disposition->dsp_parms;
   707                 clistiter *cur2;
   708                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   709                         clist_next(cur2)) {
   710                     struct mailmime_disposition_parm * param =
   711                             clist_content(cur2);
   712                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
   713                         _temp_filename_ptr = build_uri("file", param->pa_data.pa_filename);
   714                         break;
   715                     }                
   716                 }
   717             }
   718         }
   719     }
   720     /* Ok, it wasn't a CID */
   721     return _temp_filename_ptr;
   722 }
   723 
   724 static bool parameter_has_value(
   725         struct mailmime_content *content,       
   726         const char *name,
   727         const char *value
   728     )
   729 {
   730     clistiter *cur;
   731 
   732     assert(name);
   733     assert(value);
   734 
   735     clist * list = content->ct_parameters;
   736     if (list == NULL)
   737         return false;
   738 
   739     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   740         struct mailmime_parameter * param = clist_content(cur);
   741         if (param &&
   742                 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
   743                 param->pa_value && strcasecmp(value, param->pa_value) == 0)
   744             return true;
   745     }
   746 
   747     return false;
   748 }
   749 
   750 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   751 {
   752     assert(content);
   753 
   754     if (content->ct_type && content->ct_type->tp_type ==
   755             MAILMIME_TYPE_COMPOSITE_TYPE &&
   756             content->ct_type->tp_data.tp_composite_type &&
   757             content->ct_type->tp_data.tp_composite_type->ct_type ==
   758             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   759         if (subtype)
   760             return content->ct_subtype &&
   761                     strcasecmp(content->ct_subtype, subtype) == 0;
   762         else
   763             return true;
   764     }
   765 
   766     return false;
   767 }
   768 
   769 bool _is_PGP_MIME(struct mailmime_content *content)
   770 {
   771     assert(content);
   772 
   773     if (_is_multipart(content, "encrypted") &&
   774             parameter_has_value(content, "protocol",
   775                     "application/pgp-encrypted"))
   776         return true;
   777 
   778     return false;
   779 }
   780 
   781 bool _is_text_part(struct mailmime_content *content, const char *subtype)
   782 {
   783     assert(content);
   784 
   785     if (content->ct_type && content->ct_type->tp_type ==
   786             MAILMIME_TYPE_DISCRETE_TYPE &&
   787             content->ct_type->tp_data.tp_discrete_type &&
   788             content->ct_type->tp_data.tp_discrete_type->dt_type ==
   789             MAILMIME_DISCRETE_TYPE_TEXT) {
   790         if (subtype)
   791             return content->ct_subtype &&
   792                     strcasecmp(content->ct_subtype, subtype) == 0;
   793         else
   794             return true;
   795     }
   796 
   797     return false;
   798 }
   799 
   800 int _get_content_type(
   801         const struct mailmime_content *content,
   802         char **type,
   803         char **charset
   804     )
   805 {
   806     char *_type = NULL;
   807     char *_charset = NULL;
   808 
   809     assert(content);
   810     assert(type);
   811     assert(charset);
   812 
   813     *type = NULL;
   814     *charset = NULL;
   815 
   816     if (content->ct_subtype == NULL)
   817         return EINVAL;
   818 
   819     if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
   820         size_t len;
   821         const char *_main_type;
   822 
   823         switch  (content->ct_type->tp_data.tp_discrete_type->dt_type) {
   824             case MAILMIME_DISCRETE_TYPE_TEXT:
   825                 _main_type = "text";
   826                 break;
   827             case MAILMIME_DISCRETE_TYPE_IMAGE:
   828                 _main_type = "image";
   829                 break;
   830             case MAILMIME_DISCRETE_TYPE_AUDIO:
   831                 _main_type = "audio";
   832                 break;
   833             case MAILMIME_DISCRETE_TYPE_VIDEO:
   834                 _main_type = "video";
   835                 break;
   836             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   837                 _main_type = "application";
   838                 break;
   839             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   840                 _main_type = "extension";
   841                 break;
   842             default:
   843                 return EINVAL;
   844         }
   845 
   846         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   847         _type = calloc(1, len);
   848         assert(_type);
   849         if (_type == NULL)
   850             return ENOMEM;
   851 
   852         strncpy(_type, _main_type, len);
   853         len -= strlen(_main_type);
   854         strncat(_type, "/", len--);
   855         strncat(_type, content->ct_subtype, len);
   856 
   857         if (content->ct_parameters) {
   858             clistiter *cur;
   859             for (cur = clist_begin(content->ct_parameters); cur; cur =
   860                     clist_next(cur)) {
   861                 struct mailmime_parameter * param = clist_content(cur);
   862                 if (param && param->pa_name && strcasecmp(param->pa_name,
   863                             "charset") == 0) {
   864                     _charset = param->pa_value;
   865                     break;
   866                 }
   867             }
   868             if (_charset)
   869                 *charset = strdup(_charset);
   870         }
   871 
   872         *type = _type;
   873         return 0;
   874     }
   875 
   876     return EINVAL;
   877 }