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