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