src/etpan_mime.c
author Krista Bennett <krista@pep-project.org>
Wed, 15 Mar 2017 18:02:46 +0100
changeset 1674 5639f969866a
parent 1513 e7f7e42385b5
child 1829 909cb53fc069
permissions -rw-r--r--
Added pgp_encrypt_only stubs for NetPGP
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include "etpan_mime.h"
     5 #ifndef mailmime_param_new_with_data
     6 #include <libetpan/mailprivacy_tools.h>
     7 #endif
     8 
     9 #include "platform.h"
    10 
    11 #include <string.h>
    12 #include <stdlib.h>
    13 #include <assert.h>
    14 #include <errno.h>
    15 
    16 #define MAX_MESSAGE_ID 128
    17 
    18 static char * generate_boundary(void)
    19 {
    20     char id[MAX_MESSAGE_ID];
    21 
    22     // no cryptographically strong random needed here
    23     const long value1 = random();
    24     const long value2 = random();
    25     const long value3 = random();
    26     const long value4 = random();
    27 
    28     snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
    29             value3, value4);
    30     
    31     return strdup(id);
    32 }
    33 
    34 struct mailmime * part_new_empty(
    35         struct mailmime_content * content,
    36         struct mailmime_fields * mime_fields,
    37         int force_single
    38     )
    39 {
    40 	struct mailmime * build_info;
    41 	clist * list = NULL;
    42 	int r;
    43 	int mime_type;
    44     char * attr_name = NULL;
    45     char * attr_value = NULL;
    46     struct mailmime_parameter * param = NULL;
    47     clist * parameters = NULL;
    48     char *boundary = NULL;
    49 
    50 	list = NULL;
    51 
    52 	if (force_single) {
    53 		mime_type = MAILMIME_SINGLE;
    54 	}
    55 	else {
    56 		switch (content->ct_type->tp_type) {
    57 			case MAILMIME_TYPE_DISCRETE_TYPE:
    58                 mime_type = MAILMIME_SINGLE;
    59                 break;
    60 
    61 			case MAILMIME_TYPE_COMPOSITE_TYPE:
    62                 switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
    63                     case MAILMIME_COMPOSITE_TYPE_MULTIPART:
    64                         mime_type = MAILMIME_MULTIPLE;
    65                         break;
    66 
    67                     case MAILMIME_COMPOSITE_TYPE_MESSAGE:
    68                         if (strcasecmp(content->ct_subtype, "rfc822") == 0)
    69                             mime_type = MAILMIME_MESSAGE;
    70                         else
    71                             mime_type = MAILMIME_SINGLE;
    72                         break;
    73 
    74                     default:
    75                         goto enomem;
    76                 }
    77                 break;
    78 
    79 			default:
    80                 goto enomem;
    81 		}
    82 	}
    83 
    84 	if (mime_type == MAILMIME_MULTIPLE) {
    85 		list = clist_new();
    86         assert(list);
    87 		if (list == NULL)
    88 			goto enomem;
    89 
    90 		attr_name = strdup("boundary");
    91         assert(attr_name);
    92         if (attr_name == NULL)
    93             goto enomem;
    94 
    95 		boundary = generate_boundary();
    96         assert(boundary);
    97 		attr_value = boundary;
    98 		if (attr_value == NULL)
    99 			goto enomem;
   100 
   101 		param = mailmime_parameter_new(attr_name, attr_value);
   102         assert(param);
   103 		if (param == NULL)
   104 			goto enomem;
   105         attr_name = NULL;
   106         attr_value = NULL;
   107 
   108 		if (content->ct_parameters == NULL) {
   109 			parameters = clist_new();
   110             assert(parameters);
   111 			if (parameters == NULL)
   112 				goto enomem;
   113 		}
   114 		else {
   115 			parameters = content->ct_parameters;
   116         }
   117 
   118 		r = clist_append(parameters, param);
   119 		if (r)
   120 			goto enomem;
   121         param = NULL;
   122 
   123 		if (content->ct_parameters == NULL)
   124 			content->ct_parameters = parameters;
   125 	}
   126 
   127     build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
   128             NULL, NULL, list, NULL, NULL);
   129 	if (build_info == NULL)
   130 		goto enomem;
   131 
   132 	return build_info;
   133 
   134 enomem:
   135     if (list)
   136         clist_free(list);
   137     free(attr_name);
   138     free(attr_value);
   139     if (content->ct_parameters == NULL)
   140         if (parameters)
   141             clist_free(parameters);
   142     if (param)
   143         mailmime_parameter_free(param);
   144 	return NULL;
   145 }
   146 
   147 struct mailmime * get_pgp_encrypted_part(void)
   148 {
   149 	struct mailmime * mime = NULL;
   150 	struct mailmime_fields * mime_fields = NULL;
   151 	struct mailmime_content * content = NULL;
   152     int r;
   153 
   154 	content = mailmime_content_new_with_str("application/pgp-encrypted");
   155     if (content == NULL)
   156         goto enomem;
   157 
   158     mime_fields = mailmime_fields_new_empty();
   159     if (mime_fields == NULL)
   160         goto enomem;
   161 
   162 	mime = part_new_empty(content, mime_fields, 1);
   163     if (mime == NULL)
   164         goto enomem;
   165     mime_fields = NULL;
   166     content = NULL;
   167 
   168     r = mailmime_set_body_text(mime, "Version: 1\n", 10);
   169     if (r != 0)
   170         goto enomem;
   171 
   172 	return mime;
   173 
   174 enomem:
   175     if (content)
   176         mailmime_content_free(content);
   177     if (mime_fields)
   178         mailmime_fields_free(mime_fields);
   179     if (mime)
   180         mailmime_free(mime);
   181 
   182     return NULL;
   183 }
   184 
   185 struct mailmime * get_text_part(
   186         const char * filename,
   187         const char * mime_type,
   188         const char * text,
   189         size_t length,
   190         int encoding_type
   191     )
   192 {
   193     char * disposition_name = NULL;
   194 	struct mailmime_fields * mime_fields = NULL;
   195 	struct mailmime * mime = NULL;
   196 	struct mailmime_content * content = NULL;
   197 	struct mailmime_parameter * param = NULL;
   198 	struct mailmime_disposition * disposition = NULL;
   199 	struct mailmime_mechanism * encoding = NULL;
   200     int r;
   201     
   202     if (filename != NULL) {
   203         disposition_name = strdup(filename);
   204         if (disposition_name == NULL)
   205             goto enomem;
   206     }
   207 
   208     if (encoding_type) {
   209         encoding = mailmime_mechanism_new(encoding_type, NULL);
   210         if (encoding == NULL)
   211             goto enomem;
   212     }
   213 
   214     disposition =
   215             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
   216                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   217     if (disposition == NULL)
   218         goto enomem;
   219     disposition_name = NULL;
   220 
   221     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   222             disposition, NULL);
   223     if (mime_fields == NULL)
   224         goto enomem;
   225     encoding = NULL;
   226     disposition = NULL;
   227 
   228 	content = mailmime_content_new_with_str(mime_type);
   229     if (content == NULL)
   230         goto enomem;
   231     
   232     if (encoding_type != MAILMIME_MECHANISM_7BIT) {
   233         param = mailmime_param_new_with_data("charset", "utf-8");
   234         r = clist_append(content->ct_parameters, param);
   235         if (r != 0)
   236             goto enomem;
   237     }
   238 
   239 	mime = part_new_empty(content, mime_fields, 1);
   240     if (mime == NULL)
   241         goto enomem;
   242     content = NULL;
   243     mime_fields = NULL;
   244 
   245     if (text) {
   246         r = mailmime_set_body_text(mime, (char *) text, length);
   247         if (r != 0)
   248             goto enomem;
   249     }
   250 	
   251 	return mime;
   252 
   253 enomem:
   254     free(disposition_name);
   255     if (mime_fields)
   256         mailmime_fields_free(mime_fields);
   257     if (mime)
   258         mailmime_free(mime);
   259     if (content)
   260         mailmime_content_free(content);
   261     if (param)
   262         mailmime_parameter_free(param);
   263     if (disposition)
   264         mailmime_disposition_free(disposition);
   265     if (encoding)
   266         mailmime_mechanism_free(encoding);
   267 
   268     return NULL;
   269 }
   270 
   271 struct mailmime * get_file_part(
   272         const char * filename,
   273         const char * mime_type,
   274         char * data,
   275         size_t length
   276     )
   277 {
   278     char * disposition_name = NULL;
   279     int encoding_type;
   280     struct mailmime_disposition * disposition = NULL;
   281     struct mailmime_mechanism * encoding = NULL;
   282     struct mailmime_content * content = NULL;
   283     struct mailmime * mime = NULL;
   284     struct mailmime_fields * mime_fields = NULL;
   285     int r;
   286 
   287     if (filename != NULL) {
   288         disposition_name = strdup(filename);
   289         if (disposition_name == NULL)
   290             goto enomem;
   291     }
   292 
   293     disposition =
   294             mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
   295                     disposition_name, NULL, NULL, NULL, (size_t) -1);
   296     if (disposition == NULL)
   297         goto enomem;
   298     disposition_name = NULL;
   299 
   300     content = mailmime_content_new_with_str(mime_type);
   301     if (content == NULL)
   302         goto enomem;
   303 
   304     encoding_type = MAILMIME_MECHANISM_BASE64;
   305     encoding = mailmime_mechanism_new(encoding_type, NULL);
   306     if (encoding == NULL)
   307         goto enomem;
   308 
   309     mime_fields = mailmime_fields_new_with_data(encoding, NULL, NULL,
   310             disposition, NULL);
   311     if (mime_fields == NULL)
   312         goto enomem;
   313     encoding = NULL;
   314     disposition = NULL;
   315 
   316     mime = part_new_empty(content, mime_fields, 1);
   317     if (mime == NULL)
   318         goto enomem;
   319     content = NULL;
   320     mime_fields = NULL;
   321 
   322     if(length > 0)
   323     {
   324         r = mailmime_set_body_text(mime, data, length);
   325         if (r != 0)
   326             goto enomem;
   327     }
   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 clist * _get_fields(struct mailmime * mime)
   574 {
   575     clist * _fieldlist = NULL;
   576 
   577     assert(mime);
   578 
   579     if (mime->mm_data.mm_message.mm_fields &&
   580             mime->mm_data.mm_message.mm_fields->fld_list) {
   581         _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
   582     }
   583 
   584     return _fieldlist;
   585 }
   586 
   587 struct mailmime_content * _get_content(struct mailmime * mime)
   588 {
   589     struct mailmime_content * content = NULL;
   590 
   591     assert(mime);
   592 
   593     if (mime->mm_data.mm_message.mm_msg_mime)
   594         content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
   595 
   596     return content;
   597 }
   598 
   599 char * _get_filename(struct mailmime *mime)
   600 {
   601     clist * _fieldlist = NULL;
   602 
   603     assert(mime);
   604 
   605     if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
   606         _fieldlist = mime->mm_mime_fields->fld_list;
   607     else
   608         return NULL;
   609 
   610     clistiter *cur;
   611     for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
   612         struct mailmime_field * _field = clist_content(cur);
   613         if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
   614             if (_field->fld_data.fld_disposition &&
   615                     _field->fld_data.fld_disposition->dsp_parms) {
   616                 clist * _parmlist =
   617                         _field->fld_data.fld_disposition->dsp_parms;
   618                 clistiter *cur2;
   619                 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
   620                         clist_next(cur2)) {
   621                     struct mailmime_disposition_parm * param =
   622                             clist_content(cur2);
   623                     if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME)
   624                         return param->pa_data.pa_filename;
   625                 }
   626             }
   627         }
   628     }
   629 
   630     return NULL;
   631 }
   632 
   633 static bool parameter_has_value(
   634         struct mailmime_content *content,       
   635         const char *name,
   636         const char *value
   637     )
   638 {
   639     clistiter *cur;
   640 
   641     assert(name);
   642     assert(value);
   643 
   644     clist * list = content->ct_parameters;
   645     if (list == NULL)
   646         return false;
   647 
   648     for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
   649         struct mailmime_parameter * param = clist_content(cur);
   650         if (param &&
   651                 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
   652                 param->pa_value && strcasecmp(value, param->pa_value) == 0)
   653             return true;
   654     }
   655 
   656     return false;
   657 }
   658 
   659 bool _is_multipart(struct mailmime_content *content, const char *subtype)
   660 {
   661     assert(content);
   662 
   663     if (content->ct_type && content->ct_type->tp_type ==
   664             MAILMIME_TYPE_COMPOSITE_TYPE &&
   665             content->ct_type->tp_data.tp_composite_type &&
   666             content->ct_type->tp_data.tp_composite_type->ct_type ==
   667             MAILMIME_COMPOSITE_TYPE_MULTIPART) {
   668         if (subtype)
   669             return content->ct_subtype &&
   670                     strcasecmp(content->ct_subtype, subtype) == 0;
   671         else
   672             return true;
   673     }
   674 
   675     return false;
   676 }
   677 
   678 bool _is_PGP_MIME(struct mailmime_content *content)
   679 {
   680     assert(content);
   681 
   682     if (_is_multipart(content, "encrypted") &&
   683             parameter_has_value(content, "protocol",
   684                     "application/pgp-encrypted"))
   685         return true;
   686 
   687     return false;
   688 }
   689 
   690 bool _is_text_part(struct mailmime_content *content, const char *subtype)
   691 {
   692     assert(content);
   693 
   694     if (content->ct_type && content->ct_type->tp_type ==
   695             MAILMIME_TYPE_DISCRETE_TYPE &&
   696             content->ct_type->tp_data.tp_discrete_type &&
   697             content->ct_type->tp_data.tp_discrete_type->dt_type ==
   698             MAILMIME_DISCRETE_TYPE_TEXT) {
   699         if (subtype)
   700             return content->ct_subtype &&
   701                     strcasecmp(content->ct_subtype, subtype) == 0;
   702         else
   703             return true;
   704     }
   705 
   706     return false;
   707 }
   708 
   709 int _get_content_type(
   710         const struct mailmime_content *content,
   711         char **type,
   712         char **charset
   713     )
   714 {
   715     char *_type = NULL;
   716     char *_charset = NULL;
   717 
   718     assert(content);
   719     assert(type);
   720     assert(charset);
   721 
   722     *type = NULL;
   723     *charset = NULL;
   724 
   725     if (content->ct_subtype == NULL)
   726         return EINVAL;
   727 
   728     if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
   729         size_t len;
   730         const char *_main_type;
   731 
   732         switch  (content->ct_type->tp_data.tp_discrete_type->dt_type) {
   733             case MAILMIME_DISCRETE_TYPE_TEXT:
   734                 _main_type = "text";
   735                 break;
   736             case MAILMIME_DISCRETE_TYPE_IMAGE:
   737                 _main_type = "image";
   738                 break;
   739             case MAILMIME_DISCRETE_TYPE_AUDIO:
   740                 _main_type = "audio";
   741                 break;
   742             case MAILMIME_DISCRETE_TYPE_VIDEO:
   743                 _main_type = "video";
   744                 break;
   745             case MAILMIME_DISCRETE_TYPE_APPLICATION:
   746                 _main_type = "application";
   747                 break;
   748             case MAILMIME_DISCRETE_TYPE_EXTENSION:
   749                 _main_type = "extension";
   750                 break;
   751             default:
   752                 return EINVAL;
   753         }
   754 
   755         len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
   756         _type = calloc(1, len);
   757         assert(_type);
   758         if (_type == NULL)
   759             return ENOMEM;
   760 
   761         strncpy(_type, _main_type, len);
   762         len -= strlen(_main_type);
   763         strncat(_type, "/", len--);
   764         strncat(_type, content->ct_subtype, len);
   765 
   766         if (content->ct_parameters) {
   767             clistiter *cur;
   768             for (cur = clist_begin(content->ct_parameters); cur; cur =
   769                     clist_next(cur)) {
   770                 struct mailmime_parameter * param = clist_content(cur);
   771                 if (param && param->pa_name && strcasecmp(param->pa_name,
   772                             "charset") == 0) {
   773                     _charset = param->pa_value;
   774                     break;
   775                 }
   776             }
   777             if (_charset)
   778                 *charset = strdup(_charset);
   779         }
   780 
   781         *type = _type;
   782         return 0;
   783     }
   784 
   785     return EINVAL;
   786 }
   787