src/message_api.c
author Volker Birk <vb@pep-project.org>
Mon, 22 Feb 2016 13:56:31 +0100
changeset 465 89d10f171fc5
parent 445 5db143765d47
child 466 b23070ac6ddc
permissions -rw-r--r--
passive mode
     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_mistrust:
   570         return "mistrust";
   571     case PEP_rating_b0rken:
   572         return "b0rken";
   573     case PEP_rating_under_attack:
   574         return "unter_attack";
   575     default:
   576         return "undefined";
   577     }
   578 }
   579 
   580 static void decorate_message(
   581     message *msg,
   582     PEP_color color,
   583     stringlist_t *keylist
   584     )
   585 {
   586     assert(msg);
   587 
   588     add_opt_field(msg, "X-pEp-Version", "1.0");
   589     
   590     if (color != PEP_rating_undefined)
   591         add_opt_field(msg, "X-EncStatus", color_to_string(color));
   592 
   593     if (keylist) {
   594         char *_keylist = keylist_to_string(keylist);
   595         add_opt_field(msg, "X-KeyList", _keylist);
   596         free(_keylist);
   597     }
   598 }
   599 
   600 static PEP_color _rating(PEP_comm_type ct)
   601 {
   602     if (ct == PEP_ct_unknown)
   603         return PEP_rating_undefined;
   604 
   605     else if (ct == PEP_ct_compromized)
   606         return PEP_rating_under_attack;
   607 
   608     else if (ct == PEP_ct_mistrusted)
   609         return PEP_rating_mistrust;
   610     
   611     else if (ct >= PEP_ct_confirmed_enc_anon)
   612         return PEP_rating_trusted_and_anonymized;
   613 
   614     else if (ct >= PEP_ct_strong_encryption)
   615         return PEP_rating_trusted;
   616 
   617     else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
   618         return PEP_rating_reliable;
   619 
   620     else if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel)
   621         return PEP_rating_unencrypted;
   622 
   623     else
   624         return PEP_rating_unreliable;
   625 }
   626 
   627 static bool is_encrypted_attachment(const bloblist_t *blob)
   628 {
   629     char *ext;
   630 
   631     assert(blob);
   632 
   633     if (blob->filename == NULL)
   634         return false;
   635 
   636     ext = strrchr(blob->filename, '.');
   637     if (ext == NULL)
   638         return false;
   639 
   640     if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
   641         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
   642             strcmp(ext, ".asc") == 0)
   643             return true;
   644     }
   645     else if (strcmp(blob->mime_type, "text/plain") == 0) {
   646         if (strcmp(ext, ".asc") == 0)
   647             return true;
   648     }
   649 
   650     return false;
   651 }
   652 
   653 static bool is_encrypted_html_attachment(const bloblist_t *blob)
   654 {
   655     assert(blob);
   656     assert(blob->filename);
   657 
   658     if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
   659         if (strcmp(blob->filename + 11, ".pgp") == 0 ||
   660             strcmp(blob->filename + 11, ".asc") == 0)
   661             return true;
   662     }
   663 
   664     return false;
   665 }
   666 
   667 static char * without_double_ending(const char *filename)
   668 {
   669     char *ext;
   670 
   671     assert(filename);
   672 
   673     ext = strrchr(filename, '.');
   674     if (ext == NULL)
   675         return NULL;
   676 
   677     return strndup(filename, ext - filename);
   678 }
   679 
   680 static PEP_color decrypt_color(PEP_STATUS status)
   681 {
   682     switch (status) {
   683     case PEP_UNENCRYPTED:
   684     case PEP_VERIFIED:
   685     case PEP_VERIFY_NO_KEY:
   686     case PEP_VERIFIED_AND_TRUSTED:
   687         return PEP_rating_unencrypted;
   688 
   689     case PEP_DECRYPTED:
   690         return PEP_rating_unreliable;
   691 
   692     case PEP_DECRYPTED_AND_VERIFIED:
   693         return PEP_rating_reliable;
   694 
   695     case PEP_DECRYPT_NO_KEY:
   696         return PEP_rating_have_no_key;
   697 
   698     case PEP_DECRYPT_WRONG_FORMAT:
   699     case PEP_CANNOT_DECRYPT_UNKNOWN:
   700         return PEP_rating_cannot_decrypt;
   701 
   702     default:
   703         return PEP_rating_undefined;
   704     }
   705 }
   706 
   707 static PEP_color key_color(PEP_SESSION session, const char *fpr)
   708 {
   709     PEP_comm_type comm_type = PEP_ct_unknown;
   710 
   711     assert(session);
   712     assert(fpr);
   713 
   714     PEP_STATUS status = get_key_rating(session, fpr, &comm_type);
   715     if (status != PEP_STATUS_OK)
   716         return PEP_rating_undefined;
   717 
   718     return _rating(comm_type);
   719 }
   720 
   721 static PEP_color keylist_color(PEP_SESSION session, stringlist_t *keylist)
   722 {
   723     PEP_color color = PEP_rating_reliable;
   724 
   725     assert(keylist && keylist->value);
   726     if (keylist == NULL || keylist->value == NULL)
   727         return PEP_rating_unencrypted;
   728 
   729     stringlist_t *_kl;
   730     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   731         PEP_comm_type ct;
   732         PEP_STATUS status;
   733 
   734         color = key_color(session, _kl->value);
   735         if (color == PEP_rating_under_attack)
   736             return PEP_rating_under_attack;
   737 
   738         if (color >= PEP_rating_reliable) {
   739             status = least_trust(session, _kl->value, &ct);
   740             if (status != PEP_STATUS_OK)
   741                 return PEP_rating_undefined;
   742             if (ct == PEP_ct_unknown)
   743                 color = PEP_rating_unreliable;
   744             else
   745                 color = _rating(ct);
   746         }
   747     }
   748 
   749     return color;
   750 }
   751 
   752 static PEP_comm_type _get_comm_type(
   753     PEP_SESSION session,
   754     PEP_comm_type max_comm_type,
   755     pEp_identity *ident
   756     )
   757 {
   758     PEP_STATUS status = update_identity(session, ident);
   759 
   760     if (max_comm_type == PEP_ct_compromized)
   761         return PEP_ct_compromized;
   762 
   763     if (status == PEP_STATUS_OK) {
   764         if (ident->comm_type == PEP_ct_compromized)
   765             return PEP_ct_compromized;
   766         else
   767             return MIN(max_comm_type, ident->comm_type);
   768     }
   769     else {
   770         return PEP_ct_unknown;
   771     }
   772 }
   773 
   774 void import_attached_keys(PEP_SESSION session, const message *msg)
   775 {
   776     assert(session);
   777     assert(msg);
   778 
   779     bloblist_t *bl;
   780     for (bl = msg->attachments; bl && bl->value; bl = bl->next) {
   781         assert(bl && bl->value && bl->size);
   782 
   783         // workaround for Apple Mail bugs
   784         if (is_mime_type(bl, "application/x-apple-msg-attachment")) {
   785             if (is_fileending(bl, ".asc")) {
   786                 if (strlen(bl->filename) == 14 &&
   787                         bl->filename[0] == '0' && bl->filename[1] == 'x')
   788                     import_key(session, bl->value, bl->size);
   789                 else if (strlen(bl->filename) == 12)
   790                     import_key(session, bl->value, bl->size);
   791             }
   792         }
   793         else if (bl->mime_type == NULL ||
   794                     is_mime_type(bl, "application/octet-stream")) {
   795             if (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   796                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))
   797                 import_key(session, bl->value, bl->size);
   798         }
   799         else if (is_mime_type(bl, "application/pgp-keys")) {
   800             import_key(session, bl->value, bl->size);
   801         }
   802         else if (is_mime_type(bl, "text/plain")) {
   803             if (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   804                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))
   805                 import_key(session, bl->value, bl->size);
   806         }
   807     }
   808     if(msg->from && msg->from->user_id && msg->from->address)
   809         update_identity(session, msg->from);
   810 }
   811 
   812 void attach_own_key(PEP_SESSION session, message *msg)
   813 {
   814     char *keydata;
   815     size_t size;
   816     bloblist_t *bl;
   817 
   818     assert(session);
   819     assert(msg);
   820 
   821     if (msg->dir == PEP_dir_incoming)
   822         return;
   823 
   824     assert(msg->from && msg->from->fpr);
   825     if (msg->from == NULL || msg->from->fpr == NULL)
   826         return;
   827 
   828     PEP_STATUS status = export_key(session, msg->from->fpr, &keydata, &size);
   829     assert(status == PEP_STATUS_OK);
   830     if (status != PEP_STATUS_OK)
   831         return;
   832     assert(size);
   833 
   834     bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
   835             "pEp_key.asc");
   836     if (msg->attachments == NULL && bl)
   837         msg->attachments = bl;
   838 }
   839 
   840 PEP_cryptotech determine_encryption_format(message *msg)
   841 {
   842     assert(msg);
   843 
   844     if (is_PGP_message_text(msg->longmsg)) {
   845         msg->enc_format = PEP_enc_pieces;
   846         return PEP_crypt_OpenPGP;
   847     }
   848     else if (msg->attachments && msg->attachments->next &&
   849             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
   850             is_PGP_message_text(msg->attachments->next->value)
   851         ) {
   852         msg->enc_format = PEP_enc_PGP_MIME;
   853         return PEP_crypt_OpenPGP;
   854     }
   855     else {
   856         msg->enc_format = PEP_enc_none;
   857         return PEP_crypt_none;
   858     }
   859 }
   860 
   861 DYNAMIC_API PEP_STATUS encrypt_message(
   862         PEP_SESSION session,
   863         message *src,
   864         stringlist_t * extra,
   865         message **dst,
   866         PEP_enc_format enc_format
   867     )
   868 {
   869     PEP_STATUS status = PEP_STATUS_OK;
   870     message * msg = NULL;
   871     stringlist_t * keys = NULL;
   872 
   873     assert(session);
   874     assert(src);
   875     assert(dst);
   876     assert(enc_format != PEP_enc_none);
   877 
   878     if (!(session && src && dst && enc_format != PEP_enc_none))
   879         return PEP_ILLEGAL_VALUE;
   880 
   881     determine_encryption_format(src);
   882     if (src->enc_format != PEP_enc_none)
   883         return PEP_ILLEGAL_VALUE;
   884 
   885     *dst = NULL;
   886 
   887     status = myself(session, src->from);
   888     if (status != PEP_STATUS_OK)
   889         goto pep_error;
   890 
   891     keys = new_stringlist(src->from->fpr);
   892     if (keys == NULL)
   893         goto enomem;
   894 
   895     stringlist_t *_k = keys;
   896 
   897     if (extra) {
   898         _k = stringlist_append(_k, extra);
   899         if (_k == NULL)
   900             goto enomem;
   901     }
   902 
   903     bool dest_keys_found = true;
   904 
   905     identity_list * _il;
   906     for (_il = src->to; _il && _il->ident; _il = _il->next) {
   907         PEP_STATUS _status = update_identity(session, _il->ident);
   908         if (_status != PEP_STATUS_OK) {
   909             status = _status;
   910             goto pep_error;
   911         }
   912 
   913         if (_il->ident->fpr && _il->ident->fpr[0]) {
   914             _k = stringlist_add(_k, _il->ident->fpr);
   915             if (_k == NULL)
   916                 goto enomem;
   917         }
   918         else {
   919             dest_keys_found = false;
   920             status = PEP_KEY_NOT_FOUND;
   921         }
   922     }
   923 
   924     for (_il = src->cc; _il && _il->ident; _il = _il->next) {
   925         PEP_STATUS _status = update_identity(session, _il->ident);
   926         if (_status != PEP_STATUS_OK)
   927         {
   928             status = _status;
   929             goto pep_error;
   930         }
   931 
   932         if (_il->ident->fpr && _il->ident->fpr[0]) {
   933             _k = stringlist_add(_k, _il->ident->fpr);
   934             if (_k == NULL)
   935                 goto enomem;
   936         }
   937         else {
   938             dest_keys_found = false;
   939             status = PEP_KEY_NOT_FOUND;
   940         }
   941     }
   942 
   943     if (!dest_keys_found) {
   944         free_stringlist(keys);
   945         if (!session->passive_mode)
   946             attach_own_key(session, src);
   947         return PEP_UNENCRYPTED;
   948     }
   949     else {
   950         msg = clone_to_empty_message(src);
   951         if (msg == NULL)
   952             goto enomem;
   953 
   954         attach_own_key(session, src);
   955 
   956         switch (enc_format) {
   957         case PEP_enc_PGP_MIME:
   958         case PEP_enc_PEP: // BUG: should be implemented extra
   959             status = encrypt_PGP_MIME(session, src, keys, msg);
   960             break;
   961 
   962         case PEP_enc_pieces:
   963             status = encrypt_PGP_in_pieces(session, src, keys, msg);
   964             break;
   965 
   966         /* case PEP_enc_PEP:
   967             // TODO: implement
   968             NOT_IMPLEMENTED */
   969 
   970         default:
   971             assert(0);
   972             status = PEP_ILLEGAL_VALUE;
   973             goto pep_error;
   974         }
   975         
   976         if (status == PEP_OUT_OF_MEMORY)
   977             goto enomem;
   978         
   979         if (status != PEP_STATUS_OK) {
   980             attach_own_key(session, src);
   981             goto pep_error;
   982         }
   983     }
   984 
   985     free_stringlist(keys);
   986 
   987     if (msg && msg->shortmsg == NULL)
   988         msg->shortmsg = strdup("pEp");
   989 
   990     if (msg)
   991         decorate_message(msg, PEP_rating_undefined, NULL);
   992 
   993     *dst = msg;
   994     return status;
   995 
   996 enomem:
   997     status = PEP_OUT_OF_MEMORY;
   998 
   999 pep_error:
  1000     free_stringlist(keys);
  1001     free_message(msg);
  1002 
  1003     return status;
  1004 }
  1005 
  1006 DYNAMIC_API PEP_STATUS decrypt_message(
  1007         PEP_SESSION session,
  1008         message *src,
  1009         message **dst,
  1010         stringlist_t **keylist,
  1011         PEP_color *color
  1012     )
  1013 {
  1014     PEP_STATUS status = PEP_STATUS_OK;
  1015     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  1016     message *msg = NULL;
  1017     char *ctext;
  1018     size_t csize;
  1019     char *ptext = NULL;
  1020     size_t psize;
  1021     stringlist_t *_keylist = NULL;
  1022 
  1023     assert(session);
  1024     assert(src);
  1025     assert(dst);
  1026     assert(keylist);
  1027     assert(color);
  1028 
  1029     if (!(session && src && dst && keylist && color))
  1030         return PEP_ILLEGAL_VALUE;
  1031 
  1032     import_attached_keys(session, src);
  1033     PEP_cryptotech crypto = determine_encryption_format(src);
  1034 
  1035     *dst = NULL;
  1036     *keylist = NULL;
  1037     *color = PEP_rating_undefined;
  1038  
  1039     switch (src->enc_format) {
  1040         case PEP_enc_none:
  1041             *color = PEP_rating_unencrypted;
  1042             return PEP_UNENCRYPTED;
  1043 
  1044         case PEP_enc_PGP_MIME:
  1045             ctext = src->attachments->next->value;
  1046             csize = src->attachments->next->size;
  1047             break;
  1048 
  1049         case PEP_enc_pieces:
  1050             ctext = src->longmsg;
  1051             csize = strlen(ctext);
  1052             break;
  1053 
  1054         default:
  1055             NOT_IMPLEMENTED
  1056     }
  1057     status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1058                                                    csize, &ptext, &psize, &_keylist);
  1059     if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1060         goto pep_error;
  1061 
  1062     decrypt_status = status;
  1063 
  1064     if (ptext) {
  1065         switch (src->enc_format) {
  1066             case PEP_enc_PGP_MIME:
  1067                 status = mime_decode_message(ptext, psize, &msg);
  1068                 if (status != PEP_STATUS_OK)
  1069                     goto pep_error;
  1070                 break;
  1071 
  1072             case PEP_enc_pieces:
  1073                 msg = clone_to_empty_message(src);
  1074                 if (msg == NULL)
  1075                     goto enomem;
  1076 
  1077                 msg->longmsg = strdup(ptext);
  1078                 if (msg->longmsg == NULL)
  1079                     goto enomem;
  1080 
  1081                 bloblist_t *_m = msg->attachments;
  1082                 if (_m == NULL && src->attachments && src->attachments->value) {
  1083                     msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
  1084                     _m = msg->attachments;
  1085                 }
  1086 
  1087                 bloblist_t *_s;
  1088                 for (_s = src->attachments; _s && _s->value; _s = _s->next) {
  1089                     if (is_encrypted_attachment(_s)) {
  1090                         stringlist_t *_keylist = NULL;
  1091                         char *attctext;
  1092                         size_t attcsize;
  1093 
  1094                         attctext = _s->value;
  1095                         attcsize = _s->size;
  1096 
  1097                         status = decrypt_and_verify(session, attctext, attcsize,
  1098                                 &ptext, &psize, &_keylist);
  1099                         free_stringlist(_keylist);
  1100 
  1101                         if (ptext) {
  1102                             if (is_encrypted_html_attachment(_s)) {
  1103                                 msg->longmsg_formatted = strdup(ptext);
  1104                                 if (msg->longmsg_formatted == NULL)
  1105                                     goto pep_error;
  1106                             }
  1107                             else {
  1108                                 char * mime_type = "application/octet-stream";
  1109                                 char * filename =
  1110                                     without_double_ending(_s->filename);
  1111                                 if (filename == NULL)
  1112                                     goto enomem;
  1113 
  1114                                 char *_ptext = malloc(psize);
  1115                                 assert(_ptext);
  1116                                 if (_ptext == NULL)
  1117                                     goto enomem;
  1118                                 memcpy(_ptext, ptext, psize);
  1119 
  1120                                 _m = bloblist_add(_m, _ptext, psize, mime_type,
  1121                                     filename);
  1122                                 if (_m == NULL)
  1123                                     goto enomem;
  1124 
  1125                                 if (msg->attachments == NULL)
  1126                                     msg->attachments = _m;
  1127                             }
  1128                         }
  1129                         else {
  1130                             char *copy = malloc(_s->size);
  1131                             assert(copy);
  1132                             if (copy == NULL)
  1133                                 goto enomem;
  1134                             memcpy(copy, _s->value, _s->size);
  1135                             _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1136                             if (_m == NULL)
  1137                                 goto enomem;
  1138                         }
  1139                     }
  1140                     else {
  1141                         char *copy = malloc(_s->size);
  1142                         assert(copy);
  1143                         if (copy == NULL)
  1144                             goto enomem;
  1145                         memcpy(copy, _s->value, _s->size);
  1146                         _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1147                         if (_m == NULL)
  1148                             goto enomem;
  1149                     }
  1150                 }
  1151 
  1152                 break;
  1153 
  1154             default:
  1155                 // BUG: must implement more
  1156                 NOT_IMPLEMENTED
  1157         }
  1158         
  1159         switch (src->enc_format) {
  1160             case PEP_enc_PGP_MIME:
  1161             case PEP_enc_pieces:
  1162                 status = copy_fields(msg, src);
  1163                 if (status != PEP_STATUS_OK)
  1164                     goto pep_error;
  1165 
  1166                 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
  1167                 {
  1168                     char * shortmsg;
  1169                     char * longmsg;
  1170 
  1171                     int r = seperate_short_and_long(msg->longmsg, &shortmsg,
  1172                             &longmsg);
  1173                     if (r == -1)
  1174                         goto enomem;
  1175 
  1176                     free(msg->shortmsg);
  1177                     free(msg->longmsg);
  1178 
  1179                     msg->shortmsg = shortmsg;
  1180                     msg->longmsg = longmsg;
  1181                 }
  1182                 else {
  1183                     msg->shortmsg = strdup(src->shortmsg);
  1184                     if (msg->shortmsg == NULL)
  1185                         goto enomem;
  1186                 }
  1187                 break;
  1188 
  1189             default:
  1190                 // BUG: must implement more
  1191                 NOT_IMPLEMENTED
  1192         }
  1193         
  1194         import_attached_keys(session, msg);
  1195         
  1196         if(decrypt_status == PEP_DECRYPTED){
  1197             
  1198             // In case message did decrypt, but no valid signature could be found
  1199             // then retry decrypt+verify after importing key.
  1200             // TODO optimize if import_attached_keys didn't import any key
  1201             
  1202             char *re_ptext = NULL;
  1203             size_t re_psize;
  1204             
  1205             free_stringlist(_keylist);
  1206             _keylist = NULL;
  1207 
  1208             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1209                 csize, &re_ptext, &re_psize, &_keylist);
  1210             
  1211             if(re_ptext)
  1212                 free(re_ptext);
  1213             
  1214             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1215                 goto pep_error;
  1216             
  1217             decrypt_status = status;
  1218         }
  1219         
  1220         *color = decrypt_color(decrypt_status);
  1221         
  1222         if (*color != PEP_rating_under_attack) {
  1223             PEP_color kl_color = PEP_rating_undefined;
  1224             
  1225             if (_keylist)
  1226                 kl_color = keylist_color(session, _keylist);
  1227             
  1228             if (kl_color == PEP_rating_under_attack) {
  1229                 *color = PEP_rating_under_attack;
  1230             }
  1231             else if (kl_color == PEP_rating_mistrust) {
  1232                 *color = PEP_rating_mistrust;
  1233             }
  1234             else if (*color >= PEP_rating_reliable &&
  1235                      kl_color < PEP_rating_reliable) {
  1236                 *color = PEP_rating_unreliable;
  1237             }
  1238             else if (*color >= PEP_rating_reliable &&
  1239                      kl_color >= PEP_rating_reliable) {
  1240                 if (!(src->from && src->from->user_id && src->from->user_id[0])) {
  1241                     *color = PEP_rating_unreliable;
  1242                 }
  1243                 else {
  1244                     char *fpr = _keylist->value;
  1245                     pEp_identity *_from = new_identity(src->from->address, fpr,
  1246                                                        src->from->user_id, src->from->username);
  1247                     if (_from == NULL)
  1248                         goto enomem;
  1249                     status = update_identity(session, _from);
  1250                     if (_from->comm_type != PEP_ct_unknown)
  1251                         *color = _rating(_from->comm_type);
  1252                     free_identity(_from);
  1253                     if (status != PEP_STATUS_OK)
  1254                         goto pep_error;
  1255                 }
  1256             }
  1257         }
  1258     }
  1259 
  1260     if (msg)
  1261         decorate_message(msg, *color, _keylist);
  1262 
  1263     *dst = msg;
  1264     *keylist = _keylist;
  1265 
  1266     return PEP_STATUS_OK;
  1267 
  1268 enomem:
  1269     status = PEP_OUT_OF_MEMORY;
  1270 
  1271 pep_error:
  1272     free_message(msg);
  1273     free_stringlist(_keylist);
  1274 
  1275     return status;
  1276 }
  1277 
  1278 DYNAMIC_API PEP_STATUS outgoing_message_color(
  1279         PEP_SESSION session,
  1280         message *msg,
  1281         PEP_color *color
  1282     )
  1283 {
  1284     PEP_STATUS status = PEP_STATUS_OK;
  1285     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1286     bool comm_type_determined = false;
  1287     identity_list * il;
  1288 
  1289     assert(session);
  1290     assert(msg);
  1291     assert(msg->from);
  1292     assert(msg->dir == PEP_dir_outgoing);
  1293     assert(color);
  1294 
  1295     if (!(session && msg && color))
  1296         return PEP_ILLEGAL_VALUE;
  1297 
  1298     if (msg->from == NULL || msg->dir != PEP_dir_outgoing)
  1299         return PEP_ILLEGAL_VALUE;
  1300 
  1301     *color = PEP_rating_undefined;
  1302 
  1303     status = myself(session, msg->from);
  1304     if (status != PEP_STATUS_OK)
  1305         return status;
  1306 
  1307     for (il = msg->to; il != NULL; il = il->next) {
  1308         if (il->ident) {
  1309             update_identity(session, il->ident);
  1310             max_comm_type = _get_comm_type(session, max_comm_type,
  1311                     il->ident);
  1312             comm_type_determined = true;
  1313         }
  1314     }
  1315 
  1316     for (il = msg->cc; il != NULL; il = il->next) {
  1317         if (il->ident) {
  1318             update_identity(session, il->ident);
  1319             max_comm_type = _get_comm_type(session, max_comm_type,
  1320                     il->ident);
  1321             comm_type_determined = true;
  1322         }
  1323     }
  1324 
  1325     if (comm_type_determined == false)
  1326         *color = PEP_rating_undefined;
  1327     else
  1328         *color = MAX(_rating(max_comm_type), PEP_rating_unencrypted);
  1329 
  1330     return PEP_STATUS_OK;
  1331 }
  1332 
  1333 DYNAMIC_API PEP_STATUS identity_color(
  1334         PEP_SESSION session,
  1335         pEp_identity *ident,
  1336         PEP_color *color
  1337     )
  1338 {
  1339     PEP_STATUS status = PEP_STATUS_OK;
  1340 
  1341     assert(session);
  1342     assert(ident);
  1343     assert(color);
  1344 
  1345     if (!(session && ident && color))
  1346         return PEP_ILLEGAL_VALUE;
  1347 
  1348     if (ident->me)
  1349         status = myself(session, ident);
  1350     else
  1351         status = update_identity(session, ident);
  1352 
  1353     if (status == PEP_STATUS_OK)
  1354         *color = _rating(ident->comm_type);
  1355 
  1356     return status;
  1357 }
  1358