src/message_api.c
author Krista Bennett <krista@pep-project.org>
Tue, 19 Sep 2017 15:59:42 +0200
branchENGINE-214
changeset 2078 12571364b016
parent 2048 4612060c1d49
child 2092 926e2e53967d
permissions -rw-r--r--
ENGINE-214: actually works, but will need to be changed due to need to encapsulate message version in protected part of message. Infrastructure to do that comes in with this commit, but we need to refactor _decrypt_message in order to do everything cleanly. Branching for ENGINE-262 and will return to this branch once done with refactor.
</
vb@1513
     1
// This file is under GNU General Public License 3.0
vb@1513
     2
// see LICENSE.txt
vb@1513
     3
vb@125
     4
#include "pEp_internal.h"
vb@37
     5
#include "message_api.h"
vb@37
     6
vb@130
     7
#include "platform.h"
vb@220
     8
#include "mime.h"
vb@952
     9
#include "sync_fsm.h"
vb@85
    10
vb@37
    11
#include <assert.h>
vb@37
    12
#include <string.h>
vb@39
    13
#include <stdlib.h>
vb@39
    14
vb@926
    15
vb@951
    16
#ifndef _MIN
vb@951
    17
#define _MIN(A, B) ((B) > (A) ? (A) : (B))
vb@190
    18
#endif
vb@951
    19
#ifndef _MAX
vb@951
    20
#define _MAX(A, B) ((B) > (A) ? (B) : (A))
vb@300
    21
#endif
vb@190
    22
krista@1873
    23
static char* _get_resource_ptr_noown(char* uri) {
krista@1873
    24
    char* uri_delim = strstr(uri, "://");
krista@1873
    25
    if (!uri_delim)
krista@1873
    26
        return uri;
krista@1873
    27
    else
krista@1873
    28
        return uri + 3;
krista@1873
    29
}
krista@1873
    30
krista@1873
    31
static bool is_file_uri(char* str) {
krista@1873
    32
    return(strncmp(str, "file://", 7) == 0);
krista@1873
    33
}
krista@1873
    34
krista@1873
    35
static bool is_cid_uri(const char* str) {
krista@1873
    36
    return(strncmp(str, "cid://", 6) == 0);
krista@1873
    37
}
vb@235
    38
vb@235
    39
static bool string_equality(const char *s1, const char *s2)
vb@235
    40
{
vb@235
    41
    if (s1 == NULL || s2 == NULL)
vb@235
    42
        return false;
vb@235
    43
vb@235
    44
    assert(s1 && s2);
vb@235
    45
vb@235
    46
    return strcmp(s1, s2) == 0;
vb@235
    47
}
vb@235
    48
vb@235
    49
static bool is_mime_type(const bloblist_t *bl, const char *mt)
vb@235
    50
{
vb@235
    51
    assert(mt);
vb@235
    52
vb@235
    53
    return bl && string_equality(bl->mime_type, mt);
vb@235
    54
}
vb@235
    55
krista@854
    56
//
krista@854
    57
// This function presumes the file ending is a proper substring of the
krista@854
    58
// filename (i.e. if bl->filename is "a.pgp" and fe is ".pgp", it will
krista@854
    59
// return true, but if bl->filename is ".pgp" and fe is ".pgp", it will
krista@854
    60
// return false. This is desired behaviour.
krista@854
    61
//
vb@235
    62
static bool is_fileending(const bloblist_t *bl, const char *fe)
vb@235
    63
{
vb@235
    64
    assert(fe);
krista@1427
    65
krista@1873
    66
    if (bl == NULL || bl->filename == NULL || fe == NULL || is_cid_uri(bl->filename))
vb@235
    67
        return false;
vb@235
    68
vb@235
    69
    assert(bl && bl->filename);
vb@235
    70
vb@235
    71
    size_t fe_len = strlen(fe);
vb@235
    72
    size_t fn_len = strlen(bl->filename);
vb@235
    73
vb@235
    74
    if (fn_len <= fe_len)
vb@235
    75
        return false;
vb@235
    76
vb@235
    77
    assert(fn_len > fe_len);
vb@235
    78
vb@235
    79
    return strcmp(bl->filename + (fn_len - fe_len), fe) == 0;
vb@235
    80
}
vb@235
    81
vb@952
    82
void add_opt_field(message *msg, const char *name, const char *value)
vb@284
    83
{
vb@952
    84
    assert(msg && name && value);
vb@284
    85
vb@284
    86
    if (msg && name && value) {
vb@284
    87
        stringpair_t *pair = new_stringpair(name, value);
vb@284
    88
        if (pair == NULL)
vb@284
    89
            return;
vb@284
    90
vb@284
    91
        stringpair_list_t *field = stringpair_list_add(msg->opt_fields, pair);
vb@284
    92
        if (field == NULL)
Edouard@833
    93
        {
Edouard@833
    94
            free_stringpair(pair);
vb@284
    95
            return;
Edouard@833
    96
        }
vb@284
    97
vb@284
    98
        if (msg->opt_fields == NULL)
vb@284
    99
            msg->opt_fields = field;
vb@284
   100
    }
vb@284
   101
}
vb@284
   102
krista@2078
   103
static char * encapsulate_message_version(const char *msg_version, const char *longmsg)
krista@2078
   104
{
krista@2078
   105
    assert(msg_version);
krista@2078
   106
    
krista@2078
   107
    if (!msg_version) {
krista@2078
   108
        if (!longmsg)
krista@2078
   109
            return NULL;
krista@2078
   110
        else {
krista@2078
   111
            char *result = strdup(longmsg);
krista@2078
   112
            assert(result);
krista@2078
   113
            return result;            
krista@2078
   114
        }    
krista@2078
   115
    }
krista@2078
   116
    
krista@2078
   117
    if (longmsg == NULL)
krista@2078
   118
        longmsg = "";
krista@2078
   119
        
krista@2078
   120
    const char * const newlines = "\n\n";
krista@2078
   121
    const size_t NL_LEN = 2;
krista@2078
   122
        
krista@2078
   123
    const size_t bufsize = PEP_MSG_VERSION_KEY_LEN + strlen(msg_version) + NL_LEN + strlen(longmsg) + 1;
krista@2078
   124
    char * ptext = calloc(1, bufsize);
krista@2078
   125
    assert(ptext);
krista@2078
   126
    if (ptext == NULL)
krista@2078
   127
        return NULL;
krista@2078
   128
krista@2078
   129
    strlcpy(ptext, PEP_MSG_VERSION_KEY, bufsize);
krista@2078
   130
    strlcat(ptext, msg_version, bufsize);
krista@2078
   131
    strlcat(ptext, newlines, bufsize);
krista@2078
   132
    strlcat(ptext, longmsg, bufsize);
krista@2078
   133
krista@2078
   134
    return ptext;
krista@2078
   135
}
krista@2078
   136
krista@2078
   137
vb@83
   138
static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
vb@62
   139
{
vb@83
   140
    assert(shortmsg);
krista@2045
   141
    
krista@2045
   142
    unsigned char pepstr[] = PEP_SUBJ_STRING;
krista@2045
   143
    assert(strcmp(shortmsg, "pEp") != 0 && _unsigned_signed_strcmp(pepstr, shortmsg, PEP_SUBJ_BYTELEN) != 0); 
krista@2045
   144
    
krista@2045
   145
    if (!shortmsg || strcmp(shortmsg, "pEp") == 0 || 
krista@2045
   146
                     _unsigned_signed_strcmp(pepstr, shortmsg, PEP_SUBJ_BYTELEN) == 0) {
vb@857
   147
        if (!longmsg) {
vb@857
   148
            return NULL;
vb@857
   149
        }
vb@857
   150
        else {
vb@857
   151
            char *result = strdup(longmsg);
vb@857
   152
            assert(result);
vb@857
   153
            return result;
vb@857
   154
        }
vb@857
   155
    }
krista@1427
   156
vb@83
   157
    if (longmsg == NULL)
vb@63
   158
        longmsg = "";
vb@63
   159
krista@935
   160
    const char * const newlines = "\n\n";
krista@935
   161
    const size_t NL_LEN = 2;
krista@935
   162
krista@2078
   163
    const size_t bufsize = PEP_SUBJ_KEY_LEN + strlen(shortmsg) + NL_LEN + strlen(longmsg) + 1;
roker@1559
   164
    char * ptext = calloc(1, bufsize);
vb@109
   165
    assert(ptext);
vb@62
   166
    if (ptext == NULL)
vb@62
   167
        return NULL;
vb@62
   168
krista@2078
   169
    strlcpy(ptext, PEP_SUBJ_KEY, bufsize);
krista@918
   170
    strlcat(ptext, shortmsg, bufsize);
krista@935
   171
    strlcat(ptext, newlines, bufsize);
krista@918
   172
    strlcat(ptext, longmsg, bufsize);
vb@62
   173
vb@62
   174
    return ptext;
vb@62
   175
}
vb@44
   176
krista@2078
   177
/* 
krista@2078
   178
   WARNING: For the moment, this only works for the first line of decrypted
krista@2078
   179
   plaintext because we don't need more. IF WE DO, THIS MUST BE EXPANDED, or
krista@2078
   180
   we need a delineated section to parse separately
krista@2078
   181
   
krista@2078
   182
   Does case-insensitive compare of keys, so sending in a lower-cased
krista@2078
   183
   string constant saves a bit of computation
krista@2078
   184
 */
krista@2078
   185
static PEP_STATUS get_data_from_encapsulated_line(const char* plaintext, const char* key, 
krista@2078
   186
                                                  const size_t keylen, char** data, 
krista@2078
   187
                                                  char** modified_msg) {
krista@2078
   188
    char* _data = NULL;
krista@2078
   189
    char* _modified = NULL;
krista@2078
   190
    
krista@2078
   191
    if (strncasecmp(plaintext, key, keylen) == 0) {
krista@2078
   192
        const char *line_end = strchr(plaintext, '\n');
krista@2078
   193
krista@2078
   194
        if (line_end == NULL) {
krista@2078
   195
            _data = strdup(plaintext + keylen);
krista@2078
   196
            assert(_data);
krista@2078
   197
            if (_data == NULL)
krista@2078
   198
                return PEP_OUT_OF_MEMORY;
krista@2078
   199
        }
krista@2078
   200
        else {
krista@2078
   201
            size_t n = line_end - plaintext;
krista@2078
   202
krista@2078
   203
            if (*(line_end - 1) == '\r')
krista@2078
   204
                _data = strndup(plaintext + keylen, n - (keylen + 1));
krista@2078
   205
            else
krista@2078
   206
                _data = strndup(plaintext + keylen, n - keylen);
krista@2078
   207
            assert(_data);
krista@2078
   208
            if (_data == NULL)
krista@2078
   209
                return PEP_OUT_OF_MEMORY;
krista@2078
   210
krista@2078
   211
            while (*(plaintext + n) && (*(plaintext + n) == '\n' || *(plaintext + n) == '\r'))
krista@2078
   212
                ++n;
krista@2078
   213
krista@2078
   214
            if (*(plaintext + n)) {
krista@2078
   215
                _modified = strdup(plaintext + n);
krista@2078
   216
                assert(_modified);
krista@2078
   217
                if (_modified == NULL)
krista@2078
   218
                    return PEP_OUT_OF_MEMORY;
krista@2078
   219
            }
krista@2078
   220
        }
krista@2078
   221
    }
krista@2078
   222
    *data = _data;
krista@2078
   223
    *modified_msg = _modified;
krista@2078
   224
    return PEP_STATUS_OK;
krista@2078
   225
}
krista@2078
   226
krista@2078
   227
krista@2078
   228
static int separate_short_and_long(const char *src, char **shortmsg, char** msg_version, char **longmsg)
vb@82
   229
{
vb@82
   230
    char *_shortmsg = NULL;
krista@2078
   231
    char *_msg_version = NULL;
vb@82
   232
    char *_longmsg = NULL;
vb@82
   233
vb@82
   234
    assert(src);
vb@82
   235
    assert(shortmsg);
krista@2078
   236
    assert(msg_version);
vb@82
   237
    assert(longmsg);
krista@1427
   238
krista@2078
   239
    if (src == NULL || shortmsg == NULL || msg_version == NULL || longmsg == NULL)
krista@853
   240
        return -1;
vb@82
   241
vb@82
   242
    *shortmsg = NULL;
vb@82
   243
    *longmsg = NULL;
krista@2078
   244
    *msg_version = NULL;
krista@2078
   245
krista@2078
   246
    // We generated the input here. If we ever need more than one header value to be
krista@2078
   247
    // encapsulated and hidden in the encrypted text, we will have to modify this.
krista@2078
   248
    // As is, we're either doing this with a version 1.0 client, in which case
krista@2078
   249
    // the only encapsulated header value is subject, or 2.0+, in which the
krista@2078
   250
    // message version is the only encapsulated header value. If we need this
krista@2078
   251
    // to be more complex, we're going to have to do something more elegant
krista@2078
   252
    // and efficient.    
krista@2078
   253
    PEP_STATUS status = get_data_from_encapsulated_line(src, PEP_SUBJ_KEY_LC, 
krista@2078
   254
                                                        PEP_SUBJ_KEY_LEN, 
krista@2078
   255
                                                        &_shortmsg, &_longmsg);
krista@2078
   256
                                                        
krista@2078
   257
    if (_shortmsg) {
krista@2078
   258
        if (status == PEP_STATUS_OK)
krista@2078
   259
            *shortmsg = _shortmsg;
krista@2078
   260
        else
krista@2078
   261
            goto enomem;
vb@82
   262
    }
vb@82
   263
    else {
krista@2078
   264
        status = get_data_from_encapsulated_line(src, PEP_MSG_VERSION_KEY_LC, 
krista@2078
   265
                                                 PEP_MSG_VERSION_KEY_LEN, 
krista@2078
   266
                                                 &_msg_version, &_longmsg);
krista@2078
   267
        if (_msg_version) {
krista@2078
   268
            if (status == PEP_STATUS_OK)
krista@2078
   269
                *msg_version = _msg_version;
krista@2078
   270
            else
krista@2078
   271
                goto enomem;
krista@2078
   272
        }
krista@2078
   273
    }
krista@2078
   274
    
krista@2078
   275
    // If there was no secret data hiding in the first line...
krista@2078
   276
    if (!_shortmsg && !_msg_version) {
vb@82
   277
        _longmsg = strdup(src);
vb@469
   278
        assert(_longmsg);
vb@82
   279
        if (_longmsg == NULL)
vb@82
   280
            goto enomem;
vb@82
   281
    }
krista@1852
   282
    
vb@82
   283
    *longmsg = _longmsg;
vb@82
   284
vb@82
   285
    return 0;
vb@82
   286
vb@82
   287
enomem:
vb@82
   288
    free(_shortmsg);
krista@2078
   289
    free(_msg_version);
vb@82
   290
    free(_longmsg);
vb@82
   291
vb@82
   292
    return -1;
vb@82
   293
}
vb@82
   294
vb@113
   295
static PEP_STATUS copy_fields(message *dst, const message *src)
vb@113
   296
{
vb@164
   297
    assert(dst);
vb@164
   298
    assert(src);
vb@164
   299
Edouard@840
   300
    if(!(dst && src))
Edouard@840
   301
        return PEP_ILLEGAL_VALUE;
Edouard@840
   302
vb@113
   303
    free_timestamp(dst->sent);
vb@113
   304
    dst->sent = NULL;
vb@113
   305
    if (src->sent) {
vb@113
   306
        dst->sent = timestamp_dup(src->sent);
vb@113
   307
        if (dst->sent == NULL)
vb@113
   308
            return PEP_OUT_OF_MEMORY;
vb@113
   309
    }
vb@113
   310
vb@113
   311
    free_timestamp(dst->recv);
vb@113
   312
    dst->recv = NULL;
vb@113
   313
    if (src->recv) {
vb@113
   314
        dst->recv = timestamp_dup(src->recv);
vb@113
   315
        if (dst->recv == NULL)
vb@113
   316
            return PEP_OUT_OF_MEMORY;
vb@113
   317
    }
vb@113
   318
vb@113
   319
    free_identity(dst->from);
vb@113
   320
    dst->from = NULL;
vb@113
   321
    if (src->from) {
vb@113
   322
        dst->from = identity_dup(src->from);
vb@113
   323
        if (dst->from == NULL)
vb@113
   324
            return PEP_OUT_OF_MEMORY;
vb@113
   325
    }
vb@113
   326
vb@113
   327
    free_identity_list(dst->to);
vb@113
   328
    dst->to = NULL;
vb@274
   329
    if (src->to && src->to->ident) {
vb@113
   330
        dst->to = identity_list_dup(src->to);
vb@113
   331
        if (dst->to == NULL)
vb@113
   332
            return PEP_OUT_OF_MEMORY;
vb@113
   333
    }
vb@113
   334
vb@113
   335
    free_identity(dst->recv_by);
vb@113
   336
    dst->recv_by = NULL;
vb@113
   337
    if (src->recv_by) {
vb@113
   338
        dst->recv_by = identity_dup(src->recv_by);
vb@113
   339
        if (dst->recv_by == NULL)
vb@113
   340
            return PEP_OUT_OF_MEMORY;
vb@113
   341
    }
vb@113
   342
vb@113
   343
    free_identity_list(dst->cc);
vb@113
   344
    dst->cc = NULL;
vb@274
   345
    if (src->cc && src->cc->ident) {
vb@113
   346
        dst->cc = identity_list_dup(src->cc);
vb@113
   347
        if (dst->cc == NULL)
vb@113
   348
            return PEP_OUT_OF_MEMORY;
vb@113
   349
    }
vb@113
   350
vb@113
   351
    free_identity_list(dst->bcc);
vb@113
   352
    dst->bcc = NULL;
vb@274
   353
    if (src->bcc && src->bcc->ident) {
vb@113
   354
        dst->bcc = identity_list_dup(src->bcc);
vb@113
   355
        if (dst->bcc == NULL)
vb@113
   356
            return PEP_OUT_OF_MEMORY;
vb@113
   357
    }
vb@113
   358
vb@113
   359
    free_identity_list(dst->reply_to);
vb@113
   360
    dst->reply_to = NULL;
vb@274
   361
    if (src->reply_to && src->reply_to->ident) {
vb@113
   362
        dst->reply_to = identity_list_dup(src->reply_to);
vb@113
   363
        if (dst->reply_to == NULL)
vb@113
   364
            return PEP_OUT_OF_MEMORY;
vb@113
   365
    }
vb@113
   366
vb@113
   367
    free_stringlist(dst->in_reply_to);
vb@113
   368
    dst->in_reply_to = NULL;
vb@274
   369
    if (src->in_reply_to && src->in_reply_to->value) {
vb@113
   370
        dst->in_reply_to = stringlist_dup(src->in_reply_to);
vb@113
   371
        if (dst->in_reply_to == NULL)
vb@113
   372
            return PEP_OUT_OF_MEMORY;
vb@113
   373
    }
vb@113
   374
vb@113
   375
    free_stringlist(dst->references);
vb@113
   376
    dst->references = NULL;
vb@113
   377
    if (src->references) {
vb@113
   378
        dst->references = stringlist_dup(src->references);
vb@113
   379
        if (dst->references == NULL)
vb@113
   380
            return PEP_OUT_OF_MEMORY;
vb@113
   381
    }
vb@113
   382
vb@113
   383
    free_stringlist(dst->keywords);
vb@113
   384
    dst->keywords = NULL;
vb@274
   385
    if (src->keywords && src->keywords->value) {
vb@113
   386
        dst->keywords = stringlist_dup(src->keywords);
vb@113
   387
        if (dst->keywords == NULL)
vb@113
   388
            return PEP_OUT_OF_MEMORY;
vb@113
   389
    }
vb@113
   390
vb@113
   391
    free(dst->comments);
vb@113
   392
    dst->comments = NULL;
vb@113
   393
    if (src->comments) {
vb@113
   394
        dst->comments = strdup(src->comments);
vb@113
   395
        assert(dst->comments);
vb@113
   396
        if (dst->comments == NULL)
vb@113
   397
            return PEP_OUT_OF_MEMORY;
vb@113
   398
    }
vb@113
   399
vb@1092
   400
    free_stringpair_list(dst->opt_fields);
vb@1092
   401
    dst->opt_fields = NULL;
vb@1092
   402
    if (src->opt_fields) {
vb@1092
   403
        dst->opt_fields = stringpair_list_dup(src->opt_fields);
vb@1092
   404
        if (dst->opt_fields == NULL)
vb@1092
   405
            return PEP_OUT_OF_MEMORY;
vb@1092
   406
    }
vb@1092
   407
vb@113
   408
    return PEP_STATUS_OK;
vb@113
   409
}
vb@113
   410
vb@81
   411
static message * clone_to_empty_message(const message * src)
vb@80
   412
{
vb@113
   413
    PEP_STATUS status;
vb@80
   414
    message * msg = NULL;
vb@80
   415
vb@81
   416
    assert(src);
krista@853
   417
    if (src == NULL)
krista@853
   418
        return NULL;
vb@81
   419
vb@113
   420
    msg = calloc(1, sizeof(message));
vb@113
   421
    assert(msg);
vb@80
   422
    if (msg == NULL)
vb@80
   423
        goto enomem;
vb@80
   424
vb@82
   425
    msg->dir = src->dir;
vb@82
   426
vb@113
   427
    status = copy_fields(msg, src);
vb@113
   428
    if (status != PEP_STATUS_OK)
vb@113
   429
        goto enomem;
vb@81
   430
vb@80
   431
    return msg;
vb@80
   432
vb@80
   433
enomem:
vb@113
   434
    free_message(msg);
vb@80
   435
    return NULL;
vb@80
   436
}
vb@80
   437
vb@260
   438
static PEP_STATUS encrypt_PGP_MIME(
vb@311
   439
    PEP_SESSION session,
vb@311
   440
    const message *src,
vb@311
   441
    stringlist_t *keys,
krista@1639
   442
    message *dst,
krista@1639
   443
    PEP_encrypt_flags_t flags
vb@260
   444
    )
vb@260
   445
{
vb@260
   446
    PEP_STATUS status = PEP_STATUS_OK;
krista@1987
   447
    bool free_ptext = false;
vb@468
   448
    char *ptext = NULL;
Edouard@882
   449
    char *ctext = NULL;
vb@260
   450
    char *mimetext = NULL;
vb@260
   451
    size_t csize;
vb@260
   452
    assert(dst->longmsg == NULL);
vb@260
   453
    dst->enc_format = PEP_enc_PGP_MIME;
krista@2045
   454
    unsigned char pepstr[] = PEP_SUBJ_STRING;
krista@2045
   455
krista@2045
   456
    if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0 
krista@2045
   457
                      && _unsigned_signed_strcmp(pepstr, src->shortmsg, PEP_SUBJ_BYTELEN) != 0) {
krista@1987
   458
        if (session->unencrypted_subject) {
krista@1987
   459
            dst->shortmsg = strdup(src->shortmsg);
krista@1987
   460
            assert(dst->shortmsg);
krista@1987
   461
            if (dst->shortmsg == NULL)
krista@1987
   462
                goto enomem;
krista@1987
   463
            ptext = src->longmsg;
krista@1987
   464
        }
krista@1987
   465
        else {
krista@1987
   466
            ptext = combine_short_and_long(src->shortmsg, src->longmsg);
krista@1987
   467
            if (ptext == NULL)
krista@1987
   468
                goto enomem;
krista@1987
   469
            free_ptext = true;
krista@1987
   470
        }
vb@260
   471
    }
vb@260
   472
    else if (src->longmsg) {
vb@260
   473
        ptext = src->longmsg;
vb@260
   474
    }
vb@260
   475
    else {
krista@2045
   476
        ptext = (char*)pepstr;
vb@260
   477
    }
vb@260
   478
vb@260
   479
    message *_src = calloc(1, sizeof(message));
vb@260
   480
    assert(_src);
vb@260
   481
    if (_src == NULL)
vb@260
   482
        goto enomem;
vb@260
   483
    _src->longmsg = ptext;
vb@260
   484
    _src->longmsg_formatted = src->longmsg_formatted;
vb@260
   485
    _src->attachments = src->attachments;
vb@260
   486
    _src->enc_format = PEP_enc_none;
vb@260
   487
    status = mime_encode_message(_src, true, &mimetext);
vb@260
   488
    assert(status == PEP_STATUS_OK);
krista@853
   489
    if (status != PEP_STATUS_OK)
krista@853
   490
        goto pep_error;
krista@1427
   491
krista@1987
   492
    if (free_ptext){
krista@1987
   493
        free(ptext);
krista@1987
   494
        free_ptext=0;
krista@1987
   495
    }
vb@260
   496
    free(_src);
vb@260
   497
    assert(mimetext);
vb@260
   498
    if (mimetext == NULL)
vb@260
   499
        goto pep_error;
vb@260
   500
krista@1639
   501
    if (flags & PEP_encrypt_flag_force_unsigned)
krista@1639
   502
        status = encrypt_only(session, keys, mimetext, strlen(mimetext),
krista@1639
   503
            &ctext, &csize);
krista@1639
   504
    else
krista@1639
   505
        status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
krista@1639
   506
            &ctext, &csize);
vb@260
   507
    free(mimetext);
vb@260
   508
    if (ctext == NULL)
vb@260
   509
        goto pep_error;
vb@260
   510
vb@260
   511
    dst->longmsg = strdup("this message was encrypted with p≡p "
lix@487
   512
        "https://pEp-project.org");
vb@469
   513
    assert(dst->longmsg);
vb@260
   514
    if (dst->longmsg == NULL)
vb@260
   515
        goto enomem;
vb@260
   516
vb@260
   517
    char *v = strdup("Version: 1");
vb@469
   518
    assert(v);
vb@260
   519
    if (v == NULL)
vb@260
   520
        goto enomem;
vb@260
   521
roker@801
   522
    bloblist_t *_a = new_bloblist(v, strlen(v), "application/pgp-encrypted", NULL);
vb@260
   523
    if (_a == NULL)
vb@260
   524
        goto enomem;
vb@260
   525
    dst->attachments = _a;
vb@288
   526
Edouard@882
   527
    _a = bloblist_add(_a, ctext, csize, "application/octet-stream",
krista@1873
   528
        "file://msg.asc");
vb@260
   529
    if (_a == NULL)
vb@260
   530
        goto enomem;
vb@260
   531
vb@260
   532
    return PEP_STATUS_OK;
vb@260
   533
vb@260
   534
enomem:
vb@260
   535
    status = PEP_OUT_OF_MEMORY;
vb@260
   536
vb@260
   537
pep_error:
vb@260
   538
    if (free_ptext)
vb@260
   539
        free(ptext);
Edouard@882
   540
    free(ctext);
vb@260
   541
    return status;
vb@260
   542
}
vb@260
   543
vb@260
   544
static PEP_STATUS encrypt_PGP_in_pieces(
vb@311
   545
    PEP_SESSION session,
vb@311
   546
    const message *src,
vb@311
   547
    stringlist_t *keys,
krista@1639
   548
    message *dst,
krista@1639
   549
    PEP_encrypt_flags_t flags
vb@260
   550
    )
vb@260
   551
{
vb@260
   552
    PEP_STATUS status = PEP_STATUS_OK;
Edouard@882
   553
    char *ctext = NULL;
vb@260
   554
    size_t csize;
vb@468
   555
    char *ptext = NULL;
vb@466
   556
    bool free_ptext = false;
krista@2045
   557
    unsigned char pepstr[] = PEP_SUBJ_STRING;
krista@2045
   558
    
vb@260
   559
    assert(dst->longmsg == NULL);
vb@260
   560
    assert(dst->attachments == NULL);
vb@260
   561
vb@260
   562
    dst->enc_format = PEP_enc_pieces;
vb@260
   563
krista@1639
   564
    bool nosign = (flags & PEP_encrypt_flag_force_unsigned);
krista@1639
   565
krista@2045
   566
    if (src->shortmsg && src->shortmsg[0] && strcmp(src->shortmsg, "pEp") != 0 && 
krista@2045
   567
        _unsigned_signed_strcmp(pepstr, src->shortmsg, PEP_SUBJ_BYTELEN) != 0) {
vb@466
   568
        if (session->unencrypted_subject) {
vb@466
   569
            dst->shortmsg = strdup(src->shortmsg);
vb@469
   570
            assert(dst->shortmsg);
vb@466
   571
            if (dst->shortmsg == NULL)
vb@466
   572
                goto enomem;
vb@466
   573
            ptext = src->longmsg;
vb@466
   574
        }
vb@466
   575
        else {
vb@466
   576
            ptext = combine_short_and_long(src->shortmsg, src->longmsg);
vb@466
   577
            if (ptext == NULL)
vb@466
   578
                goto enomem;
vb@466
   579
            free_ptext = true;
vb@466
   580
        }
vb@260
   581
krista@1639
   582
        if (nosign)
krista@1639
   583
            status = encrypt_only(session, keys, ptext, strlen(ptext), &ctext,
krista@1639
   584
                &csize);
krista@1639
   585
        else 
krista@1639
   586
            status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
krista@1639
   587
                &csize);
vb@466
   588
        if (free_ptext)
vb@466
   589
            free(ptext);
vb@466
   590
        free_ptext = false;
vb@288
   591
        if (ctext) {
Edouard@882
   592
            dst->longmsg = ctext;
vb@288
   593
        }
vb@288
   594
        else {
vb@260
   595
            goto pep_error;
vb@288
   596
        }
vb@260
   597
    }
vb@320
   598
    else if (src->longmsg && src->longmsg[0]) {
vb@466
   599
        ptext = src->longmsg;
krista@1639
   600
        if (nosign)
krista@1639
   601
            status = encrypt_only(session, keys, ptext, strlen(ptext), &ctext,
krista@1639
   602
                &csize);
krista@1639
   603
        else 
krista@1639
   604
            status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
krista@1639
   605
                &csize);
vb@288
   606
        if (ctext) {
Edouard@882
   607
            dst->longmsg = ctext;
vb@288
   608
        }
vb@288
   609
        else {
vb@260
   610
            goto pep_error;
vb@288
   611
        }
vb@284
   612
    }
vb@284
   613
    else {
vb@284
   614
        dst->longmsg = strdup("");
vb@469
   615
        assert(dst->longmsg);
vb@284
   616
        if (dst->longmsg == NULL)
vb@284
   617
            goto enomem;
vb@260
   618
    }
vb@260
   619
vb@320
   620
    if (src->longmsg_formatted && src->longmsg_formatted[0]) {
vb@466
   621
        ptext = src->longmsg_formatted;
krista@1639
   622
        if (nosign)
krista@1639
   623
            status = encrypt_only(session, keys, ptext, strlen(ptext), &ctext,
krista@1639
   624
                &csize);
krista@1639
   625
        else 
krista@1639
   626
            status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
krista@1639
   627
                &csize);
vb@260
   628
        if (ctext) {
vb@288
   629
Edouard@882
   630
            bloblist_t *_a = bloblist_add(dst->attachments, ctext, csize,
krista@1873
   631
                "application/octet-stream", "file://PGPexch.htm.pgp");
vb@284
   632
            if (_a == NULL)
vb@260
   633
                goto enomem;
vb@284
   634
            if (dst->attachments == NULL)
vb@284
   635
                dst->attachments = _a;
vb@260
   636
        }
vb@260
   637
        else {
vb@260
   638
            goto pep_error;
vb@260
   639
        }
vb@260
   640
    }
vb@260
   641
vb@260
   642
    if (src->attachments) {
vb@284
   643
        if (dst->attachments == NULL) {
vb@284
   644
            dst->attachments = new_bloblist(NULL, 0, NULL, NULL);
vb@284
   645
            if (dst->attachments == NULL)
vb@284
   646
                goto enomem;
vb@284
   647
        }
vb@260
   648
vb@284
   649
        bloblist_t *_s = src->attachments;
vb@284
   650
        bloblist_t *_d = dst->attachments;
vb@284
   651
Edouard@754
   652
        for (int n = 0; _s; _s = _s->next) {
Edouard@754
   653
            if (_s->value == NULL && _s->size == 0) {
Edouard@754
   654
                _d = bloblist_add(_d, NULL, 0, _s->mime_type, _s->filename);
vb@260
   655
                if (_d == NULL)
vb@260
   656
                    goto enomem;
vb@260
   657
            }
vb@260
   658
            else {
Edouard@754
   659
                size_t psize = _s->size;
Edouard@754
   660
                ptext = _s->value;
krista@1639
   661
                if (nosign)
krista@1639
   662
                    status = encrypt_only(session, keys, ptext, psize, &ctext,
krista@1639
   663
                        &csize);
krista@1639
   664
                else 
krista@1639
   665
                    status = encrypt_and_sign(session, keys, ptext, psize, &ctext,
krista@1639
   666
                        &csize);
Edouard@754
   667
                if (ctext) {
Edouard@754
   668
                    char *filename = NULL;
Edouard@754
   669
krista@1873
   670
                    char *attach_fn = _s->filename;
krista@1873
   671
                    if (attach_fn && !is_cid_uri(attach_fn)) {
Edouard@754
   672
                        size_t len = strlen(_s->filename);
krista@975
   673
                        size_t bufsize = len + 5; // length of .pgp extension + NUL
krista@1873
   674
                        bool already_uri = false;
krista@1873
   675
                        if (is_file_uri(attach_fn))
krista@1873
   676
                            already_uri = true;
krista@1873
   677
                        else
krista@1873
   678
                            bufsize += 7; // length of file://
krista@1873
   679
                            
krista@975
   680
                        filename = calloc(1, bufsize);
Edouard@754
   681
                        if (filename == NULL)
Edouard@754
   682
                            goto enomem;
Edouard@754
   683
krista@1873
   684
                        if (!already_uri)
krista@1873
   685
                            strlcpy(filename, "file://", bufsize);
krista@1873
   686
                        // First char is NUL, so we're ok, even if not copying above. (calloc)
krista@1873
   687
                        strlcat(filename, _s->filename, bufsize);
krista@975
   688
                        strlcat(filename, ".pgp", bufsize);
Edouard@754
   689
                    }
Edouard@754
   690
                    else {
krista@1873
   691
                        filename = calloc(1, 27);
Edouard@754
   692
                        if (filename == NULL)
Edouard@754
   693
                            goto enomem;
Edouard@754
   694
Edouard@754
   695
                        ++n;
Edouard@754
   696
                        n &= 0xffff;
krista@1873
   697
                        snprintf(filename, 20, "file://Attachment%d.pgp", n);
Edouard@754
   698
                    }
Edouard@754
   699
Edouard@882
   700
                    _d = bloblist_add(_d, ctext, csize, "application/octet-stream",
Edouard@754
   701
                        filename);
roker@866
   702
                    free(filename);
Edouard@754
   703
                    if (_d == NULL)
Edouard@754
   704
                        goto enomem;
Edouard@754
   705
                }
Edouard@754
   706
                else {
Edouard@754
   707
                    goto pep_error;
Edouard@754
   708
                }
vb@260
   709
            }
vb@260
   710
        }
vb@260
   711
    }
vb@260
   712
vb@260
   713
    return PEP_STATUS_OK;
vb@260
   714
vb@260
   715
enomem:
vb@260
   716
    status = PEP_OUT_OF_MEMORY;
vb@260
   717
vb@260
   718
pep_error:
vb@466
   719
    if (free_ptext)
vb@466
   720
        free(ptext);
vb@260
   721
    return status;
vb@260
   722
}
vb@260
   723
vb@311
   724
static char * keylist_to_string(const stringlist_t *keylist)
vb@311
   725
{
vb@311
   726
    if (keylist) {
vb@311
   727
        size_t size = stringlist_length(keylist);
vb@311
   728
vb@311
   729
        const stringlist_t *_kl;
vb@311
   730
        for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
vb@311
   731
            size += strlen(_kl->value);
vb@311
   732
        }
vb@311
   733
vb@311
   734
        char *result = calloc(1, size);
vb@311
   735
        if (result == NULL)
vb@311
   736
            return NULL;
vb@311
   737
vb@311
   738
        char *_r = result;
vb@311
   739
        for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
vb@311
   740
            _r = stpcpy(_r, _kl->value);
vb@311
   741
            if (_kl->next && _kl->next->value)
vb@311
   742
                _r = stpcpy(_r, ",");
vb@311
   743
        }
vb@311
   744
vb@311
   745
        return result;
vb@311
   746
    }
vb@311
   747
    else {
vb@311
   748
        return NULL;
vb@311
   749
    }
vb@311
   750
}
vb@311
   751
vb@1004
   752
static const char * rating_to_string(PEP_rating rating)
vb@311
   753
{
vb@1004
   754
    switch (rating) {
vb@311
   755
    case PEP_rating_cannot_decrypt:
vb@311
   756
        return "cannot_decrypt";
vb@311
   757
    case PEP_rating_have_no_key:
vb@311
   758
        return "have_no_key";
vb@311
   759
    case PEP_rating_unencrypted:
vb@311
   760
        return "unencrypted";
vb@486
   761
    case PEP_rating_unencrypted_for_some:
vb@486
   762
        return "unencrypted_for_some";
vb@311
   763
    case PEP_rating_unreliable:
vb@311
   764
        return "unreliable";
vb@311
   765
    case PEP_rating_reliable:
vb@311
   766
        return "reliable";
vb@311
   767
    case PEP_rating_trusted:
vb@311
   768
        return "trusted";
vb@311
   769
    case PEP_rating_trusted_and_anonymized:
vb@311
   770
        return "trusted_and_anonymized";
vb@311
   771
    case PEP_rating_fully_anonymous:
vb@311
   772
        return "fully_anonymous";
Edouard@442
   773
    case PEP_rating_mistrust:
Edouard@442
   774
        return "mistrust";
Edouard@442
   775
    case PEP_rating_b0rken:
Edouard@442
   776
        return "b0rken";
vb@311
   777
    case PEP_rating_under_attack:
krista@961
   778
        return "under_attack";
vb@311
   779
    default:
vb@311
   780
        return "undefined";
vb@311
   781
    }
vb@311
   782
}
vb@311
   783
vb@311
   784
static void decorate_message(
vb@311
   785
    message *msg,
vb@1004
   786
    PEP_rating rating,
vb@311
   787
    stringlist_t *keylist
vb@311
   788
    )
vb@311
   789
{
vb@311
   790
    assert(msg);
vb@311
   791
krista@942
   792
    add_opt_field(msg, "X-pEp-Version", PEP_VERSION);
krista@1427
   793
vb@1004
   794
    if (rating != PEP_rating_undefined)
vb@1004
   795
        add_opt_field(msg, "X-EncStatus", rating_to_string(rating));
vb@311
   796
vb@311
   797
    if (keylist) {
vb@311
   798
        char *_keylist = keylist_to_string(keylist);
vb@311
   799
        add_opt_field(msg, "X-KeyList", _keylist);
vb@311
   800
        free(_keylist);
vb@311
   801
    }
vb@311
   802
}
vb@311
   803
vb@1004
   804
static PEP_rating _rating(PEP_comm_type ct, PEP_rating rating)
vb@311
   805
{
vb@311
   806
    if (ct == PEP_ct_unknown)
vb@311
   807
        return PEP_rating_undefined;
krista@1427
   808
krista@1243
   809
    else if (ct == PEP_ct_key_not_found)
krista@1243
   810
        return PEP_rating_have_no_key;
krista@1427
   811
vb@311
   812
    else if (ct == PEP_ct_compromized)
vb@311
   813
        return PEP_rating_under_attack;
vb@311
   814
Edouard@442
   815
    else if (ct == PEP_ct_mistrusted)
Edouard@442
   816
        return PEP_rating_mistrust;
krista@1427
   817
vb@1004
   818
    if (rating == PEP_rating_unencrypted_for_some)
vb@486
   819
        return PEP_rating_unencrypted_for_some;
vb@486
   820
vb@486
   821
    if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel ||
vb@486
   822
            ct == PEP_ct_my_key_not_included) {
vb@1004
   823
        if (rating > PEP_rating_unencrypted_for_some)
vb@486
   824
            return PEP_rating_unencrypted_for_some;
vb@486
   825
        else
vb@486
   826
            return PEP_rating_unencrypted;
vb@486
   827
    }
vb@486
   828
vb@1004
   829
    if (rating == PEP_rating_unencrypted)
vb@486
   830
        return PEP_rating_unencrypted_for_some;
vb@486
   831
vb@486
   832
    if (ct >= PEP_ct_confirmed_enc_anon)
vb@311
   833
        return PEP_rating_trusted_and_anonymized;
vb@311
   834
vb@311
   835
    else if (ct >= PEP_ct_strong_encryption)
vb@311
   836
        return PEP_rating_trusted;
vb@311
   837
vb@311
   838
    else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
vb@311
   839
        return PEP_rating_reliable;
vb@311
   840
vb@311
   841
    else
vb@311
   842
        return PEP_rating_unreliable;
vb@311
   843
}
vb@311
   844
vb@311
   845
static bool is_encrypted_attachment(const bloblist_t *blob)
vb@311
   846
{
vb@311
   847
    assert(blob);
vb@311
   848
krista@1873
   849
    if (blob == NULL || blob->filename == NULL || is_cid_uri(blob->filename))
vb@311
   850
        return false;
krista@1427
   851
roker@1559
   852
    char *ext = strrchr(blob->filename, '.');
vb@311
   853
    if (ext == NULL)
vb@311
   854
        return false;
vb@311
   855
vb@320
   856
    if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
vb@311
   857
        if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
vb@311
   858
            strcmp(ext, ".asc") == 0)
vb@311
   859
            return true;
vb@311
   860
    }
vb@320
   861
    else if (strcmp(blob->mime_type, "text/plain") == 0) {
vb@311
   862
        if (strcmp(ext, ".asc") == 0)
vb@311
   863
            return true;
vb@311
   864
    }
vb@311
   865
vb@311
   866
    return false;
vb@311
   867
}
vb@311
   868
vb@311
   869
static bool is_encrypted_html_attachment(const bloblist_t *blob)
vb@311
   870
{
vb@311
   871
    assert(blob);
vb@311
   872
    assert(blob->filename);
krista@1873
   873
    if (blob == NULL || blob->filename == NULL || is_cid_uri(blob->filename))
krista@853
   874
        return false;
vb@311
   875
krista@1873
   876
    const char* bare_filename_ptr = _get_resource_ptr_noown(blob->filename);
krista@1873
   877
    if (strncmp(bare_filename_ptr, "PGPexch.htm.", 12) == 0) {
krista@1873
   878
        if (strcmp(bare_filename_ptr + 11, ".pgp") == 0 ||
krista@1873
   879
            strcmp(bare_filename_ptr + 11, ".asc") == 0)
vb@311
   880
            return true;
vb@311
   881
    }
vb@311
   882
vb@311
   883
    return false;
vb@311
   884
}
vb@311
   885
vb@311
   886
static char * without_double_ending(const char *filename)
vb@311
   887
{
vb@311
   888
    assert(filename);
krista@1873
   889
    if (filename == NULL || is_cid_uri(filename))
krista@853
   890
        return NULL;
krista@1427
   891
roker@862
   892
    char *ext = strrchr(filename, '.');
vb@311
   893
    if (ext == NULL)
vb@311
   894
        return NULL;
vb@311
   895
vb@469
   896
    char *result = strndup(filename, ext - filename);
vb@469
   897
    assert(result);
vb@469
   898
    return result;
vb@311
   899
}
vb@311
   900
vb@1004
   901
static PEP_rating decrypt_rating(PEP_STATUS status)
vb@311
   902
{
vb@311
   903
    switch (status) {
vb@311
   904
    case PEP_UNENCRYPTED:
vb@311
   905
    case PEP_VERIFIED:
vb@311
   906
    case PEP_VERIFY_NO_KEY:
vb@311
   907
    case PEP_VERIFIED_AND_TRUSTED:
vb@311
   908
        return PEP_rating_unencrypted;
vb@311
   909
vb@311
   910
    case PEP_DECRYPTED:
krista@1748
   911
    case PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH:
vb@311
   912
        return PEP_rating_unreliable;
vb@311
   913
vb@311
   914
    case PEP_DECRYPTED_AND_VERIFIED:
vb@311
   915
        return PEP_rating_reliable;
vb@311
   916
vb@311
   917
    case PEP_DECRYPT_NO_KEY:
vb@311
   918
        return PEP_rating_have_no_key;
vb@311
   919
vb@311
   920
    case PEP_DECRYPT_WRONG_FORMAT:
vb@311
   921
    case PEP_CANNOT_DECRYPT_UNKNOWN:
vb@311
   922
        return PEP_rating_cannot_decrypt;
vb@311
   923
vb@311
   924
    default:
vb@311
   925
        return PEP_rating_undefined;
vb@311
   926
    }
vb@311
   927
}
vb@311
   928
vb@1004
   929
static PEP_rating key_rating(PEP_SESSION session, const char *fpr)
vb@311
   930
{
vb@311
   931
vb@311
   932
    assert(session);
vb@311
   933
    assert(fpr);
krista@1427
   934
krista@853
   935
    if (session == NULL || fpr == NULL)
krista@853
   936
        return PEP_rating_undefined;
vb@311
   937
edouard@1632
   938
edouard@1632
   939
    PEP_comm_type bare_comm_type = PEP_ct_unknown;
edouard@1876
   940
    PEP_comm_type resulting_comm_type = PEP_ct_unknown;
krista@1615
   941
    PEP_STATUS status = get_key_rating(session, fpr, &bare_comm_type);
vb@311
   942
    if (status != PEP_STATUS_OK)
vb@311
   943
        return PEP_rating_undefined;
vb@311
   944
edouard@1876
   945
    PEP_comm_type least_comm_type = PEP_ct_unknown;
edouard@1876
   946
    least_trust(session, fpr, &least_comm_type);
edouard@1876
   947
edouard@1876
   948
    if (least_comm_type == PEP_ct_unknown) {
edouard@1876
   949
        resulting_comm_type = bare_comm_type;
edouard@1876
   950
    } else if (least_comm_type < PEP_ct_strong_but_unconfirmed ||
edouard@1876
   951
               bare_comm_type < PEP_ct_strong_but_unconfirmed) {
edouard@1876
   952
        // take minimum if anything bad
edouard@1876
   953
        resulting_comm_type = least_comm_type < bare_comm_type ? 
edouard@1876
   954
                              least_comm_type : 
edouard@1876
   955
                              bare_comm_type;
edouard@1632
   956
    } else {
edouard@1876
   957
        resulting_comm_type = least_comm_type;
edouard@1632
   958
    }
edouard@1876
   959
    return _rating(resulting_comm_type, PEP_rating_undefined);
vb@311
   960
}
vb@311
   961
krista@1579
   962
static PEP_rating worst_rating(PEP_rating rating1, PEP_rating rating2) {
krista@1579
   963
    return ((rating1 < rating2) ? rating1 : rating2);
krista@1579
   964
}
krista@1579
   965
edouard@1876
   966
static PEP_rating keylist_rating(PEP_SESSION session, stringlist_t *keylist, char* sender_fpr, PEP_rating sender_rating)
vb@311
   967
{
edouard@1876
   968
    PEP_rating rating = sender_rating;
vb@311
   969
vb@311
   970
    assert(keylist && keylist->value);
vb@311
   971
    if (keylist == NULL || keylist->value == NULL)
vb@486
   972
        return PEP_rating_undefined;
vb@311
   973
vb@311
   974
    stringlist_t *_kl;
vb@311
   975
    for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
edouard@1876
   976
edouard@1876
   977
        // Ignore own fpr
edouard@1876
   978
        if(_same_fpr(sender_fpr, strlen(sender_fpr), _kl->value, strlen(_kl->value)))
edouard@1876
   979
            continue;
vb@311
   980
vb@1004
   981
        PEP_rating _rating_ = key_rating(session, _kl->value);
krista@1579
   982
         
vb@1004
   983
        if (_rating_ <= PEP_rating_mistrust)
vb@1004
   984
            return _rating_;
krista@1610
   985
            
edouard@1876
   986
        if (_rating_ == PEP_rating_unencrypted)
edouard@1876
   987
        {
vb@1004
   988
            if (rating > PEP_rating_unencrypted_for_some)
krista@1579
   989
                rating = worst_rating(rating, PEP_rating_unencrypted_for_some);
vb@311
   990
        }
edouard@1876
   991
        else
edouard@1876
   992
        {
edouard@1876
   993
            rating = worst_rating(rating, _rating_);
edouard@1876
   994
        }
vb@311
   995
    }
vb@311
   996
vb@1004
   997
    return rating;
vb@311
   998
}
vb@311
   999
vb@311
  1000
static PEP_comm_type _get_comm_type(
vb@311
  1001
    PEP_SESSION session,
vb@311
  1002
    PEP_comm_type max_comm_type,
vb@311
  1003
    pEp_identity *ident
vb@311
  1004
    )
vb@311
  1005
{
vb@311
  1006
    PEP_STATUS status = update_identity(session, ident);
vb@311
  1007
vb@311
  1008
    if (max_comm_type == PEP_ct_compromized)
vb@311
  1009
        return PEP_ct_compromized;
krista@1427
  1010
Edouard@510
  1011
    if (max_comm_type == PEP_ct_mistrusted)
Edouard@510
  1012
        return PEP_ct_mistrusted;
vb@311
  1013
vb@311
  1014
    if (status == PEP_STATUS_OK) {
vb@311
  1015
        if (ident->comm_type == PEP_ct_compromized)
vb@311
  1016
            return PEP_ct_compromized;
Edouard@510
  1017
        else if (ident->comm_type == PEP_ct_mistrusted)
Edouard@510
  1018
            return PEP_ct_mistrusted;
vb@311
  1019
        else
vb@951
  1020
            return _MIN(max_comm_type, ident->comm_type);
vb@311
  1021
    }
vb@311
  1022
    else {
vb@311
  1023
        return PEP_ct_unknown;
vb@311
  1024
    }
vb@311
  1025
}
vb@311
  1026
vb@731
  1027
static void free_bl_entry(bloblist_t *bl)
vb@731
  1028
{
vb@731
  1029
    if (bl) {
vb@731
  1030
        free(bl->value);
vb@731
  1031
        free(bl->mime_type);
vb@731
  1032
        free(bl->filename);
vb@731
  1033
        free(bl);
vb@731
  1034
    }
vb@731
  1035
}
vb@731
  1036
vb@731
  1037
static bool is_key(const bloblist_t *bl)
vb@731
  1038
{
Edouard@728
  1039
    return (// workaround for Apple Mail bugs
Edouard@728
  1040
            (is_mime_type(bl, "application/x-apple-msg-attachment") &&
Edouard@728
  1041
             is_fileending(bl, ".asc")) ||
Edouard@728
  1042
            // as binary, by file name
Edouard@728
  1043
            ((bl->mime_type == NULL ||
Edouard@728
  1044
              is_mime_type(bl, "application/octet-stream")) &&
Edouard@728
  1045
             (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
Edouard@728
  1046
                    is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
krista@1427
  1047
            // explicit mime type
Edouard@728
  1048
            is_mime_type(bl, "application/pgp-keys") ||
Edouard@728
  1049
            // as text, by file name
Edouard@728
  1050
            (is_mime_type(bl, "text/plain") &&
Edouard@728
  1051
             (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
Edouard@728
  1052
                    is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
Edouard@728
  1053
           );
vb@731
  1054
}
vb@731
  1055
vb@731
  1056
static void remove_attached_keys(message *msg)
vb@731
  1057
{
vb@731
  1058
    if (msg) {
vb@731
  1059
        bloblist_t *last = NULL;
vb@731
  1060
        for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
vb@731
  1061
            bloblist_t *next = bl->next;
vb@731
  1062
vb@731
  1063
            if (is_key(bl)) {
vb@731
  1064
                if (last) {
vb@731
  1065
                    last->next = next;
vb@731
  1066
                }
vb@731
  1067
                else {
vb@731
  1068
                    msg->attachments = next;
vb@731
  1069
                }
vb@731
  1070
                free_bl_entry(bl);
vb@731
  1071
            }
vb@731
  1072
            else {
vb@731
  1073
                last = bl;
vb@731
  1074
            }
vb@731
  1075
            bl = next;
vb@731
  1076
        }
vb@731
  1077
    }
vb@731
  1078
}
vb@731
  1079
Edouard@734
  1080
bool import_attached_keys(
krista@1427
  1081
        PEP_SESSION session,
Edouard@728
  1082
        const message *msg,
Edouard@728
  1083
        identity_list **private_idents
Edouard@728
  1084
    )
vb@311
  1085
{
vb@311
  1086
    assert(session);
vb@311
  1087
    assert(msg);
krista@1427
  1088
krista@853
  1089
    if (session == NULL || msg == NULL)
krista@853
  1090
        return false;
vb@311
  1091
vb@731
  1092
    bool remove = false;
vb@731
  1093
vb@908
  1094
    int i = 0;
roker@1559
  1095
    for (bloblist_t *bl = msg->attachments; i < MAX_KEYS_TO_IMPORT && bl && bl->value;
krista@1427
  1096
            bl = bl->next, i++)
Edouard@747
  1097
    {
vb@908
  1098
        if (bl && bl->value && bl->size && bl->size < MAX_KEY_SIZE
krista@1427
  1099
                && is_key(bl))
Edouard@728
  1100
        {
edouard@1745
  1101
            identity_list *local_private_idents = NULL;
edouard@1745
  1102
            import_key(session, bl->value, bl->size, &local_private_idents);
vb@731
  1103
            remove = true;
edouard@1863
  1104
            if (private_idents && *private_idents == NULL && local_private_idents != NULL)
edouard@1745
  1105
                *private_idents = local_private_idents;
edouard@1745
  1106
            else
edouard@1745
  1107
                free_identity_list(local_private_idents);
vb@311
  1108
        }
vb@311
  1109
    }
vb@731
  1110
    return remove;
vb@311
  1111
}
vb@311
  1112
Edouard@694
  1113
Edouard@694
  1114
PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
vb@311
  1115
{
roker@1559
  1116
    char *keydata = NULL;
vb@311
  1117
    size_t size;
vb@311
  1118
Edouard@694
  1119
    PEP_STATUS status = export_key(session, fpr, &keydata, &size);
Edouard@694
  1120
    assert(status == PEP_STATUS_OK);
Edouard@694
  1121
    if (status != PEP_STATUS_OK)
Edouard@694
  1122
        return status;
Edouard@694
  1123
    assert(size);
krista@1427
  1124
roker@1559
  1125
     bloblist_t *bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
krista@1873
  1126
                      "file://pEpkey.asc");
krista@1427
  1127
Edouard@694
  1128
    if (msg->attachments == NULL && bl)
Edouard@694
  1129
        msg->attachments = bl;
Edouard@694
  1130
Edouard@694
  1131
    return PEP_STATUS_OK;
Edouard@694
  1132
}
Edouard@694
  1133
Edouard@694
  1134
#define ONE_WEEK (7*24*3600)
Edouard@694
  1135
Edouard@694
  1136
void attach_own_key(PEP_SESSION session, message *msg)
Edouard@694
  1137
{
vb@311
  1138
    assert(session);
vb@311
  1139
    assert(msg);
krista@1427
  1140
Edouard@558
  1141
    if (msg->dir == PEP_dir_incoming)
Edouard@558
  1142
        return;
Edouard@558
  1143
vb@311
  1144
    assert(msg->from && msg->from->fpr);
vb@311
  1145
    if (msg->from == NULL || msg->from->fpr == NULL)
vb@311
  1146
        return;
vb@311
  1147
Edouard@694
  1148
    if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
vb@311
  1149
        return;
krista@1427
  1150
Edouard@694
  1151
    char *revoked_fpr = NULL;
Edouard@694
  1152
    uint64_t revocation_date = 0;
krista@1427
  1153
Edouard@694
  1154
    if(get_revoked(session, msg->from->fpr,
Edouard@694
  1155
                   &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
Edouard@694
  1156
       revoked_fpr != NULL)
Edouard@694
  1157
    {
Edouard@694
  1158
        time_t now = time(NULL);
krista@1427
  1159
Edouard@694
  1160
        if (now < (time_t)revocation_date + ONE_WEEK)
Edouard@694
  1161
        {
Edouard@694
  1162
            _attach_key(session, revoked_fpr, msg);
Edouard@694
  1163
        }
Edouard@694
  1164
    }
Edouard@694
  1165
    free(revoked_fpr);
vb@311
  1166
}
vb@311
  1167
vb@311
  1168
PEP_cryptotech determine_encryption_format(message *msg)
vb@311
  1169
{
vb@311
  1170
    assert(msg);
krista@1427
  1171
vb@311
  1172
    if (is_PGP_message_text(msg->longmsg)) {
vb@311
  1173
        msg->enc_format = PEP_enc_pieces;
vb@311
  1174
        return PEP_crypt_OpenPGP;
vb@311
  1175
    }
vb@311
  1176
    else if (msg->attachments && msg->attachments->next &&
vb@311
  1177
            is_mime_type(msg->attachments, "application/pgp-encrypted") &&
vb@311
  1178
            is_PGP_message_text(msg->attachments->next->value)
vb@311
  1179
        ) {
vb@311
  1180
        msg->enc_format = PEP_enc_PGP_MIME;
vb@311
  1181
        return PEP_crypt_OpenPGP;
vb@311
  1182
    }
edouard@1557
  1183
    else if (msg->attachments && msg->attachments->next &&
edouard@1557
  1184
            is_mime_type(msg->attachments->next, "application/pgp-encrypted") &&
edouard@1557
  1185
            is_PGP_message_text(msg->attachments->value)
edouard@1557
  1186
        ) {
edouard@1557
  1187
        msg->enc_format = PEP_enc_PGP_MIME_Outlook1;
edouard@1557
  1188
        return PEP_crypt_OpenPGP;
edouard@1557
  1189
    }
vb@311
  1190
    else {
vb@311
  1191
        msg->enc_format = PEP_enc_none;
vb@311
  1192
        return PEP_crypt_none;
vb@311
  1193
    }
vb@311
  1194
}
vb@311
  1195
vb@48
  1196
DYNAMIC_API PEP_STATUS encrypt_message(
vb@37
  1197
        PEP_SESSION session,
vb@113
  1198
        message *src,
vb@37
  1199
        stringlist_t * extra,
vb@38
  1200
        message **dst,
vb@939
  1201
        PEP_enc_format enc_format,
vb@939
  1202
        PEP_encrypt_flags_t flags
vb@37
  1203
    )
vb@37
  1204
{
vb@37
  1205
    PEP_STATUS status = PEP_STATUS_OK;
vb@63
  1206
    message * msg = NULL;
vb@63
  1207
    stringlist_t * keys = NULL;
krista@1987
  1208
vb@37
  1209
    assert(session);
vb@37
  1210
    assert(src);
vb@37
  1211
    assert(dst);
Edouard@558
  1212
    assert(enc_format != PEP_enc_none);
vb@81
  1213
Edouard@558
  1214
    if (!(session && src && dst && enc_format != PEP_enc_none))
roker@1853
  1215
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
vb@191
  1216
Edouard@550
  1217
    if (src->dir == PEP_dir_incoming)
roker@1853
  1218
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
krista@1427
  1219
vb@259
  1220
    determine_encryption_format(src);
vb@260
  1221
    if (src->enc_format != PEP_enc_none)
roker@1853
  1222
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
vb@259
  1223
vb@37
  1224
    *dst = NULL;
vb@67
  1225
vb@236
  1226
    status = myself(session, src->from);
vb@236
  1227
    if (status != PEP_STATUS_OK)
roker@1734
  1228
        GOTO(pep_error);
Edouard@558
  1229
vb@80
  1230
    keys = new_stringlist(src->from->fpr);
vb@63
  1231
    if (keys == NULL)
vb@63
  1232
        goto enomem;
vb@37
  1233
vb@39
  1234
    stringlist_t *_k = keys;
vb@39
  1235
vb@39
  1236
    if (extra) {
vb@39
  1237
        _k = stringlist_append(_k, extra);
vb@63
  1238
        if (_k == NULL)
vb@63
  1239
            goto enomem;
vb@37
  1240
    }
vb@39
  1241
vb@299
  1242
    bool dest_keys_found = true;
Edouard@510
  1243
    PEP_comm_type max_comm_type = PEP_ct_pEp;
vb@299
  1244
vb@37
  1245
    identity_list * _il;
krista@1427
  1246
Edouard@545
  1247
    if ((_il = src->bcc) && _il->ident)
Edouard@545
  1248
    {
Edouard@545
  1249
        // BCC limited support:
Edouard@545
  1250
        //     - App splits mails with BCC in multiple mails.
Edouard@545
  1251
        //     - Each email is encrypted separately
krista@1427
  1252
Edouard@789
  1253
        if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
Edouard@545
  1254
        {
Edouard@545
  1255
            // Only one Bcc with no other recipient allowed for now
Edouard@545
  1256
            return PEP_ILLEGAL_VALUE;
Edouard@545
  1257
        }
krista@1427
  1258
vb@299
  1259
        PEP_STATUS _status = update_identity(session, _il->ident);
vb@299
  1260
        if (_status != PEP_STATUS_OK) {
vb@299
  1261
            status = _status;
roker@1734
  1262
            GOTO(pep_error);
vb@299
  1263
        }
krista@1427
  1264
vb@299
  1265
        if (_il->ident->fpr && _il->ident->fpr[0]) {
vb@39
  1266
            _k = stringlist_add(_k, _il->ident->fpr);
vb@63
  1267
            if (_k == NULL)
vb@63
  1268
                goto enomem;
Edouard@510
  1269
            max_comm_type = _get_comm_type(session, max_comm_type,
Edouard@510
  1270
                                           _il->ident);
vb@37
  1271
        }
vb@299
  1272
        else {
vb@299
  1273
            dest_keys_found = false;
vb@37
  1274
            status = PEP_KEY_NOT_FOUND;
krista@1427
  1275
        }
vb@299
  1276
    }
Edouard@545
  1277
    else
Edouard@545
  1278
    {
Edouard@545
  1279
        for (_il = src->to; _il && _il->ident; _il = _il->next) {
Edouard@545
  1280
            PEP_STATUS _status = update_identity(session, _il->ident);
Edouard@545
  1281
            if (_status != PEP_STATUS_OK) {
Edouard@545
  1282
                status = _status;
roker@1734
  1283
                GOTO(pep_error);
Edouard@545
  1284
            }
vb@299
  1285
Edouard@545
  1286
            if (_il->ident->fpr && _il->ident->fpr[0]) {
Edouard@545
  1287
                _k = stringlist_add(_k, _il->ident->fpr);
Edouard@545
  1288
                if (_k == NULL)
Edouard@545
  1289
                    goto enomem;
Edouard@545
  1290
                max_comm_type = _get_comm_type(session, max_comm_type,
Edouard@545
  1291
                                               _il->ident);
Edouard@545
  1292
            }
Edouard@545
  1293
            else {
Edouard@545
  1294
                dest_keys_found = false;
Edouard@545
  1295
                status = PEP_KEY_NOT_FOUND;
Edouard@545
  1296
            }
vb@299
  1297
        }
vb@299
  1298
Edouard@545
  1299
        for (_il = src->cc; _il && _il->ident; _il = _il->next) {
Edouard@545
  1300
            PEP_STATUS _status = update_identity(session, _il->ident);
Edouard@545
  1301
            if (_status != PEP_STATUS_OK)
Edouard@545
  1302
            {
Edouard@545
  1303
                status = _status;
roker@1734
  1304
                GOTO(pep_error);
Edouard@545
  1305
            }
Edouard@545
  1306
Edouard@545
  1307
            if (_il->ident->fpr && _il->ident->fpr[0]) {
Edouard@545
  1308
                _k = stringlist_add(_k, _il->ident->fpr);
Edouard@545
  1309
                if (_k == NULL)
Edouard@545
  1310
                    goto enomem;
Edouard@545
  1311
                max_comm_type = _get_comm_type(session, max_comm_type,
Edouard@545
  1312
                                               _il->ident);
Edouard@545
  1313
            }
Edouard@545
  1314
            else {
Edouard@545
  1315
                dest_keys_found = false;
Edouard@545
  1316
                status = PEP_KEY_NOT_FOUND;
Edouard@545
  1317
            }
vb@299
  1318
        }
vb@37
  1319
    }
krista@1427
  1320
Edouard@510
  1321
    if (!dest_keys_found ||
krista@1640
  1322
        stringlist_length(keys)  == 0 ||
Edouard@510
  1323
        _rating(max_comm_type,
Edouard@510
  1324
                PEP_rating_undefined) < PEP_rating_reliable)
Edouard@510
  1325
    {
vb@301
  1326
        free_stringlist(keys);
edouard@1899
  1327
        if (!session->passive_mode && 
edouard@1899
  1328
            !(flags & PEP_encrypt_flag_force_no_attached_key)) {
vb@465
  1329
            attach_own_key(session, src);
edouard@1899
  1330
            decorate_message(src, PEP_rating_undefined, NULL);
edouard@1899
  1331
        }
roker@1853
  1332
        return ADD_TO_LOG(PEP_UNENCRYPTED);
vb@301
  1333
    }
vb@301
  1334
    else {
krista@2035
  1335
        msg = clone_to_empty_message(src);
vb@299
  1336
        if (msg == NULL)
vb@299
  1337
            goto enomem;
vb@299
  1338
krista@1640
  1339
        if (!(flags & PEP_encrypt_flag_force_no_attached_key))
krista@2035
  1340
            attach_own_key(session, src);
Edouard@428
  1341
vb@81
  1342
        switch (enc_format) {
vb@260
  1343
        case PEP_enc_PGP_MIME:
vb@336
  1344
        case PEP_enc_PEP: // BUG: should be implemented extra
krista@2035
  1345
            status = encrypt_PGP_MIME(session, src, keys, msg, flags);
vb@260
  1346
            break;
vb@62
  1347
vb@62
  1348
        case PEP_enc_pieces:
krista@2035
  1349
            status = encrypt_PGP_in_pieces(session, src, keys, msg, flags);
vb@38
  1350
            break;
vb@38
  1351
vb@336
  1352
        /* case PEP_enc_PEP:
vb@81
  1353
            // TODO: implement
vb@336
  1354
            NOT_IMPLEMENTED */
vb@81
  1355
vb@38
  1356
        default:
vb@38
  1357
            assert(0);
vb@63
  1358
            status = PEP_ILLEGAL_VALUE;
roker@1734
  1359
            GOTO(pep_error);
vb@37
  1360
        }
krista@1427
  1361
Edouard@392
  1362
        if (status == PEP_OUT_OF_MEMORY)
Edouard@392
  1363
            goto enomem;
krista@1427
  1364
vb@480
  1365
        if (status != PEP_STATUS_OK)
roker@1734
  1366
            GOTO(pep_error);
vb@37
  1367
    }
vb@37
  1368
vb@37
  1369
    free_stringlist(keys);
vb@63
  1370
vb@469
  1371
    if (msg && msg->shortmsg == NULL) {
krista@2045
  1372
        msg->shortmsg = _pep_subj_copy();
vb@469
  1373
        assert(msg->shortmsg);
krista@853
  1374
        if (msg->shortmsg == NULL)
krista@853
  1375
            goto enomem;
vb@469
  1376
    }
vb@64
  1377
edouard@1542
  1378
    if (msg) {
vb@311
  1379
        decorate_message(msg, PEP_rating_undefined, NULL);
krista@2035
  1380
        if (src->id) {
krista@2035
  1381
            msg->id = strdup(src->id);
edouard@1542
  1382
            assert(msg->id);
edouard@1542
  1383
            if (msg->id == NULL)
edouard@1542
  1384
                goto enomem;
edouard@1542
  1385
        }
edouard@1542
  1386
    }
vb@311
  1387
vb@63
  1388
    *dst = msg;
roker@1853
  1389
    return ADD_TO_LOG(status);
vb@63
  1390
vb@63
  1391
enomem:
vb@63
  1392
    status = PEP_OUT_OF_MEMORY;
vb@63
  1393
vb@63
  1394
pep_error:
vb@63
  1395
    free_stringlist(keys);
vb@63
  1396
    free_message(msg);
krista@2035
  1397
roker@1853
  1398
    return ADD_TO_LOG(status);
vb@37
  1399
}
vb@37
  1400
krista@995
  1401
DYNAMIC_API PEP_STATUS encrypt_message_for_self(
krista@992
  1402
        PEP_SESSION session,
krista@992
  1403
        pEp_identity* target_id,
krista@992
  1404
        message *src,
krista@992
  1405
        message **dst,
markus@1633
  1406
        PEP_enc_format enc_format,
markus@1633
  1407
        PEP_encrypt_flags_t flags
krista@992
  1408
    )
krista@992
  1409
{
krista@992
  1410
    PEP_STATUS status = PEP_STATUS_OK;
krista@992
  1411
    message * msg = NULL;
krista@992
  1412
    stringlist_t * keys = NULL;
krista@992
  1413
krista@992
  1414
    assert(session);
krista@992
  1415
    assert(src);
krista@992
  1416
    assert(dst);
krista@992
  1417
    assert(enc_format != PEP_enc_none);
krista@992
  1418
krista@992
  1419
    if (!(session && src && dst && enc_format != PEP_enc_none))
roker@1853
  1420
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
krista@992
  1421
krista@992
  1422
    if (src->dir == PEP_dir_incoming)
roker@1853
  1423
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
krista@1427
  1424
krista@992
  1425
    determine_encryption_format(src);
krista@992
  1426
    if (src->enc_format != PEP_enc_none)
roker@1853
  1427
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
krista@992
  1428
krista@995
  1429
    status = myself(session, target_id);
krista@995
  1430
    if (status != PEP_STATUS_OK)
roker@1734
  1431
        GOTO(pep_error);
krista@995
  1432
krista@992
  1433
    *dst = NULL;
krista@992
  1434
krista@1427
  1435
krista@992
  1436
    PEP_STATUS _status = update_identity(session, target_id);
krista@992
  1437
    if (_status != PEP_STATUS_OK) {
krista@992
  1438
        status = _status;
krista@992
  1439
        goto pep_error;
krista@992
  1440
    }
krista@992
  1441
krista@994
  1442
    char* target_fpr = target_id->fpr;
krista@994
  1443
    if (!target_fpr)
krista@994
  1444
        return PEP_KEY_NOT_FOUND; // FIXME: Error condition
krista@1685
  1445
 
krista@994
  1446
    keys = new_stringlist(target_fpr);
krista@1685
  1447
    
krista@1640
  1448
    /* KG: did we ever do this??? */
krista@1640
  1449
    if (!(flags & PEP_encrypt_flag_force_no_attached_key))
krista@1685
  1450
        _attach_key(session, target_fpr, src);
krista@1427
  1451
krista@992
  1452
    msg = clone_to_empty_message(src);
krista@992
  1453
    if (msg == NULL)
krista@992
  1454
        goto enomem;
krista@992
  1455
krista@992
  1456
    switch (enc_format) {
krista@992
  1457
        case PEP_enc_PGP_MIME:
krista@992
  1458
        case PEP_enc_PEP: // BUG: should be implemented extra
krista@2035
  1459
            status = encrypt_PGP_MIME(session, src, keys, msg, flags);
krista@992
  1460
            break;
krista@992
  1461
krista@992
  1462
        case PEP_enc_pieces:
krista@1639
  1463
            status = encrypt_PGP_in_pieces(session, src, keys, msg, flags);
krista@992
  1464
            break;
krista@992
  1465
krista@992
  1466
        /* case PEP_enc_PEP:
krista@1641
  1467
            NOT_IMPLEMENTED */
krista@992
  1468
            // TODO: implement
krista@992
  1469
krista@992
  1470
        default:
krista@992
  1471
            assert(0);
krista@992
  1472
            status = PEP_ILLEGAL_VALUE;
krista@992
  1473
            goto pep_error;
krista@992
  1474
    }
krista@1427
  1475
krista@992
  1476
    if (status == PEP_OUT_OF_MEMORY)
krista@992
  1477
        goto enomem;
krista@1427
  1478
krista@992
  1479
    if (status != PEP_STATUS_OK)
krista@992
  1480
        goto pep_error;
krista@992
  1481
krista@994
  1482
     if (msg && msg->shortmsg == NULL) {
krista@2045
  1483
         msg->shortmsg = _pep_subj_copy();
krista@994
  1484
         assert(msg->shortmsg);
krista@994
  1485
         if (msg->shortmsg == NULL)
krista@994
  1486
             goto enomem;
krista@994
  1487
     }
krista@992
  1488
edouard@1542
  1489
     if (msg) {
edouard@1542
  1490
         if (src->id) {
edouard@1542
  1491
             msg->id = strdup(src->id);
edouard@1542
  1492
             assert(msg->id);
edouard@1542
  1493
             if (msg->id == NULL)
edouard@1542
  1494
                 goto enomem;
edouard@1542
  1495
         }
edouard@1542
  1496
     }
edouard@1542
  1497
krista@992
  1498
    *dst = msg;
krista@992
  1499
    return status;
krista@992
  1500
krista@992
  1501
enomem:
krista@992
  1502
    status = PEP_OUT_OF_MEMORY;
krista@992
  1503
krista@992
  1504
pep_error:
krista@992
  1505
    free_stringlist(keys);
krista@992
  1506
    free_message(msg);
krista@992
  1507
roker@1853
  1508
    return ADD_TO_LOG(status);
krista@992
  1509
}
krista@992
  1510
vb@781
  1511
static bool is_a_pEpmessage(const message *msg)
vb@781
  1512
{
vb@781
  1513
    for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
vb@781
  1514
        if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
vb@781
  1515
            return true;
vb@781
  1516
    }
vb@781
  1517
    return false;
vb@781
  1518
}
vb@781
  1519
vb@781
  1520
// update comm_type to pEp_ct_pEp if needed
krista@2035
  1521
Edouard@858
  1522
static PEP_STATUS _update_identity_for_incoming_message(
vb@781
  1523
        PEP_SESSION session,
vb@781
  1524
        const message *src
vb@781
  1525
    )
vb@781
  1526
{
Edouard@858
  1527
    PEP_STATUS status;
Edouard@858
  1528
    if (src->from && src->from->address) {
Edouard@858
  1529
        status = update_identity(session, src->from);
krista@1192
  1530
        if (status == PEP_STATUS_OK
Edouard@858
  1531
                && is_a_pEpmessage(src)
vb@781
  1532
                && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
vb@781
  1533
                && src->from->comm_type != PEP_ct_pEp_unconfirmed
vb@781
  1534
                && src->from->comm_type != PEP_ct_pEp)
vb@781
  1535
        {
vb@781
  1536
            src->from->comm_type |= PEP_ct_pEp_unconfirmed;
krista@1823
  1537
            status = set_identity(session, src->from);
vb@781
  1538
        }
Edouard@858
  1539
        return status;
vb@781
  1540
    }
Edouard@858
  1541
    return PEP_ILLEGAL_VALUE;
vb@781
  1542
}
vb@781
  1543
krista@1397
  1544
krista@1397
  1545
PEP_STATUS _get_detached_signature(message* msg, bloblist_t** signature_blob) {
krista@1397
  1546
    bloblist_t* attach_curr = msg->attachments;
krista@1427
  1547
krista@1427
  1548
    *signature_blob = NULL;
krista@1427
  1549
krista@1397
  1550
    while (attach_curr) {
krista@1427
  1551
        if (strcasecmp(attach_curr->mime_type, "application/pgp-signature") == 0) {
krista@1397
  1552
            *signature_blob = attach_curr;
krista@1397
  1553
            break;
krista@1397
  1554
        }
krista@1397
  1555
        attach_curr = attach_curr->next;
krista@1397
  1556
    }
krista@1427
  1557
krista@1427
  1558
    return PEP_STATUS_OK;
krista@1427
  1559
}
krista@1427
  1560
krista@1427
  1561
PEP_STATUS _get_signed_text(const char* ptext, const size_t psize,
krista@1427
  1562
                            char** stext, size_t* ssize) {
krista@1427
  1563
krista@1427
  1564
    char* signed_boundary = NULL;
krista@1427
  1565
    char* signpost = strstr(ptext, "Content-Type: multipart/signed");
krista@1427
  1566
krista@1427
  1567
    *ssize = 0;
krista@1427
  1568
    *stext = NULL;
krista@1427
  1569
krista@1427
  1570
    if (!signpost)
krista@1427
  1571
        return PEP_UNKNOWN_ERROR;
krista@1427
  1572
krista@1427
  1573
    char* curr_line = signpost;
krista@1427
  1574
//    const char* end_text = ptext + psize;
krista@1755
  1575
    const char* boundary_key = "boundary=";
krista@1755
  1576
    const size_t BOUNDARY_KEY_SIZE = 9;
krista@1427
  1577
krista@1427
  1578
    char* start_boundary = strstr(curr_line, boundary_key);
krista@1427
  1579
    if (!start_boundary)
krista@1427
  1580
        return PEP_UNKNOWN_ERROR;
krista@1427
  1581
krista@1427
  1582
    start_boundary += BOUNDARY_KEY_SIZE;
krista@1427
  1583
krista@1755
  1584
    bool quoted = (*start_boundary == '"');
krista@1755
  1585
krista@1755
  1586
    if (quoted)
krista@1755
  1587
        start_boundary++;
krista@1755
  1588
        
krista@1755
  1589
    char* end_boundary = (quoted ? strstr(start_boundary, "\"") : strstr(start_boundary, ";")); // FIXME: third possiblity is CRLF, or?
krista@1427
  1590
krista@1427
  1591
    if (!end_boundary)
krista@1427
  1592
        return PEP_UNKNOWN_ERROR;
krista@1427
  1593
krista@1755
  1594
    // Add space for the "--"
krista@1427
  1595
    size_t boundary_strlen = (end_boundary - start_boundary) + 2;
krista@1427
  1596
krista@1427
  1597
    signed_boundary = calloc(1, boundary_strlen + 1);
krista@1427
  1598
    strlcpy(signed_boundary, "--", boundary_strlen + 1);
krista@1427
  1599
    strlcat(signed_boundary, start_boundary, boundary_strlen + 1);
krista@1427
  1600
krista@1427
  1601
    start_boundary = strstr(end_boundary, signed_boundary);
krista@1427
  1602
krista@1427
  1603
    if (!start_boundary)
krista@1427
  1604
        return PEP_UNKNOWN_ERROR;
krista@1427
  1605
krista@1427
  1606
    start_boundary += boundary_strlen;
krista@1427
  1607
krista@1756
  1608
    if (*start_boundary == '\r') {
krista@1756
  1609
        if (*(start_boundary + 1) == '\n')
krista@1756
  1610
            start_boundary += 2;
krista@1756
  1611
    }
krista@1756
  1612
    else if (*start_boundary == '\n')
krista@1427
  1613
        start_boundary++;
krista@1427
  1614
krista@1427
  1615
    end_boundary = strstr(start_boundary + boundary_strlen, signed_boundary);
krista@1427
  1616
krista@1427
  1617
    if (!end_boundary)
krista@1427
  1618
        return PEP_UNKNOWN_ERROR;
krista@1427
  1619
edouard@1809
  1620
    // See RFC3156 section 5...
edouard@1809
  1621
    end_boundary--; 
edouard@1809
  1622
    if (*(end_boundary - 1) == '\r')
edouard@1809
  1623
        end_boundary--; 
krista@1427
  1624
krista@1427
  1625
    *ssize = end_boundary - start_boundary;
krista@1427
  1626
    *stext = start_boundary;
krista@1427
  1627
    free(signed_boundary);
krista@1427
  1628
krista@1397
  1629
    return PEP_STATUS_OK;
krista@1397
  1630
}
krista@1397
  1631
krista@1484
  1632
PEP_STATUS combine_keylists(PEP_SESSION session, stringlist_t** verify_in, 
krista@1484
  1633
                            stringlist_t** keylist_in_out, 
krista@1484
  1634
                            pEp_identity* from) {
krista@1484
  1635
    
krista@1484
  1636
    if (!verify_in || !(*verify_in)) // this isn't really a problem.
krista@1484
  1637
        return PEP_STATUS_OK;
krista@1484
  1638
    
krista@1484
  1639
    stringlist_t* orig_verify = *verify_in;
krista@1484
  1640
    
roker@1559
  1641
    stringlist_t* verify_curr = NULL;
roker@1559
  1642
    stringlist_t* from_keys = NULL;
krista@1484
  1643
    
krista@1484
  1644
    /* FIXME: what to do if head needs to be null */
krista@1484
  1645
    PEP_STATUS status = find_keys(session, from->address, &from_keys);
krista@1484
  1646
    
krista@1484
  1647
    stringlist_t* from_fpr_node = NULL;
krista@1484
  1648
    stringlist_t* from_curr;
krista@1484
  1649
    
krista@1484
  1650
    for (from_curr = from_keys; from_curr; from_curr = from_curr->next) {
krista@1484
  1651
        for (verify_curr = orig_verify; verify_curr; verify_curr = verify_curr->next) {
krista@1484
  1652
            if (from_curr->value && verify_curr->value &&
edouard@1573
  1653
                _same_fpr(from_curr->value, strlen(from_curr->value),
edouard@1573
  1654
                          verify_curr->value, strlen(verify_curr->value))) {
krista@1484
  1655
                from_fpr_node = from_curr;
krista@1484
  1656
                break;
krista@1484
  1657
            }
krista@1484
  1658
        }
krista@1484
  1659
    }
krista@1484
  1660
    
krista@1484
  1661
    if (!from_fpr_node) {
krista@1484
  1662
        status = PEP_KEY_NOT_FOUND;
krista@1484
  1663
        goto free;
krista@1484
  1664
    }
krista@1484
  1665
krista@1484
  1666
    verify_curr = orig_verify;
krista@1484
  1667
    
krista@1484
  1668
    /* put "from" signer at the beginning of the list */
edouard@1573
  1669
    if (!_same_fpr(orig_verify->value, strlen(orig_verify->value),
edouard@1573
  1670
                   from_fpr_node->value, strlen(from_fpr_node->value))) {
krista@1484
  1671
        orig_verify = stringlist_delete(orig_verify, from_fpr_node->value);
krista@1484
  1672
        verify_curr = new_stringlist(from_fpr_node->value);
krista@1484
  1673
        verify_curr->next = orig_verify;
krista@1484
  1674
    }
krista@1484
  1675
krista@1484
  1676
    /* append keylist to signers */
krista@1484
  1677
    if (keylist_in_out && *keylist_in_out && (*keylist_in_out)->value) {
krista@1484
  1678
        stringlist_t** tail_pp = &verify_curr->next;
krista@1484
  1679
        
krista@1484
  1680
        while (*tail_pp) {
krista@1484
  1681
            tail_pp = &((*tail_pp)->next);
krista@1484
  1682
        }
krista@1615
  1683
        stringlist_t* second_list = *keylist_in_out;
krista@1615
  1684
        if (second_list) {
krista@1615
  1685
            char* listhead_val = second_list->value;
krista@1615
  1686
            if (!listhead_val || listhead_val[0] == '\0') {
krista@1615
  1687
                /* remove head, basically. This can happen when,
krista@1615
  1688
                   for example, the signature is detached and
krista@1615
  1689
                   verification is not seen directly after
krista@1615
  1690
                   decryption, so no signer is presumed in
krista@1615
  1691
                   the first construction of the keylist */
krista@1615
  1692
                *keylist_in_out = (*keylist_in_out)->next;
krista@1615
  1693
                second_list->next = NULL;
krista@1615
  1694
                free_stringlist(second_list);
krista@1615
  1695
            }
krista@1615
  1696
        }
krista@1484
  1697
        *tail_pp = *keylist_in_out;
krista@1484
  1698
    }
krista@1484
  1699
    
krista@1484
  1700
    *keylist_in_out = verify_curr;
krista@1484
  1701
    
krista@1484
  1702
    status = PEP_STATUS_OK;
krista@1484
  1703
    
krista@1484
  1704
free:
krista@1484
  1705
    free_stringlist(from_keys);
roker@1559
  1706
    return status;
krista@1484
  1707
}
krista@1484
  1708
edouard@1815
  1709
PEP_STATUS amend_rating_according_to_sender_and_recipients(
edouard@1815
  1710
    PEP_SESSION session,
edouard@1815
  1711
    PEP_rating *rating,
edouard@1815
  1712
    pEp_identity *sender,
edouard@1815
  1713
    stringlist_t *recipients) {
edouard@1815
  1714
    
edouard@1815
  1715
    PEP_STATUS status = PEP_STATUS_OK;
edouard@1815
  1716
edouard@1815
  1717
    if (*rating > PEP_rating_mistrust) {
edouard@1876
  1718
edouard@1876
  1719
        if (recipients == NULL) {
edouard@1876
  1720
            *rating = PEP_rating_undefined;
edouard@1876
  1721
            return PEP_STATUS_OK;
edouard@1815
  1722
        }
edouard@1876
  1723
edouard@1876
  1724
        char *fpr = recipients->value;
edouard@1876
  1725
edouard@1876
  1726
        if (!(sender && sender->user_id && sender->user_id[0] && fpr && fpr[0])) {
edouard@1815
  1727
            *rating = PEP_rating_unreliable;
edouard@1815
  1728
        }
edouard@1876
  1729
        else {
edouard@1876
  1730
            pEp_identity *_sender = new_identity(sender->address, fpr,
edouard@1876
  1731
                                               sender->user_id, sender->username);
edouard@1876
  1732
            if (_sender == NULL)
edouard@1876
  1733
                return PEP_OUT_OF_MEMORY;
edouard@1876
  1734
edouard@1876
  1735
            status = get_trust(session, _sender);
edouard@1876
  1736
            if (_sender->comm_type != PEP_ct_unknown) {
edouard@1876
  1737
                *rating = keylist_rating(session, recipients, 
edouard@1876
  1738
                            fpr, _rating(_sender->comm_type, 
edouard@1876
  1739
                                          PEP_rating_undefined));
edouard@1815
  1740
            }
edouard@1876
  1741
            free_identity(_sender);
edouard@1876
  1742
            if (status == PEP_CANNOT_FIND_IDENTITY)
edouard@1876
  1743
               status = PEP_STATUS_OK;
edouard@1815
  1744
        }
edouard@1815
  1745
    }
edouard@1815
  1746
    return status;
edouard@1815
  1747
}
edouard@1815
  1748
krista@1484
  1749
Edouard@741
  1750
DYNAMIC_API PEP_STATUS _decrypt_message(
vb@37
  1751
        PEP_SESSION session,
vb@113
  1752
        message *src,
vb@241
  1753
        message **dst,
vb@251
  1754
        stringlist_t **keylist,
vb@1004
  1755
        PEP_rating *rating,
krista@1427
  1756
        PEP_decrypt_flags_t *flags,
Edouard@741
  1757
        identity_list **private_il
vb@37
  1758
    )
vb@37
  1759
{
vb@37
  1760
    PEP_STATUS status = PEP_STATUS_OK;
vb@261
  1761
    PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
vb@73
  1762
    message *msg = NULL;
vb@112
  1763
    char *ctext;
vb@112
  1764
    size_t csize;
vb@269
  1765
    char *ptext = NULL;
vb@112
  1766
    size_t psize;
vb@241
  1767
    stringlist_t *_keylist = NULL;
krista@2045
  1768
    unsigned char pepstr[] = PEP_SUBJ_STRING;
vb@37
  1769
vb@74
  1770
    assert(session);
vb@74
  1771
    assert(src);
vb@74
  1772
    assert(dst);
vb@241
  1773
    assert(keylist);
vb@1004
  1774
    assert(rating);
Edouard@739
  1775
    assert(flags);
vb@73
  1776
vb@1004
  1777
    if (!(session && src && dst && keylist && rating && flags))
roker@1853
  1778
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
vb@191
  1779
Edouard@739
  1780
    *flags = 0;
Edouard@739
  1781
Edouard@734
  1782
    // Private key in unencrypted mail are ignored -> NULL
Edouard@734
  1783
    bool imported_keys = import_attached_keys(session, src, NULL);
Edouard@728
  1784
Edouard@728
  1785
    // Update src->from in case we just imported a key
Edouard@728
  1786
    // we would need to check signature
Edouard@858
  1787
    status = _update_identity_for_incoming_message(session, src);
Edouard@858
  1788
    if(status != PEP_STATUS_OK)
roker@1853
  1789
        return ADD_TO_LOG(status);
Edouard@858
  1790
krista@1397
  1791
    // Get detached signature, if any
krista@1397
  1792
    bloblist_t* detached_sig = NULL;
krista@1397
  1793
    char* dsig_text = NULL;
krista@1397
  1794
    size_t dsig_size = 0;
krista@1397
  1795
    status = _get_detached_signature(src, &detached_sig);
krista@1397
  1796
    if (detached_sig) {
krista@1397
  1797
        dsig_text = detached_sig->value;
krista@1397
  1798
        dsig_size = detached_sig->size;
krista@1397
  1799
    }
krista@1427
  1800
vb@259
  1801
    PEP_cryptotech crypto = determine_encryption_format(src);
vb@259
  1802
vb@74
  1803
    *dst = NULL;
vb@251
  1804
    *keylist = NULL;
vb@1004
  1805
    *rating = PEP_rating_undefined;
krista@1427
  1806
vb@261
  1807
    switch (src->enc_format) {
vb@269
  1808
        case PEP_enc_none:
vb@1004
  1809
            *rating = PEP_rating_unencrypted;
vb@731
  1810
            if (imported_keys)
vb@731
  1811
                remove_attached_keys(src);
edouard@1603
  1812
            if(session->sync_session->inject_sync_msg){
edouard@1165
  1813
                status = receive_DeviceState_msg(session, src, *rating, *keylist);
krista@1427
  1814
                if (status == PEP_MESSAGE_CONSUME ||
edouard@1369
  1815
                    status == PEP_MESSAGE_IGNORE) {
vb@1134
  1816
                    free_message(msg);
vb@1134
  1817
                    msg = NULL;
edouard@1369
  1818
                    *flags |= (status == PEP_MESSAGE_IGNORE) ?
edouard@1369
  1819
                                PEP_decrypt_flag_ignore :
edouard@1369
  1820
                                PEP_decrypt_flag_consume;
vb@1134
  1821
                }
vb@1134
  1822
                else if (status != PEP_STATUS_OK) {
roker@1853
  1823
                    return ADD_TO_LOG(status);
vb@1134
  1824
                }
vb@1099
  1825
            }
krista@1484
  1826
            
krista@1484
  1827
            char* slong = src->longmsg;
krista@1484
  1828
            char* sform = src->longmsg_formatted;
krista@2035
  1829
            bloblist_t* satt = src->attachments;
roker@1563
  1830
            
krista@2035
  1831
            if ((!slong || slong[0] == '\0')
krista@2035
  1832
                 && (!sform || sform[0] == '\0')) {
krista@2035
  1833
                if (satt) {
krista@2035
  1834
                    const char* inner_mime_type = satt->mime_type;
krista@2035
  1835
                    if (strcasecmp(inner_mime_type, "text/plain") == 0) {
krista@2035
  1836
                        free(slong); /* in case of "" */
krista@2035
  1837
                        src->longmsg = strndup(satt->value, satt->size); // N.B.: longmsg might be shorter, if attachment contains NUL bytes which are not allowed in text/plain!
krista@2035
  1838
                        
krista@2035
  1839
                        bloblist_t* next_node = satt->next;
krista@2035
  1840
                        if (next_node) {
krista@2035
  1841
                            inner_mime_type = next_node->mime_type;
krista@2035
  1842
                            if (strcasecmp(inner_mime_type, "text/html") == 0) {
krista@2035
  1843
                                free(sform);
krista@2035
  1844
                                src->longmsg_formatted = strndup(next_node->value, next_node->size);  // N.B.: longmsg might be shorter, if attachment contains NUL bytes which are not allowed in text/plain!
krista@2035
  1845
                            }
krista@2035
  1846
                        }
krista@2035
  1847
                    }
krista@2035
  1848
                    else if (strcasecmp(inner_mime_type, "text/html") == 0) {
krista@2035
  1849
                        free(sform);
krista@2035
  1850
                        src->longmsg_formatted = strndup(satt->value, satt->size);  // N.B.: longmsg might be shorter, if attachment contains NUL bytes which are not allowed in text/plain!
krista@2035
  1851
                    }
krista@2035
  1852
                }
krista@2035
  1853
            }
krista@1484
  1854
            
roker@1853
  1855
            return ADD_TO_LOG(PEP_UNENCRYPTED);
vb@269
  1856
vb@261
  1857
        case PEP_enc_PGP_MIME:
vb@301
  1858
            ctext = src->attachments->next->value;
vb@269
  1859
            csize = src->attachments->next->size;
vb@261
  1860
            break;
vb@261
  1861
edouard@1557
  1862
        case PEP_enc_PGP_MIME_Outlook1:
edouard@1557
  1863
            ctext = src->attachments->value;
edouard@1557
  1864
            csize = src->attachments->size;
edouard@1557
  1865
            break;
edouard@1557
  1866
vb@261
  1867
        case PEP_enc_pieces:
vb@261
  1868
            ctext = src->longmsg;
vb@261
  1869
            csize = strlen(ctext);
vb@261
  1870
            break;
vb@261
  1871
vb@261
  1872
        default:
vb@261
  1873
            NOT_IMPLEMENTED
vb@259
  1874
    }
Edouard@431
  1875
    status = cryptotech[crypto].decrypt_and_verify(session, ctext,
krista@1427
  1876
                                                   csize, dsig_text, dsig_size,
krista@1397
  1877
                                                   &ptext, &psize, &_keylist);
edouard@1195
  1878
    if (status > PEP_CANNOT_DECRYPT_UNKNOWN){
roker@1729
  1879
        GOTO(pep_error);
edouard@1271
  1880
    }
edouard@1271
  1881
edouard@1273
  1882
    decrypt_status = status;
edouard@1273
  1883
edouard@1271
  1884
    if (status == PEP_DECRYPT_NO_KEY){
edouard@1299
  1885
        PEP_STATUS sync_status = inject_DeviceState_event(session, CannotDecrypt, NULL, NULL);
edouard@1299
  1886
        if (sync_status == PEP_OUT_OF_MEMORY){
edouard@1299
  1887
            status = PEP_OUT_OF_MEMORY;
edouard@1273
  1888
            goto pep_error;
edouard@1299
  1889
        }
edouard@1195
  1890
    }
vb@113
  1891
krista@1427
  1892
    bool imported_private_key_address = false;
Edouard@728
  1893
vb@256
  1894
    if (ptext) {
vb@256
  1895
        switch (src->enc_format) {
vb@256
  1896
            case PEP_enc_PGP_MIME:
edouard@1557
  1897
            case PEP_enc_PGP_MIME_Outlook1:
vb@269
  1898
                status = mime_decode_message(ptext, psize, &msg);
vb@113
  1899
                if (status != PEP_STATUS_OK)
roker@1563
  1900
                    goto pep_error;
krista@1484
  1901
                
krista@1484
  1902
                char* mlong = msg->longmsg;
krista@1484
  1903
                char* mform = msg->longmsg_formatted;
krista@2035
  1904
                bloblist_t* matt = msg->attachments;
roker@1563
  1905
                
krista@2035
  1906
                if ((!mlong || mlong[0] == '\0')
krista@2035
  1907
                     && (!mform || mform[0] == '\0')) {
krista@2035
  1908
                    if (matt) {
krista@2035
  1909
                        const char* inner_mime_type = matt->mime_type;
krista@2035
  1910
                        if (strcasecmp(inner_mime_type, "text/plain") == 0) {
krista@2035
  1911
                            free(mlong); /* in case of "" */
krista@2035
  1912
                            msg->longmsg = strndup(matt->value, matt->size);
krista@2035
  1913
                            
krista@2035
  1914
                            bloblist_t* next_node = matt->next;
krista@2035
  1915
                            if (next_node) {
krista@2035
  1916
                                inner_mime_type = next_node->mime_type;
krista@2035
  1917
                                if (strcasecmp(inner_mime_type, "text/html") == 0) {
krista@2035
  1918
                                    free(mform);
krista@2035
  1919
                                    msg->longmsg_formatted = strndup(next_node->value, next_node->size);
krista@2035
  1920
                                }
krista@2035
  1921
                            }
krista@2035
  1922
                        }
krista@2035
  1923
                        else if (strcasecmp(inner_mime_type, "text/html") == 0) {
krista@2035
  1924
                            free(mform);
krista@2035
  1925
                            msg->longmsg_formatted = strndup(matt->value, matt->size);
krista@2035
  1926
                        }
krista@2035
  1927
                    }
krista@1484
  1928
                    if (msg->shortmsg) {
krista@1484
  1929
                        free(src->shortmsg);
krista@1484
  1930
                        src->shortmsg = strdup(msg->shortmsg);
krista@1484
  1931
                    }
roker@1563
  1932
                }
krista@1484
  1933
krista@1484
  1934
                if (decrypt_status != PEP_DECRYPTED_AND_VERIFIED) {
krista@1484
  1935
                    status = _get_detached_signature(msg, &detached_sig);
krista@1484
  1936
                    if (decrypt_status == PEP_DECRYPTED && detached_sig) {
krista@1484
  1937
                        dsig_text = detached_sig->value;
krista@1484
  1938
                        dsig_size = detached_sig->size;
krista@1484
  1939
                        size_t ssize = 0;
krista@1484
  1940
                        char* stext = NULL;
krista@1484
  1941
krista@1484
  1942
                        status = _get_signed_text(ptext, psize, &stext, &ssize);
krista@1484
  1943
                        stringlist_t *_verify_keylist = NULL;
krista@1484
  1944
krista@1484
  1945
                        if (ssize > 0 && stext) {
krista@1484
  1946
                            status = cryptotech[crypto].verify_text(session, stext,
krista@1484
  1947
                                                                    ssize, dsig_text, dsig_size,
krista@1484
  1948
                                                                    &_verify_keylist);
krista@1484
  1949
krista@1484
  1950
                            if (status == PEP_VERIFIED || status == PEP_VERIFIED_AND_TRUSTED)
edouard@2043
  1951
                            {
krista@1484
  1952
                                decrypt_status = PEP_DECRYPTED_AND_VERIFIED;
krista@1484
  1953
                            
krista@1484
  1954
                                status = combine_keylists(session, &_verify_keylist, &_keylist, src->from);
edouard@2043
  1955
                            }
krista@1484
  1956
                        }
krista@1427
  1957
                    }
krista@1427
  1958
                }
vb@256
  1959
                break;
vb@256
  1960
vb@256
  1961
            case PEP_enc_pieces:
vb@256
  1962
                msg = clone_to_empty_message(src);
vb@256
  1963
                if (msg == NULL)
vb@113
  1964
                    goto enomem;
vb@113
  1965
Edouard@882
  1966
                msg->longmsg = ptext;
Edouard@882
  1967
                ptext = NULL;
vb@256
  1968
vb@256
  1969
                bloblist_t *_m = msg->attachments;
vb@320
  1970
                if (_m == NULL && src->attachments && src->attachments->value) {
vb@320
  1971
                    msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
vb@320
  1972
                    _m = msg->attachments;
vb@320
  1973
                }
vb@320
  1974
vb@256
  1975
                bloblist_t *_s;
Edouard@754
  1976
                for (_s = src->attachments; _s; _s = _s->next) {
Edouard@754
  1977
                    if (_s->value == NULL && _s->size == 0){
Edouard@754
  1978
                        _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
Edouard@754
  1979
                        if (_m == NULL)
Edouard@754
  1980
                            goto enomem;
Edouard@754
  1981
Edouard@754
  1982
                    }
Edouard@754
  1983
                    else if (is_encrypted_attachment(_s)) {
vb@256
  1984
                        stringlist_t *_keylist = NULL;
roker@862
  1985
                        char *attctext  = _s->value;
roker@862
  1986
                        size_t attcsize = _s->size;
Edouard@431
  1987
Edouard@842
  1988
                        free(ptext);
Edouard@842
  1989
                        ptext = NULL;
Edouard@842
  1990
krista@1397
  1991
                        // FIXME: What about attachments with separate sigs???
Edouard@431
  1992
                        status = decrypt_and_verify(session, attctext, attcsize,
krista@1397
  1993
                                                    NULL, 0,
krista@1397
  1994
                                                    &ptext, &psize, &_keylist);
krista@1484
  1995
                        free_stringlist(_keylist); // FIXME: Why do we do this?
vb@256
  1996
vb@289
  1997
                        if (ptext) {
vb@289
  1998
                            if (is_encrypted_html_attachment(_s)) {
Edouard@882
  1999
                                msg->longmsg_formatted = ptext;
Edouard@882
  2000
                                ptext = NULL;
vb@289
  2001
                            }
vb@289
  2002
                            else {
roker@862
  2003
                                static const char * const mime_type = "application/octet-stream";
roker@864
  2004
                                char * const filename =
vb@289
  2005
                                    without_double_ending(_s->filename);
vb@289
  2006
                                if (filename == NULL)
vb@289
  2007
                                    goto enomem;
vb@289
  2008
Edouard@882
  2009
                                _m = bloblist_add(_m, ptext, psize, mime_type,
vb@289
  2010
                                    filename);
roker@862
  2011
                                free(filename);
vb@290
  2012
                                if (_m == NULL)
vb@289
  2013
                                    goto enomem;
vb@290
  2014
Edouard@882
  2015
                                ptext = NULL;
Edouard@882
  2016
vb@290
  2017
                                if (msg->attachments == NULL)
vb@290
  2018
                                    msg->attachments = _m;
vb@289
  2019
                            }
vb@256
  2020
                        }
vb@256
  2021
                        else {
vb@320
  2022
                            char *copy = malloc(_s->size);
vb@350
  2023
                            assert(copy);
vb@350
  2024
                            if (copy == NULL)
vb@350
  2025
                                goto enomem;
vb@320
  2026
                            memcpy(copy, _s->value, _s->size);
vb@320
  2027
                            _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
vb@256
  2028
                            if (_m == NULL)
vb@256
  2029
                                goto enomem;
vb@256
  2030
                        }
vb@256
  2031
                    }
vb@320
  2032
                    else {
vb@320
  2033
                        char *copy = malloc(_s->size);
vb@350
  2034
                        assert(copy);
vb@350
  2035
                        if (copy == NULL)
vb@350
  2036
                            goto enomem;
vb@320
  2037
                        memcpy(copy, _s->value, _s->size);
vb@320
  2038
                        _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
vb@320
  2039
                        if (_m == NULL)
vb@320
  2040
                            goto enomem;
vb@320
  2041
                    }
vb@256
  2042
                }
vb@256
  2043
vb@256
  2044
                break;
vb@256
  2045
vb@256
  2046
            default:
vb@256
  2047
                // BUG: must implement more
vb@256
  2048
                NOT_IMPLEMENTED
vb@256
  2049
        }
krista@1427
  2050
vb@256
  2051
        switch (src->enc_format) {
vb@256
  2052
            case PEP_enc_PGP_MIME:
vb@256
  2053
            case PEP_enc_pieces:
edouard@1557
  2054
            case PEP_enc_PGP_MIME_Outlook1:
krista@1987
  2055
                status = copy_fields(msg, src);
krista@1987
  2056
                if (status != PEP_STATUS_OK)
krista@1987
  2057
                {
krista@1987
  2058
                    GOTO(pep_error);
krista@1987
  2059
                }
krista@2035
  2060
krista@2045
  2061
                if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0 ||
krista@2045
  2062
                    _unsigned_signed_strcmp(pepstr, src->shortmsg, PEP_SUBJ_BYTELEN) == 0)
krista@2035
  2063
                {
krista@2035
  2064
                    char * shortmsg;
krista@2035
  2065
                    char * longmsg;
krista@2078
  2066
                    char * msg_version; // This is incorrect, but just here to get things compiling for the moment
krista@2078
  2067
                    int r = separate_short_and_long(msg->longmsg, &shortmsg, &msg_version,
krista@2035
  2068
                            &longmsg);
krista@2035
  2069
                    
krista@2035
  2070
                    if (r == -1)
krista@2035
  2071
                        goto enomem;
krista@2035
  2072
krista@2035
  2073
                    if (shortmsg == NULL) {
krista@2035
  2074
                        if (src->shortmsg == NULL)
krista@2035
  2075
                            shortmsg = strdup("");
krista@2035
  2076
                        else {
krista@2035
  2077
                            // FIXME: is msg->shortmsg always a copy of
krista@2035
  2078
                            // src->shortmsg already?
krista@2035
  2079
                            // if so, we need to change the logic so
krista@2035
  2080
                            // that in this case, we don't free msg->shortmsg
krista@2035
  2081
                            // and do this strdup, etc.
krista@2035
  2082
                            shortmsg = strdup(src->shortmsg);
krista@1987
  2083
                        }
krista@1852
  2084
                    }
krista@2035
  2085
krista@2035
  2086
krista@2035
  2087
                    free(msg->shortmsg);
krista@2035
  2088
                    free(msg->longmsg);
krista@2035
  2089
krista@2035
  2090
                    msg->shortmsg = shortmsg;
krista@2035
  2091
                    msg->longmsg = longmsg;
krista@2035
  2092
                }
krista@2035
  2093
                else {
krista@2035
  2094
                    msg->shortmsg = strdup(src->shortmsg);
krista@2035
  2095
                    assert(msg->shortmsg);
krista@2035
  2096
                    if (msg->shortmsg == NULL)
krista@2035
  2097
                        goto enomem;
vb@256
  2098
                }
vb@256
  2099
                break;
vb@256
  2100
            default:
krista@1484
  2101
                    // BUG: must implement more
krista@1484
  2102
                    NOT_IMPLEMENTED
vb@256
  2103
        }
krista@1427
  2104
Edouard@734
  2105
        // check for private key in decrypted message attachement while inporting
Edouard@741
  2106
        identity_list *_private_il = NULL;
Edouard@741
  2107
        imported_keys = import_attached_keys(session, msg, &_private_il);
krista@1427
  2108
        if (_private_il &&
Edouard@741
  2109
            identity_list_length(_private_il) == 1 &&
Edouard@741
  2110
            _private_il->ident->address)
Edouard@728
  2111
        {
Edouard@741
  2112
            imported_private_key_address = true;
Edouard@728
  2113
        }
Edouard@741
  2114
Edouard@741
  2115
        if(private_il && imported_private_key_address){
Edouard@741
  2116
            *private_il = _private_il;
Edouard@741
  2117
        }else{
Edouard@741
  2118
            free_identity_list(_private_il);
Edouard@741
  2119
        }
krista@1427
  2120
krista@1987
  2121
        if(decrypt_status == PEP_DECRYPTED){
Edouard@728
  2122
Edouard@728
  2123
            // TODO optimize if import_attached_keys didn't import any key
krista@1427
  2124
Edouard@431
  2125
            // In case message did decrypt, but no valid signature could be found
Edouard@431
  2126
            // then retry decrypt+verify after importing key.
Edouard@728
  2127
Edouard@728
  2128
            // Update msg->from in case we just imported a key
Edouard@728
  2129
            // we would need to check signature
vb@781
  2130
Edouard@861
  2131
            status = _update_identity_for_incoming_message(session, src);
Edouard@858
  2132
            if(status != PEP_STATUS_OK)
roker@1729
  2133
            {
roker@1729
  2134
                GOTO(pep_error);
roker@1729
  2135
            }
krista@1427
  2136
Edouard@431
  2137
            char *re_ptext = NULL;
Edouard@431
  2138
            size_t re_psize;
krista@1427
  2139
Edouard@431
  2140
            free_stringlist(_keylist);
Edouard@431
  2141
            _keylist = NULL;
vb@256
  2142
Edouard@431
  2143
            status = cryptotech[crypto].decrypt_and_verify(session, ctext,
krista@1397
  2144
                csize, dsig_text, dsig_size, &re_ptext, &re_psize, &_keylist);
krista@1427
  2145
Edouard@882
  2146
            free(re_ptext);
krista@1427
  2147
Edouard@431
  2148
            if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
roker@1729
  2149
            {
roker@1729
  2150
                GOTO(pep_error);
roker@1729
  2151
            }
krista@1427
  2152
Edouard@431
  2153
            decrypt_status = status;
Edouard@431
  2154
        }
krista@1427
  2155
vb@1004
  2156
        *rating = decrypt_rating(decrypt_status);
krista@1427
  2157
edouard@1815
  2158
        status = amend_rating_according_to_sender_and_recipients(session,
edouard@1815
  2159
                                                                 rating,
edouard@1815
  2160
                                                                 src->from,
edouard@1815
  2161
                                                                 _keylist);
edouard@1815
  2162
edouard@1815
  2163
        if (status != PEP_STATUS_OK)
edouard@1815
  2164
            GOTO(pep_error);
vb@113
  2165
    }
Edouard@745
  2166
    else
Edouard@745
  2167
    {
vb@1004
  2168
        *rating = decrypt_rating(decrypt_status);
Edouard@745
  2169
        goto pep_error;
Edouard@745
  2170
    }
vb@731
  2171
Edouard@728
  2172
    // Case of own key imported from own trusted message
krista@1427
  2173
    if (// Message have been reliably decrypted
Edouard@728
  2174
        msg &&
vb@1004
  2175
        *rating >= PEP_rating_trusted &&
Edouard@728
  2176
        imported_private_key_address &&
Edouard@728
  2177
        // to is [own]
Edouard@728
  2178
        msg->to->ident->user_id &&
krista@1427
  2179
        strcmp(msg->to->ident->user_id, PEP_OWN_USERID) == 0
Edouard@729
  2180
        )
Edouard@728
  2181
    {
Edouard@738
  2182
        *flags |= PEP_decrypt_flag_own_private_key;
Edouard@728
  2183
    }
vb@113
  2184
vb@731
  2185
    if (msg) {
vb@1004
  2186
        decorate_message(msg, *rating, _keylist);
vb@731
  2187
        if (imported_keys)
vb@731
  2188
            remove_attached_keys(msg);
edouard@1603
  2189
        if (*rating >= PEP_rating_reliable &&
edouard@1603
  2190
            session->sync_session->inject_sync_msg) {
edouard@1165
  2191
            status = receive_DeviceState_msg(session, msg, *rating, _keylist);
krista@1427
  2192
            if (status == PEP_MESSAGE_CONSUME ||
edouard@1369
  2193
                status == PEP_MESSAGE_IGNORE) {
vb@1099
  2194
                free_message(msg);
vb@1099
  2195
                msg = NULL;
edouard@1369
  2196
                *flags |= (status == PEP_MESSAGE_IGNORE) ?
edouard@1369
  2197
                            PEP_decrypt_flag_ignore :
edouard@1369
  2198
                            PEP_decrypt_flag_consume;
edouard@1355
  2199
vb@1099
  2200
            }
vb@1099
  2201
            else if (status != PEP_STATUS_OK){
vb@1099
  2202
                goto pep_error;
vb@1099
  2203
            }
vb@1099
  2204
        }
edouard@1558
  2205
    }
edouard@1558
  2206
    if (msg) {
edouard@1542
  2207
        if (src->id) {
edouard@1542
  2208
            msg->id = strdup(src->id);
edouard@1542
  2209
            assert(msg->id);
edouard@1542
  2210
            if (msg->id == NULL)
edouard@1542
  2211
                goto enomem;
edouard@1542
  2212
        }
vb@731
  2213
    }
vb@235
  2214
krista@2035
  2215
    *dst = msg;
vb@241
  2216
    *keylist = _keylist;
vb@241
  2217
edouard@1859
  2218
    if(decrypt_status == PEP_DECRYPTED_AND_VERIFIED)
edouard@1859
  2219
        return ADD_TO_LOG(PEP_STATUS_OK);
edouard@1859
  2220
    else
edouard@1859
  2221
        return ADD_TO_LOG(decrypt_status);
vb@73
  2222
vb@73
  2223
enomem:
vb@73
  2224
    status = PEP_OUT_OF_MEMORY;
vb@73
  2225
vb@73
  2226
pep_error:
Edouard@882
  2227
    free(ptext);
vb@73
  2228
    free_message(msg);
vb@241
  2229
    free_stringlist(_keylist);
vb@39
  2230
roker@1853
  2231
    return ADD_TO_LOG(status);
vb@37
  2232
}
vb@37
  2233
Edouard@741
  2234
DYNAMIC_API PEP_STATUS decrypt_message(
Edouard@741
  2235
        PEP_SESSION session,
Edouard@741
  2236
        message *src,
Edouard@741
  2237
        message **dst,
Edouard@741
  2238
        stringlist_t **keylist,
vb@1004
  2239
        PEP_rating *rating,
krista@1427
  2240
        PEP_decrypt_flags_t *flags
Edouard@741
  2241
    )
Edouard@741
  2242
{
vb@1004
  2243
    return _decrypt_message( session, src, dst, keylist, rating, flags, NULL );
Edouard@741
  2244
}
Edouard@741
  2245
Edouard@728
  2246
DYNAMIC_API PEP_STATUS own_message_private_key_details(
Edouard@728
  2247
        PEP_SESSION session,
Edouard@728
  2248
        message *msg,
krista@1427
  2249
        pEp_identity **ident
Edouard@728
  2250
    )
Edouard@728
  2251
{
Edouard@728
  2252
    assert(session);
Edouard@728
  2253
    assert(msg);
Edouard@728
  2254
    assert(ident);
Edouard@728
  2255
Edouard@736
  2256
    if (!(session && msg && ident))
Edouard@728
  2257
        return PEP_ILLEGAL_VALUE;
Edouard@728
  2258
krista@1427
  2259
    message *dst = NULL;
krista@956
  2260
    stringlist_t *keylist = NULL;
vb@1004
  2261
    PEP_rating rating;
krista@1427
  2262
    PEP_decrypt_flags_t flags;
Edouard@728
  2263
Edouard@729
  2264
    *ident = NULL;
Edouard@729
  2265
Edouard@740
  2266
    identity_list *private_il = NULL;
vb@1004
  2267
    PEP_STATUS status = _decrypt_message(session, msg,  &dst, &keylist, &rating, &flags, &private_il);
vb@953
  2268
    free_message(dst);
vb@953
  2269
    free_stringlist(keylist);
Edouard@728
  2270
Edouard@729
  2271
    if (status == PEP_STATUS_OK &&
Edouard@741
  2272
        flags & PEP_decrypt_flag_own_private_key &&
Edouard@741
  2273
        private_il)
Edouard@729
  2274
    {
Edouard@740
  2275
        *ident = identity_dup(private_il->ident);
Edouard@729
  2276
    }
Edouard@729
  2277
Edouard@741
  2278
    free_identity_list(private_il);
Edouard@741
  2279
roker@1853
  2280
    return ADD_TO_LOG(status);
Edouard@728
  2281
}
Edouard@728
  2282
Edouard@858
  2283
static void _max_comm_type_from_identity_list(
krista@1427
  2284
        identity_list *identities,
Edouard@858
  2285
        PEP_SESSION session,
Edouard@858
  2286
        PEP_comm_type *max_comm_type,
Edouard@858
  2287
        bool *comm_type_determined
Edouard@858
  2288
    )
Edouard@858
  2289
{
Edouard@858
  2290
    identity_list * il;
Edouard@858
  2291
    for (il = identities; il != NULL; il = il->next)
Edouard@858
  2292
    {
Edouard@858
  2293
        if (il->ident)
Edouard@858
  2294
        {
edouard@1385
  2295
            PEP_STATUS status = update_identity(session, il->ident);
Edouard@858
  2296
            if (status == PEP_STATUS_OK)
Edouard@858
  2297
            {
Edouard@858
  2298
                *max_comm_type = _get_comm_type(session, *max_comm_type,
Edouard@858
  2299
                        il->ident);
Edouard@858
  2300
                *comm_type_determined = true;
Edouard@858
  2301
            }
Edouard@858
  2302
        }
Edouard@858
  2303
    }
Edouard@858
  2304
}
Edouard@858
  2305
vb@1004
  2306
DYNAMIC_API PEP_STATUS outgoing_message_rating(
vb@190
  2307
        PEP_SESSION session,
vb@190
  2308
        message *msg,
vb@1004
  2309
        PEP_rating *rating
vb@190
  2310
    )
vb@190
  2311
{
vb@190
  2312
    PEP_comm_type max_comm_type = PEP_ct_pEp;
vb@190
  2313
    bool comm_type_determined = false;
vb@190
  2314
vb@190
  2315
    assert(session);
vb@190
  2316
    assert(msg);
vb@251
  2317
    assert(msg->dir == PEP_dir_outgoing);
vb@1004
  2318
    assert(rating);
vb@190
  2319
vb@1004
  2320
    if (!(session && msg && rating))
roker@1853
  2321
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
vb@191
  2322
vb@1360
  2323
    if (msg->dir != PEP_dir_outgoing)
roker@1853
  2324
        return ADD_TO_LOG(PEP_ILLEGAL_VALUE);
vb@251
  2325
vb@1004
  2326
    *rating = PEP_rating_undefined;
vb@190
  2327
Edouard@858
  2328
    _max_comm_type_from_identity_list(msg->to, session,
Edouard@858
  2329
                                      &max_comm_type, &comm_type_determined);
vb@190
  2330
Edouard@858
  2331
    _max_comm_type_from_identity_list(msg->cc, session,
Edouard@858
  2332
                                      &max_comm_type, &comm_type_determined);
krista@1427
  2333
Edouard@858
  2334
    _max_comm_type_from_identity_list(msg->bcc, session,
Edouard@858
  2335
                                      &max_comm_type, &comm_type_determined);
vb@190
  2336
vb@190
  2337
    if (comm_type_determined == false)
vb@1004
  2338
        *rating = PEP_rating_undefined;
vb@190
  2339
    else
vb@1004
  2340
        *rating = _MAX(_rating(max_comm_type, PEP_rating_undefined),
vb@486
  2341
                PEP_rating_unencrypted);
vb@190
  2342
vb@190
  2343
    return PEP_STATUS_OK;
vb@190
  2344
}
vb@190
  2345
vb@1004
  2346
DYNAMIC_API PEP_STATUS identity_rating(
vb@239
  2347
        PEP_SESSION session,
vb@239
  2348
        pEp_identity *ident,
vb@1004
  2349
        PEP_rating *rating
vb@239
  2350
    )
vb@239
  2351
{
vb@239
  2352
    PEP_STATUS status = PEP_STATUS_OK;
vb@239
  2353
vb@239
  2354
    assert(session);
vb@239
  2355
    assert(ident);
vb@1004
  2356
    assert(rating);
vb@239
  2357
vb@1004
  2358
    if (!(session && ident && rating))
vb@239
  2359
        return PEP_ILLEGAL_VALUE;
vb@239
  2360
edouard@1945
  2361
    if (_identity_me(ident))
edouard@1406
  2362
        status = _myself(session, ident, false, true);
vb@239
  2363
    else
vb@239
  2364
        status = update_identity(session, ident);
vb@239
  2365
vb@239
  2366
    if (status == PEP_STATUS_OK)
vb@1004
  2367
        *rating = _rating(ident->comm_type, PEP_rating_undefined);
vb@239
  2368
vb@239
  2369
    return status;
vb@239
  2370
}
vb@239
  2371
vb@507
  2372
DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
vb@507
  2373
{
vb@507
  2374
    PEP_STATUS status = PEP_STATUS_OK;
vb@507
  2375
vb@507
  2376
    assert(path);
vb@507
  2377
    if (path == NULL)
vb@507
  2378
        return PEP_ILLEGAL_VALUE;
vb@507
  2379
vb@507
  2380
    if (cryptotech[tech].binary_path == NULL)
vb@507
  2381
        *path = NULL;
vb@507
  2382
    else
vb@507
  2383
        status = cryptotech[tech].binary_path(path);
vb@507
  2384
vb@507
  2385
    return status;
vb@507
  2386
}
vb@507
  2387
vb@1004
  2388
vb@1004
  2389
DYNAMIC_API PEP_color color_from_rating(PEP_rating rating)
vb@1004
  2390
{
krista@1243
  2391
    if (rating == PEP_rating_b0rken || rating == PEP_rating_have_no_key)
vb@1004
  2392
        return PEP_color_no_color;
vb@1004
  2393
vb@1004
  2394
    if (rating < PEP_rating_undefined)
vb@1004
  2395
        return PEP_color_red;
vb@1004
  2396
vb@1004
  2397
    if (rating < PEP_rating_reliable)
vb@1004
  2398
        return PEP_color_no_color;
vb@1004
  2399
vb@1004
  2400
    if (rating < PEP_rating_trusted)
vb@1004
  2401
        return PEP_color_yellow;
vb@1004
  2402
vb@1004
  2403
    if (rating >= PEP_rating_trusted)
vb@1004
  2404
        return PEP_color_green;
vb@1004
  2405
vb@1004
  2406
    // this should never happen
vb@1004
  2407
    assert(false);
roker@1729
  2408
    return PEP_color_no_color;
krista@1312
  2409
}
krista@1312
  2410
krista@1856
  2411
/* [0-9]: 0x30 - 0x39; [A-F] = 0x41 - 0x46; [a-f] = 0x61 - 0x66 */
krista@1856
  2412
static short asciihex_to_num(char a) {
krista@1856
  2413
    short conv_num = -1;
krista@1856
  2414
    if (a >= 0x30 && a <= 0x39)
krista@1856
  2415
        conv_num = a - 0x30;
krista@1856
  2416
    else {
krista@1856
  2417
        // convert case, subtract offset, get number
krista@1856
  2418
        conv_num = ((a | 0x20) - 0x61) + 10;
krista@1856
  2419
        if (conv_num < 0xa || conv_num > 0xf)
krista@1856
  2420
            conv_num = -1;
krista@1856
  2421
    }
krista@1856
  2422
    return conv_num;
krista@1856
  2423
}
krista@1856
  2424
krista@1856
  2425
static char num_to_asciihex(short h) {
krista@1856
  2426
    if (h < 0 || h > 16)
krista@1856
  2427
        return '\0';
krista@1856
  2428
    if (h < 10)
krista@1856
  2429
        return (char)(h + 0x30);
krista@1856
  2430
    return (char)((h - 10) + 0x41); // for readability
krista@1856
  2431
}
krista@1856
  2432
krista@1856
  2433
static char xor_hex_chars(char a, char b) {
krista@1856
  2434
    short a_num = asciihex_to_num(a);
krista@1856
  2435
    short b_num = asciihex_to_num(b);
krista@1856
  2436
    if (a_num < 0 || b_num < 0)
krista@1856
  2437
        return '\0';
krista@1856
  2438
    short xor_num = a_num^b_num;
krista@1856
  2439
    return num_to_asciihex(xor_num);
krista@1856
  2440
}
krista@1856
  2441
krista@1864
  2442
static char* skip_separators(char* current, char* begin) {
krista@1864
  2443
    while (current >= begin) {
krista@1864
  2444
        /* .:,;-_ ' ' - [2c-2e] [3a-3b] [20] [5f] */
krista@1864
  2445
        char check_char = *current;
krista@1864
  2446
        switch (check_char) {
krista@1864
  2447
            case '.':
krista@1864
  2448
            case ':':
krista@1864
  2449
            case ',':
krista@1864
  2450
            case ';':
krista@1864
  2451
            case '-':
krista@1864
  2452
            case '_':
krista@1864
  2453
            case ' ':
krista@1864
  2454
                current--;
krista@1864
  2455
                continue;
krista@1864
  2456
            default:
krista@1864
  2457
                break;
krista@1864
  2458
        }
krista@1864
  2459
        break;
krista@1864
  2460
    }
krista@1864
  2461
    return current;
krista@1864
  2462
}
krista@1864
  2463
krista@1864
  2464
PEP_STATUS check_for_zero_fpr(char* fpr) {
krista@1864
  2465
    PEP_STATUS status = PEP_TRUSTWORDS_DUPLICATE_FPR;
krista@1864
  2466
    
krista@1864
  2467
    while (*fpr) {
krista@1864
  2468
        if (*fpr != '0') {