src/etpan_mime.c
author Roker <roker@pep-project.org>
Sat, 30 Jan 2016 11:20:54 +0100
changeset 438 106b9765559d
parent 165 f0894860af4a
child 747 d357dceebec6
permissions -rw-r--r--
fix random() implementation for Android, so not always the same number is returned. Add comment that random() is predictive and non-secure
     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 
   140 	return NULL;
   141 }
   142 
   143 struct mailmime * get_pgp_encrypted_part(void)
   144 {
   145 	struct mailmime * mime = NULL;
   146 	struct mailmime_fields * mime_fields = NULL;
   147 	struct mailmime_content * content = NULL;
   148     int r;
   149 
   150 	content = mailmime_content_new_with_str("application/pgp-encrypted");
   151     if (content == NULL)
   152         goto enomem;
   153 
   154     mime_fields = mailmime_fields_new_empty();
   155     if (mime_fields == NULL)
   156         goto enomem;
   157 
   158 	mime = part_new_empty(content, mime_fields, 1);
   159     if (mime == NULL)
   160         goto enomem;
   161     mime_fields = NULL;
   162     content = NULL;
   163 
   164     r = mailmime_set_body_text(mime, "Version: 1\n", 10);
   165     if (r != 0)
   166         goto enomem;
   167 
   168 	return mime;
   169 
   170 enomem:
   171     if (content)
   172         mailmime_content_free(content);
   173     if (mime_fields)
   174         mailmime_fields_free(mime_fields);
   175     if (mime)
   176         mailmime_free(mime);
   177 
   178     return NULL;
   179 }
   180 
   181 struct mailmime * get_text_part(
   182         const char * filename,
   183         const char * mime_type,
   184         const char * text,
   185         size_t length,
   186         int encoding_type
   187     )
   188 {
   189     char * disposition_name = NULL;
   190 	struct mailmime_fields * mime_fields = NULL;
   191 	struct mailmime * mime = NULL;
   192 	struct mailmime_content * content = NULL;
   193 	struct mailmime_parameter * param = NULL;
   194 	struct mailmime_disposition * disposition = NULL;
   195 	struct mailmime_mechanism * encoding = NULL;
   196     int r;
   197     
   198     if (filename != NULL) {
   199         disposition_name = strdup(filename);
   200         if (disposition_name == NULL)
   201             goto enomem;
   202     }
   203 
   204     if (encoding_type) {
   205         encoding = mailmime_mechanism_new(encoding_type, NULL);
   206         if (encoding == NULL)
   207             goto enomem;
   208     }
   209 
   210     disposition =
   211             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   212                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   213     if (disposition == NULL)
   214         goto enomem;
   215     disposition_name = NULL;
   216 
   217     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   218             disposition, NULL);
   219     if (mime_fields == NULL)
   220         goto enomem;
   221     encoding = NULL;
   222     disposition = NULL;
   223 
   224 	content = mailmime_content_new_with_str(mime_type);
   225     if (content == NULL)
   226         goto enomem;
   227     
   228     if (encoding_type != MAILMIME_MECHANISM_7BIT) {
   229         param = mailmime_param_new_with_data("charset", "utf-8");
   230         r = clist_append(content->ct_parameters, param);
   231         if (r != 0)
   232             goto enomem;
   233     }
   234 
   235 	mime = part_new_empty(content, mime_fields, 1);
   236     if (mime == NULL)
   237         goto enomem;
   238     content = NULL;
   239     mime_fields = NULL;
   240 
   241     if (text) {
   242         r = mailmime_set_body_text(mime, (char *) text, length);
   243         if (r != 0)
   244             goto enomem;
   245     }
   246 	
   247 	return mime;
   248 
   249 enomem:
   250     free(disposition_name);
   251     if (mime_fields)
   252         mailmime_fields_free(mime_fields);
   253     if (mime)
   254         mailmime_free(mime);
   255     if (content)
   256         mailmime_content_free(content);
   257     if (param)
   258         mailmime_parameter_free(param);
   259     if (disposition)
   260         mailmime_disposition_free(disposition);
   261     if (encoding)
   262         mailmime_mechanism_free(encoding);
   263 
   264     return NULL;
   265 }
   266 
   267 struct mailmime * get_file_part(
   268         const char * filename,
   269         const char * mime_type,
   270         char * data,
   271         size_t length
   272     )
   273 {
   274     char * disposition_name = NULL;
   275     int encoding_type;
   276     struct mailmime_disposition * disposition = NULL;
   277     struct mailmime_mechanism * encoding = NULL;
   278     struct mailmime_content * content = NULL;
   279     struct mailmime * mime = NULL;
   280     struct mailmime_fields * mime_fields = NULL;
   281     int r;
   282 
   283     if (filename != NULL) {
   284         disposition_name = strdup(filename);
   285         if (disposition_name == NULL)
   286             goto enomem;
   287     }
   288 
   289     disposition =
   290             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   291                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   292     if (disposition == NULL)
   293         goto enomem;
   294     disposition_name = NULL;
   295 
   296     content = mailmime_content_new_with_str(mime_type);
   297     if (content == NULL)
   298         goto enomem;
   299 
   300     encoding_type = MAILMIME_MECHANISM_BASE64;
   301     encoding = mailmime_mechanism_new(encoding_type, NULL);
   302     if (encoding == NULL)
   303         goto enomem;
   304 
   305     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   306             disposition, NULL);
   307     if (mime_fields == NULL)
   308         goto enomem;
   309     encoding = NULL;
   310     disposition = NULL;
   311 
   312     mime = part_new_empty(content, mime_fields, 1);
   313     if (mime == NULL)
   314         goto enomem;
   315     content = NULL;
   316     mime_fields = NULL;
   317 
   318     r = mailmime_set_body_text(mime, data, length);
   319     if (r != 0)
   320         goto enomem;
   321 
   322     return mime;
   323 
   324 enomem:
   325     free(disposition_name);
   326     if (disposition)
   327         mailmime_disposition_free(disposition);
   328     if (encoding)
   329         mailmime_mechanism_free(encoding);
   330     if (content)
   331         mailmime_content_free(content);
   332     if (mime_fields)
   333         mailmime_fields_free(mime_fields);
   334     if (mime)
   335         mailmime_free(mime);
   336 
   337     return NULL;
   338 }
   339 
   340 struct mailmime * part_multiple_new(const char *type)
   341 {
   342     struct mailmime_fields *mime_fields = NULL;
   343     struct mailmime_content *content = NULL;
   344     struct mailmime *mp = NULL;
   345     
   346     mime_fields = mailmime_fields_new_empty();
   347     if (mime_fields == NULL)
   348         goto enomem;
   349     
   350     content = mailmime_content_new_with_str(type);
   351     if (content == NULL)
   352         goto enomem;
   353     
   354     mp = part_new_empty(content, mime_fields, 0);
   355     if (mp == NULL)
   356         goto enomem;
   357     
   358     return mp;
   359     
   360 enomem:
   361     if (content)
   362         mailmime_content_free(content);
   363     if (mime_fields)
   364         mailmime_fields_free(mime_fields);
   365 
   366     return NULL;
   367 }
   368 
   369 struct mailimf_field * _new_field(
   370         int type,
   371         _new_func_t new_func,
   372         void *value
   373     )
   374 {
   375     void *data = new_func(value);
   376     assert(data);
   377     if (data == NULL)
   378         return NULL;
   379 
   380     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   381     assert(result);
   382     if (result == NULL) {
   383         free(data);
   384         return NULL;
   385     }
   386 
   387     result->fld_type = type;
   388     result->fld_data.fld_return_path = data;
   389 
   390     return result;
   391 }
   392 
   393 void _free_field(struct mailimf_field *field)
   394 {
   395     if (field)
   396         free(field->fld_data.fld_return_path);
   397     free(field);
   398 }
   399 
   400 int _append_field(
   401         clist *list,
   402         int type,
   403         _new_func_t new_func,
   404         void *value
   405     )
   406 {
   407     int r;
   408     struct mailimf_field * field;
   409 
   410     assert(list);
   411     assert(new_func);
   412     assert(value);
   413 
   414     field = _new_field(type, new_func, value);
   415     if (field == NULL)
   416         return -1;
   417 
   418     r = clist_append(list, field);
   419     if (r)
   420         _free_field(field);
   421 
   422     return r;
   423 }
   424 
   425 // http://media2.giga.de/2014/02/Image-28.jpg
   426 
   427 struct mailimf_date_time * timestamp_to_etpantime(const struct tm *ts)
   428 {
   429     struct mailimf_date_time * result = calloc(1,
   430             sizeof(struct mailimf_date_time));
   431     assert(result);
   432     if (result == NULL)
   433         return NULL;
   434 
   435     assert(ts);
   436 
   437     result->dt_sec = ts->tm_sec;
   438     result->dt_min = ts->tm_min;
   439     result->dt_hour = ts->tm_hour;
   440     result->dt_day = ts->tm_mday;
   441     result->dt_month = ts->tm_mon + 1;
   442     result->dt_year = ts->tm_year + 1900;
   443 #ifndef WIN32
   444     result->dt_zone = (int) (ts->tm_gmtoff / 36L);
   445 #endif
   446     return result;
   447 }
   448 
   449 struct tm * etpantime_to_timestamp(const struct mailimf_date_time *et)
   450 {
   451     struct tm * result = calloc(1, sizeof(struct tm));
   452     assert(result);
   453     if (result == NULL)
   454         return NULL;
   455 
   456     assert(et);
   457 
   458     result->tm_sec = et->dt_sec;
   459     result->tm_min = et->dt_min;
   460     result->tm_hour = et->dt_hour;
   461     result->tm_mday = et->dt_day;
   462     result->tm_mon = et->dt_month - 1;
   463     result->tm_year = et->dt_year - 1900;
   464 #ifndef WIN32
   465     result->tm_gmtoff = 36L * (long) et->dt_zone;
   466 #endif
   467     return result;
   468 }
   469 
   470 struct mailimf_mailbox * mailbox_from_string(
   471         const char *name,
   472         const char *address
   473     )
   474 {
   475     struct mailimf_mailbox *mb = NULL;
   476     char *_name = NULL;
   477     char *_address = NULL;
   478 
   479     assert(address);
   480 
   481     _name = name ? strdup(name) : strdup("");
   482     if (_name == NULL)
   483         goto enomem;
   484 
   485     _address = strdup(address);
   486     if (_address == NULL)
   487         goto enomem;
   488 
   489     mb = mailimf_mailbox_new(_name, _address);
   490     assert(mb);
   491     if (mb == NULL)
   492         goto enomem;
   493 
   494     return mb;
   495 
   496 enomem:
   497     free(_name);
   498     free(_address);
   499 
   500     return NULL;
   501 }
   502 
   503 struct mailimf_field * create_optional_field(
   504         const char *field,
   505         const char *value
   506     )
   507 {
   508     char *_field = NULL;
   509     char *_value = NULL;
   510     struct mailimf_optional_field *optional_field = NULL;
   511 
   512     _field = strdup(field);
   513     if (_field == NULL)
   514         goto enomem;
   515 
   516     _value = mailmime_encode_subject_header("utf-8", value, 0);
   517     if (_value == NULL)
   518         goto enomem;
   519 
   520     optional_field = mailimf_optional_field_new(_field, _value);
   521     if (optional_field == NULL)
   522         goto enomem;
   523 
   524     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   525     assert(result);
   526     if (result == NULL)
   527         goto enomem;
   528 
   529     result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
   530     result->fld_data.fld_optional_field = optional_field;
   531 
   532     return result;
   533 
   534 enomem:
   535     if (optional_field) {
   536         mailimf_optional_field_free(optional_field);
   537     }
   538     else {
   539         free(_field);
   540         free(_value);
   541     }
   542 
   543     return NULL;
   544 }
   545 
   546 int _append_optional_field(
   547         clist *list,
   548         const char *field,
   549         const char *value
   550     )
   551 {
   552     int r;
   553     struct mailimf_field * optional_field =
   554             create_optional_field(field, value);
   555 
   556     if (optional_field == NULL)
   557         return -1;
   558 
   559     r = clist_append(list, optional_field);
   560     if (r)
   561         mailimf_field_free(optional_field);
   562 
   563     return r;
   564 }
   565 
   566 clist * _get_fields(struct mailmime * mime)
   567 {
   568     clist * _fieldlist = NULL;
   569 
   570     assert(mime);
   571 
   572     if (mime->mm_data.mm_message.mm_fields &&
   573             mime->mm_data.mm_message.mm_fields->fld_list) {
   574         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   575     }
   576 
   577     return _fieldlist;
   578 }
   579 
   580 struct mailmime_content * _get_content(struct mailmime * mime)
   581 {
   582     struct mailmime_content * content = NULL;
   583 
   584     assert(mime);
   585 
   586     if (mime->mm_data.mm_message.mm_msg_mime)
   587         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   588 
   589     return content;
   590 }
   591 
   592 char * _get_filename(struct mailmime *mime)
   593 {
   594     clist * _fieldlist = NULL;
   595 
   596     assert(mime);
   597 
   598     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   599         _fieldlist = mime->mm_mime_fields->fld_list;
   600     else
   601         return NULL;
   602 
   603     clistiter *cur;
   604     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   605         struct mailmime_field * _field = clist_content(cur);
   606         if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   607             if (_field->fld_data.fld_disposition &&
   608                     _field->fld_data.fld_disposition->dsp_parms) {
   609                 clist * _parmlist =
   610                         _field->fld_data.fld_disposition->dsp_parms;
   611                 clistiter *cur2;
   612                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   613                         clist_next(cur2)) {
   614                     struct mailmime_disposition_parm * param =
   615                             clist_content(cur2);
   616                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME)
   617                         return param->pa_data.pa_filename;
   618                 }
   619             }
   620         }
   621     }
   622 
   623     return NULL;
   624 }
   625 
   626 static bool parameter_has_value(
   627         struct mailmime_content *content,       
   628         const char *name,
   629         const char *value
   630     )
   631 {
   632     clistiter *cur;
   633 
   634     assert(name);
   635     assert(value);
   636 
   637     clist * list = content->ct_parameters;
   638     if (list == NULL)
   639         return false;
   640 
   641     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   642         struct mailmime_parameter * param = clist_content(cur);
   643         if (param &&
   644                 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
   645                 param->pa_value && strcasecmp(value, param->pa_value) == 0)
   646             return true;
   647     }
   648 
   649     return false;
   650 }
   651 
   652 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   653 {
   654     assert(content);
   655 
   656     if (content->ct_type && content->ct_type->tp_type ==
   657             MAILMIME_TYPE_COMPOSITE_TYPE &&
   658             content->ct_type->tp_data.tp_composite_type &&
   659             content->ct_type->tp_data.tp_composite_type->ct_type ==
   660             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   661         if (subtype)
   662             return content->ct_subtype &&
   663                     strcasecmp(content->ct_subtype, subtype) == 0;
   664         else
   665             return true;
   666     }
   667 
   668     return false;
   669 }
   670 
   671 bool _is_PGP_MIME(struct mailmime_content *content)
   672 {
   673     assert(content);
   674 
   675     if (_is_multipart(content, "encrypted") &&
   676             parameter_has_value(content, "protocol",
   677                     "application/pgp-encrypted"))
   678         return true;
   679 
   680     return false;
   681 }
   682 
   683 bool _is_text_part(struct mailmime_content *content, const char *subtype)
   684 {
   685     assert(content);
   686 
   687     if (content->ct_type && content->ct_type->tp_type ==
   688             MAILMIME_TYPE_DISCRETE_TYPE &&
   689             content->ct_type->tp_data.tp_discrete_type &&
   690             content->ct_type->tp_data.tp_discrete_type->dt_type ==
   691             MAILMIME_DISCRETE_TYPE_TEXT) {
   692         if (subtype)
   693             return content->ct_subtype &&
   694                     strcasecmp(content->ct_subtype, subtype) == 0;
   695         else
   696             return true;
   697     }
   698 
   699     return false;
   700 }
   701 
   702 int _get_content_type(
   703         const struct mailmime_content *content,
   704         char **type,
   705         char **charset
   706     )
   707 {
   708     char *_type = NULL;
   709     char *_charset = NULL;
   710 
   711     assert(content);
   712     assert(type);
   713     assert(charset);
   714 
   715     *type = NULL;
   716     *charset = NULL;
   717 
   718     if (content->ct_subtype == NULL)
   719         return EINVAL;
   720 
   721     if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
   722         size_t len;
   723         const char *_main_type;
   724 
   725         switch  (content->ct_type->tp_data.tp_discrete_type->dt_type) {
   726             case MAILMIME_DISCRETE_TYPE_TEXT:
   727                 _main_type = "text";
   728                 break;
   729             case MAILMIME_DISCRETE_TYPE_IMAGE:
   730                 _main_type = "image";
   731                 break;
   732             case MAILMIME_DISCRETE_TYPE_AUDIO:
   733                 _main_type = "audio";
   734                 break;
   735             case MAILMIME_DISCRETE_TYPE_VIDEO:
   736                 _main_type = "video";
   737                 break;
   738             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   739                 _main_type = "application";
   740                 break;
   741             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   742                 _main_type = "extension";
   743                 break;
   744             default:
   745                 return EINVAL;
   746         }
   747 
   748         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   749         _type = calloc(1, len);
   750         assert(_type);
   751         if (_type == NULL)
   752             return ENOMEM;
   753 
   754         strcpy(_type, _main_type);
   755         strcat(_type, "/");
   756         strcat(_type, content->ct_subtype);
   757 
   758         if (content->ct_parameters) {
   759             clistiter *cur;
   760             for (cur = clist_begin(content->ct_parameters); cur; cur =
   761                     clist_next(cur)) {
   762                 struct mailmime_parameter * param = clist_content(cur);
   763                 if (param && param->pa_name && strcasecmp(param->pa_name,
   764                             "charset") == 0) {
   765                     _charset = param->pa_value;
   766                     break;
   767                 }
   768             }
   769             if (_charset)
   770                 *charset = strdup(_charset);
   771         }
   772 
   773         *type = _type;
   774         return 0;
   775     }
   776 
   777     return EINVAL;
   778 }
   779