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