src/mime.c
author vb
Sun, 08 Mar 2015 12:18:20 +0100
changeset 93 6f3f781caaa0
parent 91 00c67be2d56d
child 94 9ec29aa10b58
permissions -rw-r--r--
correcting address lists
     1 #include "mime.h"
     2 
     3 #include <libetpan/mailmime.h>
     4 #include <string.h>
     5 #include <stdlib.h>
     6 #include <assert.h>
     7 #include <errno.h>
     8 #include <unistd.h>
     9 
    10 #include "etpan_mime.h"
    11 #include "wrappers.h"
    12 
    13 static PEP_STATUS render_mime(struct mailmime *mime, char **mimetext)
    14 {
    15     PEP_STATUS status = PEP_STATUS_OK;
    16     int fd;
    17     FILE *file = NULL;
    18     size_t size;
    19     char *buf = NULL;
    20     int col;
    21     int r;
    22     char *template = strdup("/tmp/pEp.XXXXXXXXXXXXXXXXXXXX");
    23     assert(template);
    24     if (template == NULL)
    25         goto enomem;
    26 
    27     *mimetext = NULL;
    28 
    29     fd = Mkstemp(template);
    30     assert(fd != -1);
    31     if (fd == -1)
    32         goto err_file;
    33 
    34     r = unlink(template);
    35     assert(r == 0);
    36     if (r)
    37         goto err_file;
    38 
    39     free(template);
    40     template = NULL;
    41 
    42     file = Fdopen(fd, "w+");
    43     assert(file);
    44     if (file == NULL) {
    45         switch (errno) {
    46             case ENOMEM:
    47                 goto enomem;
    48             default:
    49                 goto err_file;
    50         }
    51     }
    52 
    53     fd = -1;
    54 
    55     col = 0;
    56     r = mailmime_write_file(file, &col, mime);
    57     assert(r == MAILIMF_NO_ERROR);
    58     if (r == MAILIMF_ERROR_MEMORY)
    59         goto enomem;
    60     else if (r != MAILIMF_NO_ERROR)
    61         goto err_file;
    62 
    63     off_t len = ftello(file);
    64     assert(len != -1);
    65     if (len == -1 && errno == EOVERFLOW)
    66         goto err_file;
    67 
    68     if (len + 1 > SIZE_MAX)
    69         goto err_buffer;
    70 
    71     size = (size_t) len;
    72 
    73     errno = 0;
    74     rewind(file);
    75     assert(errno == 0);
    76     switch (errno) {
    77         case 0:
    78             break;
    79         case ENOMEM:
    80             goto enomem;
    81         default:
    82             goto err_file;
    83     }
    84 
    85     buf = calloc(1, size + 1);
    86     assert(buf);
    87     if (buf == NULL)
    88         goto enomem;
    89  
    90     size_t _read;
    91     _read = Fread(buf, size, 1, file);
    92     assert(_read == size);
    93 
    94     r = Fclose(file);
    95     assert(r == 0);
    96 
    97     *mimetext = buf;
    98     return PEP_STATUS_OK;
    99 
   100 err_buffer:
   101     status = PEP_BUFFER_TOO_SMALL;
   102     goto pep_error;
   103 
   104 err_file:
   105     status = PEP_CANNOT_CREATE_TEMP_FILE;
   106     goto pep_error;
   107 
   108 enomem:
   109     status = PEP_OUT_OF_MEMORY;
   110 
   111 pep_error:
   112     free(buf);
   113     free(template);
   114 
   115     if (file) {
   116         r = Fclose(file);
   117         assert(r == 0);
   118     }
   119     else if (fd != -1) {
   120         r = Close(fd);
   121         assert(r == 0);
   122     }
   123 
   124     return status;
   125 }
   126 
   127 static PEP_STATUS mime_html_text(
   128         const char *plaintext,
   129         const char *htmltext,
   130         struct mailmime **result
   131     )
   132 {
   133     PEP_STATUS status = PEP_STATUS_OK;
   134     struct mailmime * mime = NULL;
   135     struct mailmime * submime = NULL;
   136     int r;
   137 
   138     assert(plaintext);
   139     assert(htmltext);
   140     assert(result);
   141 
   142     *result = NULL;
   143 
   144     mime = part_multiple_new("multipart/alternative", NULL);
   145     assert(mime);
   146     if (mime == NULL)
   147         goto enomem;
   148 
   149     submime = get_text_part("text/plain", plaintext, strlen(plaintext),
   150             MAILMIME_MECHANISM_QUOTED_PRINTABLE);
   151     assert(submime);
   152     if (submime == NULL)
   153         goto enomem;
   154 
   155     r = mailmime_smart_add_part(mime, submime);
   156     assert(r == MAILIMF_NO_ERROR);
   157     if (r == MAILIMF_ERROR_MEMORY) {
   158         goto enomem;
   159     }
   160     else {
   161         // mailmime_smart_add_part() takes ownership of submime
   162         submime = NULL;
   163     }
   164 
   165     submime = get_text_part("text/html", htmltext, strlen(htmltext),
   166             MAILMIME_MECHANISM_QUOTED_PRINTABLE);
   167     assert(submime);
   168     if (submime == NULL)
   169         goto enomem;
   170 
   171     r = mailmime_smart_add_part(mime, submime);
   172     assert(r == MAILIMF_NO_ERROR);
   173     if (r == MAILIMF_ERROR_MEMORY)
   174         goto enomem;
   175     else {
   176         // mailmime_smart_add_part() takes ownership of submime
   177         submime = NULL;
   178     }
   179 
   180     *result = mime;
   181     return PEP_STATUS_OK;
   182 
   183 enomem:
   184     status = PEP_OUT_OF_MEMORY;
   185 
   186 pep_error:
   187     if (mime)
   188         mailmime_free(mime);
   189 
   190     if (submime)
   191         mailmime_free(submime);
   192 
   193     return status;
   194 }
   195 
   196 static PEP_STATUS mime_attachment(
   197         bloblist_t *blob,
   198         struct mailmime **result
   199     )
   200 {
   201     PEP_STATUS status = PEP_STATUS_OK;
   202     struct mailmime * mime = NULL;
   203     char * mime_type;
   204 
   205     assert(blob);
   206     assert(result);
   207 
   208     *result = NULL;
   209 
   210     if (blob->mime_type == NULL)
   211         mime_type = "application/octet-stream";
   212     else
   213         mime_type = blob->mime_type;
   214 
   215     mime = get_file_part(blob->file_name, mime_type, blob->data, blob->size);
   216     assert(mime);
   217     if (mime == NULL)
   218         goto enomem;
   219 
   220     *result = mime;
   221     return PEP_STATUS_OK;
   222 
   223 enomem:
   224     status = PEP_OUT_OF_MEMORY;
   225 
   226 pep_error:
   227     if (mime)
   228         mailmime_free(mime);
   229 
   230     return status;
   231 }
   232 
   233 static struct mailimf_mailbox_list * mbl_from_identity(const pEp_identity *ident)
   234 {
   235     struct mailimf_mailbox_list *mbl = NULL;
   236     struct mailimf_mailbox *mb = NULL;
   237     clist *list = NULL;
   238     int r;
   239 
   240     assert(ident);
   241 
   242     list = clist_new();
   243     if (list == NULL)
   244         goto enomem;
   245 
   246     mb = mailbox_from_string(ident->username, ident->address);
   247     if (mb == NULL)
   248         goto enomem;
   249 
   250     r = clist_append(list, mb);
   251     if (r)
   252         goto enomem;
   253 
   254     mbl = mailimf_mailbox_list_new(list);
   255     if (mbl == NULL)
   256         goto enomem;
   257 
   258     return mbl;
   259 
   260 enomem:
   261     if (mb)
   262         mailimf_mailbox_free(mb);
   263 
   264     if (list)
   265         clist_free(list);
   266 
   267     return NULL;
   268 }
   269 
   270 static struct mailimf_address_list * mal_from_identity_list(identity_list *il)
   271 {
   272     struct mailimf_address_list *mal = NULL;
   273     struct mailimf_mailbox *mb = NULL;
   274     struct mailimf_address * addr = NULL;
   275     clist *list = NULL;
   276     int r;
   277 
   278     assert(il);
   279 
   280     list = clist_new();
   281     if (list == NULL)
   282         goto enomem;
   283 
   284     identity_list *_il;
   285     for (_il = il; _il; _il = _il->next) {
   286         mb = mailbox_from_string(_il->ident->username, _il->ident->address);
   287         if (mb == NULL)
   288             goto enomem;
   289 
   290         addr = mailimf_address_new(MAILIMF_ADDRESS_MAILBOX, mb, NULL);
   291         if (addr == NULL)
   292             goto enomem;
   293         mb = NULL;
   294 
   295         r = clist_append(list, addr);
   296         if (r)
   297             goto enomem;
   298         addr = NULL;
   299     }
   300     mal = mailimf_address_list_new(list);
   301     if (mal == NULL)
   302         goto enomem;
   303 
   304     return mal;
   305 
   306 enomem:
   307     if (mb)
   308         mailimf_mailbox_free(mb);
   309 
   310     if (addr)
   311         mailimf_address_free(addr);
   312 
   313     if (list)
   314         clist_free(list);
   315 
   316     return NULL;
   317 }
   318 
   319 static clist * clist_from_stringlist(stringlist_t *sl)
   320 {
   321     clist * cl = clist_new();
   322     assert(cl);
   323     if (cl == NULL)
   324         return NULL;
   325 
   326     stringlist_t *_sl;
   327     for (_sl = sl; _sl; _sl = _sl->next) {
   328         int r;
   329         char * value = strdup(_sl->value);
   330         assert(value);
   331         if (value == NULL) {
   332             clist_free(cl);
   333             return NULL;
   334         }
   335         r = clist_append(cl, value);
   336         assert(r == 0);
   337         if (r) {
   338             free(value);
   339             clist_free(cl);
   340             return NULL;
   341         }
   342     }
   343 
   344     return cl;
   345 }
   346 
   347 static PEP_STATUS build_fields(const message *msg, struct mailimf_fields **result)
   348 {
   349     PEP_STATUS status = PEP_STATUS_OK;
   350     struct mailimf_fields * fields = NULL;
   351     int r;
   352     clist * fields_list = NULL;
   353     char *subject = msg->shortmsg ? msg->shortmsg : "pEp";
   354 
   355     assert(msg);
   356     assert(msg->from);
   357     assert(msg->from->address);
   358     assert(result);
   359 
   360     *result = NULL;
   361 
   362     fields_list = clist_new();
   363     assert(fields_list);
   364     if (fields_list == NULL)
   365         goto enomem;
   366 
   367     if (msg->id) {
   368         char *_msgid = strdup(msg->id);
   369         if (_msgid == NULL)
   370             goto enomem;
   371 
   372         r = _append_field(fields_list, MAILIMF_FIELD_MESSAGE_ID,
   373                 (_new_func_t) mailimf_message_id_new, _msgid);
   374         if (r) {
   375             free(_msgid);
   376             goto enomem;
   377         }
   378     }
   379 
   380     /* if (subject) */ {
   381         char *_subject = strdup(subject);
   382         if (_subject == NULL)
   383             goto enomem;
   384 
   385         r = _append_field(fields_list, MAILIMF_FIELD_SUBJECT,
   386                 (_new_func_t) mailimf_subject_new, _subject);
   387         if (r) {
   388             free(_subject);
   389             goto enomem;
   390         }
   391     }
   392 
   393     if (msg->sent) {
   394         struct mailimf_date_time * dt = timestamp_to_etpantime(msg->sent);
   395         if (dt == NULL)
   396             goto enomem;
   397 
   398         r = _append_field(fields_list, MAILIMF_FIELD_ORIG_DATE,
   399                 (_new_func_t) mailimf_orig_date_new, dt);
   400         if (r) {
   401             mailimf_date_time_free(dt);
   402             goto enomem;
   403         }
   404         dt = NULL;
   405     }
   406 
   407     /* if (msg->from) */ {
   408         struct mailimf_mailbox_list *from = mbl_from_identity(msg->from);
   409         if (from == NULL)
   410             goto enomem;
   411 
   412         r = _append_field(fields_list, MAILIMF_FIELD_FROM,
   413                 (_new_func_t) mailimf_from_new, from);
   414         if (r) {
   415             mailimf_mailbox_list_free(from);
   416             goto enomem;
   417         }
   418     }
   419 
   420     if (msg->to) {
   421         struct mailimf_address_list *to = mal_from_identity_list(msg->to);
   422         if (to == NULL)
   423             goto enomem;
   424 
   425         r = _append_field(fields_list, MAILIMF_FIELD_TO,
   426                 (_new_func_t) mailimf_to_new, to);
   427         if (r) {
   428             mailimf_address_list_free(to);
   429             goto enomem;
   430         }
   431     }
   432 
   433     if (msg->cc) {
   434         struct mailimf_address_list *cc = mal_from_identity_list(msg->cc);
   435         if (cc == NULL)
   436             goto enomem;
   437 
   438         r = _append_field(fields_list, MAILIMF_FIELD_CC,
   439                 (_new_func_t) mailimf_cc_new, cc);
   440         if (r) {
   441             mailimf_address_list_free(cc);
   442             goto enomem;
   443         }
   444     }
   445     
   446     if (msg->bcc) {
   447         struct mailimf_address_list *bcc = mal_from_identity_list(msg->bcc);
   448         if (bcc == NULL)
   449             goto enomem;
   450 
   451         r = _append_field(fields_list, MAILIMF_FIELD_BCC,
   452                 (_new_func_t) mailimf_bcc_new, bcc);
   453         if (r) {
   454             mailimf_address_list_free(bcc);
   455             goto enomem;
   456         }
   457     }
   458     
   459     if (msg->reply_to) {
   460         struct mailimf_mailbox_list *reply_to= mbl_from_identity(msg->reply_to);
   461         if (reply_to == NULL)
   462             goto enomem;
   463 
   464         r = _append_field(fields_list, MAILIMF_FIELD_REPLY_TO,
   465                 (_new_func_t) mailimf_reply_to_new, reply_to);
   466         if (r) {
   467             mailimf_mailbox_list_free(reply_to);
   468             goto enomem;
   469         }
   470     }
   471 
   472     if (msg->in_reply_to) {
   473         char *in_reply_to = strdup(msg->in_reply_to);
   474         if (in_reply_to == NULL)
   475             goto enomem;
   476 
   477         r = _append_field(fields_list, MAILIMF_FIELD_IN_REPLY_TO,
   478                 (_new_func_t) mailimf_in_reply_to_new, in_reply_to);
   479         if (r) {
   480             free(in_reply_to);
   481             goto enomem;
   482         }
   483     }
   484 
   485     if (msg->references) {
   486         clist *references = clist_from_stringlist(msg->references);
   487         if (references == NULL)
   488             goto enomem;
   489 
   490         r = _append_field(fields_list, MAILIMF_FIELD_REFERENCES,
   491                 (_new_func_t) mailimf_references_new, references);
   492         if (r) {
   493             clist_free(references);
   494             goto enomem;
   495         }
   496     }
   497 
   498     if (msg->keywords) {
   499         clist *keywords = clist_from_stringlist(msg->keywords);
   500         if (keywords == NULL)
   501             goto enomem;
   502 
   503         r = _append_field(fields_list, MAILIMF_FIELD_KEYWORDS,
   504                 (_new_func_t) mailimf_keywords_new, keywords);
   505         if (r) {
   506             clist_free(keywords);
   507             goto enomem;
   508         }
   509     }
   510 
   511     if (msg->comments) {
   512         char *comments = strdup(msg->comments);
   513         if (comments == NULL)
   514             goto enomem;
   515 
   516         r = _append_field(fields_list, MAILIMF_FIELD_COMMENTS,
   517                 (_new_func_t) mailimf_comments_new, comments);
   518         if (r) {
   519             free(comments);
   520             goto enomem;
   521         }
   522     }
   523 
   524     fields = mailimf_fields_new(fields_list);
   525     assert(fields);
   526     if (fields == NULL)
   527         goto enomem;
   528 
   529     *result = fields;
   530 
   531     return PEP_STATUS_OK;
   532 
   533 enomem:
   534     status = PEP_OUT_OF_MEMORY;
   535 
   536 pep_error:
   537     if (fields_list)
   538         clist_free(fields_list);
   539 
   540     if (fields)
   541         mailimf_fields_free(fields);
   542 
   543     return status;
   544 }
   545 
   546 DYNAMIC_API PEP_STATUS mime_encode_message(
   547         const message *msg,
   548         char **mimetext
   549     )
   550 {
   551     struct mailmime * msg_mime = NULL;
   552     struct mailmime * mime = NULL;
   553     struct mailmime * submime = NULL;
   554     struct mailimf_fields * fields = NULL;
   555     char *buf = NULL;
   556     int r;
   557     PEP_STATUS status;
   558     char *subject;
   559     char *plaintext;
   560     char *htmltext;
   561 
   562     assert(msg);
   563     assert(mimetext);
   564 
   565     *mimetext = NULL;
   566 
   567     subject = (msg->shortmsg) ? msg->shortmsg : "pEp";
   568     plaintext = (msg->longmsg) ? msg->longmsg : "";
   569     htmltext = msg->longmsg_formatted;
   570 
   571     if (htmltext) {
   572         status = mime_html_text(plaintext, htmltext, &mime);
   573         if (status != PEP_STATUS_OK)
   574             goto pep_error;
   575     }
   576     else {
   577         mime = get_text_part("text/plain", plaintext, strlen(plaintext),
   578                 MAILMIME_MECHANISM_QUOTED_PRINTABLE);
   579         assert(mime);
   580         if (mime == NULL)
   581             goto enomem;
   582     }
   583 
   584     if (msg->attachments) {
   585         submime = mime;
   586         mime = part_multiple_new("multipart/mixed", NULL);
   587         assert(mime);
   588         if (mime == NULL)
   589             goto enomem;
   590 
   591         r = mailmime_smart_add_part(mime, submime);
   592         assert(r == MAILIMF_NO_ERROR);
   593         if (r == MAILIMF_ERROR_MEMORY) {
   594             goto enomem;
   595         }
   596         else {
   597             // mailmime_smart_add_part() takes ownership of submime
   598             submime = NULL;
   599         }
   600 
   601         bloblist_t *_a;
   602         for (_a = msg->attachments; _a != NULL; _a = _a->next) {
   603             char * mime_type;
   604 
   605             assert(_a->data);
   606             assert(_a->size);
   607 
   608             status = mime_attachment(_a, &submime);
   609             if (status != PEP_STATUS_OK)
   610                 goto pep_error;
   611 
   612             r = mailmime_smart_add_part(mime, submime);
   613             assert(r == MAILIMF_NO_ERROR);
   614             if (r == MAILIMF_ERROR_MEMORY) {
   615                 goto enomem;
   616             }
   617             else {
   618                 // mailmime_smart_add_part() takes ownership of submime
   619                 submime = NULL;
   620             }
   621         }
   622     }
   623 
   624     msg_mime = mailmime_new_message_data(NULL);
   625     assert(msg_mime);
   626     if (msg_mime == NULL)
   627         goto enomem;
   628 
   629     r = mailmime_add_part(msg_mime, mime);
   630     if (r) {
   631         mailmime_free(mime);
   632         goto enomem;
   633     }
   634 
   635     status = build_fields(msg, &fields);
   636     if (status != PEP_STATUS_OK)
   637         goto pep_error;
   638 
   639     mailmime_set_imf_fields(msg_mime, fields);
   640 
   641     status = render_mime(msg_mime, &buf);
   642     if (status != PEP_STATUS_OK)
   643         goto pep_error;
   644 
   645     mailmime_free(msg_mime);
   646     *mimetext = buf;
   647 
   648     return PEP_STATUS_OK;
   649 
   650 enomem:
   651     status = PEP_OUT_OF_MEMORY;
   652 
   653 pep_error:
   654     if (msg_mime)
   655         mailmime_free(msg_mime);
   656     else
   657         if (mime)
   658             mailmime_free(mime);
   659 
   660     if (submime)
   661         mailmime_free(submime);
   662 
   663     return status;
   664 }
   665 
   666 DYNAMIC_API PEP_STATUS mime_decode_message(
   667         const char *mimetext,
   668         message **msg
   669     )
   670 {
   671     PEP_STATUS status = PEP_STATUS_OK;
   672     struct mailmime * mime = NULL;
   673     int r;
   674 
   675     assert(mimetext);
   676     assert(msg);
   677 
   678     *msg = NULL;
   679     
   680     size_t index = 0;
   681     r = mailmime_parse(mimetext, strlen(mimetext), &index, &mime);
   682     assert(r == 0);
   683     assert(mime);
   684     if (r) {
   685         if (r == MAILIMF_ERROR_MEMORY)
   686             goto enomem;
   687         else
   688             goto err_mime;
   689     }
   690 
   691     mailmime_free(mime);
   692 
   693     return status;
   694 
   695 err_mime:
   696     status = PEP_ILLEGAL_VALUE;
   697     goto pep_error;
   698 
   699 enomem:
   700     status = PEP_OUT_OF_MEMORY;
   701 
   702 pep_error:
   703     if (mime)
   704         mailmime_free(mime);
   705 
   706     return status;
   707 }
   708