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