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