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