src/mime.c
author vb
Sun, 08 Mar 2015 11:58:26 +0100
changeset 91 00c67be2d56d
parent 90 42b5eb9d5af2
child 93 6f3f781caaa0
permissions -rw-r--r--
...
     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_mailbox_list * mbl_from_identity_list(identity_list *il)
   271 {
   272     struct mailimf_mailbox_list *mbl = NULL;
   273     struct mailimf_mailbox *mb = NULL;
   274     clist *list = NULL;
   275     int r;
   276 
   277     assert(il);
   278 
   279     list = clist_new();
   280     if (list == NULL)
   281         goto enomem;
   282 
   283     identity_list *_il;
   284     for (_il = il; _il; _il = _il->next) {
   285         mb = mailbox_from_string(_il->ident->username, _il->ident->address);
   286         if (mb == NULL)
   287             goto enomem;
   288 
   289         r = clist_append(list, mb);
   290         if (r)
   291             goto enomem;
   292     }
   293 
   294     mbl = mailimf_mailbox_list_new(list);
   295     if (mbl == NULL)
   296         goto enomem;
   297 
   298     return mbl;
   299 
   300 enomem:
   301     if (mb)
   302         mailimf_mailbox_free(mb);
   303 
   304     if (list)
   305         clist_free(list);
   306 
   307     return NULL;
   308 }
   309 
   310 static clist * clist_from_stringlist(stringlist_t *sl)
   311 {
   312     clist * cl = clist_new();
   313     assert(cl);
   314     if (cl == NULL)
   315         return NULL;
   316 
   317     stringlist_t *_sl;
   318     for (_sl = sl; _sl; _sl = _sl->next) {
   319         int r;
   320         char * value = strdup(_sl->value);
   321         assert(value);
   322         if (value == NULL) {
   323             clist_free(cl);
   324             return NULL;
   325         }
   326         r = clist_append(cl, value);
   327         assert(r == 0);
   328         if (r) {
   329             free(value);
   330             clist_free(cl);
   331             return NULL;
   332         }
   333     }
   334 
   335     return cl;
   336 }
   337 
   338 static PEP_STATUS build_fields(const message *msg, struct mailimf_fields **result)
   339 {
   340     PEP_STATUS status = PEP_STATUS_OK;
   341     struct mailimf_fields * fields = NULL;
   342     int r;
   343     clist * fields_list = NULL;
   344     char *subject = msg->shortmsg ? msg->shortmsg : "pEp";
   345 
   346     assert(msg);
   347     assert(msg->from);
   348     assert(msg->from->address);
   349     assert(result);
   350 
   351     *result = NULL;
   352 
   353     fields_list = clist_new();
   354     assert(fields_list);
   355     if (fields_list == NULL)
   356         goto enomem;
   357 
   358     if (msg->id) {
   359         char *_msgid = strdup(msg->id);
   360         if (_msgid == NULL)
   361             goto enomem;
   362 
   363         r = _append_field(fields_list, MAILIMF_FIELD_MESSAGE_ID,
   364                 (_new_func_t) mailimf_message_id_new, _msgid);
   365         if (r) {
   366             free(_msgid);
   367             goto enomem;
   368         }
   369     }
   370 
   371     /* if (subject) */ {
   372         char *_subject = strdup(subject);
   373         if (_subject == NULL)
   374             goto enomem;
   375 
   376         r = _append_field(fields_list, MAILIMF_FIELD_SUBJECT,
   377                 (_new_func_t) mailimf_subject_new, _subject);
   378         if (r) {
   379             free(_subject);
   380             goto enomem;
   381         }
   382     }
   383 
   384     if (msg->sent) {
   385         struct mailimf_date_time * dt = timestamp_to_etpantime(msg->sent);
   386         if (dt == NULL)
   387             goto enomem;
   388 
   389         r = _append_field(fields_list, MAILIMF_FIELD_ORIG_DATE,
   390                 (_new_func_t) mailimf_orig_date_new, dt);
   391         if (r) {
   392             mailimf_date_time_free(dt);
   393             goto enomem;
   394         }
   395         dt = NULL;
   396     }
   397 
   398     /* if (msg->from) */ {
   399         struct mailimf_mailbox_list *from = mbl_from_identity(msg->from);
   400         if (from == NULL)
   401             goto enomem;
   402 
   403         r = _append_field(fields_list, MAILIMF_FIELD_FROM,
   404                 (_new_func_t) mailimf_from_new, from);
   405         if (r) {
   406             mailimf_mailbox_list_free(from);
   407             goto enomem;
   408         }
   409     }
   410 
   411     if (msg->to) {
   412         struct mailimf_mailbox_list *to = mbl_from_identity_list(msg->to);
   413         if (to == NULL)
   414             goto enomem;
   415 
   416         r = _append_field(fields_list, MAILIMF_FIELD_TO,
   417                 (_new_func_t) mailimf_to_new, to);
   418         if (r) {
   419             mailimf_mailbox_list_free(to);
   420             goto enomem;
   421         }
   422     }
   423 
   424     if (msg->cc) {
   425         struct mailimf_mailbox_list *cc = mbl_from_identity_list(msg->cc);
   426         if (cc == NULL)
   427             goto enomem;
   428 
   429         r = _append_field(fields_list, MAILIMF_FIELD_CC,
   430                 (_new_func_t) mailimf_cc_new, cc);
   431         if (r) {
   432             mailimf_mailbox_list_free(cc);
   433             goto enomem;
   434         }
   435     }
   436     
   437     if (msg->bcc) {
   438         struct mailimf_mailbox_list *bcc = mbl_from_identity_list(msg->bcc);
   439         if (bcc == NULL)
   440             goto enomem;
   441 
   442         r = _append_field(fields_list, MAILIMF_FIELD_BCC,
   443                 (_new_func_t) mailimf_bcc_new, bcc);
   444         if (r) {
   445             mailimf_mailbox_list_free(bcc);
   446             goto enomem;
   447         }
   448     }
   449     
   450     if (msg->reply_to) {
   451         struct mailimf_mailbox_list *reply_to= mbl_from_identity(msg->reply_to);
   452         if (reply_to == NULL)
   453             goto enomem;
   454 
   455         r = _append_field(fields_list, MAILIMF_FIELD_REPLY_TO,
   456                 (_new_func_t) mailimf_reply_to_new, reply_to);
   457         if (r) {
   458             mailimf_mailbox_list_free(reply_to);
   459             goto enomem;
   460         }
   461     }
   462 
   463     if (msg->in_reply_to) {
   464         char *in_reply_to = strdup(msg->in_reply_to);
   465         if (in_reply_to == NULL)
   466             goto enomem;
   467 
   468         r = _append_field(fields_list, MAILIMF_FIELD_IN_REPLY_TO,
   469                 (_new_func_t) mailimf_in_reply_to_new, in_reply_to);
   470         if (r) {
   471             free(in_reply_to);
   472             goto enomem;
   473         }
   474     }
   475 
   476     if (msg->references) {
   477         clist *references = clist_from_stringlist(msg->references);
   478         if (references == NULL)
   479             goto enomem;
   480 
   481         r = _append_field(fields_list, MAILIMF_FIELD_REFERENCES,
   482                 (_new_func_t) mailimf_references_new, references);
   483         if (r) {
   484             clist_free(references);
   485             goto enomem;
   486         }
   487     }
   488 
   489     if (msg->keywords) {
   490         clist *keywords = clist_from_stringlist(msg->keywords);
   491         if (keywords == NULL)
   492             goto enomem;
   493 
   494         r = _append_field(fields_list, MAILIMF_FIELD_KEYWORDS,
   495                 (_new_func_t) mailimf_keywords_new, keywords);
   496         if (r) {
   497             clist_free(keywords);
   498             goto enomem;
   499         }
   500     }
   501 
   502     if (msg->comments) {
   503         char *comments = strdup(msg->comments);
   504         if (comments == NULL)
   505             goto enomem;
   506 
   507         r = _append_field(fields_list, MAILIMF_FIELD_COMMENTS,
   508                 (_new_func_t) mailimf_comments_new, comments);
   509         if (r) {
   510             free(comments);
   511             goto enomem;
   512         }
   513     }
   514 
   515     fields = mailimf_fields_new(fields_list);
   516     assert(fields);
   517     if (fields == NULL)
   518         goto enomem;
   519 
   520     *result = fields;
   521 
   522     return PEP_STATUS_OK;
   523 
   524 enomem:
   525     status = PEP_OUT_OF_MEMORY;
   526 
   527 pep_error:
   528     if (fields_list)
   529         clist_free(fields_list);
   530 
   531     if (fields)
   532         mailimf_fields_free(fields);
   533 
   534     return status;
   535 }
   536 
   537 DYNAMIC_API PEP_STATUS mime_encode_message(
   538         const message *msg,
   539         char **mimetext
   540     )
   541 {
   542     struct mailmime * msg_mime = NULL;
   543     struct mailmime * mime = NULL;
   544     struct mailmime * submime = NULL;
   545     struct mailimf_fields * fields = NULL;
   546     char *buf = NULL;
   547     int r;
   548     PEP_STATUS status;
   549     char *subject;
   550     char *plaintext;
   551     char *htmltext;
   552 
   553     assert(msg);
   554     assert(mimetext);
   555 
   556     *mimetext = NULL;
   557 
   558     subject = (msg->shortmsg) ? msg->shortmsg : "pEp";
   559     plaintext = (msg->longmsg) ? msg->longmsg : "";
   560     htmltext = msg->longmsg_formatted;
   561 
   562     if (htmltext) {
   563         status = mime_html_text(plaintext, htmltext, &mime);
   564         if (status != PEP_STATUS_OK)
   565             goto pep_error;
   566     }
   567     else {
   568         mime = get_text_part("text/plain", plaintext, strlen(plaintext),
   569                 MAILMIME_MECHANISM_QUOTED_PRINTABLE);
   570         assert(mime);
   571         if (mime == NULL)
   572             goto enomem;
   573     }
   574 
   575     if (msg->attachments) {
   576         submime = mime;
   577         mime = part_multiple_new("multipart/mixed", NULL);
   578         assert(mime);
   579         if (mime == NULL)
   580             goto enomem;
   581 
   582         r = mailmime_smart_add_part(mime, submime);
   583         assert(r == MAILIMF_NO_ERROR);
   584         if (r == MAILIMF_ERROR_MEMORY) {
   585             goto enomem;
   586         }
   587         else {
   588             // mailmime_smart_add_part() takes ownership of submime
   589             submime = NULL;
   590         }
   591 
   592         bloblist_t *_a;
   593         for (_a = msg->attachments; _a != NULL; _a = _a->next) {
   594             char * mime_type;
   595 
   596             assert(_a->data);
   597             assert(_a->size);
   598 
   599             status = mime_attachment(_a, &submime);
   600             if (status != PEP_STATUS_OK)
   601                 goto pep_error;
   602 
   603             r = mailmime_smart_add_part(mime, submime);
   604             assert(r == MAILIMF_NO_ERROR);
   605             if (r == MAILIMF_ERROR_MEMORY) {
   606                 goto enomem;
   607             }
   608             else {
   609                 // mailmime_smart_add_part() takes ownership of submime
   610                 submime = NULL;
   611             }
   612         }
   613     }
   614 
   615     msg_mime = mailmime_new_message_data(NULL);
   616     assert(msg_mime);
   617     if (msg_mime == NULL)
   618         goto enomem;
   619 
   620     r = mailmime_add_part(msg_mime, mime);
   621     if (r) {
   622         mailmime_free(mime);
   623         goto enomem;
   624     }
   625 
   626     status = build_fields(msg, &fields);
   627     if (status != PEP_STATUS_OK)
   628         goto pep_error;
   629 
   630     mailmime_set_imf_fields(msg_mime, fields);
   631 
   632     status = render_mime(msg_mime, &buf);
   633     if (status != PEP_STATUS_OK)
   634         goto pep_error;
   635 
   636     mailmime_free(msg_mime);
   637     *mimetext = buf;
   638 
   639     return PEP_STATUS_OK;
   640 
   641 enomem:
   642     status = PEP_OUT_OF_MEMORY;
   643 
   644 pep_error:
   645     if (msg_mime)
   646         mailmime_free(msg_mime);
   647     else
   648         if (mime)
   649             mailmime_free(mime);
   650 
   651     if (submime)
   652         mailmime_free(submime);
   653 
   654     return status;
   655 }
   656 
   657 DYNAMIC_API PEP_STATUS mime_decode_message(
   658         const char *mimetext,
   659         message **msg
   660     )
   661 {
   662     PEP_STATUS status = PEP_STATUS_OK;
   663     struct mailmime * mime = NULL;
   664     int r;
   665 
   666     assert(mimetext);
   667     assert(msg);
   668 
   669     *msg = NULL;
   670     
   671     size_t index = 0;
   672     r = mailmime_parse(mimetext, strlen(mimetext), &index, &mime);
   673     assert(r == 0);
   674     assert(mime);
   675     if (r) {
   676         if (r == MAILIMF_ERROR_MEMORY)
   677             goto enomem;
   678         else
   679             goto err_mime;
   680     }
   681 
   682     mailmime_free(mime);
   683 
   684     return status;
   685 
   686 err_mime:
   687     status = PEP_ILLEGAL_VALUE;
   688     goto pep_error;
   689 
   690 enomem:
   691     status = PEP_OUT_OF_MEMORY;
   692 
   693 pep_error:
   694     if (mime)
   695         mailmime_free(mime);
   696 
   697     return status;
   698 }
   699