src/message_api.c
author Volker Birk <vb@pep-project.org>
Tue, 12 Jul 2016 20:24:58 +0200
changeset 855 27364c55fbaa
parent 854 0d068c87fa66
child 857 349cbf352179
permissions -rw-r--r--
semantically more clever
     1 #include "pEp_internal.h"
     2 #include "message_api.h"
     3 
     4 #include "platform.h"
     5 #include "mime.h"
     6 
     7 #include <assert.h>
     8 #include <string.h>
     9 #include <stdlib.h>
    10 
    11 #ifndef MIN
    12 #define MIN(A, B) ((B) > (A) ? (A) : (B))
    13 #endif
    14 #ifndef MAX
    15 #define MAX(A, B) ((B) > (A) ? (B) : (A))
    16 #endif
    17 
    18 
    19 static bool string_equality(const char *s1, const char *s2)
    20 {
    21     if (s1 == NULL || s2 == NULL)
    22         return false;
    23 
    24     assert(s1 && s2);
    25 
    26     return strcmp(s1, s2) == 0;
    27 }
    28 
    29 static bool is_mime_type(const bloblist_t *bl, const char *mt)
    30 {
    31     assert(mt);
    32 
    33     return bl && string_equality(bl->mime_type, mt);
    34 }
    35 
    36 //
    37 // This function presumes the file ending is a proper substring of the
    38 // filename (i.e. if bl->filename is "a.pgp" and fe is ".pgp", it will
    39 // return true, but if bl->filename is ".pgp" and fe is ".pgp", it will
    40 // return false. This is desired behaviour.
    41 //
    42 static bool is_fileending(const bloblist_t *bl, const char *fe)
    43 {
    44     assert(fe);
    45     
    46     if (bl == NULL || bl->filename == NULL || fe == NULL)
    47         return false;
    48 
    49     assert(bl && bl->filename);
    50 
    51     size_t fe_len = strlen(fe);
    52     size_t fn_len = strlen(bl->filename);
    53 
    54     if (fn_len <= fe_len)
    55         return false;
    56 
    57     assert(fn_len > fe_len);
    58 
    59     return strcmp(bl->filename + (fn_len - fe_len), fe) == 0;
    60 }
    61 
    62 static void add_opt_field(message *msg, const char *name, const char *value)
    63 {
    64     assert(msg);
    65 
    66     if (msg && name && value) {
    67         stringpair_t *pair = new_stringpair(name, value);
    68         if (pair == NULL)
    69             return;
    70 
    71         stringpair_list_t *field = stringpair_list_add(msg->opt_fields, pair);
    72         if (field == NULL)
    73         {
    74             free_stringpair(pair);
    75             return;
    76         }
    77 
    78         if (msg->opt_fields == NULL)
    79             msg->opt_fields = field;
    80     }
    81 }
    82 
    83 static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
    84 {
    85     char * ptext;
    86 
    87     assert(shortmsg);
    88     assert(strcmp(shortmsg, "pEp") != 0);
    89 
    90     if (!shortmsg || strcmp(shortmsg, "pEp") == 0)
    91         return longmsg;
    92         
    93     if (longmsg == NULL)
    94         longmsg = "";
    95 
    96     ptext = calloc(1, strlen(shortmsg) + strlen(longmsg) + 12);
    97     assert(ptext);
    98     if (ptext == NULL)
    99         return NULL;
   100 
   101     strcpy(ptext, "Subject: ");
   102     strcat(ptext, shortmsg);
   103     strcat(ptext, "\n\n");
   104     strcat(ptext, longmsg);
   105 
   106     return ptext;
   107 }
   108 
   109 static int separate_short_and_long(const char *src, char **shortmsg, char **longmsg)
   110 {
   111     char *_shortmsg = NULL;
   112     char *_longmsg = NULL;
   113 
   114     assert(src);
   115     assert(shortmsg);
   116     assert(longmsg);
   117     
   118     if (src == NULL || shortmsg == NULL || longmsg == NULL)
   119         return -1;
   120 
   121     *shortmsg = NULL;
   122     *longmsg = NULL;
   123 
   124     if (strncasecmp(src, "subject: ", 9) == 0) {
   125         char *line_end = strchr(src, '\n');
   126 
   127         if (line_end == NULL) {
   128             _shortmsg = strdup(src + 9);
   129             assert(_shortmsg);
   130             if (_shortmsg == NULL)
   131                 goto enomem;
   132             // _longmsg = NULL;
   133         }
   134         else {
   135             size_t n = line_end - src;
   136 
   137             if (*(line_end - 1) == '\r')
   138                 _shortmsg = strndup(src + 9, n - 10);
   139             else
   140                 _shortmsg = strndup(src + 9, n - 9);
   141             assert(_shortmsg);
   142             if (_shortmsg == NULL)
   143                 goto enomem;
   144 
   145             while (*(src + n) && (*(src + n) == '\n' || *(src + n) == '\r'))
   146                 ++n;
   147 
   148             if (*(src + n)) {
   149                 _longmsg = strdup(src + n);
   150                 assert(_longmsg);
   151                 if (_longmsg == NULL)
   152                     goto enomem;
   153             }
   154         }
   155     }
   156     else {
   157         _shortmsg = strdup("");
   158         assert(_shortmsg);
   159         if (_shortmsg == NULL)
   160             goto enomem;
   161         _longmsg = strdup(src);
   162         assert(_longmsg);
   163         if (_longmsg == NULL)
   164             goto enomem;
   165     }
   166 
   167     *shortmsg = _shortmsg;
   168     *longmsg = _longmsg;
   169 
   170     return 0;
   171 
   172 enomem:
   173     free(_shortmsg);
   174     free(_longmsg);
   175 
   176     return -1;
   177 }
   178 
   179 static PEP_STATUS copy_fields(message *dst, const message *src)
   180 {
   181     assert(dst);
   182     assert(src);
   183 
   184     if(!(dst && src))
   185         return PEP_ILLEGAL_VALUE;
   186 
   187     free_timestamp(dst->sent);
   188     dst->sent = NULL;
   189     if (src->sent) {
   190         dst->sent = timestamp_dup(src->sent);
   191         if (dst->sent == NULL)
   192             return PEP_OUT_OF_MEMORY;
   193     }
   194 
   195     free_timestamp(dst->recv);
   196     dst->recv = NULL;
   197     if (src->recv) {
   198         dst->recv = timestamp_dup(src->recv);
   199         if (dst->recv == NULL)
   200             return PEP_OUT_OF_MEMORY;
   201     }
   202 
   203     free_identity(dst->from);
   204     dst->from = NULL;
   205     if (src->from) {
   206         dst->from = identity_dup(src->from);
   207         if (dst->from == NULL)
   208             return PEP_OUT_OF_MEMORY;
   209     }
   210 
   211     free_identity_list(dst->to);
   212     dst->to = NULL;
   213     if (src->to && src->to->ident) {
   214         dst->to = identity_list_dup(src->to);
   215         if (dst->to == NULL)
   216             return PEP_OUT_OF_MEMORY;
   217     }
   218 
   219     free_identity(dst->recv_by);
   220     dst->recv_by = NULL;
   221     if (src->recv_by) {
   222         dst->recv_by = identity_dup(src->recv_by);
   223         if (dst->recv_by == NULL)
   224             return PEP_OUT_OF_MEMORY;
   225     }
   226 
   227     free_identity_list(dst->cc);
   228     dst->cc = NULL;
   229     if (src->cc && src->cc->ident) {
   230         dst->cc = identity_list_dup(src->cc);
   231         if (dst->cc == NULL)
   232             return PEP_OUT_OF_MEMORY;
   233     }
   234 
   235     free_identity_list(dst->bcc);
   236     dst->bcc = NULL;
   237     if (src->bcc && src->bcc->ident) {
   238         dst->bcc = identity_list_dup(src->bcc);
   239         if (dst->bcc == NULL)
   240             return PEP_OUT_OF_MEMORY;
   241     }
   242 
   243     free_identity_list(dst->reply_to);
   244     dst->reply_to = NULL;
   245     if (src->reply_to && src->reply_to->ident) {
   246         dst->reply_to = identity_list_dup(src->reply_to);
   247         if (dst->reply_to == NULL)
   248             return PEP_OUT_OF_MEMORY;
   249     }
   250 
   251     free_stringlist(dst->in_reply_to);
   252     dst->in_reply_to = NULL;
   253     if (src->in_reply_to && src->in_reply_to->value) {
   254         dst->in_reply_to = stringlist_dup(src->in_reply_to);
   255         if (dst->in_reply_to == NULL)
   256             return PEP_OUT_OF_MEMORY;
   257     }
   258 
   259     free_stringlist(dst->references);
   260     dst->references = NULL;
   261     if (src->references) {
   262         dst->references = stringlist_dup(src->references);
   263         if (dst->references == NULL)
   264             return PEP_OUT_OF_MEMORY;
   265     }
   266 
   267     free_stringlist(dst->keywords);
   268     dst->keywords = NULL;
   269     if (src->keywords && src->keywords->value) {
   270         dst->keywords = stringlist_dup(src->keywords);
   271         if (dst->keywords == NULL)
   272             return PEP_OUT_OF_MEMORY;
   273     }
   274 
   275     free(dst->comments);
   276     dst->comments = NULL;
   277     if (src->comments) {
   278         dst->comments = strdup(src->comments);
   279         assert(dst->comments);
   280         if (dst->comments == NULL)
   281             return PEP_OUT_OF_MEMORY;
   282     }
   283 
   284     return PEP_STATUS_OK;
   285 }
   286 
   287 static message * clone_to_empty_message(const message * src)
   288 {
   289     PEP_STATUS status;
   290     message * msg = NULL;
   291 
   292     assert(src);
   293     if (src == NULL)
   294         return NULL;
   295 
   296     msg = calloc(1, sizeof(message));
   297     assert(msg);
   298     if (msg == NULL)
   299         goto enomem;
   300 
   301     msg->dir = src->dir;
   302 
   303     status = copy_fields(msg, src);
   304     if (status != PEP_STATUS_OK)
   305         goto enomem;
   306 
   307     return msg;
   308 
   309 enomem:
   310     free_message(msg);
   311     return NULL;
   312 }
   313 
   314 static PEP_STATUS encrypt_PGP_MIME(
   315     PEP_SESSION session,
   316     const message *src,
   317     stringlist_t *keys,
   318     message *dst
   319     )
   320 {
   321     PEP_STATUS status = PEP_STATUS_OK;
   322     bool free_ptext = false;
   323     char *ptext = NULL;
   324     char *ctext;
   325     char *_ctext = NULL;
   326     char *mimetext = NULL;
   327     size_t csize;
   328     assert(dst->longmsg == NULL);
   329     dst->enc_format = PEP_enc_PGP_MIME;
   330 
   331     if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
   332         if (session->unencrypted_subject) {
   333             dst->shortmsg = strdup(src->shortmsg);
   334             assert(dst->shortmsg);
   335             if (dst->shortmsg == NULL)
   336                 goto enomem;
   337             ptext = src->longmsg;
   338         }
   339         else {
   340             ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   341             if (ptext == NULL)
   342                 goto enomem;
   343             free_ptext = true;
   344         }
   345     }
   346     else if (src->longmsg) {
   347         ptext = src->longmsg;
   348     }
   349     else {
   350         ptext = "pEp";
   351     }
   352 
   353     message *_src = calloc(1, sizeof(message));
   354     assert(_src);
   355     if (_src == NULL)
   356         goto enomem;
   357     _src->longmsg = ptext;
   358     _src->longmsg_formatted = src->longmsg_formatted;
   359     _src->attachments = src->attachments;
   360     _src->enc_format = PEP_enc_none;
   361     status = mime_encode_message(_src, true, &mimetext);
   362     assert(status == PEP_STATUS_OK);
   363     if (status != PEP_STATUS_OK)
   364         goto pep_error;
   365     
   366     if (free_ptext){
   367         free(ptext);
   368         free_ptext=0;
   369     }
   370     free(_src);
   371     assert(mimetext);
   372     if (mimetext == NULL)
   373         goto pep_error;
   374 
   375     status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
   376         &ctext, &csize);
   377     free(mimetext);
   378     if (ctext == NULL)
   379         goto pep_error;
   380 
   381     dst->longmsg = strdup("this message was encrypted with p≡p "
   382         "https://pEp-project.org");
   383     assert(dst->longmsg);
   384     if (dst->longmsg == NULL)
   385         goto enomem;
   386 
   387     char *v = strdup("Version: 1");
   388     assert(v);
   389     if (v == NULL)
   390         goto enomem;
   391 
   392     bloblist_t *_a = new_bloblist(v, strlen(v), "application/pgp-encrypted", NULL);
   393     if (_a == NULL)
   394         goto enomem;
   395     dst->attachments = _a;
   396 
   397     _ctext = malloc(csize);
   398     assert(_ctext);
   399     if (_ctext == NULL)
   400         goto enomem;
   401     memcpy(_ctext, ctext, csize);
   402 
   403     _a = bloblist_add(_a, _ctext, csize, "application/octet-stream",
   404         "msg.asc");
   405     if (_a == NULL)
   406         goto enomem;
   407 
   408     return PEP_STATUS_OK;
   409 
   410 enomem:
   411     status = PEP_OUT_OF_MEMORY;
   412 
   413 pep_error:
   414     if (free_ptext)
   415         free(ptext);
   416     free(_ctext);
   417     return status;
   418 }
   419 
   420 static PEP_STATUS encrypt_PGP_in_pieces(
   421     PEP_SESSION session,
   422     const message *src,
   423     stringlist_t *keys,
   424     message *dst
   425     )
   426 {
   427     PEP_STATUS status = PEP_STATUS_OK;
   428     char *ctext;
   429     size_t csize;
   430     char *ptext = NULL;
   431     bool free_ptext = false;
   432 
   433     assert(dst->longmsg == NULL);
   434     assert(dst->attachments == NULL);
   435 
   436     dst->enc_format = PEP_enc_pieces;
   437 
   438     if (src->shortmsg && src->shortmsg[0] && strcmp(src->shortmsg, "pEp") != 0) {
   439         if (session->unencrypted_subject) {
   440             dst->shortmsg = strdup(src->shortmsg);
   441             assert(dst->shortmsg);
   442             if (dst->shortmsg == NULL)
   443                 goto enomem;
   444             ptext = src->longmsg;
   445         }
   446         else {
   447             ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   448             if (ptext == NULL)
   449                 goto enomem;
   450             free_ptext = true;
   451         }
   452 
   453         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   454             &csize);
   455         if (free_ptext)
   456             free(ptext);
   457         free_ptext = false;
   458         if (ctext) {
   459             dst->longmsg = strndup(ctext, csize);
   460             assert(dst->longmsg);
   461             if (dst->longmsg == NULL)
   462                 goto enomem;
   463         }
   464         else {
   465             goto pep_error;
   466         }
   467     }
   468     else if (src->longmsg && src->longmsg[0]) {
   469         ptext = src->longmsg;
   470         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   471             &csize);
   472         if (ctext) {
   473             dst->longmsg = strndup(ctext, csize);
   474             assert(dst->longmsg);
   475             if (dst->longmsg == NULL)
   476                 goto enomem;
   477         }
   478         else {
   479             goto pep_error;
   480         }
   481     }
   482     else {
   483         dst->longmsg = strdup("");
   484         assert(dst->longmsg);
   485         if (dst->longmsg == NULL)
   486             goto enomem;
   487     }
   488 
   489     if (src->longmsg_formatted && src->longmsg_formatted[0]) {
   490         ptext = src->longmsg_formatted;
   491         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   492             &csize);
   493         if (ctext) {
   494             char *_ctext = malloc(csize);
   495             assert(_ctext);
   496             if (_ctext == NULL)
   497                 goto enomem;
   498             memcpy(_ctext, ctext, csize);
   499 
   500             bloblist_t *_a = bloblist_add(dst->attachments, _ctext, csize,
   501                 "application/octet-stream", "PGPexch.htm.pgp");
   502             if (_a == NULL)
   503                 goto enomem;
   504             if (dst->attachments == NULL)
   505                 dst->attachments = _a;
   506         }
   507         else {
   508             goto pep_error;
   509         }
   510     }
   511 
   512     if (src->attachments) {
   513         if (dst->attachments == NULL) {
   514             dst->attachments = new_bloblist(NULL, 0, NULL, NULL);
   515             if (dst->attachments == NULL)
   516                 goto enomem;
   517         }
   518 
   519         bloblist_t *_s = src->attachments;
   520         bloblist_t *_d = dst->attachments;
   521 
   522         for (int n = 0; _s; _s = _s->next) {
   523             if (_s->value == NULL && _s->size == 0) {
   524                 _d = bloblist_add(_d, NULL, 0, _s->mime_type, _s->filename);
   525                 if (_d == NULL)
   526                     goto enomem;
   527             }
   528             else {
   529                 size_t psize = _s->size;
   530                 ptext = _s->value;
   531                 status = encrypt_and_sign(session, keys, ptext, psize, &ctext,
   532                     &csize);
   533                 if (ctext) {
   534                     char *filename = NULL;
   535 
   536                     if (_s->filename) {
   537                         size_t len = strlen(_s->filename);
   538                         filename = calloc(1, len + 5);
   539                         if (filename == NULL)
   540                             goto enomem;
   541 
   542                         strcpy(filename, _s->filename);
   543                         strcpy(filename + len, ".pgp");
   544                     }
   545                     else {
   546                         filename = calloc(1, 20);
   547                         if (filename == NULL)
   548                             goto enomem;
   549 
   550                         ++n;
   551                         n &= 0xffff;
   552                         snprintf(filename, 20, "Attachment%d.pgp", n);
   553                     }
   554 
   555                     char *_ctext = malloc(csize);
   556                     assert(_ctext);
   557                     if (_ctext == NULL)
   558                         goto enomem;
   559                     memcpy(_ctext, ctext, csize);
   560 
   561                     _d = bloblist_add(_d, _ctext, csize, "application/octet-stream",
   562                         filename);
   563                     if (_d == NULL)
   564                         goto enomem;
   565                 }
   566                 else {
   567                     goto pep_error;
   568                 }
   569             }
   570         }
   571     }
   572 
   573     return PEP_STATUS_OK;
   574 
   575 enomem:
   576     status = PEP_OUT_OF_MEMORY;
   577 
   578 pep_error:
   579     if (free_ptext)
   580         free(ptext);
   581     return status;
   582 }
   583 
   584 static char * keylist_to_string(const stringlist_t *keylist)
   585 {
   586     if (keylist) {
   587         size_t size = stringlist_length(keylist);
   588 
   589         const stringlist_t *_kl;
   590         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   591             size += strlen(_kl->value);
   592         }
   593 
   594         char *result = calloc(1, size);
   595         if (result == NULL)
   596             return NULL;
   597 
   598         char *_r = result;
   599         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   600             _r = stpcpy(_r, _kl->value);
   601             if (_kl->next && _kl->next->value)
   602                 _r = stpcpy(_r, ",");
   603         }
   604 
   605         return result;
   606     }
   607     else {
   608         return NULL;
   609     }
   610 }
   611 
   612 static const char * color_to_string(PEP_color color)
   613 {
   614     switch (color) {
   615     case PEP_rating_cannot_decrypt:
   616         return "cannot_decrypt";
   617     case PEP_rating_have_no_key:
   618         return "have_no_key";
   619     case PEP_rating_unencrypted:
   620         return "unencrypted";
   621     case PEP_rating_unencrypted_for_some:
   622         return "unencrypted_for_some";
   623     case PEP_rating_unreliable:
   624         return "unreliable";
   625     case PEP_rating_reliable:
   626         return "reliable";
   627     case PEP_rating_trusted:
   628         return "trusted";
   629     case PEP_rating_trusted_and_anonymized:
   630         return "trusted_and_anonymized";
   631     case PEP_rating_fully_anonymous:
   632         return "fully_anonymous";
   633     case PEP_rating_mistrust:
   634         return "mistrust";
   635     case PEP_rating_b0rken:
   636         return "b0rken";
   637     case PEP_rating_under_attack:
   638         return "unter_attack";
   639     default:
   640         return "undefined";
   641     }
   642 }
   643 
   644 static void decorate_message(
   645     message *msg,
   646     PEP_color color,
   647     stringlist_t *keylist
   648     )
   649 {
   650     assert(msg);
   651 
   652     add_opt_field(msg, "X-pEp-Version", "1.0");
   653     
   654     if (color != PEP_rating_undefined)
   655         add_opt_field(msg, "X-EncStatus", color_to_string(color));
   656 
   657     if (keylist) {
   658         char *_keylist = keylist_to_string(keylist);
   659         add_opt_field(msg, "X-KeyList", _keylist);
   660         free(_keylist);
   661     }
   662 }
   663 
   664 static PEP_color _rating(PEP_comm_type ct, PEP_color color)
   665 {
   666     if (ct == PEP_ct_unknown)
   667         return PEP_rating_undefined;
   668 
   669     else if (ct == PEP_ct_compromized)
   670         return PEP_rating_under_attack;
   671 
   672     else if (ct == PEP_ct_mistrusted)
   673         return PEP_rating_mistrust;
   674     
   675     if (color == PEP_rating_unencrypted_for_some)
   676         return PEP_rating_unencrypted_for_some;
   677 
   678     if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel ||
   679             ct == PEP_ct_my_key_not_included) {
   680         if (color > PEP_rating_unencrypted_for_some)
   681             return PEP_rating_unencrypted_for_some;
   682         else
   683             return PEP_rating_unencrypted;
   684     }
   685 
   686     if (color == PEP_rating_unencrypted)
   687         return PEP_rating_unencrypted_for_some;
   688 
   689     if (ct >= PEP_ct_confirmed_enc_anon)
   690         return PEP_rating_trusted_and_anonymized;
   691 
   692     else if (ct >= PEP_ct_strong_encryption)
   693         return PEP_rating_trusted;
   694 
   695     else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
   696         return PEP_rating_reliable;
   697 
   698     else
   699         return PEP_rating_unreliable;
   700 }
   701 
   702 static bool is_encrypted_attachment(const bloblist_t *blob)
   703 {
   704     char *ext;
   705 
   706     assert(blob);
   707 
   708     if (blob == NULL || blob->filename == NULL)
   709         return false;
   710     
   711     ext = strrchr(blob->filename, '.');
   712     if (ext == NULL)
   713         return false;
   714 
   715     if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
   716         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
   717             strcmp(ext, ".asc") == 0)
   718             return true;
   719     }
   720     else if (strcmp(blob->mime_type, "text/plain") == 0) {
   721         if (strcmp(ext, ".asc") == 0)
   722             return true;
   723     }
   724 
   725     return false;
   726 }
   727 
   728 static bool is_encrypted_html_attachment(const bloblist_t *blob)
   729 {
   730     assert(blob);
   731     assert(blob->filename);
   732     if (blob == NULL || blob->filename == NULL)
   733         return false;
   734 
   735     if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
   736         if (strcmp(blob->filename + 11, ".pgp") == 0 ||
   737             strcmp(blob->filename + 11, ".asc") == 0)
   738             return true;
   739     }
   740 
   741     return false;
   742 }
   743 
   744 static char * without_double_ending(const char *filename)
   745 {
   746     char *ext;
   747 
   748     assert(filename);
   749     if (filename == NULL)
   750         return NULL;
   751     
   752     ext = strrchr(filename, '.');
   753     if (ext == NULL)
   754         return NULL;
   755 
   756     char *result = strndup(filename, ext - filename);
   757     assert(result);
   758     return result;
   759 }
   760 
   761 static PEP_color decrypt_color(PEP_STATUS status)
   762 {
   763     switch (status) {
   764     case PEP_UNENCRYPTED:
   765     case PEP_VERIFIED:
   766     case PEP_VERIFY_NO_KEY:
   767     case PEP_VERIFIED_AND_TRUSTED:
   768         return PEP_rating_unencrypted;
   769 
   770     case PEP_DECRYPTED:
   771         return PEP_rating_unreliable;
   772 
   773     case PEP_DECRYPTED_AND_VERIFIED:
   774         return PEP_rating_reliable;
   775 
   776     case PEP_DECRYPT_NO_KEY:
   777         return PEP_rating_have_no_key;
   778 
   779     case PEP_DECRYPT_WRONG_FORMAT:
   780     case PEP_CANNOT_DECRYPT_UNKNOWN:
   781         return PEP_rating_cannot_decrypt;
   782 
   783     default:
   784         return PEP_rating_undefined;
   785     }
   786 }
   787 
   788 static PEP_color key_color(PEP_SESSION session, const char *fpr)
   789 {
   790     PEP_comm_type comm_type = PEP_ct_unknown;
   791 
   792     assert(session);
   793     assert(fpr);
   794     
   795     if (session == NULL || fpr == NULL)
   796         return PEP_rating_undefined;
   797 
   798     PEP_STATUS status = get_key_rating(session, fpr, &comm_type);
   799     if (status != PEP_STATUS_OK)
   800         return PEP_rating_undefined;
   801 
   802     return _rating(comm_type, PEP_rating_undefined);
   803 }
   804 
   805 static PEP_color keylist_color(PEP_SESSION session, stringlist_t *keylist)
   806 {
   807     PEP_color color = PEP_rating_reliable;
   808 
   809     assert(keylist && keylist->value);
   810     if (keylist == NULL || keylist->value == NULL)
   811         return PEP_rating_undefined;
   812 
   813     stringlist_t *_kl;
   814     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   815         PEP_comm_type ct;
   816         PEP_STATUS status;
   817 
   818         PEP_color _color = key_color(session, _kl->value);
   819         if (_color <= PEP_rating_mistrust)
   820             return _color;
   821 
   822         if (color == PEP_rating_undefined)
   823             color = _color;
   824 
   825         if (_color >= PEP_rating_reliable) {
   826             status = least_trust(session, _kl->value, &ct);
   827             if (status != PEP_STATUS_OK)
   828                 return PEP_rating_undefined;
   829             if (ct == PEP_ct_unknown)
   830                 color = PEP_rating_unencrypted_for_some;
   831             else
   832                 color = _rating(ct, color);
   833         }
   834         else if (_color == PEP_rating_unencrypted) {
   835             if (color > PEP_rating_unencrypted_for_some)
   836                 color = PEP_rating_unencrypted_for_some;
   837         }
   838     }
   839 
   840     return color;
   841 }
   842 
   843 static PEP_comm_type _get_comm_type(
   844     PEP_SESSION session,
   845     PEP_comm_type max_comm_type,
   846     pEp_identity *ident
   847     )
   848 {
   849     PEP_STATUS status = update_identity(session, ident);
   850 
   851     if (max_comm_type == PEP_ct_compromized)
   852         return PEP_ct_compromized;
   853     
   854     if (max_comm_type == PEP_ct_mistrusted)
   855         return PEP_ct_mistrusted;
   856 
   857     if (status == PEP_STATUS_OK) {
   858         if (ident->comm_type == PEP_ct_compromized)
   859             return PEP_ct_compromized;
   860         else if (ident->comm_type == PEP_ct_mistrusted)
   861             return PEP_ct_mistrusted;
   862         else
   863             return MIN(max_comm_type, ident->comm_type);
   864     }
   865     else {
   866         return PEP_ct_unknown;
   867     }
   868 }
   869 
   870 static void free_bl_entry(bloblist_t *bl)
   871 {
   872     if (bl) {
   873         free(bl->value);
   874         free(bl->mime_type);
   875         free(bl->filename);
   876         free(bl);
   877     }
   878 }
   879 
   880 static bool is_key(const bloblist_t *bl)
   881 {
   882     return (// workaround for Apple Mail bugs
   883             (is_mime_type(bl, "application/x-apple-msg-attachment") &&
   884              is_fileending(bl, ".asc")) ||
   885             // as binary, by file name
   886             ((bl->mime_type == NULL ||
   887               is_mime_type(bl, "application/octet-stream")) &&
   888              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   889                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
   890             // explicit mime type 
   891             is_mime_type(bl, "application/pgp-keys") ||
   892             // as text, by file name
   893             (is_mime_type(bl, "text/plain") &&
   894              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   895                     is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
   896            );
   897 }
   898 
   899 static void remove_attached_keys(message *msg)
   900 {
   901     if (msg) {
   902         bloblist_t *last = NULL;
   903         for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
   904             bloblist_t *next = bl->next;
   905 
   906             if (is_key(bl)) {
   907                 if (last) {
   908                     last->next = next;
   909                 }
   910                 else {
   911                     msg->attachments = next;
   912                 }
   913                 free_bl_entry(bl);
   914             }
   915             else {
   916                 last = bl;
   917             }
   918             bl = next;
   919         }
   920     }
   921 }
   922 
   923 bool import_attached_keys(
   924         PEP_SESSION session, 
   925         const message *msg,
   926         identity_list **private_idents
   927     )
   928 {
   929     assert(session);
   930     assert(msg);
   931     
   932     if (session == NULL || msg == NULL)
   933         return false;
   934 
   935     bool remove = false;
   936 
   937     bloblist_t *bl;
   938     for (bl = msg->attachments; bl && bl->value; bl = bl->next) 
   939     {
   940         if (bl && bl->value && bl->size && is_key(bl)) 
   941         {
   942             import_key(session, bl->value, bl->size, private_idents);
   943             remove = true;
   944         }
   945     }
   946     return remove;
   947 }
   948 
   949 
   950 PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
   951 {
   952     char *keydata;
   953     size_t size;
   954     bloblist_t *bl;
   955 
   956     PEP_STATUS status = export_key(session, fpr, &keydata, &size);
   957     assert(status == PEP_STATUS_OK);
   958     if (status != PEP_STATUS_OK)
   959         return status;
   960     assert(size);
   961     
   962     bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
   963                       "pEpkey.asc");
   964     
   965     if (msg->attachments == NULL && bl)
   966         msg->attachments = bl;
   967 
   968     return PEP_STATUS_OK;
   969 }
   970 
   971 #define ONE_WEEK (7*24*3600)
   972 
   973 void attach_own_key(PEP_SESSION session, message *msg)
   974 {
   975     assert(session);
   976     assert(msg);
   977     
   978     if (msg->dir == PEP_dir_incoming)
   979         return;
   980 
   981     assert(msg->from && msg->from->fpr);
   982     if (msg->from == NULL || msg->from->fpr == NULL)
   983         return;
   984 
   985     if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
   986         return;
   987     
   988     char *revoked_fpr = NULL;
   989     uint64_t revocation_date = 0;
   990     
   991     if(get_revoked(session, msg->from->fpr,
   992                    &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
   993        revoked_fpr != NULL)
   994     {
   995         time_t now = time(NULL);
   996         
   997         if (now < (time_t)revocation_date + ONE_WEEK)
   998         {
   999             _attach_key(session, revoked_fpr, msg);
  1000         }
  1001     }
  1002     free(revoked_fpr);
  1003 }
  1004 
  1005 PEP_cryptotech determine_encryption_format(message *msg)
  1006 {
  1007     assert(msg);
  1008     
  1009     if (is_PGP_message_text(msg->longmsg)) {
  1010         msg->enc_format = PEP_enc_pieces;
  1011         return PEP_crypt_OpenPGP;
  1012     }
  1013     else if (msg->attachments && msg->attachments->next &&
  1014             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
  1015             is_PGP_message_text(msg->attachments->next->value)
  1016         ) {
  1017         msg->enc_format = PEP_enc_PGP_MIME;
  1018         return PEP_crypt_OpenPGP;
  1019     }
  1020     else {
  1021         msg->enc_format = PEP_enc_none;
  1022         return PEP_crypt_none;
  1023     }
  1024 }
  1025 
  1026 DYNAMIC_API PEP_STATUS encrypt_message(
  1027         PEP_SESSION session,
  1028         message *src,
  1029         stringlist_t * extra,
  1030         message **dst,
  1031         PEP_enc_format enc_format
  1032     )
  1033 {
  1034     PEP_STATUS status = PEP_STATUS_OK;
  1035     message * msg = NULL;
  1036     stringlist_t * keys = NULL;
  1037 
  1038     assert(session);
  1039     assert(src);
  1040     assert(dst);
  1041     assert(enc_format != PEP_enc_none);
  1042 
  1043     if (!(session && src && dst && enc_format != PEP_enc_none))
  1044         return PEP_ILLEGAL_VALUE;
  1045 
  1046     if (src->dir == PEP_dir_incoming)
  1047         return PEP_ILLEGAL_VALUE;
  1048     
  1049     determine_encryption_format(src);
  1050     if (src->enc_format != PEP_enc_none)
  1051         return PEP_ILLEGAL_VALUE;
  1052 
  1053     *dst = NULL;
  1054 
  1055     status = myself(session, src->from);
  1056     if (status != PEP_STATUS_OK)
  1057         goto pep_error;
  1058 
  1059     keys = new_stringlist(src->from->fpr);
  1060     if (keys == NULL)
  1061         goto enomem;
  1062 
  1063     stringlist_t *_k = keys;
  1064 
  1065     if (extra) {
  1066         _k = stringlist_append(_k, extra);
  1067         if (_k == NULL)
  1068             goto enomem;
  1069     }
  1070 
  1071     bool dest_keys_found = true;
  1072     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1073 
  1074     identity_list * _il;
  1075     
  1076     if ((_il = src->bcc) && _il->ident)
  1077     {
  1078         // BCC limited support:
  1079         //     - App splits mails with BCC in multiple mails.
  1080         //     - Each email is encrypted separately
  1081         
  1082         if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
  1083         {
  1084             // Only one Bcc with no other recipient allowed for now
  1085             return PEP_ILLEGAL_VALUE;
  1086         }
  1087         
  1088         PEP_STATUS _status = update_identity(session, _il->ident);
  1089         if (_status != PEP_STATUS_OK) {
  1090             status = _status;
  1091             goto pep_error;
  1092         }
  1093         
  1094         if (_il->ident->fpr && _il->ident->fpr[0]) {
  1095             _k = stringlist_add(_k, _il->ident->fpr);
  1096             if (_k == NULL)
  1097                 goto enomem;
  1098             max_comm_type = _get_comm_type(session, max_comm_type,
  1099                                            _il->ident);
  1100         }
  1101         else {
  1102             dest_keys_found = false;
  1103             status = PEP_KEY_NOT_FOUND;
  1104         }        
  1105     }
  1106     else
  1107     {
  1108         for (_il = src->to; _il && _il->ident; _il = _il->next) {
  1109             PEP_STATUS _status = update_identity(session, _il->ident);
  1110             if (_status != PEP_STATUS_OK) {
  1111                 status = _status;
  1112                 goto pep_error;
  1113             }
  1114 
  1115             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1116                 _k = stringlist_add(_k, _il->ident->fpr);
  1117                 if (_k == NULL)
  1118                     goto enomem;
  1119                 max_comm_type = _get_comm_type(session, max_comm_type,
  1120                                                _il->ident);
  1121             }
  1122             else {
  1123                 dest_keys_found = false;
  1124                 status = PEP_KEY_NOT_FOUND;
  1125             }
  1126         }
  1127 
  1128         for (_il = src->cc; _il && _il->ident; _il = _il->next) {
  1129             PEP_STATUS _status = update_identity(session, _il->ident);
  1130             if (_status != PEP_STATUS_OK)
  1131             {
  1132                 status = _status;
  1133                 goto pep_error;
  1134             }
  1135 
  1136             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1137                 _k = stringlist_add(_k, _il->ident->fpr);
  1138                 if (_k == NULL)
  1139                     goto enomem;
  1140                 max_comm_type = _get_comm_type(session, max_comm_type,
  1141                                                _il->ident);
  1142             }
  1143             else {
  1144                 dest_keys_found = false;
  1145                 status = PEP_KEY_NOT_FOUND;
  1146             }
  1147         }
  1148     }
  1149     
  1150     if (!dest_keys_found ||
  1151         stringlist_length(keys) == 0 ||
  1152         _rating(max_comm_type,
  1153                 PEP_rating_undefined) < PEP_rating_reliable)
  1154     {
  1155         free_stringlist(keys);
  1156         if (!session->passive_mode)
  1157             attach_own_key(session, src);
  1158         return PEP_UNENCRYPTED;
  1159     }
  1160     else {
  1161         msg = clone_to_empty_message(src);
  1162         if (msg == NULL)
  1163             goto enomem;
  1164 
  1165         attach_own_key(session, src);
  1166 
  1167         switch (enc_format) {
  1168         case PEP_enc_PGP_MIME:
  1169         case PEP_enc_PEP: // BUG: should be implemented extra
  1170             status = encrypt_PGP_MIME(session, src, keys, msg);
  1171             break;
  1172 
  1173         case PEP_enc_pieces:
  1174             status = encrypt_PGP_in_pieces(session, src, keys, msg);
  1175             break;
  1176 
  1177         /* case PEP_enc_PEP:
  1178             // TODO: implement
  1179             NOT_IMPLEMENTED */
  1180 
  1181         default:
  1182             assert(0);
  1183             status = PEP_ILLEGAL_VALUE;
  1184             goto pep_error;
  1185         }
  1186         
  1187         if (status == PEP_OUT_OF_MEMORY)
  1188             goto enomem;
  1189         
  1190         if (status != PEP_STATUS_OK)
  1191             goto pep_error;
  1192     }
  1193 
  1194     free_stringlist(keys);
  1195 
  1196     if (msg && msg->shortmsg == NULL) {
  1197         msg->shortmsg = strdup("pEp");
  1198         assert(msg->shortmsg);
  1199         if (msg->shortmsg == NULL)
  1200             goto enomem;
  1201     }
  1202 
  1203     if (msg)
  1204         decorate_message(msg, PEP_rating_undefined, NULL);
  1205 
  1206     *dst = msg;
  1207     return status;
  1208 
  1209 enomem:
  1210     status = PEP_OUT_OF_MEMORY;
  1211 
  1212 pep_error:
  1213     free_stringlist(keys);
  1214     free_message(msg);
  1215 
  1216     return status;
  1217 }
  1218 
  1219 static bool is_a_pEpmessage(const message *msg)
  1220 {
  1221     for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  1222         if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
  1223             return true;
  1224     }
  1225     return false;
  1226 }
  1227 
  1228 // update comm_type to pEp_ct_pEp if needed
  1229 
  1230 static void _update_identity_for_incoming_message(
  1231         PEP_SESSION session,
  1232         const message *src
  1233     )
  1234 {
  1235     if (src->from && src->from->user_id && src->from->address) {
  1236         update_identity(session, src->from);
  1237         if (is_a_pEpmessage(src)
  1238                 && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
  1239                 && src->from->comm_type != PEP_ct_pEp_unconfirmed
  1240                 && src->from->comm_type != PEP_ct_pEp)
  1241         {
  1242             src->from->comm_type |= PEP_ct_pEp_unconfirmed;
  1243             update_identity(session, src->from);
  1244         }
  1245     }
  1246 }
  1247 
  1248 DYNAMIC_API PEP_STATUS _decrypt_message(
  1249         PEP_SESSION session,
  1250         message *src,
  1251         message **dst,
  1252         stringlist_t **keylist,
  1253         PEP_color *color,
  1254         PEP_decrypt_flags_t *flags, 
  1255         identity_list **private_il
  1256     )
  1257 {
  1258     PEP_STATUS status = PEP_STATUS_OK;
  1259     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  1260     message *msg = NULL;
  1261     char *ctext;
  1262     size_t csize;
  1263     char *ptext = NULL;
  1264     size_t psize;
  1265     stringlist_t *_keylist = NULL;
  1266 
  1267     assert(session);
  1268     assert(src);
  1269     assert(dst);
  1270     assert(keylist);
  1271     assert(color);
  1272     assert(flags);
  1273 
  1274     if (!(session && src && dst && keylist && color && flags))
  1275         return PEP_ILLEGAL_VALUE;
  1276 
  1277     *flags = 0;
  1278 
  1279     // Private key in unencrypted mail are ignored -> NULL
  1280     bool imported_keys = import_attached_keys(session, src, NULL);
  1281 
  1282     // Update src->from in case we just imported a key
  1283     // we would need to check signature
  1284     _update_identity_for_incoming_message(session, src);
  1285     PEP_cryptotech crypto = determine_encryption_format(src);
  1286 
  1287     *dst = NULL;
  1288     *keylist = NULL;
  1289     *color = PEP_rating_undefined;
  1290  
  1291     switch (src->enc_format) {
  1292         case PEP_enc_none:
  1293             *color = PEP_rating_unencrypted;
  1294             if (imported_keys)
  1295                 remove_attached_keys(src);
  1296             return PEP_UNENCRYPTED;
  1297 
  1298         case PEP_enc_PGP_MIME:
  1299             ctext = src->attachments->next->value;
  1300             csize = src->attachments->next->size;
  1301             break;
  1302 
  1303         case PEP_enc_pieces:
  1304             ctext = src->longmsg;
  1305             csize = strlen(ctext);
  1306             break;
  1307 
  1308         default:
  1309             NOT_IMPLEMENTED
  1310     }
  1311     status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1312                                                    csize, &ptext, &psize, &_keylist);
  1313     if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1314         goto pep_error;
  1315 
  1316     decrypt_status = status;
  1317 
  1318     bool imported_private_key_address = false; 
  1319 
  1320     if (ptext) {
  1321         switch (src->enc_format) {
  1322             case PEP_enc_PGP_MIME:
  1323                 status = mime_decode_message(ptext, psize, &msg);
  1324                 if (status != PEP_STATUS_OK)
  1325                     goto pep_error;
  1326                 break;
  1327 
  1328             case PEP_enc_pieces:
  1329                 msg = clone_to_empty_message(src);
  1330                 if (msg == NULL)
  1331                     goto enomem;
  1332 
  1333                 msg->longmsg = strdup(ptext);
  1334                 assert(msg->longmsg);
  1335                 if (msg->longmsg == NULL)
  1336                     goto enomem;
  1337 
  1338                 bloblist_t *_m = msg->attachments;
  1339                 if (_m == NULL && src->attachments && src->attachments->value) {
  1340                     msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
  1341                     _m = msg->attachments;
  1342                 }
  1343 
  1344                 bloblist_t *_s;
  1345                 for (_s = src->attachments; _s; _s = _s->next) {
  1346                     if (_s->value == NULL && _s->size == 0){
  1347                         _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
  1348                         if (_m == NULL)
  1349                             goto enomem;
  1350 
  1351                     }
  1352                     else if (is_encrypted_attachment(_s)) {
  1353                         stringlist_t *_keylist = NULL;
  1354                         char *attctext;
  1355                         size_t attcsize;
  1356 
  1357                         attctext = _s->value;
  1358                         attcsize = _s->size;
  1359 
  1360                         free(ptext);
  1361                         ptext = NULL;
  1362 
  1363                         status = decrypt_and_verify(session, attctext, attcsize,
  1364                                 &ptext, &psize, &_keylist);
  1365                         free_stringlist(_keylist);
  1366 
  1367                         if (ptext) {
  1368                             if (is_encrypted_html_attachment(_s)) {
  1369                                 msg->longmsg_formatted = strdup(ptext);
  1370                                 assert(msg->longmsg_formatted);
  1371                                 if (msg->longmsg_formatted == NULL)
  1372                                     goto pep_error;
  1373                             }
  1374                             else {
  1375                                 char * mime_type = "application/octet-stream";
  1376                                 char * filename =
  1377                                     without_double_ending(_s->filename);
  1378                                 if (filename == NULL)
  1379                                     goto enomem;
  1380 
  1381                                 char *_ptext = malloc(psize);
  1382                                 assert(_ptext);
  1383                                 if (_ptext == NULL)
  1384                                     goto enomem;
  1385                                 memcpy(_ptext, ptext, psize);
  1386 
  1387                                 _m = bloblist_add(_m, _ptext, psize, mime_type,
  1388                                     filename);
  1389                                 if (_m == NULL)
  1390                                     goto enomem;
  1391 
  1392                                 if (msg->attachments == NULL)
  1393                                     msg->attachments = _m;
  1394                             }
  1395                         }
  1396                         else {
  1397                             char *copy = malloc(_s->size);
  1398                             assert(copy);
  1399                             if (copy == NULL)
  1400                                 goto enomem;
  1401                             memcpy(copy, _s->value, _s->size);
  1402                             _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1403                             if (_m == NULL)
  1404                                 goto enomem;
  1405                         }
  1406                     }
  1407                     else {
  1408                         char *copy = malloc(_s->size);
  1409                         assert(copy);
  1410                         if (copy == NULL)
  1411                             goto enomem;
  1412                         memcpy(copy, _s->value, _s->size);
  1413                         _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1414                         if (_m == NULL)
  1415                             goto enomem;
  1416                     }
  1417                 }
  1418 
  1419                 break;
  1420 
  1421             default:
  1422                 // BUG: must implement more
  1423                 NOT_IMPLEMENTED
  1424         }
  1425         
  1426         switch (src->enc_format) {
  1427             case PEP_enc_PGP_MIME:
  1428             case PEP_enc_pieces:
  1429                 status = copy_fields(msg, src);
  1430                 if (status != PEP_STATUS_OK)
  1431                     goto pep_error;
  1432 
  1433                 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
  1434                 {
  1435                     char * shortmsg;
  1436                     char * longmsg;
  1437 
  1438                     int r = separate_short_and_long(msg->longmsg, &shortmsg,
  1439                             &longmsg);
  1440                     if (r == -1)
  1441                         goto enomem;
  1442 
  1443                     free(msg->shortmsg);
  1444                     free(msg->longmsg);
  1445 
  1446                     msg->shortmsg = shortmsg;
  1447                     msg->longmsg = longmsg;
  1448                 }
  1449                 else {
  1450                     msg->shortmsg = strdup(src->shortmsg);
  1451                     assert(msg->shortmsg);
  1452                     if (msg->shortmsg == NULL)
  1453                         goto enomem;
  1454                 }
  1455                 break;
  1456 
  1457             default:
  1458                 // BUG: must implement more
  1459                 NOT_IMPLEMENTED
  1460         }
  1461        
  1462         // check for private key in decrypted message attachement while inporting
  1463         identity_list *_private_il = NULL;
  1464         imported_keys = import_attached_keys(session, msg, &_private_il);
  1465         if (_private_il && 
  1466             identity_list_length(_private_il) == 1 &&
  1467             _private_il->ident->address)
  1468         {
  1469             imported_private_key_address = true;
  1470         }
  1471 
  1472         if(private_il && imported_private_key_address){
  1473             *private_il = _private_il;
  1474         }else{
  1475             free_identity_list(_private_il);
  1476         }
  1477          
  1478         if(decrypt_status == PEP_DECRYPTED){
  1479 
  1480             // TODO optimize if import_attached_keys didn't import any key
  1481             
  1482             // In case message did decrypt, but no valid signature could be found
  1483             // then retry decrypt+verify after importing key.
  1484 
  1485             // Update msg->from in case we just imported a key
  1486             // we would need to check signature
  1487 
  1488             _update_identity_for_incoming_message(session, src);
  1489             
  1490             char *re_ptext = NULL;
  1491             size_t re_psize;
  1492             
  1493             free_stringlist(_keylist);
  1494             _keylist = NULL;
  1495 
  1496             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1497                 csize, &re_ptext, &re_psize, &_keylist);
  1498             
  1499             if (re_ptext)
  1500                 free(re_ptext);
  1501             
  1502             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1503                 goto pep_error;
  1504             
  1505             decrypt_status = status;
  1506         }
  1507         
  1508         *color = decrypt_color(decrypt_status);
  1509         
  1510         if (*color > PEP_rating_mistrust) {
  1511             PEP_color kl_color = PEP_rating_undefined;
  1512             
  1513             if (_keylist)
  1514                 kl_color = keylist_color(session, _keylist);
  1515             
  1516             if (kl_color <= PEP_rating_mistrust) {
  1517                 *color = kl_color;
  1518             }
  1519             else if (*color >= PEP_rating_reliable &&
  1520                      kl_color < PEP_rating_reliable) {
  1521                 *color = PEP_rating_unreliable;
  1522             }
  1523             else if (*color >= PEP_rating_reliable &&
  1524                      kl_color >= PEP_rating_reliable) {
  1525                 if (!(src->from && src->from->user_id && src->from->user_id[0])) {
  1526                     *color = PEP_rating_unreliable;
  1527                 }
  1528                 else {
  1529                     char *fpr = _keylist->value;
  1530                     pEp_identity *_from = new_identity(src->from->address, fpr,
  1531                                                        src->from->user_id, src->from->username);
  1532                     if (_from == NULL)
  1533                         goto enomem;
  1534                     status = update_identity(session, _from);
  1535                     if (_from->comm_type != PEP_ct_unknown)
  1536                         *color = _rating(_from->comm_type, PEP_rating_undefined);
  1537                     free_identity(_from);
  1538                     if (status != PEP_STATUS_OK)
  1539                         goto pep_error;
  1540                 }
  1541             }
  1542         }
  1543     }
  1544     else
  1545     {
  1546         *color = decrypt_color(decrypt_status);
  1547         goto pep_error;
  1548     }
  1549 
  1550     // Case of own key imported from own trusted message
  1551     if (// Message have been reliably decrypted 
  1552         msg &&
  1553         *color >= PEP_rating_green &&
  1554         imported_private_key_address &&
  1555         // to is [own]
  1556         msg->to->ident->user_id &&
  1557         strcmp(msg->to->ident->user_id, PEP_OWN_USERID) == 0 
  1558         )
  1559     {
  1560         *flags |= PEP_decrypt_flag_own_private_key;
  1561     }
  1562 
  1563     if (msg) {
  1564         decorate_message(msg, *color, _keylist);
  1565         if (imported_keys)
  1566             remove_attached_keys(msg);
  1567     }
  1568 
  1569     *dst = msg;
  1570     *keylist = _keylist;
  1571 
  1572     return PEP_STATUS_OK;
  1573 
  1574 enomem:
  1575     status = PEP_OUT_OF_MEMORY;
  1576 
  1577 pep_error:
  1578     free_message(msg);
  1579     free_stringlist(_keylist);
  1580 
  1581     return status;
  1582 }
  1583 
  1584 DYNAMIC_API PEP_STATUS decrypt_message(
  1585         PEP_SESSION session,
  1586         message *src,
  1587         message **dst,
  1588         stringlist_t **keylist,
  1589         PEP_color *color,
  1590         PEP_decrypt_flags_t *flags 
  1591     )
  1592 {
  1593     return _decrypt_message( session, src, dst, keylist, color, flags, NULL );
  1594 }
  1595 
  1596 DYNAMIC_API PEP_STATUS own_message_private_key_details(
  1597         PEP_SESSION session,
  1598         message *msg,
  1599         pEp_identity **ident 
  1600     )
  1601 {
  1602     assert(session);
  1603     assert(msg);
  1604     assert(ident);
  1605 
  1606     if (!(session && msg && ident))
  1607         return PEP_ILLEGAL_VALUE;
  1608 
  1609     message *dst; 
  1610     stringlist_t *keylist;
  1611     PEP_color color;
  1612     PEP_decrypt_flags_t flags; 
  1613 
  1614     *ident = NULL;
  1615 
  1616     identity_list *private_il = NULL;
  1617     PEP_STATUS status = _decrypt_message(session, msg,  &dst, &keylist, &color, &flags, &private_il);
  1618 
  1619     if (status == PEP_STATUS_OK &&
  1620         flags & PEP_decrypt_flag_own_private_key &&
  1621         private_il)
  1622     {
  1623         *ident = identity_dup(private_il->ident);
  1624     }
  1625 
  1626     free_identity_list(private_il);
  1627 
  1628     return status;
  1629 
  1630 }
  1631 
  1632 DYNAMIC_API PEP_STATUS outgoing_message_color(
  1633         PEP_SESSION session,
  1634         message *msg,
  1635         PEP_color *color
  1636     )
  1637 {
  1638     PEP_STATUS status = PEP_STATUS_OK;
  1639     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1640     bool comm_type_determined = false;
  1641     identity_list * il;
  1642 
  1643     assert(session);
  1644     assert(msg);
  1645     assert(msg->from);
  1646     assert(msg->dir == PEP_dir_outgoing);
  1647     assert(color);
  1648 
  1649     if (!(session && msg && color))
  1650         return PEP_ILLEGAL_VALUE;
  1651 
  1652     if (msg->from == NULL || msg->dir != PEP_dir_outgoing)
  1653         return PEP_ILLEGAL_VALUE;
  1654 
  1655     *color = PEP_rating_undefined;
  1656 
  1657     status = myself(session, msg->from);
  1658     if (status != PEP_STATUS_OK)
  1659         return status;
  1660 
  1661     for (il = msg->to; il != NULL; il = il->next)
  1662     {
  1663         if (il->ident)
  1664         {
  1665             update_identity(session, il->ident);
  1666             max_comm_type = _get_comm_type(session, max_comm_type,
  1667                     il->ident);
  1668             comm_type_determined = true;
  1669         }
  1670     }
  1671 
  1672     for (il = msg->cc; il != NULL; il = il->next)
  1673     {
  1674         if (il->ident)
  1675         {
  1676             update_identity(session, il->ident);
  1677             max_comm_type = _get_comm_type(session, max_comm_type,
  1678                     il->ident);
  1679             comm_type_determined = true;
  1680         }
  1681     }
  1682         
  1683     for (il = msg->bcc; il != NULL; il = il->next)
  1684     {
  1685         if (il->ident)
  1686         {
  1687             update_identity(session, il->ident);
  1688             max_comm_type = _get_comm_type(session, max_comm_type,
  1689                                            il->ident);
  1690             comm_type_determined = true;
  1691         }
  1692     }
  1693 
  1694     if (comm_type_determined == false)
  1695         *color = PEP_rating_undefined;
  1696     else
  1697         *color = MAX(_rating(max_comm_type, PEP_rating_undefined),
  1698                 PEP_rating_unencrypted);
  1699 
  1700     return PEP_STATUS_OK;
  1701 }
  1702 
  1703 DYNAMIC_API PEP_STATUS identity_color(
  1704         PEP_SESSION session,
  1705         pEp_identity *ident,
  1706         PEP_color *color
  1707     )
  1708 {
  1709     PEP_STATUS status = PEP_STATUS_OK;
  1710 
  1711     assert(session);
  1712     assert(ident);
  1713     assert(color);
  1714 
  1715     if (!(session && ident && color))
  1716         return PEP_ILLEGAL_VALUE;
  1717 
  1718     if (ident->me)
  1719         status = myself(session, ident);
  1720     else
  1721         status = update_identity(session, ident);
  1722 
  1723     if (status == PEP_STATUS_OK)
  1724         *color = _rating(ident->comm_type, PEP_rating_undefined);
  1725 
  1726     return status;
  1727 }
  1728 
  1729 DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
  1730 {
  1731     PEP_STATUS status = PEP_STATUS_OK;
  1732 
  1733     assert(path);
  1734     if (path == NULL)
  1735         return PEP_ILLEGAL_VALUE;
  1736 
  1737     if (cryptotech[tech].binary_path == NULL)
  1738         *path = NULL;
  1739     else
  1740         status = cryptotech[tech].binary_path(path);
  1741 
  1742     return status;
  1743 }
  1744