src/message_api.c
author Volker Birk <vb@pep-project.org>
Mon, 04 May 2015 13:34:06 +0200
changeset 236 154a7077dbb7
parent 235 8b7468ca8034
child 237 62e817a8ea83
permissions -rw-r--r--
attach own key
     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 (bl)
   105         msg->attachments = bl;
   106 }
   107 
   108 static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
   109 {
   110     char * ptext;
   111 
   112     assert(shortmsg);
   113     assert(strcmp(shortmsg, "pEp") != 0);
   114 
   115     if (longmsg == NULL)
   116         longmsg = "";
   117 
   118     ptext = calloc(1, strlen(shortmsg) + strlen(longmsg) + 12);
   119     assert(ptext);
   120     if (ptext == NULL)
   121         return NULL;
   122 
   123     strcpy(ptext, "Subject: ");
   124     strcat(ptext, shortmsg);
   125     strcat(ptext, "\n\n");
   126     strcat(ptext, longmsg);
   127 
   128     return ptext;
   129 }
   130 
   131 static int seperate_short_and_long(const char *src, char **shortmsg, char **longmsg)
   132 {
   133     char *_shortmsg = NULL;
   134     char *_longmsg = NULL;
   135 
   136     assert(src);
   137     assert(shortmsg);
   138     assert(longmsg);
   139 
   140     *shortmsg = NULL;
   141     *longmsg = NULL;
   142 
   143     if (strncasecmp(src, "subject: ", 9) == 0) {
   144         char *line_end = strchr(src, '\n');
   145         
   146         if (line_end == NULL) {
   147             _shortmsg = strdup(src + 9);
   148             if (_shortmsg == NULL)
   149                 goto enomem;
   150             // _longmsg = NULL;
   151         }
   152         else {
   153             size_t n = line_end - src;
   154 
   155             if (*(line_end - 1) == '\r')
   156                 _shortmsg = strndup(src + 9, n - 10);
   157             else
   158                 _shortmsg = strndup(src + 9, n - 9);
   159 
   160             if (_shortmsg == NULL)
   161                 goto enomem;
   162 
   163             while (*(src + n) && (*(src + n) == '\n' || *(src + n) == '\r'))
   164                 ++n;
   165 
   166             if (*(src + n)) {
   167                 _longmsg = strdup(src + n);
   168                 if (_longmsg == NULL)
   169                     goto enomem;
   170             }
   171         }
   172     }
   173     else {
   174         _shortmsg = strdup("");
   175         if (_shortmsg == NULL)
   176             goto enomem;
   177         _longmsg = strdup(src);
   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     free_timestamp(dst->sent);
   200     dst->sent = NULL;
   201     if (src->sent) {
   202         dst->sent = timestamp_dup(src->sent);
   203         if (dst->sent == NULL)
   204             return PEP_OUT_OF_MEMORY;
   205     }
   206 
   207     free_timestamp(dst->recv);
   208     dst->recv = NULL;
   209     if (src->recv) {
   210         dst->recv = timestamp_dup(src->recv);
   211         if (dst->recv == NULL)
   212             return PEP_OUT_OF_MEMORY;
   213     }
   214 
   215     free_identity(dst->from);
   216     dst->from = NULL;
   217     if (src->from) {
   218         dst->from = identity_dup(src->from);
   219         if (dst->from == NULL)
   220             return PEP_OUT_OF_MEMORY;
   221     }
   222 
   223     free_identity_list(dst->to);
   224     dst->to = NULL;
   225     if (src->to) {
   226         dst->to = identity_list_dup(src->to);
   227         if (dst->to == NULL)
   228             return PEP_OUT_OF_MEMORY;
   229     }
   230 
   231     free_identity(dst->recv_by);
   232     dst->recv_by = NULL;
   233     if (src->recv_by) {
   234         dst->recv_by = identity_dup(src->recv_by);
   235         if (dst->recv_by == NULL)
   236             return PEP_OUT_OF_MEMORY;
   237     }
   238 
   239     free_identity_list(dst->cc);
   240     dst->cc = NULL;
   241     if (src->cc) {
   242         dst->cc = identity_list_dup(src->cc);
   243         if (dst->cc == NULL)
   244             return PEP_OUT_OF_MEMORY;
   245     }
   246 
   247     free_identity_list(dst->bcc);
   248     dst->bcc = NULL;
   249     if (src->bcc) {
   250         dst->bcc = identity_list_dup(src->bcc);
   251         if (dst->bcc == NULL)
   252             return PEP_OUT_OF_MEMORY;
   253     }
   254 
   255     free_identity_list(dst->reply_to);
   256     dst->reply_to = NULL;
   257     if (src->reply_to) {
   258         dst->reply_to = identity_list_dup(src->reply_to);
   259         if (dst->reply_to == NULL)
   260             return PEP_OUT_OF_MEMORY;
   261     }
   262 
   263     free_stringlist(dst->in_reply_to);
   264     dst->in_reply_to = NULL;
   265     if (src->in_reply_to) {
   266         dst->in_reply_to = stringlist_dup(src->in_reply_to);
   267         if (dst->in_reply_to == NULL)
   268             return PEP_OUT_OF_MEMORY;
   269     }
   270 
   271     free_stringlist(dst->references);
   272     dst->references = NULL;
   273     if (src->references) {
   274         dst->references = stringlist_dup(src->references);
   275         if (dst->references == NULL)
   276             return PEP_OUT_OF_MEMORY;
   277     }
   278 
   279     free_stringlist(dst->keywords);
   280     dst->keywords = NULL;
   281     if (src->keywords) {
   282         dst->keywords = stringlist_dup(src->keywords);
   283         if (dst->keywords == NULL)
   284             return PEP_OUT_OF_MEMORY;
   285     }
   286 
   287     free(dst->comments);
   288     dst->comments = NULL;
   289     if (src->comments) {
   290         dst->comments = strdup(src->comments);
   291         assert(dst->comments);
   292         if (dst->comments == NULL)
   293             return PEP_OUT_OF_MEMORY;
   294     }
   295 
   296     return PEP_STATUS_OK;
   297 }
   298 
   299 static message * clone_to_empty_message(const message * src)
   300 {
   301     PEP_STATUS status;
   302     message * msg = NULL;
   303 
   304     assert(src);
   305 
   306     msg = calloc(1, sizeof(message));
   307     assert(msg);
   308     if (msg == NULL)
   309         goto enomem;
   310 
   311     msg->dir = src->dir;
   312 
   313     status = copy_fields(msg, src);
   314     if (status != PEP_STATUS_OK)
   315         goto enomem;
   316 
   317     return msg;
   318 
   319 enomem:
   320     free_message(msg);
   321     return NULL;
   322 }
   323 
   324 DYNAMIC_API PEP_STATUS encrypt_message(
   325         PEP_SESSION session,
   326         message *src,
   327         stringlist_t * extra,
   328         message **dst,
   329         PEP_enc_format enc_format
   330     )
   331 {
   332     PEP_STATUS status = PEP_STATUS_OK;
   333     message * msg = NULL;
   334     stringlist_t * keys = NULL;
   335     bool free_src = false;
   336 
   337     assert(session);
   338     assert(src);
   339     assert(dst);
   340     assert(enc_format >= PEP_enc_pieces);
   341 
   342     if (!(session && src && dst && (enc_format >= PEP_enc_pieces)))
   343         return PEP_ILLEGAL_VALUE;
   344 
   345     *dst = NULL;
   346 
   347     import_attached_keys(session, src);
   348 
   349     if (src->enc_format >= PEP_enc_pieces) {
   350         if (src->enc_format == enc_format) {
   351             assert(0); // the message is encrypted this way already
   352             msg = message_dup(src);
   353             if (msg == NULL)
   354                 goto enomem;
   355             *dst = msg;
   356             return PEP_STATUS_OK;
   357         }
   358         else {
   359             // decrypt and re-encrypt again
   360             message * _dst = NULL;
   361             PEP_MIME_format mime = (enc_format == PEP_enc_PEP) ? PEP_MIME :
   362                     PEP_MIME_fields_omitted;
   363 
   364             status = decrypt_message(session, src, mime, &_dst);
   365             if (status != PEP_STATUS_OK)
   366                 goto pep_error;
   367 
   368             src = _dst;
   369             free_src = true;
   370         }
   371     }
   372 
   373     status = myself(session, src->from);
   374     if (status != PEP_STATUS_OK)
   375         goto pep_error;
   376 
   377     msg = clone_to_empty_message(src);
   378     if (msg == NULL)
   379         goto enomem;
   380 
   381     keys = new_stringlist(src->from->fpr);
   382     if (keys == NULL)
   383         goto enomem;
   384 
   385     stringlist_t *_k = keys;
   386 
   387     if (extra) {
   388         _k = stringlist_append(_k, extra);
   389         if (_k == NULL)
   390             goto enomem;
   391     }
   392 
   393     bool dest_keys_found = false;
   394     identity_list * _il;
   395     for (_il = msg->to; _il && _il->ident; _il = _il->next) {
   396         PEP_STATUS status = update_identity(session, _il->ident);
   397         if (status != PEP_STATUS_OK)
   398             goto pep_error;
   399 
   400         if (_il->ident->fpr) {
   401             dest_keys_found = true;
   402             _k = stringlist_add(_k, _il->ident->fpr);
   403             if (_k == NULL)
   404                 goto enomem;
   405         }
   406         else
   407             status = PEP_KEY_NOT_FOUND;
   408     }
   409 
   410     if (dest_keys_found) {
   411         char *ptext;
   412         char *ctext = NULL;
   413         size_t csize = 0;
   414 
   415         switch (enc_format) {
   416         case PEP_enc_PGP_MIME: {
   417             bool free_ptext = false;
   418 
   419             msg->enc_format = PEP_enc_PGP_MIME;
   420 
   421             if (src->mime == PEP_MIME) {
   422                 message *_src = NULL;
   423                 assert(src->longmsg);
   424                 status = mime_decode_message(src->longmsg, &_src);
   425                 if (status != PEP_STATUS_OK)
   426                     goto pep_error;
   427                 if (free_src)
   428                     free_message(src);
   429                 src = _src;
   430                 free_src = true;
   431             }
   432 
   433             if (src->mime == PEP_MIME_none) {
   434                 if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
   435                     ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   436                     if (ptext == NULL)
   437                         goto enomem;
   438                     free_ptext = true;
   439                 }
   440                 else if (src->longmsg) {
   441                     ptext = src->longmsg;
   442                 }
   443                 else {
   444                     ptext = "pEp";
   445                 }
   446 
   447                 message *_src = calloc(1, sizeof(message));
   448                 assert(_src);
   449                 if (_src == NULL)
   450                     goto enomem;
   451                 _src->longmsg = ptext;
   452                 _src->longmsg_formatted = src->longmsg_formatted;
   453                 _src->attachments = src->attachments;
   454                 _src->enc_format = PEP_enc_PGP_MIME;
   455                 status = mime_encode_message(_src, true, &ptext);
   456                 assert(status == PEP_STATUS_OK);
   457                 if (free_ptext)
   458                     free(_src->longmsg);
   459                 free(_src);
   460                 assert(ptext);
   461                 if (ptext == NULL)
   462                     goto pep_error;
   463                 free_ptext = true;
   464             }
   465             else /* if (src->mime == PEP_MIME_fields_omitted) */ {
   466                 ptext = src->longmsg;
   467             }
   468 
   469             status = encrypt_and_sign(session, keys, ptext, strlen(ptext),
   470                     &ctext, &csize);
   471             if (free_ptext)
   472                 free(ptext);
   473             if (ctext == NULL)
   474                 goto pep_error;
   475 
   476             msg->longmsg = strdup(ctext);
   477             if (msg->longmsg == NULL)
   478                 goto enomem;
   479         }
   480         break;
   481 
   482         case PEP_enc_pieces:
   483             msg->enc_format = PEP_enc_pieces;
   484 
   485             if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
   486                 ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   487                 if (ptext == NULL)
   488                     goto enomem;
   489 
   490                 status = encrypt_and_sign(session, keys, ptext, strlen(ptext),
   491                         &ctext, &csize);
   492                 free(ptext);
   493                 if (ctext) {
   494                     msg->longmsg = strdup(ctext);
   495                     if (msg->longmsg == NULL)
   496                         goto enomem;
   497                 }
   498                 else {
   499                     goto pep_error;
   500                 }
   501             }
   502             else if (src->longmsg) {
   503                 ptext = src->longmsg;
   504                 status = encrypt_and_sign(session, keys, ptext, strlen(ptext),
   505                         &ctext, &csize);
   506                 if (ctext) {
   507                     msg->longmsg = strdup(ctext);
   508                     if (msg->longmsg == NULL)
   509                         goto enomem;
   510                 }
   511                 else {
   512                     goto pep_error;
   513                 }
   514             }
   515 
   516             if (msg->longmsg_formatted) {
   517                 ptext = src->longmsg_formatted;
   518                 status = encrypt_and_sign(session, keys, ptext, strlen(ptext),
   519                         &ctext, &csize);
   520                 if (ctext) {
   521                     msg->longmsg_formatted = strdup(ctext);
   522                     if (msg->longmsg_formatted == NULL)
   523                         goto enomem;
   524                 }
   525                 else {
   526                     goto pep_error;
   527                 }
   528             }
   529 
   530             if (src->attachments) {
   531                 bloblist_t *_s;
   532                 bloblist_t *_d = new_bloblist(NULL, 0, NULL, NULL);
   533                 if (_d == NULL)
   534                     goto enomem;
   535 
   536                 msg->attachments = _d;
   537                 for (_s = src->attachments; _s && _s->data; _s = _s->next) {
   538                     int psize = _s->size;
   539                     ptext = _s->data;
   540                     status = encrypt_and_sign(session, keys, ptext, psize,
   541                             &ctext, &csize);
   542                     if (ctext) {
   543                         char * _c = strdup(ctext);
   544                         if (_c == NULL)
   545                             goto enomem;
   546 
   547                         _d = bloblist_add(_d, _c, csize, _s->mime_type,
   548                                 _s->filename);
   549                         if (_d == NULL)
   550                             goto enomem;
   551                     }
   552                     else {
   553                         goto pep_error;
   554                     }
   555                 }
   556             }
   557             break;
   558 
   559         case PEP_enc_PEP:
   560             // TODO: implement
   561             NOT_IMPLEMENTED
   562 
   563         default:
   564             assert(0);
   565             status = PEP_ILLEGAL_VALUE;
   566             goto pep_error;
   567         }
   568     }
   569 
   570     free_stringlist(keys);
   571     if (free_src)
   572         free_message(src);
   573 
   574     if (msg->shortmsg == NULL)
   575         msg->shortmsg = strdup("pEp");
   576 
   577     attach_own_key(session, msg);
   578 
   579     *dst = msg;
   580     return PEP_STATUS_OK;
   581 
   582 enomem:
   583     status = PEP_OUT_OF_MEMORY;
   584 
   585 pep_error:
   586     free_stringlist(keys);
   587     free_message(msg);
   588     if (free_src)
   589         free_message(src);
   590 
   591     return status;
   592 }
   593 
   594 static bool is_encrypted_attachment(const bloblist_t *blob)
   595 {
   596     char *ext;
   597  
   598     assert(blob);
   599 
   600     if (blob->filename == NULL)
   601         return false;
   602 
   603     ext = strrchr(blob->filename, '.');
   604     if (ext == NULL)
   605         return false;
   606 
   607     if (strcmp(blob->mime_type, "application/octet-stream")) {
   608         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
   609                 strcmp(ext, ".asc") == 0)
   610             return true;
   611     }
   612     else if (strcmp(blob->mime_type, "text/plain")) {
   613         if (strcmp(ext, ".asc") == 0)
   614             return true;
   615     }
   616 
   617     return false;
   618 }
   619 
   620 static bool is_encrypted_html_attachment(const bloblist_t *blob)
   621 {
   622     assert(blob);
   623     assert(blob->filename);
   624 
   625     if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
   626         if (strcmp(blob->filename + 11, ".pgp") == 0 ||
   627                 strcmp(blob->filename + 11, ".asc") == 0)
   628             return true;
   629     }
   630 
   631     return false;
   632 }
   633 
   634 static char * without_double_ending(const char *filename)
   635 {
   636     char *ext;
   637 
   638     assert(filename);
   639 
   640     ext = strrchr(filename, '.');
   641     if (ext == NULL)
   642         return NULL;
   643 
   644     return strndup(filename, ext - filename);
   645 }
   646 
   647 DYNAMIC_API PEP_STATUS decrypt_message(
   648         PEP_SESSION session,
   649         message *src,
   650         PEP_MIME_format mime,
   651         message **dst
   652     )
   653 {
   654     PEP_STATUS status = PEP_STATUS_OK;
   655     message *msg = NULL;
   656     char *ctext;
   657     size_t csize;
   658     char *ptext;
   659     size_t psize;
   660     stringlist_t *keylist;
   661     bool free_src = false;
   662 
   663     assert(session);
   664     assert(src);
   665     assert(dst);
   666 
   667     if (!(session && src && dst))
   668         return PEP_ILLEGAL_VALUE;
   669 
   670     *dst = NULL;
   671  
   672     import_attached_keys(session, src);
   673 
   674     if (src->mime == PEP_MIME_fields_omitted || src->mime == PEP_MIME) {
   675         message *_src = NULL;
   676         status = mime_decode_message(src->longmsg, &_src);
   677         if (status != PEP_STATUS_OK)
   678             goto pep_error;
   679 
   680         if ( src->mime == PEP_MIME_fields_omitted) {
   681             status = copy_fields(_src, src);
   682             if (status != PEP_STATUS_OK) {
   683                 free_message(_src);
   684                 goto pep_error;
   685             }
   686         }
   687 
   688         src = _src;
   689         free_src = true;
   690     }
   691 
   692     // src message is not MIME encoded (any more)
   693     assert(src->mime == PEP_MIME_none);
   694 
   695     if (!is_PGP_message_text(src->longmsg)) {
   696         status = PEP_UNENCRYPTED;
   697         goto pep_error;
   698     }
   699 
   700     ctext = src->longmsg;
   701     csize = strlen(src->longmsg);
   702 
   703     status = decrypt_and_verify(session, ctext, csize, &ptext, &psize,
   704             &keylist);
   705     if (ptext == NULL)
   706         goto pep_error;
   707 
   708     switch (src->enc_format) {
   709         case PEP_enc_PGP_MIME:
   710             status = mime_decode_message(ptext, &msg);
   711             if (status != PEP_STATUS_OK)
   712                 goto pep_error;
   713 
   714             break;
   715 
   716         case PEP_enc_pieces:
   717             msg = clone_to_empty_message(src);
   718             if (msg == NULL)
   719                 goto enomem;
   720 
   721             msg->longmsg = strdup(ptext);
   722             if (msg->longmsg == NULL)
   723                 goto enomem;
   724 
   725             bloblist_t *_m = msg->attachments;
   726             bloblist_t *_s;
   727             for (_s = src->attachments; _s; _s = _s->next) {
   728                 if (is_encrypted_attachment(_s)) {
   729                     ctext = _s->data;
   730                     csize = _s->size;
   731 
   732                     status = decrypt_and_verify(session, ctext, csize, &ptext,
   733                             &psize, &keylist);
   734                     if (ptext == NULL)
   735                         goto pep_error;
   736                     
   737                     if (is_encrypted_html_attachment(_s)) {
   738                         msg->longmsg_formatted = strdup(ptext);
   739                         if (msg->longmsg_formatted == NULL)
   740                             goto pep_error;
   741                     }
   742                     else {
   743                         char * mime_type = "application/octet-stream";
   744                         char * filename = without_double_ending(_s->filename);
   745                         if (filename == NULL)
   746                             goto enomem;
   747 
   748                         _m = bloblist_add(_m, ptext, psize, mime_type, filename);
   749                         if (_m == NULL)
   750                             goto enomem;
   751 
   752                        if (msg->attachments == NULL)
   753                             msg->attachments = _m;
   754                     }
   755                 }
   756             }
   757 
   758             break;
   759 
   760         default:
   761             // BUG: must implement more
   762             NOT_IMPLEMENTED
   763     }
   764 
   765     switch (src->enc_format) {
   766         case PEP_enc_PGP_MIME:
   767         case PEP_enc_pieces:
   768             status = copy_fields(msg, src);
   769             if (status != PEP_STATUS_OK)
   770                 goto pep_error;
   771 
   772             if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
   773                 free(msg->shortmsg);
   774                 msg->shortmsg = strdup(src->shortmsg);
   775                 if (msg->shortmsg == NULL)
   776                     goto enomem;
   777             }
   778 
   779             if (msg->shortmsg == NULL || strcmp(msg->shortmsg, "pEp") == 0)
   780             {
   781                 char * shortmsg;
   782                 char * longmsg;
   783 
   784                 int r = seperate_short_and_long(msg->longmsg, &shortmsg,
   785                         &longmsg);
   786                 if (r == -1)
   787                     goto enomem;
   788 
   789                 free(msg->shortmsg);
   790                 free(msg->longmsg);
   791 
   792                 msg->shortmsg = shortmsg;
   793                 msg->longmsg = longmsg;
   794             }
   795             else {
   796                 msg->shortmsg = strdup(src->shortmsg);
   797                 if (msg->shortmsg == NULL)
   798                     goto enomem;
   799                 msg->longmsg = ptext;
   800             }
   801             break;
   802 
   803         default:
   804             // BUG: must implement more
   805             NOT_IMPLEMENTED
   806     }
   807 
   808     switch (mime) {
   809         case PEP_MIME_none:
   810             break;
   811 
   812         case PEP_MIME:
   813         case PEP_MIME_fields_omitted:
   814             {
   815                 char *text = NULL;
   816                 status = mime_encode_message(msg,
   817                         mime == PEP_MIME_fields_omitted, &text);
   818                 if (status != PEP_STATUS_OK)
   819                     goto pep_error;
   820 
   821                 message *_msg = clone_to_empty_message(msg);
   822                 if (_msg == NULL) {
   823                     free(text);
   824                     goto enomem;
   825                 }
   826                 _msg->longmsg = text;
   827                 _msg->shortmsg = strdup(msg->shortmsg);
   828                 if (msg->shortmsg == NULL)
   829                     goto enomem;
   830 
   831                 free_message(msg);
   832                 msg = _msg;
   833             }
   834             break;
   835     }
   836 
   837     if (free_src)
   838         free_message(src);
   839 
   840     import_attached_keys(session, msg);
   841 
   842     *dst = msg;
   843     return PEP_STATUS_OK;
   844 
   845 enomem:
   846     status = PEP_OUT_OF_MEMORY;
   847 
   848 pep_error:
   849     free_message(msg);
   850     if (free_src)
   851         free_message(src);
   852 
   853     return status;
   854 }
   855 
   856 static PEP_comm_type _get_comm_type(
   857         PEP_SESSION session,
   858         PEP_comm_type max_comm_type,
   859         pEp_identity *ident
   860     )
   861 {
   862     PEP_STATUS status = update_identity(session, ident);
   863 
   864     if (max_comm_type == PEP_ct_compromized)
   865         return PEP_ct_compromized;
   866 
   867     if (status == PEP_STATUS_OK) {
   868         if (ident->comm_type == PEP_ct_compromized)
   869             return PEP_ct_compromized;
   870         else
   871             return MIN(max_comm_type, ident->comm_type);
   872     }
   873     else {
   874         return PEP_ct_unknown;
   875     }
   876 }
   877 
   878 DYNAMIC_API PEP_STATUS get_message_color(
   879         PEP_SESSION session,
   880         message *msg,
   881         PEP_color *color
   882     )
   883 {
   884     PEP_STATUS status = PEP_STATUS_OK;
   885     PEP_comm_type max_comm_type = PEP_ct_pEp;
   886     bool comm_type_determined = false;
   887     identity_list * il;
   888 
   889     assert(session);
   890     assert(msg);
   891     assert(color);
   892 
   893     if (!(session && msg && color))
   894         return PEP_ILLEGAL_VALUE;
   895 
   896     *color = PEP_undefined;
   897 
   898     assert(msg->from);
   899     if (msg->from == NULL)
   900         return PEP_ILLEGAL_VALUE;
   901 
   902     switch (msg->dir) {
   903         case PEP_dir_incoming:
   904             status = update_identity(session, msg->from);
   905             if (status != PEP_STATUS_OK)
   906                 return status;
   907             max_comm_type = msg->from->comm_type;
   908             comm_type_determined = true;
   909             break;
   910         
   911         case PEP_dir_outgoing:
   912             status = myself(session, msg->from);
   913             if (status != PEP_STATUS_OK)
   914                 return status;
   915 
   916             for (il = msg->to; il != NULL; il = il->next) {
   917                 if (il->ident) {
   918                     max_comm_type = _get_comm_type(session, max_comm_type,
   919                             il->ident);
   920                     comm_type_determined = true;
   921                 }
   922             }
   923 
   924             for (il = msg->cc; il != NULL; il = il->next) {
   925                 if (il->ident) {
   926                     max_comm_type = _get_comm_type(session, max_comm_type,
   927                             il->ident);
   928                     comm_type_determined = true;
   929                 }
   930             }
   931 
   932             for (il = msg->bcc; il != NULL; il = il->next) {
   933                 if (il->ident) {
   934                     max_comm_type = _get_comm_type(session, max_comm_type,
   935                             il->ident);
   936                     comm_type_determined = true;
   937                 }
   938             }
   939             break;
   940 
   941         default:
   942             return PEP_ILLEGAL_VALUE;
   943     }
   944 
   945     if (comm_type_determined == false)
   946         *color = PEP_undefined;
   947 
   948     else if (max_comm_type == PEP_ct_compromized)
   949         *color = PEP_under_attack;
   950 
   951     else if (max_comm_type >= PEP_ct_confirmed_enc_anon)
   952         *color = PEP_trusted_and_anonymized;
   953 
   954     else if (max_comm_type >= PEP_ct_strong_encryption)
   955         *color = PEP_trusted;
   956 
   957     else if (max_comm_type >= PEP_ct_strong_but_unconfirmed &&
   958             max_comm_type < PEP_ct_confirmed)
   959         *color = PEP_reliable;
   960     
   961     else if (max_comm_type == PEP_ct_no_encryption ||
   962             max_comm_type == PEP_ct_no_encrypted_channel)
   963         *color = PEP_unencrypted;
   964 
   965     else if (max_comm_type == PEP_ct_unknown)
   966         *color = PEP_undefined;
   967 
   968     else
   969         *color = PEP_unreliable;
   970 
   971     return PEP_STATUS_OK;
   972 }
   973