src/mime.c
author vb
Sat, 07 Mar 2015 20:29:13 +0100
changeset 89 aef5a4bc78f3
parent 88 ac617f70bd7f
child 90 42b5eb9d5af2
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@48
    36
    if (r == -1)
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@89
   233
static PEP_STATUS build_fields(const message *msg, struct mailimf_fields **result)
vb@89
   234
{
vb@89
   235
    PEP_STATUS status = PEP_STATUS_OK;
vb@89
   236
    struct mailimf_fields * fields = NULL;
vb@89
   237
    int r;
vb@89
   238
    clist * list;
vb@89
   239
    clist * fields_list = NULL;
vb@89
   240
    char *subject = msg->shortmsg ? msg->shortmsg : "pEp";
vb@89
   241
vb@89
   242
    assert(msg);
vb@89
   243
    assert(msg->from);
vb@89
   244
    assert(msg->from->address);
vb@89
   245
    assert(result);
vb@89
   246
vb@89
   247
    *result = NULL;
vb@89
   248
vb@89
   249
    fields_list = clist_new();
vb@89
   250
    assert(fields_list);
vb@89
   251
    if (fields_list == NULL)
vb@89
   252
        goto enomem;
vb@89
   253
vb@89
   254
    r = _append_field(fields_list, MAILIMF_FIELD_MESSAGE_ID,
vb@89
   255
            (_new_func_t) mailimf_message_id_new, msg->id);
vb@89
   256
    if (r == -1)
vb@89
   257
        goto enomem;
vb@89
   258
vb@89
   259
    r = _append_field(fields_list, MAILIMF_FIELD_SUBJECT,
vb@89
   260
            (_new_func_t) mailimf_subject_new, subject);
vb@89
   261
    if (r == -1)
vb@89
   262
        goto enomem;
vb@89
   263
vb@89
   264
    fields = mailimf_fields_new(fields_list);
vb@89
   265
    assert(fields);
vb@89
   266
    if (fields == NULL)
vb@89
   267
        goto enomem;
vb@89
   268
vb@89
   269
    *result = fields;
vb@89
   270
vb@89
   271
    return PEP_STATUS_OK;
vb@89
   272
vb@89
   273
enomem:
vb@89
   274
    status = PEP_OUT_OF_MEMORY;
vb@89
   275
vb@89
   276
pep_error:
vb@89
   277
    if (fields_list)
vb@89
   278
        clist_free(fields_list);
vb@89
   279
vb@89
   280
    if (fields)
vb@89
   281
        mailimf_fields_free(fields);
vb@89
   282
vb@89
   283
    return status;
vb@89
   284
}
vb@89
   285
vb@89
   286
DYNAMIC_API PEP_STATUS mime_encode_message(
vb@89
   287
        const message *msg,
vb@89
   288
        char **mimetext
vb@89
   289
    )
vb@89
   290
{
vb@89
   291
    struct mailmime * msg_mime = NULL;
vb@89
   292
    struct mailmime * mime = NULL;
vb@89
   293
    struct mailmime * submime = NULL;
vb@89
   294
    char *buf = NULL;
vb@89
   295
    int r;
vb@89
   296
    PEP_STATUS status;
vb@89
   297
    char *subject;
vb@89
   298
    char *plaintext;
vb@89
   299
    char *htmltext;
vb@89
   300
vb@89
   301
    assert(msg);
vb@89
   302
    assert(mimetext);
vb@89
   303
vb@89
   304
    *mimetext = NULL;
vb@89
   305
vb@89
   306
    subject = (msg->shortmsg) ? msg->shortmsg : "pEp";
vb@89
   307
    plaintext = (msg->longmsg) ? msg->longmsg : "";
vb@89
   308
    htmltext = msg->longmsg_formatted;
vb@89
   309
vb@89
   310
    if (htmltext) {
vb@89
   311
        status = mime_html_text(plaintext, htmltext, &mime);
vb@89
   312
        if (status != PEP_STATUS_OK)
vb@89
   313
            goto pep_error;
vb@89
   314
    }
vb@89
   315
    else {
vb@89
   316
        mime = get_text_part("text/plain", msg->longmsg, strlen(msg->longmsg),
vb@89
   317
                MAILMIME_MECHANISM_QUOTED_PRINTABLE);
vb@89
   318
        assert(mime);
vb@89
   319
        if (mime == NULL)
vb@89
   320
            goto enomem;
vb@89
   321
    }
vb@89
   322
vb@89
   323
    if (msg->attachments) {
vb@89
   324
        submime = mime;
vb@89
   325
        mime = part_multiple_new("multipart/mixed", NULL);
vb@89
   326
        assert(mime);
vb@89
   327
        if (mime == NULL)
vb@89
   328
            goto enomem;
vb@89
   329
vb@89
   330
        r = mailmime_smart_add_part(mime, submime);
vb@89
   331
        assert(r == MAILIMF_NO_ERROR);
vb@89
   332
        if (r == MAILIMF_ERROR_MEMORY) {
vb@89
   333
            goto enomem;
vb@89
   334
        }
vb@89
   335
        else {
vb@89
   336
            // mailmime_smart_add_part() takes ownership of submime
vb@89
   337
            submime = NULL;
vb@89
   338
        }
vb@89
   339
vb@89
   340
        bloblist_t *_a;
vb@89
   341
        for (_a = msg->attachments; _a != NULL; _a = _a->next) {
vb@89
   342
            char * mime_type;
vb@89
   343
vb@89
   344
            assert(_a->data);
vb@89
   345
            assert(_a->size);
vb@89
   346
vb@89
   347
            status = mime_attachment(_a, &submime);
vb@89
   348
            if (status != PEP_STATUS_OK)
vb@89
   349
                goto pep_error;
vb@89
   350
vb@89
   351
            r = mailmime_smart_add_part(mime, submime);
vb@89
   352
            assert(r == MAILIMF_NO_ERROR);
vb@89
   353
            if (r == MAILIMF_ERROR_MEMORY) {
vb@89
   354
                goto enomem;
vb@89
   355
            }
vb@89
   356
            else {
vb@89
   357
                // mailmime_smart_add_part() takes ownership of submime
vb@89
   358
                submime = NULL;
vb@89
   359
            }
vb@89
   360
        }
vb@89
   361
    }
vb@89
   362
vb@89
   363
    msg_mime = mailmime_new_message_data(NULL);
vb@89
   364
    mailmime_add_part(msg_mime, mime);
vb@89
   365
vb@89
   366
    status = render_mime(mime, &buf);
vb@89
   367
    if (status != PEP_STATUS_OK)
vb@89
   368
        goto pep_error;
vb@89
   369
vb@89
   370
    mailmime_free(msg_mime);
vb@89
   371
    *mimetext = buf;
vb@89
   372
    return PEP_STATUS_OK;
vb@89
   373
vb@89
   374
enomem:
vb@89
   375
    status = PEP_OUT_OF_MEMORY;
vb@89
   376
vb@89
   377
pep_error:
vb@89
   378
    if (msg_mime)
vb@89
   379
        mailmime_free(mime);
vb@89
   380
    else
vb@89
   381
        if (mime)
vb@89
   382
            mailmime_free(mime);
vb@89
   383
vb@89
   384
    if (submime)
vb@89
   385
        mailmime_free(submime);
vb@89
   386
vb@89
   387
    return status;
vb@89
   388
}
vb@89
   389
vb@89
   390
DYNAMIC_API PEP_STATUS mime_decode_message(
vb@89
   391
        const char *mimetext,
vb@89
   392
        message **msg
vb@89
   393
    )
vb@89
   394
{
vb@89
   395
    PEP_STATUS status = PEP_STATUS_OK;
vb@89
   396
    struct mailmime * mime = NULL;
vb@89
   397
    int r;
vb@89
   398
vb@89
   399
    assert(mimetext);
vb@89
   400
    assert(msg);
vb@89
   401
vb@89
   402
    *msg = NULL;
vb@89
   403
    
vb@89
   404
    size_t index = 0;
vb@89
   405
    r = mailmime_parse(mimetext, strlen(mimetext), &index, &mime);
vb@89
   406
    assert(r == 0);
vb@89
   407
    assert(mime);
vb@89
   408
    if (r != 0) {
vb@89
   409
        if (r == MAILIMF_ERROR_MEMORY)
vb@89
   410
            goto enomem;
vb@89
   411
        else
vb@89
   412
            goto err_mime;
vb@89
   413
    }
vb@89
   414
vb@89
   415
    mailmime_free(mime);
vb@89
   416
vb@89
   417
    return status;
vb@89
   418
vb@89
   419
err_mime:
vb@89
   420
    status = PEP_ILLEGAL_VALUE;
vb@89
   421
    goto pep_error;
vb@89
   422
vb@89
   423
enomem:
vb@89
   424
    status = PEP_OUT_OF_MEMORY;
vb@89
   425
vb@89
   426
pep_error:
vb@89
   427
    if (mime)
vb@89
   428
        mailmime_free(mime);
vb@89
   429
vb@89
   430
    return status;
vb@89
   431
}
vb@89
   432