diff --git a/message_cache.cc b/message_cache.cc
new file mode 100644
index 0000000..34187b4
--- /dev/null
+++ b/message_cache.cc
@@ -0,0 +1,246 @@
+#include "message_cache.hh"
+
+pEp::MessageCache message_cache;
+
+namespace pEp {
+ DYNAMIC_API PEP_STATUS MessageCache::cache_decrypt_message(
+ PEP_SESSION session,
+ message *src,
+ message **dst,
+ stringlist_t **keylist,
+ PEP_rating *rating,
+ PEP_decrypt_flags_t *flags
+ )
+ {
+ return message_cache.decrypt_message(session, src, dst, keylist,
+ rating, flags);
+ }
+
+ DYNAMIC_API PEP_STATUS MessageCache::cached_mime_encode_message(
+ which one,
+ const message * msg,
+ bool omit_fields,
+ char **mimetext,
+ bool has_pEp_msg_attachment
+ )
+ {
+ return message_cache.mime_encode_message(one, msg, omit_fields,
+ mimetext, has_pEp_msg_attachment);
+ }
+
+ static char *dup(const char *src)
+ {
+ if (!src)
+ return nullptr;
+
+ char * dst = ::strdup(src);
+ assert(dst);
+ if (!dst)
+ throw std::bad_alloc();
+
+ return dst;
+ }
+
+ static timestamp *dup(const ::timestamp *src)
+ {
+ if (!src)
+ return nullptr;
+
+ ::timestamp * dst = ::timestamp_dup(src);
+ if (!dst)
+ throw std::bad_alloc();
+
+ return dst;
+ }
+
+ static ::pEp_identity *dup(const ::pEp_identity *src)
+ {
+ if (!src)
+ return nullptr;
+
+ ::pEp_identity * dst = ::identity_dup(src);
+ if (!dst)
+ throw std::bad_alloc();
+
+ return dst;
+ }
+
+ static identity_list *dup(const ::identity_list *src)
+ {
+ if (!src)
+ return nullptr;
+
+ ::identity_list * dst = ::identity_list_dup(src);
+ if (!dst)
+ throw std::bad_alloc();
+
+ return dst;
+ }
+
+ static stringlist_t *dup(const ::stringlist_t *src)
+ {
+ if (!src)
+ return nullptr;
+
+ ::stringlist_t * dst = ::stringlist_dup(src);
+ if (!dst)
+ throw std::bad_alloc();
+
+ return dst;
+ }
+
+ static stringpair_list_t *dup(const ::stringpair_list_t *src)
+ {
+ if (!src)
+ return nullptr;
+
+ ::stringpair_list_t * dst = ::stringpair_list_dup(src);
+ if (!dst)
+ throw std::bad_alloc();
+
+ return dst;
+ }
+
+ static ::message_ref_list *dup(const ::message_ref_list *src) {
+ if (!src)
+ return nullptr;
+
+ ::message_ref_list *dst = (::message_ref_list *) ::calloc(1,
+ sizeof(::message_ref_list));
+ assert(dst);
+ if (!dst)
+ throw std::bad_alloc();
+
+ ::message_ref_list *d = dst;
+ for (const message_ref_list *s = src; s; s = s->next) {
+ d->msg_ref = s->msg_ref;
+ if (s->next) {
+ d->next = (::message_ref_list *) ::calloc(1,
+ sizeof(::message_ref_list));
+ assert(d);
+ if (!d)
+ throw std::bad_alloc();
+ d = d->next;
+ }
+ }
+
+ return dst;
+ }
+
+ static bool emptystr(const char *str)
+ {
+ if (!(str && str[0]))
+ return true;
+ return false;
+ }
+
+ ::message *MessageCache::empty_message_copy(::message *src)
+ {
+ if (!src)
+ return nullptr;
+
+ ::message *dst = ::new_message(src->dir);
+ if (!dst)
+ throw std::bad_alloc();
+
+ dst->id = dup(src->id);
+ dst->shortmsg = dup(src->shortmsg);
+
+ if (!emptystr(src->longmsg))
+ dst->longmsg = dup("pEp");
+
+ if (!emptystr(src->longmsg_formatted))
+ dst->longmsg_formatted = dup("");
+
+ // attachments are never copied
+
+ dst->rawmsg_ref = src->rawmsg_ref;
+ dst->rawmsg_size = src->rawmsg_size;
+
+ dst->sent = dup(src->sent);
+ dst->recv = dup(src->recv);
+
+ dst->from = dup(src->from);
+ dst->to = dup(src->to);
+ dst->cc = dup(src->cc);
+ dst->bcc = dup(src->bcc);
+
+ dst->reply_to = dup(src->reply_to);
+ dst->in_reply_to = dup(src->in_reply_to);
+
+ dst->refering_msg_ref = src->refering_msg_ref;
+ dst->references = dup(src->references);
+ dst->refered_by = dup(src->refered_by);
+
+ dst->keywords = dup(src->keywords);
+ dst->comments = dup(src->comments);
+ dst->opt_fields = dup(src->opt_fields);
+ dst->enc_format = src->enc_format;
+ dst->_sender_fpr = dup(src->_sender_fpr);
+
+ return dst;
+ }
+
+ DYNAMIC_API PEP_STATUS MessageCache::decrypt_message(
+ PEP_SESSION session,
+ message *src,
+ message **dst,
+ stringlist_t **keylist,
+ PEP_rating *rating,
+ PEP_decrypt_flags_t *flags
+ )
+ {
+ if (!src || emptystr(src->id))
+ return PEP_ILLEGAL_VALUE;
+
+ PEP_STATUS status = ::decrypt_message(session, src, dst, keylist,
+ rating, flags);
+
+ ::message *_cpy = empty_message_copy(src);
+
+ ::message *_src;
+ ::memcpy(_src, src, sizeof(::message));
+ ::memcpy(src, _cpy, sizeof(::message));
+
+ ::message *_dst = *dst;
+ *dst = empty_message_copy(_dst);
+
+ message_cache._cache.emplace(std::make_pair(std::string(_src->id),
+ cache_entry(_src, _dst)));
+ return status;
+ }
+
+ DYNAMIC_API PEP_STATUS MessageCache::mime_encode_message(
+ which one,
+ const message * msg,
+ bool omit_fields,
+ char **mimetext,
+ bool has_pEp_msg_attachment
+ )
+ {
+ if (!msg || emptystr(msg->id))
+ return PEP_ILLEGAL_VALUE;
+
+ if (one != msg_src && one != msg_dst)
+ return PEP_ILLEGAL_VALUE;
+
+ ::message *_msg;
+ if (one == msg_src) {
+ _msg = _cache.at(std::string(msg->id)).src;
+ ::free_message(_cache.at(msg->id).dst);
+ }
+ else /* msg_dst */ {
+ _msg = _cache.at(std::string(msg->id)).dst;
+ ::free_message(_cache.at(msg->id).src);
+ }
+
+ PEP_STATUS status = ::mime_encode_message(_msg, omit_fields, mimetext,
+ has_pEp_msg_attachment);
+
+ ::free_message(_msg);
+ _cache.erase(msg->id);
+
+ return status;
+ }
+};
+
diff --git a/message_cache.hh b/message_cache.hh
new file mode 100644
index 0000000..0502d60
--- /dev/null
+++ b/message_cache.hh
@@ -0,0 +1,73 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace pEp {
+ class MessageCache {
+ struct cache_entry {
+ cache_entry(::message *s, ::message *d)
+ : src(s), dst(d) { }
+ ~cache_entry() {
+ ::free_message(src);
+ ::free_message(dst);
+ };
+
+ ::message *src;
+ ::message *dst;
+ };
+
+ using cache = std::unordered_map;
+
+ cache _cache;
+ std::mutex _mtx;
+
+ public:
+ static
+ DYNAMIC_API PEP_STATUS cache_decrypt_message(
+ PEP_SESSION session,
+ message *src,
+ message **dst,
+ stringlist_t **keylist,
+ PEP_rating *rating,
+ PEP_decrypt_flags_t *flags
+ );
+
+ enum which { msg_src, msg_dst };;
+
+ static
+ DYNAMIC_API PEP_STATUS cached_mime_encode_message(
+ which one,
+ const message * msg,
+ bool omit_fields,
+ char **mimetext,
+ bool has_pEp_msg_attachment
+ );
+
+ protected:
+ DYNAMIC_API PEP_STATUS decrypt_message(
+ PEP_SESSION session,
+ message *src,
+ message **dst,
+ stringlist_t **keylist,
+ PEP_rating *rating,
+ PEP_decrypt_flags_t *flags
+ );
+
+ DYNAMIC_API PEP_STATUS mime_encode_message(
+ which one,
+ const message * src,
+ bool omit_fields,
+ char **mimetext,
+ bool has_pEp_msg_attachment
+ );
+
+ static ::message *empty_message_copy(::message *src);
+ };
+
+ extern MessageCache message_cache;
+};
+