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