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