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