src/etpan_mime.c
author Volker Birk <vb@pep-project.org>
Wed, 29 Apr 2015 14:51:33 +0200
changeset 229 313d152239bf
parent 165 f0894860af4a
child 438 106b9765559d
permissions -rw-r--r--
fixing preprocessor
     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 512
    14 
    15 static char * generate_boundary(void)
    16 {
    17     char id[MAX_MESSAGE_ID];
    18     long value1;
    19     long value2;
    20     long value3;
    21     long value4;
    22  
    23     // no random needed here
    24 
    25     value1 = random();
    26     value2 = random();
    27     value3 = random();
    28     value4 = random();
    29 
    30     snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
    31             value3, value4);
    32     
    33     return strdup(id);
    34 }
    35 
    36 struct mailmime * part_new_empty(
    37         struct mailmime_content * content,
    38         struct mailmime_fields * mime_fields,
    39         int force_single
    40     )
    41 {
    42 	struct mailmime * build_info;
    43 	clist * list = NULL;
    44 	int r;
    45 	int mime_type;
    46     char * attr_name = NULL;
    47     char * attr_value = NULL;
    48     struct mailmime_parameter * param = NULL;
    49     clist * parameters = NULL;
    50     char *boundary = NULL;
    51 
    52 	list = NULL;
    53 
    54 	if (force_single) {
    55 		mime_type = MAILMIME_SINGLE;
    56 	}
    57 	else {
    58 		switch (content->ct_type->tp_type) {
    59 			case MAILMIME_TYPE_DISCRETE_TYPE:
    60                 mime_type = MAILMIME_SINGLE;
    61                 break;
    62 
    63 			case MAILMIME_TYPE_COMPOSITE_TYPE:
    64                 switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
    65                     case MAILMIME_COMPOSITE_TYPE_MULTIPART:
    66                         mime_type = MAILMIME_MULTIPLE;
    67                         break;
    68 
    69                     case MAILMIME_COMPOSITE_TYPE_MESSAGE:
    70                         if (strcasecmp(content->ct_subtype, "rfc822") == 0)
    71                             mime_type = MAILMIME_MESSAGE;
    72                         else
    73                             mime_type = MAILMIME_SINGLE;
    74                         break;
    75 
    76                     default:
    77                         goto enomem;
    78                 }
    79                 break;
    80 
    81 			default:
    82                 goto enomem;
    83 		}
    84 	}
    85 
    86 	if (mime_type == MAILMIME_MULTIPLE) {
    87 		list = clist_new();
    88         assert(list);
    89 		if (list == NULL)
    90 			goto enomem;
    91 
    92 		attr_name = strdup("boundary");
    93         assert(attr_name);
    94         if (attr_name == NULL)
    95             goto enomem;
    96 
    97 		boundary = generate_boundary();
    98         assert(boundary);
    99 		attr_value = boundary;
   100 		if (attr_value == NULL)
   101 			goto enomem;
   102 
   103 		param = mailmime_parameter_new(attr_name, attr_value);
   104         assert(param);
   105 		if (param == NULL)
   106 			goto enomem;
   107         attr_name = NULL;
   108         attr_value = NULL;
   109 
   110 		if (content->ct_parameters == NULL) {
   111 			parameters = clist_new();
   112             assert(parameters);
   113 			if (parameters == NULL)
   114 				goto enomem;
   115 		}
   116 		else {
   117 			parameters = content->ct_parameters;
   118         }
   119 
   120 		r = clist_append(parameters, param);
   121 		if (r)
   122 			goto enomem;
   123         param = NULL;
   124 
   125 		if (content->ct_parameters == NULL)
   126 			content->ct_parameters = parameters;
   127 	}
   128 
   129     build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
   130             NULL, NULL, list, NULL, NULL);
   131 	if (build_info == NULL)
   132 		goto enomem;
   133 
   134 	return build_info;
   135 
   136 enomem:
   137     if (list)
   138         clist_free(list);
   139     free(attr_name);
   140     free(attr_value);
   141     if (content->ct_parameters == NULL)
   142         if (parameters)
   143             clist_free(parameters);
   144 
   145 	return NULL;
   146 }
   147 
   148 struct mailmime * get_pgp_encrypted_part(void)
   149 {
   150 	struct mailmime * mime = NULL;
   151 	struct mailmime_fields * mime_fields = NULL;
   152 	struct mailmime_content * content = NULL;
   153     int r;
   154 
   155 	content = mailmime_content_new_with_str("application/pgp-encrypted");
   156     if (content == NULL)
   157         goto enomem;
   158 
   159     mime_fields = mailmime_fields_new_empty();
   160     if (mime_fields == NULL)
   161         goto enomem;
   162 
   163 	mime = part_new_empty(content, mime_fields, 1);
   164     if (mime == NULL)
   165         goto enomem;
   166     mime_fields = NULL;
   167     content = NULL;
   168 
   169     r = mailmime_set_body_text(mime, "Version: 1\n", 10);
   170     if (r != 0)
   171         goto enomem;
   172 
   173 	return mime;
   174 
   175 enomem:
   176     if (content)
   177         mailmime_content_free(content);
   178     if (mime_fields)
   179         mailmime_fields_free(mime_fields);
   180     if (mime)
   181         mailmime_free(mime);
   182 
   183     return NULL;
   184 }
   185 
   186 struct mailmime * get_text_part(
   187         const char * filename,
   188         const char * mime_type,
   189         const char * text,
   190         size_t length,
   191         int encoding_type
   192     )
   193 {
   194     char * disposition_name = NULL;
   195 	struct mailmime_fields * mime_fields = NULL;
   196 	struct mailmime * mime = NULL;
   197 	struct mailmime_content * content = NULL;
   198 	struct mailmime_parameter * param = NULL;
   199 	struct mailmime_disposition * disposition = NULL;
   200 	struct mailmime_mechanism * encoding = NULL;
   201     int r;
   202     
   203     if (filename != NULL) {
   204         disposition_name = strdup(filename);
   205         if (disposition_name == NULL)
   206             goto enomem;
   207     }
   208 
   209     if (encoding_type) {
   210         encoding = mailmime_mechanism_new(encoding_type, NULL);
   211         if (encoding == NULL)
   212             goto enomem;
   213     }
   214 
   215     disposition =
   216             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   217                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   218     if (disposition == NULL)
   219         goto enomem;
   220     disposition_name = NULL;
   221 
   222     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   223             disposition, NULL);
   224     if (mime_fields == NULL)
   225         goto enomem;
   226     encoding = NULL;
   227     disposition = NULL;
   228 
   229 	content = mailmime_content_new_with_str(mime_type);
   230     if (content == NULL)
   231         goto enomem;
   232     
   233     if (encoding_type != MAILMIME_MECHANISM_7BIT) {
   234         param = mailmime_param_new_with_data("charset", "utf-8");
   235         r = clist_append(content->ct_parameters, param);
   236         if (r != 0)
   237             goto enomem;
   238     }
   239 
   240 	mime = part_new_empty(content, mime_fields, 1);
   241     if (mime == NULL)
   242         goto enomem;
   243     content = NULL;
   244     mime_fields = NULL;
   245 
   246     if (text) {
   247         r = mailmime_set_body_text(mime, (char *) text, length);
   248         if (r != 0)
   249             goto enomem;
   250     }
   251 	
   252 	return mime;
   253 
   254 enomem:
   255     free(disposition_name);
   256     if (mime_fields)
   257         mailmime_fields_free(mime_fields);
   258     if (mime)
   259         mailmime_free(mime);
   260     if (content)
   261         mailmime_content_free(content);
   262     if (param)
   263         mailmime_parameter_free(param);
   264     if (disposition)
   265         mailmime_disposition_free(disposition);
   266     if (encoding)
   267         mailmime_mechanism_free(encoding);
   268 
   269     return NULL;
   270 }
   271 
   272 struct mailmime * get_file_part(
   273         const char * filename,
   274         const char * mime_type,
   275         char * data,
   276         size_t length
   277     )
   278 {
   279     char * disposition_name = NULL;
   280     int encoding_type;
   281     struct mailmime_disposition * disposition = NULL;
   282     struct mailmime_mechanism * encoding = NULL;
   283     struct mailmime_content * content = NULL;
   284     struct mailmime * mime = NULL;
   285     struct mailmime_fields * mime_fields = NULL;
   286     int r;
   287 
   288     if (filename != NULL) {
   289         disposition_name = strdup(filename);
   290         if (disposition_name == NULL)
   291             goto enomem;
   292     }
   293 
   294     disposition =
   295             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   296                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   297     if (disposition == NULL)
   298         goto enomem;
   299     disposition_name = NULL;
   300 
   301     content = mailmime_content_new_with_str(mime_type);
   302     if (content == NULL)
   303         goto enomem;
   304 
   305     encoding_type = MAILMIME_MECHANISM_BASE64;
   306     encoding = mailmime_mechanism_new(encoding_type, NULL);
   307     if (encoding == NULL)
   308         goto enomem;
   309 
   310     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   311             disposition, NULL);
   312     if (mime_fields == NULL)
   313         goto enomem;
   314     encoding = NULL;
   315     disposition = NULL;
   316 
   317     mime = part_new_empty(content, mime_fields, 1);
   318     if (mime == NULL)
   319         goto enomem;
   320     content = NULL;
   321     mime_fields = NULL;
   322 
   323     r = mailmime_set_body_text(mime, data, length);
   324     if (r != 0)
   325         goto enomem;
   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