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