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