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