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