src/etpan_mime.c
author Volker Birk <vb@pep-project.org>
Sat, 28 Mar 2015 11:39:05 +0100
changeset 151 ece2659f90f6
parent 147 b03a10242e4c
child 152 f97cd2862f49
permissions -rw-r--r--
even nicer
     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 
    12 
    13 time_t mail_mkgmtime(struct tm * tmp);
    14 
    15 #define MAX_MESSAGE_ID 512
    16 
    17 static char * generate_boundary(void)
    18 {
    19     char id[MAX_MESSAGE_ID];
    20     long value1;
    21     long value2;
    22     long value3;
    23     long value4;
    24  
    25     // no random needed here
    26 
    27     value1 = random();
    28     value2 = random();
    29     value3 = random();
    30     value4 = random();
    31 
    32     snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
    33             value3, value4);
    34     
    35     return strdup(id);
    36 }
    37 
    38 struct mailmime * part_new_empty(
    39         struct mailmime_content * content,
    40         struct mailmime_fields * mime_fields,
    41         int force_single
    42     )
    43 {
    44 	struct mailmime * build_info;
    45 	clist * list = NULL;
    46 	int r;
    47 	int mime_type;
    48     char * attr_name = NULL;
    49     char * attr_value = NULL;
    50     struct mailmime_parameter * param = NULL;
    51     clist * parameters = NULL;
    52     char *boundary = NULL;
    53 
    54 	list = NULL;
    55 
    56 	if (force_single) {
    57 		mime_type = MAILMIME_SINGLE;
    58 	}
    59 	else {
    60 		switch (content->ct_type->tp_type) {
    61 			case MAILMIME_TYPE_DISCRETE_TYPE:
    62                 mime_type = MAILMIME_SINGLE;
    63                 break;
    64 
    65 			case MAILMIME_TYPE_COMPOSITE_TYPE:
    66                 switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
    67                     case MAILMIME_COMPOSITE_TYPE_MULTIPART:
    68                         mime_type = MAILMIME_MULTIPLE;
    69                         break;
    70 
    71                     case MAILMIME_COMPOSITE_TYPE_MESSAGE:
    72                         if (strcasecmp(content->ct_subtype, "rfc822") == 0)
    73                             mime_type = MAILMIME_MESSAGE;
    74                         else
    75                             mime_type = MAILMIME_SINGLE;
    76                         break;
    77 
    78                     default:
    79                         goto enomem;
    80                 }
    81                 break;
    82 
    83 			default:
    84                 goto enomem;
    85 		}
    86 	}
    87 
    88 	if (mime_type == MAILMIME_MULTIPLE) {
    89 		list = clist_new();
    90         assert(list);
    91 		if (list == NULL)
    92 			goto enomem;
    93 
    94 		attr_name = strdup("boundary");
    95         assert(attr_name);
    96         if (attr_name == NULL)
    97             goto enomem;
    98 
    99 		boundary = generate_boundary();
   100         assert(boundary);
   101 		attr_value = boundary;
   102 		if (attr_value == NULL)
   103 			goto enomem;
   104 
   105 		param = mailmime_parameter_new(attr_name, attr_value);
   106         assert(param);
   107 		if (param == NULL)
   108 			goto enomem;
   109         attr_name = NULL;
   110         attr_value = NULL;
   111 
   112 		if (content->ct_parameters == NULL) {
   113 			parameters = clist_new();
   114             assert(parameters);
   115 			if (parameters == NULL)
   116 				goto enomem;
   117 		}
   118 		else {
   119 			parameters = content->ct_parameters;
   120         }
   121 
   122 		r = clist_append(parameters, param);
   123 		if (r)
   124 			goto enomem;
   125         param = NULL;
   126 
   127 		if (content->ct_parameters == NULL)
   128 			content->ct_parameters = parameters;
   129 	}
   130 
   131     build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
   132             NULL, NULL, list, NULL, NULL);
   133 	if (build_info == NULL)
   134 		goto enomem;
   135 
   136 	return build_info;
   137 
   138 enomem:
   139     if (list)
   140         clist_free(list);
   141     free(attr_name);
   142     free(attr_value);
   143     if (content->ct_parameters == NULL)
   144         if (parameters)
   145             clist_free(parameters);
   146 
   147 	return NULL;
   148 }
   149 
   150 struct mailmime * get_pgp_encrypted_part(void)
   151 {
   152 	struct mailmime * mime = NULL;
   153 	struct mailmime_fields * mime_fields = NULL;
   154 	struct mailmime_content * content = NULL;
   155     int r;
   156 
   157 	content = mailmime_content_new_with_str("application/pgp-encrypted");
   158     if (content == NULL)
   159         goto enomem;
   160 
   161     mime_fields = mailmime_fields_new_empty();
   162     if (mime_fields == NULL)
   163         goto enomem;
   164 
   165 	mime = part_new_empty(content, mime_fields, 1);
   166     if (mime == NULL)
   167         goto enomem;
   168     mime_fields = NULL;
   169     content = NULL;
   170 
   171     r = mailmime_set_body_text(mime, "Version: 1\n", 10);
   172     if (r != 0)
   173         goto enomem;
   174 
   175 	return mime;
   176 
   177 enomem:
   178     if (content)
   179         mailmime_content_free(content);
   180     if (mime_fields)
   181         mailmime_fields_free(mime_fields);
   182     if (mime)
   183         mailmime_free(mime);
   184 
   185     return NULL;
   186 }
   187 
   188 struct mailmime * get_text_part(
   189         const char * filename,
   190         const char * mime_type,
   191         const char * text,
   192         size_t length,
   193         int encoding_type
   194     )
   195 {
   196     char * disposition_name = NULL;
   197 	struct mailmime_fields * mime_fields = NULL;
   198 	struct mailmime * mime = NULL;
   199 	struct mailmime_content * content = NULL;
   200 	struct mailmime_parameter * param = NULL;
   201 	struct mailmime_disposition * disposition = NULL;
   202 	struct mailmime_mechanism * encoding = NULL;
   203     int r;
   204     
   205     if (filename != NULL) {
   206         disposition_name = strdup(filename);
   207         if (disposition_name == NULL)
   208             goto enomem;
   209     }
   210 
   211     if (encoding_type) {
   212         encoding = mailmime_mechanism_new(encoding_type, NULL);
   213         if (encoding == NULL)
   214             goto enomem;
   215     }
   216 
   217     disposition =
   218             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   219                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   220     if (disposition == NULL)
   221         goto enomem;
   222     disposition_name = NULL;
   223 
   224     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   225             disposition, NULL);
   226     if (mime_fields == NULL)
   227         goto enomem;
   228     encoding = NULL;
   229     disposition = NULL;
   230 
   231 	content = mailmime_content_new_with_str(mime_type);
   232     if (content == NULL)
   233         goto enomem;
   234     
   235     if (encoding_type != MAILMIME_MECHANISM_7BIT) {
   236         param = mailmime_param_new_with_data("charset", "utf-8");
   237         r = clist_append(content->ct_parameters, param);
   238         if (r != 0)
   239             goto enomem;
   240     }
   241 
   242 	mime = part_new_empty(content, mime_fields, 1);
   243     if (mime == NULL)
   244         goto enomem;
   245     content = NULL;
   246     mime_fields = NULL;
   247 
   248     if (text) {
   249         r = mailmime_set_body_text(mime, (char *) text, length);
   250         if (r != 0)
   251             goto enomem;
   252     }
   253 	
   254 	return mime;
   255 
   256 enomem:
   257     free(disposition_name);
   258     if (mime_fields)
   259         mailmime_fields_free(mime_fields);
   260     if (mime)
   261         mailmime_free(mime);
   262     if (content)
   263         mailmime_content_free(content);
   264     if (param)
   265         mailmime_parameter_free(param);
   266     if (disposition)
   267         mailmime_disposition_free(disposition);
   268     if (encoding)
   269         mailmime_mechanism_free(encoding);
   270 
   271     return NULL;
   272 }
   273 
   274 struct mailmime * get_file_part(
   275         const char * filename,
   276         const char * mime_type,
   277         char * data,
   278         size_t length
   279     )
   280 {
   281     char * disposition_name = NULL;
   282     int encoding_type;
   283     struct mailmime_disposition * disposition = NULL;
   284     struct mailmime_mechanism * encoding = NULL;
   285     struct mailmime_content * content = NULL;
   286     struct mailmime * mime = NULL;
   287     struct mailmime_fields * mime_fields = NULL;
   288     int r;
   289 
   290     if (filename != NULL) {
   291         disposition_name = strdup(filename);
   292         if (disposition_name == NULL)
   293             goto enomem;
   294     }
   295 
   296     disposition =
   297             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   298                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   299     if (disposition == NULL)
   300         goto enomem;
   301     disposition_name = NULL;
   302 
   303     content = mailmime_content_new_with_str(mime_type);
   304     if (content == NULL)
   305         goto enomem;
   306 
   307     encoding_type = MAILMIME_MECHANISM_BASE64;
   308     encoding = mailmime_mechanism_new(encoding_type, NULL);
   309     if (encoding == NULL)
   310         goto enomem;
   311 
   312     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   313             disposition, NULL);
   314     if (mime_fields == NULL)
   315         goto enomem;
   316     encoding = NULL;
   317     disposition = NULL;
   318 
   319     mime = part_new_empty(content, mime_fields, 1);
   320     if (mime == NULL)
   321         goto enomem;
   322     content = NULL;
   323     mime_fields = NULL;
   324 
   325     r = mailmime_set_body_text(mime, data, length);
   326     if (r != 0)
   327         goto enomem;
   328 
   329     return mime;
   330 
   331 enomem:
   332     free(disposition_name);
   333     if (disposition)
   334         mailmime_disposition_free(disposition);
   335     if (encoding)
   336         mailmime_mechanism_free(encoding);
   337     if (content)
   338         mailmime_content_free(content);
   339     if (mime_fields)
   340         mailmime_fields_free(mime_fields);
   341     if (mime)
   342         mailmime_free(mime);
   343 
   344     return NULL;
   345 }
   346 
   347 struct mailmime * part_multiple_new(const char *type)
   348 {
   349     struct mailmime_fields *mime_fields = NULL;
   350     struct mailmime_content *content = NULL;
   351     struct mailmime *mp = NULL;
   352     
   353     mime_fields = mailmime_fields_new_empty();
   354     if (mime_fields == NULL)
   355         goto enomem;
   356     
   357     content = mailmime_content_new_with_str(type);
   358     if (content == NULL)
   359         goto enomem;
   360     
   361     mp = part_new_empty(content, mime_fields, 0);
   362     if (mp == NULL)
   363         goto enomem;
   364     
   365     return mp;
   366     
   367 enomem:
   368     if (content)
   369         mailmime_content_free(content);
   370     if (mime_fields)
   371         mailmime_fields_free(mime_fields);
   372 
   373     return NULL;
   374 }
   375 
   376 struct mailimf_field * _new_field(
   377         int type,
   378         _new_func_t new_func,
   379         void *value
   380     )
   381 {
   382     void *data = new_func(value);
   383     assert(data);
   384     if (data == NULL)
   385         return NULL;
   386 
   387     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   388     assert(result);
   389     if (result == NULL) {
   390         free(data);
   391         return NULL;
   392     }
   393 
   394     result->fld_type = type;
   395     result->fld_data.fld_return_path = data;
   396 
   397     return result;
   398 }
   399 
   400 void _free_field(struct mailimf_field *field)
   401 {
   402     if (field)
   403         free(field->fld_data.fld_return_path);
   404     free(field);
   405 }
   406 
   407 int _append_field(
   408         clist *list,
   409         int type,
   410         _new_func_t new_func,
   411         void *value
   412     )
   413 {
   414     int r;
   415     struct mailimf_field * field;
   416 
   417     assert(list);
   418     assert(new_func);
   419     assert(value);
   420 
   421     field = _new_field(type, new_func, value);
   422     if (field == NULL)
   423         return -1;
   424 
   425     r = clist_append(list, field);
   426     if (r)
   427         _free_field(field);
   428 
   429     return r;
   430 }
   431 
   432 // http://media2.giga.de/2014/02/Image-28.jpg
   433 
   434 struct mailimf_date_time * timestamp_to_etpantime(const struct tm *ts)
   435 {
   436     struct mailimf_date_time * result = calloc(1,
   437             sizeof(struct mailimf_date_time));
   438     assert(result);
   439     if (result == NULL)
   440         return NULL;
   441 
   442     assert(ts);
   443 
   444     result->dt_sec = ts->tm_sec;
   445     result->dt_min = ts->tm_min;
   446     result->dt_hour = ts->tm_hour;
   447     result->dt_day = ts->tm_mday;
   448     result->dt_month = ts->tm_mon + 1;
   449     result->dt_year = ts->tm_year + 1900;
   450 #ifndef WIN32
   451     result->dt_zone = (int) (ts->tm_gmtoff / 36L);
   452 #endif
   453     return result;
   454 }
   455 
   456 struct tm * etpantime_to_timestamp(const struct mailimf_date_time *et)
   457 {
   458     struct tm * result = calloc(1, sizeof(struct tm));
   459     assert(result);
   460     if (result == NULL)
   461         return NULL;
   462 
   463     assert(et);
   464 
   465     result->tm_sec = et->dt_sec;
   466     result->tm_min = et->dt_min;
   467     result->tm_hour = et->dt_hour;
   468     result->tm_mday = et->dt_day;
   469     result->tm_mon = et->dt_month - 1;
   470     result->tm_year = et->dt_year - 1900;
   471 #ifndef WIN32
   472     result->tm_gmtoff = 36L * (long) et->dt_zone;
   473 #endif
   474     return result;
   475 }
   476 
   477 struct mailimf_mailbox * mailbox_from_string(
   478         const char *name,
   479         const char *address
   480     )
   481 {
   482     struct mailimf_mailbox *mb = NULL;
   483     char *_name = NULL;
   484     char *_address = NULL;
   485 
   486     assert(address);
   487 
   488     _name = name ? strdup(name) : strdup("");
   489     if (_name == NULL)
   490         goto enomem;
   491 
   492     _address = strdup(address);
   493     if (_address == NULL)
   494         goto enomem;
   495 
   496     mb = mailimf_mailbox_new(_name, _address);
   497     assert(mb);
   498     if (mb == NULL)
   499         goto enomem;
   500 
   501     return mb;
   502 
   503 enomem:
   504     free(_name);
   505     free(_address);
   506 
   507     return NULL;
   508 }
   509 
   510 struct mailimf_field * create_optional_field(
   511         const char *field,
   512         const char *value
   513     )
   514 {
   515     char *_field = NULL;
   516     char *_value = NULL;
   517     struct mailimf_optional_field *optional_field = NULL;
   518 
   519     _field = strdup(field);
   520     if (_field == NULL)
   521         goto enomem;
   522 
   523     _value = mailmime_encode_subject_header("utf-8", value, 0);
   524     if (_value == NULL)
   525         goto enomem;
   526 
   527     optional_field = mailimf_optional_field_new(_field, _value);
   528     if (optional_field == NULL)
   529         goto enomem;
   530 
   531     struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
   532     assert(result);
   533     if (result == NULL)
   534         goto enomem;
   535 
   536     result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
   537     result->fld_data.fld_optional_field = optional_field;
   538 
   539     return result;
   540 
   541 enomem:
   542     if (optional_field) {
   543         mailimf_optional_field_free(optional_field);
   544     }
   545     else {
   546         free(_field);
   547         free(_value);
   548     }
   549 
   550     return NULL;
   551 }
   552 
   553 int _append_optional_field(
   554         clist *list,
   555         const char *field,
   556         const char *value
   557     )
   558 {
   559     int r;
   560     struct mailimf_field * optional_field =
   561             create_optional_field(field, value);
   562 
   563     if (optional_field == NULL)
   564         return -1;
   565 
   566     r = clist_append(list, optional_field);
   567     if (r)
   568         mailimf_field_free(optional_field);
   569 
   570     return r;
   571 }
   572 
   573 static bool parameter_has_value(
   574         clist *list,
   575         const char *name,
   576         const char *value
   577     )
   578 {
   579     clistiter *cur;
   580 
   581     assert(name);
   582     assert(value);
   583 
   584     if (list == NULL)
   585         return false;
   586 
   587     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   588         struct mailmime_parameter * param = clist_content(cur);
   589         if (param &&
   590                 param->pa_name && strcmp(name, param->pa_name) == 0 &&
   591                 param->pa_value && strcmp(value, param->pa_value) == 0)
   592             return true;
   593     }
   594 
   595     return false;
   596 }
   597 
   598 clist * _get_fields(struct mailmime * mime)
   599 {
   600     clist * _fieldlist = NULL;
   601 
   602     assert(mime);
   603 
   604     if (mime->mm_data.mm_message.mm_fields &&
   605             mime->mm_data.mm_message.mm_fields->fld_list) {
   606         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   607     }
   608 
   609     return _fieldlist;
   610 }
   611 
   612 struct mailmime_content * _get_content(struct mailmime * mime)
   613 {
   614     struct mailmime_content * content = NULL;
   615 
   616     assert(mime);
   617 
   618     if (mime->mm_data.mm_message.mm_msg_mime)
   619         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   620 
   621     return content;
   622 }
   623 
   624 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   625 {
   626     assert(content);
   627 
   628     if (content->ct_type && content->ct_type->tp_type ==
   629             MAILMIME_TYPE_COMPOSITE_TYPE &&
   630             content->ct_type->tp_data.tp_composite_type &&
   631             content->ct_type->tp_data.tp_composite_type->ct_type ==
   632             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   633         if (subtype)
   634             return content->ct_subtype &&
   635                     strcmp(content->ct_subtype, subtype) == 0;
   636         else
   637             return true;
   638     }
   639 
   640     return false;
   641 }
   642 
   643 bool _is_PGP_MIME(struct mailmime_content *content)
   644 {
   645     assert(content);
   646 
   647     if (_is_multipart(content, "encrypted") &&
   648             parameter_has_value(content->ct_parameters, "protocol",
   649                     "application/pgp-encrypted"))
   650         return true;
   651 
   652     return false;
   653 }
   654