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