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