src/message_api.c
author Edouard Tisserant <edouard@pep-project.org>
Thu, 08 Jun 2017 12:03:50 +0200
changeset 1835 ca0126bfcd52
parent 1832 93c627c7f709
child 1843 d56e2234b665
child 1852 8b786dad8b60
permissions -rw-r--r--
ENGINE-179 update sender identity in re_evaluate_message_rating
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include "pEp_internal.h"
     5 #include "message_api.h"
     6 
     7 #include "platform.h"
     8 #include "mime.h"
     9 #include "sync_fsm.h"
    10 
    11 #include <assert.h>
    12 #include <string.h>
    13 #include <stdlib.h>
    14 
    15 
    16 #ifndef _MIN
    17 #define _MIN(A, B) ((B) > (A) ? (A) : (B))
    18 #endif
    19 #ifndef _MAX
    20 #define _MAX(A, B) ((B) > (A) ? (B) : (A))
    21 #endif
    22 
    23 
    24 static bool string_equality(const char *s1, const char *s2)
    25 {
    26     if (s1 == NULL || s2 == NULL)
    27         return false;
    28 
    29     assert(s1 && s2);
    30 
    31     return strcmp(s1, s2) == 0;
    32 }
    33 
    34 static bool is_mime_type(const bloblist_t *bl, const char *mt)
    35 {
    36     assert(mt);
    37 
    38     return bl && string_equality(bl->mime_type, mt);
    39 }
    40 
    41 //
    42 // This function presumes the file ending is a proper substring of the
    43 // filename (i.e. if bl->filename is "a.pgp" and fe is ".pgp", it will
    44 // return true, but if bl->filename is ".pgp" and fe is ".pgp", it will
    45 // return false. This is desired behaviour.
    46 //
    47 static bool is_fileending(const bloblist_t *bl, const char *fe)
    48 {
    49     assert(fe);
    50 
    51     if (bl == NULL || bl->filename == NULL || fe == NULL)
    52         return false;
    53 
    54     assert(bl && bl->filename);
    55 
    56     size_t fe_len = strlen(fe);
    57     size_t fn_len = strlen(bl->filename);
    58 
    59     if (fn_len <= fe_len)
    60         return false;
    61 
    62     assert(fn_len > fe_len);
    63 
    64     return strcmp(bl->filename + (fn_len - fe_len), fe) == 0;
    65 }
    66 
    67 void add_opt_field(message *msg, const char *name, const char *value)
    68 {
    69     assert(msg && name && value);
    70 
    71     if (msg && name && value) {
    72         stringpair_t *pair = new_stringpair(name, value);
    73         if (pair == NULL)
    74             return;
    75 
    76         stringpair_list_t *field = stringpair_list_add(msg->opt_fields, pair);
    77         if (field == NULL)
    78         {
    79             free_stringpair(pair);
    80             return;
    81         }
    82 
    83         if (msg->opt_fields == NULL)
    84             msg->opt_fields = field;
    85     }
    86 }
    87 
    88 static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
    89 {
    90     assert(shortmsg);
    91     assert(strcmp(shortmsg, "pEp") != 0);
    92 
    93     if (!shortmsg || strcmp(shortmsg, "pEp") == 0) {
    94         if (!longmsg) {
    95             return NULL;
    96         }
    97         else {
    98             char *result = strdup(longmsg);
    99             assert(result);
   100             return result;
   101         }
   102     }
   103 
   104     if (longmsg == NULL)
   105         longmsg = "";
   106 
   107     const char * const subject = "Subject: ";
   108     const size_t SUBJ_LEN = 9;
   109     const char * const newlines = "\n\n";
   110     const size_t NL_LEN = 2;
   111 
   112     const size_t bufsize = SUBJ_LEN + strlen(shortmsg) + NL_LEN + strlen(longmsg) + 1;
   113     char * ptext = calloc(1, bufsize);
   114     assert(ptext);
   115     if (ptext == NULL)
   116         return NULL;
   117 
   118     strlcpy(ptext, subject, bufsize);
   119     strlcat(ptext, shortmsg, bufsize);
   120     strlcat(ptext, newlines, bufsize);
   121     strlcat(ptext, longmsg, bufsize);
   122 
   123     return ptext;
   124 }
   125 
   126 static int separate_short_and_long(const char *src, char **shortmsg, char **longmsg)
   127 {
   128     char *_shortmsg = NULL;
   129     char *_longmsg = NULL;
   130 
   131     assert(src);
   132     assert(shortmsg);
   133     assert(longmsg);
   134 
   135     if (src == NULL || shortmsg == NULL || longmsg == NULL)
   136         return -1;
   137 
   138     *shortmsg = NULL;
   139     *longmsg = NULL;
   140 
   141     if (strncasecmp(src, "subject: ", 9) == 0) {
   142         char *line_end = strchr(src, '\n');
   143 
   144         if (line_end == NULL) {
   145             _shortmsg = strdup(src + 9);
   146             assert(_shortmsg);
   147             if (_shortmsg == NULL)
   148                 goto enomem;
   149             // _longmsg = NULL;
   150         }
   151         else {
   152             size_t n = line_end - src;
   153 
   154             if (*(line_end - 1) == '\r')
   155                 _shortmsg = strndup(src + 9, n - 10);
   156             else
   157                 _shortmsg = strndup(src + 9, n - 9);
   158             assert(_shortmsg);
   159             if (_shortmsg == NULL)
   160                 goto enomem;
   161 
   162             while (*(src + n) && (*(src + n) == '\n' || *(src + n) == '\r'))
   163                 ++n;
   164 
   165             if (*(src + n)) {
   166                 _longmsg = strdup(src + n);
   167                 assert(_longmsg);
   168                 if (_longmsg == NULL)
   169                     goto enomem;
   170             }
   171         }
   172     }
   173     else {
   174         _shortmsg = strdup("");
   175         assert(_shortmsg);
   176         if (_shortmsg == NULL)
   177             goto enomem;
   178         _longmsg = strdup(src);
   179         assert(_longmsg);
   180         if (_longmsg == NULL)
   181             goto enomem;
   182     }
   183 
   184     *shortmsg = _shortmsg;
   185     *longmsg = _longmsg;
   186 
   187     return 0;
   188 
   189 enomem:
   190     free(_shortmsg);
   191     free(_longmsg);
   192 
   193     return -1;
   194 }
   195 
   196 static PEP_STATUS copy_fields(message *dst, const message *src)
   197 {
   198     assert(dst);
   199     assert(src);
   200 
   201     if(!(dst && src))
   202         return PEP_ILLEGAL_VALUE;
   203 
   204     free_timestamp(dst->sent);
   205     dst->sent = NULL;
   206     if (src->sent) {
   207         dst->sent = timestamp_dup(src->sent);
   208         if (dst->sent == NULL)
   209             return PEP_OUT_OF_MEMORY;
   210     }
   211 
   212     free_timestamp(dst->recv);
   213     dst->recv = NULL;
   214     if (src->recv) {
   215         dst->recv = timestamp_dup(src->recv);
   216         if (dst->recv == NULL)
   217             return PEP_OUT_OF_MEMORY;
   218     }
   219 
   220     free_identity(dst->from);
   221     dst->from = NULL;
   222     if (src->from) {
   223         dst->from = identity_dup(src->from);
   224         if (dst->from == NULL)
   225             return PEP_OUT_OF_MEMORY;
   226     }
   227 
   228     free_identity_list(dst->to);
   229     dst->to = NULL;
   230     if (src->to && src->to->ident) {
   231         dst->to = identity_list_dup(src->to);
   232         if (dst->to == NULL)
   233             return PEP_OUT_OF_MEMORY;
   234     }
   235 
   236     free_identity(dst->recv_by);
   237     dst->recv_by = NULL;
   238     if (src->recv_by) {
   239         dst->recv_by = identity_dup(src->recv_by);
   240         if (dst->recv_by == NULL)
   241             return PEP_OUT_OF_MEMORY;
   242     }
   243 
   244     free_identity_list(dst->cc);
   245     dst->cc = NULL;
   246     if (src->cc && src->cc->ident) {
   247         dst->cc = identity_list_dup(src->cc);
   248         if (dst->cc == NULL)
   249             return PEP_OUT_OF_MEMORY;
   250     }
   251 
   252     free_identity_list(dst->bcc);
   253     dst->bcc = NULL;
   254     if (src->bcc && src->bcc->ident) {
   255         dst->bcc = identity_list_dup(src->bcc);
   256         if (dst->bcc == NULL)
   257             return PEP_OUT_OF_MEMORY;
   258     }
   259 
   260     free_identity_list(dst->reply_to);
   261     dst->reply_to = NULL;
   262     if (src->reply_to && src->reply_to->ident) {
   263         dst->reply_to = identity_list_dup(src->reply_to);
   264         if (dst->reply_to == NULL)
   265             return PEP_OUT_OF_MEMORY;
   266     }
   267 
   268     free_stringlist(dst->in_reply_to);
   269     dst->in_reply_to = NULL;
   270     if (src->in_reply_to && src->in_reply_to->value) {
   271         dst->in_reply_to = stringlist_dup(src->in_reply_to);
   272         if (dst->in_reply_to == NULL)
   273             return PEP_OUT_OF_MEMORY;
   274     }
   275 
   276     free_stringlist(dst->references);
   277     dst->references = NULL;
   278     if (src->references) {
   279         dst->references = stringlist_dup(src->references);
   280         if (dst->references == NULL)
   281             return PEP_OUT_OF_MEMORY;
   282     }
   283 
   284     free_stringlist(dst->keywords);
   285     dst->keywords = NULL;
   286     if (src->keywords && src->keywords->value) {
   287         dst->keywords = stringlist_dup(src->keywords);
   288         if (dst->keywords == NULL)
   289             return PEP_OUT_OF_MEMORY;
   290     }
   291 
   292     free(dst->comments);
   293     dst->comments = NULL;
   294     if (src->comments) {
   295         dst->comments = strdup(src->comments);
   296         assert(dst->comments);
   297         if (dst->comments == NULL)
   298             return PEP_OUT_OF_MEMORY;
   299     }
   300 
   301     free_stringpair_list(dst->opt_fields);
   302     dst->opt_fields = NULL;
   303     if (src->opt_fields) {
   304         dst->opt_fields = stringpair_list_dup(src->opt_fields);
   305         if (dst->opt_fields == NULL)
   306             return PEP_OUT_OF_MEMORY;
   307     }
   308 
   309     return PEP_STATUS_OK;
   310 }
   311 
   312 static message * clone_to_empty_message(const message * src)
   313 {
   314     PEP_STATUS status;
   315     message * msg = NULL;
   316 
   317     assert(src);
   318     if (src == NULL)
   319         return NULL;
   320 
   321     msg = calloc(1, sizeof(message));
   322     assert(msg);
   323     if (msg == NULL)
   324         goto enomem;
   325 
   326     msg->dir = src->dir;
   327 
   328     status = copy_fields(msg, src);
   329     if (status != PEP_STATUS_OK)
   330         goto enomem;
   331 
   332     return msg;
   333 
   334 enomem:
   335     free_message(msg);
   336     return NULL;
   337 }
   338 
   339 static PEP_STATUS encrypt_PGP_MIME(
   340     PEP_SESSION session,
   341     const message *src,
   342     stringlist_t *keys,
   343     message *dst,
   344     PEP_encrypt_flags_t flags
   345     )
   346 {
   347     PEP_STATUS status = PEP_STATUS_OK;
   348     bool free_ptext = false;
   349     char *ptext = NULL;
   350     char *ctext = NULL;
   351     char *mimetext = NULL;
   352     size_t csize;
   353     assert(dst->longmsg == NULL);
   354     dst->enc_format = PEP_enc_PGP_MIME;
   355 
   356     if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
   357         if (session->unencrypted_subject) {
   358             dst->shortmsg = strdup(src->shortmsg);
   359             assert(dst->shortmsg);
   360             if (dst->shortmsg == NULL)
   361                 goto enomem;
   362             ptext = src->longmsg;
   363         }
   364         else {
   365             ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   366             if (ptext == NULL)
   367                 goto enomem;
   368             free_ptext = true;
   369         }
   370     }
   371     else if (src->longmsg) {
   372         ptext = src->longmsg;
   373     }
   374     else {
   375         ptext = "pEp";
   376     }
   377 
   378     message *_src = calloc(1, sizeof(message));
   379     assert(_src);
   380     if (_src == NULL)
   381         goto enomem;
   382     _src->longmsg = ptext;
   383     _src->longmsg_formatted = src->longmsg_formatted;
   384     _src->attachments = src->attachments;
   385     _src->enc_format = PEP_enc_none;
   386     status = mime_encode_message(_src, true, &mimetext);
   387     assert(status == PEP_STATUS_OK);
   388     if (status != PEP_STATUS_OK)
   389         goto pep_error;
   390 
   391     if (free_ptext){
   392         free(ptext);
   393         free_ptext=0;
   394     }
   395     free(_src);
   396     assert(mimetext);
   397     if (mimetext == NULL)
   398         goto pep_error;
   399 
   400     if (flags & PEP_encrypt_flag_force_unsigned)
   401         status = encrypt_only(session, keys, mimetext, strlen(mimetext),
   402             &ctext, &csize);
   403     else
   404         status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
   405             &ctext, &csize);
   406     free(mimetext);
   407     if (ctext == NULL)
   408         goto pep_error;
   409 
   410     dst->longmsg = strdup("this message was encrypted with p≡p "
   411         "https://pEp-project.org");
   412     assert(dst->longmsg);
   413     if (dst->longmsg == NULL)
   414         goto enomem;
   415 
   416     char *v = strdup("Version: 1");
   417     assert(v);
   418     if (v == NULL)
   419         goto enomem;
   420 
   421     bloblist_t *_a = new_bloblist(v, strlen(v), "application/pgp-encrypted", NULL);
   422     if (_a == NULL)
   423         goto enomem;
   424     dst->attachments = _a;
   425 
   426     _a = bloblist_add(_a, ctext, csize, "application/octet-stream",
   427         "msg.asc");
   428     if (_a == NULL)
   429         goto enomem;
   430 
   431     return PEP_STATUS_OK;
   432 
   433 enomem:
   434     status = PEP_OUT_OF_MEMORY;
   435 
   436 pep_error:
   437     if (free_ptext)
   438         free(ptext);
   439     free(ctext);
   440     return status;
   441 }
   442 
   443 static PEP_STATUS encrypt_PGP_in_pieces(
   444     PEP_SESSION session,
   445     const message *src,
   446     stringlist_t *keys,
   447     message *dst,
   448     PEP_encrypt_flags_t flags
   449     )
   450 {
   451     PEP_STATUS status = PEP_STATUS_OK;
   452     char *ctext = NULL;
   453     size_t csize;
   454     char *ptext = NULL;
   455     bool free_ptext = false;
   456 
   457     assert(dst->longmsg == NULL);
   458     assert(dst->attachments == NULL);
   459 
   460     dst->enc_format = PEP_enc_pieces;
   461 
   462     bool nosign = (flags & PEP_encrypt_flag_force_unsigned);
   463 
   464     if (src->shortmsg && src->shortmsg[0] && strcmp(src->shortmsg, "pEp") != 0) {
   465         if (session->unencrypted_subject) {
   466             dst->shortmsg = strdup(src->shortmsg);
   467             assert(dst->shortmsg);
   468             if (dst->shortmsg == NULL)
   469                 goto enomem;
   470             ptext = src->longmsg;
   471         }
   472         else {
   473             ptext = combine_short_and_long(src->shortmsg, src->longmsg);
   474             if (ptext == NULL)
   475                 goto enomem;
   476             free_ptext = true;
   477         }
   478 
   479         if (nosign)
   480             status = encrypt_only(session, keys, ptext, strlen(ptext), &ctext,
   481                 &csize);
   482         else 
   483             status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   484                 &csize);
   485         if (free_ptext)
   486             free(ptext);
   487         free_ptext = false;
   488         if (ctext) {
   489             dst->longmsg = ctext;
   490         }
   491         else {
   492             goto pep_error;
   493         }
   494     }
   495     else if (src->longmsg && src->longmsg[0]) {
   496         ptext = src->longmsg;
   497         if (nosign)
   498             status = encrypt_only(session, keys, ptext, strlen(ptext), &ctext,
   499                 &csize);
   500         else 
   501             status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   502                 &csize);
   503         if (ctext) {
   504             dst->longmsg = ctext;
   505         }
   506         else {
   507             goto pep_error;
   508         }
   509     }
   510     else {
   511         dst->longmsg = strdup("");
   512         assert(dst->longmsg);
   513         if (dst->longmsg == NULL)
   514             goto enomem;
   515     }
   516 
   517     if (src->longmsg_formatted && src->longmsg_formatted[0]) {
   518         ptext = src->longmsg_formatted;
   519         if (nosign)
   520             status = encrypt_only(session, keys, ptext, strlen(ptext), &ctext,
   521                 &csize);
   522         else 
   523             status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
   524                 &csize);
   525         if (ctext) {
   526 
   527             bloblist_t *_a = bloblist_add(dst->attachments, ctext, csize,
   528                 "application/octet-stream", "PGPexch.htm.pgp");
   529             if (_a == NULL)
   530                 goto enomem;
   531             if (dst->attachments == NULL)
   532                 dst->attachments = _a;
   533         }
   534         else {
   535             goto pep_error;
   536         }
   537     }
   538 
   539     if (src->attachments) {
   540         if (dst->attachments == NULL) {
   541             dst->attachments = new_bloblist(NULL, 0, NULL, NULL);
   542             if (dst->attachments == NULL)
   543                 goto enomem;
   544         }
   545 
   546         bloblist_t *_s = src->attachments;
   547         bloblist_t *_d = dst->attachments;
   548 
   549         for (int n = 0; _s; _s = _s->next) {
   550             if (_s->value == NULL && _s->size == 0) {
   551                 _d = bloblist_add(_d, NULL, 0, _s->mime_type, _s->filename);
   552                 if (_d == NULL)
   553                     goto enomem;
   554             }
   555             else {
   556                 size_t psize = _s->size;
   557                 ptext = _s->value;
   558                 if (nosign)
   559                     status = encrypt_only(session, keys, ptext, psize, &ctext,
   560                         &csize);
   561                 else 
   562                     status = encrypt_and_sign(session, keys, ptext, psize, &ctext,
   563                         &csize);
   564                 if (ctext) {
   565                     char *filename = NULL;
   566 
   567                     if (_s->filename) {
   568                         size_t len = strlen(_s->filename);
   569                         size_t bufsize = len + 5; // length of .pgp extension + NUL
   570                         filename = calloc(1, bufsize);
   571                         if (filename == NULL)
   572                             goto enomem;
   573 
   574                         strlcpy(filename, _s->filename, bufsize);
   575                         strlcat(filename, ".pgp", bufsize);
   576                     }
   577                     else {
   578                         filename = calloc(1, 20);
   579                         if (filename == NULL)
   580                             goto enomem;
   581 
   582                         ++n;
   583                         n &= 0xffff;
   584                         snprintf(filename, 20, "Attachment%d.pgp", n);
   585                     }
   586 
   587                     _d = bloblist_add(_d, ctext, csize, "application/octet-stream",
   588                         filename);
   589                     free(filename);
   590                     if (_d == NULL)
   591                         goto enomem;
   592                 }
   593                 else {
   594                     goto pep_error;
   595                 }
   596             }
   597         }
   598     }
   599 
   600     return PEP_STATUS_OK;
   601 
   602 enomem:
   603     status = PEP_OUT_OF_MEMORY;
   604 
   605 pep_error:
   606     if (free_ptext)
   607         free(ptext);
   608     return status;
   609 }
   610 
   611 static char * keylist_to_string(const stringlist_t *keylist)
   612 {
   613     if (keylist) {
   614         size_t size = stringlist_length(keylist);
   615 
   616         const stringlist_t *_kl;
   617         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   618             size += strlen(_kl->value);
   619         }
   620 
   621         char *result = calloc(1, size);
   622         if (result == NULL)
   623             return NULL;
   624 
   625         char *_r = result;
   626         for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   627             _r = stpcpy(_r, _kl->value);
   628             if (_kl->next && _kl->next->value)
   629                 _r = stpcpy(_r, ",");
   630         }
   631 
   632         return result;
   633     }
   634     else {
   635         return NULL;
   636     }
   637 }
   638 
   639 static const char * rating_to_string(PEP_rating rating)
   640 {
   641     switch (rating) {
   642     case PEP_rating_cannot_decrypt:
   643         return "cannot_decrypt";
   644     case PEP_rating_have_no_key:
   645         return "have_no_key";
   646     case PEP_rating_unencrypted:
   647         return "unencrypted";
   648     case PEP_rating_unencrypted_for_some:
   649         return "unencrypted_for_some";
   650     case PEP_rating_unreliable:
   651         return "unreliable";
   652     case PEP_rating_reliable:
   653         return "reliable";
   654     case PEP_rating_trusted:
   655         return "trusted";
   656     case PEP_rating_trusted_and_anonymized:
   657         return "trusted_and_anonymized";
   658     case PEP_rating_fully_anonymous:
   659         return "fully_anonymous";
   660     case PEP_rating_mistrust:
   661         return "mistrust";
   662     case PEP_rating_b0rken:
   663         return "b0rken";
   664     case PEP_rating_under_attack:
   665         return "under_attack";
   666     default:
   667         return "undefined";
   668     }
   669 }
   670 
   671 static void decorate_message(
   672     message *msg,
   673     PEP_rating rating,
   674     stringlist_t *keylist
   675     )
   676 {
   677     assert(msg);
   678 
   679     add_opt_field(msg, "X-pEp-Version", PEP_VERSION);
   680 
   681     if (rating != PEP_rating_undefined)
   682         add_opt_field(msg, "X-EncStatus", rating_to_string(rating));
   683 
   684     if (keylist) {
   685         char *_keylist = keylist_to_string(keylist);
   686         add_opt_field(msg, "X-KeyList", _keylist);
   687         free(_keylist);
   688     }
   689 }
   690 
   691 static PEP_rating _rating(PEP_comm_type ct, PEP_rating rating)
   692 {
   693     if (ct == PEP_ct_unknown)
   694         return PEP_rating_undefined;
   695 
   696     else if (ct == PEP_ct_key_not_found)
   697         return PEP_rating_have_no_key;
   698 
   699     else if (ct == PEP_ct_compromized)
   700         return PEP_rating_under_attack;
   701 
   702     else if (ct == PEP_ct_mistrusted)
   703         return PEP_rating_mistrust;
   704 
   705     if (rating == PEP_rating_unencrypted_for_some)
   706         return PEP_rating_unencrypted_for_some;
   707 
   708     if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel ||
   709             ct == PEP_ct_my_key_not_included) {
   710         if (rating > PEP_rating_unencrypted_for_some)
   711             return PEP_rating_unencrypted_for_some;
   712         else
   713             return PEP_rating_unencrypted;
   714     }
   715 
   716     if (rating == PEP_rating_unencrypted)
   717         return PEP_rating_unencrypted_for_some;
   718 
   719     if (ct >= PEP_ct_confirmed_enc_anon)
   720         return PEP_rating_trusted_and_anonymized;
   721 
   722     else if (ct >= PEP_ct_strong_encryption)
   723         return PEP_rating_trusted;
   724 
   725     else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
   726         return PEP_rating_reliable;
   727 
   728     else
   729         return PEP_rating_unreliable;
   730 }
   731 
   732 static bool is_encrypted_attachment(const bloblist_t *blob)
   733 {
   734     assert(blob);
   735 
   736     if (blob == NULL || blob->filename == NULL)
   737         return false;
   738 
   739     char *ext = strrchr(blob->filename, '.');
   740     if (ext == NULL)
   741         return false;
   742 
   743     if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
   744         if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
   745             strcmp(ext, ".asc") == 0)
   746             return true;
   747     }
   748     else if (strcmp(blob->mime_type, "text/plain") == 0) {
   749         if (strcmp(ext, ".asc") == 0)
   750             return true;
   751     }
   752 
   753     return false;
   754 }
   755 
   756 static bool is_encrypted_html_attachment(const bloblist_t *blob)
   757 {
   758     assert(blob);
   759     assert(blob->filename);
   760     if (blob == NULL || blob->filename == NULL)
   761         return false;
   762 
   763     if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
   764         if (strcmp(blob->filename + 11, ".pgp") == 0 ||
   765             strcmp(blob->filename + 11, ".asc") == 0)
   766             return true;
   767     }
   768 
   769     return false;
   770 }
   771 
   772 static char * without_double_ending(const char *filename)
   773 {
   774     assert(filename);
   775     if (filename == NULL)
   776         return NULL;
   777 
   778     char *ext = strrchr(filename, '.');
   779     if (ext == NULL)
   780         return NULL;
   781 
   782     char *result = strndup(filename, ext - filename);
   783     assert(result);
   784     return result;
   785 }
   786 
   787 static PEP_rating decrypt_rating(PEP_STATUS status)
   788 {
   789     switch (status) {
   790     case PEP_UNENCRYPTED:
   791     case PEP_VERIFIED:
   792     case PEP_VERIFY_NO_KEY:
   793     case PEP_VERIFIED_AND_TRUSTED:
   794         return PEP_rating_unencrypted;
   795 
   796     case PEP_DECRYPTED:
   797     case PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH:
   798         return PEP_rating_unreliable;
   799 
   800     case PEP_DECRYPTED_AND_VERIFIED:
   801         return PEP_rating_reliable;
   802 
   803     case PEP_DECRYPT_NO_KEY:
   804         return PEP_rating_have_no_key;
   805 
   806     case PEP_DECRYPT_WRONG_FORMAT:
   807     case PEP_CANNOT_DECRYPT_UNKNOWN:
   808         return PEP_rating_cannot_decrypt;
   809 
   810     default:
   811         return PEP_rating_undefined;
   812     }
   813 }
   814 
   815 static PEP_rating key_rating(PEP_SESSION session, const char *fpr)
   816 {
   817 
   818     assert(session);
   819     assert(fpr);
   820 
   821     if (session == NULL || fpr == NULL)
   822         return PEP_rating_undefined;
   823 
   824 
   825     PEP_comm_type bare_comm_type = PEP_ct_unknown;
   826     PEP_STATUS status = get_key_rating(session, fpr, &bare_comm_type);
   827     if (status != PEP_STATUS_OK)
   828         return PEP_rating_undefined;
   829 
   830     PEP_comm_type least_trust_type = PEP_ct_unknown;
   831     least_trust(session, fpr, &least_trust_type);
   832 
   833     if (least_trust_type == PEP_ct_unknown) {
   834         return _rating(bare_comm_type, PEP_rating_undefined);
   835     } else {
   836         return _rating(least_trust_type, PEP_rating_undefined);
   837     }
   838 }
   839 
   840 static PEP_rating worst_rating(PEP_rating rating1, PEP_rating rating2) {
   841     return ((rating1 < rating2) ? rating1 : rating2);
   842 }
   843 
   844 static PEP_rating keylist_rating(PEP_SESSION session, stringlist_t *keylist)
   845 {
   846     PEP_rating rating = PEP_rating_undefined;
   847 
   848     assert(keylist && keylist->value);
   849     if (keylist == NULL || keylist->value == NULL)
   850         return PEP_rating_undefined;
   851 
   852     stringlist_t *_kl;
   853     bool first = true;
   854     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   855         PEP_comm_type ct;
   856         PEP_STATUS status;
   857 
   858         PEP_rating _rating_ = key_rating(session, _kl->value);
   859          
   860         if (_rating_ <= PEP_rating_mistrust)
   861             return _rating_;
   862             
   863         if (first) {
   864             rating = _rating_;
   865             first = false;
   866         }
   867         else if (rating == PEP_rating_undefined)
   868             rating = worst_rating(rating, _rating_);
   869 
   870         if (_rating_ >= PEP_rating_reliable) {
   871             status = least_trust(session, _kl->value, &ct);
   872             if (status != PEP_STATUS_OK)
   873                 return PEP_rating_undefined;
   874             if (ct == PEP_ct_unknown){
   875                 /* per edouard, we reduce reliable+ ratings to reliable because
   876                    ct unknown */
   877                 if (rating >= PEP_rating_reliable){
   878                     rating = PEP_rating_reliable; 
   879                 }
   880             }
   881             else{
   882                 rating = worst_rating(rating, _rating(ct, rating));
   883             }
   884         }
   885         else if (_rating_ == PEP_rating_unencrypted) {
   886             if (rating > PEP_rating_unencrypted_for_some)
   887                 rating = worst_rating(rating, PEP_rating_unencrypted_for_some);
   888         }
   889     }
   890 
   891     return rating;
   892 }
   893 
   894 static PEP_comm_type _get_comm_type(
   895     PEP_SESSION session,
   896     PEP_comm_type max_comm_type,
   897     pEp_identity *ident
   898     )
   899 {
   900     PEP_STATUS status = update_identity(session, ident);
   901 
   902     if (max_comm_type == PEP_ct_compromized)
   903         return PEP_ct_compromized;
   904 
   905     if (max_comm_type == PEP_ct_mistrusted)
   906         return PEP_ct_mistrusted;
   907 
   908     if (status == PEP_STATUS_OK) {
   909         if (ident->comm_type == PEP_ct_compromized)
   910             return PEP_ct_compromized;
   911         else if (ident->comm_type == PEP_ct_mistrusted)
   912             return PEP_ct_mistrusted;
   913         else
   914             return _MIN(max_comm_type, ident->comm_type);
   915     }
   916     else {
   917         return PEP_ct_unknown;
   918     }
   919 }
   920 
   921 static void free_bl_entry(bloblist_t *bl)
   922 {
   923     if (bl) {
   924         free(bl->value);
   925         free(bl->mime_type);
   926         free(bl->filename);
   927         free(bl);
   928     }
   929 }
   930 
   931 static bool is_key(const bloblist_t *bl)
   932 {
   933     return (// workaround for Apple Mail bugs
   934             (is_mime_type(bl, "application/x-apple-msg-attachment") &&
   935              is_fileending(bl, ".asc")) ||
   936             // as binary, by file name
   937             ((bl->mime_type == NULL ||
   938               is_mime_type(bl, "application/octet-stream")) &&
   939              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   940                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
   941             // explicit mime type
   942             is_mime_type(bl, "application/pgp-keys") ||
   943             // as text, by file name
   944             (is_mime_type(bl, "text/plain") &&
   945              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   946                     is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
   947            );
   948 }
   949 
   950 static void remove_attached_keys(message *msg)
   951 {
   952     if (msg) {
   953         bloblist_t *last = NULL;
   954         for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
   955             bloblist_t *next = bl->next;
   956 
   957             if (is_key(bl)) {
   958                 if (last) {
   959                     last->next = next;
   960                 }
   961                 else {
   962                     msg->attachments = next;
   963                 }
   964                 free_bl_entry(bl);
   965             }
   966             else {
   967                 last = bl;
   968             }
   969             bl = next;
   970         }
   971     }
   972 }
   973 
   974 bool import_attached_keys(
   975         PEP_SESSION session,
   976         const message *msg,
   977         identity_list **private_idents
   978     )
   979 {
   980     assert(session);
   981     assert(msg);
   982 
   983     if (session == NULL || msg == NULL)
   984         return false;
   985 
   986     bool remove = false;
   987 
   988     int i = 0;
   989     for (bloblist_t *bl = msg->attachments; i < MAX_KEYS_TO_IMPORT && bl && bl->value;
   990             bl = bl->next, i++)
   991     {
   992         if (bl && bl->value && bl->size && bl->size < MAX_KEY_SIZE
   993                 && is_key(bl))
   994         {
   995             import_key(session, bl->value, bl->size, private_idents);
   996             remove = true;
   997         }
   998     }
   999     return remove;
  1000 }
  1001 
  1002 
  1003 PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
  1004 {
  1005     char *keydata = NULL;
  1006     size_t size;
  1007 
  1008     PEP_STATUS status = export_key(session, fpr, &keydata, &size);
  1009     assert(status == PEP_STATUS_OK);
  1010     if (status != PEP_STATUS_OK)
  1011         return status;
  1012     assert(size);
  1013 
  1014      bloblist_t *bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
  1015                       "pEpkey.asc");
  1016 
  1017     if (msg->attachments == NULL && bl)
  1018         msg->attachments = bl;
  1019 
  1020     return PEP_STATUS_OK;
  1021 }
  1022 
  1023 #define ONE_WEEK (7*24*3600)
  1024 
  1025 void attach_own_key(PEP_SESSION session, message *msg)
  1026 {
  1027     assert(session);
  1028     assert(msg);
  1029 
  1030     if (msg->dir == PEP_dir_incoming)
  1031         return;
  1032 
  1033     assert(msg->from && msg->from->fpr);
  1034     if (msg->from == NULL || msg->from->fpr == NULL)
  1035         return;
  1036 
  1037     if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
  1038         return;
  1039 
  1040     char *revoked_fpr = NULL;
  1041     uint64_t revocation_date = 0;
  1042 
  1043     if(get_revoked(session, msg->from->fpr,
  1044                    &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
  1045        revoked_fpr != NULL)
  1046     {
  1047         time_t now = time(NULL);
  1048 
  1049         if (now < (time_t)revocation_date + ONE_WEEK)
  1050         {
  1051             _attach_key(session, revoked_fpr, msg);
  1052         }
  1053     }
  1054     free(revoked_fpr);
  1055 }
  1056 
  1057 PEP_cryptotech determine_encryption_format(message *msg)
  1058 {
  1059     assert(msg);
  1060 
  1061     if (is_PGP_message_text(msg->longmsg)) {
  1062         msg->enc_format = PEP_enc_pieces;
  1063         return PEP_crypt_OpenPGP;
  1064     }
  1065     else if (msg->attachments && msg->attachments->next &&
  1066             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
  1067             is_PGP_message_text(msg->attachments->next->value)
  1068         ) {
  1069         msg->enc_format = PEP_enc_PGP_MIME;
  1070         return PEP_crypt_OpenPGP;
  1071     }
  1072     else if (msg->attachments && msg->attachments->next &&
  1073             is_mime_type(msg->attachments->next, "application/pgp-encrypted") &&
  1074             is_PGP_message_text(msg->attachments->value)
  1075         ) {
  1076         msg->enc_format = PEP_enc_PGP_MIME_Outlook1;
  1077         return PEP_crypt_OpenPGP;
  1078     }
  1079     else {
  1080         msg->enc_format = PEP_enc_none;
  1081         return PEP_crypt_none;
  1082     }
  1083 }
  1084 
  1085 DYNAMIC_API PEP_STATUS encrypt_message(
  1086         PEP_SESSION session,
  1087         message *src,
  1088         stringlist_t * extra,
  1089         message **dst,
  1090         PEP_enc_format enc_format,
  1091         PEP_encrypt_flags_t flags
  1092     )
  1093 {
  1094     PEP_STATUS status = PEP_STATUS_OK;
  1095     message * msg = NULL;
  1096     stringlist_t * keys = NULL;
  1097 
  1098     assert(session);
  1099     assert(src);
  1100     assert(dst);
  1101     assert(enc_format != PEP_enc_none);
  1102 
  1103     if (!(session && src && dst && enc_format != PEP_enc_none))
  1104         return ERROR(PEP_ILLEGAL_VALUE);
  1105 
  1106     if (src->dir == PEP_dir_incoming)
  1107         return ERROR(PEP_ILLEGAL_VALUE);
  1108 
  1109     determine_encryption_format(src);
  1110     if (src->enc_format != PEP_enc_none)
  1111         return ERROR(PEP_ILLEGAL_VALUE);
  1112 
  1113     *dst = NULL;
  1114 
  1115     status = myself(session, src->from);
  1116     if (status != PEP_STATUS_OK)
  1117         GOTO(pep_error);
  1118 
  1119     keys = new_stringlist(src->from->fpr);
  1120     if (keys == NULL)
  1121         goto enomem;
  1122 
  1123     stringlist_t *_k = keys;
  1124 
  1125     if (extra) {
  1126         _k = stringlist_append(_k, extra);
  1127         if (_k == NULL)
  1128             goto enomem;
  1129     }
  1130 
  1131     bool dest_keys_found = true;
  1132     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1133 
  1134     identity_list * _il;
  1135 
  1136     if ((_il = src->bcc) && _il->ident)
  1137     {
  1138         // BCC limited support:
  1139         //     - App splits mails with BCC in multiple mails.
  1140         //     - Each email is encrypted separately
  1141 
  1142         if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
  1143         {
  1144             // Only one Bcc with no other recipient allowed for now
  1145             return PEP_ILLEGAL_VALUE;
  1146         }
  1147 
  1148         PEP_STATUS _status = update_identity(session, _il->ident);
  1149         if (_status != PEP_STATUS_OK) {
  1150             status = _status;
  1151             GOTO(pep_error);
  1152         }
  1153 
  1154         if (_il->ident->fpr && _il->ident->fpr[0]) {
  1155             _k = stringlist_add(_k, _il->ident->fpr);
  1156             if (_k == NULL)
  1157                 goto enomem;
  1158             max_comm_type = _get_comm_type(session, max_comm_type,
  1159                                            _il->ident);
  1160         }
  1161         else {
  1162             dest_keys_found = false;
  1163             status = PEP_KEY_NOT_FOUND;
  1164         }
  1165     }
  1166     else
  1167     {
  1168         for (_il = src->to; _il && _il->ident; _il = _il->next) {
  1169             PEP_STATUS _status = update_identity(session, _il->ident);
  1170             if (_status != PEP_STATUS_OK) {
  1171                 status = _status;
  1172                 GOTO(pep_error);
  1173             }
  1174 
  1175             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1176                 _k = stringlist_add(_k, _il->ident->fpr);
  1177                 if (_k == NULL)
  1178                     goto enomem;
  1179                 max_comm_type = _get_comm_type(session, max_comm_type,
  1180                                                _il->ident);
  1181             }
  1182             else {
  1183                 dest_keys_found = false;
  1184                 status = PEP_KEY_NOT_FOUND;
  1185             }
  1186         }
  1187 
  1188         for (_il = src->cc; _il && _il->ident; _il = _il->next) {
  1189             PEP_STATUS _status = update_identity(session, _il->ident);
  1190             if (_status != PEP_STATUS_OK)
  1191             {
  1192                 status = _status;
  1193                 GOTO(pep_error);
  1194             }
  1195 
  1196             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1197                 _k = stringlist_add(_k, _il->ident->fpr);
  1198                 if (_k == NULL)
  1199                     goto enomem;
  1200                 max_comm_type = _get_comm_type(session, max_comm_type,
  1201                                                _il->ident);
  1202             }
  1203             else {
  1204                 dest_keys_found = false;
  1205                 status = PEP_KEY_NOT_FOUND;
  1206             }
  1207         }
  1208     }
  1209 
  1210     if (!dest_keys_found ||
  1211         stringlist_length(keys)  == 0 ||
  1212         _rating(max_comm_type,
  1213                 PEP_rating_undefined) < PEP_rating_reliable)
  1214     {
  1215         free_stringlist(keys);
  1216         if (!session->passive_mode && !(flags & PEP_encrypt_flag_force_no_attached_key))
  1217             attach_own_key(session, src);
  1218         return ERROR(PEP_UNENCRYPTED);
  1219     }
  1220     else {
  1221         msg = clone_to_empty_message(src);
  1222         if (msg == NULL)
  1223             goto enomem;
  1224 
  1225         if (!(flags & PEP_encrypt_flag_force_no_attached_key))
  1226             attach_own_key(session, src);
  1227 
  1228         switch (enc_format) {
  1229         case PEP_enc_PGP_MIME:
  1230         case PEP_enc_PEP: // BUG: should be implemented extra
  1231             status = encrypt_PGP_MIME(session, src, keys, msg, flags);
  1232             break;
  1233 
  1234         case PEP_enc_pieces:
  1235             status = encrypt_PGP_in_pieces(session, src, keys, msg, flags);
  1236             break;
  1237 
  1238         /* case PEP_enc_PEP:
  1239             // TODO: implement
  1240             NOT_IMPLEMENTED */
  1241 
  1242         default:
  1243             assert(0);
  1244             status = PEP_ILLEGAL_VALUE;
  1245             GOTO(pep_error);
  1246         }
  1247 
  1248         if (status == PEP_OUT_OF_MEMORY)
  1249             goto enomem;
  1250 
  1251         if (status != PEP_STATUS_OK)
  1252             GOTO(pep_error);
  1253     }
  1254 
  1255     free_stringlist(keys);
  1256 
  1257     if (msg && msg->shortmsg == NULL) {
  1258         msg->shortmsg = strdup("pEp");
  1259         assert(msg->shortmsg);
  1260         if (msg->shortmsg == NULL)
  1261             goto enomem;
  1262     }
  1263 
  1264     if (msg) {
  1265         decorate_message(msg, PEP_rating_undefined, NULL);
  1266         if (src->id) {
  1267             msg->id = strdup(src->id);
  1268             assert(msg->id);
  1269             if (msg->id == NULL)
  1270                 goto enomem;
  1271         }
  1272     }
  1273 
  1274     *dst = msg;
  1275     return ERROR(status);
  1276 
  1277 enomem:
  1278     status = PEP_OUT_OF_MEMORY;
  1279 
  1280 pep_error:
  1281     free_stringlist(keys);
  1282     free_message(msg);
  1283 
  1284     return ERROR(status);
  1285 }
  1286 
  1287 DYNAMIC_API PEP_STATUS encrypt_message_for_self(
  1288         PEP_SESSION session,
  1289         pEp_identity* target_id,
  1290         message *src,
  1291         message **dst,
  1292         PEP_enc_format enc_format,
  1293         PEP_encrypt_flags_t flags
  1294     )
  1295 {
  1296     PEP_STATUS status = PEP_STATUS_OK;
  1297     message * msg = NULL;
  1298     stringlist_t * keys = NULL;
  1299 
  1300     assert(session);
  1301     assert(src);
  1302     assert(dst);
  1303     assert(enc_format != PEP_enc_none);
  1304 
  1305     if (!(session && src && dst && enc_format != PEP_enc_none))
  1306         return ERROR(PEP_ILLEGAL_VALUE);
  1307 
  1308     if (src->dir == PEP_dir_incoming)
  1309         return ERROR(PEP_ILLEGAL_VALUE);
  1310 
  1311     determine_encryption_format(src);
  1312     if (src->enc_format != PEP_enc_none)
  1313         return ERROR(PEP_ILLEGAL_VALUE);
  1314 
  1315     status = myself(session, target_id);
  1316     if (status != PEP_STATUS_OK)
  1317         GOTO(pep_error);
  1318 
  1319     *dst = NULL;
  1320 
  1321 
  1322     PEP_STATUS _status = update_identity(session, target_id);
  1323     if (_status != PEP_STATUS_OK) {
  1324         status = _status;
  1325         goto pep_error;
  1326     }
  1327 
  1328     char* target_fpr = target_id->fpr;
  1329     if (!target_fpr)
  1330         return PEP_KEY_NOT_FOUND; // FIXME: Error condition
  1331  
  1332     keys = new_stringlist(target_fpr);
  1333     
  1334     /* KG: did we ever do this??? */
  1335     if (!(flags & PEP_encrypt_flag_force_no_attached_key))
  1336         _attach_key(session, target_fpr, src);
  1337 
  1338     msg = clone_to_empty_message(src);
  1339     if (msg == NULL)
  1340         goto enomem;
  1341 
  1342     switch (enc_format) {
  1343         case PEP_enc_PGP_MIME:
  1344         case PEP_enc_PEP: // BUG: should be implemented extra
  1345             status = encrypt_PGP_MIME(session, src, keys, msg, flags);
  1346             break;
  1347 
  1348         case PEP_enc_pieces:
  1349             status = encrypt_PGP_in_pieces(session, src, keys, msg, flags);
  1350             break;
  1351 
  1352         /* case PEP_enc_PEP:
  1353             NOT_IMPLEMENTED */
  1354             // TODO: implement
  1355 
  1356         default:
  1357             assert(0);
  1358             status = PEP_ILLEGAL_VALUE;
  1359             goto pep_error;
  1360     }
  1361 
  1362     if (status == PEP_OUT_OF_MEMORY)
  1363         goto enomem;
  1364 
  1365     if (status != PEP_STATUS_OK)
  1366         goto pep_error;
  1367 
  1368      if (msg && msg->shortmsg == NULL) {
  1369          msg->shortmsg = strdup("pEp");
  1370          assert(msg->shortmsg);
  1371          if (msg->shortmsg == NULL)
  1372              goto enomem;
  1373      }
  1374 
  1375      if (msg) {
  1376          if (src->id) {
  1377              msg->id = strdup(src->id);
  1378              assert(msg->id);
  1379              if (msg->id == NULL)
  1380                  goto enomem;
  1381          }
  1382      }
  1383 
  1384     *dst = msg;
  1385     return status;
  1386 
  1387 enomem:
  1388     status = PEP_OUT_OF_MEMORY;
  1389 
  1390 pep_error:
  1391     free_stringlist(keys);
  1392     free_message(msg);
  1393 
  1394     return ERROR(status);
  1395 }
  1396 
  1397 static bool is_a_pEpmessage(const message *msg)
  1398 {
  1399     for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  1400         if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
  1401             return true;
  1402     }
  1403     return false;
  1404 }
  1405 
  1406 // update comm_type to pEp_ct_pEp if needed
  1407 
  1408 static PEP_STATUS _update_identity_for_incoming_message(
  1409         PEP_SESSION session,
  1410         const message *src
  1411     )
  1412 {
  1413     PEP_STATUS status;
  1414     if (src->from && src->from->address) {
  1415         status = update_identity(session, src->from);
  1416         if (status == PEP_STATUS_OK
  1417                 && is_a_pEpmessage(src)
  1418                 && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
  1419                 && src->from->comm_type != PEP_ct_pEp_unconfirmed
  1420                 && src->from->comm_type != PEP_ct_pEp)
  1421         {
  1422             src->from->comm_type |= PEP_ct_pEp_unconfirmed;
  1423             status = set_identity(session, src->from);
  1424         }
  1425         return status;
  1426     }
  1427     return PEP_ILLEGAL_VALUE;
  1428 }
  1429 
  1430 
  1431 PEP_STATUS _get_detached_signature(message* msg, bloblist_t** signature_blob) {
  1432     bloblist_t* attach_curr = msg->attachments;
  1433 
  1434     *signature_blob = NULL;
  1435 
  1436     while (attach_curr) {
  1437         if (strcasecmp(attach_curr->mime_type, "application/pgp-signature") == 0) {
  1438             *signature_blob = attach_curr;
  1439             break;
  1440         }
  1441         attach_curr = attach_curr->next;
  1442     }
  1443 
  1444     return PEP_STATUS_OK;
  1445 }
  1446 
  1447 PEP_STATUS _get_signed_text(const char* ptext, const size_t psize,
  1448                             char** stext, size_t* ssize) {
  1449 
  1450     char* signed_boundary = NULL;
  1451     char* signpost = strstr(ptext, "Content-Type: multipart/signed");
  1452 
  1453     *ssize = 0;
  1454     *stext = NULL;
  1455 
  1456     if (!signpost)
  1457         return PEP_UNKNOWN_ERROR;
  1458 
  1459     char* curr_line = signpost;
  1460 //    const char* end_text = ptext + psize;
  1461     const char* boundary_key = "boundary=";
  1462     const size_t BOUNDARY_KEY_SIZE = 9;
  1463 
  1464     char* start_boundary = strstr(curr_line, boundary_key);
  1465     if (!start_boundary)
  1466         return PEP_UNKNOWN_ERROR;
  1467 
  1468     start_boundary += BOUNDARY_KEY_SIZE;
  1469 
  1470     bool quoted = (*start_boundary == '"');
  1471 
  1472     if (quoted)
  1473         start_boundary++;
  1474         
  1475     char* end_boundary = (quoted ? strstr(start_boundary, "\"") : strstr(start_boundary, ";")); // FIXME: third possiblity is CRLF, or?
  1476 
  1477     if (!end_boundary)
  1478         return PEP_UNKNOWN_ERROR;
  1479 
  1480     // Add space for the "--"
  1481     size_t boundary_strlen = (end_boundary - start_boundary) + 2;
  1482 
  1483     signed_boundary = calloc(1, boundary_strlen + 1);
  1484     strlcpy(signed_boundary, "--", boundary_strlen + 1);
  1485     strlcat(signed_boundary, start_boundary, boundary_strlen + 1);
  1486 
  1487     start_boundary = strstr(end_boundary, signed_boundary);
  1488 
  1489     if (!start_boundary)
  1490         return PEP_UNKNOWN_ERROR;
  1491 
  1492     start_boundary += boundary_strlen;
  1493 
  1494     if (*start_boundary == '\r') {
  1495         if (*(start_boundary + 1) == '\n')
  1496             start_boundary += 2;
  1497     }
  1498     else if (*start_boundary == '\n')
  1499         start_boundary++;
  1500 
  1501     end_boundary = strstr(start_boundary + boundary_strlen, signed_boundary);
  1502 
  1503     if (!end_boundary)
  1504         return PEP_UNKNOWN_ERROR;
  1505 
  1506     // See RFC3156 section 5...
  1507     end_boundary--; 
  1508     if (*(end_boundary - 1) == '\r')
  1509         end_boundary--; 
  1510 
  1511     *ssize = end_boundary - start_boundary;
  1512     *stext = start_boundary;
  1513     free(signed_boundary);
  1514 
  1515     return PEP_STATUS_OK;
  1516 }
  1517 
  1518 PEP_STATUS combine_keylists(PEP_SESSION session, stringlist_t** verify_in, 
  1519                             stringlist_t** keylist_in_out, 
  1520                             pEp_identity* from) {
  1521     
  1522     if (!verify_in || !(*verify_in)) // this isn't really a problem.
  1523         return PEP_STATUS_OK;
  1524     
  1525     stringlist_t* orig_verify = *verify_in;
  1526     
  1527     stringlist_t* verify_curr = NULL;
  1528     stringlist_t* from_keys = NULL;
  1529     
  1530     /* FIXME: what to do if head needs to be null */
  1531     PEP_STATUS status = find_keys(session, from->address, &from_keys);
  1532     
  1533     stringlist_t* from_fpr_node = NULL;
  1534     stringlist_t* from_curr;
  1535     
  1536     for (from_curr = from_keys; from_curr; from_curr = from_curr->next) {
  1537         for (verify_curr = orig_verify; verify_curr; verify_curr = verify_curr->next) {
  1538             if (from_curr->value && verify_curr->value &&
  1539                 _same_fpr(from_curr->value, strlen(from_curr->value),
  1540                           verify_curr->value, strlen(verify_curr->value))) {
  1541                 from_fpr_node = from_curr;
  1542                 break;
  1543             }
  1544         }
  1545     }
  1546     
  1547     if (!from_fpr_node) {
  1548         status = PEP_KEY_NOT_FOUND;
  1549         goto free;
  1550     }
  1551 
  1552     verify_curr = orig_verify;
  1553     
  1554     /* put "from" signer at the beginning of the list */
  1555     if (!_same_fpr(orig_verify->value, strlen(orig_verify->value),
  1556                    from_fpr_node->value, strlen(from_fpr_node->value))) {
  1557         orig_verify = stringlist_delete(orig_verify, from_fpr_node->value);
  1558         verify_curr = new_stringlist(from_fpr_node->value);
  1559         verify_curr->next = orig_verify;
  1560     }
  1561 
  1562     /* append keylist to signers */
  1563     if (keylist_in_out && *keylist_in_out && (*keylist_in_out)->value) {
  1564         stringlist_t** tail_pp = &verify_curr->next;
  1565         
  1566         while (*tail_pp) {
  1567             tail_pp = &((*tail_pp)->next);
  1568         }
  1569         stringlist_t* second_list = *keylist_in_out;
  1570         if (second_list) {
  1571             char* listhead_val = second_list->value;
  1572             if (!listhead_val || listhead_val[0] == '\0') {
  1573                 /* remove head, basically. This can happen when,
  1574                    for example, the signature is detached and
  1575                    verification is not seen directly after
  1576                    decryption, so no signer is presumed in
  1577                    the first construction of the keylist */
  1578                 *keylist_in_out = (*keylist_in_out)->next;
  1579                 second_list->next = NULL;
  1580                 free_stringlist(second_list);
  1581             }
  1582         }
  1583         *tail_pp = *keylist_in_out;
  1584     }
  1585     
  1586     *keylist_in_out = verify_curr;
  1587     
  1588     status = PEP_STATUS_OK;
  1589     
  1590 free:
  1591     free_stringlist(from_keys);
  1592     return status;
  1593 }
  1594 
  1595 PEP_STATUS amend_rating_according_to_sender_and_recipients(
  1596     PEP_SESSION session,
  1597     PEP_rating *rating,
  1598     pEp_identity *sender,
  1599     stringlist_t *recipients) {
  1600     
  1601     PEP_STATUS status = PEP_STATUS_OK;
  1602 
  1603     if (*rating > PEP_rating_mistrust) {
  1604         PEP_rating kl_rating = PEP_rating_undefined;
  1605 
  1606         if (recipients)
  1607             kl_rating = keylist_rating(session, recipients);
  1608 
  1609         if (kl_rating <= PEP_rating_mistrust) {
  1610             *rating = kl_rating;
  1611         }
  1612         else if (*rating >= PEP_rating_reliable &&
  1613                  kl_rating < PEP_rating_reliable) {
  1614             *rating = PEP_rating_unreliable;
  1615         }
  1616         else if (*rating >= PEP_rating_reliable &&
  1617                  kl_rating >= PEP_rating_reliable) {
  1618             if (!(sender && sender->user_id && sender->user_id[0])) {
  1619                 *rating = PEP_rating_unreliable;
  1620             }
  1621             else {
  1622                 char *fpr = recipients->value;
  1623                 pEp_identity *_sender = new_identity(sender->address, fpr,
  1624                                                    sender->user_id, sender->username);
  1625                 if (_sender == NULL)
  1626                     return PEP_OUT_OF_MEMORY;
  1627                 status = get_trust(session, _sender);
  1628                 if (_sender->comm_type != PEP_ct_unknown) {
  1629                     *rating = worst_rating(_rating(_sender->comm_type, PEP_rating_undefined),
  1630                               kl_rating);
  1631                 }
  1632                 free_identity(_sender);
  1633                 if (status == PEP_CANNOT_FIND_IDENTITY)
  1634                    status = PEP_STATUS_OK;
  1635             }
  1636         }
  1637     }
  1638     return status;
  1639 }
  1640 
  1641 
  1642 DYNAMIC_API PEP_STATUS _decrypt_message(
  1643         PEP_SESSION session,
  1644         message *src,
  1645         message **dst,
  1646         stringlist_t **keylist,
  1647         PEP_rating *rating,
  1648         PEP_decrypt_flags_t *flags,
  1649         identity_list **private_il
  1650     )
  1651 {
  1652     PEP_STATUS status = PEP_STATUS_OK;
  1653     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  1654     message *msg = NULL;
  1655     char *ctext;
  1656     size_t csize;
  1657     char *ptext = NULL;
  1658     size_t psize;
  1659     stringlist_t *_keylist = NULL;
  1660 
  1661     assert(session);
  1662     assert(src);
  1663     assert(dst);
  1664     assert(keylist);
  1665     assert(rating);
  1666     assert(flags);
  1667 
  1668     if (!(session && src && dst && keylist && rating && flags))
  1669         return ERROR(PEP_ILLEGAL_VALUE);
  1670 
  1671     *flags = 0;
  1672 
  1673     // Private key in unencrypted mail are ignored -> NULL
  1674     bool imported_keys = import_attached_keys(session, src, NULL);
  1675 
  1676     // Update src->from in case we just imported a key
  1677     // we would need to check signature
  1678     status = _update_identity_for_incoming_message(session, src);
  1679     if(status != PEP_STATUS_OK)
  1680         return ERROR(status);
  1681 
  1682     // Get detached signature, if any
  1683     bloblist_t* detached_sig = NULL;
  1684     char* dsig_text = NULL;
  1685     size_t dsig_size = 0;
  1686     status = _get_detached_signature(src, &detached_sig);
  1687     if (detached_sig) {
  1688         dsig_text = detached_sig->value;
  1689         dsig_size = detached_sig->size;
  1690     }
  1691 
  1692     PEP_cryptotech crypto = determine_encryption_format(src);
  1693 
  1694     *dst = NULL;
  1695     *keylist = NULL;
  1696     *rating = PEP_rating_undefined;
  1697 
  1698     switch (src->enc_format) {
  1699         case PEP_enc_none:
  1700             *rating = PEP_rating_unencrypted;
  1701             if (imported_keys)
  1702                 remove_attached_keys(src);
  1703             if(session->sync_session->inject_sync_msg){
  1704                 status = receive_DeviceState_msg(session, src, *rating, *keylist);
  1705                 if (status == PEP_MESSAGE_CONSUME ||
  1706                     status == PEP_MESSAGE_IGNORE) {
  1707                     free_message(msg);
  1708                     msg = NULL;
  1709                     *flags |= (status == PEP_MESSAGE_IGNORE) ?
  1710                                 PEP_decrypt_flag_ignore :
  1711                                 PEP_decrypt_flag_consume;
  1712                 }
  1713                 else if (status != PEP_STATUS_OK) {
  1714                     return ERROR(status);
  1715                 }
  1716             }
  1717             
  1718             char* slong = src->longmsg;
  1719             char* sform = src->longmsg_formatted;
  1720             bloblist_t* satt = src->attachments;
  1721             
  1722             if ((!slong || slong[0] == '\0')
  1723                  && (!sform || sform[0] == '\0')) {
  1724                 if (satt) {
  1725                     const char* inner_mime_type = satt->mime_type;
  1726                     if (strcasecmp(inner_mime_type, "text/plain") == 0) {
  1727                         free(slong); /* in case of "" */
  1728                         src->longmsg = strndup(satt->value, satt->size); // N.B.: longmsg might be shorter, if attachment contains NUL bytes which are not allowed in text/plain!
  1729                         
  1730                         bloblist_t* next_node = satt->next;
  1731                         if (next_node) {
  1732                             inner_mime_type = next_node->mime_type;
  1733                             if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1734                                 free(sform);
  1735                                 src->longmsg_formatted = strndup(next_node->value, next_node->size);  // N.B.: longmsg might be shorter, if attachment contains NUL bytes which are not allowed in text/plain!
  1736                             }
  1737                         }
  1738                     }
  1739                     else if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1740                         free(sform);
  1741                         src->longmsg_formatted = strndup(satt->value, satt->size);  // N.B.: longmsg might be shorter, if attachment contains NUL bytes which are not allowed in text/plain!
  1742                     }
  1743                 }
  1744             }
  1745             
  1746             return ERROR(PEP_UNENCRYPTED);
  1747 
  1748         case PEP_enc_PGP_MIME:
  1749             ctext = src->attachments->next->value;
  1750             csize = src->attachments->next->size;
  1751             break;
  1752 
  1753         case PEP_enc_PGP_MIME_Outlook1:
  1754             ctext = src->attachments->value;
  1755             csize = src->attachments->size;
  1756             break;
  1757 
  1758         case PEP_enc_pieces:
  1759             ctext = src->longmsg;
  1760             csize = strlen(ctext);
  1761             break;
  1762 
  1763         default:
  1764             NOT_IMPLEMENTED
  1765     }
  1766     status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1767                                                    csize, dsig_text, dsig_size,
  1768                                                    &ptext, &psize, &_keylist);
  1769     if (status > PEP_CANNOT_DECRYPT_UNKNOWN){
  1770         GOTO(pep_error);
  1771     }
  1772 
  1773     decrypt_status = status;
  1774 
  1775     if (status == PEP_DECRYPT_NO_KEY){
  1776         PEP_STATUS sync_status = inject_DeviceState_event(session, CannotDecrypt, NULL, NULL);
  1777         if (sync_status == PEP_OUT_OF_MEMORY){
  1778             status = PEP_OUT_OF_MEMORY;
  1779             goto pep_error;
  1780         }
  1781     }
  1782 
  1783     bool imported_private_key_address = false;
  1784 
  1785     if (ptext) {
  1786         switch (src->enc_format) {
  1787             case PEP_enc_PGP_MIME:
  1788             case PEP_enc_PGP_MIME_Outlook1:
  1789                 status = mime_decode_message(ptext, psize, &msg);
  1790                 if (status != PEP_STATUS_OK)
  1791                     goto pep_error;
  1792                 
  1793                 char* mlong = msg->longmsg;
  1794                 char* mform = msg->longmsg_formatted;
  1795                 bloblist_t* matt = msg->attachments;
  1796                 
  1797                 if ((!mlong || mlong[0] == '\0')
  1798                      && (!mform || mform[0] == '\0')) {
  1799                     if (matt) {
  1800                         const char* inner_mime_type = matt->mime_type;
  1801                         if (strcasecmp(inner_mime_type, "text/plain") == 0) {
  1802                             free(mlong); /* in case of "" */
  1803                             msg->longmsg = strndup(matt->value, matt->size);
  1804                             
  1805                             bloblist_t* next_node = matt->next;
  1806                             if (next_node) {
  1807                                 inner_mime_type = next_node->mime_type;
  1808                                 if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1809                                     free(mform);
  1810                                     msg->longmsg_formatted = strndup(next_node->value, next_node->size);
  1811                                 }
  1812                             }
  1813                         }
  1814                         else if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1815                             free(mform);
  1816                             msg->longmsg_formatted = strndup(matt->value, matt->size);
  1817                         }
  1818                     }
  1819                     if (msg->shortmsg) {
  1820                         free(src->shortmsg);
  1821                         src->shortmsg = strdup(msg->shortmsg);
  1822                     }
  1823                 }
  1824 
  1825                 if (decrypt_status != PEP_DECRYPTED_AND_VERIFIED) {
  1826                     status = _get_detached_signature(msg, &detached_sig);
  1827                     if (decrypt_status == PEP_DECRYPTED && detached_sig) {
  1828                         dsig_text = detached_sig->value;
  1829                         dsig_size = detached_sig->size;
  1830                         size_t ssize = 0;
  1831                         char* stext = NULL;
  1832 
  1833                         status = _get_signed_text(ptext, psize, &stext, &ssize);
  1834                         stringlist_t *_verify_keylist = NULL;
  1835 
  1836                         if (ssize > 0 && stext) {
  1837                             status = cryptotech[crypto].verify_text(session, stext,
  1838                                                                     ssize, dsig_text, dsig_size,
  1839                                                                     &_verify_keylist);
  1840 
  1841                             if (status == PEP_VERIFIED || status == PEP_VERIFIED_AND_TRUSTED)
  1842                                 decrypt_status = PEP_DECRYPTED_AND_VERIFIED;
  1843                             
  1844                                 status = combine_keylists(session, &_verify_keylist, &_keylist, src->from);
  1845                         }
  1846                     }
  1847                 }
  1848                 break;
  1849 
  1850             case PEP_enc_pieces:
  1851                 msg = clone_to_empty_message(src);
  1852                 if (msg == NULL)
  1853                     goto enomem;
  1854 
  1855                 msg->longmsg = ptext;
  1856                 ptext = NULL;
  1857 
  1858                 bloblist_t *_m = msg->attachments;
  1859                 if (_m == NULL && src->attachments && src->attachments->value) {
  1860                     msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
  1861                     _m = msg->attachments;
  1862                 }
  1863 
  1864                 bloblist_t *_s;
  1865                 for (_s = src->attachments; _s; _s = _s->next) {
  1866                     if (_s->value == NULL && _s->size == 0){
  1867                         _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
  1868                         if (_m == NULL)
  1869                             goto enomem;
  1870 
  1871                     }
  1872                     else if (is_encrypted_attachment(_s)) {
  1873                         stringlist_t *_keylist = NULL;
  1874                         char *attctext  = _s->value;
  1875                         size_t attcsize = _s->size;
  1876 
  1877                         free(ptext);
  1878                         ptext = NULL;
  1879 
  1880                         // FIXME: What about attachments with separate sigs???
  1881                         status = decrypt_and_verify(session, attctext, attcsize,
  1882                                                     NULL, 0,
  1883                                                     &ptext, &psize, &_keylist);
  1884                         free_stringlist(_keylist); // FIXME: Why do we do this?
  1885 
  1886                         if (ptext) {
  1887                             if (is_encrypted_html_attachment(_s)) {
  1888                                 msg->longmsg_formatted = ptext;
  1889                                 ptext = NULL;
  1890                             }
  1891                             else {
  1892                                 static const char * const mime_type = "application/octet-stream";
  1893                                 char * const filename =
  1894                                     without_double_ending(_s->filename);
  1895                                 if (filename == NULL)
  1896                                     goto enomem;
  1897 
  1898                                 _m = bloblist_add(_m, ptext, psize, mime_type,
  1899                                     filename);
  1900                                 free(filename);
  1901                                 if (_m == NULL)
  1902                                     goto enomem;
  1903 
  1904                                 ptext = NULL;
  1905 
  1906                                 if (msg->attachments == NULL)
  1907                                     msg->attachments = _m;
  1908                             }
  1909                         }
  1910                         else {
  1911                             char *copy = malloc(_s->size);
  1912                             assert(copy);
  1913                             if (copy == NULL)
  1914                                 goto enomem;
  1915                             memcpy(copy, _s->value, _s->size);
  1916                             _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1917                             if (_m == NULL)
  1918                                 goto enomem;
  1919                         }
  1920                     }
  1921                     else {
  1922                         char *copy = malloc(_s->size);
  1923                         assert(copy);
  1924                         if (copy == NULL)
  1925                             goto enomem;
  1926                         memcpy(copy, _s->value, _s->size);
  1927                         _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1928                         if (_m == NULL)
  1929                             goto enomem;
  1930                     }
  1931                 }
  1932 
  1933                 break;
  1934 
  1935             default:
  1936                 // BUG: must implement more
  1937                 NOT_IMPLEMENTED
  1938         }
  1939 
  1940         switch (src->enc_format) {
  1941             case PEP_enc_PGP_MIME:
  1942             case PEP_enc_pieces:
  1943             case PEP_enc_PGP_MIME_Outlook1:
  1944                 status = copy_fields(msg, src);
  1945                 if (status != PEP_STATUS_OK)
  1946                 {
  1947                     GOTO(pep_error);
  1948                 }
  1949 
  1950                 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
  1951                 {
  1952                     char * shortmsg;
  1953                     char * longmsg;
  1954 
  1955                     int r = separate_short_and_long(msg->longmsg, &shortmsg,
  1956                             &longmsg);
  1957                     if (r == -1)
  1958                         goto enomem;
  1959 
  1960                     free(msg->shortmsg);
  1961                     free(msg->longmsg);
  1962 
  1963                     msg->shortmsg = shortmsg;
  1964                     msg->longmsg = longmsg;
  1965                 }
  1966                 else {
  1967                     msg->shortmsg = strdup(src->shortmsg);
  1968                     assert(msg->shortmsg);
  1969                     if (msg->shortmsg == NULL)
  1970                         goto enomem;
  1971                 }
  1972                 break;
  1973             default:
  1974                     // BUG: must implement more
  1975                     NOT_IMPLEMENTED
  1976         }
  1977 
  1978         // check for private key in decrypted message attachement while inporting
  1979         identity_list *_private_il = NULL;
  1980         imported_keys = import_attached_keys(session, msg, &_private_il);
  1981         if (_private_il &&
  1982             identity_list_length(_private_il) == 1 &&
  1983             _private_il->ident->address)
  1984         {
  1985             imported_private_key_address = true;
  1986         }
  1987 
  1988         if(private_il && imported_private_key_address){
  1989             *private_il = _private_il;
  1990         }else{
  1991             free_identity_list(_private_il);
  1992         }
  1993 
  1994         if(decrypt_status == PEP_DECRYPTED){
  1995 
  1996             // TODO optimize if import_attached_keys didn't import any key
  1997 
  1998             // In case message did decrypt, but no valid signature could be found
  1999             // then retry decrypt+verify after importing key.
  2000 
  2001             // Update msg->from in case we just imported a key
  2002             // we would need to check signature
  2003 
  2004             status = _update_identity_for_incoming_message(session, src);
  2005             if(status != PEP_STATUS_OK)
  2006             {
  2007                 GOTO(pep_error);
  2008             }
  2009 
  2010             char *re_ptext = NULL;
  2011             size_t re_psize;
  2012 
  2013             free_stringlist(_keylist);
  2014             _keylist = NULL;
  2015 
  2016             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  2017                 csize, dsig_text, dsig_size, &re_ptext, &re_psize, &_keylist);
  2018 
  2019             free(re_ptext);
  2020 
  2021             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  2022             {
  2023                 GOTO(pep_error);
  2024             }
  2025 
  2026             decrypt_status = status;
  2027         }
  2028 
  2029         *rating = decrypt_rating(decrypt_status);
  2030 
  2031         status = amend_rating_according_to_sender_and_recipients(session,
  2032                                                                  rating,
  2033                                                                  src->from,
  2034                                                                  _keylist);
  2035 
  2036         if (status != PEP_STATUS_OK)
  2037             GOTO(pep_error);
  2038     }
  2039     else
  2040     {
  2041         *rating = decrypt_rating(decrypt_status);
  2042         goto pep_error;
  2043     }
  2044 
  2045     // Case of own key imported from own trusted message
  2046     if (// Message have been reliably decrypted
  2047         msg &&
  2048         *rating >= PEP_rating_trusted &&
  2049         imported_private_key_address &&
  2050         // to is [own]
  2051         msg->to->ident->user_id &&
  2052         strcmp(msg->to->ident->user_id, PEP_OWN_USERID) == 0
  2053         )
  2054     {
  2055         *flags |= PEP_decrypt_flag_own_private_key;
  2056     }
  2057 
  2058     if (msg) {
  2059         decorate_message(msg, *rating, _keylist);
  2060         if (imported_keys)
  2061             remove_attached_keys(msg);
  2062         if (*rating >= PEP_rating_reliable &&
  2063             session->sync_session->inject_sync_msg) {
  2064             status = receive_DeviceState_msg(session, msg, *rating, _keylist);
  2065             if (status == PEP_MESSAGE_CONSUME ||
  2066                 status == PEP_MESSAGE_IGNORE) {
  2067                 free_message(msg);
  2068                 msg = NULL;
  2069                 *flags |= (status == PEP_MESSAGE_IGNORE) ?
  2070                             PEP_decrypt_flag_ignore :
  2071                             PEP_decrypt_flag_consume;
  2072 
  2073                 status = decrypt_status;
  2074             }
  2075             else if (status != PEP_STATUS_OK){
  2076                 goto pep_error;
  2077             }
  2078         }
  2079     }
  2080     if (msg) {
  2081         if (src->id) {
  2082             msg->id = strdup(src->id);
  2083             assert(msg->id);
  2084             if (msg->id == NULL)
  2085                 goto enomem;
  2086         }
  2087     }
  2088 
  2089     *dst = msg;
  2090     *keylist = _keylist;
  2091 
  2092     return PEP_STATUS_OK;
  2093 
  2094 enomem:
  2095     status = PEP_OUT_OF_MEMORY;
  2096 
  2097 pep_error:
  2098     free(ptext);
  2099     free_message(msg);
  2100     free_stringlist(_keylist);
  2101 
  2102     return ERROR(status);
  2103 }
  2104 
  2105 DYNAMIC_API PEP_STATUS decrypt_message(
  2106         PEP_SESSION session,
  2107         message *src,
  2108         message **dst,
  2109         stringlist_t **keylist,
  2110         PEP_rating *rating,
  2111         PEP_decrypt_flags_t *flags
  2112     )
  2113 {
  2114     return _decrypt_message( session, src, dst, keylist, rating, flags, NULL );
  2115 }
  2116 
  2117 DYNAMIC_API PEP_STATUS own_message_private_key_details(
  2118         PEP_SESSION session,
  2119         message *msg,
  2120         pEp_identity **ident
  2121     )
  2122 {
  2123     assert(session);
  2124     assert(msg);
  2125     assert(ident);
  2126 
  2127     if (!(session && msg && ident))
  2128         return PEP_ILLEGAL_VALUE;
  2129 
  2130     message *dst = NULL;
  2131     stringlist_t *keylist = NULL;
  2132     PEP_rating rating;
  2133     PEP_decrypt_flags_t flags;
  2134 
  2135     *ident = NULL;
  2136 
  2137     identity_list *private_il = NULL;
  2138     PEP_STATUS status = _decrypt_message(session, msg,  &dst, &keylist, &rating, &flags, &private_il);
  2139     free_message(dst);
  2140     free_stringlist(keylist);
  2141 
  2142     if (status == PEP_STATUS_OK &&
  2143         flags & PEP_decrypt_flag_own_private_key &&
  2144         private_il)
  2145     {
  2146         *ident = identity_dup(private_il->ident);
  2147     }
  2148 
  2149     free_identity_list(private_il);
  2150 
  2151     return ERROR(status);
  2152 }
  2153 
  2154 static void _max_comm_type_from_identity_list(
  2155         identity_list *identities,
  2156         PEP_SESSION session,
  2157         PEP_comm_type *max_comm_type,
  2158         bool *comm_type_determined
  2159     )
  2160 {
  2161     identity_list * il;
  2162     for (il = identities; il != NULL; il = il->next)
  2163     {
  2164         if (il->ident)
  2165         {
  2166             PEP_STATUS status = update_identity(session, il->ident);
  2167             if (status == PEP_STATUS_OK)
  2168             {
  2169                 *max_comm_type = _get_comm_type(session, *max_comm_type,
  2170                         il->ident);
  2171                 *comm_type_determined = true;
  2172             }
  2173         }
  2174     }
  2175 }
  2176 
  2177 DYNAMIC_API PEP_STATUS outgoing_message_rating(
  2178         PEP_SESSION session,
  2179         message *msg,
  2180         PEP_rating *rating
  2181     )
  2182 {
  2183     PEP_comm_type max_comm_type = PEP_ct_pEp;
  2184     bool comm_type_determined = false;
  2185 
  2186     assert(session);
  2187     assert(msg);
  2188     assert(msg->dir == PEP_dir_outgoing);
  2189     assert(rating);
  2190 
  2191     if (!(session && msg && rating))
  2192         return ERROR(PEP_ILLEGAL_VALUE);
  2193 
  2194     if (msg->dir != PEP_dir_outgoing)
  2195         return ERROR(PEP_ILLEGAL_VALUE);
  2196 
  2197     *rating = PEP_rating_undefined;
  2198 
  2199     _max_comm_type_from_identity_list(msg->to, session,
  2200                                       &max_comm_type, &comm_type_determined);
  2201 
  2202     _max_comm_type_from_identity_list(msg->cc, session,
  2203                                       &max_comm_type, &comm_type_determined);
  2204 
  2205     _max_comm_type_from_identity_list(msg->bcc, session,
  2206                                       &max_comm_type, &comm_type_determined);
  2207 
  2208     if (comm_type_determined == false)
  2209         *rating = PEP_rating_undefined;
  2210     else
  2211         *rating = _MAX(_rating(max_comm_type, PEP_rating_undefined),
  2212                 PEP_rating_unencrypted);
  2213 
  2214     return PEP_STATUS_OK;
  2215 }
  2216 
  2217 DYNAMIC_API PEP_STATUS identity_rating(
  2218         PEP_SESSION session,
  2219         pEp_identity *ident,
  2220         PEP_rating *rating
  2221     )
  2222 {
  2223     PEP_STATUS status = PEP_STATUS_OK;
  2224 
  2225     assert(session);
  2226     assert(ident);
  2227     assert(rating);
  2228 
  2229     if (!(session && ident && rating))
  2230         return PEP_ILLEGAL_VALUE;
  2231 
  2232     if (ident->me)
  2233         status = _myself(session, ident, false, true);
  2234     else
  2235         status = update_identity(session, ident);
  2236 
  2237     if (status == PEP_STATUS_OK)
  2238         *rating = _rating(ident->comm_type, PEP_rating_undefined);
  2239 
  2240     return status;
  2241 }
  2242 
  2243 DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
  2244 {
  2245     PEP_STATUS status = PEP_STATUS_OK;
  2246 
  2247     assert(path);
  2248     if (path == NULL)
  2249         return PEP_ILLEGAL_VALUE;
  2250 
  2251     if (cryptotech[tech].binary_path == NULL)
  2252         *path = NULL;
  2253     else
  2254         status = cryptotech[tech].binary_path(path);
  2255 
  2256     return status;
  2257 }
  2258 
  2259 
  2260 DYNAMIC_API PEP_color color_from_rating(PEP_rating rating)
  2261 {
  2262     if (rating == PEP_rating_b0rken || rating == PEP_rating_have_no_key)
  2263         return PEP_color_no_color;
  2264 
  2265     if (rating < PEP_rating_undefined)
  2266         return PEP_color_red;
  2267 
  2268     if (rating < PEP_rating_reliable)
  2269         return PEP_color_no_color;
  2270 
  2271     if (rating < PEP_rating_trusted)
  2272         return PEP_color_yellow;
  2273 
  2274     if (rating >= PEP_rating_trusted)
  2275         return PEP_color_green;
  2276 
  2277     // this should never happen
  2278     assert(false);
  2279     return PEP_color_no_color;
  2280 }
  2281 
  2282 DYNAMIC_API PEP_STATUS get_trustwords(
  2283     PEP_SESSION session, const pEp_identity* id1, const pEp_identity* id2,
  2284     const char* lang, char **words, size_t *wsize, bool full
  2285 )
  2286 {
  2287     assert(session);
  2288     assert(id1);
  2289     assert(id2);
  2290     assert(id1->fpr);
  2291     assert(id2->fpr);
  2292     assert(words);
  2293     assert(wsize);
  2294 
  2295     if (!(session && id1 && id2 && words && wsize) ||
  2296         !(id1->fpr) || (!id2->fpr))
  2297         return PEP_ILLEGAL_VALUE;
  2298 
  2299     const char *source1 = id1->fpr;
  2300     const char *source2 = id2->fpr;
  2301 
  2302     *words = NULL;
  2303     *wsize = 0;
  2304 
  2305     const size_t SHORT_NUM_TWORDS = 5;
  2306 
  2307     // N.B. THIS will have to be changed once we start checking trustword entropy.
  2308     // For now, full is ALL, and otherwise it's 5-per-id.
  2309     size_t max_words_per_id = (full ? 0 : SHORT_NUM_TWORDS);
  2310 
  2311     char* first_set = NULL;
  2312     char* second_set = NULL;
  2313     size_t first_wsize = 0;
  2314     size_t second_wsize = 0;
  2315 
  2316     int fpr_comparison = -255;
  2317     PEP_STATUS status = _compare_fprs(source1, strlen(source1), source2, strlen(source2), &fpr_comparison);
  2318     if (status != PEP_STATUS_OK)
  2319         return status;
  2320 
  2321     char* _retstr = NULL;
  2322 
  2323     switch (fpr_comparison) {
  2324         case 1: // source1 > source2
  2325             status = trustwords(session, source2, lang, &first_set, &first_wsize, max_words_per_id);
  2326             if (status != PEP_STATUS_OK)
  2327                 goto error_release;
  2328             status = trustwords(session, source1, lang, &second_set, &second_wsize, max_words_per_id);
  2329             if (status != PEP_STATUS_OK)
  2330                 goto error_release;
  2331             break;
  2332         case 0:
  2333         case -1: // source1 <= source2
  2334             status = trustwords(session, source1, lang, &first_set, &first_wsize, max_words_per_id);
  2335             if (status != PEP_STATUS_OK)
  2336                 goto error_release;
  2337             status = trustwords(session, source2, lang, &second_set, &second_wsize, max_words_per_id);
  2338             if (status != PEP_STATUS_OK)
  2339                 goto error_release;
  2340             break;
  2341         default:
  2342             return ERROR(PEP_UNKNOWN_ERROR); // shouldn't be possible
  2343     }
  2344 
  2345     size_t _wsize = first_wsize + second_wsize;
  2346 
  2347     bool needs_space = (first_set[first_wsize - 1] != ' ');
  2348 
  2349     if (needs_space)
  2350         _wsize++;
  2351 
  2352     _retstr = calloc(1, _wsize + 1);
  2353 
  2354     size_t len = strlcpy(_retstr, first_set, _wsize);
  2355     if (len >= _wsize) {
  2356         status = PEP_UNKNOWN_ERROR;
  2357         goto error_release;
  2358     }
  2359     if (needs_space) {
  2360         strlcat(_retstr, " ", _wsize);
  2361         if (len >= _wsize) {
  2362             status = PEP_UNKNOWN_ERROR;
  2363             goto error_release;
  2364         }
  2365     }
  2366     strlcat(_retstr, second_set, _wsize);
  2367     if (len >= _wsize){
  2368         status = PEP_UNKNOWN_ERROR;
  2369         goto error_release;
  2370     }
  2371 
  2372     *words = _retstr;
  2373     *wsize = _wsize;
  2374     status = PEP_STATUS_OK;
  2375 
  2376     goto the_end;
  2377 
  2378     error_release:
  2379     free(_retstr);
  2380 
  2381     the_end:
  2382     free(first_set);
  2383     free(second_set);
  2384     return ERROR(status);
  2385 }
  2386 
  2387 DYNAMIC_API PEP_STATUS get_message_trustwords(
  2388     PEP_SESSION session, 
  2389     message *msg,
  2390     stringlist_t *keylist,
  2391     pEp_identity* received_by,
  2392     const char* lang, char **words, bool full
  2393 )
  2394 {
  2395     assert(session);
  2396     assert(msg);
  2397     assert(received_by);
  2398     assert(received_by->address);
  2399     assert(lang);
  2400     assert(words);
  2401 
  2402     if (!(session && 
  2403           msg &&
  2404           received_by && 
  2405           received_by->address && 
  2406           lang && 
  2407           words))
  2408         return PEP_ILLEGAL_VALUE;
  2409     
  2410     pEp_identity* partner = NULL;
  2411      
  2412     PEP_STATUS status = PEP_STATUS_OK;
  2413     
  2414     *words = NULL;
  2415 
  2416     // We want fingerprint of key that did sign the message
  2417 
  2418     if (keylist == NULL) {
  2419 
  2420         // Message is to be decrypted
  2421         message *dst = NULL;
  2422         stringlist_t *_keylist = keylist;
  2423         PEP_rating rating;
  2424         PEP_decrypt_flags_t flags;
  2425         status = decrypt_message( session, msg, &dst, &_keylist, &rating, &flags);
  2426 
  2427         if (status != PEP_STATUS_OK) {
  2428             free_message(dst);
  2429             free_stringlist(_keylist);
  2430             return status;
  2431         }
  2432 
  2433         if (dst && dst->from && _keylist) {
  2434             partner = identity_dup(dst->from); 
  2435             if(partner){
  2436                 free(partner->fpr);
  2437                 partner->fpr = strdup(_keylist->value);
  2438                 if (partner->fpr == NULL)
  2439                     status = PEP_OUT_OF_MEMORY;
  2440             } else {
  2441                 status = PEP_OUT_OF_MEMORY;
  2442             }
  2443         } else {
  2444             status = PEP_UNKNOWN_ERROR;
  2445         }
  2446 
  2447         free_message(dst);
  2448         free_stringlist(_keylist);
  2449 
  2450     } else {
  2451 
  2452         // Message already decrypted
  2453         if (keylist->value) {
  2454             partner = identity_dup(msg->from); 
  2455             if(partner){
  2456                 free(partner->fpr);
  2457                 partner->fpr = strdup(keylist->value);
  2458                 if (partner->fpr == NULL)
  2459                     status = PEP_OUT_OF_MEMORY;
  2460             } else {
  2461                 status = PEP_OUT_OF_MEMORY;
  2462             }
  2463         } else {
  2464             status = PEP_ILLEGAL_VALUE;
  2465         }
  2466     }
  2467 
  2468     if (status != PEP_STATUS_OK) {
  2469         free_identity(partner);
  2470         return ERROR(status);
  2471     }
  2472    
  2473     // Find own identity corresponding to given account address.
  2474     // In that case we want default key attached to own identity
  2475     pEp_identity *stored_identity = NULL;
  2476     status = get_identity(session,
  2477                           received_by->address,
  2478                           PEP_OWN_USERID,
  2479                           &stored_identity);
  2480 
  2481     if (status != PEP_STATUS_OK) {
  2482         free_identity(stored_identity);
  2483         return ERROR(status);
  2484     }
  2485 
  2486     // get the trustwords
  2487     size_t wsize;
  2488     status = get_trustwords(session, 
  2489                             partner, received_by, 
  2490                             lang, words, &wsize, full);
  2491 
  2492     return ERROR(status);
  2493 }
  2494 
  2495 DYNAMIC_API PEP_STATUS MIME_decrypt_message(
  2496     PEP_SESSION session,
  2497     const char *mimetext,
  2498     size_t size,
  2499     char** mime_plaintext,
  2500     stringlist_t **keylist,
  2501     PEP_rating *rating,
  2502     PEP_decrypt_flags_t *flags
  2503 )
  2504 {
  2505     assert(mimetext);
  2506     assert(mime_plaintext);
  2507     assert(keylist);
  2508     assert(rating);
  2509     assert(flags);
  2510 
  2511     PEP_STATUS status = PEP_STATUS_OK;
  2512     message* tmp_msg = NULL;
  2513     message* dec_msg = NULL;
  2514     *mime_plaintext = NULL;
  2515 
  2516     status = mime_decode_message(mimetext, size, &tmp_msg);
  2517     if (status != PEP_STATUS_OK)
  2518         GOTO(pep_error);
  2519 
  2520     PEP_STATUS decrypt_status = decrypt_message(session,
  2521                                                 tmp_msg,
  2522                                                 &dec_msg,
  2523                                                 keylist,
  2524                                                 rating,
  2525                                                 flags);
  2526                                                 
  2527     if (!dec_msg && (decrypt_status == PEP_UNENCRYPTED || decrypt_status == PEP_VERIFIED)) {
  2528         dec_msg = message_dup(tmp_msg);
  2529     }
  2530         
  2531     if (decrypt_status > PEP_CANNOT_DECRYPT_UNKNOWN || !dec_msg)
  2532     {
  2533         status = decrypt_status;
  2534         GOTO(pep_error);
  2535     }
  2536 
  2537     status = mime_encode_message(dec_msg, false, mime_plaintext);
  2538 
  2539     if (status == PEP_STATUS_OK)
  2540     {
  2541         free(tmp_msg);
  2542         free(dec_msg);
  2543         return ERROR(decrypt_status);
  2544     }
  2545     
  2546 pep_error:
  2547     free_message(tmp_msg);
  2548     free_message(dec_msg);
  2549 
  2550     return ERROR(status);
  2551 }
  2552 
  2553 
  2554 DYNAMIC_API PEP_STATUS MIME_encrypt_message(
  2555     PEP_SESSION session,
  2556     const char *mimetext,
  2557     size_t size,
  2558     stringlist_t* extra,
  2559     char** mime_ciphertext,
  2560     PEP_enc_format enc_format,
  2561     PEP_encrypt_flags_t flags
  2562 )
  2563 {
  2564     PEP_STATUS status = PEP_STATUS_OK;
  2565     message* tmp_msg = NULL;
  2566     message* enc_msg = NULL;
  2567 
  2568     status = mime_decode_message(mimetext, size, &tmp_msg);
  2569     if (status != PEP_STATUS_OK)
  2570         GOTO(pep_error);
  2571 
  2572     // This isn't incoming, though... so we need to reverse the direction
  2573     tmp_msg->dir = PEP_dir_outgoing;
  2574     status = encrypt_message(session,
  2575                              tmp_msg,
  2576                              extra,
  2577                              &enc_msg,
  2578                              enc_format,
  2579                              flags);
  2580     if (status != PEP_STATUS_OK)
  2581         GOTO(pep_error);
  2582 
  2583 
  2584     if (!enc_msg) {
  2585         status = PEP_UNKNOWN_ERROR;
  2586         GOTO(pep_error);
  2587     }
  2588 
  2589     status = mime_encode_message(enc_msg, false, mime_ciphertext);
  2590 
  2591 pep_error:
  2592     free_message(tmp_msg);
  2593     free_message(enc_msg);
  2594 
  2595     return ERROR(status);
  2596 
  2597 }
  2598 
  2599 DYNAMIC_API PEP_STATUS MIME_encrypt_message_for_self(
  2600     PEP_SESSION session,
  2601     pEp_identity* target_id,
  2602     const char *mimetext,
  2603     size_t size,
  2604     char** mime_ciphertext,
  2605     PEP_enc_format enc_format,
  2606     PEP_encrypt_flags_t flags
  2607 )
  2608 {
  2609     PEP_STATUS status = PEP_STATUS_OK;
  2610     message* tmp_msg = NULL;
  2611     message* enc_msg = NULL;
  2612 
  2613     status = mime_decode_message(mimetext, size, &tmp_msg);
  2614     if (status != PEP_STATUS_OK)
  2615         goto pep_error;
  2616 
  2617     // This isn't incoming, though... so we need to reverse the direction
  2618     tmp_msg->dir = PEP_dir_outgoing;
  2619     status = encrypt_message_for_self(session,
  2620                                       target_id,
  2621                                       tmp_msg,
  2622                                       &enc_msg,
  2623                                       enc_format,
  2624                                       flags);
  2625     if (status != PEP_STATUS_OK)
  2626         goto pep_error;
  2627  
  2628     if (!enc_msg) {
  2629         status = PEP_UNKNOWN_ERROR;
  2630         goto pep_error;
  2631     }
  2632 
  2633     status = mime_encode_message(enc_msg, false, mime_ciphertext);
  2634 
  2635 pep_error:
  2636     free_message(tmp_msg);
  2637     free_message(enc_msg);
  2638 
  2639     return ERROR(status);
  2640 }
  2641 
  2642 static PEP_rating string_to_rating(const char * rating)
  2643 {
  2644     if (rating == NULL)
  2645         return PEP_rating_undefined;
  2646     if (strcmp(rating, "cannot_decrypt") == 0)
  2647         return PEP_rating_cannot_decrypt;
  2648     if (strcmp(rating, "have_no_key") == 0)
  2649         return PEP_rating_have_no_key;
  2650     if (strcmp(rating, "unencrypted") == 0)
  2651         return PEP_rating_unencrypted;
  2652     if (strcmp(rating, "unencrypted_for_some") == 0)
  2653         return PEP_rating_unencrypted_for_some;
  2654     if (strcmp(rating, "unreliable") == 0)
  2655         return PEP_rating_unreliable;
  2656     if (strcmp(rating, "reliable") == 0)
  2657         return PEP_rating_reliable;
  2658     if (strcmp(rating, "trusted") == 0)
  2659         return PEP_rating_trusted;
  2660     if (strcmp(rating, "trusted_and_anonymized") == 0)
  2661         return PEP_rating_trusted_and_anonymized;
  2662     if (strcmp(rating, "fully_anonymous") == 0)
  2663         return PEP_rating_fully_anonymous;
  2664     if (strcmp(rating, "mistrust") == 0)
  2665         return PEP_rating_mistrust;
  2666     if (strcmp(rating, "b0rken") == 0)
  2667         return PEP_rating_b0rken;
  2668     if (strcmp(rating, "under_attack") == 0)
  2669         return PEP_rating_under_attack;
  2670     return PEP_rating_undefined;
  2671 }
  2672 
  2673 static PEP_STATUS string_to_keylist(const char * skeylist, stringlist_t **keylist)
  2674 {
  2675     if (skeylist == NULL || keylist == NULL)
  2676         return PEP_ILLEGAL_VALUE;
  2677 
  2678     stringlist_t *rkeylist = NULL;
  2679     stringlist_t *_kcurr = NULL;
  2680     const char * fpr_begin = skeylist;
  2681     const char * fpr_end = NULL;
  2682 
  2683     do {
  2684         fpr_end = strstr(fpr_begin, ",");
  2685         
  2686         char * fpr = strndup(
  2687             fpr_begin,
  2688             (fpr_end == NULL) ? strlen(fpr_begin) : fpr_end - fpr_begin);
  2689         
  2690         if (fpr == NULL)
  2691             goto enomem;
  2692         
  2693         _kcurr = stringlist_add(_kcurr, fpr);
  2694         if (_kcurr == NULL) {
  2695             free(fpr);
  2696             goto enomem;
  2697         }
  2698         
  2699         if (rkeylist == NULL)
  2700             rkeylist = _kcurr;
  2701         
  2702         fpr_begin = fpr_end ? fpr_end + 1 : NULL;
  2703         
  2704     } while (fpr_begin);
  2705     
  2706     *keylist = rkeylist;
  2707     return PEP_STATUS_OK;
  2708     
  2709 enomem:
  2710     free_stringlist(rkeylist);
  2711     return PEP_OUT_OF_MEMORY;
  2712 }
  2713 
  2714 DYNAMIC_API PEP_STATUS re_evaluate_message_rating(
  2715     PEP_SESSION session,
  2716     message *msg,
  2717     stringlist_t *x_keylist,
  2718     PEP_rating x_enc_status,
  2719     PEP_rating *rating
  2720 )
  2721 {
  2722     PEP_STATUS status = PEP_STATUS_OK;
  2723     stringlist_t *_keylist = x_keylist;
  2724     bool must_free_keylist = false;
  2725     PEP_rating _rating;
  2726 
  2727     assert(session);
  2728     assert(msg);
  2729     assert(rating);
  2730 
  2731     if (!(session && msg && rating))
  2732         return ERROR(PEP_ILLEGAL_VALUE);
  2733 
  2734     *rating = PEP_rating_undefined;
  2735 
  2736     if (x_enc_status == PEP_rating_undefined){
  2737         for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  2738             if (strcasecmp(i->value->key, "X-EncStatus") == 0){
  2739                 x_enc_status = string_to_rating(i->value->value);
  2740                 goto got_rating;
  2741             }
  2742         }
  2743         return ERROR(PEP_ILLEGAL_VALUE);
  2744     }
  2745 
  2746 got_rating:
  2747 
  2748     _rating = x_enc_status;
  2749 
  2750     if (_keylist == NULL){
  2751         for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  2752             if (strcasecmp(i->value->key, "X-KeyList") == 0){
  2753                 status = string_to_keylist(i->value->value, &_keylist);
  2754                 if (status != PEP_STATUS_OK)
  2755                     GOTO(pep_error);
  2756                 must_free_keylist = true;
  2757                 goto got_keylist;
  2758             }
  2759         }
  2760         return ERROR(PEP_ILLEGAL_VALUE);
  2761     }
  2762 got_keylist:
  2763 
  2764     status = update_identity(session, msg->from);
  2765     if (status != PEP_STATUS_OK)
  2766         GOTO(pep_error);
  2767 
  2768     status = amend_rating_according_to_sender_and_recipients(session,
  2769                                                              &_rating,
  2770                                                              msg->from,
  2771                                                              _keylist);
  2772     if (status == PEP_STATUS_OK)
  2773         *rating = _rating;
  2774     
  2775 pep_error:
  2776     if (must_free_keylist)
  2777         free_stringlist(_keylist);
  2778 
  2779     return ERROR(status);
  2780 }