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