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