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