src/message_api.c
author Edouard Tisserant
Thu, 21 Jan 2016 19:59:14 +0100
changeset 427 556fb9c7d97c
parent 419 d3bb685f489b
child 428 05d3a6d5a6a9
permissions -rw-r--r--
reverted 8c8a44854e2a 'PGPMime's msg.asc attachment becomes PGPexch.htm.pgp'
     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_ptext=0;
   333     }
   334     free(_src);
   335     assert(mimetext);
   336     if (mimetext == NULL)
   337         goto pep_error;
   338 
   339     status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
   340         &ctext, &csize);
   341     free(mimetext);
   342     if (ctext == NULL)
   343         goto pep_error;
   344 
   345     dst->longmsg = strdup("this message was encrypted with p≡p "
   346         "http://pEp-project.org");
   347     if (dst->longmsg == NULL)
   348         goto enomem;
   349 
   350     char *v = strdup("Version: 1");
   351     if (v == NULL)
   352         goto enomem;
   353 
   354     bloblist_t *_a = new_bloblist(v, 11, "application/pgp-encrypted", NULL);
   355     if (_a == NULL)
   356         goto enomem;
   357     dst->attachments = _a;
   358 
   359     _ctext = malloc(csize);
   360     assert(_ctext);
   361     if (_ctext == NULL)
   362         goto enomem;
   363     memcpy(_ctext, ctext, csize);
   364 
   365     _a = bloblist_add(_a, _ctext, csize, "application/octet-stream",
   366         "msg.asc");
   367     if (_a == NULL)
   368         goto enomem;
   369 
   370     return PEP_STATUS_OK;
   371 
   372 enomem:
   373     status = PEP_OUT_OF_MEMORY;
   374 
   375 pep_error:
   376     if (free_ptext)
   377         free(ptext);
   378     free(_ctext);
   379     return status;
   380 }
   381 
   382 static PEP_STATUS encrypt_PGP_in_pieces(
   383     PEP_SESSION session,
   384     const message *src,
   385     stringlist_t *keys,
   386     message *dst
   387     )
   388 {
   389     PEP_STATUS status = PEP_STATUS_OK;
   390     char *ctext;
   391     size_t csize;
   392 
   393     assert(dst->longmsg == NULL);
   394     assert(dst->attachments == NULL);
   395 
   396     dst->enc_format = PEP_enc_pieces;
   397 
   398     if (src->shortmsg && src->shortmsg[0] && strcmp(src->shortmsg, "pEp") != 0) {
   399         char *ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   400         if (ptext == NULL)
   401             goto enomem;
   402 
   403         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   404             &csize);
   405         free(ptext);
   406         if (ctext) {
   407             dst->longmsg = strndup(ctext, csize);
   408             assert(dst->longmsg);
   409             if (dst->longmsg == NULL)
   410                 goto enomem;
   411         }
   412         else {
   413             goto pep_error;
   414         }
   415     }
   416     else if (src->longmsg && src->longmsg[0]) {
   417         char *ptext = src->longmsg;
   418         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   419             &csize);
   420         if (ctext) {
   421             dst->longmsg = strndup(ctext, csize);
   422             assert(dst->longmsg);
   423             if (dst->longmsg == NULL)
   424                 goto enomem;
   425         }
   426         else {
   427             goto pep_error;
   428         }
   429     }
   430     else {
   431         dst->longmsg = strdup("");
   432         if (dst->longmsg == NULL)
   433             goto enomem;
   434     }
   435 
   436     if (src->longmsg_formatted && src->longmsg_formatted[0]) {
   437         char *ptext = src->longmsg_formatted;
   438         status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   439             &csize);
   440         if (ctext) {
   441             char *_ctext = malloc(csize);
   442             assert(_ctext);
   443             if (_ctext == NULL)
   444                 goto enomem;
   445             memcpy(_ctext, ctext, csize);
   446 
   447             bloblist_t *_a = bloblist_add(dst->attachments, _ctext, csize,
   448                 "application/octet-stream", "PGPexch.htm.pgp");
   449             if (_a == NULL)
   450                 goto enomem;
   451             if (dst->attachments == NULL)
   452                 dst->attachments = _a;
   453         }
   454         else {
   455             goto pep_error;
   456         }
   457     }
   458 
   459     if (src->attachments) {
   460         if (dst->attachments == NULL) {
   461             dst->attachments = new_bloblist(NULL, 0, NULL, NULL);
   462             if (dst->attachments == NULL)
   463                 goto enomem;
   464         }
   465 
   466         bloblist_t *_s = src->attachments;
   467         bloblist_t *_d = dst->attachments;
   468 
   469         for (int n = 0; _s && _s->value; _s = _s->next) {
   470             size_t psize = _s->size;
   471             char *ptext = _s->value;
   472             status = encrypt_and_sign(session, keys, ptext, psize, &ctext,
   473                 &csize);
   474             if (ctext) {
   475                 char *filename = NULL;
   476 
   477                 if (_s->filename) {
   478                     size_t len = strlen(_s->filename);
   479                     filename = calloc(1, len + 5);
   480                     if (filename == NULL)
   481                         goto enomem;
   482 
   483                     strcpy(filename, _s->filename);
   484                     strcpy(filename + len, ".pgp");
   485                 }
   486                 else {
   487                     filename = calloc(1, 20);
   488                     if (filename == NULL)
   489                         goto enomem;
   490 
   491                     ++n;
   492                     n &= 0xffff;
   493                     snprintf(filename, 20, "Attachment%d.pgp", n);
   494                 }
   495 
   496                 char *_ctext = malloc(csize);
   497                 assert(_ctext);
   498                 if (_ctext == NULL)
   499                     goto enomem;
   500                 memcpy(_ctext, ctext, csize);
   501 
   502                 _d = bloblist_add(_d, _ctext, csize, "application/octet-stream",
   503                     filename);
   504                 if (_d == NULL)
   505                     goto enomem;
   506             }
   507             else {
   508                 goto pep_error;
   509             }
   510         }
   511     }
   512 
   513     return PEP_STATUS_OK;
   514 
   515 enomem:
   516     status = PEP_OUT_OF_MEMORY;
   517 
   518 pep_error:
   519     return status;
   520 }
   521 
   522 static char * keylist_to_string(const stringlist_t *keylist)
   523 {
   524     if (keylist) {
   525         size_t size = stringlist_length(keylist);
   526 
   527         const stringlist_t *_kl;
   528         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   529             size += strlen(_kl->value);
   530         }
   531 
   532         char *result = calloc(1, size);
   533         if (result == NULL)
   534             return NULL;
   535 
   536         char *_r = result;
   537         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   538             _r = stpcpy(_r, _kl->value);
   539             if (_kl->next && _kl->next->value)
   540                 _r = stpcpy(_r, ",");
   541         }
   542 
   543         return result;
   544     }
   545     else {
   546         return NULL;
   547     }
   548 }
   549 
   550 static const char * color_to_string(PEP_color color)
   551 {
   552     switch (color) {
   553     case PEP_rating_cannot_decrypt:
   554         return "cannot_decrypt";
   555     case PEP_rating_have_no_key:
   556         return "have_no_key";
   557     case PEP_rating_unencrypted:
   558         return "unencrypted";
   559     case PEP_rating_unreliable:
   560         return "unreliable";
   561     case PEP_rating_reliable:
   562         return "reliable";
   563     case PEP_rating_trusted:
   564         return "trusted";
   565     case PEP_rating_trusted_and_anonymized:
   566         return "trusted_and_anonymized";
   567     case PEP_rating_fully_anonymous:
   568         return "fully_anonymous";
   569     case PEP_rating_under_attack:
   570         return "unter_attack";
   571     case PEP_rating_b0rken:
   572         return "b0rken";
   573     default:
   574         return "undefined";
   575     }
   576 }
   577 
   578 static void decorate_message(
   579     message *msg,
   580     PEP_color color,
   581     stringlist_t *keylist
   582     )
   583 {
   584     assert(msg);
   585 
   586     add_opt_field(msg, "X-pEp-Version", "1.0");
   587     
   588     if (color != PEP_rating_undefined)
   589         add_opt_field(msg, "X-EncStatus", color_to_string(color));
   590 
   591     if (keylist) {
   592         char *_keylist = keylist_to_string(keylist);
   593         add_opt_field(msg, "X-KeyList", _keylist);
   594         free(_keylist);
   595     }
   596 }
   597 
   598 static PEP_color _rating(PEP_comm_type ct)
   599 {
   600     if (ct == PEP_ct_unknown)
   601         return PEP_rating_undefined;
   602 
   603     else if (ct == PEP_ct_compromized)
   604         return PEP_rating_under_attack;
   605 
   606     else if (ct >= PEP_ct_confirmed_enc_anon)
   607         return PEP_rating_trusted_and_anonymized;
   608 
   609     else if (ct >= PEP_ct_strong_encryption)
   610         return PEP_rating_trusted;
   611 
   612     else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
   613         return PEP_rating_reliable;
   614 
   615     else if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel)
   616         return PEP_rating_unencrypted;
   617 
   618     else
   619         return PEP_rating_unreliable;
   620 }
   621 
   622 static bool is_encrypted_attachment(const bloblist_t *blob)
   623 {
   624     char *ext;
   625 
   626     assert(blob);
   627 
   628     if (blob->filename == NULL)
   629         return false;
   630 
   631     ext = strrchr(blob->filename, '.');
   632     if (ext == NULL)
   633         return false;
   634 
   635     if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
   636         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
   637             strcmp(ext, ".asc") == 0)
   638             return true;
   639     }
   640     else if (strcmp(blob->mime_type, "text/plain") == 0) {
   641         if (strcmp(ext, ".asc") == 0)
   642             return true;
   643     }
   644 
   645     return false;
   646 }
   647 
   648 static bool is_encrypted_html_attachment(const bloblist_t *blob)
   649 {
   650     assert(blob);
   651     assert(blob->filename);
   652 
   653     if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
   654         if (strcmp(blob->filename + 11, ".pgp") == 0 ||
   655             strcmp(blob->filename + 11, ".asc") == 0)
   656             return true;
   657     }
   658 
   659     return false;
   660 }
   661 
   662 static char * without_double_ending(const char *filename)
   663 {
   664     char *ext;
   665 
   666     assert(filename);
   667 
   668     ext = strrchr(filename, '.');
   669     if (ext == NULL)
   670         return NULL;
   671 
   672     return strndup(filename, ext - filename);
   673 }
   674 
   675 static PEP_color decrypt_color(PEP_STATUS status)
   676 {
   677     switch (status) {
   678     case PEP_UNENCRYPTED:
   679     case PEP_VERIFIED:
   680     case PEP_VERIFY_NO_KEY:
   681     case PEP_VERIFIED_AND_TRUSTED:
   682         return PEP_rating_unencrypted;
   683 
   684     case PEP_DECRYPTED:
   685         return PEP_rating_unreliable;
   686 
   687     case PEP_DECRYPTED_AND_VERIFIED:
   688         return PEP_rating_reliable;
   689 
   690     case PEP_DECRYPT_NO_KEY:
   691         return PEP_rating_have_no_key;
   692 
   693     case PEP_DECRYPT_WRONG_FORMAT:
   694     case PEP_CANNOT_DECRYPT_UNKNOWN:
   695         return PEP_rating_cannot_decrypt;
   696 
   697     default:
   698         return PEP_rating_undefined;
   699     }
   700 }
   701 
   702 static PEP_color key_color(PEP_SESSION session, const char *fpr)
   703 {
   704     PEP_comm_type comm_type = PEP_ct_unknown;
   705 
   706     assert(session);
   707     assert(fpr);
   708 
   709     PEP_STATUS status = get_key_rating(session, fpr, &comm_type);
   710     if (status != PEP_STATUS_OK)
   711         return PEP_rating_undefined;
   712 
   713     return _rating(comm_type);
   714 }
   715 
   716 static PEP_color keylist_color(PEP_SESSION session, stringlist_t *keylist)
   717 {
   718     PEP_color color = PEP_rating_reliable;
   719 
   720     assert(keylist && keylist->value);
   721     if (keylist == NULL || keylist->value == NULL)
   722         return PEP_rating_unencrypted;
   723 
   724     stringlist_t *_kl;
   725     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   726         PEP_comm_type ct;
   727         PEP_STATUS status;
   728 
   729         color = key_color(session, _kl->value);
   730         if (color == PEP_rating_under_attack)
   731             return PEP_rating_under_attack;
   732 
   733         if (color >= PEP_rating_reliable) {
   734             status = least_trust(session, _kl->value, &ct);
   735             if (status != PEP_STATUS_OK)
   736                 return PEP_rating_undefined;
   737             if (ct == PEP_ct_unknown)
   738                 color = PEP_rating_unreliable;
   739             else
   740                 color = _rating(ct);
   741         }
   742     }
   743 
   744     return color;
   745 }
   746 
   747 static PEP_comm_type _get_comm_type(
   748     PEP_SESSION session,
   749     PEP_comm_type max_comm_type,
   750     pEp_identity *ident
   751     )
   752 {
   753     PEP_STATUS status = update_identity(session, ident);
   754 
   755     if (max_comm_type == PEP_ct_compromized)
   756         return PEP_ct_compromized;
   757 
   758     if (status == PEP_STATUS_OK) {
   759         if (ident->comm_type == PEP_ct_compromized)
   760             return PEP_ct_compromized;
   761         else
   762             return MIN(max_comm_type, ident->comm_type);
   763     }
   764     else {
   765         return PEP_ct_unknown;
   766     }
   767 }
   768 
   769 void import_attached_keys(PEP_SESSION session, const message *msg)
   770 {
   771     assert(session);
   772     assert(msg);
   773 
   774     bloblist_t *bl;
   775     for (bl = msg->attachments; bl && bl->value; bl = bl->next) {
   776         assert(bl && bl->value && bl->size);
   777 
   778         // workaround for Apple Mail bugs
   779         if (is_mime_type(bl, "application/x-apple-msg-attachment")) {
   780             if (is_fileending(bl, ".asc")) {
   781                 if (strlen(bl->filename) == 14 &&
   782                         bl->filename[0] == '0' && bl->filename[1] == 'x')
   783                     import_key(session, bl->value, bl->size);
   784                 else if (strlen(bl->filename) == 12)
   785                     import_key(session, bl->value, bl->size);
   786             }
   787         }
   788         else if (bl->mime_type == NULL ||
   789                     is_mime_type(bl, "application/octet-stream")) {
   790             if (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   791                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))
   792                 import_key(session, bl->value, bl->size);
   793         }
   794         else if (is_mime_type(bl, "application/pgp-keys")) {
   795             import_key(session, bl->value, bl->size);
   796         }
   797         else if (is_mime_type(bl, "text/plain")) {
   798             if (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   799                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))
   800                 import_key(session, bl->value, bl->size);
   801         }
   802     }
   803 }
   804 
   805 void attach_own_key(PEP_SESSION session, message *msg)
   806 {
   807     char *keydata;
   808     size_t size;
   809     bloblist_t *bl;
   810 
   811     assert(session);
   812     assert(msg);
   813 
   814     if (msg->dir == PEP_dir_incoming)
   815         return;
   816 
   817     assert(msg->from && msg->from->fpr);
   818     if (msg->from == NULL || msg->from->fpr == NULL)
   819         return;
   820 
   821     PEP_STATUS status = export_key(session, msg->from->fpr, &keydata, &size);
   822     assert(status == PEP_STATUS_OK);
   823     if (status != PEP_STATUS_OK)
   824         return;
   825     assert(size);
   826 
   827     bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
   828             "pEp_key.asc");
   829     if (msg->attachments == NULL && bl)
   830         msg->attachments = bl;
   831 }
   832 
   833 PEP_cryptotech determine_encryption_format(message *msg)
   834 {
   835     assert(msg);
   836 
   837     if (is_PGP_message_text(msg->longmsg)) {
   838         msg->enc_format = PEP_enc_pieces;
   839         return PEP_crypt_OpenPGP;
   840     }
   841     else if (msg->attachments && msg->attachments->next &&
   842             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
   843             is_PGP_message_text(msg->attachments->next->value)
   844         ) {
   845         msg->enc_format = PEP_enc_PGP_MIME;
   846         return PEP_crypt_OpenPGP;
   847     }
   848     else {
   849         msg->enc_format = PEP_enc_none;
   850         return PEP_crypt_none;
   851     }
   852 }
   853 
   854 DYNAMIC_API PEP_STATUS encrypt_message(
   855         PEP_SESSION session,
   856         message *src,
   857         stringlist_t * extra,
   858         message **dst,
   859         PEP_enc_format enc_format
   860     )
   861 {
   862     PEP_STATUS status = PEP_STATUS_OK;
   863     message * msg = NULL;
   864     stringlist_t * keys = NULL;
   865 
   866     assert(session);
   867     assert(src);
   868     assert(dst);
   869     assert(enc_format != PEP_enc_none);
   870 
   871     if (!(session && src && dst && enc_format != PEP_enc_none))
   872         return PEP_ILLEGAL_VALUE;
   873 
   874     determine_encryption_format(src);
   875     if (src->enc_format != PEP_enc_none)
   876         return PEP_ILLEGAL_VALUE;
   877 
   878     *dst = NULL;
   879 
   880     status = myself(session, src->from);
   881     if (status != PEP_STATUS_OK)
   882         goto pep_error;
   883 
   884     keys = new_stringlist(src->from->fpr);
   885     if (keys == NULL)
   886         goto enomem;
   887 
   888     stringlist_t *_k = keys;
   889 
   890     if (extra) {
   891         _k = stringlist_append(_k, extra);
   892         if (_k == NULL)
   893             goto enomem;
   894     }
   895 
   896     bool dest_keys_found = true;
   897 
   898     identity_list * _il;
   899     for (_il = src->to; _il && _il->ident; _il = _il->next) {
   900         PEP_STATUS _status = update_identity(session, _il->ident);
   901         if (_status != PEP_STATUS_OK) {
   902             status = _status;
   903             goto pep_error;
   904         }
   905 
   906         if (_il->ident->fpr && _il->ident->fpr[0]) {
   907             _k = stringlist_add(_k, _il->ident->fpr);
   908             if (_k == NULL)
   909                 goto enomem;
   910         }
   911         else {
   912             dest_keys_found = false;
   913             status = PEP_KEY_NOT_FOUND;
   914         }
   915     }
   916 
   917     for (_il = src->cc; _il && _il->ident; _il = _il->next) {
   918         PEP_STATUS _status = update_identity(session, _il->ident);
   919         if (_status != PEP_STATUS_OK)
   920         {
   921             status = _status;
   922             goto pep_error;
   923         }
   924 
   925         if (_il->ident->fpr && _il->ident->fpr[0]) {
   926             _k = stringlist_add(_k, _il->ident->fpr);
   927             if (_k == NULL)
   928                 goto enomem;
   929         }
   930         else {
   931             dest_keys_found = false;
   932             status = PEP_KEY_NOT_FOUND;
   933         }
   934     }
   935 
   936     if (!dest_keys_found) {
   937         free_stringlist(keys);
   938         attach_own_key(session, src);
   939         return PEP_UNENCRYPTED;
   940     }
   941     else {
   942         msg = clone_to_empty_message(src);
   943         if (msg == NULL)
   944             goto enomem;
   945 
   946         switch (enc_format) {
   947         case PEP_enc_PGP_MIME:
   948         case PEP_enc_PEP: // BUG: should be implemented extra
   949             status = encrypt_PGP_MIME(session, src, keys, msg);
   950             break;
   951 
   952         case PEP_enc_pieces:
   953             status = encrypt_PGP_in_pieces(session, src, keys, msg);
   954             break;
   955 
   956         /* case PEP_enc_PEP:
   957             // TODO: implement
   958             NOT_IMPLEMENTED */
   959 
   960         default:
   961             assert(0);
   962             status = PEP_ILLEGAL_VALUE;
   963             goto pep_error;
   964         }
   965         
   966         if (status == PEP_OUT_OF_MEMORY)
   967             goto enomem;
   968         
   969         if (status != PEP_STATUS_OK) {
   970             attach_own_key(session, src);
   971             goto pep_error;
   972         }
   973         else {
   974             attach_own_key(session, msg);
   975         }
   976     }
   977 
   978     free_stringlist(keys);
   979 
   980     if (msg && msg->shortmsg == NULL)
   981         msg->shortmsg = strdup("pEp");
   982 
   983     if (msg)
   984         decorate_message(msg, PEP_rating_undefined, NULL);
   985 
   986     *dst = msg;
   987     return status;
   988 
   989 enomem:
   990     status = PEP_OUT_OF_MEMORY;
   991 
   992 pep_error:
   993     free_stringlist(keys);
   994     free_message(msg);
   995 
   996     return status;
   997 }
   998 
   999 DYNAMIC_API PEP_STATUS decrypt_message(
  1000         PEP_SESSION session,
  1001         message *src,
  1002         message **dst,
  1003         stringlist_t **keylist,
  1004         PEP_color *color
  1005     )
  1006 {
  1007     PEP_STATUS status = PEP_STATUS_OK;
  1008     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  1009     message *msg = NULL;
  1010     char *ctext;
  1011     size_t csize;
  1012     char *ptext = NULL;
  1013     size_t psize;
  1014     stringlist_t *_keylist = NULL;
  1015 
  1016     assert(session);
  1017     assert(src);
  1018     assert(dst);
  1019     assert(keylist);
  1020     assert(color);
  1021 
  1022     if (!(session && src && dst && keylist && color))
  1023         return PEP_ILLEGAL_VALUE;
  1024 
  1025     import_attached_keys(session, src);
  1026     PEP_cryptotech crypto = determine_encryption_format(src);
  1027 
  1028     *dst = NULL;
  1029     *keylist = NULL;
  1030     *color = PEP_rating_undefined;
  1031  
  1032     switch (src->enc_format) {
  1033         case PEP_enc_none:
  1034             *color = PEP_rating_unencrypted;
  1035             return PEP_UNENCRYPTED;
  1036 
  1037         case PEP_enc_PGP_MIME:
  1038             ctext = src->attachments->next->value;
  1039             csize = src->attachments->next->size;
  1040 
  1041             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1042                     csize, &ptext, &psize, &_keylist);
  1043             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1044                 goto pep_error;
  1045             decrypt_status = status;
  1046             break;
  1047 
  1048         case PEP_enc_pieces:
  1049             ctext = src->longmsg;
  1050             csize = strlen(ctext);
  1051 
  1052             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1053                     csize, &ptext, &psize, &_keylist);
  1054             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1055                 goto pep_error;
  1056             decrypt_status = status;
  1057             break;
  1058 
  1059         default:
  1060             NOT_IMPLEMENTED
  1061     }
  1062 
  1063     *color = decrypt_color(status);
  1064 
  1065     if (*color != PEP_rating_under_attack) {
  1066         PEP_color kl_color = PEP_rating_undefined;
  1067 
  1068         if (_keylist)
  1069             kl_color = keylist_color(session, _keylist);
  1070 
  1071         if (kl_color == PEP_rating_under_attack) {
  1072             *color = PEP_rating_under_attack;
  1073         }
  1074         else if (*color >= PEP_rating_reliable &&
  1075                kl_color < PEP_rating_reliable) {
  1076             *color = PEP_rating_unreliable;
  1077         }
  1078         else if (*color >= PEP_rating_reliable &&
  1079                kl_color >= PEP_rating_reliable) {
  1080             if (!(src->from && src->from->user_id && src->from->user_id[0])) {
  1081                 *color = PEP_rating_unreliable;
  1082             }
  1083             else {
  1084                 char *fpr = _keylist->value;
  1085                 pEp_identity *_from = new_identity(src->from->address, fpr,
  1086                         src->from->user_id, src->from->username);
  1087                 if (_from == NULL)
  1088                     goto enomem;
  1089                 status = update_identity(session, _from);
  1090                 if (_from->comm_type != PEP_ct_unknown)
  1091                     *color = _rating(_from->comm_type);
  1092                 free_identity(_from);
  1093                 if (status != PEP_STATUS_OK)
  1094                     goto pep_error;
  1095             }
  1096         }
  1097     }
  1098 
  1099     if (ptext) {
  1100         switch (src->enc_format) {
  1101             case PEP_enc_PGP_MIME:
  1102                 status = mime_decode_message(ptext, psize, &msg);
  1103                 if (status != PEP_STATUS_OK)
  1104                     goto pep_error;
  1105                 break;
  1106 
  1107             case PEP_enc_pieces:
  1108                 msg = clone_to_empty_message(src);
  1109                 if (msg == NULL)
  1110                     goto enomem;
  1111 
  1112                 msg->longmsg = strdup(ptext);
  1113                 if (msg->longmsg == NULL)
  1114                     goto enomem;
  1115 
  1116                 bloblist_t *_m = msg->attachments;
  1117                 if (_m == NULL && src->attachments && src->attachments->value) {
  1118                     msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
  1119                     _m = msg->attachments;
  1120                 }
  1121 
  1122                 bloblist_t *_s;
  1123                 for (_s = src->attachments; _s && _s->value; _s = _s->next) {
  1124                     if (is_encrypted_attachment(_s)) {
  1125                         stringlist_t *_keylist = NULL;
  1126                         ctext = _s->value;
  1127                         csize = _s->size;
  1128 
  1129                         status = decrypt_and_verify(session, ctext, csize,
  1130                                 &ptext, &psize, &_keylist);
  1131                         free_stringlist(_keylist);
  1132 
  1133                         if (ptext) {
  1134                             if (is_encrypted_html_attachment(_s)) {
  1135                                 msg->longmsg_formatted = strdup(ptext);
  1136                                 if (msg->longmsg_formatted == NULL)
  1137                                     goto pep_error;
  1138                             }
  1139                             else {
  1140                                 char * mime_type = "application/octet-stream";
  1141                                 char * filename =
  1142                                     without_double_ending(_s->filename);
  1143                                 if (filename == NULL)
  1144                                     goto enomem;
  1145 
  1146                                 char *_ptext = malloc(psize);
  1147                                 assert(_ptext);
  1148                                 if (_ptext == NULL)
  1149                                     goto enomem;
  1150                                 memcpy(_ptext, ptext, psize);
  1151 
  1152                                 _m = bloblist_add(_m, _ptext, psize, mime_type,
  1153                                     filename);
  1154                                 if (_m == NULL)
  1155                                     goto enomem;
  1156 
  1157                                 if (msg->attachments == NULL)
  1158                                     msg->attachments = _m;
  1159                             }
  1160                         }
  1161                         else {
  1162                             char *copy = malloc(_s->size);
  1163                             assert(copy);
  1164                             if (copy == NULL)
  1165                                 goto enomem;
  1166                             memcpy(copy, _s->value, _s->size);
  1167                             _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1168                             if (_m == NULL)
  1169                                 goto enomem;
  1170                         }
  1171                     }
  1172                     else {
  1173                         char *copy = malloc(_s->size);
  1174                         assert(copy);
  1175                         if (copy == NULL)
  1176                             goto enomem;
  1177                         memcpy(copy, _s->value, _s->size);
  1178                         _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1179                         if (_m == NULL)
  1180                             goto enomem;
  1181                     }
  1182                 }
  1183 
  1184                 break;
  1185 
  1186             default:
  1187                 // BUG: must implement more
  1188                 NOT_IMPLEMENTED
  1189         }
  1190 
  1191         switch (src->enc_format) {
  1192             case PEP_enc_PGP_MIME:
  1193             case PEP_enc_pieces:
  1194                 status = copy_fields(msg, src);
  1195                 if (status != PEP_STATUS_OK)
  1196                     goto pep_error;
  1197 
  1198                 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
  1199                 {
  1200                     char * shortmsg;
  1201                     char * longmsg;
  1202 
  1203                     int r = seperate_short_and_long(msg->longmsg, &shortmsg,
  1204                             &longmsg);
  1205                     if (r == -1)
  1206                         goto enomem;
  1207 
  1208                     free(msg->shortmsg);
  1209                     free(msg->longmsg);
  1210 
  1211                     msg->shortmsg = shortmsg;
  1212                     msg->longmsg = longmsg;
  1213                 }
  1214                 else {
  1215                     msg->shortmsg = strdup(src->shortmsg);
  1216                     if (msg->shortmsg == NULL)
  1217                         goto enomem;
  1218                 }
  1219                 break;
  1220 
  1221             default:
  1222                 // BUG: must implement more
  1223                 NOT_IMPLEMENTED
  1224         }
  1225 
  1226         import_attached_keys(session, msg);
  1227     }
  1228 
  1229     if (msg)
  1230         decorate_message(msg, *color, _keylist);
  1231 
  1232     *dst = msg;
  1233     *keylist = _keylist;
  1234 
  1235     return PEP_STATUS_OK;
  1236 
  1237 enomem:
  1238     status = PEP_OUT_OF_MEMORY;
  1239 
  1240 pep_error:
  1241     free_message(msg);
  1242     free_stringlist(_keylist);
  1243 
  1244     return status;
  1245 }
  1246 
  1247 DYNAMIC_API PEP_STATUS outgoing_message_color(
  1248         PEP_SESSION session,
  1249         message *msg,
  1250         PEP_color *color
  1251     )
  1252 {
  1253     PEP_STATUS status = PEP_STATUS_OK;
  1254     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1255     bool comm_type_determined = false;
  1256     identity_list * il;
  1257 
  1258     assert(session);
  1259     assert(msg);
  1260     assert(msg->from);
  1261     assert(msg->dir == PEP_dir_outgoing);
  1262     assert(color);
  1263 
  1264     if (!(session && msg && color))
  1265         return PEP_ILLEGAL_VALUE;
  1266 
  1267     if (msg->from == NULL || msg->dir != PEP_dir_outgoing)
  1268         return PEP_ILLEGAL_VALUE;
  1269 
  1270     *color = PEP_rating_undefined;
  1271 
  1272     status = myself(session, msg->from);
  1273     if (status != PEP_STATUS_OK)
  1274         return status;
  1275 
  1276     for (il = msg->to; il != NULL; il = il->next) {
  1277         if (il->ident) {
  1278             update_identity(session, il->ident);
  1279             max_comm_type = _get_comm_type(session, max_comm_type,
  1280                     il->ident);
  1281             comm_type_determined = true;
  1282         }
  1283     }
  1284 
  1285     for (il = msg->cc; il != NULL; il = il->next) {
  1286         if (il->ident) {
  1287             update_identity(session, il->ident);
  1288             max_comm_type = _get_comm_type(session, max_comm_type,
  1289                     il->ident);
  1290             comm_type_determined = true;
  1291         }
  1292     }
  1293 
  1294     if (comm_type_determined == false)
  1295         *color = PEP_rating_undefined;
  1296     else
  1297         *color = MAX(_rating(max_comm_type), PEP_rating_unencrypted);
  1298 
  1299     return PEP_STATUS_OK;
  1300 }
  1301 
  1302 DYNAMIC_API PEP_STATUS identity_color(
  1303         PEP_SESSION session,
  1304         pEp_identity *ident,
  1305         PEP_color *color
  1306     )
  1307 {
  1308     PEP_STATUS status = PEP_STATUS_OK;
  1309 
  1310     assert(session);
  1311     assert(ident);
  1312     assert(color);
  1313 
  1314     if (!(session && ident && color))
  1315         return PEP_ILLEGAL_VALUE;
  1316 
  1317     if (ident->me)
  1318         status = myself(session, ident);
  1319     else
  1320         status = update_identity(session, ident);
  1321 
  1322     if (status == PEP_STATUS_OK)
  1323         *color = _rating(ident->comm_type);
  1324 
  1325     return status;
  1326 }
  1327