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