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