src/message_api.c
author Edouard Tisserant <edouard@pep-project.org>
Thu, 15 Jun 2017 13:26:02 +0200
branchENGINE-204
changeset 1861 c8b6b3d58d40
parent 1745 373a181d7185
child 1862 881f5a1d8a40
permissions -rw-r--r--
ENGINE-204 closed
     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         return PEP_rating_unreliable;
   798 
   799     case PEP_DECRYPTED_AND_VERIFIED:
   800         return PEP_rating_reliable;
   801 
   802     case PEP_DECRYPT_NO_KEY:
   803         return PEP_rating_have_no_key;
   804 
   805     case PEP_DECRYPT_WRONG_FORMAT:
   806     case PEP_CANNOT_DECRYPT_UNKNOWN:
   807         return PEP_rating_cannot_decrypt;
   808 
   809     default:
   810         return PEP_rating_undefined;
   811     }
   812 }
   813 
   814 static PEP_rating key_rating(PEP_SESSION session, const char *fpr)
   815 {
   816 
   817     assert(session);
   818     assert(fpr);
   819 
   820     if (session == NULL || fpr == NULL)
   821         return PEP_rating_undefined;
   822 
   823 
   824     PEP_comm_type bare_comm_type = PEP_ct_unknown;
   825     PEP_STATUS status = get_key_rating(session, fpr, &bare_comm_type);
   826     if (status != PEP_STATUS_OK)
   827         return PEP_rating_undefined;
   828 
   829     PEP_comm_type least_trust_type = PEP_ct_unknown;
   830     least_trust(session, fpr, &least_trust_type);
   831 
   832     if (least_trust_type == PEP_ct_unknown) {
   833         return _rating(bare_comm_type, PEP_rating_undefined);
   834     } else {
   835         return _rating(least_trust_type, PEP_rating_undefined);
   836     }
   837 }
   838 
   839 static PEP_rating worst_rating(PEP_rating rating1, PEP_rating rating2) {
   840     return ((rating1 < rating2) ? rating1 : rating2);
   841 }
   842 
   843 static PEP_rating keylist_rating(PEP_SESSION session, stringlist_t *keylist)
   844 {
   845     PEP_rating rating = PEP_rating_undefined;
   846 
   847     assert(keylist && keylist->value);
   848     if (keylist == NULL || keylist->value == NULL)
   849         return PEP_rating_undefined;
   850 
   851     stringlist_t *_kl;
   852     bool first = true;
   853     for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
   854         PEP_comm_type ct;
   855         PEP_STATUS status;
   856 
   857         PEP_rating _rating_ = key_rating(session, _kl->value);
   858          
   859         if (_rating_ <= PEP_rating_mistrust)
   860             return _rating_;
   861             
   862         if (first) {
   863             rating = _rating_;
   864             first = false;
   865         }
   866         else if (rating == PEP_rating_undefined)
   867             rating = worst_rating(rating, _rating_);
   868 
   869         if (_rating_ >= PEP_rating_reliable) {
   870             status = least_trust(session, _kl->value, &ct);
   871             if (status != PEP_STATUS_OK)
   872                 return PEP_rating_undefined;
   873             if (ct == PEP_ct_unknown){
   874                 /* per edouard, we reduce reliable+ ratings to reliable because
   875                    ct unknown */
   876                 if (rating >= PEP_rating_reliable){
   877                     rating = PEP_rating_reliable; 
   878                 }
   879             }
   880             else{
   881                 rating = worst_rating(rating, _rating(ct, rating));
   882             }
   883         }
   884         else if (_rating_ == PEP_rating_unencrypted) {
   885             if (rating > PEP_rating_unencrypted_for_some)
   886                 rating = worst_rating(rating, PEP_rating_unencrypted_for_some);
   887         }
   888     }
   889 
   890     return rating;
   891 }
   892 
   893 static PEP_comm_type _get_comm_type(
   894     PEP_SESSION session,
   895     PEP_comm_type max_comm_type,
   896     pEp_identity *ident
   897     )
   898 {
   899     PEP_STATUS status = update_identity(session, ident);
   900 
   901     if (max_comm_type == PEP_ct_compromized)
   902         return PEP_ct_compromized;
   903 
   904     if (max_comm_type == PEP_ct_mistrusted)
   905         return PEP_ct_mistrusted;
   906 
   907     if (status == PEP_STATUS_OK) {
   908         if (ident->comm_type == PEP_ct_compromized)
   909             return PEP_ct_compromized;
   910         else if (ident->comm_type == PEP_ct_mistrusted)
   911             return PEP_ct_mistrusted;
   912         else
   913             return _MIN(max_comm_type, ident->comm_type);
   914     }
   915     else {
   916         return PEP_ct_unknown;
   917     }
   918 }
   919 
   920 static void free_bl_entry(bloblist_t *bl)
   921 {
   922     if (bl) {
   923         free(bl->value);
   924         free(bl->mime_type);
   925         free(bl->filename);
   926         free(bl);
   927     }
   928 }
   929 
   930 static bool is_key(const bloblist_t *bl)
   931 {
   932     return (// workaround for Apple Mail bugs
   933             (is_mime_type(bl, "application/x-apple-msg-attachment") &&
   934              is_fileending(bl, ".asc")) ||
   935             // as binary, by file name
   936             ((bl->mime_type == NULL ||
   937               is_mime_type(bl, "application/octet-stream")) &&
   938              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   939                     is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
   940             // explicit mime type
   941             is_mime_type(bl, "application/pgp-keys") ||
   942             // as text, by file name
   943             (is_mime_type(bl, "text/plain") &&
   944              (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
   945                     is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
   946            );
   947 }
   948 
   949 static void remove_attached_keys(message *msg)
   950 {
   951     if (msg) {
   952         bloblist_t *last = NULL;
   953         for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
   954             bloblist_t *next = bl->next;
   955 
   956             if (is_key(bl)) {
   957                 if (last) {
   958                     last->next = next;
   959                 }
   960                 else {
   961                     msg->attachments = next;
   962                 }
   963                 free_bl_entry(bl);
   964             }
   965             else {
   966                 last = bl;
   967             }
   968             bl = next;
   969         }
   970     }
   971 }
   972 
   973 bool import_attached_keys(
   974         PEP_SESSION session,
   975         const message *msg,
   976         identity_list **private_idents
   977     )
   978 {
   979     assert(session);
   980     assert(msg);
   981 
   982     if (session == NULL || msg == NULL)
   983         return false;
   984 
   985     bool remove = false;
   986 
   987     int i = 0;
   988     for (bloblist_t *bl = msg->attachments; i < MAX_KEYS_TO_IMPORT && bl && bl->value;
   989             bl = bl->next, i++)
   990     {
   991         if (bl && bl->value && bl->size && bl->size < MAX_KEY_SIZE
   992                 && is_key(bl))
   993         {
   994             identity_list *local_private_idents = NULL;
   995             import_key(session, bl->value, bl->size, &local_private_idents);
   996             remove = true;
   997             if (*private_idents == NULL && local_private_idents != NULL)
   998                 *private_idents = local_private_idents;
   999             else
  1000                 free_identity_list(local_private_idents);
  1001         }
  1002     }
  1003     return remove;
  1004 }
  1005 
  1006 
  1007 PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
  1008 {
  1009     char *keydata = NULL;
  1010     size_t size;
  1011 
  1012     PEP_STATUS status = export_key(session, fpr, &keydata, &size);
  1013     assert(status == PEP_STATUS_OK);
  1014     if (status != PEP_STATUS_OK)
  1015         return status;
  1016     assert(size);
  1017 
  1018      bloblist_t *bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
  1019                       "pEpkey.asc");
  1020 
  1021     if (msg->attachments == NULL && bl)
  1022         msg->attachments = bl;
  1023 
  1024     return PEP_STATUS_OK;
  1025 }
  1026 
  1027 #define ONE_WEEK (7*24*3600)
  1028 
  1029 void attach_own_key(PEP_SESSION session, message *msg)
  1030 {
  1031     assert(session);
  1032     assert(msg);
  1033 
  1034     if (msg->dir == PEP_dir_incoming)
  1035         return;
  1036 
  1037     assert(msg->from && msg->from->fpr);
  1038     if (msg->from == NULL || msg->from->fpr == NULL)
  1039         return;
  1040 
  1041     if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
  1042         return;
  1043 
  1044     char *revoked_fpr = NULL;
  1045     uint64_t revocation_date = 0;
  1046 
  1047     if(get_revoked(session, msg->from->fpr,
  1048                    &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
  1049        revoked_fpr != NULL)
  1050     {
  1051         time_t now = time(NULL);
  1052 
  1053         if (now < (time_t)revocation_date + ONE_WEEK)
  1054         {
  1055             _attach_key(session, revoked_fpr, msg);
  1056         }
  1057     }
  1058     free(revoked_fpr);
  1059 }
  1060 
  1061 PEP_cryptotech determine_encryption_format(message *msg)
  1062 {
  1063     assert(msg);
  1064 
  1065     if (is_PGP_message_text(msg->longmsg)) {
  1066         msg->enc_format = PEP_enc_pieces;
  1067         return PEP_crypt_OpenPGP;
  1068     }
  1069     else if (msg->attachments && msg->attachments->next &&
  1070             is_mime_type(msg->attachments, "application/pgp-encrypted") &&
  1071             is_PGP_message_text(msg->attachments->next->value)
  1072         ) {
  1073         msg->enc_format = PEP_enc_PGP_MIME;
  1074         return PEP_crypt_OpenPGP;
  1075     }
  1076     else if (msg->attachments && msg->attachments->next &&
  1077             is_mime_type(msg->attachments->next, "application/pgp-encrypted") &&
  1078             is_PGP_message_text(msg->attachments->value)
  1079         ) {
  1080         msg->enc_format = PEP_enc_PGP_MIME_Outlook1;
  1081         return PEP_crypt_OpenPGP;
  1082     }
  1083     else {
  1084         msg->enc_format = PEP_enc_none;
  1085         return PEP_crypt_none;
  1086     }
  1087 }
  1088 
  1089 DYNAMIC_API PEP_STATUS encrypt_message(
  1090         PEP_SESSION session,
  1091         message *src,
  1092         stringlist_t * extra,
  1093         message **dst,
  1094         PEP_enc_format enc_format,
  1095         PEP_encrypt_flags_t flags
  1096     )
  1097 {
  1098     PEP_STATUS status = PEP_STATUS_OK;
  1099     message * msg = NULL;
  1100     stringlist_t * keys = NULL;
  1101 
  1102     assert(session);
  1103     assert(src);
  1104     assert(dst);
  1105     assert(enc_format != PEP_enc_none);
  1106 
  1107     if (!(session && src && dst && enc_format != PEP_enc_none))
  1108         return PEP_ILLEGAL_VALUE;
  1109 
  1110     if (src->dir == PEP_dir_incoming)
  1111         return PEP_ILLEGAL_VALUE;
  1112 
  1113     determine_encryption_format(src);
  1114     if (src->enc_format != PEP_enc_none)
  1115         return PEP_ILLEGAL_VALUE;
  1116 
  1117     *dst = NULL;
  1118 
  1119     status = myself(session, src->from);
  1120     if (status != PEP_STATUS_OK)
  1121         goto pep_error;
  1122 
  1123     keys = new_stringlist(src->from->fpr);
  1124     if (keys == NULL)
  1125         goto enomem;
  1126 
  1127     stringlist_t *_k = keys;
  1128 
  1129     if (extra) {
  1130         _k = stringlist_append(_k, extra);
  1131         if (_k == NULL)
  1132             goto enomem;
  1133     }
  1134 
  1135     bool dest_keys_found = true;
  1136     PEP_comm_type max_comm_type = PEP_ct_pEp;
  1137 
  1138     identity_list * _il;
  1139 
  1140     if ((_il = src->bcc) && _il->ident)
  1141     {
  1142         // BCC limited support:
  1143         //     - App splits mails with BCC in multiple mails.
  1144         //     - Each email is encrypted separately
  1145 
  1146         if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
  1147         {
  1148             // Only one Bcc with no other recipient allowed for now
  1149             return PEP_ILLEGAL_VALUE;
  1150         }
  1151 
  1152         PEP_STATUS _status = update_identity(session, _il->ident);
  1153         if (_status != PEP_STATUS_OK) {
  1154             status = _status;
  1155             goto pep_error;
  1156         }
  1157 
  1158         if (_il->ident->fpr && _il->ident->fpr[0]) {
  1159             _k = stringlist_add(_k, _il->ident->fpr);
  1160             if (_k == NULL)
  1161                 goto enomem;
  1162             max_comm_type = _get_comm_type(session, max_comm_type,
  1163                                            _il->ident);
  1164         }
  1165         else {
  1166             dest_keys_found = false;
  1167             status = PEP_KEY_NOT_FOUND;
  1168         }
  1169     }
  1170     else
  1171     {
  1172         for (_il = src->to; _il && _il->ident; _il = _il->next) {
  1173             PEP_STATUS _status = update_identity(session, _il->ident);
  1174             if (_status != PEP_STATUS_OK) {
  1175                 status = _status;
  1176                 goto pep_error;
  1177             }
  1178 
  1179             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1180                 _k = stringlist_add(_k, _il->ident->fpr);
  1181                 if (_k == NULL)
  1182                     goto enomem;
  1183                 max_comm_type = _get_comm_type(session, max_comm_type,
  1184                                                _il->ident);
  1185             }
  1186             else {
  1187                 dest_keys_found = false;
  1188                 status = PEP_KEY_NOT_FOUND;
  1189             }
  1190         }
  1191 
  1192         for (_il = src->cc; _il && _il->ident; _il = _il->next) {
  1193             PEP_STATUS _status = update_identity(session, _il->ident);
  1194             if (_status != PEP_STATUS_OK)
  1195             {
  1196                 status = _status;
  1197                 goto pep_error;
  1198             }
  1199 
  1200             if (_il->ident->fpr && _il->ident->fpr[0]) {
  1201                 _k = stringlist_add(_k, _il->ident->fpr);
  1202                 if (_k == NULL)
  1203                     goto enomem;
  1204                 max_comm_type = _get_comm_type(session, max_comm_type,
  1205                                                _il->ident);
  1206             }
  1207             else {
  1208                 dest_keys_found = false;
  1209                 status = PEP_KEY_NOT_FOUND;
  1210             }
  1211         }
  1212     }
  1213 
  1214     if (!dest_keys_found ||
  1215         stringlist_length(keys)  == 0 ||
  1216         _rating(max_comm_type,
  1217                 PEP_rating_undefined) < PEP_rating_reliable)
  1218     {
  1219         free_stringlist(keys);
  1220         if (!session->passive_mode && !(flags & PEP_encrypt_flag_force_no_attached_key))
  1221             attach_own_key(session, src);
  1222         return PEP_UNENCRYPTED;
  1223     }
  1224     else {
  1225         msg = clone_to_empty_message(src);
  1226         if (msg == NULL)
  1227             goto enomem;
  1228 
  1229         if (!(flags & PEP_encrypt_flag_force_no_attached_key))
  1230             attach_own_key(session, src);
  1231 
  1232         switch (enc_format) {
  1233         case PEP_enc_PGP_MIME:
  1234         case PEP_enc_PEP: // BUG: should be implemented extra
  1235             status = encrypt_PGP_MIME(session, src, keys, msg, flags);
  1236             break;
  1237 
  1238         case PEP_enc_pieces:
  1239             status = encrypt_PGP_in_pieces(session, src, keys, msg, flags);
  1240             break;
  1241 
  1242         /* case PEP_enc_PEP:
  1243             // TODO: implement
  1244             NOT_IMPLEMENTED */
  1245 
  1246         default:
  1247             assert(0);
  1248             status = PEP_ILLEGAL_VALUE;
  1249             goto pep_error;
  1250         }
  1251 
  1252         if (status == PEP_OUT_OF_MEMORY)
  1253             goto enomem;
  1254 
  1255         if (status != PEP_STATUS_OK)
  1256             goto pep_error;
  1257     }
  1258 
  1259     free_stringlist(keys);
  1260 
  1261     if (msg && msg->shortmsg == NULL) {
  1262         msg->shortmsg = strdup("pEp");
  1263         assert(msg->shortmsg);
  1264         if (msg->shortmsg == NULL)
  1265             goto enomem;
  1266     }
  1267 
  1268     if (msg) {
  1269         decorate_message(msg, PEP_rating_undefined, NULL);
  1270         if (src->id) {
  1271             msg->id = strdup(src->id);
  1272             assert(msg->id);
  1273             if (msg->id == NULL)
  1274                 goto enomem;
  1275         }
  1276     }
  1277 
  1278     *dst = msg;
  1279     return status;
  1280 
  1281 enomem:
  1282     status = PEP_OUT_OF_MEMORY;
  1283 
  1284 pep_error:
  1285     free_stringlist(keys);
  1286     free_message(msg);
  1287 
  1288     return status;
  1289 }
  1290 
  1291 DYNAMIC_API PEP_STATUS encrypt_message_for_self(
  1292         PEP_SESSION session,
  1293         pEp_identity* target_id,
  1294         message *src,
  1295         message **dst,
  1296         PEP_enc_format enc_format,
  1297         PEP_encrypt_flags_t flags
  1298     )
  1299 {
  1300     PEP_STATUS status = PEP_STATUS_OK;
  1301     message * msg = NULL;
  1302     stringlist_t * keys = NULL;
  1303 
  1304     assert(session);
  1305     assert(src);
  1306     assert(dst);
  1307     assert(enc_format != PEP_enc_none);
  1308 
  1309     if (!(session && src && dst && enc_format != PEP_enc_none))
  1310         return PEP_ILLEGAL_VALUE;
  1311 
  1312     if (src->dir == PEP_dir_incoming)
  1313         return PEP_ILLEGAL_VALUE;
  1314 
  1315     determine_encryption_format(src);
  1316     if (src->enc_format != PEP_enc_none)
  1317         return PEP_ILLEGAL_VALUE;
  1318 
  1319     status = myself(session, target_id);
  1320     if (status != PEP_STATUS_OK)
  1321         goto pep_error;
  1322 
  1323     *dst = NULL;
  1324 
  1325 
  1326     PEP_STATUS _status = update_identity(session, target_id);
  1327     if (_status != PEP_STATUS_OK) {
  1328         status = _status;
  1329         goto pep_error;
  1330     }
  1331 
  1332     char* target_fpr = target_id->fpr;
  1333     if (!target_fpr)
  1334         return PEP_KEY_NOT_FOUND; // FIXME: Error condition
  1335  
  1336     keys = new_stringlist(target_fpr);
  1337     
  1338     /* KG: did we ever do this??? */
  1339     if (!(flags & PEP_encrypt_flag_force_no_attached_key))
  1340         _attach_key(session, target_fpr, src);
  1341 
  1342     msg = clone_to_empty_message(src);
  1343     if (msg == NULL)
  1344         goto enomem;
  1345 
  1346     switch (enc_format) {
  1347         case PEP_enc_PGP_MIME:
  1348         case PEP_enc_PEP: // BUG: should be implemented extra
  1349             status = encrypt_PGP_MIME(session, src, keys, msg, flags);
  1350             break;
  1351 
  1352         case PEP_enc_pieces:
  1353             status = encrypt_PGP_in_pieces(session, src, keys, msg, flags);
  1354             break;
  1355 
  1356         /* case PEP_enc_PEP:
  1357             NOT_IMPLEMENTED */
  1358             // TODO: implement
  1359 
  1360         default:
  1361             assert(0);
  1362             status = PEP_ILLEGAL_VALUE;
  1363             goto pep_error;
  1364     }
  1365 
  1366     if (status == PEP_OUT_OF_MEMORY)
  1367         goto enomem;
  1368 
  1369     if (status != PEP_STATUS_OK)
  1370         goto pep_error;
  1371 
  1372      if (msg && msg->shortmsg == NULL) {
  1373          msg->shortmsg = strdup("pEp");
  1374          assert(msg->shortmsg);
  1375          if (msg->shortmsg == NULL)
  1376              goto enomem;
  1377      }
  1378 
  1379      if (msg) {
  1380          if (src->id) {
  1381              msg->id = strdup(src->id);
  1382              assert(msg->id);
  1383              if (msg->id == NULL)
  1384                  goto enomem;
  1385          }
  1386      }
  1387 
  1388     *dst = msg;
  1389     return status;
  1390 
  1391 enomem:
  1392     status = PEP_OUT_OF_MEMORY;
  1393 
  1394 pep_error:
  1395     free_stringlist(keys);
  1396     free_message(msg);
  1397 
  1398     return status;
  1399 }
  1400 
  1401 static bool is_a_pEpmessage(const message *msg)
  1402 {
  1403     for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
  1404         if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
  1405             return true;
  1406     }
  1407     return false;
  1408 }
  1409 
  1410 // update comm_type to pEp_ct_pEp if needed
  1411 
  1412 static PEP_STATUS _update_identity_for_incoming_message(
  1413         PEP_SESSION session,
  1414         const message *src
  1415     )
  1416 {
  1417     PEP_STATUS status;
  1418     if (src->from && src->from->address) {
  1419         status = update_identity(session, src->from);
  1420         if (status == PEP_STATUS_OK
  1421                 && is_a_pEpmessage(src)
  1422                 && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
  1423                 && src->from->comm_type != PEP_ct_pEp_unconfirmed
  1424                 && src->from->comm_type != PEP_ct_pEp)
  1425         {
  1426             src->from->comm_type |= PEP_ct_pEp_unconfirmed;
  1427             status = update_identity(session, src->from);
  1428         }
  1429         return status;
  1430     }
  1431     return PEP_ILLEGAL_VALUE;
  1432 }
  1433 
  1434 
  1435 PEP_STATUS _get_detached_signature(message* msg, bloblist_t** signature_blob) {
  1436     bloblist_t* attach_curr = msg->attachments;
  1437 
  1438     *signature_blob = NULL;
  1439 
  1440     while (attach_curr) {
  1441         if (strcasecmp(attach_curr->mime_type, "application/pgp-signature") == 0) {
  1442             *signature_blob = attach_curr;
  1443             break;
  1444         }
  1445         attach_curr = attach_curr->next;
  1446     }
  1447 
  1448     return PEP_STATUS_OK;
  1449 }
  1450 
  1451 PEP_STATUS _get_signed_text(const char* ptext, const size_t psize,
  1452                             char** stext, size_t* ssize) {
  1453 
  1454     char* signed_boundary = NULL;
  1455     char* signpost = strstr(ptext, "Content-Type: multipart/signed");
  1456 
  1457     *ssize = 0;
  1458     *stext = NULL;
  1459 
  1460     if (!signpost)
  1461         return PEP_UNKNOWN_ERROR;
  1462 
  1463     char* curr_line = signpost;
  1464 //    const char* end_text = ptext + psize;
  1465     const char* boundary_key = "boundary=\"";
  1466     const size_t BOUNDARY_KEY_SIZE = 10;
  1467 
  1468     char* start_boundary = strstr(curr_line, boundary_key);
  1469     if (!start_boundary)
  1470         return PEP_UNKNOWN_ERROR;
  1471 
  1472     start_boundary += BOUNDARY_KEY_SIZE;
  1473 
  1474     char* end_boundary = strstr(start_boundary, "\"");
  1475 
  1476     if (!end_boundary)
  1477         return PEP_UNKNOWN_ERROR;
  1478 
  1479     size_t boundary_strlen = (end_boundary - start_boundary) + 2;
  1480 
  1481     signed_boundary = calloc(1, boundary_strlen + 1);
  1482     strlcpy(signed_boundary, "--", boundary_strlen + 1);
  1483     strlcat(signed_boundary, start_boundary, boundary_strlen + 1);
  1484 
  1485     start_boundary = strstr(end_boundary, signed_boundary);
  1486 
  1487     if (!start_boundary)
  1488         return PEP_UNKNOWN_ERROR;
  1489 
  1490     start_boundary += boundary_strlen;
  1491 
  1492     while (*start_boundary == '\n')
  1493         start_boundary++;
  1494 
  1495     end_boundary = strstr(start_boundary + boundary_strlen, signed_boundary);
  1496 
  1497     if (!end_boundary)
  1498         return PEP_UNKNOWN_ERROR;
  1499 
  1500     end_boundary--; // See RFC3156 section 5...
  1501 
  1502     *ssize = end_boundary - start_boundary;
  1503     *stext = start_boundary;
  1504     free(signed_boundary);
  1505 
  1506     return PEP_STATUS_OK;
  1507 }
  1508 
  1509 PEP_STATUS combine_keylists(PEP_SESSION session, stringlist_t** verify_in, 
  1510                             stringlist_t** keylist_in_out, 
  1511                             pEp_identity* from) {
  1512     
  1513     if (!verify_in || !(*verify_in)) // this isn't really a problem.
  1514         return PEP_STATUS_OK;
  1515     
  1516     stringlist_t* orig_verify = *verify_in;
  1517     
  1518     stringlist_t* verify_curr = NULL;
  1519     stringlist_t* from_keys = NULL;
  1520     
  1521     /* FIXME: what to do if head needs to be null */
  1522     PEP_STATUS status = find_keys(session, from->address, &from_keys);
  1523     
  1524     stringlist_t* from_fpr_node = NULL;
  1525     stringlist_t* from_curr;
  1526     
  1527     for (from_curr = from_keys; from_curr; from_curr = from_curr->next) {
  1528         for (verify_curr = orig_verify; verify_curr; verify_curr = verify_curr->next) {
  1529             if (from_curr->value && verify_curr->value &&
  1530                 _same_fpr(from_curr->value, strlen(from_curr->value),
  1531                           verify_curr->value, strlen(verify_curr->value))) {
  1532                 from_fpr_node = from_curr;
  1533                 break;
  1534             }
  1535         }
  1536     }
  1537     
  1538     if (!from_fpr_node) {
  1539         status = PEP_KEY_NOT_FOUND;
  1540         goto free;
  1541     }
  1542 
  1543     verify_curr = orig_verify;
  1544     
  1545     /* put "from" signer at the beginning of the list */
  1546     if (!_same_fpr(orig_verify->value, strlen(orig_verify->value),
  1547                    from_fpr_node->value, strlen(from_fpr_node->value))) {
  1548         orig_verify = stringlist_delete(orig_verify, from_fpr_node->value);
  1549         verify_curr = new_stringlist(from_fpr_node->value);
  1550         verify_curr->next = orig_verify;
  1551     }
  1552 
  1553     /* append keylist to signers */
  1554     if (keylist_in_out && *keylist_in_out && (*keylist_in_out)->value) {
  1555         stringlist_t** tail_pp = &verify_curr->next;
  1556         
  1557         while (*tail_pp) {
  1558             tail_pp = &((*tail_pp)->next);
  1559         }
  1560         stringlist_t* second_list = *keylist_in_out;
  1561         if (second_list) {
  1562             char* listhead_val = second_list->value;
  1563             if (!listhead_val || listhead_val[0] == '\0') {
  1564                 /* remove head, basically. This can happen when,
  1565                    for example, the signature is detached and
  1566                    verification is not seen directly after
  1567                    decryption, so no signer is presumed in
  1568                    the first construction of the keylist */
  1569                 *keylist_in_out = (*keylist_in_out)->next;
  1570                 second_list->next = NULL;
  1571                 free_stringlist(second_list);
  1572             }
  1573         }
  1574         *tail_pp = *keylist_in_out;
  1575     }
  1576     
  1577     *keylist_in_out = verify_curr;
  1578     
  1579     status = PEP_STATUS_OK;
  1580     
  1581 free:
  1582     free_stringlist(from_keys);
  1583     return status;
  1584 }
  1585 
  1586 
  1587 DYNAMIC_API PEP_STATUS _decrypt_message(
  1588         PEP_SESSION session,
  1589         message *src,
  1590         message **dst,
  1591         stringlist_t **keylist,
  1592         PEP_rating *rating,
  1593         PEP_decrypt_flags_t *flags,
  1594         identity_list **private_il
  1595     )
  1596 {
  1597     PEP_STATUS status = PEP_STATUS_OK;
  1598     PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
  1599     message *msg = NULL;
  1600     char *ctext;
  1601     size_t csize;
  1602     char *ptext = NULL;
  1603     size_t psize;
  1604     stringlist_t *_keylist = NULL;
  1605 
  1606     assert(session);
  1607     assert(src);
  1608     assert(dst);
  1609     assert(keylist);
  1610     assert(rating);
  1611     assert(flags);
  1612 
  1613     if (!(session && src && dst && keylist && rating && flags))
  1614         return PEP_ILLEGAL_VALUE;
  1615 
  1616     *flags = 0;
  1617 
  1618     // Private key in unencrypted mail are ignored -> NULL
  1619     bool imported_keys = import_attached_keys(session, src, NULL);
  1620 
  1621     // Update src->from in case we just imported a key
  1622     // we would need to check signature
  1623     status = _update_identity_for_incoming_message(session, src);
  1624     if(status != PEP_STATUS_OK)
  1625         return status;
  1626 
  1627     // Get detached signature, if any
  1628     bloblist_t* detached_sig = NULL;
  1629     char* dsig_text = NULL;
  1630     size_t dsig_size = 0;
  1631     status = _get_detached_signature(src, &detached_sig);
  1632     if (detached_sig) {
  1633         dsig_text = detached_sig->value;
  1634         dsig_size = detached_sig->size;
  1635     }
  1636 
  1637     PEP_cryptotech crypto = determine_encryption_format(src);
  1638 
  1639     *dst = NULL;
  1640     *keylist = NULL;
  1641     *rating = PEP_rating_undefined;
  1642 
  1643     switch (src->enc_format) {
  1644         case PEP_enc_none:
  1645             *rating = PEP_rating_unencrypted;
  1646             if (imported_keys)
  1647                 remove_attached_keys(src);
  1648             if(session->sync_session->inject_sync_msg){
  1649                 status = receive_DeviceState_msg(session, src, *rating, *keylist);
  1650                 if (status == PEP_MESSAGE_CONSUME ||
  1651                     status == PEP_MESSAGE_IGNORE) {
  1652                     free_message(msg);
  1653                     msg = NULL;
  1654                     *flags |= (status == PEP_MESSAGE_IGNORE) ?
  1655                                 PEP_decrypt_flag_ignore :
  1656                                 PEP_decrypt_flag_consume;
  1657                 }
  1658                 else if (status != PEP_STATUS_OK) {
  1659                     return status;
  1660                 }
  1661             }
  1662             
  1663             char* slong = src->longmsg;
  1664             char* sform = src->longmsg_formatted;
  1665             bloblist_t* satt = src->attachments;
  1666             
  1667             if ((!slong || slong[0] == '\0')
  1668                  && (!sform || sform[0] == '\0')) {
  1669                 if (satt) {
  1670                     const char* inner_mime_type = satt->mime_type;
  1671                     if (strcasecmp(inner_mime_type, "text/plain") == 0) {
  1672                         free(slong); /* in case of "" */
  1673                         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!
  1674                         
  1675                         bloblist_t* next_node = satt->next;
  1676                         if (next_node) {
  1677                             inner_mime_type = next_node->mime_type;
  1678                             if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1679                                 free(sform);
  1680                                 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!
  1681                             }
  1682                         }
  1683                     }
  1684                     else if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1685                         free(sform);
  1686                         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!
  1687                     }
  1688                 }
  1689             }
  1690             
  1691             return PEP_UNENCRYPTED;
  1692 
  1693         case PEP_enc_PGP_MIME:
  1694             ctext = src->attachments->next->value;
  1695             csize = src->attachments->next->size;
  1696             break;
  1697 
  1698         case PEP_enc_PGP_MIME_Outlook1:
  1699             ctext = src->attachments->value;
  1700             csize = src->attachments->size;
  1701             break;
  1702 
  1703         case PEP_enc_pieces:
  1704             ctext = src->longmsg;
  1705             csize = strlen(ctext);
  1706             break;
  1707 
  1708         default:
  1709             NOT_IMPLEMENTED
  1710     }
  1711     status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1712                                                    csize, dsig_text, dsig_size,
  1713                                                    &ptext, &psize, &_keylist);
  1714     if (status > PEP_CANNOT_DECRYPT_UNKNOWN){
  1715         goto pep_error;
  1716     }
  1717 
  1718     decrypt_status = status;
  1719 
  1720     if (status == PEP_DECRYPT_NO_KEY){
  1721         PEP_STATUS sync_status = inject_DeviceState_event(session, CannotDecrypt, NULL, NULL);
  1722         if (sync_status == PEP_OUT_OF_MEMORY){
  1723             status = PEP_OUT_OF_MEMORY;
  1724             goto pep_error;
  1725         }
  1726     }
  1727 
  1728     bool imported_private_key_address = false;
  1729 
  1730     if (ptext) {
  1731         switch (src->enc_format) {
  1732             case PEP_enc_PGP_MIME:
  1733             case PEP_enc_PGP_MIME_Outlook1:
  1734                 status = mime_decode_message(ptext, psize, &msg);
  1735                 if (status != PEP_STATUS_OK)
  1736                     goto pep_error;
  1737                 
  1738                 char* mlong = msg->longmsg;
  1739                 char* mform = msg->longmsg_formatted;
  1740                 bloblist_t* matt = msg->attachments;
  1741                 
  1742                 if ((!mlong || mlong[0] == '\0')
  1743                      && (!mform || mform[0] == '\0')) {
  1744                     if (matt) {
  1745                         const char* inner_mime_type = matt->mime_type;
  1746                         if (strcasecmp(inner_mime_type, "text/plain") == 0) {
  1747                             free(mlong); /* in case of "" */
  1748                             msg->longmsg = strndup(matt->value, matt->size);
  1749                             
  1750                             bloblist_t* next_node = matt->next;
  1751                             if (next_node) {
  1752                                 inner_mime_type = next_node->mime_type;
  1753                                 if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1754                                     free(mform);
  1755                                     msg->longmsg_formatted = strndup(next_node->value, next_node->size);
  1756                                 }
  1757                             }
  1758                         }
  1759                         else if (strcasecmp(inner_mime_type, "text/html") == 0) {
  1760                             free(mform);
  1761                             msg->longmsg_formatted = strndup(matt->value, matt->size);
  1762                         }
  1763                     }
  1764                     if (msg->shortmsg) {
  1765                         free(src->shortmsg);
  1766                         src->shortmsg = strdup(msg->shortmsg);
  1767                     }
  1768                 }
  1769 
  1770                 if (decrypt_status != PEP_DECRYPTED_AND_VERIFIED) {
  1771                     status = _get_detached_signature(msg, &detached_sig);
  1772                     if (decrypt_status == PEP_DECRYPTED && detached_sig) {
  1773                         dsig_text = detached_sig->value;
  1774                         dsig_size = detached_sig->size;
  1775                         size_t ssize = 0;
  1776                         char* stext = NULL;
  1777 
  1778                         status = _get_signed_text(ptext, psize, &stext, &ssize);
  1779                         stringlist_t *_verify_keylist = NULL;
  1780 
  1781                         if (ssize > 0 && stext) {
  1782                             status = cryptotech[crypto].verify_text(session, stext,
  1783                                                                     ssize, dsig_text, dsig_size,
  1784                                                                     &_verify_keylist);
  1785 
  1786                             if (status == PEP_VERIFIED || status == PEP_VERIFIED_AND_TRUSTED)
  1787                                 decrypt_status = PEP_DECRYPTED_AND_VERIFIED;
  1788                             
  1789                                 status = combine_keylists(session, &_verify_keylist, &_keylist, src->from);
  1790                         }
  1791                     }
  1792                 }
  1793                 break;
  1794 
  1795             case PEP_enc_pieces:
  1796                 msg = clone_to_empty_message(src);
  1797                 if (msg == NULL)
  1798                     goto enomem;
  1799 
  1800                 msg->longmsg = ptext;
  1801                 ptext = NULL;
  1802 
  1803                 bloblist_t *_m = msg->attachments;
  1804                 if (_m == NULL && src->attachments && src->attachments->value) {
  1805                     msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
  1806                     _m = msg->attachments;
  1807                 }
  1808 
  1809                 bloblist_t *_s;
  1810                 for (_s = src->attachments; _s; _s = _s->next) {
  1811                     if (_s->value == NULL && _s->size == 0){
  1812                         _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
  1813                         if (_m == NULL)
  1814                             goto enomem;
  1815 
  1816                     }
  1817                     else if (is_encrypted_attachment(_s)) {
  1818                         stringlist_t *_keylist = NULL;
  1819                         char *attctext  = _s->value;
  1820                         size_t attcsize = _s->size;
  1821 
  1822                         free(ptext);
  1823                         ptext = NULL;
  1824 
  1825                         // FIXME: What about attachments with separate sigs???
  1826                         status = decrypt_and_verify(session, attctext, attcsize,
  1827                                                     NULL, 0,
  1828                                                     &ptext, &psize, &_keylist);
  1829                         free_stringlist(_keylist); // FIXME: Why do we do this?
  1830 
  1831                         if (ptext) {
  1832                             if (is_encrypted_html_attachment(_s)) {
  1833                                 msg->longmsg_formatted = ptext;
  1834                                 ptext = NULL;
  1835                             }
  1836                             else {
  1837                                 static const char * const mime_type = "application/octet-stream";
  1838                                 char * const filename =
  1839                                     without_double_ending(_s->filename);
  1840                                 if (filename == NULL)
  1841                                     goto enomem;
  1842 
  1843                                 _m = bloblist_add(_m, ptext, psize, mime_type,
  1844                                     filename);
  1845                                 free(filename);
  1846                                 if (_m == NULL)
  1847                                     goto enomem;
  1848 
  1849                                 ptext = NULL;
  1850 
  1851                                 if (msg->attachments == NULL)
  1852                                     msg->attachments = _m;
  1853                             }
  1854                         }
  1855                         else {
  1856                             char *copy = malloc(_s->size);
  1857                             assert(copy);
  1858                             if (copy == NULL)
  1859                                 goto enomem;
  1860                             memcpy(copy, _s->value, _s->size);
  1861                             _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1862                             if (_m == NULL)
  1863                                 goto enomem;
  1864                         }
  1865                     }
  1866                     else {
  1867                         char *copy = malloc(_s->size);
  1868                         assert(copy);
  1869                         if (copy == NULL)
  1870                             goto enomem;
  1871                         memcpy(copy, _s->value, _s->size);
  1872                         _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
  1873                         if (_m == NULL)
  1874                             goto enomem;
  1875                     }
  1876                 }
  1877 
  1878                 break;
  1879 
  1880             default:
  1881                 // BUG: must implement more
  1882                 NOT_IMPLEMENTED
  1883         }
  1884 
  1885         switch (src->enc_format) {
  1886             case PEP_enc_PGP_MIME:
  1887             case PEP_enc_pieces:
  1888             case PEP_enc_PGP_MIME_Outlook1:
  1889                 status = copy_fields(msg, src);
  1890                 if (status != PEP_STATUS_OK)
  1891                     goto pep_error;
  1892 
  1893                 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
  1894                 {
  1895                     char * shortmsg;
  1896                     char * longmsg;
  1897 
  1898                     int r = separate_short_and_long(msg->longmsg, &shortmsg,
  1899                             &longmsg);
  1900                     if (r == -1)
  1901                         goto enomem;
  1902 
  1903                     free(msg->shortmsg);
  1904                     free(msg->longmsg);
  1905 
  1906                     msg->shortmsg = shortmsg;
  1907                     msg->longmsg = longmsg;
  1908                 }
  1909                 else {
  1910                     msg->shortmsg = strdup(src->shortmsg);
  1911                     assert(msg->shortmsg);
  1912                     if (msg->shortmsg == NULL)
  1913                         goto enomem;
  1914                 }
  1915                 break;
  1916             default:
  1917                     // BUG: must implement more
  1918                     NOT_IMPLEMENTED
  1919         }
  1920 
  1921         // check for private key in decrypted message attachement while inporting
  1922         identity_list *_private_il = NULL;
  1923         imported_keys = import_attached_keys(session, msg, &_private_il);
  1924         if (_private_il &&
  1925             identity_list_length(_private_il) == 1 &&
  1926             _private_il->ident->address)
  1927         {
  1928             imported_private_key_address = true;
  1929         }
  1930 
  1931         if(private_il && imported_private_key_address){
  1932             *private_il = _private_il;
  1933         }else{
  1934             free_identity_list(_private_il);
  1935         }
  1936 
  1937         if(decrypt_status == PEP_DECRYPTED){
  1938 
  1939             // TODO optimize if import_attached_keys didn't import any key
  1940 
  1941             // In case message did decrypt, but no valid signature could be found
  1942             // then retry decrypt+verify after importing key.
  1943 
  1944             // Update msg->from in case we just imported a key
  1945             // we would need to check signature
  1946 
  1947             status = _update_identity_for_incoming_message(session, src);
  1948             if(status != PEP_STATUS_OK)
  1949                 goto pep_error;
  1950 
  1951             char *re_ptext = NULL;
  1952             size_t re_psize;
  1953 
  1954             free_stringlist(_keylist);
  1955             _keylist = NULL;
  1956 
  1957             status = cryptotech[crypto].decrypt_and_verify(session, ctext,
  1958                 csize, dsig_text, dsig_size, &re_ptext, &re_psize, &_keylist);
  1959 
  1960             free(re_ptext);
  1961 
  1962             if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
  1963                 goto pep_error;
  1964 
  1965             decrypt_status = status;
  1966         }
  1967 
  1968         *rating = decrypt_rating(decrypt_status);
  1969 
  1970         if (*rating > PEP_rating_mistrust) {
  1971             PEP_rating kl_rating = PEP_rating_undefined;
  1972 
  1973             if (_keylist)
  1974                 kl_rating = keylist_rating(session, _keylist);
  1975 
  1976             if (kl_rating <= PEP_rating_mistrust) {
  1977                 *rating = kl_rating;
  1978             }
  1979             else if (*rating >= PEP_rating_reliable &&
  1980                      kl_rating < PEP_rating_reliable) {
  1981                 *rating = PEP_rating_unreliable;
  1982             }
  1983             else if (*rating >= PEP_rating_reliable &&
  1984                      kl_rating >= PEP_rating_reliable) {
  1985                 if (!(src->from && src->from->user_id && src->from->user_id[0])) {
  1986                     *rating = PEP_rating_unreliable;
  1987                 }
  1988                 else {
  1989                     char *fpr = _keylist->value;
  1990                     pEp_identity *_from = new_identity(src->from->address, fpr,
  1991                                                        src->from->user_id, src->from->username);
  1992                     if (_from == NULL)
  1993                         goto enomem;
  1994                     status = get_trust(session, _from);
  1995                     if (_from->comm_type != PEP_ct_unknown) {
  1996                         *rating = worst_rating(_rating(_from->comm_type, PEP_rating_undefined),
  1997                                   kl_rating);
  1998                     }
  1999                     free_identity(_from);
  2000                     if (status == PEP_CANNOT_FIND_IDENTITY)
  2001                        status = PEP_STATUS_OK;
  2002                     if (status != PEP_STATUS_OK)
  2003                         goto pep_error;
  2004                 }
  2005             }
  2006         }
  2007     }
  2008     else
  2009     {
  2010         *rating = decrypt_rating(decrypt_status);
  2011         goto pep_error;
  2012     }
  2013 
  2014     // Case of own key imported from own trusted message
  2015     if (// Message have been reliably decrypted
  2016         msg &&
  2017         *rating >= PEP_rating_trusted &&
  2018         imported_private_key_address &&
  2019         // to is [own]
  2020         msg->to->ident->user_id &&
  2021         strcmp(msg->to->ident->user_id, PEP_OWN_USERID) == 0
  2022         )
  2023     {
  2024         *flags |= PEP_decrypt_flag_own_private_key;
  2025     }
  2026 
  2027     if (msg) {
  2028         decorate_message(msg, *rating, _keylist);
  2029         if (imported_keys)
  2030             remove_attached_keys(msg);
  2031         if (*rating >= PEP_rating_reliable &&
  2032             session->sync_session->inject_sync_msg) {
  2033             status = receive_DeviceState_msg(session, msg, *rating, _keylist);
  2034             if (status == PEP_MESSAGE_CONSUME ||
  2035                 status == PEP_MESSAGE_IGNORE) {
  2036                 free_message(msg);
  2037                 msg = NULL;
  2038                 *flags |= (status == PEP_MESSAGE_IGNORE) ?
  2039                             PEP_decrypt_flag_ignore :
  2040                             PEP_decrypt_flag_consume;
  2041 
  2042                 status = decrypt_status;
  2043             }
  2044             else if (status != PEP_STATUS_OK){
  2045                 goto pep_error;
  2046             }
  2047         }
  2048     }
  2049     if (msg) {
  2050         if (src->id) {
  2051             msg->id = strdup(src->id);
  2052             assert(msg->id);
  2053             if (msg->id == NULL)
  2054                 goto enomem;
  2055         }
  2056     }
  2057 
  2058     *dst = msg;
  2059     *keylist = _keylist;
  2060 
  2061     return status;
  2062 
  2063 enomem:
  2064     status = PEP_OUT_OF_MEMORY;
  2065 
  2066 pep_error:
  2067     free(ptext);
  2068     free_message(msg);
  2069     free_stringlist(_keylist);
  2070 
  2071     return status;
  2072 }
  2073 
  2074 DYNAMIC_API PEP_STATUS decrypt_message(
  2075         PEP_SESSION session,
  2076         message *src,
  2077         message **dst,
  2078         stringlist_t **keylist,
  2079         PEP_rating *rating,
  2080         PEP_decrypt_flags_t *flags
  2081     )
  2082 {
  2083     return _decrypt_message( session, src, dst, keylist, rating, flags, NULL );
  2084 }
  2085 
  2086 DYNAMIC_API PEP_STATUS own_message_private_key_details(
  2087         PEP_SESSION session,
  2088         message *msg,
  2089         pEp_identity **ident
  2090     )
  2091 {
  2092     assert(session);
  2093     assert(msg);
  2094     assert(ident);
  2095 
  2096     if (!(session && msg && ident))
  2097         return PEP_ILLEGAL_VALUE;
  2098 
  2099     message *dst = NULL;
  2100     stringlist_t *keylist = NULL;
  2101     PEP_rating rating;
  2102     PEP_decrypt_flags_t flags;
  2103 
  2104     *ident = NULL;
  2105 
  2106     identity_list *private_il = NULL;
  2107     PEP_STATUS status = _decrypt_message(session, msg,  &dst, &keylist, &rating, &flags, &private_il);
  2108     free_message(dst);
  2109     free_stringlist(keylist);
  2110 
  2111     if (status == PEP_STATUS_OK &&
  2112         flags & PEP_decrypt_flag_own_private_key &&
  2113         private_il)
  2114     {
  2115         *ident = identity_dup(private_il->ident);
  2116     }
  2117 
  2118     free_identity_list(private_il);
  2119 
  2120     return status;
  2121 
  2122 }
  2123 
  2124 static void _max_comm_type_from_identity_list(
  2125         identity_list *identities,
  2126         PEP_SESSION session,
  2127         PEP_comm_type *max_comm_type,
  2128         bool *comm_type_determined
  2129     )
  2130 {
  2131     identity_list * il;
  2132     for (il = identities; il != NULL; il = il->next)
  2133     {
  2134         if (il->ident)
  2135         {
  2136             PEP_STATUS status = update_identity(session, il->ident);
  2137             if (status == PEP_STATUS_OK)
  2138             {
  2139                 *max_comm_type = _get_comm_type(session, *max_comm_type,
  2140                         il->ident);
  2141                 *comm_type_determined = true;
  2142             }
  2143         }
  2144     }
  2145 }
  2146 
  2147 DYNAMIC_API PEP_STATUS outgoing_message_rating(
  2148         PEP_SESSION session,
  2149         message *msg,
  2150         PEP_rating *rating
  2151     )
  2152 {
  2153     PEP_comm_type max_comm_type = PEP_ct_pEp;
  2154     bool comm_type_determined = false;
  2155 
  2156     assert(session);
  2157     assert(msg);
  2158     assert(msg->dir == PEP_dir_outgoing);
  2159     assert(rating);
  2160 
  2161     if (!(session && msg && rating))
  2162         return PEP_ILLEGAL_VALUE;
  2163 
  2164     if (msg->dir != PEP_dir_outgoing)
  2165         return PEP_ILLEGAL_VALUE;
  2166 
  2167     *rating = PEP_rating_undefined;
  2168 
  2169     _max_comm_type_from_identity_list(msg->to, session,
  2170                                       &max_comm_type, &comm_type_determined);
  2171 
  2172     _max_comm_type_from_identity_list(msg->cc, session,
  2173                                       &max_comm_type, &comm_type_determined);
  2174 
  2175     _max_comm_type_from_identity_list(msg->bcc, session,
  2176                                       &max_comm_type, &comm_type_determined);
  2177 
  2178     if (comm_type_determined == false)
  2179         *rating = PEP_rating_undefined;
  2180     else
  2181         *rating = _MAX(_rating(max_comm_type, PEP_rating_undefined),
  2182                 PEP_rating_unencrypted);
  2183 
  2184     return PEP_STATUS_OK;
  2185 }
  2186 
  2187 DYNAMIC_API PEP_STATUS identity_rating(
  2188         PEP_SESSION session,
  2189         pEp_identity *ident,
  2190         PEP_rating *rating
  2191     )
  2192 {
  2193     PEP_STATUS status = PEP_STATUS_OK;
  2194 
  2195     assert(session);
  2196     assert(ident);
  2197     assert(rating);
  2198 
  2199     if (!(session && ident && rating))
  2200         return PEP_ILLEGAL_VALUE;
  2201 
  2202     if (ident->me)
  2203         status = _myself(session, ident, false, true);
  2204     else
  2205         status = update_identity(session, ident);
  2206 
  2207     if (status == PEP_STATUS_OK)
  2208         *rating = _rating(ident->comm_type, PEP_rating_undefined);
  2209 
  2210     return status;
  2211 }
  2212 
  2213 DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
  2214 {
  2215     PEP_STATUS status = PEP_STATUS_OK;
  2216 
  2217     assert(path);
  2218     if (path == NULL)
  2219         return PEP_ILLEGAL_VALUE;
  2220 
  2221     if (cryptotech[tech].binary_path == NULL)
  2222         *path = NULL;
  2223     else
  2224         status = cryptotech[tech].binary_path(path);
  2225 
  2226     return status;
  2227 }
  2228 
  2229 
  2230 DYNAMIC_API PEP_color color_from_rating(PEP_rating rating)
  2231 {
  2232     if (rating == PEP_rating_b0rken || rating == PEP_rating_have_no_key)
  2233         return PEP_color_no_color;
  2234 
  2235     if (rating < PEP_rating_undefined)
  2236         return PEP_color_red;
  2237 
  2238     if (rating < PEP_rating_reliable)
  2239         return PEP_color_no_color;
  2240 
  2241     if (rating < PEP_rating_trusted)
  2242         return PEP_color_yellow;
  2243 
  2244     if (rating >= PEP_rating_trusted)
  2245         return PEP_color_green;
  2246 
  2247     // this should never happen
  2248     assert(false);
  2249 	return PEP_color_no_color;
  2250 }
  2251 
  2252 static bool _is_valid_hex(const char* hexstr) {
  2253     if (!hexstr)
  2254         return false;
  2255 
  2256     const char* curr = hexstr;
  2257     char currchar;
  2258 
  2259     for (currchar = *curr; currchar != '\0'; currchar = *(++curr)) {
  2260         if ((currchar >= '0' && currchar <= '9') ||
  2261             (currchar >= 'a' && currchar <= 'f') ||
  2262             (currchar >= 'A' && currchar <= 'F'))
  2263         {
  2264             continue;
  2265         }
  2266         return false;
  2267     }
  2268     return true;
  2269 }
  2270 
  2271 // Returns, in comparison: 1 if fpr1 > fpr2, 0 if equal, -1 if fpr1 < fpr2
  2272 static PEP_STATUS _compare_fprs(const char* fpr1, const char* fpr2, int* comparison) {
  2273 
  2274     const int _FULL_FINGERPRINT_LENGTH = 40;
  2275     const int _ASCII_LOWERCASE_OFFSET = 32;
  2276 
  2277     size_t fpr1_len = strlen(fpr1);
  2278     size_t fpr2_len = strlen(fpr2);
  2279 
  2280     if (fpr1_len != _FULL_FINGERPRINT_LENGTH || fpr2_len != _FULL_FINGERPRINT_LENGTH)
  2281         return PEP_TRUSTWORDS_FPR_WRONG_LENGTH;
  2282 
  2283     if (!_is_valid_hex(fpr1) || !_is_valid_hex(fpr2))
  2284         return PEP_ILLEGAL_VALUE;
  2285 
  2286     const char* fpr1_curr = fpr1;
  2287     const char* fpr2_curr = fpr2;
  2288 
  2289     char current;
  2290 
  2291     // Advance past leading zeros.
  2292     for (current = *fpr1_curr; current != '0' && current != '\0'; current = *(++fpr1_curr), fpr1_len--);
  2293     for (current = *fpr2_curr; current != '0' && current != '\0'; current = *(++fpr2_curr), fpr2_len--);
  2294 
  2295     if (fpr1_len == fpr2_len) {
  2296         char digit1;
  2297         char digit2;
  2298 
  2299         while (fpr1_curr && *fpr1_curr != '\0') {
  2300             digit1 = *fpr1_curr++;
  2301             digit2 = *fpr2_curr++;
  2302 
  2303             // Adjust for case-insensitive compare
  2304             if (digit1 >= 'a' && digit1 <= 'f')
  2305                 digit1 -= _ASCII_LOWERCASE_OFFSET;
  2306             if (digit2 >= 'a' && digit2 <= 'f')
  2307                 digit2 -= _ASCII_LOWERCASE_OFFSET;
  2308 
  2309             // We take advantage of the fact that 'a'-'f' are larger
  2310             // integer values in the ASCII table than '0'-'9'.
  2311             // This allows us to compare digits directly.
  2312             if (digit1 > digit2) {
  2313                 *comparison = 1;
  2314                 return PEP_STATUS_OK;
  2315             } else if (digit1 < digit2) {
  2316                 *comparison = -1;
  2317                 return PEP_STATUS_OK;
  2318             }
  2319 
  2320             // pointers already advanced above. Keep going.
  2321         }
  2322         *comparison = 0;
  2323         return PEP_STATUS_OK;
  2324     }
  2325     else if (fpr1_len > fpr2_len) {
  2326         *comparison = 1;
  2327         return PEP_STATUS_OK;
  2328     }
  2329     // Otherwise, fpr1_len < fpr2_len
  2330     *comparison = -1;
  2331     return PEP_STATUS_OK;
  2332 }
  2333 
  2334 DYNAMIC_API PEP_STATUS get_trustwords(
  2335     PEP_SESSION session, const pEp_identity* id1, const pEp_identity* id2,
  2336     const char* lang, char **words, size_t *wsize, bool full
  2337 )
  2338 {
  2339     assert(session);
  2340     assert(id1);
  2341     assert(id2);
  2342     assert(id1->fpr);
  2343     assert(id2->fpr);
  2344     assert(words);
  2345     assert(wsize);
  2346 
  2347     if (!(session && id1 && id2 && words && wsize) ||
  2348         !(id1->fpr) || (!id2->fpr))
  2349         return PEP_ILLEGAL_VALUE;
  2350 
  2351     const char *source1 = id1->fpr;
  2352     const char *source2 = id2->fpr;
  2353 
  2354     *words = NULL;
  2355     *wsize = 0;
  2356 
  2357     const size_t SHORT_NUM_TWORDS = 5;
  2358 
  2359     // N.B. THIS will have to be changed once we start checking trustword entropy.
  2360     // For now, full is ALL, and otherwise it's 5-per-id.
  2361     size_t max_words_per_id = (full ? 0 : SHORT_NUM_TWORDS);
  2362 
  2363     char* first_set = NULL;
  2364     char* second_set = NULL;
  2365     size_t first_wsize = 0;
  2366     size_t second_wsize = 0;
  2367 
  2368     int fpr_comparison = -255;
  2369     PEP_STATUS status = _compare_fprs(source1, source2, &fpr_comparison);
  2370     if (status != PEP_STATUS_OK)
  2371         return status;
  2372 
  2373     char* _retstr = NULL;
  2374 
  2375     switch (fpr_comparison) {
  2376         case 1: // source1 > source2
  2377             status = trustwords(session, source2, lang, &first_set, &first_wsize, max_words_per_id);
  2378             if (status != PEP_STATUS_OK)
  2379                 goto error_release;
  2380             status = trustwords(session, source1, lang, &second_set, &second_wsize, max_words_per_id);
  2381             if (status != PEP_STATUS_OK)
  2382                 goto error_release;
  2383             break;
  2384         case 0:
  2385         case -1: // source1 <= source2
  2386             status = trustwords(session, source1, lang, &first_set, &first_wsize, max_words_per_id);
  2387             if (status != PEP_STATUS_OK)
  2388                 goto error_release;
  2389             status = trustwords(session, source2, lang, &second_set, &second_wsize, max_words_per_id);
  2390             if (status != PEP_STATUS_OK)
  2391                 goto error_release;
  2392             break;
  2393         default:
  2394             return PEP_UNKNOWN_ERROR; // shouldn't be possible
  2395     }
  2396 
  2397     size_t _wsize = first_wsize + second_wsize;
  2398 
  2399     bool needs_space = (first_set[first_wsize - 1] != ' ');
  2400 
  2401     if (needs_space)
  2402         _wsize++;
  2403 
  2404     _retstr = calloc(1, _wsize + 1);
  2405 
  2406     size_t len = strlcpy(_retstr, first_set, _wsize);
  2407     if (len >= _wsize) {
  2408         status = PEP_UNKNOWN_ERROR;
  2409         goto error_release;
  2410     }
  2411     if (needs_space) {
  2412         strlcat(_retstr, " ", _wsize);
  2413         if (len >= _wsize) {
  2414             status = PEP_UNKNOWN_ERROR;
  2415             goto error_release;
  2416         }
  2417     }
  2418     strlcat(_retstr, second_set, _wsize);
  2419     if (len >= _wsize){
  2420         status = PEP_UNKNOWN_ERROR;
  2421         goto error_release;
  2422     }
  2423 
  2424     *words = _retstr;
  2425     *wsize = _wsize;
  2426     status = PEP_STATUS_OK;
  2427 
  2428     goto the_end;
  2429 
  2430     error_release:
  2431     free(_retstr);
  2432 
  2433     the_end:
  2434     free(first_set);
  2435     free(second_set);
  2436     return status;
  2437 }
  2438 
  2439 DYNAMIC_API PEP_STATUS get_message_trustwords(
  2440     PEP_SESSION session, 
  2441     message *msg,
  2442     stringlist_t *keylist,
  2443     pEp_identity* received_by,
  2444     const char* lang, char **words, bool full
  2445 )
  2446 {
  2447     assert(session);
  2448     assert(msg);
  2449     assert(received_by);
  2450     assert(received_by->address);
  2451     assert(lang);
  2452     assert(words);
  2453 
  2454     if (!(session && 
  2455           msg &&
  2456           received_by && 
  2457           received_by->address && 
  2458           lang && 
  2459           words))
  2460         return PEP_ILLEGAL_VALUE;
  2461     
  2462     pEp_identity* partner = NULL;
  2463      
  2464     PEP_STATUS status = PEP_STATUS_OK;
  2465     
  2466     *words = NULL;
  2467 
  2468     // We want fingerprint of key that did sign the message
  2469 
  2470     if (keylist == NULL) {
  2471 
  2472         // Message is to be decrypted
  2473         message *dst = NULL;
  2474         stringlist_t *_keylist = keylist;
  2475         PEP_rating rating;
  2476         PEP_decrypt_flags_t flags;
  2477         status = decrypt_message( session, msg, &dst, &_keylist, &rating, &flags);
  2478 
  2479         if (status != PEP_STATUS_OK) {
  2480             free_message(dst);
  2481             free_stringlist(_keylist);
  2482             return status;
  2483         }
  2484 
  2485         if (dst && dst->from && _keylist) {
  2486             partner = identity_dup(dst->from); 
  2487             if(partner){
  2488                 free(partner->fpr);
  2489                 partner->fpr = strdup(_keylist->value);
  2490                 if (partner->fpr == NULL)
  2491                     status = PEP_OUT_OF_MEMORY;
  2492             } else {
  2493                 status = PEP_OUT_OF_MEMORY;
  2494             }
  2495         } else {
  2496             status = PEP_UNKNOWN_ERROR;
  2497         }
  2498 
  2499         free_message(dst);
  2500         free_stringlist(_keylist);
  2501 
  2502     } else {
  2503 
  2504         // Message already decrypted
  2505         if (keylist->value) {
  2506             partner = identity_dup(msg->from); 
  2507             if(partner){
  2508                 free(partner->fpr);
  2509                 partner->fpr = strdup(keylist->value);
  2510                 if (partner->fpr == NULL)
  2511                     status = PEP_OUT_OF_MEMORY;
  2512             } else {
  2513                 status = PEP_OUT_OF_MEMORY;
  2514             }
  2515         } else {
  2516             status = PEP_ILLEGAL_VALUE;
  2517         }
  2518     }
  2519 
  2520     if (status != PEP_STATUS_OK) {
  2521         free_identity(partner);
  2522         return status;
  2523     }
  2524    
  2525     // Find own identity corresponding to given account address.
  2526     // In that case we want default key attached to own identity
  2527     pEp_identity *stored_identity = NULL;
  2528     status = get_identity(session,
  2529                           received_by->address,
  2530                           PEP_OWN_USERID,
  2531                           &stored_identity);
  2532 
  2533     if (status != PEP_STATUS_OK) {
  2534         free_identity(stored_identity);
  2535         return status;
  2536     }
  2537 
  2538     // get the trustwords
  2539     size_t wsize;
  2540     status = get_trustwords(session, 
  2541                             partner, received_by, 
  2542                             lang, words, &wsize, full);
  2543 
  2544     return status;
  2545 }
  2546 
  2547 DYNAMIC_API PEP_STATUS MIME_decrypt_message(
  2548     PEP_SESSION session,
  2549     const char *mimetext,
  2550     size_t size,
  2551     char** mime_plaintext,
  2552     stringlist_t **keylist,
  2553     PEP_rating *rating,
  2554     PEP_decrypt_flags_t *flags
  2555 )
  2556 {
  2557     assert(mimetext);
  2558     assert(mime_plaintext);
  2559     assert(keylist);
  2560     assert(rating);
  2561     assert(flags);
  2562 
  2563     PEP_STATUS status = PEP_STATUS_OK;
  2564     message* tmp_msg = NULL;
  2565     message* dec_msg = NULL;
  2566 
  2567     status = mime_decode_message(mimetext, size, &tmp_msg);
  2568     if (status != PEP_STATUS_OK)
  2569         goto pep_error;
  2570 
  2571     PEP_STATUS decrypt_status = decrypt_message(session,
  2572                                                 tmp_msg,
  2573                                                 &dec_msg,
  2574                                                 keylist,
  2575                                                 rating,
  2576                                                 flags);
  2577                                                 
  2578     if (!dec_msg && (decrypt_status == PEP_UNENCRYPTED || decrypt_status == PEP_VERIFIED)) {
  2579         dec_msg = message_dup(tmp_msg);
  2580     }
  2581         
  2582     if (decrypt_status > PEP_CANNOT_DECRYPT_UNKNOWN)
  2583     {
  2584         status = decrypt_status;
  2585         goto pep_error;
  2586     }
  2587 
  2588     assert(dec_msg);
  2589     
  2590     if (!dec_msg) {
  2591         status = PEP_UNKNOWN_ERROR;
  2592         goto pep_error;
  2593     }
  2594 
  2595     status = mime_encode_message(dec_msg, false, mime_plaintext);
  2596 
  2597     if (status == PEP_STATUS_OK)
  2598     {
  2599         free(tmp_msg);
  2600         free(dec_msg);
  2601         return decrypt_status;
  2602     }
  2603     
  2604 pep_error:
  2605     free_message(tmp_msg);
  2606     free_message(dec_msg);
  2607 
  2608     return status;
  2609 }
  2610 
  2611 
  2612 DYNAMIC_API PEP_STATUS MIME_encrypt_message(
  2613     PEP_SESSION session,
  2614     const char *mimetext,
  2615     size_t size,
  2616     stringlist_t* extra,
  2617     char** mime_ciphertext,
  2618     PEP_enc_format enc_format,
  2619     PEP_encrypt_flags_t flags
  2620 )
  2621 {
  2622     PEP_STATUS status = PEP_STATUS_OK;
  2623     message* tmp_msg = NULL;
  2624     message* enc_msg = NULL;
  2625 
  2626     status = mime_decode_message(mimetext, size, &tmp_msg);
  2627     if (status != PEP_STATUS_OK)
  2628         goto pep_error;
  2629 
  2630     // This isn't incoming, though... so we need to reverse the direction
  2631     tmp_msg->dir = PEP_dir_outgoing;
  2632     status = encrypt_message(session,
  2633                              tmp_msg,
  2634                              extra,
  2635                              &enc_msg,
  2636                              enc_format,
  2637                              flags);
  2638     if (status != PEP_STATUS_OK)
  2639         goto pep_error;
  2640 
  2641 
  2642     if (!enc_msg) {
  2643         status = PEP_UNKNOWN_ERROR;
  2644         goto pep_error;
  2645     }
  2646 
  2647     status = mime_encode_message(enc_msg, false, mime_ciphertext);
  2648 
  2649 pep_error:
  2650     free_message(tmp_msg);
  2651     free_message(enc_msg);
  2652 
  2653     return status;
  2654 
  2655 }
  2656 
  2657 DYNAMIC_API PEP_STATUS MIME_encrypt_message_for_self(
  2658     PEP_SESSION session,
  2659     pEp_identity* target_id,
  2660     const char *mimetext,
  2661     size_t size,
  2662     char** mime_ciphertext,
  2663     PEP_enc_format enc_format,
  2664     PEP_encrypt_flags_t flags
  2665 )
  2666 {
  2667     PEP_STATUS status = PEP_STATUS_OK;
  2668     message* tmp_msg = NULL;
  2669     message* enc_msg = NULL;
  2670 
  2671     status = mime_decode_message(mimetext, size, &tmp_msg);
  2672     if (status != PEP_STATUS_OK)
  2673         goto pep_error;
  2674 
  2675     // This isn't incoming, though... so we need to reverse the direction
  2676     tmp_msg->dir = PEP_dir_outgoing;
  2677     status = encrypt_message_for_self(session,
  2678                                       target_id,
  2679                                       tmp_msg,
  2680                                       &enc_msg,
  2681                                       enc_format,
  2682                                       flags);
  2683     if (status != PEP_STATUS_OK)
  2684         goto pep_error;
  2685  
  2686     if (!enc_msg) {
  2687         status = PEP_UNKNOWN_ERROR;
  2688         goto pep_error;
  2689     }
  2690 
  2691     status = mime_encode_message(enc_msg, false, mime_ciphertext);
  2692 
  2693 pep_error:
  2694     free_message(tmp_msg);
  2695     free_message(enc_msg);
  2696 
  2697     return status;
  2698 }