src/message_api.c
author Krista Grothoff <krista@pep-project.org>
Sat, 24 Sep 2016 18:09:18 +0200
branchENGINE-27
changeset 1185 4b01328f3cf2
parent 995 6d8b1c28c9a5
child 1001 42e0841b71f0
child 1126 129b561095a6
child 1186 3b9e4b2666d4
permissions -rw-r--r--
ENGINE-27: update_identity no longer pays attention to the input identity's fpr, but pulls one from elect_pubkey.
     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                         size_t bufsize = len + 5; // length of .pgp extension + NUL
   536                         filename = calloc(1, bufsize);
   537                         if (filename == NULL)
   538                             goto enomem;
   539 
   540                         strlcpy(filename, _s->filename, bufsize);
   541                         strlcat(filename, ".pgp", bufsize);
   542                     }
   543                     else {
   544                         filename = calloc(1, 20);
   545                         if (filename == NULL)
   546                             goto enomem;
   547 
   548                         ++n;
   549                         n &= 0xffff;
   550                         snprintf(filename, 20, "Attachment%d.pgp", n);
   551                     }
   552 
   553                     _d = bloblist_add(_d, ctext, csize, "application/octet-stream",
   554                         filename);
   555                     free(filename);
   556                     if (_d == NULL)
   557                         goto enomem;
   558                 }
   559                 else {
   560                     goto pep_error;
   561                 }
   562             }
   563         }
   564     }
   565 
   566     return PEP_STATUS_OK;
   567 
   568 enomem:
   569     status = PEP_OUT_OF_MEMORY;
   570 
   571 pep_error:
   572     if (free_ptext)
   573         free(ptext);
   574     return status;
   575 }
   576 
   577 static char * keylist_to_string(const stringlist_t *keylist)
   578 {
   579     if (keylist) {
   580         size_t size = stringlist_length(keylist);
   581 
   582         const stringlist_t *_kl;
   583         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   584             size += strlen(_kl->value);
   585         }
   586 
   587         char *result = calloc(1, size);
   588         if (result == NULL)
   589             return NULL;
   590 
   591         char *_r = result;
   592         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   593             _r = stpcpy(_r, _kl->value);
   594             if (_kl->next && _kl->next->value)
   595                 _r = stpcpy(_r, ",");
   596         }
   597 
   598         return result;
   599     }
   600     else {
   601         return NULL;
   602     }
   603 }
   604 
   605 static const char * color_to_string(PEP_color color)
   606 {
   607     switch (color) {
   608     case PEP_rating_cannot_decrypt:
   609         return "cannot_decrypt";
   610     case PEP_rating_have_no_key:
   611         return "have_no_key";
   612     case PEP_rating_unencrypted:
   613         return "unencrypted";
   614     case PEP_rating_unencrypted_for_some:
   615         return "unencrypted_for_some";
   616     case PEP_rating_unreliable:
   617         return "unreliable";
   618     case PEP_rating_reliable:
   619         return "reliable";
   620     case PEP_rating_trusted:
   621         return "trusted";
   622     case PEP_rating_trusted_and_anonymized:
   623         return "trusted_and_anonymized";
   624     case PEP_rating_fully_anonymous:
   625         return "fully_anonymous";
   626     case PEP_rating_mistrust:
   627         return "mistrust";
   628     case PEP_rating_b0rken:
   629         return "b0rken";
   630     case PEP_rating_under_attack:
   631         return "under_attack";
   632     default:
   633         return "undefined";
   634     }
   635 }
   636 
   637 static void decorate_message(
   638     message *msg,
   639     PEP_color color,
   640     stringlist_t *keylist
   641     )
   642 {
   643     assert(msg);
   644 
   645     add_opt_field(msg, "X-pEp-Version", PEP_VERSION);
   646     
   647     if (color != PEP_rating_undefined)
   648         add_opt_field(msg, "X-EncStatus", color_to_string(color));
   649 
   650     if (keylist) {
   651         char *_keylist = keylist_to_string(keylist);
   652         add_opt_field(msg, "X-KeyList", _keylist);
   653         free(_keylist);
   654     }
   655 }
   656 
   657 static PEP_color _rating(PEP_comm_type ct, PEP_color color)
   658 {
   659     if (ct == PEP_ct_unknown)
   660         return PEP_rating_undefined;
   661 
   662     else if (ct == PEP_ct_compromized)
   663         return PEP_rating_under_attack;
   664 
   665     else if (ct == PEP_ct_mistrusted)
   666         return PEP_rating_mistrust;
   667     
   668     if (color == PEP_rating_unencrypted_for_some)
   669         return PEP_rating_unencrypted_for_some;
   670 
   671     if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel ||
   672             ct == PEP_ct_my_key_not_included) {
   673         if (color > PEP_rating_unencrypted_for_some)
   674             return PEP_rating_unencrypted_for_some;
   675         else
   676             return PEP_rating_unencrypted;
   677     }
   678 
   679     if (color == PEP_rating_unencrypted)
   680         return PEP_rating_unencrypted_for_some;
   681 
   682     if (ct >= PEP_ct_confirmed_enc_anon)
   683         return PEP_rating_trusted_and_anonymized;
   684 
   685     else if (ct >= PEP_ct_strong_encryption)
   686         return PEP_rating_trusted;
   687 
   688     else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
   689         return PEP_rating_reliable;
   690 
   691     else
   692         return PEP_rating_unreliable;
   693 }
   694 
   695 static bool is_encrypted_attachment(const bloblist_t *blob)
   696 {
   697     char *ext;
   698 
   699     assert(blob);
   700 
   701     if (blob == NULL || blob->filename == NULL)
   702         return false;
   703     
   704     ext = strrchr(blob->filename, '.');
   705     if (ext == NULL)
   706         return false;
   707 
   708     if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
   709         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
   710             strcmp(ext, ".asc") == 0)
   711             return true;
   712     }
   713     else if (strcmp(blob->mime_type, "text/plain") == 0) {
   714         if (strcmp(ext, ".asc") == 0)
   715             return true;
   716     }
   717 
   718     return false;
   719 }
   720 
   721 static bool is_encrypted_html_attachment(const bloblist_t *blob)
   722 {
   723     assert(blob);
   724     assert(blob->filename);
   725     if (blob == NULL || blob->filename == NULL)
   726         return false;
   727 
   728     if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
   729         if (strcmp(blob->filename + 11, ".pgp") == 0 ||
   730             strcmp(blob->filename + 11, ".asc") == 0)
   731             return true;
   732     }
   733 
   734     return false;
   735 }
   736 
   737 static char * without_double_ending(const char *filename)
   738 {
   739     assert(filename);
   740     if (filename == NULL)
   741         return NULL;
   742     
   743     char *ext = strrchr(filename, '.');
   744     if (ext == NULL)
   745         return NULL;
   746 
   747     char *result = strndup(filename, ext - filename);
   748     assert(result);
   749     return result;
   750 }
   751 
   752 static PEP_color decrypt_color(PEP_STATUS status)
   753 {
   754     switch (status) {
   755     case PEP_UNENCRYPTED:
   756     case PEP_VERIFIED:
   757     case PEP_VERIFY_NO_KEY:
   758     case PEP_VERIFIED_AND_TRUSTED:
   759         return PEP_rating_unencrypted;
   760 
   761     case PEP_DECRYPTED:
   762         return PEP_rating_unreliable;
   763 
   764     case PEP_DECRYPTED_AND_VERIFIED:
   765         return PEP_rating_reliable;
   766 
   767     case PEP_DECRYPT_NO_KEY:
   768         return PEP_rating_have_no_key;
   769 
   770     case PEP_DECRYPT_WRONG_FORMAT:
   771     case PEP_CANNOT_DECRYPT_UNKNOWN:
   772         return PEP_rating_cannot_decrypt;
   773 
   774     default:
   775         return PEP_rating_undefined;
   776     }
   777 }
   778 
   779 static PEP_color key_color(PEP_SESSION session, const char *fpr)
   780 {
   781     PEP_comm_type comm_type = PEP_ct_unknown;
   782 
   783     assert(session);
   784     assert(fpr);
   785     
   786     if (session == NULL || fpr == NULL)
   787         return PEP_rating_undefined;
   788 
   789     PEP_STATUS status = get_key_rating(session, fpr, &comm_type);
   790     if (status != PEP_STATUS_OK)
   791         return PEP_rating_undefined;
   792 
   793     return _rating(comm_type, PEP_rating_undefined);
   794 }
   795 
   796 static PEP_color keylist_color(PEP_SESSION session, stringlist_t *keylist)
   797 {
   798     PEP_color color = PEP_rating_reliable;
   799 
   800     assert(keylist && keylist->value);
   801     if (keylist == NULL || keylist->value == NULL)
   802         return PEP_rating_undefined;
   803 
   804     stringlist_t *_kl;
   805     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   806         PEP_comm_type ct;
   807         PEP_STATUS status;
   808 
   809         PEP_color _color = key_color(session, _kl->value);
   810         if (_color <= PEP_rating_mistrust)
   811             return _color;
   812 
   813         if (color == PEP_rating_undefined)
   814             color = _color;
   815 
   816         if (_color >= PEP_rating_reliable) {
   817             status = least_trust(session, _kl->value, &ct);
   818             if (status != PEP_STATUS_OK)
   819                 return PEP_rating_undefined;
   820             if (ct == PEP_ct_unknown)
   821                 color = PEP_rating_unencrypted_for_some;
   822             else
   823                 color = _rating(ct, color);
   824         }
   825         else if (_color == PEP_rating_unencrypted) {
   826             if (color > PEP_rating_unencrypted_for_some)
   827                 color = PEP_rating_unencrypted_for_some;
   828         }
   829     }
   830 
   831     return color;
   832 }
   833 
   834 static PEP_comm_type _get_comm_type(
   835     PEP_SESSION session,
   836     PEP_comm_type max_comm_type,
   837     pEp_identity *ident
   838     )
   839 {
   840     PEP_STATUS status = update_identity(session, ident);
   841 
   842     if (max_comm_type == PEP_ct_compromized)
   843         return PEP_ct_compromized;
   844     
   845     if (max_comm_type == PEP_ct_mistrusted)
   846         return PEP_ct_mistrusted;
   847 
   848     if (status == PEP_STATUS_OK) {
   849         if (ident->comm_type == PEP_ct_compromized)
   850             return PEP_ct_compromized;
   851         else if (ident->comm_type == PEP_ct_mistrusted)
   852             return PEP_ct_mistrusted;
   853         else
   854             return MIN(max_comm_type, ident->comm_type);
   855     }
   856     else {
   857         return PEP_ct_unknown;
   858     }
   859 }
   860 
   861 static void free_bl_entry(bloblist_t *bl)
   862 {
   863     if (bl) {
   864         free(bl->value);
   865         free(bl->mime_type);
   866         free(bl->filename);
   867         free(bl);
   868     }
   869 }
   870 
   871 static bool is_key(const bloblist_t *bl)
   872 {
   873     return (// workaround for Apple Mail bugs
   874             (is_mime_type(bl, "application/x-apple-msg-attachment") &&
   875              is_fileending(bl, ".asc")) ||
   876             // as binary, by file name
   877             ((bl->mime_type == NULL ||
   878               is_mime_type(bl, "application/octet-stream")) &&
   879              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   880                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
   881             // explicit mime type 
   882             is_mime_type(bl, "application/pgp-keys") ||
   883             // as text, by file name
   884             (is_mime_type(bl, "text/plain") &&
   885              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   886                     is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
   887            );
   888 }
   889 
   890 static void remove_attached_keys(message *msg)
   891 {
   892     if (msg) {
   893         bloblist_t *last = NULL;
   894         for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
   895             bloblist_t *next = bl->next;
   896 
   897             if (is_key(bl)) {
   898                 if (last) {
   899                     last->next = next;
   900                 }
   901                 else {
   902                     msg->attachments = next;
   903                 }
   904                 free_bl_entry(bl);
   905             }
   906             else {
   907                 last = bl;
   908             }
   909             bl = next;
   910         }
   911     }
   912 }
   913 
   914 bool import_attached_keys(
   915         PEP_SESSION session, 
   916         const message *msg,
   917         identity_list **private_idents
   918     )
   919 {
   920     assert(session);
   921     assert(msg);
   922     
   923     if (session == NULL || msg == NULL)
   924         return false;
   925 
   926     bool remove = false;
   927 
   928     bloblist_t *bl;
   929     int i = 0;
   930     for (bl = msg->attachments; i < MAX_KEYS_TO_IMPORT && bl && bl->value;
   931             bl = bl->next, i++) 
   932     {
   933         if (bl && bl->value && bl->size && bl->size < MAX_KEY_SIZE
   934                 && is_key(bl)) 
   935         {
   936             import_key(session, bl->value, bl->size, private_idents);
   937             remove = true;
   938         }
   939     }
   940     return remove;
   941 }
   942 
   943 
   944 PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
   945 {
   946     char *keydata;
   947     size_t size;
   948     bloblist_t *bl;
   949 
   950     PEP_STATUS status = export_key(session, fpr, &keydata, &size);
   951     assert(status == PEP_STATUS_OK);
   952     if (status != PEP_STATUS_OK)
   953         return status;
   954     assert(size);
   955     
   956     bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
   957                       "pEpkey.asc");
   958     
   959     if (msg->attachments == NULL && bl)
   960         msg->attachments = bl;
   961 
   962     return PEP_STATUS_OK;
   963 }
   964 
   965 #define ONE_WEEK (7*24*3600)
   966 
   967 void attach_own_key(PEP_SESSION session, message *msg)
   968 {
   969     assert(session);
   970     assert(msg);
   971     
   972     if (msg->dir == PEP_dir_incoming)
   973         return;
   974 
   975     assert(msg->from && msg->from->fpr);
   976     if (msg->from == NULL || msg->from->fpr == NULL)
   977         return;
   978 
   979     if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
   980         return;
   981     
   982     char *revoked_fpr = NULL;
   983     uint64_t revocation_date = 0;
   984     
   985     if(get_revoked(session, msg->from->fpr,
   986                    &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
   987        revoked_fpr != NULL)
   988     {
   989         time_t now = time(NULL);
   990         
   991         if (now < (time_t)revocation_date + ONE_WEEK)
   992         {
   993             _attach_key(session, revoked_fpr, msg);
   994         }
   995     }
   996     free(revoked_fpr);
   997 }
   998 
   999 PEP_cryptotech determine_encryption_format(message *msg)
  1000 {
  1001     assert(msg);
  1002     
  1003     if (is_PGP_message_text(msg->longmsg)) {
  1004         msg->enc_format = PEP_enc_pieces;
  1005         return PEP_crypt_OpenPGP;
  1006     }
  1007     else if (msg->attachments && msg->attachments->next &&
  1008             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
  1009             is_PGP_message_text(msg->attachments->next->value)
  1010         ) {
  1011         msg->enc_format = PEP_enc_PGP_MIME;
  1012         return PEP_crypt_OpenPGP;
  1013     }
  1014     else {
  1015         msg->enc_format = PEP_enc_none;
  1016         return PEP_crypt_none;
  1017     }
  1018 }
  1019 
  1020 DYNAMIC_API PEP_STATUS encrypt_message(
  1021         PEP_SESSION session,
  1022         message *src,
  1023         stringlist_t * extra,
  1024         message **dst,
  1025         PEP_enc_format enc_format
  1026     )
  1027 {
  1028     PEP_STATUS status = PEP_STATUS_OK;
  1029     message * msg = NULL;
  1030     stringlist_t * keys = NULL;
  1031 
  1032     assert(session);
  1033     assert(src);
  1034     assert(dst);
  1035     assert(enc_format != PEP_enc_none);
  1036 
  1037     if (!(session && src && dst && enc_format != PEP_enc_none))
  1038         return PEP_ILLEGAL_VALUE;
  1039 
  1040     if (src->dir == PEP_dir_incoming)
  1041         return PEP_ILLEGAL_VALUE;
  1042     
  1043     determine_encryption_format(src);
  1044     if (src->enc_format != PEP_enc_none)
  1045         return PEP_ILLEGAL_VALUE;
  1046 
  1047     *dst = NULL;
  1048 
  1049     status = myself(session, src->from);
  1050     if (status != PEP_STATUS_OK)
  1051         goto pep_error;
  1052 
  1053     keys = new_stringlist(src->from->fpr);
  1054     if (keys == NULL)
  1055         goto enomem;
  1056 
  1057     stringlist_t *_k = keys;
  1058 
  1059     if (extra) {
  1060         _k = stringlist_append(_k, extra);
  1061         if (_k == NULL)
  1062             goto enomem;
  1063     }
  1064 
  1065     bool dest_keys_found = true;
  1066     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1067 
  1068     identity_list * _il;
  1069     
  1070     if ((_il = src->bcc) && _il->ident)
  1071     {
  1072         // BCC limited support:
  1073         //     - App splits mails with BCC in multiple mails.
  1074         //     - Each email is encrypted separately
  1075         
  1076         if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
  1077         {
  1078             // Only one Bcc with no other recipient allowed for now
  1079             return PEP_ILLEGAL_VALUE;
  1080         }
  1081         
  1082         PEP_STATUS _status = update_identity(session, _il->ident);
  1083         if (_status != PEP_STATUS_OK) {
  1084             status = _status;
  1085             goto pep_error;
  1086         }
  1087         
  1088         if (_il->ident->fpr && _il->ident->fpr[0]) {
  1089             _k = stringlist_add(_k, _il->ident->fpr);
  1090             if (_k == NULL)
  1091                 goto enomem;
  1092             max_comm_type = _get_comm_type(session, max_comm_type,
  1093                                            _il->ident);
  1094         }
  1095         else {
  1096             dest_keys_found = false;
  1097             status = PEP_KEY_NOT_FOUND;
  1098         }        
  1099     }
  1100     else
  1101     {
  1102         for (_il = src->to; _il && _il->ident; _il = _il->next) {
  1103             PEP_STATUS _status = update_identity(session, _il->ident);
  1104             if (_status != PEP_STATUS_OK) {
  1105                 status = _status;
  1106                 goto pep_error;
  1107             }
  1108 
  1109             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1110                 _k = stringlist_add(_k, _il->ident->fpr);
  1111                 if (_k == NULL)
  1112                     goto enomem;
  1113                 max_comm_type = _get_comm_type(session, max_comm_type,
  1114                                                _il->ident);
  1115             }
  1116             else {
  1117                 dest_keys_found = false;
  1118                 status = PEP_KEY_NOT_FOUND;
  1119             }
  1120         }
  1121 
  1122         for (_il = src->cc; _il && _il->ident; _il = _il->next) {
  1123             PEP_STATUS _status = update_identity(session, _il->ident);
  1124             if (_status != PEP_STATUS_OK)
  1125             {
  1126                 status = _status;
  1127                 goto pep_error;
  1128             }
  1129 
  1130             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1131                 _k = stringlist_add(_k, _il->ident->fpr);
  1132                 if (_k == NULL)
  1133                     goto enomem;
  1134                 max_comm_type = _get_comm_type(session, max_comm_type,
  1135                                                _il->ident);
  1136             }
  1137             else {
  1138                 dest_keys_found = false;
  1139                 status = PEP_KEY_NOT_FOUND;
  1140             }
  1141         }
  1142     }
  1143     
  1144     if (!dest_keys_found ||
  1145         stringlist_length(keys) == 0 ||
  1146         _rating(max_comm_type,
  1147                 PEP_rating_undefined) < PEP_rating_reliable)
  1148     {
  1149         free_stringlist(keys);
  1150         if (!session->passive_mode)
  1151             attach_own_key(session, src);
  1152         return PEP_UNENCRYPTED;
  1153     }
  1154     else {
  1155         msg = clone_to_empty_message(src);
  1156         if (msg == NULL)
  1157             goto enomem;
  1158 
  1159         attach_own_key(session, src);
  1160 
  1161         switch (enc_format) {
  1162         case PEP_enc_PGP_MIME:
  1163         case PEP_enc_PEP: // BUG: should be implemented extra
  1164             status = encrypt_PGP_MIME(session, src, keys, msg);
  1165             break;
  1166 
  1167         case PEP_enc_pieces:
  1168             status = encrypt_PGP_in_pieces(session, src, keys, msg);
  1169             break;
  1170 
  1171         /* case PEP_enc_PEP:
  1172             // TODO: implement
  1173             NOT_IMPLEMENTED */
  1174 
  1175         default:
  1176             assert(0);
  1177             status = PEP_ILLEGAL_VALUE;
  1178             goto pep_error;
  1179         }
  1180         
  1181         if (status == PEP_OUT_OF_MEMORY)
  1182             goto enomem;
  1183         
  1184         if (status != PEP_STATUS_OK)
  1185             goto pep_error;
  1186     }
  1187 
  1188     free_stringlist(keys);
  1189 
  1190     if (msg && msg->shortmsg == NULL) {
  1191         msg->shortmsg = strdup("pEp");
  1192         assert(msg->shortmsg);
  1193         if (msg->shortmsg == NULL)
  1194             goto enomem;
  1195     }
  1196 
  1197     if (msg)
  1198         decorate_message(msg, PEP_rating_undefined, NULL);
  1199 
  1200     *dst = msg;
  1201     return status;
  1202 
  1203 enomem:
  1204     status = PEP_OUT_OF_MEMORY;
  1205 
  1206 pep_error:
  1207     free_stringlist(keys);
  1208     free_message(msg);
  1209 
  1210     return status;
  1211 }
  1212 
  1213 DYNAMIC_API PEP_STATUS encrypt_message_for_self(
  1214         PEP_SESSION session,
  1215         pEp_identity* target_id,
  1216         message *src,
  1217         message **dst,
  1218         PEP_enc_format enc_format
  1219     )
  1220 {
  1221     PEP_STATUS status = PEP_STATUS_OK;
  1222     message * msg = NULL;
  1223     stringlist_t * keys = NULL;
  1224 
  1225     assert(session);
  1226     assert(src);
  1227     assert(dst);
  1228     assert(enc_format != PEP_enc_none);
  1229 
  1230     if (!(session && src && dst && enc_format != PEP_enc_none))
  1231         return PEP_ILLEGAL_VALUE;
  1232 
  1233     if (src->dir == PEP_dir_incoming)
  1234         return PEP_ILLEGAL_VALUE;
  1235     
  1236     determine_encryption_format(src);
  1237     if (src->enc_format != PEP_enc_none)
  1238         return PEP_ILLEGAL_VALUE;
  1239 
  1240     status = myself(session, target_id);
  1241     if (status != PEP_STATUS_OK)
  1242         goto pep_error;
  1243 
  1244     *dst = NULL;
  1245 
  1246     
  1247     PEP_STATUS _status = update_identity(session, target_id);
  1248     if (_status != PEP_STATUS_OK) {
  1249         status = _status;
  1250         goto pep_error;
  1251     }
  1252 
  1253     char* target_fpr = target_id->fpr;
  1254     if (!target_fpr)
  1255         return PEP_KEY_NOT_FOUND; // FIXME: Error condition
  1256         
  1257     keys = new_stringlist(target_fpr);
  1258 
  1259     
  1260     msg = clone_to_empty_message(src);
  1261     if (msg == NULL)
  1262         goto enomem;
  1263 
  1264     switch (enc_format) {
  1265         case PEP_enc_PGP_MIME:
  1266         case PEP_enc_PEP: // BUG: should be implemented extra
  1267             status = encrypt_PGP_MIME(session, src, keys, msg);
  1268             break;
  1269 
  1270         case PEP_enc_pieces:
  1271             status = encrypt_PGP_in_pieces(session, src, keys, msg);
  1272             break;
  1273 
  1274         /* case PEP_enc_PEP:
  1275             // TODO: implement
  1276             NOT_IMPLEMENTED */
  1277 
  1278         default:
  1279             assert(0);
  1280             status = PEP_ILLEGAL_VALUE;
  1281             goto pep_error;
  1282     }
  1283         
  1284     if (status == PEP_OUT_OF_MEMORY)
  1285         goto enomem;
  1286     
  1287     if (status != PEP_STATUS_OK)
  1288         goto pep_error;
  1289 
  1290      if (msg && msg->shortmsg == NULL) {
  1291          msg->shortmsg = strdup("pEp");
  1292          assert(msg->shortmsg);
  1293          if (msg->shortmsg == NULL)
  1294              goto enomem;
  1295      }
  1296 
  1297     *dst = msg;
  1298     return status;
  1299 
  1300 enomem:
  1301     status = PEP_OUT_OF_MEMORY;
  1302 
  1303 pep_error:
  1304     free_stringlist(keys);
  1305     free_message(msg);
  1306 
  1307     return status;
  1308 }
  1309 
  1310 static bool is_a_pEpmessage(const message *msg)
  1311 {
  1312     for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  1313         if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
  1314             return true;
  1315     }
  1316     return false;
  1317 }
  1318 
  1319 // update comm_type to pEp_ct_pEp if needed
  1320 
  1321 static PEP_STATUS _update_identity_for_incoming_message(
  1322         PEP_SESSION session,
  1323         const message *src
  1324     )
  1325 {
  1326     PEP_STATUS status;
  1327     if (src->from && src->from->address) {
  1328         status = update_identity(session, src->from);
  1329         if (status == PEP_STATUS_OK
  1330                 && is_a_pEpmessage(src)
  1331                 && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
  1332                 && src->from->comm_type != PEP_ct_pEp_unconfirmed
  1333                 && src->from->comm_type != PEP_ct_pEp)
  1334         {
  1335             src->from->comm_type |= PEP_ct_pEp_unconfirmed;
  1336             status = update_identity(session, src->from);
  1337         }
  1338         return status;
  1339     }
  1340     return PEP_ILLEGAL_VALUE;
  1341 }
  1342 
  1343 DYNAMIC_API PEP_STATUS _decrypt_message(
  1344         PEP_SESSION session,
  1345         message *src,
  1346         message **dst,
  1347         stringlist_t **keylist,
  1348         PEP_color *color,
  1349         PEP_decrypt_flags_t *flags, 
  1350         identity_list **private_il
  1351     )
  1352 {
  1353     PEP_STATUS status = PEP_STATUS_OK;
  1354     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  1355     message *msg = NULL;
  1356     char *ctext;
  1357     size_t csize;
  1358     char *ptext = NULL;
  1359     size_t psize;
  1360     stringlist_t *_keylist = NULL;
  1361 
  1362     assert(session);
  1363     assert(src);
  1364     assert(dst);
  1365     assert(keylist);
  1366     assert(color);
  1367     assert(flags);
  1368 
  1369     if (!(session && src && dst && keylist && color && flags))
  1370         return PEP_ILLEGAL_VALUE;
  1371 
  1372     *flags = 0;
  1373 
  1374     // Private key in unencrypted mail are ignored -> NULL
  1375     bool imported_keys = import_attached_keys(session, src, NULL);
  1376 
  1377     // Update src->from in case we just imported a key
  1378     // we would need to check signature
  1379     status = _update_identity_for_incoming_message(session, src);
  1380     if(status != PEP_STATUS_OK)
  1381         return status;
  1382 
  1383     PEP_cryptotech crypto = determine_encryption_format(src);
  1384 
  1385     *dst = NULL;
  1386     *keylist = NULL;
  1387     *color = PEP_rating_undefined;
  1388  
  1389     switch (src->enc_format) {
  1390         case PEP_enc_none:
  1391             *color = PEP_rating_unencrypted;
  1392             if (imported_keys)
  1393                 remove_attached_keys(src);
  1394             return PEP_UNENCRYPTED;
  1395 
  1396         case PEP_enc_PGP_MIME:
  1397             ctext = src->attachments->next->value;
  1398             csize = src->attachments->next->size;
  1399             break;
  1400 
  1401         case PEP_enc_pieces:
  1402             ctext = src->longmsg;
  1403             csize = strlen(ctext);
  1404             break;
  1405 
  1406         default:
  1407             NOT_IMPLEMENTED
  1408     }
  1409     status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1410                                                    csize, &ptext, &psize, &_keylist);
  1411     if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1412         goto pep_error;
  1413 
  1414     decrypt_status = status;
  1415 
  1416     bool imported_private_key_address = false; 
  1417 
  1418     if (ptext) {
  1419         switch (src->enc_format) {
  1420             case PEP_enc_PGP_MIME:
  1421                 status = mime_decode_message(ptext, psize, &msg);
  1422                 if (status != PEP_STATUS_OK)
  1423                     goto pep_error;
  1424                 break;
  1425 
  1426             case PEP_enc_pieces:
  1427                 msg = clone_to_empty_message(src);
  1428                 if (msg == NULL)
  1429                     goto enomem;
  1430 
  1431                 msg->longmsg = ptext;
  1432                 ptext = NULL;
  1433 
  1434                 bloblist_t *_m = msg->attachments;
  1435                 if (_m == NULL && src->attachments && src->attachments->value) {
  1436                     msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
  1437                     _m = msg->attachments;
  1438                 }
  1439 
  1440                 bloblist_t *_s;
  1441                 for (_s = src->attachments; _s; _s = _s->next) {
  1442                     if (_s->value == NULL && _s->size == 0){
  1443                         _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
  1444                         if (_m == NULL)
  1445                             goto enomem;
  1446 
  1447                     }
  1448                     else if (is_encrypted_attachment(_s)) {
  1449                         stringlist_t *_keylist = NULL;
  1450                         char *attctext  = _s->value;
  1451                         size_t attcsize = _s->size;
  1452 
  1453                         free(ptext);
  1454                         ptext = NULL;
  1455 
  1456                         status = decrypt_and_verify(session, attctext, attcsize,
  1457                                 &ptext, &psize, &_keylist);
  1458                         free_stringlist(_keylist);
  1459 
  1460                         if (ptext) {
  1461                             if (is_encrypted_html_attachment(_s)) {
  1462                                 msg->longmsg_formatted = ptext;
  1463                                 ptext = NULL;
  1464                             }
  1465                             else {
  1466                                 static const char * const mime_type = "application/octet-stream";
  1467                                 char * const filename =
  1468                                     without_double_ending(_s->filename);
  1469                                 if (filename == NULL)
  1470                                     goto enomem;
  1471 
  1472                                 _m = bloblist_add(_m, ptext, psize, mime_type,
  1473                                     filename);
  1474                                 free(filename);
  1475                                 if (_m == NULL)
  1476                                     goto enomem;
  1477 
  1478                                 ptext = NULL;
  1479 
  1480                                 if (msg->attachments == NULL)
  1481                                     msg->attachments = _m;
  1482                             }
  1483                         }
  1484                         else {
  1485                             char *copy = malloc(_s->size);
  1486                             assert(copy);
  1487                             if (copy == NULL)
  1488                                 goto enomem;
  1489                             memcpy(copy, _s->value, _s->size);
  1490                             _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1491                             if (_m == NULL)
  1492                                 goto enomem;
  1493                         }
  1494                     }
  1495                     else {
  1496                         char *copy = malloc(_s->size);
  1497                         assert(copy);
  1498                         if (copy == NULL)
  1499                             goto enomem;
  1500                         memcpy(copy, _s->value, _s->size);
  1501                         _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1502                         if (_m == NULL)
  1503                             goto enomem;
  1504                     }
  1505                 }
  1506 
  1507                 break;
  1508 
  1509             default:
  1510                 // BUG: must implement more
  1511                 NOT_IMPLEMENTED
  1512         }
  1513         
  1514         switch (src->enc_format) {
  1515             case PEP_enc_PGP_MIME:
  1516             case PEP_enc_pieces:
  1517                 status = copy_fields(msg, src);
  1518                 if (status != PEP_STATUS_OK)
  1519                     goto pep_error;
  1520 
  1521                 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
  1522                 {
  1523                     char * shortmsg;
  1524                     char * longmsg;
  1525 
  1526                     int r = separate_short_and_long(msg->longmsg, &shortmsg,
  1527                             &longmsg);
  1528                     if (r == -1)
  1529                         goto enomem;
  1530 
  1531                     free(msg->shortmsg);
  1532                     free(msg->longmsg);
  1533 
  1534                     msg->shortmsg = shortmsg;
  1535                     msg->longmsg = longmsg;
  1536                 }
  1537                 else {
  1538                     msg->shortmsg = strdup(src->shortmsg);
  1539                     assert(msg->shortmsg);
  1540                     if (msg->shortmsg == NULL)
  1541                         goto enomem;
  1542                 }
  1543                 break;
  1544 
  1545             default:
  1546                 // BUG: must implement more
  1547                 NOT_IMPLEMENTED
  1548         }
  1549        
  1550         // check for private key in decrypted message attachement while inporting
  1551         identity_list *_private_il = NULL;
  1552         imported_keys = import_attached_keys(session, msg, &_private_il);
  1553         if (_private_il && 
  1554             identity_list_length(_private_il) == 1 &&
  1555             _private_il->ident->address)
  1556         {
  1557             imported_private_key_address = true;
  1558         }
  1559 
  1560         if(private_il && imported_private_key_address){
  1561             *private_il = _private_il;
  1562         }else{
  1563             free_identity_list(_private_il);
  1564         }
  1565          
  1566         if(decrypt_status == PEP_DECRYPTED){
  1567 
  1568             // TODO optimize if import_attached_keys didn't import any key
  1569             
  1570             // In case message did decrypt, but no valid signature could be found
  1571             // then retry decrypt+verify after importing key.
  1572 
  1573             // Update msg->from in case we just imported a key
  1574             // we would need to check signature
  1575 
  1576             status = _update_identity_for_incoming_message(session, src);
  1577             if(status != PEP_STATUS_OK)
  1578                 goto pep_error;
  1579             
  1580             char *re_ptext = NULL;
  1581             size_t re_psize;
  1582             
  1583             free_stringlist(_keylist);
  1584             _keylist = NULL;
  1585 
  1586             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1587                 csize, &re_ptext, &re_psize, &_keylist);
  1588             
  1589             free(re_ptext);
  1590             
  1591             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1592                 goto pep_error;
  1593             
  1594             decrypt_status = status;
  1595         }
  1596         
  1597         *color = decrypt_color(decrypt_status);
  1598         
  1599         if (*color > PEP_rating_mistrust) {
  1600             PEP_color kl_color = PEP_rating_undefined;
  1601             
  1602             if (_keylist)
  1603                 kl_color = keylist_color(session, _keylist);
  1604             
  1605             if (kl_color <= PEP_rating_mistrust) {
  1606                 *color = kl_color;
  1607             }
  1608             else if (*color >= PEP_rating_reliable &&
  1609                      kl_color < PEP_rating_reliable) {
  1610                 *color = PEP_rating_unreliable;
  1611             }
  1612             else if (*color >= PEP_rating_reliable &&
  1613                      kl_color >= PEP_rating_reliable) {
  1614                 if (!(src->from && src->from->user_id && src->from->user_id[0])) {
  1615                     *color = PEP_rating_unreliable;
  1616                 }
  1617                 else {
  1618                     char *fpr = _keylist->value;
  1619                     pEp_identity *_from = new_identity(src->from->address, fpr,
  1620                                                        src->from->user_id, src->from->username);
  1621                     if (_from == NULL)
  1622                         goto enomem;
  1623                     status = update_identity(session, _from);
  1624                     if (_from->comm_type != PEP_ct_unknown)
  1625                         *color = _rating(_from->comm_type, PEP_rating_undefined);
  1626                     free_identity(_from);
  1627                     if (status != PEP_STATUS_OK)
  1628                         goto pep_error;
  1629                 }
  1630             }
  1631         }
  1632     }
  1633     else
  1634     {
  1635         *color = decrypt_color(decrypt_status);
  1636         goto pep_error;
  1637     }
  1638 
  1639     // Case of own key imported from own trusted message
  1640     if (// Message have been reliably decrypted 
  1641         msg &&
  1642         *color >= PEP_rating_green &&
  1643         imported_private_key_address &&
  1644         // to is [own]
  1645         msg->to->ident->user_id &&
  1646         strcmp(msg->to->ident->user_id, PEP_OWN_USERID) == 0 
  1647         )
  1648     {
  1649         *flags |= PEP_decrypt_flag_own_private_key;
  1650     }
  1651 
  1652     if (msg) {
  1653         decorate_message(msg, *color, _keylist);
  1654         if (imported_keys)
  1655             remove_attached_keys(msg);
  1656     }
  1657 
  1658     *dst = msg;
  1659     *keylist = _keylist;
  1660 
  1661     return PEP_STATUS_OK;
  1662 
  1663 enomem:
  1664     status = PEP_OUT_OF_MEMORY;
  1665 
  1666 pep_error:
  1667     free(ptext);
  1668     free_message(msg);
  1669     free_stringlist(_keylist);
  1670 
  1671     return status;
  1672 }
  1673 
  1674 DYNAMIC_API PEP_STATUS decrypt_message(
  1675         PEP_SESSION session,
  1676         message *src,
  1677         message **dst,
  1678         stringlist_t **keylist,
  1679         PEP_color *color,
  1680         PEP_decrypt_flags_t *flags 
  1681     )
  1682 {
  1683     return _decrypt_message( session, src, dst, keylist, color, flags, NULL );
  1684 }
  1685 
  1686 DYNAMIC_API PEP_STATUS own_message_private_key_details(
  1687         PEP_SESSION session,
  1688         message *msg,
  1689         pEp_identity **ident 
  1690     )
  1691 {
  1692     assert(session);
  1693     assert(msg);
  1694     assert(ident);
  1695 
  1696     if (!(session && msg && ident))
  1697         return PEP_ILLEGAL_VALUE;
  1698 
  1699     message *dst = NULL; 
  1700     stringlist_t *keylist = NULL;
  1701     PEP_color color;
  1702     PEP_decrypt_flags_t flags; 
  1703 
  1704     *ident = NULL;
  1705 
  1706     identity_list *private_il = NULL;
  1707     PEP_STATUS status = _decrypt_message(session, msg,  &dst, &keylist, &color, &flags, &private_il);
  1708 
  1709     if (status == PEP_STATUS_OK &&
  1710         flags & PEP_decrypt_flag_own_private_key &&
  1711         private_il)
  1712     {
  1713         *ident = identity_dup(private_il->ident);
  1714     }
  1715 
  1716     free_identity_list(private_il);
  1717     free_stringlist(keylist);
  1718     free_message(dst);
  1719 
  1720     return status;
  1721 
  1722 }
  1723 
  1724 static void _max_comm_type_from_identity_list(
  1725         identity_list *identities, 
  1726         PEP_SESSION session,
  1727         PEP_comm_type *max_comm_type,
  1728         bool *comm_type_determined
  1729     )
  1730 {
  1731     identity_list * il;
  1732     for (il = identities; il != NULL; il = il->next)
  1733     {
  1734         if (il->ident)
  1735         {
  1736             PEP_STATUS status = update_identity(session, il->ident);
  1737             if (status == PEP_STATUS_OK)
  1738             {
  1739                 *max_comm_type = _get_comm_type(session, *max_comm_type,
  1740                         il->ident);
  1741                 *comm_type_determined = true;
  1742             }
  1743         }
  1744     }
  1745 }
  1746 
  1747 DYNAMIC_API PEP_STATUS outgoing_message_color(
  1748         PEP_SESSION session,
  1749         message *msg,
  1750         PEP_color *color
  1751     )
  1752 {
  1753     PEP_STATUS status = PEP_STATUS_OK;
  1754     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1755     bool comm_type_determined = false;
  1756 
  1757     assert(session);
  1758     assert(msg);
  1759     assert(msg->from);
  1760     assert(msg->dir == PEP_dir_outgoing);
  1761     assert(color);
  1762 
  1763     if (!(session && msg && color))
  1764         return PEP_ILLEGAL_VALUE;
  1765 
  1766     if (msg->from == NULL || msg->dir != PEP_dir_outgoing)
  1767         return PEP_ILLEGAL_VALUE;
  1768 
  1769     *color = PEP_rating_undefined;
  1770 
  1771     status = myself(session, msg->from);
  1772     if (status != PEP_STATUS_OK)
  1773         return status;
  1774 
  1775     _max_comm_type_from_identity_list(msg->to, session,
  1776                                       &max_comm_type, &comm_type_determined);
  1777 
  1778     _max_comm_type_from_identity_list(msg->cc, session,
  1779                                       &max_comm_type, &comm_type_determined);
  1780         
  1781     _max_comm_type_from_identity_list(msg->bcc, session,
  1782                                       &max_comm_type, &comm_type_determined);
  1783 
  1784     if (comm_type_determined == false)
  1785         *color = PEP_rating_undefined;
  1786     else
  1787         *color = MAX(_rating(max_comm_type, PEP_rating_undefined),
  1788                 PEP_rating_unencrypted);
  1789 
  1790     return PEP_STATUS_OK;
  1791 }
  1792 
  1793 DYNAMIC_API PEP_STATUS identity_color(
  1794         PEP_SESSION session,
  1795         pEp_identity *ident,
  1796         PEP_color *color
  1797     )
  1798 {
  1799     PEP_STATUS status = PEP_STATUS_OK;
  1800 
  1801     assert(session);
  1802     assert(ident);
  1803     assert(color);
  1804 
  1805     if (!(session && ident && color))
  1806         return PEP_ILLEGAL_VALUE;
  1807 
  1808     if (ident->me)
  1809         status = myself(session, ident);
  1810     else
  1811         status = update_identity(session, ident);
  1812 
  1813     if (status == PEP_STATUS_OK)
  1814         *color = _rating(ident->comm_type, PEP_rating_undefined);
  1815 
  1816     return status;
  1817 }
  1818 
  1819 DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
  1820 {
  1821     PEP_STATUS status = PEP_STATUS_OK;
  1822 
  1823     assert(path);
  1824     if (path == NULL)
  1825         return PEP_ILLEGAL_VALUE;
  1826 
  1827     if (cryptotech[tech].binary_path == NULL)
  1828         *path = NULL;
  1829     else
  1830         status = cryptotech[tech].binary_path(path);
  1831 
  1832     return status;
  1833 }
  1834