src/message_api.c
author Volker Birk <vb@pep.foundation>
Tue, 05 Nov 2019 06:24:35 +0100
branchsync
changeset 4149 d17296807b70
parent 4126 d6a2fa2086a0
child 4234 b720642cd9e0
permissions -rw-r--r--
Do not treat trailing zeros as part of the crypto text
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include "pEp_internal.h"
     5 #include "message_api.h"
     6 
     7 #include "platform.h"
     8 #include "mime.h"
     9 #include "blacklist.h"
    10 #include "baseprotocol.h"
    11 #include "KeySync_fsm.h"
    12 #include "base64.h"
    13 #include "resource_id.h"
    14 
    15 #include <assert.h>
    16 #include <string.h>
    17 #include <stdlib.h>
    18 #include <math.h>
    19 
    20 
    21 // These are globals used in generating message IDs and should only be
    22 // computed once, as they're either really constants or OS-dependent
    23 
    24 int _pEp_rand_max_bits;
    25 double _pEp_log2_36;
    26 
    27 static bool is_a_pEpmessage(const message *msg)
    28 {
    29     for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
    30         if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
    31             return true;
    32     }
    33     return false;
    34 }
    35 
    36 static char * keylist_to_string(const stringlist_t *keylist)
    37 {
    38     if (keylist) {
    39         size_t size = stringlist_length(keylist);
    40 
    41         const stringlist_t *_kl;
    42         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
    43             size += strlen(_kl->value);
    44         }
    45 
    46         char *result = calloc(size, 1);
    47         if (result == NULL)
    48             return NULL;
    49 
    50         char *_r = result;
    51         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
    52             _r = stpcpy(_r, _kl->value);
    53             if (_kl->next && _kl->next->value)
    54                 _r = stpcpy(_r, ",");
    55         }
    56 
    57         return result;
    58     }
    59     else {
    60         return NULL;
    61     }
    62 }
    63 
    64 static const char * rating_to_string(PEP_rating rating)
    65 {
    66     switch (rating) {
    67     case PEP_rating_cannot_decrypt:
    68         return "cannot_decrypt";
    69     case PEP_rating_have_no_key:
    70         return "have_no_key";
    71     case PEP_rating_unencrypted:
    72         return "unencrypted";
    73     case PEP_rating_unencrypted_for_some: // don't use this any more
    74         return "undefined";
    75     case PEP_rating_unreliable:
    76         return "unreliable";
    77     case PEP_rating_reliable:
    78         return "reliable";
    79     case PEP_rating_trusted:
    80         return "trusted";
    81     case PEP_rating_trusted_and_anonymized:
    82         return "trusted_and_anonymized";
    83     case PEP_rating_fully_anonymous:
    84         return "fully_anonymous";
    85     case PEP_rating_mistrust:
    86         return "mistrust";
    87     case PEP_rating_b0rken:
    88         return "b0rken";
    89     case PEP_rating_under_attack:
    90         return "under_attack";
    91     default:
    92         return "undefined";
    93     }
    94 }
    95 
    96 bool _memnmemn(const char* needle, 
    97                 size_t needle_size,
    98                 const char* haystack, 
    99                 size_t haystack_size) 
   100 {
   101     if (needle_size > haystack_size) {
   102         return false;
   103     }
   104     else if (needle_size == 0) {
   105         return true;
   106     }
   107                         
   108     bool found = true;
   109     const char* haystack_ptr = haystack;
   110     unsigned int i = 0;
   111     size_t remaining_hay = haystack_size;
   112     for (i = 0; i < haystack_size && (remaining_hay >= needle_size); i++, haystack_ptr++) {
   113         found = false;
   114         const char* needle_ptr = needle;
   115         if (*haystack_ptr == *needle) {
   116             const char* haystack_tmp = haystack_ptr;
   117             unsigned int j;
   118             found = true;
   119             for (j = 0; j < needle_size; j++) {
   120                 if (*needle_ptr++ != *haystack_tmp++) {
   121                     found = false;
   122                     break;
   123                 }
   124             }
   125             if (found)
   126                 break;
   127         }
   128         remaining_hay--;
   129     }
   130     return found;
   131 }
   132 
   133 void add_opt_field(message *msg, const char *name, const char *value)
   134 {
   135     assert(msg && name && value);
   136 
   137     if (msg && name && value) {
   138         stringpair_t *pair = new_stringpair(name, value);
   139         if (pair == NULL)
   140             return;
   141 
   142         stringpair_list_t *field = stringpair_list_add(msg->opt_fields, pair);
   143         if (field == NULL)
   144         {
   145             free_stringpair(pair);
   146             return;
   147         }
   148 
   149         if (msg->opt_fields == NULL)
   150             msg->opt_fields = field;
   151     }
   152 }
   153 
   154 void replace_opt_field(message *msg, 
   155                        const char *name, 
   156                        const char *value,
   157                        bool clobber)
   158 {
   159     assert(msg && name && value);
   160     
   161     if (msg && name && value) {
   162         stringpair_list_t* opt_fields = msg->opt_fields;
   163         stringpair_t* pair = NULL;
   164         
   165         if (opt_fields) {
   166             while (opt_fields) {
   167                 pair = opt_fields->value;
   168                 if (pair && (strcmp(name, pair->key) == 0))
   169                     break;
   170                     
   171                 pair = NULL;
   172                 opt_fields = opt_fields->next;
   173             }
   174         }
   175         
   176         if (pair) {
   177             if (clobber) {
   178                 free(pair->value);
   179                 pair->value = strdup(value);
   180             }
   181         }
   182         else {
   183             add_opt_field(msg, name, value);
   184         }
   185     }
   186 }
   187 
   188 void decorate_message(
   189     message *msg,
   190     PEP_rating rating,
   191     stringlist_t *keylist,
   192     bool add_version,
   193     bool clobber
   194     )
   195 {
   196     assert(msg);
   197 
   198     if (add_version)
   199         replace_opt_field(msg, "X-pEp-Version", PEP_VERSION, clobber);
   200 
   201     if (rating != PEP_rating_undefined)
   202         replace_opt_field(msg, "X-EncStatus", rating_to_string(rating), clobber);
   203 
   204     if (keylist) {
   205         char *_keylist = keylist_to_string(keylist);
   206         replace_opt_field(msg, "X-KeyList", _keylist, clobber);
   207         free(_keylist);
   208     }
   209 }
   210 
   211 static char* _get_resource_ptr_noown(char* uri) {
   212     char* uri_delim = strstr(uri, "://");
   213     if (!uri_delim)
   214         return uri;
   215     else
   216         return uri + 3;
   217 }
   218 
   219 static bool string_equality(const char *s1, const char *s2)
   220 {
   221     if (s1 == NULL || s2 == NULL)
   222         return false;
   223 
   224     assert(s1 && s2);
   225 
   226     return strcmp(s1, s2) == 0;
   227 }
   228 
   229 static bool is_mime_type(const bloblist_t *bl, const char *mt)
   230 {
   231     assert(mt);
   232 
   233     return bl && string_equality(bl->mime_type, mt);
   234 }
   235 
   236 //
   237 // This function presumes the file ending is a proper substring of the
   238 // filename (i.e. if bl->filename is "a.pgp" and fe is ".pgp", it will
   239 // return true, but if bl->filename is ".pgp" and fe is ".pgp", it will
   240 // return false. This is desired behaviour.
   241 //
   242 static bool is_fileending(const bloblist_t *bl, const char *fe)
   243 {
   244     assert(fe);
   245 
   246     if (bl == NULL || bl->filename == NULL || fe == NULL || is_cid_uri(bl->filename))
   247         return false;
   248 
   249     assert(bl && bl->filename);
   250 
   251     size_t fe_len = strlen(fe);
   252     size_t fn_len = strlen(bl->filename);
   253 
   254     if (fn_len <= fe_len)
   255         return false;
   256 
   257     assert(fn_len > fe_len);
   258 
   259     return strcmp(bl->filename + (fn_len - fe_len), fe) == 0;
   260 }
   261 
   262 char * encapsulate_message_wrap_info(const char *msg_wrap_info, const char *longmsg)
   263 {
   264     assert(msg_wrap_info);
   265     
   266     if (!msg_wrap_info) {
   267         if (!longmsg)
   268             return NULL;
   269         else {
   270             char *result = strdup(longmsg);
   271             assert(result);
   272             return result;            
   273         }    
   274     }
   275     
   276     if (longmsg == NULL)
   277         longmsg = "";
   278         
   279     const char * const newlines = "\n\n";
   280     const size_t NL_LEN = 2;
   281         
   282     const size_t bufsize = PEP_MSG_WRAP_KEY_LEN + strlen(msg_wrap_info) + NL_LEN + strlen(longmsg) + 1;
   283     char * ptext = calloc(bufsize, 1);
   284     assert(ptext);
   285     if (ptext == NULL)
   286         return NULL;
   287 
   288     strlcpy(ptext, PEP_MSG_WRAP_KEY, bufsize);
   289     strlcat(ptext, msg_wrap_info, bufsize);
   290     strlcat(ptext, newlines, bufsize);
   291     strlcat(ptext, longmsg, bufsize);
   292 
   293     return ptext;
   294 }
   295 
   296 static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
   297 {
   298     assert(shortmsg);
   299     
   300     unsigned char pEpstr[] = PEP_SUBJ_STRING;
   301     assert(strcmp(shortmsg, "pEp") != 0 && _unsigned_signed_strcmp(pEpstr, shortmsg, PEP_SUBJ_BYTELEN) != 0); 
   302     
   303     if (!shortmsg || strcmp(shortmsg, "pEp") == 0 || 
   304                      _unsigned_signed_strcmp(pEpstr, shortmsg, PEP_SUBJ_BYTELEN) == 0) {
   305         if (!longmsg) {
   306             return NULL;
   307         }
   308         else {
   309             char *result = strdup(longmsg);
   310             assert(result);
   311             return result;
   312         }
   313     }
   314 
   315     if (longmsg == NULL)
   316         longmsg = "";
   317 
   318     const char * const newlines = "\n\n";
   319     const size_t NL_LEN = 2;
   320 
   321     const size_t bufsize = PEP_SUBJ_KEY_LEN + strlen(shortmsg) + NL_LEN + strlen(longmsg) + 1;
   322     char * ptext = calloc(bufsize, 1);
   323     assert(ptext);
   324     if (ptext == NULL)
   325         return NULL;
   326 
   327     strlcpy(ptext, PEP_SUBJ_KEY, bufsize);
   328     strlcat(ptext, shortmsg, bufsize);
   329     strlcat(ptext, newlines, bufsize);
   330     strlcat(ptext, longmsg, bufsize);
   331 
   332     return ptext;
   333 }
   334 
   335 static PEP_STATUS replace_subject(message* msg) {
   336     unsigned char pEpstr[] = PEP_SUBJ_STRING;
   337     if (msg->shortmsg && *(msg->shortmsg) != '\0') {
   338         char* longmsg = combine_short_and_long(msg->shortmsg, msg->longmsg);
   339         if (!longmsg)
   340             return PEP_OUT_OF_MEMORY;
   341         else {
   342             free(msg->longmsg);
   343             msg->longmsg = longmsg;
   344         }
   345     }
   346     free(msg->shortmsg);
   347 #ifdef WIN32
   348     msg->shortmsg = strdup("pEp");
   349 #else
   350     msg->shortmsg = strdup((char*)pEpstr);
   351 #endif    
   352     
   353     if (!msg->shortmsg)
   354         return PEP_OUT_OF_MEMORY;
   355     
   356     return PEP_STATUS_OK;
   357 }
   358 
   359 unsigned long long get_bitmask(int num_bits) {
   360     if (num_bits <= 0)
   361         return 0;
   362         
   363     unsigned long long bitmask = 0;
   364     int i;
   365     for (i = 1; i < num_bits; i++) {
   366         bitmask = bitmask << 1;
   367         bitmask |= 1;
   368     }
   369     return bitmask;
   370 }
   371 
   372 static char* get_base_36_rep(unsigned long long value, int num_sig_bits) {
   373         
   374     int bufsize = ((int) ceil((double) num_sig_bits / _pEp_log2_36)) + 1;
   375     
   376     // based on
   377     // https://en.wikipedia.org/wiki/Base36#C_implementation
   378     // ok, we supposedly have a 64-bit kinda sorta random blob
   379     const char base_36_symbols[37] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   380 
   381     char* retbuf = calloc(bufsize, 1); 
   382     assert(retbuf);
   383     if (!retbuf)
   384         return NULL;
   385 
   386     int i = bufsize - 1; // (end index)
   387 
   388     while (i > 0) {
   389         retbuf[--i] = base_36_symbols[value % 36];
   390         value /= 36;
   391     }
   392 
   393     return retbuf;
   394 }
   395 
   396 
   397 static char* message_id_prand_part(void) {
   398     // RAND modulus
   399     int num_bits = _pEp_rand_max_bits;
   400 
   401     if (num_bits < 0)
   402         return NULL;
   403         
   404     const int DESIRED_BITS = 64;
   405 
   406     num_bits = MIN(num_bits, DESIRED_BITS);
   407     
   408     int i;
   409     
   410     // at least 64 bits
   411     unsigned long long bitmask = get_bitmask(num_bits);
   412     
   413     unsigned long long output_value = 0;
   414     
   415     i = DESIRED_BITS;
   416     
   417     while (i > 0) {
   418         int bitshift = 0;
   419         int randval = rand();
   420         unsigned long long temp_val = randval & bitmask;
   421 
   422         output_value |= temp_val;
   423 
   424         i -= MIN(num_bits, i); 
   425         
   426         bitshift = MIN(num_bits, i);
   427         output_value <<= bitshift;        
   428         bitmask = get_bitmask(bitshift);
   429     }
   430 
   431     return get_base_36_rep(output_value, DESIRED_BITS);
   432 }
   433 
   434 static PEP_STATUS generate_message_id(message* msg) {
   435 
   436     if (!msg || !msg->from || !msg->from->address)
   437         return PEP_ILLEGAL_VALUE;
   438 
   439     char* time_prefix = NULL;
   440     char* random_id = NULL;
   441     char* retval = NULL;
   442     
   443     size_t buf_len = 2; // NUL + @
   444     
   445     char* from_addr = msg->from->address;
   446     char* domain_ptr = strstr(from_addr, "@");
   447     if (!domain_ptr || *(domain_ptr + 1) == '\0')
   448         domain_ptr = "localhost";
   449     else
   450         domain_ptr++;
   451 
   452     buf_len += strlen(domain_ptr);
   453     
   454     if (msg->id)
   455         free(msg->id);
   456 
   457     msg->id = NULL;
   458     
   459     time_t curr_time = time(NULL);
   460     
   461     time_prefix = get_base_36_rep(curr_time, (int) ceil(log2((double) curr_time)));
   462 
   463     if (!time_prefix)
   464         goto enomem;
   465     
   466     buf_len += strlen(time_prefix);
   467 
   468     random_id = message_id_prand_part();
   469 
   470     if (!random_id)
   471         goto enomem;
   472     
   473         
   474     buf_len += strlen(random_id);
   475     
   476     // make a new uuid - depending on rand() impl, time precision, etc,
   477     // we may still not be unique. We'd better make sure. So. 
   478     char new_uuid[37];
   479     pEpUUID uuid;
   480     uuid_generate_random(uuid);
   481     uuid_unparse_upper(uuid, new_uuid);
   482 
   483     buf_len += strlen(new_uuid);
   484 
   485     buf_len += 6; // "pEp" and 3 '.' chars
   486 
   487     retval = calloc(buf_len, 1);
   488     
   489     if (!retval)
   490         goto enomem;
   491     
   492     strlcpy(retval, "pEp.", buf_len);
   493     strlcat(retval, time_prefix, buf_len);
   494     strlcat(retval, ".", buf_len);
   495     strlcat(retval, random_id, buf_len);
   496     strlcat(retval, ".", buf_len);
   497     strlcat(retval, new_uuid, buf_len);        
   498     strlcat(retval, "@", buf_len);    
   499     strlcat(retval, domain_ptr, buf_len);    
   500 
   501     msg->id = retval;
   502     
   503     free(time_prefix);
   504     free(random_id);
   505     
   506     return PEP_STATUS_OK;
   507         
   508 enomem:
   509     free(time_prefix);
   510     free(random_id);
   511     return PEP_OUT_OF_MEMORY;
   512 }
   513 
   514 /* 
   515    WARNING: For the moment, this only works for the first line of decrypted
   516    plaintext because we don't need more. IF WE DO, THIS MUST BE EXPANDED, or
   517    we need a delineated section to parse separately
   518    
   519    Does case-insensitive compare of keys, so sending in a lower-cased
   520    string constant saves a bit of computation
   521  */
   522 static PEP_STATUS get_data_from_encapsulated_line(const char* plaintext, const char* key, 
   523                                                   const size_t keylen, char** data, 
   524                                                   char** modified_msg) {
   525     char* _data = NULL;
   526     char* _modified = NULL;
   527     
   528     if (strncasecmp(plaintext, key, keylen) == 0) {
   529         const char *line_end = strchr(plaintext, '\n');
   530 
   531         if (line_end == NULL) {
   532             _data = strdup(plaintext + keylen);
   533             assert(_data);
   534             if (_data == NULL)
   535                 return PEP_OUT_OF_MEMORY;
   536         }
   537         else {
   538             size_t n = line_end - plaintext;
   539 
   540             if (*(line_end - 1) == '\r')
   541                 _data = strndup(plaintext + keylen, n - (keylen + 1));
   542             else
   543                 _data = strndup(plaintext + keylen, n - keylen);
   544             assert(_data);
   545             if (_data == NULL)
   546                 return PEP_OUT_OF_MEMORY;
   547 
   548             while (*(plaintext + n) && (*(plaintext + n) == '\n' || *(plaintext + n) == '\r'))
   549                 ++n;
   550 
   551             if (*(plaintext + n)) {
   552                 _modified = strdup(plaintext + n);
   553                 assert(_modified);
   554                 if (_modified == NULL)
   555                     return PEP_OUT_OF_MEMORY;
   556             }
   557         }
   558     }
   559     *data = _data;
   560     *modified_msg = _modified;
   561     return PEP_STATUS_OK;
   562 }
   563 
   564 
   565 static int separate_short_and_long(const char *src, char **shortmsg, char** msg_wrap_info, char **longmsg)
   566 {
   567     char *_shortmsg = NULL;
   568     char *_msg_wrap_info = NULL;
   569     char *_longmsg = NULL;
   570 
   571     assert(src);
   572     assert(shortmsg);
   573     assert(msg_wrap_info);
   574     assert(longmsg);
   575 
   576     if (src == NULL || shortmsg == NULL || msg_wrap_info == NULL || longmsg == NULL)
   577         return -1;
   578 
   579     *shortmsg = NULL;
   580     *longmsg = NULL;
   581     *msg_wrap_info = NULL;
   582 
   583     // We generated the input here. If we ever need more than one header value to be
   584     // encapsulated and hidden in the encrypted text, we will have to modify this.
   585     // As is, we're either doing this with a version 1.0 client, in which case
   586     // the only encapsulated header value is subject, or 2.0+, in which the
   587     // message wrap info is the only encapsulated header value. If we need this
   588     // to be more complex, we're going to have to do something more elegant
   589     // and efficient.    
   590     PEP_STATUS status = get_data_from_encapsulated_line(src, PEP_SUBJ_KEY_LC, 
   591                                                         PEP_SUBJ_KEY_LEN, 
   592                                                         &_shortmsg, &_longmsg);
   593                                                         
   594     if (_shortmsg) {
   595         if (status == PEP_STATUS_OK)
   596             *shortmsg = _shortmsg;
   597         else
   598             goto enomem;
   599     }
   600     else {
   601         status = get_data_from_encapsulated_line(src, PEP_MSG_WRAP_KEY_LC, 
   602                                                  PEP_MSG_WRAP_KEY_LEN, 
   603                                                  &_msg_wrap_info, &_longmsg);
   604         if (_msg_wrap_info) {
   605             if (status == PEP_STATUS_OK)
   606                 *msg_wrap_info = _msg_wrap_info;
   607             else
   608                 goto enomem;
   609         }
   610     }
   611     
   612     // If there was no secret data hiding in the first line...
   613     if (!_shortmsg && !_msg_wrap_info) {
   614         _longmsg = strdup(src);
   615         assert(_longmsg);
   616         if (_longmsg == NULL)
   617             goto enomem;
   618     }
   619     
   620     *longmsg = _longmsg;
   621 
   622     return 0;
   623 
   624 enomem:
   625     free(_shortmsg);
   626     free(_msg_wrap_info);
   627     free(_longmsg);
   628 
   629     return -1;
   630 }
   631 
   632 static PEP_STATUS copy_fields(message *dst, const message *src)
   633 {
   634     assert(dst);
   635     assert(src);
   636 
   637     if(!(dst && src))
   638         return PEP_ILLEGAL_VALUE;
   639 
   640     free_timestamp(dst->sent);
   641     dst->sent = NULL;
   642     if (src->sent) {
   643         dst->sent = timestamp_dup(src->sent);
   644         if (dst->sent == NULL)
   645             return PEP_OUT_OF_MEMORY;
   646     }
   647 
   648     free_timestamp(dst->recv);
   649     dst->recv = NULL;
   650     if (src->recv) {
   651         dst->recv = timestamp_dup(src->recv);
   652         if (dst->recv == NULL)
   653             return PEP_OUT_OF_MEMORY;
   654     }
   655 
   656     free_identity(dst->from);
   657     dst->from = NULL;
   658     if (src->from) {
   659         dst->from = identity_dup(src->from);
   660         if (dst->from == NULL)
   661             return PEP_OUT_OF_MEMORY;
   662     }
   663 
   664     free_identity_list(dst->to);
   665     dst->to = NULL;
   666     if (src->to && src->to->ident) {
   667         dst->to = identity_list_dup(src->to);
   668         if (dst->to == NULL)
   669             return PEP_OUT_OF_MEMORY;
   670     }
   671 
   672     free_identity(dst->recv_by);
   673     dst->recv_by = NULL;
   674     if (src->recv_by) {
   675         dst->recv_by = identity_dup(src->recv_by);
   676         if (dst->recv_by == NULL)
   677             return PEP_OUT_OF_MEMORY;
   678     }
   679 
   680     free_identity_list(dst->cc);
   681     dst->cc = NULL;
   682     if (src->cc && src->cc->ident) {
   683         dst->cc = identity_list_dup(src->cc);
   684         if (dst->cc == NULL)
   685             return PEP_OUT_OF_MEMORY;
   686     }
   687 
   688     free_identity_list(dst->bcc);
   689     dst->bcc = NULL;
   690     if (src->bcc && src->bcc->ident) {
   691         dst->bcc = identity_list_dup(src->bcc);
   692         if (dst->bcc == NULL)
   693             return PEP_OUT_OF_MEMORY;
   694     }
   695 
   696     free_identity_list(dst->reply_to);
   697     dst->reply_to = NULL;
   698     if (src->reply_to && src->reply_to->ident) {
   699         dst->reply_to = identity_list_dup(src->reply_to);
   700         if (dst->reply_to == NULL)
   701             return PEP_OUT_OF_MEMORY;
   702     }
   703 
   704     free_stringlist(dst->in_reply_to);
   705     dst->in_reply_to = NULL;
   706     if (src->in_reply_to && src->in_reply_to->value) {
   707         dst->in_reply_to = stringlist_dup(src->in_reply_to);
   708         if (dst->in_reply_to == NULL)
   709             return PEP_OUT_OF_MEMORY;
   710     }
   711 
   712     free_stringlist(dst->references);
   713     dst->references = NULL;
   714     if (src->references) {
   715         dst->references = stringlist_dup(src->references);
   716         if (dst->references == NULL)
   717             return PEP_OUT_OF_MEMORY;
   718     }
   719 
   720     free_stringlist(dst->keywords);
   721     dst->keywords = NULL;
   722     if (src->keywords && src->keywords->value) {
   723         dst->keywords = stringlist_dup(src->keywords);
   724         if (dst->keywords == NULL)
   725             return PEP_OUT_OF_MEMORY;
   726     }
   727 
   728     free(dst->comments);
   729     dst->comments = NULL;
   730     if (src->comments) {
   731         dst->comments = strdup(src->comments);
   732         assert(dst->comments);
   733         if (dst->comments == NULL)
   734             return PEP_OUT_OF_MEMORY;
   735     }
   736 
   737     free_stringpair_list(dst->opt_fields);
   738     dst->opt_fields = NULL;
   739     if (src->opt_fields) {
   740         dst->opt_fields = stringpair_list_dup(src->opt_fields);
   741         if (dst->opt_fields == NULL)
   742             return PEP_OUT_OF_MEMORY;
   743     }
   744 
   745     return PEP_STATUS_OK;
   746 }
   747 
   748 // FIXME: error mem leakage
   749 static message* extract_minimal_envelope(const message* src, 
   750                                          PEP_msg_direction direct) {
   751                                                  
   752     message* envelope = new_message(direct);
   753     if (!envelope)
   754         return NULL;
   755         
   756     envelope->shortmsg = _pEp_subj_copy();
   757     if (!envelope->shortmsg)
   758         goto enomem;
   759 
   760     if (src->from) {
   761         envelope->from = identity_dup(src->from);
   762         if (!envelope->from)
   763             goto enomem;
   764     }
   765 
   766     if (src->to) {
   767         envelope->to = identity_list_dup(src->to);
   768         if (!envelope->to)
   769             goto enomem;
   770     }
   771 
   772     if (src->cc) {
   773         envelope->cc = identity_list_dup(src->cc);
   774         if (!envelope->cc)
   775             goto enomem;
   776     }
   777 
   778     if (src->bcc) {
   779         envelope->bcc = identity_list_dup(src->bcc);
   780         if (!envelope->bcc)
   781             goto enomem;
   782     }
   783 
   784     // For Outlook Force-Encryption
   785     // const char* pull_keys[] = {"pEp-auto-consume",
   786     //                            "pEp-force-protection",
   787     //                            "X-pEp-Never-Unsecure"};
   788     // int pull_keys_len = 3; // UPDATE WHEN MORE ADDED ABOVE
   789     // 
   790     // int i = 0;
   791     // stringpair_t* opt_add = NULL;    
   792     // for( ; i < pull_keys_len; i++) {        
   793     //     opt_add = search_optfields(src, pull_keys[i]);
   794     //     stringpair_list_t* add_ptr = NULL;
   795     //     if (opt_add) {
   796     //         add_ptr = stringpair_list_add(src->opt_fields, stringpair_dup(opt_add));
   797     //         if (!add_ptr)
   798     //             goto enomem;
   799     //     }
   800     //     opt_add = NULL;
   801     //     add_ptr = NULL;
   802     // }
   803         
   804     envelope->enc_format = src->enc_format;        
   805     
   806     return envelope;
   807     
   808 enomem:
   809     free(envelope);
   810     return NULL;
   811 }
   812 
   813 static message * clone_to_empty_message(const message * src)
   814 {
   815     PEP_STATUS status;
   816     message * msg = NULL;
   817 
   818     assert(src);
   819     if (src == NULL)
   820         return NULL;
   821 
   822     msg = calloc(1, sizeof(message));
   823     assert(msg);
   824     if (msg == NULL)
   825         goto enomem;
   826 
   827     msg->dir = src->dir;
   828 
   829     status = copy_fields(msg, src);
   830     if (status != PEP_STATUS_OK)
   831         goto enomem;
   832 
   833     return msg;
   834 
   835 enomem:
   836     free_message(msg);
   837     return NULL;
   838 }
   839 
   840 static message* wrap_message_as_attachment(message* envelope, 
   841     message* attachment, message_wrap_type wrap_type, bool keep_orig_subject, unsigned int max_major, unsigned int max_minor) {
   842     
   843     if (!attachment)
   844         return NULL;
   845     
   846     message* _envelope = envelope;
   847 
   848     PEP_STATUS status = PEP_STATUS_OK;
   849 
   850     replace_opt_field(attachment, "X-pEp-Version", PEP_VERSION, true);
   851         
   852     if (!_envelope && (wrap_type != PEP_message_transport)) {
   853         _envelope = extract_minimal_envelope(attachment, PEP_dir_outgoing);
   854         status = generate_message_id(_envelope);
   855         
   856         if (status != PEP_STATUS_OK)
   857             goto enomem;
   858         
   859         const char* inner_type_string = "";
   860         switch (wrap_type) {
   861             case PEP_message_key_reset:
   862                 inner_type_string = "KEY_RESET";
   863                 break;
   864             default:
   865                 inner_type_string = "INNER";
   866         }
   867         if (max_major < 2 || (max_major == 2 && max_minor == 0)) {
   868             attachment->longmsg = encapsulate_message_wrap_info(inner_type_string, attachment->longmsg);        
   869             _envelope->longmsg = encapsulate_message_wrap_info("OUTER", _envelope->longmsg);
   870         }
   871         else {
   872             _envelope->longmsg = strdup(
   873                 "This message was encrypted with p≡p (https://pep.software). If you are seeing this message,\n" 
   874                 "your client does not support raising message attachments. Please click on the message attachment to\n"
   875                 "to view it, or better yet, consider using p≡p!\n"
   876             );
   877         }
   878         // 2.1, to replace the above
   879         add_opt_field(attachment, X_PEP_MSG_WRAP_KEY, inner_type_string); 
   880     }
   881     else if (_envelope) {
   882         // 2.1 - how do we peel this particular union when we get there?
   883         _envelope->longmsg = encapsulate_message_wrap_info("TRANSPORT", _envelope->longmsg);
   884     }
   885     else {
   886         return NULL;
   887     }
   888     
   889     if (!attachment->id || attachment->id[0] == '\0') {
   890         free(attachment->id);
   891         if (!_envelope->id) {
   892             status = generate_message_id(_envelope);
   893         
   894             if (status != PEP_STATUS_OK)
   895                 goto enomem;
   896         }
   897             
   898         attachment->id = strdup(_envelope->id);
   899     }
   900     
   901     char* message_text = NULL;
   902 
   903     /* prevent introduction of pEp in inner message */
   904 
   905     if (!attachment->shortmsg) {
   906         attachment->shortmsg = strdup("");
   907         if (!attachment->shortmsg)
   908             goto enomem;
   909     }
   910     
   911     /* add sender fpr to inner message */
   912     add_opt_field(attachment, 
   913                   "X-pEp-Sender-FPR", 
   914                   (attachment->_sender_fpr ? attachment->_sender_fpr : "")
   915               );
   916             
   917     /* Turn message into a MIME-blob */
   918     status = _mime_encode_message_internal(attachment, false, &message_text, true, false);
   919         
   920     if (status != PEP_STATUS_OK)
   921         goto enomem;
   922     
   923     size_t message_len = strlen(message_text);
   924     
   925     bloblist_t* message_blob = new_bloblist(message_text, message_len,
   926                                             "message/rfc822", NULL);
   927     
   928     _envelope->attachments = message_blob;
   929     if (keep_orig_subject && attachment->shortmsg)
   930         _envelope->shortmsg = strdup(attachment->shortmsg);
   931     return _envelope;
   932     
   933 enomem:
   934     if (!envelope) {
   935         free_message(_envelope);
   936     }
   937     return NULL;    
   938 }
   939 
   940 static PEP_STATUS encrypt_PGP_inline(
   941         PEP_SESSION session,
   942         const message *src,
   943         stringlist_t *keys,
   944         message *dst,
   945         PEP_encrypt_flags_t flags
   946     )
   947 {
   948     char *ctext = NULL;
   949     size_t csize = 0;
   950 
   951     PEP_STATUS status = encrypt_and_sign(session, keys, src->longmsg,
   952             strlen(src->longmsg), &ctext, &csize);
   953     if (status)
   954         return status;
   955 
   956     dst->enc_format = PEP_enc_inline;
   957 
   958     // shortmsg is being copied
   959     if (src->shortmsg) {
   960         dst->shortmsg = strdup(src->shortmsg);
   961         assert(dst->shortmsg);
   962         if (!dst->shortmsg)
   963             return PEP_OUT_OF_MEMORY;
   964     }
   965 
   966     // id is staying the same
   967     if (src->id) {
   968         dst->id = strdup(src->id);
   969         assert(dst->id);
   970         if (!dst->id)
   971             return PEP_OUT_OF_MEMORY;
   972     }
   973 
   974     char *_ctext = realloc(ctext, csize + 1);
   975     assert(_ctext);
   976     if (!_ctext)
   977         return PEP_OUT_OF_MEMORY;
   978     _ctext[csize] = 0;
   979 
   980     dst->longmsg = _ctext;
   981 
   982     // longmsg_formatted is unsupported
   983 
   984     // attachments are going unencrypted
   985     bloblist_t *bl = bloblist_dup(src->attachments);
   986     if (!bl)
   987         return PEP_OUT_OF_MEMORY;
   988     dst->attachments = bl;
   989 
   990     return PEP_STATUS_OK;
   991 }
   992 
   993 static PEP_STATUS encrypt_PGP_MIME(
   994     PEP_SESSION session,
   995     const message *src,
   996     stringlist_t *keys,
   997     message *dst,
   998     PEP_encrypt_flags_t flags,
   999     message_wrap_type wrap_type
  1000     )
  1001 {
  1002     PEP_STATUS status = PEP_STATUS_OK;
  1003     bool free_ptext = false;
  1004     char *ptext = NULL;
  1005     char *ctext = NULL;
  1006     char *mimetext = NULL;
  1007     size_t csize;
  1008     assert(dst->longmsg == NULL);
  1009     dst->enc_format = PEP_enc_PGP_MIME;
  1010 
  1011     if (src->shortmsg)
  1012         dst->shortmsg = strdup(src->shortmsg);
  1013         
  1014     message *_src = calloc(1, sizeof(message));
  1015     assert(_src);
  1016     if (_src == NULL)
  1017         goto enomem;
  1018 //    _src->longmsg = ptext;
  1019     _src->longmsg = src->longmsg;
  1020     _src->longmsg_formatted = src->longmsg_formatted;
  1021     _src->attachments = src->attachments;
  1022     _src->enc_format = PEP_enc_none;
  1023     
  1024     // These vars are here to be clear, and because I don't know how this may change in the near future.
  1025     bool wrapped = (wrap_type != PEP_message_unwrapped);
  1026     bool mime_encode = !wrapped;
  1027     status = _mime_encode_message_internal(_src, true, &mimetext, mime_encode, wrapped);
  1028     assert(status == PEP_STATUS_OK);
  1029     if (status != PEP_STATUS_OK)
  1030         goto pEp_error;
  1031 
  1032     if (free_ptext){
  1033         free(ptext);
  1034         free_ptext=0;
  1035     }
  1036     free(_src);
  1037     _src = NULL;
  1038     assert(mimetext);
  1039     if (mimetext == NULL)
  1040         goto pEp_error;
  1041 
  1042     if (flags & PEP_encrypt_flag_force_unsigned)
  1043         status = encrypt_only(session, keys, mimetext, strlen(mimetext),
  1044             &ctext, &csize);
  1045     else
  1046         status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
  1047             &ctext, &csize);
  1048     free(mimetext);
  1049     if (ctext == NULL)
  1050         goto pEp_error;
  1051 
  1052     dst->longmsg = strdup("this message was encrypted with p≡p "
  1053         "https://pEp-project.org");
  1054     assert(dst->longmsg);
  1055     if (dst->longmsg == NULL)
  1056         goto enomem;
  1057 
  1058     char *v = strdup("Version: 1");
  1059     assert(v);
  1060     if (v == NULL)
  1061         goto enomem;
  1062 
  1063     bloblist_t *_a = new_bloblist(v, strlen(v), "application/pgp-encrypted", NULL);
  1064     if (_a == NULL)
  1065         goto enomem;
  1066     dst->attachments = _a;
  1067 
  1068     _a = bloblist_add(_a, ctext, csize, "application/octet-stream",
  1069         "file://msg.asc");
  1070     if (_a == NULL)
  1071         goto enomem;
  1072 
  1073     return PEP_STATUS_OK;
  1074 
  1075 enomem:
  1076     status = PEP_OUT_OF_MEMORY;
  1077 
  1078 pEp_error:
  1079     if (free_ptext)
  1080         free(ptext);
  1081     free(_src);
  1082     free(ctext);
  1083     return status;
  1084 }
  1085 
  1086 /*
  1087 static bool _has_PGP_MIME_format(message* msg) {
  1088     if (!msg || !msg->attachments || !msg->attachments->next)
  1089         return false;
  1090     if (msg->attachments->next->next)
  1091         return false;
  1092     if (!msg->attachments->mime_type)
  1093         return false;        
  1094     if (strcmp(msg->attachments->mime_type, "application/pgp-encrypted") != 0)    
  1095         return false;
  1096     if (!msg->attachments->next->mime_type || 
  1097         strcmp(msg->attachments->next->mime_type, "application/octet-stream") != 0)        
  1098         return false;
  1099     return true;    
  1100 }
  1101 */
  1102 
  1103 static inline PEP_rating _rating(PEP_comm_type ct)
  1104 {
  1105     if (ct == PEP_ct_unknown)
  1106         return PEP_rating_undefined;
  1107 
  1108     else if (ct == PEP_ct_key_not_found)
  1109         return PEP_rating_have_no_key;
  1110 
  1111     else if (ct == PEP_ct_compromised)
  1112         return PEP_rating_under_attack;
  1113 
  1114     else if (ct == PEP_ct_mistrusted)
  1115         return PEP_rating_mistrust;
  1116 
  1117     if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel ||
  1118             ct == PEP_ct_my_key_not_included)
  1119             return PEP_rating_unencrypted;
  1120 
  1121     if (ct >= PEP_ct_confirmed_enc_anon)
  1122         return PEP_rating_trusted_and_anonymized;
  1123 
  1124     else if (ct >= PEP_ct_strong_encryption)
  1125         return PEP_rating_trusted;
  1126 
  1127     else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
  1128         return PEP_rating_reliable;
  1129 
  1130     else
  1131         return PEP_rating_unreliable;
  1132 }
  1133 
  1134 DYNAMIC_API PEP_rating rating_from_comm_type(PEP_comm_type ct)
  1135 {
  1136     return _rating(ct);
  1137 }
  1138 
  1139 static bool is_encrypted_attachment(const bloblist_t *blob)
  1140 {
  1141     assert(blob);
  1142 
  1143     if (blob == NULL || blob->filename == NULL || is_cid_uri(blob->filename))
  1144         return false;
  1145 
  1146     char *ext = strrchr(blob->filename, '.');
  1147     if (ext == NULL)
  1148         return false;
  1149 
  1150     if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
  1151         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0)
  1152             return true;
  1153     }
  1154     if (strcmp(ext, ".asc") == 0 && blob->size > 0) {            
  1155         const char* pubk_needle = "BEGIN PGP PUBLIC KEY";
  1156         size_t pubk_needle_size = strlen(pubk_needle);
  1157         const char* privk_needle = "BEGIN PGP PRIVATE KEY";
  1158         size_t privk_needle_size = strlen(privk_needle);
  1159 
  1160         if (!(_memnmemn(pubk_needle, pubk_needle_size, blob->value, blob->size)) &&
  1161             !(_memnmemn(privk_needle, privk_needle_size, blob->value, blob->size)))
  1162             return true;
  1163     }
  1164 
  1165     return false;
  1166 }
  1167 
  1168 static bool is_encrypted_html_attachment(const bloblist_t *blob)
  1169 {
  1170     assert(blob);
  1171     assert(blob->filename);
  1172     if (blob == NULL || blob->filename == NULL || is_cid_uri(blob->filename))
  1173         return false;
  1174 
  1175     const char* bare_filename_ptr = _get_resource_ptr_noown(blob->filename);
  1176     bare_filename_ptr += strlen(bare_filename_ptr) - 15;
  1177     if (strncmp(bare_filename_ptr, "PGPexch.htm.", 12) == 0) {
  1178         if (strcmp(bare_filename_ptr + 11, ".pgp") == 0 ||
  1179             strcmp(bare_filename_ptr + 11, ".asc") == 0)
  1180             return true;
  1181     }
  1182 
  1183     return false;
  1184 }
  1185 
  1186 static char * without_double_ending(const char *filename)
  1187 {
  1188     assert(filename);
  1189     if (filename == NULL || is_cid_uri(filename))
  1190         return NULL;
  1191 
  1192     char *ext = strrchr(filename, '.');
  1193     if (ext == NULL)
  1194         return NULL;
  1195 
  1196     char *result = strndup(filename, ext - filename);
  1197     assert(result);
  1198     return result;
  1199 }
  1200 
  1201 static PEP_rating decrypt_rating(PEP_STATUS status)
  1202 {
  1203     switch (status) {
  1204     case PEP_UNENCRYPTED:
  1205     case PEP_VERIFIED:
  1206     case PEP_VERIFY_NO_KEY:
  1207     case PEP_VERIFIED_AND_TRUSTED:
  1208         return PEP_rating_unencrypted;
  1209 
  1210     case PEP_DECRYPTED:
  1211     case PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH:
  1212         return PEP_rating_unreliable;
  1213 
  1214     case PEP_DECRYPTED_AND_VERIFIED:
  1215         return PEP_rating_reliable;
  1216 
  1217     case PEP_DECRYPT_NO_KEY:
  1218         return PEP_rating_have_no_key;
  1219 
  1220     case PEP_DECRYPT_WRONG_FORMAT:
  1221     case PEP_CANNOT_DECRYPT_UNKNOWN:
  1222         return PEP_rating_cannot_decrypt;
  1223 
  1224     default:
  1225         return PEP_rating_undefined;
  1226     }
  1227 }
  1228 
  1229 static PEP_rating key_rating(PEP_SESSION session, const char *fpr)
  1230 {
  1231 
  1232     assert(session);
  1233     assert(fpr);
  1234 
  1235     if (session == NULL || fpr == NULL)
  1236         return PEP_rating_undefined;
  1237 
  1238 
  1239     PEP_comm_type bare_comm_type = PEP_ct_unknown;
  1240     PEP_comm_type resulting_comm_type = PEP_ct_unknown;
  1241     PEP_STATUS status = get_key_rating(session, fpr, &bare_comm_type);
  1242     if (status != PEP_STATUS_OK)
  1243         return PEP_rating_undefined;
  1244 
  1245     PEP_comm_type least_comm_type = PEP_ct_unknown;
  1246     least_trust(session, fpr, &least_comm_type);
  1247 
  1248     if (least_comm_type == PEP_ct_unknown) {
  1249         resulting_comm_type = bare_comm_type;
  1250     } else if (least_comm_type < PEP_ct_strong_but_unconfirmed ||
  1251                bare_comm_type < PEP_ct_strong_but_unconfirmed) {
  1252         // take minimum if anything bad
  1253         resulting_comm_type = least_comm_type < bare_comm_type ? 
  1254                               least_comm_type : 
  1255                               bare_comm_type;
  1256     } else {
  1257         resulting_comm_type = least_comm_type;
  1258     }
  1259     return _rating(resulting_comm_type);
  1260 }
  1261 
  1262 static PEP_rating worst_rating(PEP_rating rating1, PEP_rating rating2) {
  1263     return ((rating1 < rating2) ? rating1 : rating2);
  1264 }
  1265 
  1266 static PEP_rating keylist_rating(PEP_SESSION session, stringlist_t *keylist, char* sender_fpr, PEP_rating sender_rating)
  1267 {
  1268     PEP_rating rating = sender_rating;
  1269 
  1270     assert(keylist && keylist->value);
  1271     if (keylist == NULL || keylist->value == NULL)
  1272         return PEP_rating_undefined;
  1273 
  1274     stringlist_t *_kl;
  1275     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
  1276 
  1277         // Ignore own fpr
  1278         if(_same_fpr(sender_fpr, strlen(sender_fpr), _kl->value, strlen(_kl->value)))
  1279             continue;
  1280 
  1281         PEP_rating _rating_ = key_rating(session, _kl->value);
  1282          
  1283         if (_rating_ <= PEP_rating_mistrust)
  1284             return _rating_;
  1285             
  1286         rating = worst_rating(rating, _rating_);
  1287     }
  1288 
  1289     return rating;
  1290 }
  1291 
  1292 // Internal function WARNING:
  1293 // Only call this on an ident that might have its FPR set from retrieval!
  1294 // (or on one without an fpr)
  1295 // We do not want myself() setting the fpr here.
  1296 static PEP_comm_type _get_comm_type(
  1297     PEP_SESSION session,
  1298     PEP_comm_type max_comm_type,
  1299     pEp_identity *ident
  1300     )
  1301 {
  1302     PEP_STATUS status = PEP_STATUS_OK;
  1303 
  1304     if (max_comm_type == PEP_ct_compromised)
  1305         return PEP_ct_compromised;
  1306 
  1307     if (max_comm_type == PEP_ct_mistrusted)
  1308         return PEP_ct_mistrusted;
  1309 
  1310     if (!is_me(session, ident))
  1311         status = update_identity(session, ident);
  1312     else
  1313         // ???
  1314         status = _myself(session, ident, false, false, true);
  1315 
  1316     if (status == PEP_STATUS_OK) {
  1317         if (ident->comm_type == PEP_ct_compromised)
  1318             return PEP_ct_compromised;
  1319         else if (ident->comm_type == PEP_ct_mistrusted)
  1320             return PEP_ct_mistrusted;
  1321         else
  1322             return MIN(max_comm_type, ident->comm_type);
  1323     }
  1324     else {
  1325         return PEP_ct_unknown;
  1326     }
  1327 }
  1328 
  1329 static PEP_comm_type _get_comm_type_preview(
  1330     PEP_SESSION session,
  1331     PEP_comm_type max_comm_type,
  1332     pEp_identity *ident
  1333     )
  1334 {
  1335     assert(session);
  1336     assert(ident);
  1337 
  1338     PEP_STATUS status = PEP_STATUS_OK;
  1339 
  1340     if (max_comm_type == PEP_ct_compromised)
  1341         return PEP_ct_compromised;
  1342 
  1343     if (max_comm_type == PEP_ct_mistrusted)
  1344         return PEP_ct_mistrusted;
  1345 
  1346     PEP_comm_type comm_type = PEP_ct_unknown;
  1347     if (ident && !EMPTYSTR(ident->address) && !EMPTYSTR(ident->user_id)) {
  1348         pEp_identity *ident2;
  1349         status = get_identity(session, ident->address, ident->user_id, &ident2);
  1350         comm_type = ident2 ? ident2->comm_type : PEP_ct_unknown;
  1351         free_identity(ident2);
  1352 
  1353         if (status == PEP_STATUS_OK) {
  1354             if (comm_type == PEP_ct_compromised)
  1355                 comm_type = PEP_ct_compromised;
  1356             else if (comm_type == PEP_ct_mistrusted)
  1357                 comm_type = PEP_ct_mistrusted;
  1358             else
  1359                 comm_type = _MIN(max_comm_type, comm_type);
  1360         }
  1361         else {
  1362             comm_type = PEP_ct_unknown;
  1363         }
  1364     }
  1365     return comm_type;
  1366 }
  1367 
  1368 // static void free_bl_entry(bloblist_t *bl)
  1369 // {
  1370 //     if (bl) {
  1371 //         free(bl->value);
  1372 //         free(bl->mime_type);
  1373 //         free(bl->filename);
  1374 //         free(bl);
  1375 //     }
  1376 // }
  1377 
  1378 static bool is_key(const bloblist_t *bl)
  1379 {
  1380     return (// workaround for Apple Mail bugs
  1381             (is_mime_type(bl, "application/x-apple-msg-attachment") &&
  1382              is_fileending(bl, ".asc")) ||
  1383             // as binary, by file name
  1384             ((bl->mime_type == NULL ||
  1385               is_mime_type(bl, "application/octet-stream")) &&
  1386              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
  1387                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
  1388             // explicit mime type
  1389             is_mime_type(bl, "application/pgp-keys") ||
  1390             // as text, by file name
  1391             (is_mime_type(bl, "text/plain") &&
  1392              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
  1393                     is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
  1394            );
  1395 }
  1396 
  1397 // static void remove_attached_keys(message *msg)
  1398 // {
  1399 //     if (msg) {
  1400 //         bloblist_t *last = NULL;
  1401 //         for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
  1402 //             bloblist_t *next = bl->next;
  1403 // 
  1404 //             if (is_key(bl)) {
  1405 //                 if (last) {
  1406 //                     last->next = next;
  1407 //                 }
  1408 //                 else {
  1409 //                     msg->attachments = next;
  1410 //                 }
  1411 //                 free_bl_entry(bl);
  1412 //             }
  1413 //             else {
  1414 //                 last = bl;
  1415 //             }
  1416 //             bl = next;
  1417 //         }
  1418 //     }
  1419 // }
  1420 
  1421 static bool compare_first_n_bytes(const char* first, const char* second, size_t n) {
  1422     int i;
  1423     for (i = 0; i < n; i++) {
  1424         char num1 = *first;
  1425         char num2 = *second;
  1426 
  1427         if (num1 != num2)
  1428             return false;
  1429                     
  1430         if (num1 == '\0') {
  1431             if (num2 == '\0')
  1432                 return true;
  1433         }   
  1434         first++;
  1435         second++;                     
  1436     }
  1437     return true;
  1438 }
  1439 
  1440 bool import_attached_keys(
  1441         PEP_SESSION session,
  1442         message *msg,
  1443         identity_list **private_idents
  1444     )
  1445 {
  1446     assert(session);
  1447     assert(msg);
  1448 
  1449     if (session == NULL || msg == NULL)
  1450         return false;
  1451 
  1452     bool remove = false;
  1453 
  1454     int i = 0;
  1455     
  1456     bloblist_t* prev = NULL;
  1457     
  1458     bool do_not_advance = false;
  1459     const char* pubkey_header = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
  1460     const char* privkey_header = "-----BEGIN PGP PRIVATE KEY BLOCK-----";
  1461     // Hate my magic numbers at your peril, but I don't want a strlen each time
  1462     const size_t PUBKEY_HSIZE = 36;
  1463     const size_t PRIVKEY_HSIZE = 37;
  1464 
  1465     for (bloblist_t *bl = msg->attachments; i < MAX_KEYS_TO_IMPORT && bl && bl->value;
  1466          i++)
  1467     {
  1468         do_not_advance = false;
  1469         if (bl && bl->value && bl->size && bl->size < MAX_KEY_SIZE
  1470                 && is_key(bl))
  1471         {
  1472             char* blob_value = bl->value;
  1473             size_t blob_size = bl->size;
  1474             bool free_blobval = false;
  1475             
  1476             if (is_encrypted_attachment(bl)) {
  1477                     
  1478                 char* bl_ptext = NULL;
  1479                 size_t bl_psize = 0;
  1480                 stringlist_t* bl_keylist = NULL;
  1481                 PEP_STATUS _status = decrypt_and_verify(session, 
  1482                                                         blob_value, blob_size,
  1483                                                         NULL, 0,
  1484                                                         &bl_ptext, &bl_psize, 
  1485                                                         &bl_keylist,
  1486                                                         NULL);
  1487                 free_stringlist(bl_keylist); // we don't care about key encryption as long as we decrypt
  1488                 if (_status == PEP_DECRYPTED || _status == PEP_DECRYPTED_AND_VERIFIED) {
  1489                     free_blobval = true;
  1490                     blob_value = bl_ptext;
  1491                     blob_size = bl_psize;
  1492                 }
  1493                 else {
  1494                     // This is an encrypted attachment we can do nothing with.
  1495                     // We shouldn't delete it or import it, because we can't
  1496                     // do the latter.
  1497                     free(bl_ptext);
  1498                     prev = bl;
  1499                     bl = bl->next;
  1500                     continue;
  1501                 }
  1502             }
  1503             identity_list *local_private_idents = NULL;
  1504             PEP_STATUS import_status = import_key(session, blob_value, blob_size, &local_private_idents);
  1505             bloblist_t* to_delete = NULL;
  1506             switch (import_status) {
  1507                 case PEP_NO_KEY_IMPORTED:
  1508                     break;
  1509                 case PEP_KEY_IMPORT_STATUS_UNKNOWN:
  1510                     // We'll delete armoured stuff, at least
  1511                     if (blob_size <= PUBKEY_HSIZE)
  1512                         break;
  1513                     if ((!compare_first_n_bytes(pubkey_header, (const char*)blob_value, PUBKEY_HSIZE)) &&
  1514                        (!compare_first_n_bytes(privkey_header, (const char*)blob_value, PRIVKEY_HSIZE)))
  1515                         break;
  1516                     // else fall through and delete    
  1517                 case PEP_KEY_IMPORTED:
  1518                 case PEP_STATUS_OK:
  1519                     to_delete = bl;
  1520                     if (prev)
  1521                         prev->next = bl->next;
  1522                     else
  1523                         msg->attachments = bl->next;
  1524                     bl = bl->next;
  1525                     to_delete->next = NULL;
  1526                     free_bloblist(to_delete);
  1527                     do_not_advance = true;
  1528                     remove = true;
  1529                     break;
  1530                 default:  
  1531                     // bad stuff, but ok.
  1532                     break;
  1533             }
  1534             if (private_idents && *private_idents == NULL && local_private_idents != NULL)
  1535                 *private_idents = local_private_idents;
  1536             else
  1537                 free_identity_list(local_private_idents);
  1538             if (free_blobval)
  1539                 free(blob_value);
  1540         }
  1541         if (!do_not_advance) {
  1542             prev = bl;
  1543             bl = bl->next;
  1544         }
  1545     }
  1546     return remove;
  1547 }
  1548 
  1549 
  1550 PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
  1551 {
  1552     char *keydata = NULL;
  1553     size_t size = 0;
  1554 
  1555     PEP_STATUS status = export_key(session, fpr, &keydata, &size);
  1556     assert(status == PEP_STATUS_OK);
  1557     if (status != PEP_STATUS_OK)
  1558         return status;
  1559     assert(size);
  1560 
  1561     bloblist_t *bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
  1562                       "file://pEpkey.asc");
  1563 
  1564     if (msg->attachments == NULL && bl)
  1565         msg->attachments = bl;
  1566 
  1567     return PEP_STATUS_OK;
  1568 }
  1569 
  1570 #define ONE_WEEK (7*24*3600)
  1571 
  1572 void attach_own_key(PEP_SESSION session, message *msg)
  1573 {
  1574     assert(session);
  1575     assert(msg);
  1576 
  1577     if (msg->dir == PEP_dir_incoming)
  1578         return;
  1579 
  1580     assert(msg->from && msg->from->fpr);
  1581     if (msg->from == NULL || msg->from->fpr == NULL)
  1582         return;
  1583 
  1584     if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
  1585         return;
  1586 
  1587     char *revoked_fpr = NULL;
  1588     uint64_t revocation_date = 0;
  1589 
  1590     if(get_revoked(session, msg->from->fpr,
  1591                    &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
  1592        revoked_fpr != NULL)
  1593     {
  1594         time_t now = time(NULL);
  1595 
  1596         if (now < (time_t)revocation_date + ONE_WEEK)
  1597         {
  1598             _attach_key(session, revoked_fpr, msg);
  1599         }
  1600     }
  1601     free(revoked_fpr);
  1602 }
  1603 
  1604 PEP_cryptotech determine_encryption_format(message *msg)
  1605 {
  1606     assert(msg);
  1607 
  1608     if (is_PGP_message_text(msg->longmsg)) {
  1609         msg->enc_format = PEP_enc_inline;
  1610         return PEP_crypt_OpenPGP;
  1611     }
  1612     else if (msg->attachments && msg->attachments->next &&
  1613             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
  1614             is_PGP_message_text(msg->attachments->next->value)
  1615         ) {
  1616         msg->enc_format = PEP_enc_PGP_MIME;
  1617         return PEP_crypt_OpenPGP;
  1618     }
  1619     else if (msg->attachments && msg->attachments->next &&
  1620             is_mime_type(msg->attachments->next, "application/pgp-encrypted") &&
  1621             is_PGP_message_text(msg->attachments->value)
  1622         ) {
  1623         msg->enc_format = PEP_enc_PGP_MIME_Outlook1;
  1624         return PEP_crypt_OpenPGP;
  1625     }
  1626     else {
  1627         msg->enc_format = PEP_enc_none;
  1628         return PEP_crypt_none;
  1629     }
  1630 }
  1631 
  1632 static void _cleanup_src(message* src, bool remove_attached_key) {
  1633     assert(src);
  1634     
  1635     if (!src)
  1636         return;
  1637         
  1638     char* longmsg = NULL;
  1639     char* shortmsg = NULL;
  1640     char* msg_wrap_info = NULL;
  1641     if (src->longmsg)
  1642         separate_short_and_long(src->longmsg, &shortmsg, &msg_wrap_info,
  1643                                 &longmsg);
  1644     if (longmsg) {                    
  1645         free(src->longmsg);
  1646         free(shortmsg);
  1647         free(msg_wrap_info);
  1648         src->longmsg = longmsg;
  1649     }
  1650     if (remove_attached_key) {
  1651         // End of the attachment list
  1652         if (src->attachments) {
  1653             bloblist_t* tmp = src->attachments;
  1654             while (tmp->next && tmp->next->next) {
  1655                 tmp = tmp->next;
  1656             }
  1657             free_bloblist(tmp->next);
  1658             tmp->next = NULL;
  1659         }    
  1660     }                   
  1661 }
  1662 
  1663 DYNAMIC_API PEP_STATUS encrypt_message(
  1664         PEP_SESSION session,
  1665         message *src,
  1666         stringlist_t * extra,
  1667         message **dst,
  1668         PEP_enc_format enc_format,
  1669         PEP_encrypt_flags_t flags
  1670     )
  1671 {
  1672     PEP_STATUS status = PEP_STATUS_OK;
  1673     message * msg = NULL;
  1674     stringlist_t * keys = NULL;
  1675     message* _src = src;
  1676 
  1677     bool added_key_to_real_src = false;
  1678     
  1679     assert(session);
  1680     assert(src && src->from);
  1681     assert(dst);
  1682 
  1683     if (!(session && src && src->from && dst))
  1684         return PEP_ILLEGAL_VALUE;
  1685 
  1686     if (src->dir == PEP_dir_incoming)
  1687         return PEP_ILLEGAL_VALUE;
  1688 
  1689     determine_encryption_format(src);
  1690     // TODO: change this for multi-encryption in message format 2.0
  1691     if (src->enc_format != PEP_enc_none)
  1692         return PEP_ILLEGAL_VALUE;
  1693 
  1694     bool force_v_1 = flags & PEP_encrypt_flag_force_version_1;
  1695     
  1696     *dst = NULL;
  1697 
  1698     if (!src->from->user_id || src->from->user_id[0] == '\0') {
  1699         char* own_id = NULL;
  1700         status = get_default_own_userid(session, &own_id);
  1701         if (own_id) {
  1702             free(src->from->user_id);
  1703             src->from->user_id = own_id; // ownership transfer
  1704         }
  1705     }
  1706     
  1707     status = myself(session, src->from);
  1708     if (status != PEP_STATUS_OK)
  1709         goto pEp_error;
  1710 
  1711     char* send_fpr = strdup(src->from->fpr ? src->from->fpr : "");
  1712     src->_sender_fpr = send_fpr;
  1713     
  1714     keys = new_stringlist(send_fpr);
  1715     if (keys == NULL)
  1716         goto enomem;
  1717 
  1718     stringlist_t *_k = keys;
  1719 
  1720     if (extra) {
  1721         _k = stringlist_append(_k, extra);
  1722         if (_k == NULL)
  1723             goto enomem;
  1724     }
  1725 
  1726     bool dest_keys_found = true;
  1727     bool has_pEp_user = false;
  1728     
  1729     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1730     unsigned int max_version_major = 0;
  1731     unsigned int max_version_minor = 0;
  1732     pEp_version_major_minor(PEP_VERSION, &max_version_major, &max_version_minor);
  1733     
  1734     identity_list * _il = NULL;
  1735 
  1736     if (enc_format != PEP_enc_none && (_il = src->bcc) && _il->ident)
  1737     // BCC limited support:
  1738     {
  1739         //     - App splits mails with BCC in multiple mails.
  1740         //     - Each email is encrypted separately
  1741         if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
  1742         {
  1743             // Only one Bcc with no other recipient allowed for now
  1744             return PEP_ILLEGAL_VALUE;
  1745         }
  1746 
  1747         PEP_STATUS _status = PEP_STATUS_OK;
  1748         if (!is_me(session, _il->ident)) {
  1749             _status = update_identity(session, _il->ident);
  1750             if (_status == PEP_CANNOT_FIND_IDENTITY) {
  1751                 _il->ident->comm_type = PEP_ct_key_not_found;
  1752                 _status = PEP_STATUS_OK;
  1753             }
  1754             // 0 unless set, so safe.
  1755             
  1756             set_min_version( _il->ident->major_ver, _il->ident->minor_ver, 
  1757                              max_version_major, max_version_minor,
  1758                              &max_version_major, &max_version_minor);
  1759 
  1760             bool is_blacklisted = false;
  1761             if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
  1762                 _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
  1763                 if (_status != PEP_STATUS_OK) {
  1764                     // DB error
  1765                     status = PEP_UNENCRYPTED;
  1766                     goto pEp_error;
  1767                 }
  1768                 if (is_blacklisted) {
  1769                     bool user_default, ident_default, address_default; 
  1770                     _status = get_valid_pubkey(session, _il->ident,
  1771                                                &ident_default, &user_default,
  1772                                                &address_default,
  1773                                                true);
  1774                     if (_status != PEP_STATUS_OK || _il->ident->fpr == NULL) {
  1775                         _il->ident->comm_type = PEP_ct_key_not_found;
  1776                         _status = PEP_STATUS_OK;                        
  1777                     }
  1778                 }    
  1779             }
  1780             if (!has_pEp_user && !EMPTYSTR(_il->ident->user_id))
  1781                 is_pEp_user(session, _il->ident, &has_pEp_user);
  1782         }
  1783         else
  1784             _status = myself(session, _il->ident);
  1785         
  1786         if (_status != PEP_STATUS_OK) {
  1787             status = PEP_UNENCRYPTED;
  1788             goto pEp_error;
  1789         }
  1790 
  1791         if (_il->ident->fpr && _il->ident->fpr[0]) {
  1792             _k = stringlist_add(_k, _il->ident->fpr);
  1793             if (_k == NULL)
  1794                 goto enomem;
  1795             max_comm_type = _get_comm_type(session, max_comm_type,
  1796                                            _il->ident);
  1797         }
  1798         else {
  1799             dest_keys_found = false;
  1800             status = PEP_KEY_NOT_FOUND;
  1801         }
  1802     }
  1803     else // Non BCC
  1804     {
  1805         for (_il = src->to; _il && _il->ident; _il = _il->next) {
  1806             PEP_STATUS _status = PEP_STATUS_OK;
  1807             if (!is_me(session, _il->ident)) {
  1808                 _status = update_identity(session, _il->ident);
  1809                 if (_status == PEP_CANNOT_FIND_IDENTITY) {
  1810                     _il->ident->comm_type = PEP_ct_key_not_found;
  1811                     _status = PEP_STATUS_OK;
  1812                 }
  1813                 // 0 unless set, so safe.
  1814                 set_min_version( _il->ident->major_ver, _il->ident->minor_ver, 
  1815                                  max_version_major, max_version_minor,
  1816                                  &max_version_major, &max_version_minor);
  1817                 
  1818                 bool is_blacklisted = false;
  1819                 if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
  1820                     _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
  1821                     if (_status != PEP_STATUS_OK) {
  1822                         // DB error
  1823                         status = PEP_UNENCRYPTED;
  1824                         goto pEp_error;
  1825                     }
  1826                     if (is_blacklisted) {
  1827                         bool user_default, ident_default, address_default; 
  1828                         _status = get_valid_pubkey(session, _il->ident,
  1829                                                    &ident_default, &user_default,
  1830                                                    &address_default,
  1831                                                    true);
  1832                         if (_status != PEP_STATUS_OK || _il->ident->fpr == NULL) {
  1833                             _il->ident->comm_type = PEP_ct_key_not_found;
  1834                             _status = PEP_STATUS_OK;                        
  1835                         }
  1836                     }    
  1837                 }
  1838                 if (!has_pEp_user && !EMPTYSTR(_il->ident->user_id))
  1839                     is_pEp_user(session, _il->ident, &has_pEp_user);
  1840                 
  1841                 _status = bind_own_ident_with_contact_ident(session, src->from, _il->ident);
  1842                 if (_status != PEP_STATUS_OK) {
  1843                     status = PEP_UNKNOWN_DB_ERROR;
  1844                     goto pEp_error;
  1845                 }
  1846     
  1847             }
  1848             else
  1849                 _status = myself(session, _il->ident);
  1850                 
  1851             if (_status != PEP_STATUS_OK) {
  1852                 status = PEP_UNENCRYPTED;
  1853                 goto pEp_error;
  1854             }
  1855 
  1856             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1857                 _k = stringlist_add(_k, _il->ident->fpr);
  1858                 if (_k == NULL)
  1859                     goto enomem;
  1860                 max_comm_type = _get_comm_type(session, max_comm_type,
  1861                                                _il->ident);
  1862             }
  1863             else {
  1864                 dest_keys_found = false;
  1865                 status = PEP_KEY_NOT_FOUND;
  1866             }
  1867         }
  1868 
  1869         for (_il = src->cc; _il && _il->ident; _il = _il->next) {
  1870             PEP_STATUS _status = PEP_STATUS_OK;
  1871             if (!is_me(session, _il->ident)) {
  1872                 _status = update_identity(session, _il->ident);
  1873                 if (_status == PEP_CANNOT_FIND_IDENTITY) {
  1874                     _il->ident->comm_type = PEP_ct_key_not_found;
  1875                     _status = PEP_STATUS_OK;
  1876                 }
  1877                 bool is_blacklisted = false;
  1878                 if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
  1879                     _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
  1880                     if (_status != PEP_STATUS_OK) {
  1881                         // DB error
  1882                         status = PEP_UNENCRYPTED;
  1883                         goto pEp_error;
  1884                     }
  1885                     if (is_blacklisted) {
  1886                         bool user_default, ident_default, address_default; 
  1887                         _status = get_valid_pubkey(session, _il->ident,
  1888                                                    &ident_default, &user_default,
  1889                                                    &address_default,
  1890                                                    true);
  1891                         if (_status != PEP_STATUS_OK || _il->ident->fpr == NULL) {
  1892                             _il->ident->comm_type = PEP_ct_key_not_found;
  1893                             _status = PEP_STATUS_OK;                        
  1894                         }
  1895                     }    
  1896                 }
  1897                 if (!has_pEp_user && !EMPTYSTR(_il->ident->user_id))
  1898                     is_pEp_user(session, _il->ident, &has_pEp_user);
  1899             }
  1900             else
  1901                 _status = myself(session, _il->ident);
  1902             if (_status != PEP_STATUS_OK)
  1903             {
  1904                 status = PEP_UNENCRYPTED;
  1905                 goto pEp_error;
  1906             }
  1907 
  1908             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1909                 _k = stringlist_add(_k, _il->ident->fpr);
  1910                 if (_k == NULL)
  1911                     goto enomem;
  1912                 max_comm_type = _get_comm_type(session, max_comm_type,
  1913                                                _il->ident);
  1914             }
  1915             else {
  1916                 dest_keys_found = false;
  1917             }
  1918         }
  1919     }
  1920     
  1921     if (max_version_major == 1)
  1922         force_v_1 = true;
  1923         
  1924     if (enc_format == PEP_enc_none || !dest_keys_found ||
  1925         stringlist_length(keys)  == 0 ||
  1926         _rating(max_comm_type) < PEP_rating_reliable)
  1927     {
  1928         free_stringlist(keys);
  1929         if ((has_pEp_user || !session->passive_mode) && 
  1930             !(flags & PEP_encrypt_flag_force_no_attached_key)) {
  1931             attach_own_key(session, src);
  1932             added_key_to_real_src = true;
  1933         }
  1934         decorate_message(src, PEP_rating_undefined, NULL, true, true);
  1935         return PEP_UNENCRYPTED;
  1936     }
  1937     else {
  1938         // First, dedup the keylist
  1939         if (keys && keys->next)
  1940             dedup_stringlist(keys->next);
  1941             
  1942         // FIXME - we need to deal with transport types (via flag)
  1943         message_wrap_type wrap_type = PEP_message_unwrapped;
  1944         if ((enc_format != PEP_enc_inline) && (!force_v_1) && ((max_comm_type | PEP_ct_confirmed) == PEP_ct_pEp)) {
  1945             wrap_type = ((flags & PEP_encrypt_flag_key_reset_only) ? PEP_message_key_reset : PEP_message_default);
  1946             _src = wrap_message_as_attachment(NULL, src, wrap_type, false, max_version_major, max_version_minor);
  1947             if (!_src)
  1948                 goto pEp_error;
  1949         }
  1950         else {
  1951             // hide subject
  1952             if (enc_format != PEP_enc_inline) {
  1953                 status = replace_subject(_src);
  1954                 if (status == PEP_OUT_OF_MEMORY)
  1955                     goto enomem;
  1956             }
  1957             if (!(flags & PEP_encrypt_flag_force_no_attached_key))
  1958                 added_key_to_real_src = true;            
  1959         }
  1960         if (!(flags & PEP_encrypt_flag_force_no_attached_key))
  1961             attach_own_key(session, _src);
  1962 
  1963         msg = clone_to_empty_message(_src);
  1964         if (msg == NULL)
  1965             goto enomem;
  1966 
  1967         switch (enc_format) {
  1968             case PEP_enc_PGP_MIME:
  1969             case PEP_enc_PEP: // BUG: should be implemented extra
  1970                 status = encrypt_PGP_MIME(session, _src, keys, msg, flags, wrap_type);
  1971                 break;
  1972 
  1973             case PEP_enc_inline:
  1974                 status = encrypt_PGP_inline(session, _src, keys, msg, flags);
  1975                 break;
  1976 
  1977             default:
  1978                 assert(0);
  1979                 status = PEP_ILLEGAL_VALUE;
  1980                 goto pEp_error;
  1981         }
  1982 
  1983         if (status == PEP_OUT_OF_MEMORY)
  1984             goto enomem;
  1985 
  1986         if (status != PEP_STATUS_OK)
  1987             goto pEp_error;
  1988     }
  1989 
  1990     free_stringlist(keys);
  1991 
  1992     if (msg && msg->shortmsg == NULL) {
  1993         msg->shortmsg = strdup("");
  1994         assert(msg->shortmsg);
  1995         if (msg->shortmsg == NULL)
  1996             goto enomem;
  1997     }
  1998 
  1999     if (msg) {
  2000         decorate_message(msg, PEP_rating_undefined, NULL, true, true);
  2001         if (_src->id) {
  2002             msg->id = strdup(_src->id);
  2003             assert(msg->id);
  2004             if (msg->id == NULL)
  2005                 goto enomem;
  2006         }
  2007     }
  2008 
  2009     *dst = msg;
  2010     
  2011     // ??? FIXME: Check to be sure we don't have references btw _src and msg. 
  2012     // I don't think we do.
  2013     if (_src && _src != src)
  2014         free_message(_src);
  2015     
  2016     _cleanup_src(src, added_key_to_real_src);
  2017         
  2018     return status;
  2019 
  2020 enomem:
  2021     status = PEP_OUT_OF_MEMORY;
  2022 
  2023 pEp_error:
  2024     free_stringlist(keys);
  2025     free_message(msg);
  2026     if (_src && _src != src)
  2027         free_message(_src);
  2028 
  2029     _cleanup_src(src, added_key_to_real_src);
  2030 
  2031     return status;
  2032 }
  2033 
  2034 DYNAMIC_API PEP_STATUS encrypt_message_and_add_priv_key(
  2035         PEP_SESSION session,
  2036         message *src,
  2037         message **dst,
  2038         const char* to_fpr,
  2039         PEP_enc_format enc_format,
  2040         PEP_encrypt_flags_t flags
  2041     )
  2042 {
  2043     assert(session);
  2044     assert(src);
  2045     assert(dst);
  2046     assert(to_fpr);
  2047         
  2048     if (!session || !src || !dst || !to_fpr)
  2049         return PEP_ILLEGAL_VALUE;
  2050         
  2051     if (enc_format == PEP_enc_none)
  2052         return PEP_ILLEGAL_VALUE;
  2053     
  2054     if (src->cc || src->bcc)
  2055         return PEP_ILLEGAL_VALUE;
  2056         
  2057     if (!src->to || src->to->next)
  2058         return PEP_ILLEGAL_VALUE;
  2059         
  2060     if (!src->from->address || !src->to->ident || !src->to->ident->address)
  2061         return PEP_ILLEGAL_VALUE;
  2062             
  2063     if (strcasecmp(src->from->address, src->to->ident->address) != 0)
  2064         return PEP_ILLEGAL_VALUE;
  2065     
  2066     stringlist_t* keys = NULL;
  2067 
  2068     char* own_id = NULL;
  2069     char* default_id = NULL;
  2070     
  2071     pEp_identity* own_identity = NULL;
  2072     char* own_private_fpr = NULL;
  2073 
  2074     char* priv_key_data = NULL;
  2075     
  2076     PEP_STATUS status = get_default_own_userid(session, &own_id);
  2077     
  2078     if (!own_id)
  2079         return PEP_UNKNOWN_ERROR; // Probably a DB error at this point
  2080         
  2081     if (src->from->user_id) {
  2082         if (strcmp(src->from->user_id, own_id) != 0) {
  2083             status = get_userid_alias_default(session, src->from->user_id, &default_id);
  2084             if (status != PEP_STATUS_OK || !default_id || strcmp(default_id, own_id) != 0) {
  2085                 status = PEP_ILLEGAL_VALUE;
  2086                 goto pEp_free;
  2087             }
  2088         }        
  2089     }
  2090     
  2091     // Ok, we are at least marginally sure the initial stuff is ok.
  2092         
  2093     // Let's get our own, normal identity
  2094     own_identity = identity_dup(src->from);
  2095     status = myself(session, own_identity);
  2096 
  2097     if (status != PEP_STATUS_OK)
  2098         goto pEp_free;
  2099 
  2100     // Ok, now we know the address is an own address. All good. Then...
  2101     own_private_fpr = own_identity->fpr;
  2102     own_identity->fpr = strdup(to_fpr);
  2103     
  2104     status = get_trust(session, own_identity);
  2105     
  2106     if (status != PEP_STATUS_OK) {
  2107         if (status == PEP_CANNOT_FIND_IDENTITY)
  2108             status = PEP_ILLEGAL_VALUE;
  2109         goto pEp_free;
  2110     }
  2111         
  2112     if ((own_identity->comm_type & PEP_ct_confirmed) != PEP_ct_confirmed) {
  2113         status = PEP_ILLEGAL_VALUE;
  2114         goto pEp_free;
  2115     }
  2116                 
  2117     // Ok, so all the things are now allowed.
  2118     // So let's get our own private key and roll with it.
  2119     size_t priv_key_size = 0;
  2120     
  2121     status = export_secret_key(session, own_private_fpr, &priv_key_data, 
  2122                                 &priv_key_size);
  2123 
  2124     if (status != PEP_STATUS_OK)
  2125         goto pEp_free;
  2126     
  2127     if (!priv_key_data) {
  2128         status = PEP_CANNOT_EXPORT_KEY;
  2129         goto pEp_free;
  2130     }
  2131     
  2132     // Ok, fine... let's encrypt yon blob
  2133     keys = new_stringlist(own_private_fpr);
  2134     if (!keys) {
  2135         status = PEP_OUT_OF_MEMORY;
  2136         goto pEp_free;
  2137     }
  2138     
  2139     stringlist_add(keys, to_fpr);
  2140     
  2141     char* encrypted_key_text = NULL;
  2142     size_t encrypted_key_size = 0;
  2143     
  2144     if (flags & PEP_encrypt_flag_force_unsigned)
  2145         status = encrypt_only(session, keys, priv_key_data, priv_key_size,
  2146                               &encrypted_key_text, &encrypted_key_size);
  2147     else
  2148         status = encrypt_and_sign(session, keys, priv_key_data, priv_key_size,
  2149                                   &encrypted_key_text, &encrypted_key_size);
  2150     
  2151     if (!encrypted_key_text) {
  2152         status = PEP_UNKNOWN_ERROR;
  2153         goto pEp_free;
  2154     }
  2155 
  2156     // We will have to delete this before returning, as we allocated it.
  2157     bloblist_t* created_bl = NULL;
  2158     bloblist_t* created_predecessor = NULL;
  2159     
  2160     bloblist_t* old_head = NULL;
  2161     
  2162     if (!src->attachments || src->attachments->value == NULL) {
  2163         if (src->attachments && src->attachments->value == NULL) {
  2164             old_head = src->attachments;
  2165             src->attachments = NULL;
  2166         }
  2167         src->attachments = new_bloblist(encrypted_key_text, encrypted_key_size,
  2168                                         "application/octet-stream", 
  2169                                         "file://pEpkey.asc.pgp");
  2170         created_bl = src->attachments;
  2171     } 
  2172     else {
  2173         bloblist_t* tmp = src->attachments;
  2174         while (tmp && tmp->next) {
  2175             tmp = tmp->next;
  2176         }
  2177         created_predecessor = tmp;                                    
  2178         created_bl = bloblist_add(tmp, 
  2179                                   encrypted_key_text, encrypted_key_size,
  2180                                   "application/octet-stream", 
  2181                                    "file://pEpkey.asc.pgp");
  2182     }
  2183     
  2184     if (!created_bl) {
  2185         status = PEP_OUT_OF_MEMORY;
  2186         goto pEp_free;
  2187     }
  2188             
  2189     // Ok, it's in there. Let's do this.        
  2190     status = encrypt_message(session, src, keys, dst, enc_format, flags);
  2191     
  2192     // Delete what we added to src
  2193     free_bloblist(created_bl);
  2194     if (created_predecessor)
  2195         created_predecessor->next = NULL;
  2196     else {
  2197         if (old_head)
  2198             src->attachments = old_head;
  2199         else
  2200             src->attachments = NULL;    
  2201     }
  2202     
  2203 pEp_free:
  2204     free(own_id);
  2205     free(default_id);
  2206     free(own_private_fpr);
  2207     free(priv_key_data);
  2208     free_identity(own_identity);
  2209     free_stringlist(keys);
  2210     return status;
  2211 }
  2212 
  2213 
  2214 DYNAMIC_API PEP_STATUS encrypt_message_for_self(
  2215         PEP_SESSION session,
  2216         pEp_identity* target_id,
  2217         message *src,
  2218         stringlist_t* extra,
  2219         message **dst,
  2220         PEP_enc_format enc_format,
  2221         PEP_encrypt_flags_t flags
  2222     )
  2223 {
  2224     PEP_STATUS status = PEP_STATUS_OK;
  2225     message * msg = NULL;
  2226     stringlist_t * keys = NULL;
  2227     message* _src = src;
  2228 
  2229     assert(session);
  2230     assert(target_id);
  2231     assert(src);
  2232     assert(dst);
  2233     assert(enc_format != PEP_enc_none);
  2234 
  2235     if (!(session && target_id && src && dst && enc_format != PEP_enc_none))
  2236         return PEP_ILLEGAL_VALUE;
  2237 
  2238     // if (src->dir == PEP_dir_incoming)
  2239     //     return PEP_ILLEGAL_VALUE;
  2240 
  2241     determine_encryption_format(src);
  2242     if (src->enc_format != PEP_enc_none)
  2243         return PEP_ILLEGAL_VALUE;
  2244 
  2245     if (!target_id->user_id || target_id->user_id[0] == '\0') {
  2246         char* own_id = NULL;
  2247         status = get_default_own_userid(session, &own_id);
  2248         if (own_id) {
  2249             free(target_id->user_id);
  2250             target_id->user_id = own_id; // ownership transfer
  2251         }
  2252     }
  2253 
  2254     if (!target_id->user_id || target_id->user_id[0] == '\0')
  2255         return PEP_CANNOT_FIND_IDENTITY;
  2256 
  2257     if (target_id->address) {
  2258         status = myself(session, target_id);
  2259         if (status != PEP_STATUS_OK)
  2260             goto pEp_error;
  2261     }
  2262     else if (!target_id->fpr) {
  2263         return PEP_ILLEGAL_VALUE;
  2264     }
  2265     
  2266     *dst = NULL;
  2267 
  2268     // PEP_STATUS _status = update_identity(session, target_id);
  2269     // if (_status != PEP_STATUS_OK) {
  2270     //     status = _status;
  2271     //     goto pEp_error;
  2272     // }
  2273 
  2274     char* target_fpr = target_id->fpr;
  2275     if (!target_fpr)
  2276         return PEP_KEY_NOT_FOUND; // FIXME: Error condition
  2277  
  2278     keys = new_stringlist(target_fpr);
  2279     
  2280     stringlist_t *_k = keys;
  2281 
  2282     if (extra) {
  2283         _k = stringlist_append(_k, extra);
  2284         if (_k == NULL)
  2285             goto enomem;
  2286     }
  2287 
  2288     /* KG: did we ever do this??? */
  2289     // if (!(flags & PEP_encrypt_flag_force_no_attached_key))
  2290     //     _attach_key(session, target_fpr, src);
  2291 
  2292     unsigned int major_ver, minor_ver;
  2293     pEp_version_major_minor(PEP_VERSION, &major_ver, &minor_ver);
  2294     _src = wrap_message_as_attachment(NULL, src, PEP_message_default, false, major_ver, minor_ver);
  2295     if (!_src)
  2296         goto pEp_error;
  2297 
  2298     msg = clone_to_empty_message(_src);
  2299     if (msg == NULL)
  2300         goto enomem;
  2301 
  2302     switch (enc_format) {
  2303         case PEP_enc_PGP_MIME:
  2304         case PEP_enc_PEP: // BUG: should be implemented extra
  2305             status = encrypt_PGP_MIME(session, _src, keys, msg, flags, PEP_message_default);
  2306             if (status == PEP_STATUS_OK || (src->longmsg && strstr(src->longmsg, "INNER")))
  2307                 _cleanup_src(src, false);
  2308             break;
  2309 
  2310         case PEP_enc_inline:
  2311             status = encrypt_PGP_inline(session, _src, keys, msg, flags);
  2312             break;
  2313 
  2314         default:
  2315             assert(0);
  2316             status = PEP_ILLEGAL_VALUE;
  2317             goto pEp_error;
  2318     }
  2319 
  2320     if (status == PEP_OUT_OF_MEMORY)
  2321         goto enomem;
  2322 
  2323     if (status != PEP_STATUS_OK)
  2324         goto pEp_error;
  2325 
  2326     if (msg) {
  2327         if (!src->shortmsg) {
  2328             free(msg->shortmsg);
  2329             msg->shortmsg = _pEp_subj_copy();
  2330             assert(msg->shortmsg);
  2331             if (msg->shortmsg == NULL)
  2332                 goto enomem;
  2333         }
  2334         else {
  2335             if (session->unencrypted_subject && (flags & PEP_encrypt_reencrypt)) {
  2336                 free(msg->shortmsg);
  2337                 msg->shortmsg = strdup(src->shortmsg);
  2338             }    
  2339         }
  2340 
  2341         if (_src->id) {
  2342             msg->id = strdup(_src->id);
  2343             assert(msg->id);
  2344             if (msg->id == NULL)
  2345                 goto enomem;
  2346         }
  2347         decorate_message(msg, PEP_rating_undefined, NULL, true, true);
  2348     }
  2349 
  2350     *dst = msg;
  2351     
  2352     if (src != _src)
  2353         free_message(_src);
  2354 
  2355     return status;
  2356 
  2357 enomem:
  2358     status = PEP_OUT_OF_MEMORY;
  2359 
  2360 pEp_error:
  2361     free_stringlist(keys);
  2362     free_message(msg);
  2363     if (src != _src)
  2364         free_message(_src);
  2365 
  2366     return status;
  2367 }
  2368 
  2369 // static PEP_STATUS _update_identity_for_incoming_message(
  2370 //         PEP_SESSION session,
  2371 //         const message *src
  2372 //     )
  2373 // {
  2374 //     PEP_STATUS status;
  2375 // 
  2376 //     if (src->from && src->from->address) {
  2377 //         if (!is_me(session, src->from))
  2378 //             status = update_identity(session, src->from);
  2379 //         else
  2380 //             status = myself(session, src->from);
  2381 //         if (status == PEP_STATUS_OK
  2382 //                 && is_a_pEpmessage(src)
  2383 //                 && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
  2384 //                 && src->from->comm_type != PEP_ct_pEp_unconfirmed
  2385 //                 && src->from->comm_type != PEP_ct_pEp)
  2386 //         {
  2387 //             src->from->comm_type |= PEP_ct_pEp_unconfirmed;
  2388 //             status = set_identity(session, src->from);
  2389 //         }
  2390 //         return status;
  2391 //     }
  2392 //     return PEP_ILLEGAL_VALUE;
  2393 // }
  2394 
  2395 
  2396 static PEP_STATUS _get_detached_signature(message* msg, 
  2397                                           bloblist_t** signature_blob) {
  2398     bloblist_t* attach_curr = msg->attachments;
  2399 
  2400     *signature_blob = NULL;
  2401 
  2402     while (attach_curr) {
  2403         if (attach_curr->mime_type &&
  2404             (strcasecmp(attach_curr->mime_type, "application/pgp-signature") == 0)) {
  2405             *signature_blob = attach_curr;
  2406             break;
  2407         }
  2408         attach_curr = attach_curr->next;
  2409     }
  2410 
  2411     return PEP_STATUS_OK;
  2412 }
  2413 
  2414 static PEP_STATUS _get_signed_text(const char* ptext, const size_t psize,
  2415                                    char** stext, size_t* ssize) {
  2416 
  2417     char* signed_boundary = NULL;
  2418     char* signpost = strstr(ptext, "Content-Type: multipart/signed");
  2419 
  2420     *ssize = 0;
  2421     *stext = NULL;
  2422 
  2423     if (!signpost)
  2424         return PEP_UNKNOWN_ERROR;
  2425 
  2426     char* curr_line = signpost;
  2427 //    const char* end_text = ptext + psize;
  2428     const char* boundary_key = "boundary=";
  2429     const size_t BOUNDARY_KEY_SIZE = 9;
  2430 
  2431     char* start_boundary = strstr(curr_line, boundary_key);
  2432     if (!start_boundary)
  2433         return PEP_UNKNOWN_ERROR;
  2434 
  2435     start_boundary += BOUNDARY_KEY_SIZE;
  2436 
  2437     bool quoted = (*start_boundary == '"');
  2438 
  2439     if (quoted)
  2440         start_boundary++;
  2441         
  2442     char* end_boundary = (quoted ? strstr(start_boundary, "\"") : strstr(start_boundary, ";")); // FIXME: third possiblity is CRLF, or?
  2443 
  2444     if (!end_boundary)
  2445         return PEP_UNKNOWN_ERROR;
  2446 
  2447     // Add space for the "--"
  2448     size_t boundary_strlen = (end_boundary - start_boundary) + 2;
  2449 
  2450     signed_boundary = calloc(boundary_strlen + 1, 1);
  2451     assert(signed_boundary);
  2452     if (!signed_boundary)
  2453         return PEP_OUT_OF_MEMORY;
  2454 
  2455     strlcpy(signed_boundary, "--", boundary_strlen + 1);
  2456     strlcat(signed_boundary, start_boundary, boundary_strlen + 1);
  2457 
  2458     start_boundary = strstr(end_boundary, signed_boundary);
  2459 
  2460     if (!start_boundary)
  2461         return PEP_UNKNOWN_ERROR;
  2462 
  2463     start_boundary += boundary_strlen;
  2464 
  2465     if (*start_boundary == '\r') {
  2466         if (*(start_boundary + 1) == '\n')
  2467             start_boundary += 2;
  2468     }
  2469     else if (*start_boundary == '\n')
  2470         start_boundary++;
  2471 
  2472     end_boundary = strstr(start_boundary + boundary_strlen, signed_boundary);
  2473 
  2474     if (!end_boundary)
  2475         return PEP_UNKNOWN_ERROR;
  2476 
  2477     // See RFC3156 section 5...
  2478     end_boundary--; 
  2479     if (*(end_boundary - 1) == '\r')
  2480         end_boundary--; 
  2481 
  2482     *ssize = end_boundary - start_boundary;
  2483     *stext = start_boundary;
  2484     free(signed_boundary);
  2485 
  2486     return PEP_STATUS_OK;
  2487 }
  2488 
  2489 static PEP_STATUS combine_keylists(PEP_SESSION session, stringlist_t** verify_in, 
  2490                                    stringlist_t** keylist_in_out, 
  2491                                    pEp_identity* from) {
  2492     
  2493     if (!verify_in || !(*verify_in)) // this isn't really a problem.
  2494         return PEP_STATUS_OK;
  2495     
  2496     stringlist_t* orig_verify = *verify_in;
  2497     
  2498     stringlist_t* verify_curr = NULL;
  2499     stringlist_t* from_keys = NULL;
  2500     
  2501     /* FIXME: what to do if head needs to be null */
  2502     PEP_STATUS status = find_keys(session, from->address, &from_keys);
  2503     
  2504     stringlist_t* from_fpr_node = NULL;
  2505     stringlist_t* from_curr;
  2506     
  2507     for (from_curr = from_keys; from_curr; from_curr = from_curr->next) {
  2508         for (verify_curr = orig_verify; verify_curr; verify_curr = verify_curr->next) {
  2509             if (from_curr->value && verify_curr->value &&
  2510                 _same_fpr(from_curr->value, strlen(from_curr->value),
  2511                           verify_curr->value, strlen(verify_curr->value))) {
  2512                 from_fpr_node = from_curr;
  2513                 break;
  2514             }
  2515         }
  2516     }
  2517     
  2518     if (!from_fpr_node) {
  2519         status = PEP_KEY_NOT_FOUND;
  2520         goto free;
  2521     }
  2522 
  2523     verify_curr = orig_verify;
  2524     
  2525     /* put "from" signer at the beginning of the list */
  2526     if (!_same_fpr(orig_verify->value, strlen(orig_verify->value),
  2527                    from_fpr_node->value, strlen(from_fpr_node->value))) {
  2528         orig_verify = stringlist_delete(orig_verify, from_fpr_node->value);
  2529         verify_curr = new_stringlist(from_fpr_node->value);
  2530         verify_curr->next = orig_verify;
  2531     }
  2532 
  2533     if (keylist_in_out) {
  2534         /* append keylist to signers */
  2535         if (*keylist_in_out && (*keylist_in_out)->value) {
  2536             stringlist_t** tail_pp = &verify_curr->next;
  2537 
  2538             while (*tail_pp) {
  2539                 tail_pp = &((*tail_pp)->next);
  2540             }
  2541             stringlist_t* second_list = *keylist_in_out;
  2542             if (second_list) {
  2543                 char* listhead_val = second_list->value;
  2544                 if (!listhead_val || listhead_val[0] == '\0') {
  2545                     /* remove head, basically. This can happen when,
  2546                        for example, the signature is detached and
  2547                        verification is not seen directly after
  2548                        decryption, so no signer is presumed in
  2549                        the first construction of the keylist */
  2550                     *keylist_in_out = (*keylist_in_out)->next;
  2551                     second_list->next = NULL;
  2552                     free_stringlist(second_list);
  2553                 }
  2554             }
  2555             *tail_pp = *keylist_in_out;
  2556         }
  2557 
  2558         *keylist_in_out = verify_curr;
  2559     }
  2560 
  2561     status = PEP_STATUS_OK;
  2562     
  2563 free:
  2564     free_stringlist(from_keys);
  2565     return status;
  2566 }
  2567 
  2568 static PEP_STATUS amend_rating_according_to_sender_and_recipients(
  2569        PEP_SESSION session,
  2570        PEP_rating *rating,
  2571        pEp_identity *sender,
  2572        stringlist_t *recipients) {
  2573     
  2574     PEP_STATUS status = PEP_STATUS_OK;
  2575 
  2576     if (*rating > PEP_rating_mistrust) {
  2577 
  2578         if (recipients == NULL) {
  2579             *rating = PEP_rating_undefined;
  2580             return PEP_STATUS_OK;
  2581         }
  2582 
  2583         char *fpr = recipients->value;
  2584 
  2585         if (!(sender && sender->user_id && sender->user_id[0] && fpr && fpr[0])) {
  2586             *rating = PEP_rating_unreliable;
  2587         }
  2588         else {
  2589             pEp_identity *_sender = new_identity(sender->address, fpr,
  2590                                                  sender->user_id, sender->username);
  2591             if (_sender == NULL)
  2592                 return PEP_OUT_OF_MEMORY;
  2593 
  2594             status = get_trust(session, _sender);
  2595             if (_sender->comm_type == PEP_ct_unknown) {
  2596                 get_key_rating(session, fpr, &_sender->comm_type);
  2597             }
  2598             if (_sender->comm_type != PEP_ct_unknown) {
  2599                 *rating = keylist_rating(session, recipients, 
  2600                             fpr, _rating(_sender->comm_type));
  2601             }
  2602             
  2603             free_identity(_sender);
  2604             if (status == PEP_CANNOT_FIND_IDENTITY)
  2605                status = PEP_STATUS_OK;
  2606         }
  2607     }
  2608     return status;
  2609 }
  2610 
  2611 // FIXME: Do we need to remove the attachment? I think we do...
  2612 static bool pull_up_attached_main_msg(message* src) {
  2613     char* slong = src->longmsg;
  2614     char* sform = src->longmsg_formatted;
  2615     bloblist_t* satt = src->attachments;
  2616     
  2617     if ((!slong || slong[0] == '\0')
  2618          && (!sform || sform[0] == '\0')) {
  2619         const char* inner_mime_type = (satt ? satt->mime_type : NULL);     
  2620         if (inner_mime_type) {
  2621             if (strcasecmp(inner_mime_type, "text/plain") == 0) {
  2622                 free(slong); /* in case of "" */
  2623                 src->longmsg = strndup(satt->value, satt->size); 
  2624                 
  2625                 bloblist_t* next_node = satt->next;
  2626                 if (next_node) {
  2627                     inner_mime_type = next_node->mime_type;
  2628                     if (strcasecmp(inner_mime_type, "text/html") == 0) {
  2629                         free(sform);
  2630                         src->longmsg_formatted = strndup(next_node->value, next_node->size);
  2631                     }
  2632                 }
  2633             }
  2634             else if (strcasecmp(inner_mime_type, "text/html") == 0) {
  2635                 free(sform);
  2636                 src->longmsg_formatted = strndup(satt->value, satt->size);
  2637             }
  2638         }
  2639         return true;
  2640     }
  2641     return false;
  2642 }
  2643 
  2644 
  2645 
  2646 static PEP_STATUS unencapsulate_hidden_fields(message* src, message* msg,
  2647                                               char** msg_wrap_info) {
  2648     if (!src)
  2649         return PEP_ILLEGAL_VALUE;
  2650     unsigned char pEpstr[] = PEP_SUBJ_STRING;
  2651     PEP_STATUS status = PEP_STATUS_OK;
  2652 
  2653     bool change_source_in_place = (msg ? false : true);
  2654     
  2655     if (change_source_in_place)
  2656         msg = src;
  2657         
  2658     
  2659     switch (src->enc_format) {
  2660         case PEP_enc_PGP_MIME:
  2661         case PEP_enc_inline:
  2662         case PEP_enc_PGP_MIME_Outlook1:
  2663 //        case PEP_enc_none: // FIXME - this is wrong
  2664 
  2665             if (!change_source_in_place)
  2666                 status = copy_fields(msg, src);
  2667                 
  2668             if (status != PEP_STATUS_OK)
  2669                 return status;
  2670                 
  2671             // FIXME: This is a mess. Talk with VB about how far we go to identify
  2672             if (is_a_pEpmessage(src) || (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0 ||
  2673                 _unsigned_signed_strcmp(pEpstr, src->shortmsg, PEP_SUBJ_BYTELEN) == 0) ||
  2674                 (strcmp(src->shortmsg, "p=p") == 0))
  2675             {
  2676                 char * shortmsg = NULL;
  2677                 char * longmsg = NULL;
  2678         
  2679                 if (msg->longmsg) {
  2680                     int r = separate_short_and_long(msg->longmsg, 
  2681                                                     &shortmsg, 
  2682                                                     msg_wrap_info,
  2683                                                     &longmsg);
  2684                 
  2685                     if (r == -1)
  2686                         return PEP_OUT_OF_MEMORY;
  2687                 }
  2688 
  2689                 // We only use the shortmsg in version 1.0 messages; if it occurs where we
  2690                 // didn't replace the subject, we ignore this all
  2691                 if (!(*msg_wrap_info || change_source_in_place)) {
  2692                     if (!shortmsg || 
  2693                         (src->shortmsg != NULL && strcmp(src->shortmsg, "pEp") != 0 &&
  2694                          _unsigned_signed_strcmp(pEpstr, src->shortmsg, PEP_SUBJ_BYTELEN) != 0 &&
  2695                         strcmp(src->shortmsg, "p=p") != 0)) {
  2696                              
  2697                         if (shortmsg != NULL)
  2698                             free(shortmsg);                        
  2699                             
  2700                         if (src->shortmsg == NULL) {
  2701                             shortmsg = strdup("");
  2702                         }
  2703                         else {
  2704                             // FIXME: is msg->shortmsg always a copy of
  2705                             // src->shortmsg already?
  2706                             // if so, we need to change the logic so
  2707                             // that in this case, we don't free msg->shortmsg
  2708                             // and do this strdup, etc
  2709                             shortmsg = strdup(src->shortmsg);
  2710                         }        
  2711                     }
  2712                     free(msg->shortmsg);
  2713                     msg->shortmsg = shortmsg;
  2714                 }
  2715                 
  2716                 free(msg->longmsg);
  2717 
  2718                 msg->longmsg = longmsg;
  2719             }
  2720             else {
  2721                 if (!change_source_in_place) {
  2722                     msg->shortmsg = strdup(src->shortmsg);
  2723                     assert(msg->shortmsg);
  2724                     if (msg->shortmsg == NULL)
  2725                         return PEP_OUT_OF_MEMORY;
  2726                 }
  2727             }
  2728             break;
  2729         default:
  2730                 // BUG: must implement more
  2731                 NOT_IMPLEMENTED
  2732     }
  2733     return PEP_STATUS_OK;
  2734 
  2735 }
  2736 
  2737 static PEP_STATUS get_crypto_text(message* src, char** crypto_text, size_t* text_size) {
  2738                 
  2739     // this is only here because of how NOT_IMPLEMENTED works            
  2740     PEP_STATUS status = PEP_STATUS_OK;
  2741                     
  2742     switch (src->enc_format) {
  2743         case PEP_enc_PGP_MIME:
  2744 		case PEP_enc_PGP_MIME_Outlook1:
  2745 			*crypto_text = src->attachments->next->value;
  2746 			if (src->attachments->next->value[src->attachments->next->size - 1]) {
  2747 				// if the attachment is not ending with a trailing 0
  2748 				// then it is containing the crypto text directly
  2749 				*text_size = src->attachments->next->size;
  2750 			}
  2751 			else {
  2752 				// if the attachment is ending with trailing 0
  2753 				// then it is containting a string
  2754 				*text_size = strlen(src->attachments->next->value);
  2755 			}
  2756             break;
  2757 
  2758         case PEP_enc_inline:
  2759             *crypto_text = src->longmsg;
  2760             *text_size = strlen(*crypto_text);
  2761             break;
  2762 
  2763         default:
  2764             NOT_IMPLEMENTED
  2765     }
  2766     
  2767     return status;
  2768 }
  2769 
  2770 
  2771 static PEP_STATUS verify_decrypted(PEP_SESSION session,
  2772                                    message* src,
  2773                                    message* msg, 
  2774                                    char* plaintext, 
  2775                                    size_t plaintext_size,
  2776                                    stringlist_t** keylist,
  2777                                    PEP_STATUS* decrypt_status,
  2778                                    PEP_cryptotech crypto) {
  2779 
  2780     assert(src && src->from);
  2781     
  2782     if (!src && !src->from)
  2783         return PEP_ILLEGAL_VALUE;
  2784 
  2785     PEP_STATUS _cached_decrypt_status = *decrypt_status;
  2786         
  2787     pEp_identity* sender = src->from;
  2788 
  2789     bloblist_t* detached_sig = NULL;
  2790     PEP_STATUS status = _get_detached_signature(msg, &detached_sig);
  2791     stringlist_t *verify_keylist = NULL;
  2792     
  2793     
  2794     if (detached_sig) {
  2795         char* dsig_text = detached_sig->value;
  2796         size_t dsig_size = detached_sig->size;
  2797         size_t ssize = 0;
  2798         char* stext = NULL;
  2799 
  2800         status = _get_signed_text(plaintext, plaintext_size, &stext, &ssize);
  2801 
  2802         if (ssize > 0 && stext) {
  2803             status = cryptotech[crypto].verify_text(session, stext,
  2804                                                     ssize, dsig_text, dsig_size,
  2805                                                     &verify_keylist);
  2806         }
  2807         
  2808         if (status == PEP_VERIFIED || status == PEP_VERIFIED_AND_TRUSTED)
  2809         {
  2810             *decrypt_status = PEP_DECRYPTED_AND_VERIFIED;
  2811         
  2812             status = combine_keylists(session, &verify_keylist, keylist, sender);
  2813         }
  2814     }
  2815     else {
  2816         size_t csize, psize;
  2817         char* ctext;
  2818         char* ptext;
  2819         get_crypto_text(src, &ctext, &csize);
  2820         // reverify - we may have imported a key in the meantime
  2821         // status = cryptotech[crypto].verify_text(session, ctext,
  2822         //                                         csize, NULL, 0,
  2823         //                                         &verify_keylist);
  2824         free_stringlist(*keylist);
  2825         *decrypt_status = decrypt_and_verify(session, ctext, csize,
  2826                                              NULL, 0,
  2827                                              &ptext, &psize, keylist,
  2828                                              NULL);
  2829         
  2830     }
  2831 
  2832     if (*decrypt_status != PEP_DECRYPTED_AND_VERIFIED)
  2833         *decrypt_status = _cached_decrypt_status;                                
  2834 
  2835     return PEP_STATUS_OK;
  2836 }
  2837 
  2838 static PEP_STATUS _decrypt_in_pieces(PEP_SESSION session, 
  2839                                      message* src, 
  2840                                      message** msg_ptr, 
  2841                                      char* ptext,
  2842                                      size_t psize) {
  2843                             
  2844     PEP_STATUS status = PEP_STATUS_OK;
  2845     
  2846     *msg_ptr = clone_to_empty_message(src);
  2847 
  2848     if (*msg_ptr == NULL)
  2849         return PEP_OUT_OF_MEMORY;
  2850 
  2851     message* msg = *msg_ptr;
  2852 
  2853     msg->longmsg = strdup(ptext);
  2854     ptext = NULL;
  2855 
  2856     bloblist_t *_m = msg->attachments;
  2857     if (_m == NULL && src->attachments && src->attachments->value) {
  2858         msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
  2859         _m = msg->attachments;
  2860     }
  2861 
  2862     bloblist_t *_s;
  2863     for (_s = src->attachments; _s && _s->value; _s = _s->next) {
  2864         if (_s->value == NULL && _s->size == 0){
  2865             _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
  2866             if (_m == NULL)
  2867                 return PEP_OUT_OF_MEMORY;
  2868 
  2869         }
  2870         else if (is_encrypted_attachment(_s)) {
  2871             stringlist_t *_keylist = NULL;
  2872             char *attctext  = _s->value;
  2873             size_t attcsize = _s->size;
  2874 
  2875             free(ptext);
  2876             ptext = NULL;
  2877 
  2878             char* pgp_filename = NULL;
  2879             status = decrypt_and_verify(session, attctext, attcsize,
  2880                                         NULL, 0,
  2881                                         &ptext, &psize, &_keylist,
  2882                                         &pgp_filename);
  2883                                         
  2884             free_stringlist(_keylist);
  2885 
  2886             char* filename_uri = NULL;
  2887 
  2888             bool has_uri_prefix = (pgp_filename ? (is_file_uri(pgp_filename) || is_cid_uri(pgp_filename)) :
  2889                                                   (_s->filename ? (is_file_uri(_s->filename) || is_cid_uri(_s->filename)) :
  2890                                                                   false
  2891                                                   )
  2892                                   );
  2893             
  2894 
  2895             if (ptext) {
  2896                 if (is_encrypted_html_attachment(_s)) {
  2897                     msg->longmsg_formatted = ptext;
  2898                     ptext = NULL;
  2899                 }
  2900                 else {
  2901                     static const char * const mime_type = "application/octet-stream";                    
  2902                     if (pgp_filename) {
  2903                         if (!has_uri_prefix)
  2904                             filename_uri = build_uri("file", pgp_filename);
  2905 
  2906                         _m = bloblist_add(_m, ptext, psize, mime_type,
  2907                              (filename_uri ? filename_uri : pgp_filename));
  2908 
  2909                         free(pgp_filename);
  2910                         free(filename_uri);
  2911                         if (_m == NULL)
  2912                             return PEP_OUT_OF_MEMORY;
  2913                     }
  2914                     else {
  2915                         char * const filename =
  2916                             without_double_ending(_s->filename);
  2917                         if (filename == NULL)
  2918                             return PEP_OUT_OF_MEMORY;
  2919 
  2920                         if (!has_uri_prefix)
  2921                             filename_uri = build_uri("file", filename);
  2922 
  2923                         _m = bloblist_add(_m, ptext, psize, mime_type,
  2924                              (filename_uri ? filename_uri : filename));
  2925                         free(filename);
  2926                         free(filename_uri);
  2927                         if (_m == NULL)
  2928                             return PEP_OUT_OF_MEMORY;
  2929                     }
  2930                     ptext = NULL;
  2931 
  2932                     if (msg->attachments == NULL)
  2933                         msg->attachments = _m;
  2934                 }
  2935             }
  2936             else {
  2937                 char *copy = malloc(_s->size);
  2938                 assert(copy);
  2939                 if (copy == NULL)
  2940                     return PEP_OUT_OF_MEMORY;
  2941                 memcpy(copy, _s->value, _s->size);
  2942 
  2943                 if (!has_uri_prefix && _s->filename)
  2944                     filename_uri = build_uri("file", _s->filename);
  2945 
  2946                 _m = bloblist_add(_m, copy, _s->size, _s->mime_type, 
  2947                         (filename_uri ? filename_uri : _s->filename));
  2948                 if (_m == NULL)
  2949                     return PEP_OUT_OF_MEMORY;
  2950             }
  2951         }
  2952         else {
  2953             char *copy = malloc(_s->size);
  2954             assert(copy);
  2955             if (copy == NULL)
  2956                 return PEP_OUT_OF_MEMORY;
  2957             memcpy(copy, _s->value, _s->size);
  2958 
  2959             char* filename_uri = NULL;
  2960 
  2961             _m = bloblist_add(_m, copy, _s->size, _s->mime_type, 
  2962                     ((_s->filename && !(is_file_uri(_s->filename) || is_cid_uri(_s->filename))) ?
  2963                          (filename_uri = build_uri("file", _s->filename)) : _s->filename));
  2964             free(filename_uri);
  2965             if (_m == NULL)
  2966                 return PEP_OUT_OF_MEMORY;
  2967         }
  2968     }
  2969 
  2970     return status;
  2971 }
  2972 
  2973 static PEP_STATUS import_priv_keys_from_decrypted_msg(PEP_SESSION session,
  2974                                                       message* msg,
  2975                                                       bool* imported_keys,
  2976                                                       bool* imported_private,
  2977                                                       identity_list** private_il)
  2978 {
  2979     assert(msg && imported_keys && imported_private);
  2980     if (!(msg && imported_keys && imported_private))
  2981         return PEP_ILLEGAL_VALUE;
  2982 
  2983     PEP_STATUS status = PEP_STATUS_OK;
  2984     *imported_keys = NULL;
  2985     *imported_private = false;
  2986     if (private_il)
  2987         *private_il = NULL;
  2988 
  2989     // check for private key in decrypted message attachment while importing
  2990     identity_list *_private_il = NULL;
  2991 
  2992     bool _imported_keys = import_attached_keys(session, msg, &_private_il);
  2993     bool _imported_private = false;
  2994     if (_private_il && _private_il->ident && _private_il->ident->address)
  2995         _imported_private = true;
  2996 
  2997     if (private_il && _imported_private) {
  2998         // the private identity list should NOT be subject to myself() or
  2999         // update_identity() at this point.
  3000         // If the receiving app wants them to be in the trust DB, it
  3001         // should call set_own_key() on them upon return.
  3002         // We do, however, prepare these so the app can use them
  3003         // directly in a set_own_key() call by putting the own_id on it.
  3004         char* own_id = NULL;
  3005         status = get_default_own_userid(session, &own_id);
  3006         
  3007         for (identity_list* il = _private_il; il; il = il->next) {
  3008             if (own_id) {
  3009                 free(il->ident->user_id);
  3010                 il->ident->user_id = strdup(own_id);
  3011                 assert(il->ident->user_id);
  3012                 if (!il->ident->user_id) {
  3013                     status = PEP_OUT_OF_MEMORY;
  3014                     break;
  3015                 }
  3016             }
  3017             il->ident->me = true;
  3018         }
  3019         free(own_id);
  3020         if (!status)
  3021             *private_il = _private_il;
  3022     }
  3023     else {
  3024         free_identity_list(_private_il);
  3025     }
  3026  
  3027     if (!status) {
  3028         *imported_keys = _imported_keys;
  3029         *imported_private = _imported_private;
  3030     }
  3031 
  3032     return status;
  3033 }
  3034 
  3035 // ident is in_only and should have been updated
  3036 static PEP_STATUS pEp_version_upgrade_or_ignore(
  3037         PEP_SESSION session,
  3038         pEp_identity* ident,
  3039         unsigned int major,
  3040         unsigned int minor) {
  3041             
  3042     PEP_STATUS status = PEP_STATUS_OK;        
  3043     int ver_compare = compare_versions(major, minor, ident->major_ver, ident->minor_ver);
  3044     if (ver_compare > 0)
  3045         status = set_pEp_version(session, ident, major, minor);        
  3046     
  3047     return status;    
  3048 }
  3049 
  3050 // FIXME: myself ??????
  3051 static PEP_STATUS update_sender_to_pEp_trust(
  3052         PEP_SESSION session, 
  3053         pEp_identity* sender, 
  3054         stringlist_t* keylist,
  3055         unsigned int major,
  3056         unsigned int minor) 
  3057 {
  3058     assert(session);
  3059     assert(sender);
  3060     assert(keylist && !EMPTYSTR(keylist->value));
  3061     
  3062     if (!session || !sender || !keylist || EMPTYSTR(keylist->value))
  3063         return PEP_ILLEGAL_VALUE;
  3064         
  3065     free(sender->fpr);
  3066     sender->fpr = NULL;
  3067 
  3068     PEP_STATUS status = PEP_STATUS_OK;
  3069 
  3070     // Seems status doesn't matter
  3071     is_me(session, sender) ? myself(session, sender) : update_identity(session, sender);
  3072 
  3073     if (EMPTYSTR(sender->fpr) || strcmp(sender->fpr, keylist->value) != 0) {
  3074         free(sender->fpr);
  3075         sender->fpr = strdup(keylist->value);
  3076         if (!sender->fpr)
  3077             return PEP_OUT_OF_MEMORY;
  3078         status = set_pgp_keypair(session, sender->fpr);
  3079         if (status != PEP_STATUS_OK)
  3080             return status;
  3081             
  3082         status = get_trust(session, sender);
  3083         
  3084         if (status == PEP_CANNOT_FIND_IDENTITY || sender->comm_type == PEP_ct_unknown) {
  3085             PEP_comm_type ct = PEP_ct_unknown;
  3086             status = get_key_rating(session, sender->fpr, &ct);
  3087             if (status != PEP_STATUS_OK)
  3088                 return status;
  3089                 
  3090             sender->comm_type = ct;    
  3091         }
  3092     }
  3093     
  3094     // Could be done elegantly, but we do this explicitly here for readability.
  3095     // This file's code is difficult enough to parse. But change at will.
  3096     switch (sender->comm_type) {            
  3097         case PEP_ct_OpenPGP_unconfirmed:
  3098         case PEP_ct_OpenPGP:
  3099             sender->comm_type = PEP_ct_pEp_unconfirmed | (sender->comm_type & PEP_ct_confirmed);
  3100             status = set_trust(session, sender);
  3101             if (status != PEP_STATUS_OK)
  3102                 break;
  3103         case PEP_ct_pEp:
  3104         case PEP_ct_pEp_unconfirmed:
  3105             // set version
  3106             if (major == 0) {
  3107                 major = 2;
  3108                 minor = 0;
  3109             }
  3110             status = pEp_version_upgrade_or_ignore(session, sender, major, minor);    
  3111             break;
  3112         default:
  3113             status = PEP_CANNOT_SET_TRUST;
  3114             break;
  3115     }
  3116     
  3117     return status;
  3118 }
  3119 
  3120 static PEP_STATUS reconcile_identity(pEp_identity* srcid,
  3121                                      pEp_identity* resultid) {
  3122     assert(srcid);
  3123     assert(resultid);
  3124 
  3125     if (!srcid || !resultid)
  3126         return PEP_ILLEGAL_VALUE;
  3127         
  3128     if (!EMPTYSTR(srcid->user_id)) {
  3129         if (EMPTYSTR(resultid->user_id) ||
  3130              strcmp(srcid->user_id, resultid->user_id) != 0) {
  3131             free(resultid->user_id);
  3132             resultid->user_id = strdup(srcid->user_id);
  3133         }
  3134     }
  3135     
  3136     resultid->lang[0] = srcid->lang[0];
  3137     resultid->lang[1] = srcid->lang[1];
  3138     resultid->lang[2] = 0;
  3139     resultid->me = srcid->me;
  3140     resultid->flags = srcid->flags;
  3141 
  3142     return PEP_STATUS_OK;
  3143 }
  3144 
  3145 static PEP_STATUS reconcile_identity_lists(identity_list* src_ids,
  3146                                            identity_list* result_ids) {
  3147                                            
  3148     identity_list* curr_id = result_ids;
  3149     
  3150     PEP_STATUS status = PEP_STATUS_OK;
  3151     
  3152     while (curr_id) {
  3153         identity_list* curr_src_id = src_ids;
  3154         pEp_identity* result_identity = curr_id->ident;
  3155         
  3156         while (curr_src_id) {
  3157             pEp_identity* source_identity = curr_src_id->ident;
  3158             
  3159             if (EMPTYSTR(source_identity->address) || EMPTYSTR(result_identity->address))
  3160                 return PEP_ILLEGAL_VALUE; // something went badly wrong
  3161             
  3162             if (strcasecmp(source_identity->address, result_identity->address) == 0) {
  3163                 status = reconcile_identity(source_identity, result_identity);
  3164                 if (status != PEP_STATUS_OK)
  3165                     return status;
  3166             }
  3167             curr_src_id = curr_src_id->next;        
  3168         }
  3169         curr_id = curr_id->next;
  3170     }
  3171     return status;    
  3172 }
  3173 
  3174 static PEP_STATUS reconcile_sent_and_recv_info(message* src, message* inner_message) {
  3175     if (!src || !inner_message)
  3176         return PEP_ILLEGAL_VALUE;
  3177         
  3178     if (!inner_message->sent)
  3179         inner_message->sent = timestamp_dup(src->sent);
  3180         
  3181     // This will never be set otherwise, since it's a transport header on the outside    
  3182     inner_message->recv = timestamp_dup(src->recv);
  3183     
  3184     return PEP_STATUS_OK;
  3185 }
  3186 
  3187 static PEP_STATUS reconcile_src_and_inner_messages(message* src, 
  3188                                              message* inner_message) {
  3189 
  3190     PEP_STATUS status = PEP_STATUS_OK;
  3191     
  3192     if (strcasecmp(src->from->address, inner_message->from->address) == 0)
  3193         status = reconcile_identity(src->from, inner_message->from);
  3194     
  3195     if (status == PEP_STATUS_OK && inner_message->to)
  3196         status = reconcile_identity_lists(src->to, inner_message->to);
  3197 
  3198     if (status == PEP_STATUS_OK && inner_message->cc)
  3199         status = reconcile_identity_lists(src->cc, inner_message->cc);
  3200 
  3201     if (status == PEP_STATUS_OK && inner_message->bcc)
  3202         status = reconcile_identity_lists(src->bcc, inner_message->bcc);
  3203 
  3204     if (status == PEP_STATUS_OK)
  3205         status = reconcile_sent_and_recv_info(src, inner_message);
  3206         
  3207     return status;
  3208     // FIXME - are there any flags or anything else we need to be sure are carried?
  3209 }
  3210 
  3211 static bool is_trusted_own_priv_fpr(PEP_SESSION session, 
  3212                        const char* own_id, 
  3213                        const char* fpr
  3214     ) 
  3215 {   
  3216     bool retval = false;
  3217     if (!EMPTYSTR(fpr)) {
  3218         pEp_identity* test_identity = new_identity(NULL, fpr, own_id, NULL);
  3219         if (test_identity) {
  3220             PEP_STATUS status = get_trust(session, test_identity);
  3221             if (status == PEP_STATUS_OK) {
  3222                 if (test_identity->comm_type & PEP_ct_confirmed) {
  3223                     bool has_priv = false;
  3224                     status = contains_priv_key(session, fpr, &has_priv);
  3225                     if (status == PEP_STATUS_OK && has_priv)
  3226                         retval = true;
  3227                 }
  3228             }
  3229             free(test_identity);
  3230         }
  3231     }
  3232     return retval;
  3233 }
  3234 
  3235 static bool reject_fpr(PEP_SESSION session, const char* fpr) {
  3236     bool reject = true;
  3237 
  3238     PEP_STATUS status = key_revoked(session, fpr, &reject);
  3239 
  3240     if (!reject) {
  3241         status = key_expired(session, fpr, time(NULL), &reject);
  3242         if (reject) {
  3243             timestamp *ts = new_timestamp(time(NULL) + KEY_EXPIRE_DELTA);
  3244             status = renew_key(session, fpr, ts);
  3245             free_timestamp(ts);
  3246             if (status == PEP_STATUS_OK)
  3247                 reject = false;
  3248         }
  3249     }
  3250     return reject;
  3251 }
  3252 
  3253 static char* seek_good_trusted_private_fpr(PEP_SESSION session, char* own_id, 
  3254                                            stringlist_t* keylist) {
  3255     if (!own_id || !keylist)
  3256         return NULL;
  3257         
  3258     stringlist_t* kl_curr = keylist;
  3259     while (kl_curr) {
  3260         char* fpr = kl_curr->value;
  3261         
  3262         if (is_trusted_own_priv_fpr(session, own_id, fpr)) { 
  3263             if (!reject_fpr(session, fpr))
  3264                 return strdup(fpr);
  3265         }
  3266             
  3267         kl_curr = kl_curr->next;
  3268     }
  3269 
  3270     char* target_own_fpr = NULL;
  3271     
  3272     // Last shot...
  3273     PEP_STATUS status = get_user_default_key(session, own_id, 
  3274                                              &target_own_fpr);
  3275 
  3276     if (status == PEP_STATUS_OK && !EMPTYSTR(target_own_fpr)) {
  3277         if (is_trusted_own_priv_fpr(session, own_id, target_own_fpr)) { 
  3278             if (!reject_fpr(session, target_own_fpr))
  3279                 return target_own_fpr;
  3280         }
  3281     }
  3282     
  3283     // TODO: We can also go through all of the other available fprs for the
  3284     // own identity, but then I submit this function requires a little refactoring
  3285         
  3286     return NULL;
  3287 }
  3288 
  3289 static bool import_header_keys(PEP_SESSION session, message* src) {
  3290     stringpair_list_t* header_keys = stringpair_list_find(src->opt_fields, "Autocrypt"); 
  3291     if (!header_keys || !header_keys->value)
  3292         return false;
  3293     const char* value = header_keys->value->value;
  3294     if (!value)
  3295         return false;
  3296     const char* start_key = strstr(value, "keydata=");
  3297     if (!start_key)
  3298         return false;
  3299     start_key += 8; // length of "keydata="
  3300     int length = strlen(start_key);
  3301     bloblist_t* the_key = base64_str_to_binary_blob(start_key, length);
  3302     if (!the_key)
  3303         return false;
  3304     PEP_STATUS status = import_key(session, the_key->value, the_key->size, NULL);
  3305     free_bloblist(the_key);
  3306     if (status == PEP_STATUS_OK || status == PEP_KEY_IMPORTED)
  3307         return true;
  3308     return false;
  3309 }
  3310 
  3311 PEP_STATUS check_for_own_revoked_key(
  3312         PEP_SESSION session, 
  3313         stringlist_t* keylist,
  3314         stringpair_list_t** revoked_fpr_pairs
  3315     ) 
  3316 {
  3317     if (!session || !revoked_fpr_pairs)
  3318         return PEP_ILLEGAL_VALUE;
  3319         
  3320     *revoked_fpr_pairs = NULL;
  3321 
  3322     PEP_STATUS status = PEP_STATUS_OK;
  3323     stringpair_list_t* _the_list = new_stringpair_list(NULL);
  3324         
  3325     stringlist_t* _k = keylist;
  3326     for ( ; _k; _k = _k->next) {
  3327 
  3328         if (EMPTYSTR(_k->value))
  3329             continue; // Maybe the right thing to do is choke. 
  3330                       // But we can have NULL-valued empty list heads.
  3331 
  3332         const char* recip_fpr = _k->value;
  3333         char* replace_fpr = NULL;
  3334         uint64_t revoke_date = 0; 
  3335         status = get_replacement_fpr(session, 
  3336                                      recip_fpr, 
  3337                                      &replace_fpr, 
  3338                                      &revoke_date);
  3339 
  3340         bool own_key = false;
  3341         
  3342         switch (status) {
  3343             case PEP_CANNOT_FIND_IDENTITY:
  3344                 status = PEP_STATUS_OK;
  3345                 continue;
  3346             case PEP_STATUS_OK:
  3347         
  3348                 status = is_own_key(session, recip_fpr, &own_key);
  3349                 
  3350                 if (status != PEP_STATUS_OK) {
  3351                     free(replace_fpr);
  3352                     return status;
  3353                 }
  3354                 
  3355                 if (own_key)
  3356                     stringpair_list_add(_the_list, new_stringpair(recip_fpr, replace_fpr));
  3357 
  3358                 free(replace_fpr);
  3359                 replace_fpr = NULL;
  3360                 break;
  3361             default:    
  3362                 goto pEp_free;    
  3363         }
  3364     }
  3365     
  3366     if (_the_list && _the_list->value) {
  3367         *revoked_fpr_pairs = _the_list;
  3368         _the_list = NULL;
  3369     }
  3370             
  3371 pEp_free:
  3372     free_stringpair_list(_the_list);
  3373     return status;
  3374 
  3375 }
  3376 
  3377 static bool _have_extrakeys(stringlist_t *keylist)
  3378 {
  3379     return keylist
  3380         && keylist->value
  3381         && keylist->value[0];
  3382 }
  3383 
  3384 static PEP_STATUS _decrypt_message(
  3385         PEP_SESSION session,
  3386         message *src,
  3387         message **dst,
  3388         stringlist_t **keylist,
  3389         PEP_rating *rating,
  3390         PEP_decrypt_flags_t *flags,
  3391         identity_list **private_il
  3392     )
  3393 {
  3394     assert(session);
  3395     assert(src);
  3396     assert(dst);
  3397     assert(keylist);
  3398     assert(rating);
  3399     assert(flags);
  3400 
  3401     if (!(session && src && dst && keylist && rating && flags))
  3402         return PEP_ILLEGAL_VALUE;
  3403 
  3404     /*** Begin init ***/
  3405     PEP_STATUS status = PEP_STATUS_OK;
  3406     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  3407     PEP_STATUS _decrypt_in_pieces_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  3408     message* msg = NULL;
  3409     message* calculated_src = src;
  3410     message* reset_msg = NULL;
  3411     
  3412     char *ctext;
  3413     size_t csize;
  3414     char *ptext = NULL;
  3415     size_t psize;
  3416     stringlist_t *_keylist = NULL;
  3417     bool is_pEp_msg = is_a_pEpmessage(src);
  3418     bool myself_read_only = (src->dir == PEP_dir_incoming);
  3419     unsigned int major_ver = 0;
  3420     unsigned int minor_ver = 0;
  3421     
  3422     // Grab input flags
  3423     bool reencrypt = ((*flags & PEP_decrypt_flag_untrusted_server) &&
  3424             (_have_extrakeys(*keylist) || session->unencrypted_subject));
  3425     
  3426     // We own this pointer, and we take control of *keylist if reencrypting.
  3427     stringlist_t* extra = NULL;
  3428     if (reencrypt)
  3429         extra = *keylist;
  3430             
  3431     *dst = NULL;
  3432     *keylist = NULL;
  3433     *rating = PEP_rating_undefined;
  3434 //    *flags = 0;
  3435 
  3436     /*** End init ***/
  3437 
  3438     // Ok, before we do anything, if it's a pEp message, regardless of whether it's
  3439     // encrypted or not, we set the sender as a pEp user. This has NOTHING to do
  3440     // with the key.
  3441     if (src->from && !(is_me(session, src->from))) {
  3442         if (is_pEp_msg) {
  3443             pEp_identity* tmp_from = src->from;
  3444     
  3445             // Ensure there's a user id
  3446             if (EMPTYSTR(tmp_from->user_id) && tmp_from->address) {
  3447                 status = update_identity(session, tmp_from);
  3448                 if (status == PEP_CANNOT_FIND_IDENTITY) {
  3449                     tmp_from->user_id = calloc(1, strlen(tmp_from->address) + 6);
  3450                     if (!tmp_from->user_id)
  3451                         return PEP_OUT_OF_MEMORY;
  3452                     snprintf(tmp_from->user_id, strlen(tmp_from->address) + 6,
  3453                              "TOFU_%s", tmp_from->address);        
  3454                     status = PEP_STATUS_OK;
  3455                 }
  3456             }
  3457             if (status == PEP_STATUS_OK) {
  3458                 // Now set user as PEP (may also create an identity if none existed yet)
  3459                 status = set_as_pEp_user(session, tmp_from);
  3460             }
  3461         }
  3462     }
  3463     // We really need key used in signing to do anything further on the pEp comm_type.
  3464     // So we can't adjust the rating of the sender just yet.
  3465 
  3466     /*** Begin Import any attached public keys and update identities accordingly ***/
  3467     // Private key in unencrypted mail are ignored -> NULL
  3468     //
  3469     // This import is from the outermost message.
  3470     // We don't do this for PGP_mime.
  3471     bool imported_keys = false;
  3472     PEP_cryptotech enc_type = determine_encryption_format(src);
  3473     if (enc_type != PEP_crypt_OpenPGP || !(src->enc_format == PEP_enc_PGP_MIME || src->enc_format == PEP_enc_PGP_MIME_Outlook1))
  3474         imported_keys = import_attached_keys(session, src, NULL);
  3475             
  3476     import_header_keys(session, src);
  3477     
  3478     // FIXME: is this really necessary here?
  3479     // if (src->from) {
  3480     //     if (!is_me(session, src->from))
  3481     //         status = update_identity(session, src->from);
  3482     //     else
  3483     //         status = _myself(session, src->from, false, false, myself_read_only);
  3484     // 
  3485     //     // We absolutely should NOT be bailing here unless it's a serious error
  3486     //     if (status == PEP_OUT_OF_MEMORY)
  3487     //         return status;
  3488     // }
  3489     
  3490     /*** End Import any attached public keys and update identities accordingly ***/
  3491     
  3492     /*** Begin get detached signatures that are attached to the encrypted message ***/
  3493     // Get detached signature, if any
  3494     bloblist_t* detached_sig = NULL;
  3495     char* dsig_text = NULL;
  3496     size_t dsig_size = 0;
  3497     status = _get_detached_signature(src, &detached_sig);
  3498     if (detached_sig) {
  3499         dsig_text = detached_sig->value;
  3500         dsig_size = detached_sig->size;
  3501     }
  3502     /*** End get detached signatures that are attached to the encrypted message ***/
  3503 
  3504     /*** Determine encryption format ***/
  3505     PEP_cryptotech crypto = determine_encryption_format(src);
  3506 
  3507     // Check for and deal with unencrypted messages
  3508     if (src->enc_format == PEP_enc_none) {
  3509 
  3510         *rating = PEP_rating_unencrypted;
  3511 
  3512         // We remove these from the outermost source message
  3513         // if (imported_keys)
  3514         //     remove_attached_keys(src);
  3515                                     
  3516         pull_up_attached_main_msg(src);
  3517         
  3518         return PEP_UNENCRYPTED;
  3519     }
  3520 
  3521     status = get_crypto_text(src, &ctext, &csize);
  3522     if (status != PEP_STATUS_OK)
  3523         return status;
  3524         
  3525     /** Ok, we should be ready to decrypt. Try decrypt and verify first! **/
  3526     status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  3527                                                    csize, dsig_text, dsig_size,
  3528                                                    &ptext, &psize, &_keylist,
  3529                                                    NULL);
  3530 
  3531     if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  3532         goto pEp_error;
  3533 
  3534     decrypt_status = status;
  3535     
  3536     bool imported_private_key_address = false;
  3537     bool has_inner = false;
  3538 
  3539     if (ptext) { 
  3540         /* we got a plaintext from decryption */
  3541         switch (src->enc_format) {
  3542             
  3543             case PEP_enc_PGP_MIME:
  3544             case PEP_enc_PGP_MIME_Outlook1:
  3545             
  3546                 status = _mime_decode_message_internal(ptext, psize, &msg, &has_inner);
  3547                 if (status != PEP_STATUS_OK)
  3548                     goto pEp_error;
  3549                 
  3550                 /* Ensure messages whose maintext is in the attachments
  3551                    move main text into message struct longmsg et al */
  3552                 /* KG: This IS a src modification of old - we're adding to it
  3553                    w/ memhole subject, but the question is whether or not
  3554                    this is OK overall... */
  3555                 pull_up_attached_main_msg(msg);
  3556                 if (msg->shortmsg) {
  3557                     free(src->shortmsg);
  3558                     src->shortmsg = strdup(msg->shortmsg);                    
  3559                 }
  3560 
  3561                 // check for private key in decrypted message attachment while importing
  3562                 // N.B. Apparently, we always import private keys into the keyring; however,
  3563                 // we do NOT always allow those to be used for encryption. THAT is controlled
  3564                 // by setting it as an own identity associated with the key in the DB.
  3565                 //
  3566                 // We are importing from the decrypted outermost message now.
  3567                 //
  3568                 status = import_priv_keys_from_decrypted_msg(session, msg,
  3569                                                              &imported_keys,
  3570                                                              &imported_private_key_address,
  3571                                                              private_il);
  3572                 if (status != PEP_STATUS_OK)
  3573                     goto pEp_error;            
  3574 
  3575                 /* if decrypted, but not verified... */
  3576                 if (decrypt_status == PEP_DECRYPTED) {
  3577                     
  3578                     if (src->from)                                                                 
  3579                         status = verify_decrypted(session,
  3580                                                   src, msg,
  3581                                                   ptext, psize,
  3582                                                   &_keylist,
  3583                                                   &decrypt_status,
  3584                                                   crypto);
  3585                 }
  3586                 break;
  3587 
  3588             case PEP_enc_inline:
  3589                 status = PEP_STATUS_OK;
  3590                 
  3591                 _decrypt_in_pieces_status = _decrypt_in_pieces(session, src, &msg, ptext, psize);
  3592             
  3593                 switch (_decrypt_in_pieces_status) {
  3594                     case PEP_DECRYPTED:
  3595                     case PEP_DECRYPTED_AND_VERIFIED:
  3596                         if (decrypt_status <= PEP_DECRYPTED_AND_VERIFIED)
  3597                             decrypt_status = MIN(decrypt_status, _decrypt_in_pieces_status);
  3598                         break;
  3599                     case PEP_STATUS_OK:
  3600                         break;    
  3601                     case PEP_OUT_OF_MEMORY:
  3602                         goto enomem;
  3603                     default:
  3604                         decrypt_status = _decrypt_in_pieces_status;
  3605                         break;
  3606                 }
  3607                 break;
  3608             default:
  3609                 // BUG: must implement more
  3610                 NOT_IMPLEMENTED
  3611         }
  3612 
  3613         if (status == PEP_OUT_OF_MEMORY)
  3614             goto enomem;
  3615             
  3616         if (status != PEP_STATUS_OK)
  3617             goto pEp_error;
  3618 
  3619         if (decrypt_status == PEP_DECRYPTED || decrypt_status == PEP_DECRYPTED_AND_VERIFIED) {
  3620             char* wrap_info = NULL;
  3621             
  3622             if (!has_inner) {
  3623                 status = unencapsulate_hidden_fields(src, msg, &wrap_info);
  3624                 if (status == PEP_OUT_OF_MEMORY)
  3625                     goto enomem;                
  3626                 if (status != PEP_STATUS_OK)
  3627                     goto pEp_error;
  3628             }        
  3629 
  3630 //            bool is_transport_wrapper = false;
  3631             
  3632         
  3633             // FIXME: replace with enums, check status
  3634             if (has_inner || wrap_info) { // Given that only wrap_info OUTER happens as of the end of wrap_info use, we don't need to strcmp it
  3635                 // if (strcmp(wrap_info, "OUTER") == 0) {
  3636                 //     // this only occurs in with a direct outer wrapper
  3637                 //     // where the actual content is in the inner wrapper
  3638                 message* inner_message = NULL;
  3639                     
  3640                 // For a wrapped message, this is ALWAYS the second attachment; the 
  3641                 // mime tree is:
  3642                 // multipart/mixed
  3643                 //     |
  3644                 //     |----- text/plain 
  3645                 //     |----- message/rfc822
  3646                 //     |----- ...
  3647                 //
  3648                 // We leave this in below, but once we're rid of 2.0 format,
  3649                 // we can dispense with the loop, as has_inner -> 1st message struct attachment is message/rfc822
  3650                 //                   
  3651 
  3652                 bloblist_t* message_blob = msg->attachments;
  3653                                     
  3654                 if (msg->attachments) {
  3655                     message_blob = msg->attachments;
  3656                     if (!has_inner && strcmp(message_blob->mime_type, "message/rfc822") != 0
  3657                                    && strcmp(message_blob->mime_type, "text/rfc822") != 0)
  3658                         message_blob = NULL;
  3659                 }
  3660                     
  3661                 if (!message_blob) {
  3662                     bloblist_t* actual_message = msg->attachments;
  3663                 
  3664                     while (actual_message) {
  3665                         char* mime_type = actual_message->mime_type;
  3666                         if (mime_type) {
  3667                         
  3668                             // libetpan appears to change the mime_type on this one.
  3669                             // *growl*
  3670                             if (strcmp("message/rfc822", mime_type) == 0 ||
  3671                                 strcmp("text/rfc822", mime_type) == 0) {
  3672                                 message_blob = actual_message;
  3673                                 break;
  3674                             }
  3675                         }
  3676                         actual_message = actual_message->next;
  3677                     }        
  3678                 }    
  3679                 if (message_blob) {              
  3680                     status = mime_decode_message(message_blob->value, 
  3681                                                  message_blob->size, 
  3682                                                  &inner_message);
  3683                     if (status != PEP_STATUS_OK)
  3684                         goto pEp_error;
  3685                                 
  3686                     if (inner_message) {
  3687                         is_pEp_msg = is_a_pEpmessage(inner_message);
  3688                         
  3689                         // Though this will strip any message info on the
  3690                         // attachment, this is safe, as we do not
  3691                         // produce more than one attachment-as-message,
  3692                         // and those are the only ones with such info.
  3693                         // Since we capture the information, this is ok.
  3694                         wrap_info = NULL;
  3695                         inner_message->enc_format = src->enc_format;
  3696 
  3697                         const stringpair_list_t* pEp_protocol_version = NULL;
  3698                         pEp_protocol_version = stringpair_list_find(inner_message->opt_fields, "X-pEp-Version");
  3699                         
  3700                         if (pEp_protocol_version && pEp_protocol_version->value)
  3701                             pEp_version_major_minor(pEp_protocol_version->value->value, &major_ver, &minor_ver);
  3702 
  3703                         // Sort out pEp user status and version number based on INNER message.
  3704                         
  3705                         bool is_inner = false;
  3706                         bool is_key_reset = false;
  3707 
  3708                         // Deal with plaintext modification in 1.0 and 2.0 messages
  3709                         status = unencapsulate_hidden_fields(inner_message, NULL, &wrap_info);   
  3710                         
  3711                         if (status == PEP_OUT_OF_MEMORY)
  3712                             goto enomem;                
  3713                         if (status != PEP_STATUS_OK)
  3714                             goto pEp_error;                                         
  3715                             
  3716                         if (major_ver > 2 || (major_ver == 2 && minor_ver > 0)) {
  3717                             stringpair_list_t* searched = stringpair_list_find(inner_message->opt_fields, "X-pEp-Sender-FPR");                             
  3718                             inner_message->_sender_fpr = ((searched && searched->value && searched->value->value) ? strdup(searched->value->value) : NULL);
  3719                             searched = stringpair_list_find(inner_message->opt_fields, X_PEP_MSG_WRAP_KEY);
  3720                             if (searched && searched->value && searched->value->value) {
  3721                                 is_inner = (strcmp(searched->value->value, "INNER") == 0);
  3722                                 if (!is_inner)
  3723                                     is_key_reset = (strcmp(searched->value->value, "KEY_RESET") == 0);
  3724                                 if (is_inner || is_key_reset)
  3725                                     inner_message->opt_fields = stringpair_list_delete_by_key(inner_message->opt_fields, X_PEP_MSG_WRAP_KEY);
  3726                             }
  3727                         }
  3728                         else {
  3729                             is_inner = (strcmp(wrap_info, "INNER") == 0);
  3730                             if (!is_inner)
  3731                                 is_key_reset = (strcmp(wrap_info, "KEY_RESET") == 0);
  3732                         }
  3733                             
  3734 
  3735                         if (is_key_reset) {
  3736                             if (decrypt_status == PEP_DECRYPTED || decrypt_status == PEP_DECRYPTED_AND_VERIFIED) {
  3737                                 status = receive_key_reset(session,
  3738                                                            inner_message);
  3739                                 if (status != PEP_STATUS_OK) {
  3740                                     free_message(inner_message);
  3741                                     goto pEp_error;
  3742                                 }
  3743                                 *flags |= PEP_decrypt_flag_consume;
  3744                             }
  3745                         }
  3746                         else if (is_inner) {
  3747 
  3748                             // check for private key in decrypted message attachment while importing
  3749                             // N.B. Apparently, we always import private keys into the keyring; however,
  3750                             // we do NOT always allow those to be used for encryption. THAT is controlled
  3751                             // by setting it as an own identity associated with the key in the DB.
  3752                             
  3753                             // If we have a message 2.0 message, we are ONLY going to be ok with keys
  3754                             // we imported from THIS part of the message.
  3755                             imported_private_key_address = false;
  3756                             free(private_il); 
  3757                             private_il = NULL;
  3758                             
  3759                             // import keys from decrypted INNER source
  3760                             status = import_priv_keys_from_decrypted_msg(session, inner_message,
  3761                                                                          &imported_keys,
  3762                                                                          &imported_private_key_address,
  3763                                                                          private_il);
  3764                             if (status != PEP_STATUS_OK)
  3765                                 goto pEp_error;            
  3766 
  3767                             // THIS is our message
  3768                             // Now, let's make sure we've copied in 
  3769                             // any information sent in by the app if
  3770                             // needed...
  3771                             reconcile_src_and_inner_messages(src, inner_message);
  3772                             
  3773 
  3774                             // FIXME: free msg, but check references
  3775                             //src = msg = inner_message;
  3776                             calculated_src = msg = inner_message;
  3777                             
  3778                         }
  3779                         else { // should never happen
  3780                             status = PEP_UNKNOWN_ERROR;
  3781                             free_message(inner_message);
  3782                             goto pEp_error;
  3783                         }
  3784                         inner_message->enc_format = PEP_enc_none;
  3785                     }
  3786                     else { // forwarded message, leave it alone
  3787                         free_message(inner_message);
  3788                     }
  3789                 } // end if (message_blob)
  3790                 
  3791                 //  else if (strcmp(wrap_info, "TRANSPORT") == 0) {
  3792                 //      // FIXME: this gets even messier.
  3793                 //      // (TBI in ENGINE-278)
  3794                 //  }
  3795                 //  else {} // shouldn't be anything to be done here
  3796     
  3797             } // end if (has_inner || wrap_info)
  3798             else {
  3799                 
  3800             } // this we do if this isn't an inner message
  3801             
  3802             pEp_identity* cs_from = calculated_src->from;
  3803             if (cs_from && !EMPTYSTR(cs_from->address)) {
  3804                 if (!is_me(session, cs_from)) {
  3805                     status = update_identity(session, cs_from);
  3806                     if (status == PEP_CANNOT_FIND_IDENTITY) {
  3807                         cs_from->user_id = calloc(1, strlen(cs_from->address) + 6);
  3808                         if (!cs_from->user_id)
  3809                             return PEP_OUT_OF_MEMORY;
  3810                         snprintf(cs_from->user_id, strlen(cs_from->address) + 6,
  3811                                  "TOFU_%s", cs_from->address);        
  3812                         status = PEP_STATUS_OK;
  3813                     }
  3814                 }
  3815                 else
  3816                     status = _myself(session, cs_from, false, false, myself_read_only);
  3817             }                                                                        
  3818         } // end if (decrypt_status == PEP_DECRYPTED || decrypt_status == PEP_DECRYPTED_AND_VERIFIED)
  3819         
  3820         *rating = decrypt_rating(decrypt_status);
  3821         
  3822         // Ok, so if it was signed and it's all verified, we can update
  3823         // eligible signer comm_types to PEP_ct_pEp_*
  3824         // This also sets and upgrades pEp version
  3825         if (decrypt_status == PEP_DECRYPTED_AND_VERIFIED && is_pEp_msg && calculated_src->from)
  3826             status = update_sender_to_pEp_trust(session, calculated_src->from, _keylist, major_ver, minor_ver);
  3827 
  3828         /* Ok, now we have a keylist used for decryption/verification.
  3829            now we need to update the message rating with the 
  3830            sender and recipients in mind */
  3831         status = amend_rating_according_to_sender_and_recipients(session,
  3832                  rating, calculated_src->from, _keylist);
  3833 
  3834         if (status != PEP_STATUS_OK)
  3835             goto pEp_error;
  3836         
  3837         /* We decrypted ok, hallelujah. */
  3838         msg->enc_format = PEP_enc_none;    
  3839     } 
  3840     else {
  3841         // We did not get a plaintext out of the decryption process.
  3842         // Abort and return error.
  3843         *rating = decrypt_rating(decrypt_status);
  3844         goto pEp_error;
  3845     }
  3846 
  3847     /* 
  3848        Ok, at this point, we know we have a reliably decrypted message.
  3849        Prepare the output message for return.
  3850     */
  3851     
  3852     // 1. Check to see if this message is to us and contains an own key imported 
  3853     // from own trusted message
  3854     if (*rating >= PEP_rating_trusted && imported_private_key_address) {
  3855 
  3856         if (msg && msg->to && msg->to->ident) {            
  3857             // This will only happen rarely, so we can do this.
  3858             PEP_STATUS _tmp_status = PEP_STATUS_OK;
  3859             
  3860             if (!is_me(session, msg->to->ident))
  3861                 _tmp_status = update_identity(session, msg->to->ident);
  3862             
  3863             if (_tmp_status == PEP_STATUS_OK && is_me(session, msg->to->ident)) {
  3864                 // flag it as such
  3865                 *flags |= PEP_decrypt_flag_own_private_key;
  3866             }
  3867         }
  3868     }
  3869 
  3870     // 2. Clean up message and prepare for return 
  3871     if (msg) {
  3872         if (_keylist && _keylist->next)
  3873             dedup_stringlist(_keylist->next);
  3874             
  3875         /* add pEp-related status flags to header */
  3876         decorate_message(msg, *rating, _keylist, false, false);
  3877 
  3878         // Maybe unnecessary
  3879         // if (imported_keys)
  3880         //     remove_attached_keys(msg);
  3881                     
  3882         if (calculated_src->id && calculated_src != msg) {
  3883             msg->id = strdup(calculated_src->id);
  3884             assert(msg->id);
  3885             if (msg->id == NULL)
  3886                 goto enomem;
  3887         }
  3888     } // End prepare output message for return
  3889 
  3890     // 3. Check to see if the sender used any of our revoked keys
  3891     stringpair_list_t* revoke_replace_pairs = NULL;
  3892     status = check_for_own_revoked_key(session, _keylist, &revoke_replace_pairs);
  3893 
  3894     //assert(status != PEP_STATUS_OK); // FIXME: FOR DEBUGGING ONLY DO NOT LEAVE IN    
  3895     if (status != PEP_STATUS_OK) {
  3896         // This should really never choke unless the DB is broken.
  3897         status = PEP_UNKNOWN_DB_ERROR;
  3898         goto pEp_error;
  3899     }
  3900     
  3901     if (msg) {
  3902         stringpair_list_t* curr_pair_node;
  3903         stringpair_t* curr_pair;
  3904 
  3905         for (curr_pair_node = revoke_replace_pairs; curr_pair_node; curr_pair_node = curr_pair_node->next) {
  3906             curr_pair = curr_pair_node->value;
  3907 
  3908             if (!curr_pair)
  3909                 continue; // Again, shouldn't occur
  3910 
  3911             if (curr_pair->key && curr_pair->value) {
  3912                 status = create_standalone_key_reset_message(session,
  3913                     &reset_msg,
  3914                     msg->from,
  3915                     curr_pair->key,
  3916                     curr_pair->value);
  3917 
  3918                 // If we can't find the identity, this is someone we've never mailed, so we just
  3919                 // go on letting them use the wrong key until we mail them ourselves. (Spammers, etc)
  3920                 if (status != PEP_CANNOT_FIND_IDENTITY) {
  3921                     if (status != PEP_STATUS_OK)
  3922                         goto pEp_error;
  3923 
  3924                     if (!reset_msg) {
  3925                         status = PEP_OUT_OF_MEMORY;
  3926                         goto pEp_error;
  3927                     }
  3928                     // insert into queue
  3929                     if (session->messageToSend)
  3930                         status = session->messageToSend(reset_msg);
  3931                     else
  3932                         status = PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
  3933 
  3934 
  3935                     if (status == PEP_STATUS_OK) {
  3936                         // Put into notified DB
  3937                         status = set_reset_contact_notified(session, curr_pair->key, msg->from->user_id);
  3938                         if (status != PEP_STATUS_OK) // It's ok to barf because it's a DB problem??
  3939                             goto pEp_error;
  3940                     }
  3941                     else {
  3942                         // According to Volker, this would only be a fatal error, so...
  3943                         free_message(reset_msg); // ??
  3944                         reset_msg = NULL; // ??
  3945                         goto pEp_error;
  3946                     }
  3947                 }
  3948             }
  3949         }
  3950     }
  3951 
  3952 
  3953     bool reenc_signer_key_is_own_key = false; // only matters for reencrypted messages 
  3954     
  3955     // 4. Reencrypt if necessary
  3956     bool has_extra_keys = _have_extrakeys(extra);
  3957     if (reencrypt && session->unencrypted_subject && !has_extra_keys) {
  3958         if (src->shortmsg && msg->shortmsg) {
  3959             if (strcmp(src->shortmsg, msg->shortmsg) == 0)
  3960                 reencrypt = false;
  3961         }
  3962         else if (src->shortmsg == NULL && msg->shortmsg == NULL)
  3963             reencrypt = false;
  3964     }    
  3965 
  3966     if (reencrypt) {
  3967         if (decrypt_status == PEP_DECRYPTED || decrypt_status == PEP_DECRYPTED_AND_VERIFIED) {
  3968             const char* sfpr = NULL;
  3969             if (has_extra_keys)
  3970                 sfpr = _keylist->value;
  3971 
  3972             if (sfpr && decrypt_status == PEP_DECRYPTED_AND_VERIFIED) {
  3973                 own_key_is_listed(session, sfpr, &reenc_signer_key_is_own_key);
  3974                 
  3975                 if (!reenc_signer_key_is_own_key) {
  3976                     message* reencrypt_msg = NULL;
  3977                     PEP_STATUS reencrypt_status = PEP_CANNOT_REENCRYPT;
  3978                     char* own_id = NULL;
  3979                     status = get_default_own_userid(session, &own_id);
  3980                     if (own_id) {
  3981                         char* target_own_fpr = seek_good_trusted_private_fpr(session,
  3982                                                                              own_id,
  3983                                                                              _keylist);
  3984                         if (target_own_fpr) {
  3985                             pEp_identity* target_id = new_identity(NULL, target_own_fpr, 
  3986                                                                    own_id, NULL);
  3987                             if (target_id) {
  3988                                 reencrypt_status = encrypt_message_for_self(session, target_id, msg,
  3989                                                                             extra, &reencrypt_msg, PEP_enc_PGP_MIME,
  3990                                                                             PEP_encrypt_reencrypt);
  3991                                 if (reencrypt_status != PEP_STATUS_OK)
  3992                                     reencrypt_status = PEP_CANNOT_REENCRYPT;
  3993                                 
  3994                                 free_identity(target_id);
  3995                             }
  3996                             free(target_own_fpr);
  3997                         }     
  3998                         free(own_id);
  3999                     }
  4000                     free_stringlist(extra); // This was an input variable for us. Keylist is overwritten above.
  4001                     
  4002                     if (reencrypt_status != PEP_CANNOT_REENCRYPT && reencrypt_msg) {
  4003                         message_transfer(src, reencrypt_msg);
  4004                         *flags |= PEP_decrypt_flag_src_modified;
  4005                         free_message(reencrypt_msg);
  4006                     }
  4007                     else
  4008                         decrypt_status = PEP_CANNOT_REENCRYPT;
  4009                 }
  4010             }            
  4011             else if (!has_extra_keys && session->unencrypted_subject) {
  4012                 free(src->shortmsg);
  4013                 src->shortmsg = strdup(msg->shortmsg);
  4014                 assert(src->shortmsg);
  4015                 if (!src->shortmsg)
  4016                     goto enomem;
  4017                 *flags |= PEP_decrypt_flag_src_modified;
  4018             }
  4019         }
  4020     }
  4021     
  4022     // 5. Set up return values
  4023     *dst = msg;
  4024     *keylist = _keylist;
  4025     
  4026     // Double-check for message 2.1: (note, we don't do this for already-reencrypted-messages)
  4027     if (!(reencrypt && reenc_signer_key_is_own_key)) { 
  4028         if (major_ver > 2 || (major_ver == 2 && minor_ver > 0)) {
  4029             if (EMPTYSTR((*dst)->_sender_fpr) || 
  4030                (!EMPTYSTR(_keylist->value) && (strcasecmp((*dst)->_sender_fpr, _keylist->value) != 0))) {
  4031                 if (decrypt_status == PEP_DECRYPTED_AND_VERIFIED)
  4032                     decrypt_status = PEP_DECRYPTED;
  4033                 if (*rating > PEP_rating_unreliable)
  4034                     *rating = PEP_rating_unreliable;
  4035             }
  4036         }
  4037     }
  4038     
  4039     if (decrypt_status == PEP_DECRYPTED_AND_VERIFIED)
  4040         return PEP_STATUS_OK;
  4041     else
  4042         return decrypt_status;
  4043 
  4044 enomem:
  4045     status = PEP_OUT_OF_MEMORY;
  4046 
  4047 pEp_error:
  4048     free(ptext);
  4049     free_message(msg);
  4050     free_message(reset_msg);
  4051     free_stringlist(_keylist);
  4052 
  4053     return status;
  4054 }
  4055 
  4056 DYNAMIC_API PEP_STATUS decrypt_message(
  4057         PEP_SESSION session,
  4058         message *src,
  4059         message **dst,
  4060         stringlist_t **keylist,
  4061         PEP_rating *rating,
  4062         PEP_decrypt_flags_t *flags
  4063     )
  4064 {
  4065     assert(session);
  4066     assert(src);
  4067     assert(dst);
  4068     assert(keylist);
  4069     assert(rating);
  4070     assert(flags);
  4071 
  4072     if (!(session && src && dst && keylist && rating && flags))
  4073         return PEP_ILLEGAL_VALUE;
  4074 
  4075     if (!(*flags & PEP_decrypt_flag_untrusted_server))
  4076         *keylist = NULL;
  4077     //*keylist = NULL; // NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO!!!!!! This fucks up reencryption
  4078     PEP_STATUS status = _decrypt_message(session, src, dst, keylist, rating, flags, NULL);
  4079 
  4080     message *msg = *dst ? *dst : src;
  4081 
  4082     if (session->inject_sync_event && msg && msg->from &&
  4083             !(*flags & PEP_decrypt_flag_dont_trigger_sync)) {
  4084         size_t size;
  4085         const char *data;
  4086         char *sender_fpr = NULL;
  4087         PEP_STATUS tmpstatus = base_extract_message(session, msg, &size, &data, &sender_fpr);
  4088         if (!tmpstatus && size && data) {
  4089             if (sender_fpr)
  4090                 signal_Sync_message(session, *rating, data, size, msg->from, sender_fpr);
  4091             // FIXME: this must be changed to sender_fpr
  4092             else if (*keylist)
  4093                 signal_Sync_message(session, *rating, data, size, msg->from, (*keylist)->value);
  4094         }
  4095         free(sender_fpr);
  4096     }
  4097 
  4098 
  4099     // Removed for now - partial fix in ENGINE-647, but we have sync issues. Need to 
  4100     // fix testing issue.
  4101     //
  4102     // if (status == PEP_UNENCRYPTED || status == PEP_DECRYPTED_AND_VERIFIED) {
  4103     //     if (session->inject_sync_event && msg && msg->from &&
  4104     //             !(*flags & PEP_decrypt_flag_dont_trigger_sync)) {
  4105     //         size_t size;
  4106     //         const char *data;
  4107     //         char *sender_fpr = NULL;
  4108     // 
  4109     //         PEP_STATUS tmpstatus = base_extract_message(session, msg, &size, &data, &sender_fpr);
  4110     //         if (!tmpstatus && size && data) {
  4111     //             bool use_extracted_fpr = (status != PEP_DECRYPTED_AND_VERIFIED) ||
  4112     //                                       !dst || !(*dst) || !((*dst)->_sender_fpr);
  4113     // 
  4114     //             const char* event_sender_fpr = (use_extracted_fpr ? sender_fpr : (*dst)->_sender_fpr);
  4115     //             // FIXME - I don't think this is OK anymore. We either have a signed beacon or a properly encrypted/signed 2.1 message
  4116     //             // if ((!event_sender_fpr) && *keylist)
  4117     //             //     event_sender_fpr = (*keylist)->value;
  4118     //             if (event_sender_fpr)
  4119     //                 signal_Sync_message(session, *rating, data, size, msg->from, event_sender_fpr);
  4120     //         }
  4121     //         free(sender_fpr);
  4122     //     }
  4123 
  4124     return status;
  4125 }
  4126 
  4127 DYNAMIC_API PEP_STATUS own_message_private_key_details(
  4128         PEP_SESSION session,
  4129         message *msg,
  4130         pEp_identity **ident
  4131     )
  4132 {
  4133     assert(session);
  4134     assert(msg);
  4135     assert(ident);
  4136 
  4137     if (!(session && msg && ident))
  4138         return PEP_ILLEGAL_VALUE;
  4139 
  4140     message *dst = NULL;
  4141     stringlist_t *keylist = NULL;
  4142     PEP_rating rating;
  4143     PEP_decrypt_flags_t flags;
  4144 
  4145     *ident = NULL;
  4146 
  4147     identity_list *private_il = NULL;
  4148     PEP_STATUS status = _decrypt_message(session, msg,  &dst, &keylist, &rating, &flags, &private_il);
  4149     free_message(dst);
  4150     free_stringlist(keylist);
  4151 
  4152     if (status == PEP_STATUS_OK &&
  4153         flags & PEP_decrypt_flag_own_private_key &&
  4154         private_il)
  4155     {
  4156         *ident = identity_dup(private_il->ident);
  4157     }
  4158 
  4159     free_identity_list(private_il);
  4160 
  4161     return status;
  4162 }
  4163 
  4164 // Note: if comm_type_determine is false, it generally means that
  4165 // we were unable to get key information for anyone in the list,
  4166 // likely because a key is missing.
  4167 static void _max_comm_type_from_identity_list(
  4168         identity_list *identities,
  4169         PEP_SESSION session,
  4170         PEP_comm_type *max_comm_type,
  4171         bool *comm_type_determined
  4172     )
  4173 {
  4174     identity_list * il;
  4175     for (il = identities; il != NULL; il = il->next)
  4176     {
  4177         if (il->ident)
  4178         {   
  4179             PEP_STATUS status = PEP_STATUS_OK;
  4180             *max_comm_type = _get_comm_type(session, *max_comm_type,
  4181                 il->ident);
  4182             *comm_type_determined = true;
  4183             
  4184             bool is_blacklisted = false;
  4185             if (il->ident->fpr && IS_PGP_CT(il->ident->comm_type)) {
  4186                 status = blacklist_is_listed(session, il->ident->fpr, &is_blacklisted);
  4187                 if (is_blacklisted) {
  4188                     bool user_default, ident_default, address_default; 
  4189                     status = get_valid_pubkey(session, il->ident,
  4190                                               &ident_default, &user_default,
  4191                                               &address_default,
  4192                                               true);
  4193                     if (status != PEP_STATUS_OK || il->ident->fpr == NULL) {
  4194                         il->ident->comm_type = PEP_ct_key_not_found;
  4195                         if (*max_comm_type > PEP_ct_no_encryption)
  4196                             *max_comm_type = PEP_ct_no_encryption;
  4197                     }
  4198                 }    
  4199             }
  4200     
  4201             // check for the return statuses which might not a representative
  4202             // value in the comm_type
  4203             if (status == PEP_ILLEGAL_VALUE || status == PEP_CANNOT_SET_PERSON ||
  4204                 status == PEP_CANNOT_FIND_IDENTITY) {
  4205                 // PEP_CANNOT_FIND_IDENTITY only comes back when we've really
  4206                 // got nothing from update_identity after applying the whole
  4207                 // heuristic
  4208                 *max_comm_type = PEP_ct_no_encryption;
  4209                 *comm_type_determined = true;
  4210             }
  4211         }
  4212     }
  4213 }
  4214 
  4215 static void _max_comm_type_from_identity_list_preview(
  4216         identity_list *identities,
  4217         PEP_SESSION session,
  4218         PEP_comm_type *max_comm_type
  4219     )
  4220 {
  4221     identity_list * il;
  4222     for (il = identities; il != NULL; il = il->next)
  4223     {
  4224         if (il->ident)
  4225         {   
  4226             *max_comm_type = _get_comm_type_preview(session, *max_comm_type,
  4227                 il->ident);
  4228         }
  4229     }
  4230 }
  4231 
  4232 DYNAMIC_API PEP_STATUS outgoing_message_rating(
  4233         PEP_SESSION session,
  4234         message *msg,
  4235         PEP_rating *rating
  4236     )
  4237 {
  4238     PEP_comm_type max_comm_type = PEP_ct_pEp;
  4239     bool comm_type_determined = false;
  4240 
  4241     assert(session);
  4242     assert(msg);
  4243     assert(msg->dir == PEP_dir_outgoing);
  4244     assert(rating);
  4245 
  4246     if (!(session && msg && rating))
  4247         return PEP_ILLEGAL_VALUE;
  4248 
  4249     if (msg->dir != PEP_dir_outgoing)
  4250         return PEP_ILLEGAL_VALUE;
  4251 
  4252     *rating = PEP_rating_undefined;
  4253 
  4254     _max_comm_type_from_identity_list(msg->to, session,
  4255                                       &max_comm_type, &comm_type_determined);
  4256 
  4257     _max_comm_type_from_identity_list(msg->cc, session,
  4258                                       &max_comm_type, &comm_type_determined);
  4259 
  4260     _max_comm_type_from_identity_list(msg->bcc, session,
  4261                                       &max_comm_type, &comm_type_determined);
  4262 
  4263     if (comm_type_determined == false) {
  4264         // likely means there was a massive screwup with no sender or recipient
  4265         // keys
  4266         *rating = PEP_rating_undefined;
  4267     }
  4268     else
  4269         *rating = _MAX(_rating(max_comm_type), PEP_rating_unencrypted);
  4270 
  4271     return PEP_STATUS_OK;
  4272 }
  4273 
  4274 DYNAMIC_API PEP_STATUS outgoing_message_rating_preview(
  4275         PEP_SESSION session,
  4276         message *msg,
  4277         PEP_rating *rating
  4278     )
  4279 {
  4280     PEP_comm_type max_comm_type = PEP_ct_pEp;
  4281 
  4282     assert(session);
  4283     assert(msg);
  4284     assert(msg->dir == PEP_dir_outgoing);
  4285     assert(rating);
  4286 
  4287     if (!(session && msg && rating))
  4288         return PEP_ILLEGAL_VALUE;
  4289 
  4290     if (msg->dir != PEP_dir_outgoing)
  4291         return PEP_ILLEGAL_VALUE;
  4292 
  4293     *rating = PEP_rating_undefined;
  4294 
  4295     _max_comm_type_from_identity_list_preview(msg->to, session,
  4296             &max_comm_type);
  4297 
  4298     _max_comm_type_from_identity_list_preview(msg->cc, session,
  4299             &max_comm_type);
  4300 
  4301     _max_comm_type_from_identity_list_preview(msg->bcc, session,
  4302             &max_comm_type);
  4303 
  4304     *rating = _MAX(_rating(max_comm_type), PEP_rating_unencrypted);
  4305 
  4306     return PEP_STATUS_OK;
  4307 }
  4308 
  4309 DYNAMIC_API PEP_STATUS identity_rating(
  4310         PEP_SESSION session,
  4311         pEp_identity *ident,
  4312         PEP_rating *rating
  4313     )
  4314 {
  4315     PEP_STATUS status = PEP_STATUS_OK;
  4316 
  4317     assert(session);
  4318     assert(ident);
  4319     assert(rating);
  4320 
  4321     if (!(session && ident && rating))
  4322         return PEP_ILLEGAL_VALUE;
  4323 
  4324     *rating = PEP_rating_undefined;
  4325 
  4326     if (ident->me)
  4327         status = _myself(session, ident, false, true, true);
  4328     else
  4329         status = update_identity(session, ident);
  4330 
  4331     bool is_blacklisted = false;
  4332     
  4333     if (ident->fpr && IS_PGP_CT(ident->comm_type)) {
  4334         status = blacklist_is_listed(session, ident->fpr, &is_blacklisted);
  4335         if (status != PEP_STATUS_OK) {
  4336             return status; // DB ERROR
  4337         }
  4338         if (is_blacklisted) {
  4339             bool user_default, ident_default, address_default; 
  4340             status = get_valid_pubkey(session, ident,
  4341                                        &ident_default, &user_default,
  4342                                        &address_default,
  4343                                        true);
  4344             if (status != PEP_STATUS_OK || ident->fpr == NULL) {
  4345                 ident->comm_type = PEP_ct_key_not_found;
  4346                 status = PEP_STATUS_OK;                        
  4347             }
  4348         }    
  4349     }
  4350 
  4351     if (status == PEP_STATUS_OK)
  4352         *rating = _rating(ident->comm_type);
  4353 
  4354     return status;
  4355 }
  4356 
  4357 DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
  4358 {
  4359     PEP_STATUS status = PEP_STATUS_OK;
  4360 
  4361     assert(path);
  4362     if (path == NULL)
  4363         return PEP_ILLEGAL_VALUE;
  4364 
  4365     if (cryptotech[tech].binary_path == NULL)
  4366         *path = NULL;
  4367     else
  4368         status = cryptotech[tech].binary_path(path);
  4369 
  4370     return status;
  4371 }
  4372 
  4373 
  4374 DYNAMIC_API PEP_color color_from_rating(PEP_rating rating)
  4375 {
  4376     if (rating == PEP_rating_b0rken || rating == PEP_rating_have_no_key)
  4377         return PEP_color_no_color;
  4378 
  4379     if (rating < PEP_rating_undefined)
  4380         return PEP_color_red;
  4381 
  4382     if (rating < PEP_rating_reliable)
  4383         return PEP_color_no_color;
  4384 
  4385     if (rating < PEP_rating_trusted)
  4386         return PEP_color_yellow;
  4387 
  4388     if (rating >= PEP_rating_trusted)
  4389         return PEP_color_green;
  4390 
  4391     // this should never happen
  4392     assert(false);
  4393     return PEP_color_no_color;
  4394 }
  4395 
  4396 /* [0-9]: 0x30 - 0x39; [A-F] = 0x41 - 0x46; [a-f] = 0x61 - 0x66 */
  4397 static short asciihex_to_num(char a) {
  4398     short conv_num = -1;
  4399     if (a >= 0x30 && a <= 0x39)
  4400         conv_num = a - 0x30;
  4401     else {
  4402         // convert case, subtract offset, get number
  4403         conv_num = ((a | 0x20) - 0x61) + 10;
  4404         if (conv_num < 0xa || conv_num > 0xf)
  4405             conv_num = -1;
  4406     }
  4407     return conv_num;
  4408 }
  4409 
  4410 static char num_to_asciihex(short h) {
  4411     if (h < 0 || h > 16)
  4412         return '\0';
  4413     if (h < 10)
  4414         return (char)(h + 0x30);
  4415     return (char)((h - 10) + 0x41); // for readability
  4416 }
  4417 
  4418 static char xor_hex_chars(char a, char b) {
  4419     short a_num = asciihex_to_num(a);
  4420     short b_num = asciihex_to_num(b);
  4421     if (a_num < 0 || b_num < 0)
  4422         return '\0';
  4423     short xor_num = a_num^b_num;
  4424     return num_to_asciihex(xor_num);
  4425 }
  4426 
  4427 static const char* skip_separators(const char* current, const char* begin) {
  4428     while (current >= begin) {
  4429         /* .:,;-_ ' ' - [2c-2e] [3a-3b] [20] [5f] */
  4430         char check_char = *current;
  4431         switch (check_char) {
  4432             case '.':
  4433             case ':':
  4434             case ',':
  4435             case ';':
  4436             case '-':
  4437             case '_':
  4438             case ' ':
  4439                 current--;
  4440                 continue;
  4441             default:
  4442                 break;
  4443         }
  4444         break;
  4445     }
  4446     return current;
  4447 }
  4448 
  4449 PEP_STATUS check_for_zero_fpr(char* fpr) {
  4450     PEP_STATUS status = PEP_TRUSTWORDS_DUPLICATE_FPR;
  4451     
  4452     while (*fpr) {
  4453         if (*fpr != '0') {
  4454             status = PEP_STATUS_OK;
  4455             break;
  4456         }
  4457         fpr++;    
  4458     }
  4459     
  4460     return status;
  4461     
  4462 }
  4463 
  4464 DYNAMIC_API PEP_STATUS get_trustwords(
  4465         PEP_SESSION session, const pEp_identity* id1, const pEp_identity* id2,
  4466         const char* lang, char **words, size_t *wsize, bool full
  4467     )
  4468 {
  4469     assert(session && id1 && id1->fpr && id2 && id2->fpr&& lang && words &&
  4470             wsize);
  4471     if (!(session && id1 && id1->fpr && id2 && id2->fpr&& lang && words &&
  4472                 wsize))
  4473         return PEP_ILLEGAL_VALUE;
  4474 
  4475     return get_trustwords_for_fprs(session, id1->fpr, id2->fpr, lang, words,
  4476             wsize, full);
  4477 }
  4478 
  4479 DYNAMIC_API PEP_STATUS get_trustwords_for_fprs(
  4480         PEP_SESSION session, const char* fpr1, const char* fpr2,
  4481         const char* lang, char **words, size_t *wsize, bool full
  4482     )
  4483 {
  4484     assert(session && fpr1 && fpr2 && words && wsize);
  4485     if (!(session && fpr1 && fpr2 && words && wsize))
  4486         return PEP_ILLEGAL_VALUE;
  4487 
  4488     const int SHORT_NUM_TWORDS = 5; 
  4489     PEP_STATUS status = PEP_STATUS_OK;
  4490     
  4491     *words = NULL;    
  4492     *wsize = 0;
  4493 
  4494     int fpr1_len = strlen(fpr1);
  4495     int fpr2_len = strlen(fpr2);
  4496         
  4497     int max_len = (fpr1_len > fpr2_len ? fpr1_len : fpr2_len);
  4498     
  4499     char* XORed_fpr = (char*)(calloc(max_len + 1, 1));
  4500     *(XORed_fpr + max_len) = '\0';
  4501     char* result_curr = XORed_fpr + max_len - 1;
  4502     const char* fpr1_curr = fpr1 + fpr1_len - 1;
  4503     const char* fpr2_curr = fpr2 + fpr2_len - 1;
  4504 
  4505     while (fpr1 <= fpr1_curr && fpr2 <= fpr2_curr) {
  4506         fpr1_curr = skip_separators(fpr1_curr, fpr1);
  4507         fpr2_curr = skip_separators(fpr2_curr, fpr2);
  4508         
  4509         if (fpr1_curr < fpr1 || fpr2_curr < fpr2)
  4510             break;
  4511             
  4512         char xor_hex = xor_hex_chars(*fpr1_curr, *fpr2_curr);
  4513         if (xor_hex == '\0') {
  4514             status = PEP_ILLEGAL_VALUE;
  4515             goto error_release;
  4516         }
  4517         
  4518         *result_curr = xor_hex;
  4519         result_curr--; fpr1_curr--; fpr2_curr--;
  4520     }
  4521 
  4522     const char* remainder_start = NULL;
  4523     const char* remainder_curr = NULL;
  4524     
  4525     if (fpr1 <= fpr1_curr) {
  4526         remainder_start = fpr1;
  4527         remainder_curr = fpr1_curr;
  4528     }
  4529     else if (fpr2 <= fpr2_curr) {
  4530         remainder_start = fpr2;
  4531         remainder_curr = fpr2_curr;
  4532     }
  4533     if (remainder_curr) {
  4534         while (remainder_start <= remainder_curr) {
  4535             remainder_curr = skip_separators(remainder_curr, remainder_start);
  4536             
  4537             if (remainder_curr < remainder_start)
  4538                 break;
  4539             
  4540             char the_char = *remainder_curr;
  4541             
  4542             if (asciihex_to_num(the_char) < 0) {
  4543                 status = PEP_ILLEGAL_VALUE;
  4544                 goto error_release;
  4545             }
  4546             
  4547             *result_curr = the_char;                
  4548             result_curr--;
  4549             remainder_curr--;
  4550         }
  4551     }
  4552     
  4553     result_curr++;
  4554 
  4555     if (result_curr > XORed_fpr) {
  4556         char* tempstr = strdup(result_curr);
  4557         free(XORed_fpr);
  4558         XORed_fpr = tempstr;
  4559     }
  4560     
  4561     status = check_for_zero_fpr(XORed_fpr);
  4562     
  4563     if (status != PEP_STATUS_OK)
  4564         goto error_release;
  4565     
  4566     size_t max_words_per_id = (full ? 0 : SHORT_NUM_TWORDS);
  4567 
  4568     char* the_words = NULL;
  4569     size_t the_size = 0;
  4570 
  4571     status = trustwords(session, XORed_fpr, lang, &the_words, &the_size, max_words_per_id);
  4572     if (status != PEP_STATUS_OK)
  4573         goto error_release;
  4574 
  4575     *words = the_words;
  4576     *wsize = the_size;
  4577     
  4578     status = PEP_STATUS_OK;
  4579 
  4580     goto the_end;
  4581 
  4582     error_release:
  4583         free (XORed_fpr);
  4584         
  4585     the_end:
  4586     return status;
  4587 }
  4588 
  4589 DYNAMIC_API PEP_STATUS get_message_trustwords(
  4590     PEP_SESSION session, 
  4591     message *msg,
  4592     stringlist_t *keylist,
  4593     pEp_identity* received_by,
  4594     const char* lang, char **words, bool full
  4595 )
  4596 {
  4597     assert(session);
  4598     assert(msg);
  4599     assert(received_by);
  4600     assert(received_by->address);
  4601     assert(lang);
  4602     assert(words);
  4603 
  4604     if (!(session && 
  4605           msg &&
  4606           received_by && 
  4607           received_by->address && 
  4608           lang && 
  4609           words))
  4610         return PEP_ILLEGAL_VALUE;
  4611     
  4612     pEp_identity* partner = NULL;
  4613      
  4614     PEP_STATUS status = PEP_STATUS_OK;
  4615     
  4616     *words = NULL;
  4617 
  4618     // We want fingerprint of key that did sign the message
  4619 
  4620     if (keylist == NULL) {
  4621 
  4622         // Message is to be decrypted
  4623         message *dst = NULL;
  4624         stringlist_t *_keylist = keylist;
  4625         PEP_rating rating;
  4626         PEP_decrypt_flags_t flags;
  4627         status = decrypt_message( session, msg, &dst, &_keylist, &rating, &flags);
  4628 
  4629         if (status != PEP_STATUS_OK) {
  4630             free_message(dst);
  4631             free_stringlist(_keylist);
  4632             return status;
  4633         }
  4634 
  4635         if (dst && dst->from && _keylist) {
  4636             partner = identity_dup(dst->from); 
  4637             if(partner){
  4638                 free(partner->fpr);
  4639                 partner->fpr = strdup(_keylist->value);
  4640                 if (partner->fpr == NULL)
  4641                     status = PEP_OUT_OF_MEMORY;
  4642             } else {
  4643                 status = PEP_OUT_OF_MEMORY;
  4644             }
  4645         } else {
  4646             status = PEP_UNKNOWN_ERROR;
  4647         }
  4648 
  4649         free_message(dst);
  4650         free_stringlist(_keylist);
  4651 
  4652     } else {
  4653 
  4654         // Message already decrypted
  4655         if (keylist->value) {
  4656             partner = identity_dup(msg->from); 
  4657             if(partner){
  4658                 free(partner->fpr);
  4659                 partner->fpr = strdup(keylist->value);
  4660                 if (partner->fpr == NULL)
  4661                     status = PEP_OUT_OF_MEMORY;
  4662             } else {
  4663                 status = PEP_OUT_OF_MEMORY;
  4664             }
  4665         } else {
  4666             status = PEP_ILLEGAL_VALUE;
  4667         }
  4668     }
  4669 
  4670     if (status != PEP_STATUS_OK) {
  4671         free_identity(partner);
  4672         return status;
  4673     }
  4674    
  4675     // Find own identity corresponding to given account address.
  4676     // In that case we want default key attached to own identity
  4677     pEp_identity *stored_identity = NULL;
  4678     
  4679     char* own_id = NULL;
  4680     status = get_default_own_userid(session, &own_id);
  4681 
  4682     if (!(status == PEP_STATUS_OK && own_id)) {
  4683         free(own_id);
  4684         return PEP_CANNOT_FIND_IDENTITY;
  4685     }
  4686     
  4687     status = get_identity(session,
  4688                           received_by->address,
  4689                           own_id,
  4690                           &stored_identity);
  4691     free(own_id);
  4692     own_id = NULL;                      
  4693 
  4694     if (status != PEP_STATUS_OK) {
  4695         free_identity(stored_identity);
  4696         return status;
  4697     }
  4698 
  4699     // get the trustwords
  4700     size_t wsize;
  4701     status = get_trustwords(session, 
  4702                             partner, received_by, 
  4703                             lang, words, &wsize, full);
  4704 
  4705     return status;
  4706 }
  4707 
  4708 static PEP_rating string_to_rating(const char * rating)
  4709 {
  4710     if (rating == NULL)
  4711         return PEP_rating_undefined;
  4712     if (strcmp(rating, "cannot_decrypt") == 0)
  4713         return PEP_rating_cannot_decrypt;
  4714     if (strcmp(rating, "have_no_key") == 0)
  4715         return PEP_rating_have_no_key;
  4716     if (strcmp(rating, "unencrypted") == 0)
  4717         return PEP_rating_unencrypted;
  4718     if (strcmp(rating, "unencrypted_for_some") == 0)
  4719         return PEP_rating_undefined; // don't use this any more
  4720     if (strcmp(rating, "unreliable") == 0)
  4721         return PEP_rating_unreliable;
  4722     if (strcmp(rating, "reliable") == 0)
  4723         return PEP_rating_reliable;
  4724     if (strcmp(rating, "trusted") == 0)
  4725         return PEP_rating_trusted;
  4726     if (strcmp(rating, "trusted_and_anonymized") == 0)
  4727         return PEP_rating_trusted_and_anonymized;
  4728     if (strcmp(rating, "fully_anonymous") == 0)
  4729         return PEP_rating_fully_anonymous;
  4730     if (strcmp(rating, "mistrust") == 0)
  4731         return PEP_rating_mistrust;
  4732     if (strcmp(rating, "b0rken") == 0)
  4733         return PEP_rating_b0rken;
  4734     if (strcmp(rating, "under_attack") == 0)
  4735         return PEP_rating_under_attack;
  4736     return PEP_rating_undefined;
  4737 }
  4738 
  4739 static PEP_STATUS string_to_keylist(const char * skeylist, stringlist_t **keylist)
  4740 {
  4741     if (skeylist == NULL || keylist == NULL)
  4742         return PEP_ILLEGAL_VALUE;
  4743 
  4744     stringlist_t *rkeylist = NULL;
  4745     stringlist_t *_kcurr = NULL;
  4746     const char * fpr_begin = skeylist;
  4747     const char * fpr_end = NULL;
  4748 
  4749     do {
  4750         fpr_end = strstr(fpr_begin, ",");
  4751         
  4752         char * fpr = strndup(
  4753             fpr_begin,
  4754             (fpr_end == NULL) ? strlen(fpr_begin) : fpr_end - fpr_begin);
  4755         
  4756         if (fpr == NULL)
  4757             goto enomem;
  4758         
  4759         _kcurr = stringlist_add(_kcurr, fpr);
  4760         if (_kcurr == NULL) {
  4761             free(fpr);
  4762             goto enomem;
  4763         }
  4764         
  4765         if (rkeylist == NULL)
  4766             rkeylist = _kcurr;
  4767         
  4768         fpr_begin = fpr_end ? fpr_end + 1 : NULL;
  4769         
  4770     } while (fpr_begin);
  4771     
  4772     *keylist = rkeylist;
  4773     return PEP_STATUS_OK;
  4774     
  4775 enomem:
  4776     free_stringlist(rkeylist);
  4777     return PEP_OUT_OF_MEMORY;
  4778 }
  4779 
  4780 DYNAMIC_API PEP_STATUS re_evaluate_message_rating(
  4781     PEP_SESSION session,
  4782     message *msg,
  4783     stringlist_t *x_keylist,
  4784     PEP_rating x_enc_status,
  4785     PEP_rating *rating
  4786 )
  4787 {
  4788     PEP_STATUS status = PEP_STATUS_OK;
  4789     stringlist_t *_keylist = x_keylist;
  4790     bool must_free_keylist = false;
  4791     PEP_rating _rating;
  4792 
  4793     assert(session);
  4794     assert(msg);
  4795     assert(rating);
  4796 
  4797     if (!(session && msg && rating))
  4798         return PEP_ILLEGAL_VALUE;
  4799 
  4800     *rating = PEP_rating_undefined;
  4801 
  4802     if (x_enc_status == PEP_rating_undefined){
  4803         for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  4804             if (strcasecmp(i->value->key, "X-EncStatus") == 0){
  4805                 x_enc_status = string_to_rating(i->value->value);
  4806                 goto got_rating;
  4807             }
  4808         }
  4809         return PEP_ILLEGAL_VALUE;
  4810     }
  4811 
  4812 got_rating:
  4813 
  4814     _rating = x_enc_status;
  4815 
  4816     if (_keylist == NULL){
  4817         for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  4818             if (strcasecmp(i->value->key, "X-KeyList") == 0){
  4819                 status = string_to_keylist(i->value->value, &_keylist);
  4820                 if (status != PEP_STATUS_OK)
  4821                     goto pEp_error;
  4822                 must_free_keylist = true;
  4823                 goto got_keylist;
  4824             }
  4825         }
  4826 
  4827         // there was no rcpt fpr, it could be an unencrypted mail
  4828         if(_rating == PEP_rating_unencrypted) {
  4829             *rating = _rating;
  4830             return PEP_STATUS_OK;
  4831         }
  4832 
  4833         return PEP_ILLEGAL_VALUE;
  4834     }
  4835 got_keylist:
  4836 
  4837     if (!is_me(session, msg->from))
  4838         status = update_identity(session, msg->from);
  4839     else
  4840         status = _myself(session, msg->from, false, false, true);
  4841 
  4842     switch (status) {
  4843         case PEP_KEY_NOT_FOUND:
  4844         case PEP_KEY_UNSUITABLE:
  4845         case PEP_KEY_BLACKLISTED:
  4846         case PEP_CANNOT_FIND_IDENTITY:
  4847         case PEP_CANNOT_FIND_ALIAS:
  4848             status = PEP_STATUS_OK;
  4849         case PEP_STATUS_OK:
  4850             break;
  4851         default:
  4852             goto pEp_error;
  4853     }
  4854 
  4855     status = amend_rating_according_to_sender_and_recipients(session, &_rating,
  4856             msg->from, _keylist);
  4857     if (status == PEP_STATUS_OK)
  4858         *rating = _rating;
  4859     
  4860 pEp_error:
  4861     if (must_free_keylist)
  4862         free_stringlist(_keylist);
  4863 
  4864     return status;
  4865 }
  4866 
  4867 DYNAMIC_API PEP_STATUS get_key_rating_for_user(
  4868         PEP_SESSION session,
  4869         const char *user_id,
  4870         const char *fpr,
  4871         PEP_rating *rating
  4872     )
  4873 {
  4874     assert(session && user_id && user_id[0] && fpr && fpr[0] && rating);
  4875     if (!(session && user_id && user_id[0] && fpr && fpr[0] && rating))
  4876         return PEP_ILLEGAL_VALUE;
  4877 
  4878     *rating = PEP_rating_undefined;
  4879 
  4880     pEp_identity *ident = new_identity(NULL, fpr, user_id, NULL);
  4881     if (!ident)
  4882         return PEP_OUT_OF_MEMORY;
  4883 
  4884     PEP_STATUS status = get_trust(session, ident);
  4885     if (status)
  4886         goto the_end;
  4887 
  4888     if (!ident->comm_type) {
  4889         status = PEP_RECORD_NOT_FOUND;
  4890         goto the_end;
  4891     }
  4892 
  4893     *rating = _rating(ident->comm_type);
  4894 
  4895 the_end:
  4896     free_identity(ident);
  4897     return status;
  4898 }