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