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