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