src/etpan_mime.c
author Krista Grothoff <krista@pep-project.org>
Sun, 10 Jul 2016 08:32:24 +0200
changeset 819 72e21a35eb23
parent 747 d357dceebec6
child 820 03e6929fe32b
permissions -rw-r--r--
Added call to mailmime_parameter_free to deal with enomem cases which do not originate in the setup of the param data structure. (See trac issue 21)
     1 #include "etpan_mime.h"
     2 #ifndef mailmime_param_new_with_data
     3 #include <libetpan/mailprivacy_tools.h>
     4 #endif
     5 
     6 #include "platform.h"
     7 
     8 #include <string.h>
     9 #include <stdlib.h>
    10 #include <assert.h>
    11 #include <errno.h>
    12 
    13 #define MAX_MESSAGE_ID 128
    14 
    15 static char * generate_boundary(void)
    16 {
    17     char id[MAX_MESSAGE_ID];
    18 
    19     // no cryptographically strong random needed here
    20     const long value1 = random();
    21     const long value2 = random();
    22     const long value3 = random();
    23     const long value4 = random();
    24 
    25     snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
    26             value3, value4);
    27     
    28     return strdup(id);
    29 }
    30 
    31 struct mailmime * part_new_empty(
    32         struct mailmime_content * content,
    33         struct mailmime_fields * mime_fields,
    34         int force_single
    35     )
    36 {
    37 	struct mailmime * build_info;
    38 	clist * list = NULL;
    39 	int r;
    40 	int mime_type;
    41     char * attr_name = NULL;
    42     char * attr_value = NULL;
    43     struct mailmime_parameter * param = NULL;
    44     clist * parameters = NULL;
    45     char *boundary = NULL;
    46 
    47 	list = NULL;
    48 
    49 	if (force_single) {
    50 		mime_type = MAILMIME_SINGLE;
    51 	}
    52 	else {
    53 		switch (content->ct_type->tp_type) {
    54 			case MAILMIME_TYPE_DISCRETE_TYPE:
    55                 mime_type = MAILMIME_SINGLE;
    56                 break;
    57 
    58 			case MAILMIME_TYPE_COMPOSITE_TYPE:
    59                 switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
    60                     case MAILMIME_COMPOSITE_TYPE_MULTIPART:
    61                         mime_type = MAILMIME_MULTIPLE;
    62                         break;
    63 
    64                     case MAILMIME_COMPOSITE_TYPE_MESSAGE:
    65                         if (strcasecmp(content->ct_subtype, "rfc822") == 0)
    66                             mime_type = MAILMIME_MESSAGE;
    67                         else
    68                             mime_type = MAILMIME_SINGLE;
    69                         break;
    70 
    71                     default:
    72                         goto enomem;
    73                 }
    74                 break;
    75 
    76 			default:
    77                 goto enomem;
    78 		}
    79 	}
    80 
    81 	if (mime_type == MAILMIME_MULTIPLE) {
    82 		list = clist_new();
    83         assert(list);
    84 		if (list == NULL)
    85 			goto enomem;
    86 
    87 		attr_name = strdup("boundary");
    88         assert(attr_name);
    89         if (attr_name == NULL)
    90             goto enomem;
    91 
    92 		boundary = generate_boundary();
    93         assert(boundary);
    94 		attr_value = boundary;
    95 		if (attr_value == NULL)
    96 			goto enomem;
    97 
    98 		param = mailmime_parameter_new(attr_name, attr_value);
    99         assert(param);
   100 		if (param == NULL)
   101 			goto enomem;
   102         attr_name = NULL;
   103         attr_value = NULL;
   104 
   105 		if (content->ct_parameters == NULL) {
   106 			parameters = clist_new();
   107             assert(parameters);
   108 			if (parameters == NULL)
   109 				goto enomem;
   110 		}
   111 		else {
   112 			parameters = content->ct_parameters;
   113         }
   114 
   115 		r = clist_append(parameters, param);
   116 		if (r)
   117 			goto enomem;
   118         param = NULL;
   119 
   120 		if (content->ct_parameters == NULL)
   121 			content->ct_parameters = parameters;
   122 	}
   123 
   124     build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
   125             NULL, NULL, list, NULL, NULL);
   126 	if (build_info == NULL)
   127 		goto enomem;
   128 
   129 	return build_info;
   130 
   131 enomem:
   132     if (list)
   133         clist_free(list);
   134     free(attr_name);
   135     free(attr_value);
   136     if (content->ct_parameters == NULL)
   137         if (parameters)
   138             clist_free(parameters);
   139     if (param)
   140         mailmime_parameter_free(param);
   141 
   142 	return NULL;
   143 }
   144 
   145 struct mailmime * get_pgp_encrypted_part(void)
   146 {
   147 	struct mailmime * mime = NULL;
   148 	struct mailmime_fields * mime_fields = NULL;
   149 	struct mailmime_content * content = NULL;
   150     int r;
   151 
   152 	content = mailmime_content_new_with_str("application/pgp-encrypted");
   153     if (content == NULL)
   154         goto enomem;
   155 
   156     mime_fields = mailmime_fields_new_empty();
   157     if (mime_fields == NULL)
   158         goto enomem;
   159 
   160 	mime = part_new_empty(content, mime_fields, 1);
   161     if (mime == NULL)
   162         goto enomem;
   163     mime_fields = NULL;
   164     content = NULL;
   165 
   166     r = mailmime_set_body_text(mime, "Version: 1\n", 10);
   167     if (r != 0)
   168         goto enomem;
   169 
   170 	return mime;
   171 
   172 enomem:
   173     if (content)
   174         mailmime_content_free(content);
   175     if (mime_fields)
   176         mailmime_fields_free(mime_fields);
   177     if (mime)
   178         mailmime_free(mime);
   179 
   180     return NULL;
   181 }
   182 
   183 struct mailmime * get_text_part(
   184         const char * filename,
   185         const char * mime_type,
   186         const char * text,
   187         size_t length,
   188         int encoding_type
   189     )
   190 {
   191     char * disposition_name = NULL;
   192 	struct mailmime_fields * mime_fields = NULL;
   193 	struct mailmime * mime = NULL;
   194 	struct mailmime_content * content = NULL;
   195 	struct mailmime_parameter * param = NULL;
   196 	struct mailmime_disposition * disposition = NULL;
   197 	struct mailmime_mechanism * encoding = NULL;
   198     int r;
   199     
   200     if (filename != NULL) {
   201         disposition_name = strdup(filename);
   202         if (disposition_name == NULL)
   203             goto enomem;
   204     }
   205 
   206     if (encoding_type) {
   207         encoding = mailmime_mechanism_new(encoding_type, NULL);
   208         if (encoding == NULL)
   209             goto enomem;
   210     }
   211 
   212     disposition =
   213             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   214                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   215     if (disposition == NULL)
   216         goto enomem;
   217     disposition_name = NULL;
   218 
   219     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   220             disposition, NULL);
   221     if (mime_fields == NULL)
   222         goto enomem;
   223     encoding = NULL;
   224     disposition = NULL;
   225 
   226 	content = mailmime_content_new_with_str(mime_type);
   227     if (content == NULL)
   228         goto enomem;
   229     
   230     if (encoding_type != MAILMIME_MECHANISM_7BIT) {
   231         param = mailmime_param_new_with_data("charset", "utf-8");
   232         r = clist_append(content->ct_parameters, param);
   233         if (r != 0)
   234             goto enomem;
   235     }
   236 
   237 	mime = part_new_empty(content, mime_fields, 1);
   238     if (mime == NULL)
   239         goto enomem;
   240     content = NULL;
   241     mime_fields = NULL;
   242 
   243     if (text) {
   244         r = mailmime_set_body_text(mime, (char *) text, length);
   245         if (r != 0)
   246             goto enomem;
   247     }
   248 	
   249 	return mime;
   250 
   251 enomem:
   252     free(disposition_name);
   253     if (mime_fields)
   254         mailmime_fields_free(mime_fields);
   255     if (mime)
   256         mailmime_free(mime);
   257     if (content)
   258         mailmime_content_free(content);
   259     if (param)
   260         mailmime_parameter_free(param);
   261     if (disposition)
   262         mailmime_disposition_free(disposition);
   263     if (encoding)
   264         mailmime_mechanism_free(encoding);
   265 
   266     return NULL;
   267 }
   268 
   269 struct mailmime * get_file_part(
   270         const char * filename,
   271         const char * mime_type,
   272         char * data,
   273         size_t length
   274     )
   275 {
   276     char * disposition_name = NULL;
   277     int encoding_type;
   278     struct mailmime_disposition * disposition = NULL;
   279     struct mailmime_mechanism * encoding = NULL;
   280     struct mailmime_content * content = NULL;
   281     struct mailmime * mime = NULL;
   282     struct mailmime_fields * mime_fields = NULL;
   283     int r;
   284 
   285     if (filename != NULL) {
   286         disposition_name = strdup(filename);
   287         if (disposition_name == NULL)
   288             goto enomem;
   289     }
   290 
   291     disposition =
   292             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   293                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   294     if (disposition == NULL)
   295         goto enomem;
   296     disposition_name = NULL;
   297 
   298     content = mailmime_content_new_with_str(mime_type);
   299     if (content == NULL)
   300         goto enomem;
   301 
   302     encoding_type = MAILMIME_MECHANISM_BASE64;
   303     encoding = mailmime_mechanism_new(encoding_type, NULL);
   304     if (encoding == NULL)
   305         goto enomem;
   306 
   307     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   308             disposition, NULL);
   309     if (mime_fields == NULL)
   310         goto enomem;
   311     encoding = NULL;
   312     disposition = NULL;
   313 
   314     mime = part_new_empty(content, mime_fields, 1);
   315     if (mime == NULL)
   316         goto enomem;
   317     content = NULL;
   318     mime_fields = NULL;
   319 
   320     if(length > 0)
   321     {
   322         r = mailmime_set_body_text(mime, data, length);
   323         if (r != 0)
   324             goto enomem;
   325     }
   326 
   327     return mime;
   328 
   329 enomem:
   330     free(disposition_name);
   331     if (disposition)
   332         mailmime_disposition_free(disposition);
   333     if (encoding)
   334         mailmime_mechanism_free(encoding);
   335     if (content)
   336         mailmime_content_free(content);
   337     if (mime_fields)
   338         mailmime_fields_free(mime_fields);
   339     if (mime)
   340         mailmime_free(mime);
   341 
   342     return NULL;
   343 }
   344 
   345 struct mailmime * part_multiple_new(const char *type)
   346 {
   347     struct mailmime_fields *mime_fields = NULL;
   348     struct mailmime_content *content = NULL;
   349     struct mailmime *mp = NULL;
   350     
   351     mime_fields = mailmime_fields_new_empty();
   352     if (mime_fields == NULL)
   353         goto enomem;
   354     
   355     content = mailmime_content_new_with_str(type);
   356     if (content == NULL)
   357         goto enomem;
   358     
   359     mp = part_new_empty(content, mime_fields, 0);
   360     if (mp == NULL)
   361         goto enomem;
   362     
   363     return mp;
   364     
   365 enomem:
   366     if (content)
   367         mailmime_content_free(content);
   368     if (mime_fields)
   369         mailmime_fields_free(mime_fields);
   370 
   371     return NULL;
   372 }
   373 
   374 struct mailimf_field * _new_field(
   375         int type,
   376         _new_func_t new_func,
   377         void *value
   378     )
   379 {
   380     void *data = new_func(value);
   381     assert(data);
   382     if (data == NULL)
   383         return NULL;
   384 
   385     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   386     assert(result);
   387     if (result == NULL) {
   388         free(data);
   389         return NULL;
   390     }
   391 
   392     result->fld_type = type;
   393     result->fld_data.fld_return_path = data;
   394 
   395     return result;
   396 }
   397 
   398 void _free_field(struct mailimf_field *field)
   399 {
   400     if (field)
   401         free(field->fld_data.fld_return_path);
   402     free(field);
   403 }
   404 
   405 int _append_field(
   406         clist *list,
   407         int type,
   408         _new_func_t new_func,
   409         void *value
   410     )
   411 {
   412     int r;
   413     struct mailimf_field * field;
   414 
   415     assert(list);
   416     assert(new_func);
   417     assert(value);
   418 
   419     field = _new_field(type, new_func, value);
   420     if (field == NULL)
   421         return -1;
   422 
   423     r = clist_append(list, field);
   424     if (r)
   425         _free_field(field);
   426 
   427     return r;
   428 }
   429 
   430 // http://media2.giga.de/2014/02/Image-28.jpg
   431 
   432 struct mailimf_date_time * timestamp_to_etpantime(const struct tm *ts)
   433 {
   434     struct mailimf_date_time * result = calloc(1,
   435             sizeof(struct mailimf_date_time));
   436     assert(result);
   437     if (result == NULL)
   438         return NULL;
   439 
   440     assert(ts);
   441 
   442     result->dt_sec = ts->tm_sec;
   443     result->dt_min = ts->tm_min;
   444     result->dt_hour = ts->tm_hour;
   445     result->dt_day = ts->tm_mday;
   446     result->dt_month = ts->tm_mon + 1;
   447     result->dt_year = ts->tm_year + 1900;
   448 #ifndef WIN32
   449     result->dt_zone = (int) (ts->tm_gmtoff / 36L);
   450 #endif
   451     return result;
   452 }
   453 
   454 struct tm * etpantime_to_timestamp(const struct mailimf_date_time *et)
   455 {
   456     struct tm * result = calloc(1, sizeof(struct tm));
   457     assert(result);
   458     if (result == NULL)
   459         return NULL;
   460 
   461     assert(et);
   462 
   463     result->tm_sec = et->dt_sec;
   464     result->tm_min = et->dt_min;
   465     result->tm_hour = et->dt_hour;
   466     result->tm_mday = et->dt_day;
   467     result->tm_mon = et->dt_month - 1;
   468     result->tm_year = et->dt_year - 1900;
   469 #ifndef WIN32
   470     result->tm_gmtoff = 36L * (long) et->dt_zone;
   471 #endif
   472     return result;
   473 }
   474 
   475 struct mailimf_mailbox * mailbox_from_string(
   476         const char *name,
   477         const char *address
   478     )
   479 {
   480     struct mailimf_mailbox *mb = NULL;
   481     char *_name = NULL;
   482     char *_address = NULL;
   483 
   484     assert(address);
   485 
   486     _name = name ? strdup(name) : strdup("");
   487     if (_name == NULL)
   488         goto enomem;
   489 
   490     _address = strdup(address);
   491     if (_address == NULL)
   492         goto enomem;
   493 
   494     mb = mailimf_mailbox_new(_name, _address);
   495     assert(mb);
   496     if (mb == NULL)
   497         goto enomem;
   498 
   499     return mb;
   500 
   501 enomem:
   502     free(_name);
   503     free(_address);
   504 
   505     return NULL;
   506 }
   507 
   508 struct mailimf_field * create_optional_field(
   509         const char *field,
   510         const char *value
   511     )
   512 {
   513     char *_field = NULL;
   514     char *_value = NULL;
   515     struct mailimf_optional_field *optional_field = NULL;
   516 
   517     _field = strdup(field);
   518     if (_field == NULL)
   519         goto enomem;
   520 
   521     _value = mailmime_encode_subject_header("utf-8", value, 0);
   522     if (_value == NULL)
   523         goto enomem;
   524 
   525     optional_field = mailimf_optional_field_new(_field, _value);
   526     if (optional_field == NULL)
   527         goto enomem;
   528 
   529     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   530     assert(result);
   531     if (result == NULL)
   532         goto enomem;
   533 
   534     result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
   535     result->fld_data.fld_optional_field = optional_field;
   536 
   537     return result;
   538 
   539 enomem:
   540     if (optional_field) {
   541         mailimf_optional_field_free(optional_field);
   542     }
   543     else {
   544         free(_field);
   545         free(_value);
   546     }
   547 
   548     return NULL;
   549 }
   550 
   551 int _append_optional_field(
   552         clist *list,
   553         const char *field,
   554         const char *value
   555     )
   556 {
   557     int r;
   558     struct mailimf_field * optional_field =
   559             create_optional_field(field, value);
   560 
   561     if (optional_field == NULL)
   562         return -1;
   563 
   564     r = clist_append(list, optional_field);
   565     if (r)
   566         mailimf_field_free(optional_field);
   567 
   568     return r;
   569 }
   570 
   571 clist * _get_fields(struct mailmime * mime)
   572 {
   573     clist * _fieldlist = NULL;
   574 
   575     assert(mime);
   576 
   577     if (mime->mm_data.mm_message.mm_fields &&
   578             mime->mm_data.mm_message.mm_fields->fld_list) {
   579         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   580     }
   581 
   582     return _fieldlist;
   583 }
   584 
   585 struct mailmime_content * _get_content(struct mailmime * mime)
   586 {
   587     struct mailmime_content * content = NULL;
   588 
   589     assert(mime);
   590 
   591     if (mime->mm_data.mm_message.mm_msg_mime)
   592         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   593 
   594     return content;
   595 }
   596 
   597 char * _get_filename(struct mailmime *mime)
   598 {
   599     clist * _fieldlist = NULL;
   600 
   601     assert(mime);
   602 
   603     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   604         _fieldlist = mime->mm_mime_fields->fld_list;
   605     else
   606         return NULL;
   607 
   608     clistiter *cur;
   609     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   610         struct mailmime_field * _field = clist_content(cur);
   611         if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   612             if (_field->fld_data.fld_disposition &&
   613                     _field->fld_data.fld_disposition->dsp_parms) {
   614                 clist * _parmlist =
   615                         _field->fld_data.fld_disposition->dsp_parms;
   616                 clistiter *cur2;
   617                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   618                         clist_next(cur2)) {
   619                     struct mailmime_disposition_parm * param =
   620                             clist_content(cur2);
   621                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME)
   622                         return param->pa_data.pa_filename;
   623                 }
   624             }
   625         }
   626     }
   627 
   628     return NULL;
   629 }
   630 
   631 static bool parameter_has_value(
   632         struct mailmime_content *content,       
   633         const char *name,
   634         const char *value
   635     )
   636 {
   637     clistiter *cur;
   638 
   639     assert(name);
   640     assert(value);
   641 
   642     clist * list = content->ct_parameters;
   643     if (list == NULL)
   644         return false;
   645 
   646     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   647         struct mailmime_parameter * param = clist_content(cur);
   648         if (param &&
   649                 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
   650                 param->pa_value && strcasecmp(value, param->pa_value) == 0)
   651             return true;
   652     }
   653 
   654     return false;
   655 }
   656 
   657 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   658 {
   659     assert(content);
   660 
   661     if (content->ct_type && content->ct_type->tp_type ==
   662             MAILMIME_TYPE_COMPOSITE_TYPE &&
   663             content->ct_type->tp_data.tp_composite_type &&
   664             content->ct_type->tp_data.tp_composite_type->ct_type ==
   665             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   666         if (subtype)
   667             return content->ct_subtype &&
   668                     strcasecmp(content->ct_subtype, subtype) == 0;
   669         else
   670             return true;
   671     }
   672 
   673     return false;
   674 }
   675 
   676 bool _is_PGP_MIME(struct mailmime_content *content)
   677 {
   678     assert(content);
   679 
   680     if (_is_multipart(content, "encrypted") &&
   681             parameter_has_value(content, "protocol",
   682                     "application/pgp-encrypted"))
   683         return true;
   684 
   685     return false;
   686 }
   687 
   688 bool _is_text_part(struct mailmime_content *content, const char *subtype)
   689 {
   690     assert(content);
   691 
   692     if (content->ct_type && content->ct_type->tp_type ==
   693             MAILMIME_TYPE_DISCRETE_TYPE &&
   694             content->ct_type->tp_data.tp_discrete_type &&
   695             content->ct_type->tp_data.tp_discrete_type->dt_type ==
   696             MAILMIME_DISCRETE_TYPE_TEXT) {
   697         if (subtype)
   698             return content->ct_subtype &&
   699                     strcasecmp(content->ct_subtype, subtype) == 0;
   700         else
   701             return true;
   702     }
   703 
   704     return false;
   705 }
   706 
   707 int _get_content_type(
   708         const struct mailmime_content *content,
   709         char **type,
   710         char **charset
   711     )
   712 {
   713     char *_type = NULL;
   714     char *_charset = NULL;
   715 
   716     assert(content);
   717     assert(type);
   718     assert(charset);
   719 
   720     *type = NULL;
   721     *charset = NULL;
   722 
   723     if (content->ct_subtype == NULL)
   724         return EINVAL;
   725 
   726     if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
   727         size_t len;
   728         const char *_main_type;
   729 
   730         switch  (content->ct_type->tp_data.tp_discrete_type->dt_type) {
   731             case MAILMIME_DISCRETE_TYPE_TEXT:
   732                 _main_type = "text";
   733                 break;
   734             case MAILMIME_DISCRETE_TYPE_IMAGE:
   735                 _main_type = "image";
   736                 break;
   737             case MAILMIME_DISCRETE_TYPE_AUDIO:
   738                 _main_type = "audio";
   739                 break;
   740             case MAILMIME_DISCRETE_TYPE_VIDEO:
   741                 _main_type = "video";
   742                 break;
   743             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   744                 _main_type = "application";
   745                 break;
   746             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   747                 _main_type = "extension";
   748                 break;
   749             default:
   750                 return EINVAL;
   751         }
   752 
   753         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   754         _type = calloc(1, len);
   755         assert(_type);
   756         if (_type == NULL)
   757             return ENOMEM;
   758 
   759         strcpy(_type, _main_type);
   760         strcat(_type, "/");
   761         strcat(_type, content->ct_subtype);
   762 
   763         if (content->ct_parameters) {
   764             clistiter *cur;
   765             for (cur = clist_begin(content->ct_parameters); cur; cur =
   766                     clist_next(cur)) {
   767                 struct mailmime_parameter * param = clist_content(cur);
   768                 if (param && param->pa_name && strcasecmp(param->pa_name,
   769                             "charset") == 0) {
   770                     _charset = param->pa_value;
   771                     break;
   772                 }
   773             }
   774             if (_charset)
   775                 *charset = strdup(_charset);
   776         }
   777 
   778         *type = _type;
   779         return 0;
   780     }
   781 
   782     return EINVAL;
   783 }
   784