src/message_api.c
author Volker Birk <vb@pep-project.org>
Thu, 28 May 2015 15:01:08 +0200
changeset 288 e60e0989931e
parent 285 2b2db7f21020
child 290 afa519200312
permissions -rw-r--r--
bugfix memory handling
     1 #include "pEp_internal.h"
     2 #include "message_api.h"
     3 
     4 #include "platform.h"
     5 #include "mime.h"
     6 
     7 #include <assert.h>
     8 #include <string.h>
     9 #include <stdlib.h>
    10 
    11 #ifndef MIN
    12 #define MIN(A, B) ((B) > (A) ? (A) : (B))
    13 #endif
    14 
    15 
    16 static bool string_equality(const char *s1, const char *s2)
    17 {
    18     if (s1 == NULL || s2 == NULL)
    19         return false;
    20 
    21     assert(s1 && s2);
    22 
    23     return strcmp(s1, s2) == 0;
    24 }
    25 
    26 static bool is_mime_type(const bloblist_t *bl, const char *mt)
    27 {
    28     assert(mt);
    29 
    30     return bl && string_equality(bl->mime_type, mt);
    31 }
    32 
    33 static bool is_fileending(const bloblist_t *bl, const char *fe)
    34 {
    35     assert(fe);
    36 
    37     if (bl == NULL || bl->filename == NULL)
    38         return false;
    39 
    40     assert(bl && bl->filename);
    41 
    42     size_t fe_len = strlen(fe);
    43     size_t fn_len = strlen(bl->filename);
    44 
    45     if (fn_len <= fe_len)
    46         return false;
    47 
    48     assert(fn_len > fe_len);
    49 
    50     return strcmp(bl->filename + (fn_len - fe_len), fe) == 0;
    51 }
    52 
    53 void import_attached_keys(PEP_SESSION session, const message *msg)
    54 {
    55     assert(session);
    56     assert(msg);
    57 
    58     bloblist_t *bl;
    59     for (bl = msg->attachments; bl && bl->data; bl = bl->next) {
    60         assert(bl && bl->data && bl->size);
    61 
    62         if (bl->mime_type == NULL ||
    63                     is_mime_type(bl, "application/octet-stream")) {
    64             if (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
    65                     is_fileending(bl, ".key") ||
    66                     string_equality(bl->filename, "key.asc"))
    67                 import_key(session, bl->data, bl->size);
    68         }
    69         else if (is_mime_type(bl, "application/pgp-keys")) {
    70             import_key(session, bl->data, bl->size);
    71         }
    72         else if (is_mime_type(bl, "text/plain")) {
    73             if (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
    74                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))
    75                 import_key(session, bl->data, bl->size);
    76         }
    77     }
    78 }
    79 
    80 void attach_own_key(PEP_SESSION session, message *msg)
    81 {
    82     char *keydata;
    83     size_t size;
    84     bloblist_t *bl;
    85 
    86     assert(session);
    87     assert(msg);
    88 
    89     if (msg->dir == PEP_dir_incoming)
    90         return;
    91 
    92     assert(msg->from && msg->from->fpr);
    93     if (msg->from == NULL || msg->from->fpr == NULL)
    94         return;
    95 
    96     PEP_STATUS status = export_key(session, msg->from->fpr, &keydata, &size);
    97     assert(status == PEP_STATUS_OK);
    98     if (status != PEP_STATUS_OK)
    99         return;
   100     assert(size);
   101 
   102     bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
   103             "pEp_key.asc");
   104     if (msg->attachments == NULL && bl)
   105         msg->attachments = bl;
   106 }
   107 
   108 static void add_opt_field(message *msg, const char *name, const char *value)
   109 {
   110     assert(msg);
   111 
   112     if (msg && name && value) {
   113         stringpair_t *pair = new_stringpair(name, value);
   114         if (pair == NULL)
   115             return;
   116 
   117         stringpair_list_t *field = stringpair_list_add(msg->opt_fields, pair);
   118         if (field == NULL)
   119             return;
   120 
   121         if (msg->opt_fields == NULL)
   122             msg->opt_fields = field;
   123     }
   124 }
   125 
   126 PEP_cryptotech determine_encryption_format(message *msg)
   127 {
   128     assert(msg);
   129 
   130     if (is_PGP_message_text(msg->longmsg)) {
   131         msg->enc_format = PEP_enc_pieces;
   132         return PEP_crypt_OpenPGP;
   133     }
   134     else if (msg->attachments && msg->attachments->next &&
   135             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
   136             is_PGP_message_text(msg->attachments->next->data)
   137         ) {
   138         msg->enc_format = PEP_enc_PGP_MIME;
   139         return PEP_crypt_OpenPGP;
   140     }
   141     else {
   142         msg->enc_format = PEP_enc_none;
   143         return PEP_crypt_none;
   144     }
   145 }
   146 
   147 static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
   148 {
   149     char * ptext;
   150 
   151     assert(shortmsg);
   152     assert(strcmp(shortmsg, "pEp") != 0);
   153 
   154     if (longmsg == NULL)
   155         longmsg = "";
   156 
   157     ptext = calloc(1, strlen(shortmsg) + strlen(longmsg) + 12);
   158     assert(ptext);
   159     if (ptext == NULL)
   160         return NULL;
   161 
   162     strcpy(ptext, "Subject: ");
   163     strcat(ptext, shortmsg);
   164     strcat(ptext, "\n\n");
   165     strcat(ptext, longmsg);
   166 
   167     return ptext;
   168 }
   169 
   170 static int seperate_short_and_long(const char *src, char **shortmsg, char **longmsg)
   171 {
   172     char *_shortmsg = NULL;
   173     char *_longmsg = NULL;
   174 
   175     assert(src);
   176     assert(shortmsg);
   177     assert(longmsg);
   178 
   179     *shortmsg = NULL;
   180     *longmsg = NULL;
   181 
   182     if (strncasecmp(src, "subject: ", 9) == 0) {
   183         char *line_end = strchr(src, '\n');
   184         
   185         if (line_end == NULL) {
   186             _shortmsg = strdup(src + 9);
   187             if (_shortmsg == NULL)
   188                 goto enomem;
   189             // _longmsg = NULL;
   190         }
   191         else {
   192             size_t n = line_end - src;
   193 
   194             if (*(line_end - 1) == '\r')
   195                 _shortmsg = strndup(src + 9, n - 10);
   196             else
   197                 _shortmsg = strndup(src + 9, n - 9);
   198 
   199             if (_shortmsg == NULL)
   200                 goto enomem;
   201 
   202             while (*(src + n) && (*(src + n) == '\n' || *(src + n) == '\r'))
   203                 ++n;
   204 
   205             if (*(src + n)) {
   206                 _longmsg = strdup(src + n);
   207                 if (_longmsg == NULL)
   208                     goto enomem;
   209             }
   210         }
   211     }
   212     else {
   213         _shortmsg = strdup("");
   214         if (_shortmsg == NULL)
   215             goto enomem;
   216         _longmsg = strdup(src);
   217         if (_longmsg == NULL)
   218             goto enomem;
   219     }
   220     
   221     *shortmsg = _shortmsg;
   222     *longmsg = _longmsg;
   223 
   224     return 0;
   225 
   226 enomem:
   227     free(_shortmsg);
   228     free(_longmsg);
   229 
   230     return -1;
   231 }
   232 
   233 static PEP_STATUS copy_fields(message *dst, const message *src)
   234 {
   235     assert(dst);
   236     assert(src);
   237 
   238     free_timestamp(dst->sent);
   239     dst->sent = NULL;
   240     if (src->sent) {
   241         dst->sent = timestamp_dup(src->sent);
   242         if (dst->sent == NULL)
   243             return PEP_OUT_OF_MEMORY;
   244     }
   245 
   246     free_timestamp(dst->recv);
   247     dst->recv = NULL;
   248     if (src->recv) {
   249         dst->recv = timestamp_dup(src->recv);
   250         if (dst->recv == NULL)
   251             return PEP_OUT_OF_MEMORY;
   252     }
   253 
   254     free_identity(dst->from);
   255     dst->from = NULL;
   256     if (src->from) {
   257         dst->from = identity_dup(src->from);
   258         if (dst->from == NULL)
   259             return PEP_OUT_OF_MEMORY;
   260     }
   261 
   262     free_identity_list(dst->to);
   263     dst->to = NULL;
   264     if (src->to && src->to->ident) {
   265         dst->to = identity_list_dup(src->to);
   266         if (dst->to == NULL)
   267             return PEP_OUT_OF_MEMORY;
   268     }
   269 
   270     free_identity(dst->recv_by);
   271     dst->recv_by = NULL;
   272     if (src->recv_by) {
   273         dst->recv_by = identity_dup(src->recv_by);
   274         if (dst->recv_by == NULL)
   275             return PEP_OUT_OF_MEMORY;
   276     }
   277 
   278     free_identity_list(dst->cc);
   279     dst->cc = NULL;
   280     if (src->cc && src->cc->ident) {
   281         dst->cc = identity_list_dup(src->cc);
   282         if (dst->cc == NULL)
   283             return PEP_OUT_OF_MEMORY;
   284     }
   285 
   286     free_identity_list(dst->bcc);
   287     dst->bcc = NULL;
   288     if (src->bcc && src->bcc->ident) {
   289         dst->bcc = identity_list_dup(src->bcc);
   290         if (dst->bcc == NULL)
   291             return PEP_OUT_OF_MEMORY;
   292     }
   293 
   294     free_identity_list(dst->reply_to);
   295     dst->reply_to = NULL;
   296     if (src->reply_to && src->reply_to->ident) {
   297         dst->reply_to = identity_list_dup(src->reply_to);
   298         if (dst->reply_to == NULL)
   299             return PEP_OUT_OF_MEMORY;
   300     }
   301 
   302     free_stringlist(dst->in_reply_to);
   303     dst->in_reply_to = NULL;
   304     if (src->in_reply_to && src->in_reply_to->value) {
   305         dst->in_reply_to = stringlist_dup(src->in_reply_to);
   306         if (dst->in_reply_to == NULL)
   307             return PEP_OUT_OF_MEMORY;
   308     }
   309 
   310     free_stringlist(dst->references);
   311     dst->references = NULL;
   312     if (src->references) {
   313         dst->references = stringlist_dup(src->references);
   314         if (dst->references == NULL)
   315             return PEP_OUT_OF_MEMORY;
   316     }
   317 
   318     free_stringlist(dst->keywords);
   319     dst->keywords = NULL;
   320     if (src->keywords && src->keywords->value) {
   321         dst->keywords = stringlist_dup(src->keywords);
   322         if (dst->keywords == NULL)
   323             return PEP_OUT_OF_MEMORY;
   324     }
   325 
   326     free(dst->comments);
   327     dst->comments = NULL;
   328     if (src->comments) {
   329         dst->comments = strdup(src->comments);
   330         assert(dst->comments);
   331         if (dst->comments == NULL)
   332             return PEP_OUT_OF_MEMORY;
   333     }
   334 
   335     return PEP_STATUS_OK;
   336 }
   337 
   338 static message * clone_to_empty_message(const message * src)
   339 {
   340     PEP_STATUS status;
   341     message * msg = NULL;
   342 
   343     assert(src);
   344 
   345     msg = calloc(1, sizeof(message));
   346     assert(msg);
   347     if (msg == NULL)
   348         goto enomem;
   349 
   350     msg->dir = src->dir;
   351 
   352     status = copy_fields(msg, src);
   353     if (status != PEP_STATUS_OK)
   354         goto enomem;
   355 
   356     return msg;
   357 
   358 enomem:
   359     free_message(msg);
   360     return NULL;
   361 }
   362 
   363 static PEP_STATUS encrypt_PGP_MIME(
   364         PEP_SESSION session,
   365         const message *src,
   366         stringlist_t *keys,
   367         message *dst
   368     )
   369 {
   370     PEP_STATUS status = PEP_STATUS_OK;
   371     bool free_ptext = false;
   372     char *ptext;
   373     char *ctext;
   374     char *_ctext = NULL;
   375     char *mimetext = NULL;
   376     size_t csize;
   377     assert(dst->longmsg == NULL);
   378     dst->enc_format = PEP_enc_PGP_MIME;
   379 
   380     if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
   381         ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   382         if (ptext == NULL)
   383             goto enomem;
   384         free_ptext = true;
   385     }
   386     else if (src->longmsg) {
   387         ptext = src->longmsg;
   388     }
   389     else {
   390         ptext = "pEp";
   391     }
   392 
   393     message *_src = calloc(1, sizeof(message));
   394     assert(_src);
   395     if (_src == NULL)
   396         goto enomem;
   397     _src->longmsg = ptext;
   398     _src->longmsg_formatted = src->longmsg_formatted;
   399     _src->attachments = src->attachments;
   400     _src->enc_format = PEP_enc_none;
   401     status = mime_encode_message(_src, true, &mimetext);
   402     assert(status == PEP_STATUS_OK);
   403     if (free_ptext)
   404         free(ptext);
   405     free(_src);
   406     assert(mimetext);
   407     if (mimetext == NULL)
   408         goto pep_error;
   409 
   410     status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
   411             &ctext, &csize);
   412     free(mimetext);
   413     if (ctext == NULL)
   414         goto pep_error;
   415 
   416     dst->longmsg = strdup("this message was encrypted with p≡p "
   417             "http://pEp-project.org");
   418     if (dst->longmsg == NULL)
   419         goto enomem;
   420 
   421     char *v = strdup("Version: 1");
   422     if (v == NULL)
   423         goto enomem;
   424 
   425     bloblist_t *_a = new_bloblist(v, 11, "application/pgp-encrypted", NULL);
   426     if (_a == NULL)
   427         goto enomem;
   428     dst->attachments = _a;
   429 
   430     _ctext = malloc(csize);
   431     assert(_ctext);
   432     if (_ctext == NULL)
   433         goto enomem;
   434     memcpy(_ctext, ctext, csize);
   435 
   436     _a = bloblist_add(_a, _ctext, csize, "application/octet-stream",
   437             "msg.asc");
   438     if (_a == NULL)
   439         goto enomem;
   440 
   441     return PEP_STATUS_OK;
   442 
   443 enomem:
   444     status = PEP_OUT_OF_MEMORY;
   445 
   446 pep_error:
   447     if (free_ptext)
   448         free(ptext);
   449     free(_ctext);
   450     return status;
   451 }
   452 
   453 static PEP_STATUS encrypt_PGP_in_pieces(
   454         PEP_SESSION session,
   455         const message *src,
   456         stringlist_t *keys,
   457         message *dst
   458     )
   459 {
   460     PEP_STATUS status = PEP_STATUS_OK;
   461     char *ctext;
   462     size_t csize;
   463 
   464     assert(dst->longmsg == NULL);
   465     assert(dst->attachments == NULL);
   466 
   467     dst->enc_format = PEP_enc_pieces;
   468 
   469     if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
   470         char *ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   471         if (ptext == NULL)
   472             goto enomem;
   473 
   474         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   475                 &csize);
   476         free(ptext);
   477         if (ctext) {
   478             dst->longmsg = strndup(ctext, csize);
   479             assert(dst->longmsg);
   480             if (dst->longmsg == NULL)
   481                 goto enomem;
   482         }
   483         else {
   484             goto pep_error;
   485         }
   486     }
   487     else if (src->longmsg) {
   488         char *ptext = src->longmsg;
   489         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   490                 &csize);
   491         if (ctext) {
   492             dst->longmsg = strndup(ctext, csize);
   493             assert(dst->longmsg);
   494             if (dst->longmsg == NULL)
   495                 goto enomem;
   496         }
   497         else {
   498             goto pep_error;
   499         }
   500     }
   501     else {
   502         dst->longmsg = strdup("");
   503         if (dst->longmsg == NULL)
   504             goto enomem;
   505     }
   506 
   507     if (src->longmsg_formatted) {
   508         char *ptext = src->longmsg_formatted;
   509         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   510                 &csize);
   511         if (ctext) {
   512             char *_ctext = malloc(csize);
   513             assert(_ctext);
   514             if (_ctext == NULL)
   515                 goto enomem;
   516             memcpy(_ctext, ctext, csize);
   517 
   518             bloblist_t *_a = bloblist_add(dst->attachments, _ctext, csize,
   519                     "application/octet-stream", "PGPexch.htm.pgp");
   520             if (_a == NULL)
   521                 goto enomem;
   522             if (dst->attachments == NULL)
   523                 dst->attachments = _a;
   524         }
   525         else {
   526             goto pep_error;
   527         }
   528     }
   529 
   530     if (src->attachments) {
   531         if (dst->attachments == NULL) {
   532             dst->attachments = new_bloblist(NULL, 0, NULL, NULL);
   533             if (dst->attachments == NULL)
   534                 goto enomem;
   535         }
   536 
   537         bloblist_t *_s = src->attachments;
   538         bloblist_t *_d = dst->attachments;
   539 
   540         for (int n = 0; _s && _s->data; _s = _s->next) {
   541             int psize = _s->size;
   542             char *ptext = _s->data;
   543             status = encrypt_and_sign(session, keys, ptext, psize, &ctext,
   544                     &csize);
   545             if (ctext) {
   546                 char *filename = NULL;
   547 
   548                 if (_s->filename) {
   549                     size_t len = strlen(_s->filename);
   550                     filename = calloc(1, len + 5);
   551                     if (filename == NULL)
   552                         goto enomem;
   553 
   554                     strcpy(filename, _s->filename);
   555                     strcpy(filename + len, ".pgp");
   556                 }
   557                 else {
   558                     filename = calloc(1, 20);
   559                     if (filename == NULL)
   560                         goto enomem;
   561 
   562                     ++n;
   563                     n &= 0xffff;
   564                     snprintf(filename, 20, "Attachment%d.pgp", n);
   565                 }
   566 
   567                 char *_ctext = malloc(csize);
   568                 assert(_ctext);
   569                 if (_ctext == NULL)
   570                     goto enomem;
   571                 memcpy(_ctext, ctext, csize);
   572 
   573                 _d = bloblist_add(_d, _ctext, csize, "application/octet-stream",
   574                         filename);
   575                 if (_d == NULL)
   576                     goto enomem;
   577             }
   578             else {
   579                 goto pep_error;
   580             }
   581         }
   582     }
   583 
   584     return PEP_STATUS_OK;
   585 
   586 enomem:
   587     status = PEP_OUT_OF_MEMORY;
   588 
   589 pep_error:
   590     return status;
   591 }
   592 
   593 DYNAMIC_API PEP_STATUS encrypt_message(
   594         PEP_SESSION session,
   595         message *src,
   596         stringlist_t * extra,
   597         message **dst,
   598         PEP_enc_format enc_format
   599     )
   600 {
   601     PEP_STATUS status = PEP_STATUS_OK;
   602     message * msg = NULL;
   603     stringlist_t * keys = NULL;
   604 
   605     assert(session);
   606     assert(src);
   607     assert(dst);
   608     assert(enc_format != PEP_enc_none);
   609 
   610     if (!(session && src && dst && enc_format != PEP_enc_none))
   611         return PEP_ILLEGAL_VALUE;
   612 
   613     determine_encryption_format(src);
   614     if (src->enc_format != PEP_enc_none)
   615         return PEP_ILLEGAL_VALUE;
   616 
   617     *dst = NULL;
   618 
   619     status = myself(session, src->from);
   620     if (status != PEP_STATUS_OK)
   621         goto pep_error;
   622 
   623     msg = clone_to_empty_message(src);
   624     if (msg == NULL)
   625         goto enomem;
   626 
   627     keys = new_stringlist(src->from->fpr);
   628     if (keys == NULL)
   629         goto enomem;
   630 
   631     stringlist_t *_k = keys;
   632 
   633     if (extra) {
   634         _k = stringlist_append(_k, extra);
   635         if (_k == NULL)
   636             goto enomem;
   637     }
   638 
   639     bool dest_keys_found = false;
   640     identity_list * _il;
   641     for (_il = msg->to; _il && _il->ident; _il = _il->next) {
   642         PEP_STATUS status = update_identity(session, _il->ident);
   643         if (status != PEP_STATUS_OK)
   644             goto pep_error;
   645 
   646         if (_il->ident->fpr) {
   647             dest_keys_found = true;
   648             _k = stringlist_add(_k, _il->ident->fpr);
   649             if (_k == NULL)
   650                 goto enomem;
   651         }
   652         else
   653             status = PEP_KEY_NOT_FOUND;
   654     }
   655 
   656     if (dest_keys_found) {
   657         switch (enc_format) {
   658         case PEP_enc_PGP_MIME:
   659             status = encrypt_PGP_MIME(session, src, keys, msg);
   660             if (status != PEP_STATUS_OK)
   661                 goto pep_error;
   662             break;
   663 
   664         case PEP_enc_pieces:
   665             status = encrypt_PGP_in_pieces(session, src, keys, msg);
   666             if (status != PEP_STATUS_OK)
   667                 goto pep_error;
   668             attach_own_key(session, msg);
   669             break;
   670 
   671         case PEP_enc_PEP:
   672             // TODO: implement
   673             NOT_IMPLEMENTED
   674 
   675         default:
   676             assert(0);
   677             status = PEP_ILLEGAL_VALUE;
   678             goto pep_error;
   679         }
   680     }
   681     else {
   682         attach_own_key(session, msg);
   683     }
   684 
   685     free_stringlist(keys);
   686 
   687     if (msg->shortmsg == NULL)
   688         msg->shortmsg = strdup("pEp");
   689 
   690     *dst = msg;
   691     return PEP_STATUS_OK;
   692 
   693 enomem:
   694     status = PEP_OUT_OF_MEMORY;
   695 
   696 pep_error:
   697     free_stringlist(keys);
   698     free_message(msg);
   699 
   700     return status;
   701 }
   702 
   703 static bool is_encrypted_attachment(const bloblist_t *blob)
   704 {
   705     char *ext;
   706  
   707     assert(blob);
   708 
   709     if (blob->filename == NULL)
   710         return false;
   711 
   712     ext = strrchr(blob->filename, '.');
   713     if (ext == NULL)
   714         return false;
   715 
   716     if (strcmp(blob->mime_type, "application/octet-stream")) {
   717         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
   718                 strcmp(ext, ".asc") == 0)
   719             return true;
   720     }
   721     else if (strcmp(blob->mime_type, "text/plain")) {
   722         if (strcmp(ext, ".asc") == 0)
   723             return true;
   724     }
   725 
   726     return false;
   727 }
   728 
   729 static bool is_encrypted_html_attachment(const bloblist_t *blob)
   730 {
   731     assert(blob);
   732     assert(blob->filename);
   733 
   734     if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
   735         if (strcmp(blob->filename + 11, ".pgp") == 0 ||
   736                 strcmp(blob->filename + 11, ".asc") == 0)
   737             return true;
   738     }
   739 
   740     return false;
   741 }
   742 
   743 static char * without_double_ending(const char *filename)
   744 {
   745     char *ext;
   746 
   747     assert(filename);
   748 
   749     ext = strrchr(filename, '.');
   750     if (ext == NULL)
   751         return NULL;
   752 
   753     return strndup(filename, ext - filename);
   754 }
   755 
   756 static PEP_color decrypt_color(PEP_STATUS status)
   757 {
   758     switch (status) {
   759         case PEP_UNENCRYPTED:
   760         case PEP_VERIFIED:
   761         case PEP_VERIFY_NO_KEY:
   762         case PEP_VERIFIED_AND_TRUSTED:
   763             return PEP_rating_unencrypted;
   764 
   765         case PEP_DECRYPTED:
   766             return PEP_rating_unreliable;
   767 
   768         case PEP_DECRYPTED_AND_VERIFIED:
   769             return PEP_rating_reliable;
   770 
   771         case PEP_DECRYPT_NO_KEY:
   772             return PEP_rating_have_no_key;
   773 
   774         case PEP_DECRYPT_WRONG_FORMAT:
   775         case PEP_CANNOT_DECRYPT_UNKNOWN:
   776             return PEP_rating_cannot_decrypt;
   777 
   778         default:
   779             return PEP_rating_undefined;
   780     }
   781 }
   782 
   783 static PEP_color _rating(PEP_comm_type ct)
   784 {
   785     if (ct == PEP_ct_unknown)
   786         return PEP_rating_undefined;
   787 
   788     else if (ct == PEP_ct_compromized)
   789         return PEP_rating_under_attack;
   790 
   791     else if (ct >= PEP_ct_confirmed_enc_anon)
   792         return PEP_rating_trusted_and_anonymized;
   793 
   794     else if (ct >= PEP_ct_strong_encryption)
   795         return PEP_rating_trusted;
   796 
   797     else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
   798         return PEP_rating_reliable;
   799     
   800     else if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel)
   801         return PEP_rating_unencrypted;
   802 
   803     else
   804         return PEP_rating_unreliable;
   805 }
   806 
   807 static PEP_color key_color(PEP_SESSION session, const char *fpr) {
   808     PEP_comm_type comm_type = PEP_ct_unknown;
   809 
   810     assert(session);
   811     assert(fpr);
   812 
   813     PEP_STATUS status = get_key_rating(session, fpr, &comm_type);
   814     if (status != PEP_STATUS_OK)
   815         return PEP_rating_undefined;
   816 
   817     return _rating(comm_type);
   818 }
   819 
   820 static PEP_color keylist_color(PEP_SESSION session, stringlist_t *keylist)
   821 {
   822     PEP_color color = PEP_rating_reliable;
   823 
   824     assert(keylist && keylist->value);
   825     if (keylist == NULL || keylist->value == NULL)
   826         return PEP_rating_unencrypted;
   827 
   828     stringlist_t *_kl;
   829     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   830         PEP_comm_type ct;
   831         PEP_STATUS status;
   832 
   833         color = key_color(session, _kl->value);
   834         if (color == PEP_rating_under_attack)
   835             return PEP_rating_under_attack;
   836 
   837         if (color >= PEP_rating_reliable) {
   838             status = least_trust(session, _kl->value, &ct);
   839             if (status != PEP_STATUS_OK)
   840                 return PEP_rating_undefined;
   841             if (ct == PEP_ct_unknown)
   842                 color = PEP_rating_unreliable;
   843             else
   844                 color = _rating(ct);
   845         }
   846     }
   847 
   848     return color;
   849 }
   850 
   851 static char * keylist_to_string(const stringlist_t *keylist)
   852 {
   853     if (keylist) {
   854         size_t size = stringlist_length(keylist);
   855 
   856         const stringlist_t *_kl;
   857         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   858             size += strlen(_kl->value);
   859         }
   860 
   861         char *result = calloc(1, size);
   862         if (result == NULL)
   863             return NULL;
   864 
   865         char *_r = result;
   866         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   867             _r = stpcpy(_r, _kl->value);
   868             if (_kl->next && _kl->next->value)
   869                 _r = stpcpy(_r, ",");
   870         }
   871 
   872         return result;
   873     }
   874     else {
   875         return NULL;
   876     }
   877 }
   878 
   879 static const char * color_to_string(PEP_color color)
   880 {
   881     switch (color) {
   882         case PEP_rating_cannot_decrypt:
   883             return "cannot_decrypt";
   884         case PEP_rating_have_no_key:
   885             return "have_no_key";
   886         case PEP_rating_unencrypted:
   887             return "unencrypted";
   888         case PEP_rating_unreliable:
   889             return "unreliable";
   890         case PEP_rating_reliable:
   891             return "reliable";
   892         case PEP_rating_trusted:
   893             return "trusted";
   894         case PEP_rating_trusted_and_anonymized:
   895             return "trusted_and_anonymized";
   896         case PEP_rating_fully_anonymous:
   897             return "fully_anonymous";
   898         case PEP_rating_under_attack:
   899             return "unter_attack";
   900         case PEP_rating_b0rken:
   901             return "b0rken";
   902         default:
   903             return "undefined";
   904     }
   905 }
   906 
   907 static void decorate_message(
   908         message *msg,
   909         stringlist_t *keylist,
   910         PEP_color color
   911     )
   912 {
   913     assert(msg);
   914 
   915     add_opt_field(msg, "X-pEp-Version", "1.0");
   916     add_opt_field(msg, "X-EncStatus", color_to_string(color));
   917 
   918     char *_keylist = keylist_to_string(keylist);
   919     add_opt_field(msg, "X-KeyList", _keylist);
   920     free(_keylist);
   921 }
   922 
   923 DYNAMIC_API PEP_STATUS decrypt_message(
   924         PEP_SESSION session,
   925         message *src,
   926         message **dst,
   927         stringlist_t **keylist,
   928         PEP_color *color
   929     )
   930 {
   931     PEP_STATUS status = PEP_STATUS_OK;
   932     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
   933     message *msg = NULL;
   934     char *ctext;
   935     size_t csize;
   936     char *ptext = NULL;
   937     size_t psize;
   938     stringlist_t *_keylist = NULL;
   939 
   940     assert(session);
   941     assert(src);
   942     assert(dst);
   943     assert(keylist);
   944     assert(color);
   945 
   946     if (!(session && src && dst && keylist && color))
   947         return PEP_ILLEGAL_VALUE;
   948 
   949     import_attached_keys(session, src);
   950     PEP_cryptotech crypto = determine_encryption_format(src);
   951 
   952     *dst = NULL;
   953     *keylist = NULL;
   954     *color = PEP_rating_undefined;
   955  
   956     switch (src->enc_format) {
   957         case PEP_enc_none:
   958             status = PEP_UNENCRYPTED;
   959             break;
   960 
   961         case PEP_enc_PGP_MIME:
   962             ctext = src->attachments->next->data;
   963             csize = src->attachments->next->size;
   964 
   965             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
   966                     csize, &ptext, &psize, &_keylist);
   967             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
   968                 goto pep_error;
   969             decrypt_status = status;
   970             break;
   971 
   972         case PEP_enc_pieces:
   973             ctext = src->longmsg;
   974             csize = strlen(ctext);
   975 
   976             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
   977                     csize, &ptext, &psize, &_keylist);
   978             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
   979                 goto pep_error;
   980             decrypt_status = status;
   981             break;
   982 
   983         default:
   984             NOT_IMPLEMENTED
   985     }
   986 
   987     *color = decrypt_color(status);
   988 
   989     if (*color != PEP_rating_under_attack) {
   990         PEP_color kl_color = PEP_rating_undefined;
   991 
   992         if (_keylist)
   993             kl_color = keylist_color(session, _keylist);
   994 
   995         if (kl_color == PEP_rating_under_attack)
   996             *color = PEP_rating_under_attack;
   997 
   998         else if (*color >= PEP_rating_reliable &&
   999                 kl_color < PEP_rating_reliable)
  1000             *color = PEP_rating_unreliable;
  1001 
  1002         else if (*color >= PEP_rating_reliable &&
  1003                 kl_color >= PEP_rating_trusted)
  1004             *color = kl_color;
  1005     }
  1006 
  1007     if (ptext) {
  1008         switch (src->enc_format) {
  1009             case PEP_enc_PGP_MIME:
  1010                 status = mime_decode_message(ptext, psize, &msg);
  1011                 if (status != PEP_STATUS_OK)
  1012                     goto pep_error;
  1013                 break;
  1014 
  1015             case PEP_enc_pieces:
  1016                 msg = clone_to_empty_message(src);
  1017                 if (msg == NULL)
  1018                     goto enomem;
  1019 
  1020                 msg->longmsg = strdup(ptext);
  1021                 if (msg->longmsg == NULL)
  1022                     goto enomem;
  1023 
  1024                 bloblist_t *_m = msg->attachments;
  1025                 bloblist_t *_s;
  1026                 for (_s = src->attachments; _s; _s = _s->next) {
  1027                     if (is_encrypted_attachment(_s)) {
  1028                         stringlist_t *_keylist = NULL;
  1029                         ctext = _s->data;
  1030                         csize = _s->size;
  1031 
  1032                         status = decrypt_and_verify(session, ctext, csize,
  1033                                 &ptext, &psize, &_keylist);
  1034                         if (ptext == NULL)
  1035                             goto pep_error;
  1036                         free_stringlist(_keylist);
  1037 
  1038                         if (is_encrypted_html_attachment(_s)) {
  1039                             msg->longmsg_formatted = strdup(ptext);
  1040                             if (msg->longmsg_formatted == NULL)
  1041                                 goto pep_error;
  1042                         }
  1043                         else {
  1044                             char * mime_type = "application/octet-stream";
  1045                             char * filename =
  1046                                     without_double_ending(_s->filename);
  1047                             if (filename == NULL)
  1048                                 goto enomem;
  1049 
  1050                             char *_ptext = malloc(psize);
  1051                             assert(_ptext);
  1052                             if (_ptext == NULL)
  1053                                 goto enomem;
  1054                             memcpy(_ptext, ptext, psize);
  1055 
  1056                             _m = bloblist_add(_m, _ptext, psize, mime_type,
  1057                                     filename);
  1058                             if (_m == NULL)
  1059                                 goto enomem;
  1060 
  1061                            if (msg->attachments == NULL)
  1062                                 msg->attachments = _m;
  1063                         }
  1064                     }
  1065                 }
  1066 
  1067                 break;
  1068 
  1069             default:
  1070                 // BUG: must implement more
  1071                 NOT_IMPLEMENTED
  1072         }
  1073 
  1074         switch (src->enc_format) {
  1075             case PEP_enc_PGP_MIME:
  1076             case PEP_enc_pieces:
  1077                 status = copy_fields(msg, src);
  1078                 if (status != PEP_STATUS_OK)
  1079                     goto pep_error;
  1080 
  1081                 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
  1082                 {
  1083                     char * shortmsg;
  1084                     char * longmsg;
  1085 
  1086                     int r = seperate_short_and_long(msg->longmsg, &shortmsg,
  1087                             &longmsg);
  1088                     if (r == -1)
  1089                         goto enomem;
  1090 
  1091                     free(msg->shortmsg);
  1092                     free(msg->longmsg);
  1093 
  1094                     msg->shortmsg = shortmsg;
  1095                     msg->longmsg = longmsg;
  1096                 }
  1097                 else {
  1098                     msg->shortmsg = strdup(src->shortmsg);
  1099                     if (msg->shortmsg == NULL)
  1100                         goto enomem;
  1101                 }
  1102                 break;
  1103 
  1104             default:
  1105                 // BUG: must implement more
  1106                 NOT_IMPLEMENTED
  1107         }
  1108 
  1109         import_attached_keys(session, msg);
  1110     }
  1111 
  1112     if (msg)
  1113         decorate_message(msg, _keylist, *color);
  1114 
  1115     *dst = msg;
  1116     *keylist = _keylist;
  1117 
  1118     return PEP_STATUS_OK;
  1119 
  1120 enomem:
  1121     status = PEP_OUT_OF_MEMORY;
  1122 
  1123 pep_error:
  1124     free_message(msg);
  1125     free_stringlist(_keylist);
  1126 
  1127     return status;
  1128 }
  1129 
  1130 static PEP_comm_type _get_comm_type(
  1131         PEP_SESSION session,
  1132         PEP_comm_type max_comm_type,
  1133         pEp_identity *ident
  1134     )
  1135 {
  1136     PEP_STATUS status = update_identity(session, ident);
  1137 
  1138     if (max_comm_type == PEP_ct_compromized)
  1139         return PEP_ct_compromized;
  1140 
  1141     if (status == PEP_STATUS_OK) {
  1142         if (ident->comm_type == PEP_ct_compromized)
  1143             return PEP_ct_compromized;
  1144         else
  1145             return MIN(max_comm_type, ident->comm_type);
  1146     }
  1147     else {
  1148         return PEP_ct_unknown;
  1149     }
  1150 }
  1151 
  1152 DYNAMIC_API PEP_STATUS outgoing_message_color(
  1153         PEP_SESSION session,
  1154         message *msg,
  1155         PEP_color *color
  1156     )
  1157 {
  1158     PEP_STATUS status = PEP_STATUS_OK;
  1159     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1160     bool comm_type_determined = false;
  1161     identity_list * il;
  1162 
  1163     assert(session);
  1164     assert(msg);
  1165     assert(msg->from);
  1166     assert(msg->dir == PEP_dir_outgoing);
  1167     assert(color);
  1168 
  1169     if (!(session && msg && color))
  1170         return PEP_ILLEGAL_VALUE;
  1171 
  1172     if (msg->from == NULL || msg->dir != PEP_dir_outgoing)
  1173         return PEP_ILLEGAL_VALUE;
  1174 
  1175     *color = PEP_rating_undefined;
  1176 
  1177     assert(msg->from);
  1178     if (msg->from == NULL)
  1179         return PEP_ILLEGAL_VALUE;
  1180 
  1181     status = myself(session, msg->from);
  1182     if (status != PEP_STATUS_OK)
  1183         return status;
  1184 
  1185     for (il = msg->to; il != NULL; il = il->next) {
  1186         if (il->ident) {
  1187             max_comm_type = _get_comm_type(session, max_comm_type,
  1188                     il->ident);
  1189             comm_type_determined = true;
  1190         }
  1191     }
  1192 
  1193     for (il = msg->cc; il != NULL; il = il->next) {
  1194         if (il->ident) {
  1195             max_comm_type = _get_comm_type(session, max_comm_type,
  1196                     il->ident);
  1197             comm_type_determined = true;
  1198         }
  1199     }
  1200 
  1201     if (comm_type_determined == false)
  1202         *color = PEP_rating_undefined;
  1203     else
  1204         *color = _rating(max_comm_type);
  1205 
  1206     return PEP_STATUS_OK;
  1207 }
  1208 
  1209 DYNAMIC_API PEP_STATUS identity_color(
  1210         PEP_SESSION session,
  1211         pEp_identity *ident,
  1212         PEP_color *color
  1213     )
  1214 {
  1215     PEP_STATUS status = PEP_STATUS_OK;
  1216 
  1217     assert(session);
  1218     assert(ident);
  1219     assert(color);
  1220 
  1221     if (!(session && ident && color))
  1222         return PEP_ILLEGAL_VALUE;
  1223 
  1224     if (ident->me)
  1225         status = myself(session, ident);
  1226     else
  1227         status = update_identity(session, ident);
  1228 
  1229     if (status == PEP_STATUS_OK)
  1230         *color = _rating(ident->comm_type);
  1231 
  1232     return status;
  1233 }
  1234