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