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