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