src/message_api.c
author us@chu.huenfield.org
Tue, 25 Dec 2018 14:46:45 +0100
branchsync
changeset 3209 c15b4ca2b52a
parent 3184 557a689068aa
child 3243 09257addf431
permissions -rw-r--r--
Replace use of Sequoia's backend with a custom key store.

- Sequoia's key store doesn't meet pep's needs (in particular, the
ability to search on a key's user id) and trying to shoehorn pep's
needs onto Sequoia's key store abstractions is just introducing
overhead with no appreciable gain in functionality.

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