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