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