CpEpEngine.cpp
author Thomas
Thu, 21 Nov 2019 17:27:18 +0100
branchsync
changeset 376 c29ae3e408a8
parent 375 fe31c63ea77d
child 377 f3db78b86204
permissions -rw-r--r--
Shut down sync before key reset and start up again afterwards
     1 // CpEpEngine.cpp : Implementation of CpEpEngine
     2 
     3 #include "stdafx.h"
     4 #include "CpEpEngine.h"
     5 #include <mutex>
     6 #include "GateKeeper.h"
     7 #include "..\libpEpAdapter\Adapter.hh"
     8 #include "pEp\status_to_string.h"
     9 
    10 using namespace std;
    11 using namespace pEp::utility;
    12 using namespace pEp::Adapter;
    13 
    14 // CpEpEngine
    15 
    16 CpEpEngine::callback_container CpEpEngine::sync_callbacks;
    17 
    18 // the init_mutex protects our initialization and destruction
    19 // against a running keysync thread, and it ensures that the
    20 // keysync thread actually has finished before we're destructed.
    21 
    22 std::mutex CpEpEngine::init_mutex;
    23 atomic< int > CpEpEngine::count = 0;
    24 
    25 STDMETHODIMP CpEpEngine::InterfaceSupportsErrorInfo(REFIID riid)
    26 {
    27     static const IID* const arr[] =
    28     {
    29         &IID_IpEpEngine,
    30     };
    31 
    32     for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    33     {
    34         if (InlineIsEqualGUID(*arr[i], riid))
    35             return S_OK;
    36     }
    37     return S_FALSE;
    38 }
    39 
    40 // The second argument is optional, and currently supports PEP_STATUS.
    41 #define FAIL(msg, ...) error(msg, __VA_ARGS__)
    42 
    43 STDMETHODIMP CpEpEngine::VerboseLogging(VARIANT_BOOL enable)
    44 {
    45     verbose_mode = enable != VARIANT_FALSE;
    46     return S_OK;
    47 }
    48 
    49 STDMETHODIMP CpEpEngine::PassiveMode(VARIANT_BOOL enable)
    50 {
    51     ::config_passive_mode(session(), enable != VARIANT_FALSE);
    52     return S_OK;
    53 }
    54 
    55 STDMETHODIMP CpEpEngine::UnencryptedSubject(VARIANT_BOOL enable)
    56 {
    57     ::config_unencrypted_subject(session(), enable != VARIANT_FALSE);
    58     return S_OK;
    59 }
    60 
    61 STDMETHODIMP CpEpEngine::ConfigCipherSuite(pEpCipherSuite cipherSuite)
    62 {
    63     PEP_STATUS status = ::config_cipher_suite(session(), (PEP_CIPHER_SUITE)cipherSuite);
    64 
    65     if (status)
    66         return FAIL(L"config_cipher_suite", status);
    67     
    68     return S_OK;
    69 }
    70 
    71 STDMETHODIMP CpEpEngine::ImportKey(BSTR keyData, LPSAFEARRAY * privateKeys)
    72 {
    73     assert(keyData);
    74 
    75     if (!keyData)
    76         return E_INVALIDARG;
    77 
    78     string key_data = utf8_string(keyData);
    79     size_t size = SysStringLen(keyData);
    80     ::identity_list *private_keys = nullptr;
    81 
    82     PEP_STATUS status = ::import_key(session(), key_data.c_str(), size, &private_keys);
    83     assert(status != ::PEP_OUT_OF_MEMORY);
    84     if (status == ::PEP_OUT_OF_MEMORY)
    85         return E_OUTOFMEMORY;
    86 
    87     if ((status != PEP_STATUS_OK) && (status != PEP_KEY_IMPORTED))
    88         return FAIL(L"import_key", status);
    89 
    90     SAFEARRAY * _privateKeys = nullptr;
    91     try {
    92         _privateKeys = array_from_C<pEpIdentity, identity_list>(private_keys);
    93     }
    94     catch (exception& ex)
    95     {
    96         ::free_identity_list(private_keys);
    97         try {
    98             dynamic_cast<bad_alloc&>(ex);
    99         }
   100         catch (bad_cast&)
   101         {
   102             return FAIL(ex.what());
   103         }
   104         return E_OUTOFMEMORY;
   105     }
   106     free_identity_list(private_keys);
   107 
   108     *privateKeys = _privateKeys;
   109 
   110     return status;
   111 }
   112 
   113 
   114 STDMETHODIMP CpEpEngine::ExportKey(BSTR fpr, BSTR * keyData)
   115 {
   116     assert(fpr);
   117     assert(keyData);
   118 
   119     if (!(fpr && keyData))
   120         return E_INVALIDARG;
   121 
   122     string _fpr = utf8_string(fpr);
   123     char *_key_data = NULL;
   124     size_t _size = 0;
   125 
   126     PEP_STATUS status = ::export_key(session(), _fpr.c_str(), &_key_data, &_size);
   127     assert(status != ::PEP_OUT_OF_MEMORY);
   128     if (status == ::PEP_OUT_OF_MEMORY)
   129         return E_OUTOFMEMORY;
   130 
   131     if (status != PEP_STATUS_OK)
   132         return FAIL(L"export_key", status);
   133 
   134     _bstr_t b_key_data(utf16_string(_key_data).c_str());
   135     pEp_free(_key_data);
   136     *keyData = b_key_data.Detach();
   137 
   138     return S_OK;
   139 }
   140 
   141 STDMETHODIMP CpEpEngine::LeaveDeviceGroup()
   142 {
   143     PEP_STATUS status = ::leave_device_group(session());
   144 
   145     if (status == PEP_OUT_OF_MEMORY)
   146         return E_OUTOFMEMORY;
   147 
   148     if (status != PEP_STATUS_OK)
   149         return FAIL(L"cannot leave device group", status);
   150 
   151     return S_OK;
   152 }
   153 
   154 STDMETHODIMP CpEpEngine::Log(BSTR title, BSTR entity, BSTR description, BSTR comment)
   155 {
   156     string _title;
   157     string _entity;
   158     string _description;
   159     string _comment;
   160     HRESULT result = S_OK;
   161 
   162     assert(title);
   163     if (title)
   164         _title = utf8_string(title);
   165     else
   166         result = E_INVALIDARG;
   167 
   168     assert(entity);
   169     if (entity)
   170         _entity = utf8_string(entity);
   171     else
   172         result = E_INVALIDARG;
   173 
   174     if (description)
   175         _description = utf8_string(description);
   176 
   177     if (comment)
   178         _comment = utf8_string(comment);
   179 
   180     if (result != S_OK)
   181         return result;
   182 
   183     PEP_STATUS _status = ::log_event(session(), _title.c_str(), _entity.c_str(), _description.c_str(), _comment.c_str());
   184     assert(_status == PEP_STATUS_OK);
   185     if (_status != PEP_STATUS_OK)
   186         return FAIL(L"log_event", _status);
   187     else
   188         return S_OK;
   189 }
   190 
   191 STDMETHODIMP CpEpEngine::Trustwords(BSTR fpr, BSTR lang, LONG max_words, BSTR * words)
   192 {
   193     assert(fpr);
   194     assert(max_words >= 0);
   195     assert(words);
   196 
   197     HRESULT result = S_OK;
   198 
   199     string _fpr;
   200     if (fpr)
   201         _fpr = utf8_string(fpr);
   202     else
   203         result = E_INVALIDARG;
   204 
   205     string _lang;
   206     if (lang) {
   207         _lang = utf8_string(lang);
   208         if (_lang.length()) {
   209             if (_lang.length() != 2)
   210                 result = E_INVALIDARG;
   211         }
   212         else
   213             _lang = "en";
   214     }
   215     else
   216         _lang = "en";
   217 
   218     if (max_words < 0)
   219         result = E_INVALIDARG;
   220 
   221     if (words == NULL)
   222         result = E_INVALIDARG;
   223 
   224     if (result != S_OK)
   225         return result;
   226 
   227     char *_words = NULL;
   228     size_t _wsize = 0;
   229 
   230     PEP_STATUS status = ::trustwords(session(), _fpr.c_str(), _lang.c_str(), &_words, &_wsize, max_words);
   231     assert(status != PEP_OUT_OF_MEMORY);
   232     if (status == PEP_OUT_OF_MEMORY)
   233         return E_OUTOFMEMORY;
   234 
   235     if (_words == NULL) {
   236         *words = NULL;
   237         return FAIL(L"Trustwords: _words == NULL", status);
   238     }
   239     else {
   240         *words = utf16_bstr(_words);
   241         pEp_free(_words);
   242         return S_OK;
   243     }
   244 }
   245 
   246 STDMETHODIMP CpEpEngine::GetTrustwordsForFprs(BSTR fpr1, BSTR fpr2, BSTR lang, VARIANT_BOOL full, BSTR *words)
   247 {
   248     assert(fpr1);
   249     assert(fpr2);
   250     assert(words);
   251 
   252     if (!(fpr1 && fpr2 && words))
   253     {
   254         return E_INVALIDARG;
   255     }
   256 
   257     HRESULT result = S_OK;
   258     string _fpr1;
   259     string _fpr2;
   260     string _lang;
   261     *words = NULL;
   262 
   263     try {
   264 
   265         _fpr1 = utf8_string(fpr1);
   266         _fpr2 = utf8_string(fpr2);
   267         
   268         if (lang) {
   269             _lang = utf8_string(lang);
   270             if (_lang.length() == 0) {
   271                 _lang = "en";
   272             }
   273             else if (_lang.length() != 2) {
   274                 result = E_INVALIDARG;
   275             }
   276         }
   277         else {
   278             _lang = "en";
   279         }
   280     }
   281     catch (bad_alloc&) {
   282         result = E_OUTOFMEMORY;
   283     }
   284     catch (exception& ex) {
   285         result = FAIL(ex.what());
   286     }
   287 
   288     char* _words;
   289     size_t _size;
   290     if (result == S_OK) {
   291         auto status = ::get_trustwords_for_fprs(session(), _fpr1.c_str(), _fpr2.c_str(), _lang.c_str(), &_words, &_size, full != 0 /* convert variant bool to C bool */);
   292 
   293         if (status == PEP_OUT_OF_MEMORY) {
   294             result = E_OUTOFMEMORY;
   295         }
   296         else if (status == PEP_TRUSTWORD_NOT_FOUND) {
   297             result = FAIL(L"GetTrustwords: Trustword not found", status);
   298         }
   299         else if (!words) {
   300             result = FAIL(L"GetTrustwords: _words == NULL", status);
   301         }
   302         else {
   303             *words = utf16_bstr(_words);
   304             pEp_free(_words);
   305         }
   306     }
   307 
   308     return result;
   309 }
   310 
   311 STDMETHODIMP CpEpEngine::GetTrustwords(struct pEpIdentity *id1, struct pEpIdentity *id2, BSTR lang, VARIANT_BOOL full, BSTR *words)
   312 {
   313     assert(id1);
   314     assert(id2);
   315     assert(words);
   316 
   317     if (!(id1 && id2 && words))
   318     {
   319         return E_INVALIDARG;
   320     }
   321 
   322     HRESULT result = S_OK;
   323 
   324     pEp_identity* _id1 = NULL;
   325     pEp_identity* _id2 = NULL;
   326     string _lang;
   327     *words = NULL;
   328 
   329     try {
   330         _id1 = new_identity(id1);
   331         _id2 = new_identity(id2);
   332 
   333         if (lang) {
   334             _lang = utf8_string(lang);
   335             if (_lang.length() == 0) {
   336                 _lang = "en";
   337             }
   338             else if (_lang.length() != 2) {
   339                 result = E_INVALIDARG;
   340             }
   341         }
   342         else {
   343             _lang = "en";
   344         }
   345     }
   346     catch (bad_alloc&) {
   347         result = E_OUTOFMEMORY;
   348     }
   349     catch (exception& ex) {
   350         result = FAIL(ex.what());
   351     }
   352 
   353     char* _words;
   354     size_t _size;
   355     if (result == S_OK) {
   356         auto status = ::get_trustwords(session(), _id1, _id2, _lang.c_str(), &_words, &_size, full != 0 /* convert variant bool to C bool */);
   357 
   358         if (status == PEP_OUT_OF_MEMORY) {
   359             result = E_OUTOFMEMORY;
   360         }
   361         else if (status == PEP_TRUSTWORD_NOT_FOUND) {
   362             result = FAIL(L"GetTrustwords: Trustword not found", status);
   363         }
   364         else if (!words) {
   365             result = FAIL(L"GetTrustwords: _words == NULL", status);
   366         }
   367         else {
   368             *words = utf16_bstr(_words);
   369             pEp_free(_words);
   370         }
   371     }
   372 
   373     free_identity(_id1);
   374     free_identity(_id2);
   375 
   376     return result;
   377 }
   378 
   379 STDMETHODIMP CpEpEngine::GetMessageTrustwords(
   380     /* [in] */ struct TextMessage *msg,
   381     /* [in] */ struct pEpIdentity *receivedBy,
   382     /* [in] */ SAFEARRAY *keylist,
   383     /* [defaultvalue][in] */ BSTR lang,
   384     /* [defaultvalue][in] */ VARIANT_BOOL full,
   385     /* [retval][out] */ BSTR *words) {
   386     assert(msg);
   387     assert(receivedBy);
   388     assert(words);
   389 
   390     if (!(msg && receivedBy && words))
   391     {
   392         return E_INVALIDARG;
   393     }
   394 
   395     HRESULT result = S_OK;
   396 
   397     pEp_identity * _received_by = NULL;
   398     ::message * _msg = NULL;
   399     ::stringlist_t *_keylist = NULL;
   400     string _lang;
   401     *words = NULL;
   402 
   403     try {
   404         _received_by = new_identity(receivedBy);
   405         _msg = text_message_to_C(msg);
   406 
   407         if (keylist) {
   408             _keylist = new_stringlist(keylist);
   409         }
   410 
   411         if (lang) {
   412             _lang = utf8_string(lang);
   413             if (_lang.length() == 0) {
   414                 _lang = "en";
   415             }
   416             else if (_lang.length() != 2) {
   417                 result = E_INVALIDARG;
   418             }
   419         }
   420         else {
   421             _lang = "en";
   422         }
   423     }
   424     catch (bad_alloc&) {
   425         result = E_OUTOFMEMORY;
   426     }
   427     catch (exception& ex) {
   428         result = FAIL(ex.what());
   429     }
   430 
   431     char* _words = NULL;
   432     if (result == S_OK) {
   433         auto status = ::get_message_trustwords(
   434             session(),
   435             _msg,
   436             _keylist,
   437             _received_by,
   438             _lang.c_str(),
   439             &_words,
   440             full != 0 /* convert variant bool to C bool */);
   441 
   442         if (status == PEP_OUT_OF_MEMORY) {
   443             result = E_OUTOFMEMORY;
   444         }
   445         else if (status == PEP_TRUSTWORD_NOT_FOUND) {
   446             result = FAIL(L"GetTrustwords: Trustword not found", status);
   447         }
   448         else if (!words) {
   449             result = FAIL(L"GetTrustwords: _words == NULL", status);
   450         }
   451         else {
   452             *words = utf16_bstr(_words);
   453         }
   454     }
   455 
   456     ::pEp_free(_words);
   457     ::free_message(_msg);
   458     ::free_stringlist(_keylist);
   459     ::free_identity(_received_by);
   460 
   461     return result;
   462 }
   463 
   464 STDMETHODIMP CpEpEngine::GetCrashdumpLog(LONG maxlines, BSTR * log)
   465 {
   466     // COM-18: Currently, long == int on windows, so the check
   467     // for INT_MAX is not strictly necessary. However, the code
   468     // might get copy-pasted to other adapters in the future,
   469     // so safety first...
   470     assert(maxlines >= 0 && maxlines <= INT_MAX);
   471     assert(log);
   472 
   473     if (!(maxlines >= 0 && maxlines <= INT_MAX && log))
   474         return E_INVALIDARG;
   475 
   476     char *_log;
   477     PEP_STATUS status = ::get_crashdump_log(session(), (int)maxlines, &_log);
   478     assert(status == PEP_STATUS_OK);
   479     if (status == PEP_OUT_OF_MEMORY)
   480         return E_OUTOFMEMORY;
   481     if (status != PEP_STATUS_OK)
   482         return FAIL(L"GetCrashdumpLog", status);
   483     if (_log == NULL)
   484         return FAIL(L"GetCrashdumpLog: _log == NULL");
   485 
   486     *log = utf16_bstr(_log);
   487     pEp_free(_log);
   488     return S_OK;
   489 }
   490 
   491 STDMETHODIMP CpEpEngine::GetEngineVersion(BSTR * engine_version)
   492 {
   493     assert(engine_version);
   494 
   495     if (!engine_version)
   496         return E_INVALIDARG;
   497 
   498     const char *_engine_version = ::get_engine_version();
   499 
   500     if (_engine_version == NULL)
   501         return FAIL(L"GetEngineVersion: _engine_version == NULL");
   502 
   503     *engine_version = utf16_bstr(_engine_version);
   504 
   505     return S_OK;
   506 }
   507 
   508 STDMETHODIMP CpEpEngine::GetLanguageList(BSTR * languages)
   509 {
   510     assert(languages);
   511 
   512     if (!languages)
   513         return E_INVALIDARG;
   514 
   515     char *_languages;
   516     PEP_STATUS status = ::get_languagelist(session(), &_languages);
   517     assert(status == PEP_STATUS_OK);
   518     if (status == PEP_OUT_OF_MEMORY)
   519         return E_OUTOFMEMORY;
   520     if (status != PEP_STATUS_OK)
   521         return FAIL(L"GetLanguageList", status);
   522     if (_languages == NULL)
   523         return FAIL(L"GetLanguageList: _languages == NULL");
   524 
   525     *languages = utf16_bstr(_languages);
   526     pEp_free(_languages);
   527     return S_OK;
   528 }
   529 
   530 STDMETHODIMP CpEpEngine::SetIdentityFlags(struct pEpIdentity *identity, pEpIdentityFlags flags)
   531 {
   532     assert(identity);
   533     if (!identity)
   534         return E_INVALIDARG;
   535 
   536     ::pEp_identity *_ident = nullptr;
   537 
   538     try {
   539         _ident = new_identity(identity);
   540         assert(_ident);
   541         if (_ident == NULL)
   542             return E_OUTOFMEMORY;
   543     }
   544     catch (bad_alloc&) {
   545         return E_OUTOFMEMORY;
   546     }
   547     catch (exception& ex) {
   548         return FAIL(ex.what());;
   549     }
   550 
   551     PEP_STATUS status = ::set_identity_flags(session(), _ident, (identity_flags_t)flags);
   552     ::free_identity(_ident);
   553     if (status != PEP_STATUS_OK)
   554         return FAIL(_T("SetIdentityFlags"), status);
   555 
   556     return S_OK;
   557 }
   558 
   559 STDMETHODIMP CpEpEngine::UnsetIdentityFlags(struct pEpIdentity *identity, pEpIdentityFlags flags)
   560 {
   561     assert(identity);
   562     if (!identity)
   563         return E_INVALIDARG;
   564 
   565     ::pEp_identity *_ident = nullptr;
   566 
   567     try {
   568         _ident = new_identity(identity);
   569         assert(_ident);
   570         if (_ident == NULL)
   571             return E_OUTOFMEMORY;
   572     }
   573     catch (bad_alloc&) {
   574         return E_OUTOFMEMORY;
   575     }
   576     catch (exception& ex) {
   577         return FAIL(ex.what());;
   578     }
   579 
   580     PEP_STATUS status = ::unset_identity_flags(session(), _ident, (identity_flags_t)flags);
   581     ::free_identity(_ident);
   582     if (status != PEP_STATUS_OK)
   583         return FAIL(_T("UnsetIdentityFlags"), status);
   584 
   585     return S_OK;
   586 }
   587 
   588 STDMETHODIMP CpEpEngine::StartKeyserverLookup()
   589 {
   590     if (identity_queue.load())
   591         return S_OK;
   592 
   593     identity_queue.store(new identity_queue_t());
   594     keymanagement_thread = new thread(::do_keymanagement, retrieve_next_identity, (void *)identity_queue.load());
   595 
   596     return S_OK;
   597 }
   598 
   599 STDMETHODIMP CpEpEngine::StopKeyserverLookup()
   600 {
   601     if (identity_queue.load() == NULL)
   602         return S_OK;
   603 
   604     identity_queue_t *_iq = identity_queue.load();
   605     identity_queue.store(NULL);
   606 
   607     pEp_identity_cpp shutdown;
   608     _iq->push_front(shutdown);
   609 
   610     keymanagement_thread->join();
   611     delete keymanagement_thread;
   612     keymanagement_thread = NULL;
   613 
   614     delete _iq;
   615 
   616     return S_OK;
   617 }
   618 
   619 STDMETHODIMP CpEpEngine::Myself(struct pEpIdentity *ident, struct pEpIdentity *result)
   620 {
   621     assert(ident);
   622     assert(result);
   623 
   624     if (!(ident && result))
   625         return E_INVALIDARG;
   626 
   627     ::pEp_identity *_ident = 0;
   628 
   629     try {
   630         _ident = new_identity(ident);
   631         assert(_ident);
   632         if (_ident == NULL)
   633             return E_OUTOFMEMORY;
   634     }
   635     catch (bad_alloc&) {
   636         return E_OUTOFMEMORY;
   637     }
   638     catch (exception& ex) {
   639         return FAIL(ex.what());;
   640     }
   641 
   642     PEP_STATUS status = ::myself(session(), _ident);
   643 
   644     if (status == PEP_STATUS_OK) {
   645         assert(_ident->fpr);
   646         copy_identity(result, _ident);
   647         ::free_identity(_ident);
   648         return S_OK;
   649     }
   650     else {
   651         ::free_identity(_ident);
   652         if (status == PEP_OUT_OF_MEMORY)
   653             return E_OUTOFMEMORY;
   654         else
   655             return FAIL(L"myself", status);
   656     }
   657 }
   658 
   659 STDMETHODIMP CpEpEngine::UpdateIdentity(struct pEpIdentity *ident, struct pEpIdentity *result)
   660 {
   661     assert(ident);
   662     assert(result);
   663 
   664     if (!(ident && result))
   665         return E_INVALIDARG;
   666 
   667     ::pEp_identity *_ident;
   668     try {
   669         _ident = new_identity(ident);
   670     }
   671     catch (bad_alloc&) {
   672         return E_OUTOFMEMORY;
   673     }
   674     catch (exception& ex) {
   675         return FAIL(ex.what());
   676     }
   677 
   678     assert(_ident);
   679     if (_ident == NULL)
   680         return E_OUTOFMEMORY;
   681 
   682     PEP_STATUS status = ::update_identity(session(), _ident);
   683 
   684     if (status == PEP_STATUS_OK) {
   685         copy_identity(result, _ident);
   686         ::free_identity(_ident);
   687         return S_OK;
   688     }
   689     else if (status == PEP_GET_KEY_FAILED || status == PEP_KEY_NOT_FOUND) {
   690         if (_ident->fpr) {
   691             pEp_free(_ident->fpr);
   692             _ident->fpr = NULL;
   693         }
   694         copy_identity(result, _ident);
   695         result->Fpr = NULL;
   696         ::free_identity(_ident);
   697         return S_OK;
   698     }
   699     else {
   700         ::free_identity(_ident);
   701         if (status == PEP_OUT_OF_MEMORY)
   702             return E_OUTOFMEMORY;
   703         else
   704             return FAIL(L"UpdateIdentity", status);
   705     }
   706 }
   707 
   708 STDMETHODIMP CpEpEngine::KeyMistrusted(struct pEpIdentity *ident)
   709 {
   710     ::pEp_identity *_ident;
   711 
   712     assert(ident);
   713     if (!ident)
   714         return E_INVALIDARG;
   715 
   716     try {
   717         _ident = new_identity(ident);
   718     }
   719     catch (bad_alloc&) {
   720         return E_OUTOFMEMORY;
   721     }
   722     catch (exception& ex) {
   723         return FAIL(ex.what());;
   724     }
   725 
   726     PEP_STATUS status = ::key_mistrusted(session(), _ident);
   727     free_identity(_ident);
   728 
   729     if (status == PEP_OUT_OF_MEMORY)
   730         return E_OUTOFMEMORY;
   731 
   732     if (status == PEP_KEY_NOT_FOUND)
   733         return FAIL(L"key not found");
   734 
   735     if (status != PEP_STATUS_OK)
   736         return FAIL(L"cannot revoke compromised key", status);
   737 
   738     return S_OK;
   739 }
   740 
   741 STDMETHODIMP CpEpEngine::IspEpUser(/* [in] */ struct pEpIdentity *ident, /* [retval][out] */ VARIANT_BOOL *ispEp) 
   742 {
   743     ::pEp_identity *_ident;
   744 
   745     assert(ident);
   746     if (!ident)
   747         return E_INVALIDARG;
   748 
   749     try {
   750         _ident = new_identity(ident);
   751     }
   752     catch (bad_alloc&) {
   753         return E_OUTOFMEMORY;
   754     }
   755     catch (exception& ex) {
   756         return FAIL(ex.what());;
   757     }
   758 
   759     bool is_pep = FALSE;
   760     PEP_STATUS status = ::is_pEp_user(session(), _ident, &is_pep);
   761 
   762     *ispEp = is_pep;
   763 
   764     if (status == PEP_CANNOT_FIND_PERSON)
   765         return FAIL(L"Cannot find identity!", status);
   766 
   767     if (status == PEP_ILLEGAL_VALUE)
   768         return E_INVALIDARG;
   769 
   770     if (status != PEP_STATUS_OK)
   771         return FAIL(L"Engine is_pEp_user returned error", status);
   772 
   773     return S_OK;
   774 }
   775 
   776 STDMETHODIMP CpEpEngine::KeyResetIdentity(struct pEpIdentity ident, BSTR fpr)
   777 {
   778     ::pEp_identity *_ident;
   779 
   780     try {
   781         _ident = new_identity(&ident);
   782     }
   783     catch (bad_alloc&) {
   784         return E_OUTOFMEMORY;
   785     }
   786     catch (exception& ex) {
   787         return FAIL(ex.what());;
   788     }
   789 
   790     string _fpr = utf8_string(fpr);
   791 
   792     PEP_STATUS status = ::key_reset_identity(session(), _ident, _fpr.c_str());
   793 
   794     free_identity(_ident);
   795 
   796     if (status == PEP_OUT_OF_MEMORY)
   797         return E_OUTOFMEMORY;
   798 
   799     if (status == PEP_KEY_NOT_FOUND)
   800         return FAIL(L"key not found");
   801 
   802     if (status != PEP_STATUS_OK)
   803         return FAIL(L"cannot reset identity", status);
   804 
   805     return S_OK;
   806 }
   807 
   808 STDMETHODIMP CpEpEngine::KeyResetUser(BSTR userId, BSTR fpr)
   809 {
   810     string _userId = utf8_string(userId);
   811     string _fpr = utf8_string(fpr);
   812 
   813     PEP_STATUS status = ::key_reset_user(session(), _userId.c_str(), _fpr.c_str());
   814 
   815     if (status == PEP_OUT_OF_MEMORY)
   816         return E_OUTOFMEMORY;
   817 
   818     if (status == PEP_KEY_NOT_FOUND)
   819         return FAIL(L"key not found");
   820 
   821     if (status != PEP_STATUS_OK)
   822         return FAIL(L"cannot reset user", status);
   823 
   824     return S_OK;
   825 }
   826 
   827 STDMETHODIMP CpEpEngine::KeyResetAllOwnKeys()
   828 {
   829     // We have to stop sync before resetting all own keys and enable it again afterwards
   830     ShutDownSync();
   831 
   832     PEP_STATUS status = ::key_reset_all_own_keys(session());
   833 
   834     Startup();
   835 
   836     if (status == PEP_OUT_OF_MEMORY)
   837         return E_OUTOFMEMORY;
   838 
   839     if (status != PEP_STATUS_OK)
   840         return FAIL(L"cannot reset all own keys", status);
   841 
   842     return S_OK;
   843 }
   844 
   845 STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
   846 {
   847     ::pEp_identity *_ident;
   848 
   849     assert(ident);
   850 
   851     if (!ident)
   852         return E_INVALIDARG;
   853 
   854     try {
   855         _ident = new_identity(ident);
   856     }
   857     catch (bad_alloc&) {
   858         return E_OUTOFMEMORY;
   859     }
   860     catch (exception& ex) {
   861         return FAIL(ex.what());;
   862     }
   863 
   864     PEP_STATUS status = ::key_reset_trust(session(), _ident);
   865     free_identity(_ident);
   866 
   867     if (status == PEP_OUT_OF_MEMORY)
   868         return E_OUTOFMEMORY;
   869 
   870     if (status == PEP_KEY_NOT_FOUND)
   871         return FAIL(L"key not found");
   872 
   873     if (status != PEP_STATUS_OK)
   874         return FAIL(L"cannot reset trust", status);
   875 
   876     return S_OK;
   877 }
   878 
   879 int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
   880 {
   881     assert(ident);
   882     assert(management);
   883     if (!(ident && management))
   884         return -1;
   885 
   886     CpEpEngine *me = (CpEpEngine *)management;
   887 
   888     if (me->identity_queue.load() == NULL)
   889         return 0;
   890 
   891     try {
   892         me->identity_queue.load()->push_back(ident);
   893     }
   894     catch (exception&) {
   895         return -1;
   896     }
   897 
   898     return 0;
   899 }
   900 
   901 ::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
   902 {
   903     assert(management);
   904     if (!management)
   905         return NULL;
   906 
   907     identity_queue_t *iq = (identity_queue_t *)management;
   908 
   909     do /* poll queue */ {
   910         if (iq->size())
   911             break;
   912         ::Sleep(100);
   913     } while (true);
   914 
   915     ::pEp_identity *_ident;
   916     pEp_identity_cpp& ident = iq->front();
   917 
   918     if (ident.address.size() == 0)
   919         return NULL;
   920 
   921     _ident = ident.to_pEp_identity();
   922     iq->pop_front();
   923 
   924     return _ident;
   925 }
   926 
   927 static IpEpEngineCallbacks * _unmarshaled_consumer(CpEpEngine::callback_container::Container::iterator p)
   928 {
   929     if (!p->cdata && p->pdata && p->pdata->marshaled) {
   930         HRESULT r = CoGetInterfaceAndReleaseStream(p->pdata->marshaled, IID_IpEpEngineCallbacks, (LPVOID*) &p->cdata);
   931         if (!SUCCEEDED(r))
   932             throw runtime_error("_unmarshaled_consumer(): CoGetInterfaceAndReleaseStream() failed");
   933         p->pdata->marshaled = nullptr;
   934     }
   935     else if (p->cdata && !p->pdata) {
   936         p->cdata->Release();
   937         p->cdata = nullptr;
   938     }
   939 
   940     return p->cdata;
   941 }
   942 
   943 PEP_STATUS CpEpEngine::messageToSend(message *msg)
   944 {
   945     assert(msg);
   946     if (!msg)
   947         return PEP_ILLEGAL_VALUE;
   948 
   949     bool in_sync = on_sync_thread();
   950 
   951     for (auto p = sync_callbacks.begin(); p != sync_callbacks.end(); ++p) {
   952         IpEpEngineCallbacks *cb = in_sync ? _unmarshaled_consumer(p) : p->pdata->unmarshaled;
   953 
   954         if (cb) {
   955             TextMessage _msg;
   956             memset(&_msg, 0, sizeof(TextMessage));
   957 
   958             text_message_from_C(&_msg, msg);
   959             HRESULT r = cb->MessageToSend(&_msg);
   960             assert(r == S_OK);
   961             clear_text_message(&_msg);
   962             if (r == E_OUTOFMEMORY)
   963                 return PEP_OUT_OF_MEMORY;
   964             if (r != S_OK)
   965                 return PEP_UNKNOWN_ERROR;
   966         }
   967     }
   968 
   969     sync_callbacks.compact();
   970 
   971     return PEP_STATUS_OK;
   972 }
   973 
   974 PEP_STATUS CpEpEngine::notifyHandshake(::pEp_identity *self, ::pEp_identity *partner, sync_handshake_signal signal)
   975 {
   976     assert(self && partner);
   977     if (!(self && partner))
   978         return PEP_ILLEGAL_VALUE;
   979 
   980     bool in_sync = on_sync_thread();
   981 
   982     // fire all of them
   983     for (auto p = sync_callbacks.begin(); p != sync_callbacks.end(); ++p) {
   984         IpEpEngineCallbacks *cb = in_sync ? _unmarshaled_consumer(p) : p->pdata->unmarshaled;
   985 
   986         if (cb) {
   987             pEpIdentity _self;
   988             copy_identity(&_self, self);
   989             pEpIdentity _partner;
   990             copy_identity(&_partner, partner);
   991 
   992             SyncHandshakeSignal _signal = (SyncHandshakeSignal)signal;
   993             HRESULT r = cb->NotifyHandshake(&_self, &_partner, _signal);
   994             assert(r == S_OK);
   995             clear_identity_s(_self);
   996             clear_identity_s(_partner);
   997             if (r == E_OUTOFMEMORY)
   998                 return PEP_OUT_OF_MEMORY;
   999         }
  1000     }
  1001 
  1002     sync_callbacks.compact();
  1003 
  1004     return PEP_STATUS_OK;
  1005 }
  1006 
  1007 STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
  1008 {
  1009     assert(fpr);
  1010     if (!fpr)
  1011         return E_INVALIDARG;
  1012 
  1013     string _fpr = utf8_string(fpr);
  1014     PEP_STATUS status = ::blacklist_add(session(), _fpr.c_str());
  1015     assert(status == PEP_STATUS_OK);
  1016     if (status != PEP_STATUS_OK)
  1017         return FAIL(L"blacklist_add failed in pEp engine", status);
  1018 
  1019     return S_OK;
  1020 }
  1021 
  1022 STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
  1023 {
  1024     assert(fpr);
  1025     if (!fpr)
  1026         return E_INVALIDARG;
  1027 
  1028     string _fpr = utf8_string(fpr);
  1029     PEP_STATUS status = ::blacklist_delete(session(), _fpr.c_str());
  1030     assert(status == PEP_STATUS_OK);
  1031     if (status != PEP_STATUS_OK)
  1032         return FAIL(L"blacklist_delete failed in pEp engine", status);
  1033 
  1034     return S_OK;
  1035 }
  1036 
  1037 STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
  1038 {
  1039     assert(fpr);
  1040     assert(listed);
  1041 
  1042     if (!(fpr && listed))
  1043         return E_INVALIDARG;
  1044 
  1045     string _fpr = utf8_string(fpr);
  1046     bool result;
  1047     PEP_STATUS status = ::blacklist_is_listed(session(), _fpr.c_str(), &result);
  1048     assert(status == PEP_STATUS_OK);
  1049     if (status != PEP_STATUS_OK)
  1050         return FAIL(L"blacklist_is_listed failed in pEp engine", status);
  1051 
  1052     *listed = result ? VARIANT_TRUE : VARIANT_FALSE;
  1053     return S_OK;
  1054 }
  1055 
  1056 STDMETHODIMP CpEpEngine::BlacklistRetrieve(SAFEARRAY **blacklist)
  1057 {
  1058     assert(blacklist);
  1059 
  1060     if (!blacklist)
  1061         return E_INVALIDARG;
  1062 
  1063     ::stringlist_t *_blacklist = NULL;
  1064     PEP_STATUS status = ::blacklist_retrieve(session(), &_blacklist);
  1065     assert(status == PEP_STATUS_OK);
  1066     if (status != PEP_STATUS_OK)
  1067         return FAIL(L"blacklist_retrieve failed in pEp engine", status);
  1068     assert(_blacklist);
  1069 
  1070     *blacklist = string_array(_blacklist);
  1071     ::free_stringlist(_blacklist);
  1072     return S_OK;
  1073 }
  1074 
  1075 HRESULT CpEpEngine::error(_bstr_t msg)
  1076 {
  1077     _bstr_t helpFile = L"";
  1078     _bstr_t source = L"pEp COM Adapter";
  1079 
  1080     ICreateErrorInfo *cei;
  1081     if (SUCCEEDED(CreateErrorInfo(&cei))) {
  1082         cei->SetDescription(msg);
  1083         cei->SetGUID(__uuidof(IpEpEngine));
  1084         cei->SetHelpContext(0);
  1085         cei->SetHelpFile(helpFile);
  1086         cei->SetSource(source);
  1087 
  1088         IErrorInfo *errinfo;
  1089         if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
  1090             SetErrorInfo(0, errinfo);
  1091             errinfo->Release();
  1092         }
  1093         cei->Release();
  1094     }
  1095     return E_FAIL;
  1096 }
  1097 
  1098 HRESULT CpEpEngine::error(_bstr_t msg, PEP_STATUS status)
  1099 {
  1100     std::stringstream stream;
  1101     stream << msg;
  1102     stream << ": ";
  1103     stream << pEp_status_to_string(status);
  1104     stream << std::hex << " (" << status << ")";
  1105 
  1106     error(stream.str().c_str());
  1107 
  1108     if (status == ::PEP_OUT_OF_MEMORY)
  1109         return E_OUTOFMEMORY;
  1110 
  1111     return MAKE_HRESULT(1, FACILITY_ITF, (0xFFFF & status));
  1112 }
  1113 
  1114 STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags, pEpEncFormat encFormat)
  1115 {
  1116     assert(src);
  1117     assert(dst);
  1118 
  1119     if (!(src && dst))
  1120         return E_INVALIDARG;
  1121 
  1122     ::message *_src = NULL;
  1123     try {
  1124         _src = text_message_to_C(src);
  1125     }
  1126     catch (bad_alloc&) {
  1127         return E_OUTOFMEMORY;
  1128     }
  1129     catch (exception& ex) {
  1130         return FAIL(ex.what());
  1131     }
  1132 
  1133     _PEP_enc_format _encFormat = (_PEP_enc_format)encFormat;
  1134 
  1135     // COM-19: Initialize msg_dst to NULL, or we end up calling
  1136     // free_message() below with a pointer to random garbage in
  1137     // case of an error in encrypt_message().
  1138     ::message *msg_dst = NULL;
  1139     ::stringlist_t *_extra = new_stringlist(extra); // can cope with NULL
  1140 
  1141     // _PEP_enc_format used to be intentionally hardcoded to PEP_enc_PEP:
  1142     // Since COM-74, this has been changed to an explicit parameter, to allow the engine to attach
  1143     // the keys and headers to outgoing, unencrypted messages.
  1144     PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
  1145     PEP_STATUS status = ::encrypt_message(session(), _src, _extra, &msg_dst, _encFormat, engineFlags);
  1146     ::free_stringlist(_extra);
  1147 
  1148     if (status == PEP_STATUS_OK)
  1149         text_message_from_C(dst, msg_dst);
  1150     else
  1151         text_message_from_C(dst, _src);
  1152 
  1153     ::free_message(msg_dst);
  1154     ::free_message(_src);
  1155 
  1156     if (status == PEP_OUT_OF_MEMORY)
  1157         return E_OUTOFMEMORY;
  1158 
  1159     // COM-41: Enhanced PEP status handling
  1160     if ((status != PEP_STATUS_OK) && (status < PEP_UNENCRYPTED || status >= PEP_TRUSTWORD_NOT_FOUND))
  1161         return FAIL("Failure to encrypt message", status);
  1162 
  1163     // Statii like PEP_UNENCRYPTED due to no private key
  1164     // should not be a catastrophic failure here. Using S_FALSE
  1165     // still allows clients to differentiate with S_OK,
  1166     // although this does not work out of the box with
  1167     // the standard .NET mapping of COM.
  1168     if (status != PEP_STATUS_OK)
  1169         return S_FALSE;
  1170 
  1171     return S_OK;
  1172 }
  1173 
  1174 STDMETHODIMP CpEpEngine::EncryptMessageAndAddPrivKey(TextMessage * src, TextMessage * dst, BSTR to_fpr, pEpEncryptFlags flags, pEpEncFormat encFormat)
  1175 {
  1176     assert(src);
  1177     assert(dst);
  1178     assert(to_fpr);
  1179 
  1180     if (!(src && dst))
  1181         return E_INVALIDARG;
  1182 
  1183     ::message *_src = NULL;
  1184     try {
  1185         _src = text_message_to_C(src);
  1186     }
  1187     catch (bad_alloc&) {
  1188         return E_OUTOFMEMORY;
  1189     }
  1190     catch (exception& ex) {
  1191         return FAIL(ex.what());
  1192     }
  1193 
  1194     _PEP_enc_format _encFormat = (_PEP_enc_format)encFormat;
  1195 
  1196     // COM-19: Initialize msg_dst to NULL, or we end up calling
  1197     // free_message() below with a pointer to random garbage in
  1198     // case of an error in encrypt_message().
  1199     ::message *msg_dst = NULL;
  1200 
  1201     string _to_fpr = utf8_string(to_fpr);
  1202                                                     // _PEP_enc_format used to be intentionally hardcoded to PEP_enc_PEP:
  1203                                                     // Since COM-74, this has been changed to an explicit parameter, to allow the engine to attach
  1204                                                     // the keys and headers to outgoing, unencrypted messages.
  1205     PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
  1206     PEP_STATUS status = ::encrypt_message_and_add_priv_key(session(), _src, &msg_dst, _to_fpr.c_str(), _encFormat, engineFlags);
  1207 
  1208     if (status == PEP_STATUS_OK)
  1209         text_message_from_C(dst, msg_dst);
  1210     else
  1211         text_message_from_C(dst, _src);
  1212 
  1213     ::free_message(msg_dst);
  1214     ::free_message(_src);
  1215 
  1216     if (status == PEP_OUT_OF_MEMORY)
  1217         return E_OUTOFMEMORY;
  1218 
  1219     // COM-41: Enhanced PEP status handling
  1220     if ((status != PEP_STATUS_OK) && (status < PEP_UNENCRYPTED || status >= PEP_TRUSTWORD_NOT_FOUND))
  1221         return FAIL("Failure to encrypt message", status);
  1222 
  1223     // Statii like PEP_UNENCRYPTED due to no private key
  1224     // should not be a catastrophic failure here. Using S_FALSE
  1225     // still allows clients to differentiate with S_OK,
  1226     // although this does not work out of the box with
  1227     // the standard .NET mapping of COM.
  1228     if (status != PEP_STATUS_OK)
  1229         return S_FALSE;
  1230 
  1231     return S_OK;
  1232 }
  1233 
  1234 STDMETHODIMP CpEpEngine::EncryptMessageForSelf(pEpIdentity * targetId, TextMessage * src,
  1235     /* [in] */ SAFEARRAY *extra, TextMessage * dst, pEpEncryptFlags flags)
  1236 {
  1237     assert(targetId);
  1238     assert(src);
  1239     assert(dst);
  1240 
  1241     if (!(targetId && src && dst))
  1242         return E_INVALIDARG;
  1243 
  1244     PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
  1245 
  1246     ::pEp_identity *_target_id = new_identity(targetId);
  1247 
  1248     ::message *_src = NULL;
  1249     try {
  1250         _src = text_message_to_C(src);
  1251     }
  1252     catch (bad_alloc&) {
  1253         return E_OUTOFMEMORY;
  1254     }
  1255     catch (exception& ex) {
  1256         return FAIL(ex.what());
  1257     }
  1258 
  1259     ::stringlist_t* _extra = NULL;
  1260     HRESULT result = S_OK;
  1261     ::message *msg_dst = NULL;
  1262     PEP_STATUS status = PEP_STATUS_OK;
  1263 
  1264     try {
  1265         if (extra) {
  1266             _extra = new_stringlist(extra);
  1267         }
  1268 
  1269         // COM-19: Initialize msg_dst to NULL, or we end up calling
  1270         // free_message() below with a pointer to random garbage in
  1271         // case of an error in encrypt_message_for_self().
  1272         status = ::encrypt_message_for_self(session(), _target_id, _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
  1273 
  1274         if (status == PEP_STATUS_OK)
  1275             text_message_from_C(dst, msg_dst);
  1276         else
  1277             text_message_from_C(dst, _src);
  1278     } catch (bad_alloc&) {
  1279         result = E_OUTOFMEMORY;
  1280     }
  1281     catch (exception& ex) {
  1282         result = FAIL(ex.what());
  1283     }
  1284 
  1285     ::free_message(msg_dst);
  1286     ::free_message(_src);
  1287     ::free_identity(_target_id);
  1288     ::free_stringlist(_extra);
  1289 
  1290     if (status == PEP_OUT_OF_MEMORY)
  1291         return E_OUTOFMEMORY;
  1292 
  1293     // Different to encrypt_message, this should never fail (we ought to always
  1294     // have a private key for ourself).#
  1295     if (status != PEP_STATUS_OK)
  1296         return FAIL("Failure to encrypt message", status);
  1297 
  1298     return result;
  1299 }
  1300 
  1301 STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
  1302 {
  1303     assert(src);
  1304     assert(dst);
  1305     assert(keylist);
  1306     assert(flags);
  1307     assert(rating);
  1308 
  1309     if (!(src && dst && keylist && flags && rating))
  1310         return E_INVALIDARG;
  1311 
  1312     *rating = pEpRatingUndefined;
  1313 
  1314     ::message *_src = NULL;
  1315     try {
  1316         _src = text_message_to_C(src);
  1317     }
  1318     catch (bad_alloc&) {
  1319         return E_OUTOFMEMORY;
  1320     }
  1321     catch (exception& ex) {
  1322         return FAIL(ex.what());
  1323     }
  1324     ::message *msg_dst = NULL;
  1325     ::stringlist_t *_keylist = new_stringlist(*keylist);
  1326     ::PEP_rating _rating;
  1327 
  1328     PEP_decrypt_flags_t engineflags = (PEP_decrypt_flags_t) *flags;
  1329     PEP_STATUS status = ::decrypt_message(session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
  1330 
  1331     *flags = (pEpDecryptFlags)engineflags;
  1332 
  1333     if (_src)
  1334         text_message_from_C(src, _src);
  1335 
  1336     if (msg_dst)
  1337         text_message_from_C(dst, msg_dst);
  1338 
  1339     ::free_message(_src);
  1340     ::free_message(msg_dst);
  1341 
  1342     if (_keylist) {
  1343         *keylist = string_array(_keylist);
  1344         free_stringlist(_keylist);
  1345     }
  1346 
  1347     *rating = (pEpRating)_rating;
  1348 
  1349     return S_OK;
  1350 }
  1351 
  1352 STDMETHODIMP CpEpEngine::ReEvaluateMessageRating(TextMessage * msg, SAFEARRAY * x_KeyList, pEpRating x_EncStatus, pEpRating *rating)
  1353 {
  1354     assert(msg);
  1355     assert(x_EncStatus != PEP_rating_undefined);
  1356     assert(rating);
  1357 
  1358     if (!(msg && x_EncStatus != PEP_rating_undefined && rating))
  1359         return E_INVALIDARG;
  1360 
  1361     *rating = pEpRatingUndefined;
  1362 
  1363     ::message *_msg = NULL;
  1364     try {
  1365         _msg = text_message_to_C(msg);
  1366     }
  1367     catch (bad_alloc&) {
  1368         return E_OUTOFMEMORY;
  1369     }
  1370     catch (exception& ex) {
  1371         return FAIL(ex.what());
  1372     }
  1373 
  1374     ::stringlist_t *_keylist = new_stringlist(x_KeyList);
  1375     ::PEP_rating _rating = PEP_rating_undefined;
  1376 
  1377     PEP_STATUS status = ::re_evaluate_message_rating(session(), _msg, _keylist, (PEP_rating)x_EncStatus, &_rating);
  1378 
  1379     ::free_stringlist(_keylist);
  1380     ::free_message(_msg);
  1381 
  1382     *rating = (pEpRating)_rating;
  1383 
  1384     return S_OK;
  1385 }
  1386 
  1387 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
  1388 {
  1389     assert(msg);
  1390     assert(pVal);
  1391 
  1392     if (!(msg  && pVal))
  1393         return E_INVALIDARG;
  1394 
  1395     ::message *_msg = NULL;
  1396     try {
  1397         _msg = text_message_to_C(msg);
  1398     }
  1399     catch (bad_alloc&) {
  1400         return E_OUTOFMEMORY;
  1401     }
  1402     catch (exception& ex) {
  1403         return FAIL(ex.what());
  1404     }
  1405 
  1406     PEP_rating _rating;
  1407     PEP_STATUS status = ::outgoing_message_rating(session(), _msg, &_rating);
  1408     if (status != PEP_STATUS_OK)
  1409         return FAIL(L"cannot get message rating", status);
  1410 
  1411     *pVal = (pEpRating)_rating;
  1412     return S_OK;
  1413 }
  1414 
  1415 STDMETHODIMP CpEpEngine::OutgoingMessageRatingPreview(TextMessage *msg, pEpRating * pVal)
  1416 {
  1417     assert(msg);
  1418     assert(pVal);
  1419 
  1420     if (!(msg  && pVal))
  1421         return E_INVALIDARG;
  1422 
  1423     ::message *_msg = NULL;
  1424     try {
  1425         _msg = text_message_to_C(msg);
  1426     }
  1427     catch (bad_alloc&) {
  1428         return E_OUTOFMEMORY;
  1429     }
  1430     catch (exception& ex) {
  1431         return FAIL(ex.what());
  1432     }
  1433 
  1434     PEP_rating _rating;
  1435     PEP_STATUS status = ::outgoing_message_rating_preview(session(), _msg, &_rating);
  1436     if (status != PEP_STATUS_OK)
  1437         return FAIL(L"cannot get message rating", status);
  1438 
  1439     *pVal = (pEpRating)_rating;
  1440     return S_OK;
  1441 }
  1442 
  1443 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
  1444 {
  1445     ::pEp_identity *_ident;
  1446 
  1447     assert(ident);
  1448     assert(pVal);
  1449 
  1450     if (!(ident  && pVal))
  1451         return E_INVALIDARG;
  1452 
  1453     try {
  1454         _ident = new_identity(ident);
  1455     }
  1456     catch (bad_alloc&) {
  1457         return E_OUTOFMEMORY;
  1458     }
  1459     catch (exception& ex) {
  1460         return FAIL(ex.what());;
  1461     }
  1462 
  1463     PEP_rating _rating;
  1464     PEP_STATUS status = ::identity_rating(session(), _ident, &_rating);
  1465     free_identity(_ident);
  1466 
  1467     if (status != PEP_STATUS_OK)
  1468         return FAIL(L"cannot get message color", status);
  1469 
  1470     *pVal = (pEpRating)_rating;
  1471     return S_OK;
  1472 }
  1473 
  1474 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
  1475 {
  1476     assert(pVal);
  1477 
  1478     if (!pVal)
  1479         return E_INVALIDARG;
  1480 
  1481     PEP_rating engineRating = (PEP_rating)rating;
  1482     PEP_color _color = ::color_from_rating(engineRating);
  1483 
  1484     *pVal = (pEpColor)_color;
  1485      
  1486     return S_OK;
  1487 }
  1488 
  1489 STDMETHODIMP CpEpEngine::OwnIdentitiesRetrieve(LPSAFEARRAY* ownIdentities)
  1490 {
  1491     assert(ownIdentities);
  1492     if (!ownIdentities)
  1493         return E_INVALIDARG;
  1494 
  1495     *ownIdentities = nullptr;
  1496 
  1497     ::identity_list *il = nullptr;
  1498     PEP_STATUS status = ::own_identities_retrieve(session(), &il);
  1499     if (status == PEP_OUT_OF_MEMORY) {
  1500         return E_OUTOFMEMORY;
  1501     }
  1502     else if (status != PEP_STATUS_OK)
  1503     {
  1504         return FAIL(_T("OwnIdentitiesRetrieve"), status);
  1505     }
  1506 
  1507     SAFEARRAY * _own_identities = nullptr;
  1508     try {
  1509         _own_identities = array_from_C<pEpIdentity, identity_list>(il);
  1510     }
  1511     catch (exception& ex)
  1512     {
  1513         ::free_identity_list(il);
  1514         try {
  1515             dynamic_cast<bad_alloc&>(ex);
  1516         }
  1517         catch (bad_cast&)
  1518         {
  1519             return FAIL(ex.what());
  1520         }
  1521         return E_OUTOFMEMORY;
  1522     }
  1523     free_identity_list(il);
  1524 
  1525     *ownIdentities = _own_identities;
  1526     return S_OK;
  1527 }
  1528 
  1529 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
  1530 {
  1531     ::pEp_identity *_ident;
  1532 
  1533     assert(ident);
  1534     assert(result);
  1535 
  1536     if (!ident || !result)
  1537         return E_INVALIDARG;
  1538 
  1539     try {
  1540         _ident = new_identity(ident);
  1541     }
  1542     catch (bad_alloc&) {
  1543         return E_OUTOFMEMORY;
  1544     }
  1545     catch (exception& ex) {
  1546         return FAIL(ex.what());;
  1547     }
  1548 
  1549     if (verbose_mode) {
  1550         stringstream ss;
  1551         ss << "TrustPersonalKey called with ";
  1552         ss << utf8_string(ident->Address);
  1553         ss << L": ";
  1554         ss << ident->CommType;
  1555         verbose(ss.str());
  1556     }
  1557 
  1558     PEP_STATUS status = ::trust_personal_key(session(), _ident);
  1559 
  1560     if (verbose_mode) {
  1561         stringstream ss;
  1562         ss << "result ";
  1563         ss << status;
  1564         ss << " for ";
  1565         ss << _ident->address;
  1566         ss << L": ";
  1567         ss << _ident->comm_type;
  1568         verbose(ss.str());
  1569     }
  1570 
  1571     if (status == PEP_STATUS_OK)
  1572         copy_identity(result, _ident);
  1573 
  1574     free_identity(_ident);
  1575     if (status == PEP_OUT_OF_MEMORY)
  1576         return E_OUTOFMEMORY;
  1577     else if (status != PEP_STATUS_OK)
  1578         return FAIL(L"failure while executing TrustPersonalKey()", status);
  1579 
  1580     return S_OK;
  1581 }
  1582 
  1583 // Force an update check now
  1584 STDMETHODIMP CpEpEngine::UpdateNow(BSTR productCode, VARIANT_BOOL *didUpdate)
  1585 {
  1586     BOOL result = FALSE;
  1587 
  1588     try
  1589     {
  1590         _bstr_t pc(productCode);
  1591         wstring _pc = pc;
  1592 
  1593         auto products = pEp::GateKeeper::gatekeeper()->registered_products();
  1594         for (auto p = products.begin(); p != products.end(); ++p) {
  1595             if (_pc == p->first) {
  1596                 result = pEp::GateKeeper::gatekeeper()->update_product(*p);
  1597                 break;
  1598             }
  1599         }
  1600     }
  1601     catch (bad_alloc&) {
  1602         return E_OUTOFMEMORY;
  1603     }
  1604     catch (exception& ex) {
  1605         return FAIL(ex.what());;
  1606     }
  1607 
  1608     *didUpdate = result;
  1609     return S_OK;
  1610 }
  1611 
  1612 // Event callbacks
  1613 
  1614 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
  1615 {
  1616     // check for valid parameter
  1617     if (!new_callbacks)
  1618         return E_INVALIDARG;
  1619 
  1620     // don't allow double registration.
  1621     if (this->client_callbacks)
  1622         return E_ILLEGAL_STATE_CHANGE;
  1623 
  1624     this->client_callbacks = new_callbacks;
  1625     new_callbacks->AddRef();
  1626 
  1627     // provide callbacks to sync
  1628     LPSTREAM marshaled_callbacks = nullptr;
  1629     auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
  1630     assert(SUCCEEDED(result));
  1631     assert(marshaled_callbacks);
  1632     sync_callbacks.insert(new MarshaledCallbacks({ this->client_callbacks, marshaled_callbacks }));
  1633 
  1634     return S_OK;
  1635 }
  1636 
  1637 STDMETHODIMP CpEpEngine::UnregisterCallbacks()
  1638 {
  1639     // don't allow double deregistration.
  1640     // S_FALSE still is no error (as double deregistration is not fatal).
  1641     if (!this->client_callbacks)
  1642         return S_FALSE;
  1643 
  1644     for (auto p = sync_callbacks.begin(); p != sync_callbacks.end(); ++p) {
  1645         if (p->pdata && p->pdata->unmarshaled == this->client_callbacks) {
  1646             if (p->pdata->marshaled)
  1647                 p->pdata->marshaled->Release();
  1648             if (p->pdata->unmarshaled)
  1649                 p->pdata->unmarshaled->Release();
  1650             delete p->pdata;
  1651             p->pdata = nullptr;
  1652             break;
  1653         }
  1654     }
  1655 
  1656     this->client_callbacks->Release();
  1657     this->client_callbacks = nullptr;
  1658 
  1659     return S_OK;
  1660 }
  1661 
  1662 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
  1663     assert(keyinfo_list);
  1664 
  1665     if (keyinfo_list == NULL)
  1666         return E_INVALIDARG;
  1667 
  1668     string _pattern = "";
  1669     if (search_pattern)
  1670         _pattern = utf8_string(search_pattern);
  1671     ::stringpair_list_t* _keyinfo_list = NULL;
  1672 
  1673     PEP_STATUS status = ::OpenPGP_list_keyinfo(session(), _pattern.c_str(), &_keyinfo_list);
  1674     assert(status != PEP_OUT_OF_MEMORY);
  1675     if (status == PEP_OUT_OF_MEMORY)
  1676         return E_OUTOFMEMORY;
  1677 
  1678     if (status != PEP_STATUS_OK)
  1679         return FAIL(L"OpenPGP_list_keyinfo", status);
  1680 
  1681     if (_keyinfo_list && _keyinfo_list->value) {
  1682         ::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
  1683     }
  1684     else {
  1685         ::free_stringpair_list(_keyinfo_list);
  1686         return FAIL(L"OpenPGP_list_keyinfo: no keys found");
  1687     }
  1688 
  1689     ::free_stringpair_list(_keyinfo_list);
  1690     return S_OK;
  1691 
  1692 }
  1693 
  1694 STDMETHODIMP CpEpEngine::SetOwnKey(pEpIdentity * ident, BSTR fpr, struct pEpIdentity *result)
  1695 {
  1696     assert(ident);
  1697     assert(result);
  1698     assert(fpr);
  1699 
  1700     if (!(ident && result))
  1701         return E_INVALIDARG;
  1702 
  1703     ::pEp_identity *_ident;
  1704     try {
  1705         _ident = new_identity(ident);
  1706     }
  1707     catch (bad_alloc&) {
  1708         return E_OUTOFMEMORY;
  1709     }
  1710     catch (exception& ex) {
  1711         return FAIL(ex.what());
  1712     }
  1713 
  1714     assert(_ident);
  1715     if (_ident == NULL)
  1716         return E_OUTOFMEMORY;
  1717 
  1718     string _fpr = utf8_string(fpr);
  1719     PEP_STATUS status = ::set_own_key(session(), _ident, _fpr.c_str());
  1720 
  1721     if (status == PEP_STATUS_OK) {
  1722         copy_identity(result, _ident);
  1723         ::free_identity(_ident);
  1724         return S_OK;
  1725     }
  1726     else {
  1727         ::free_identity(_ident);
  1728         if (status == PEP_OUT_OF_MEMORY)
  1729             return E_OUTOFMEMORY;
  1730         else
  1731             return FAIL(L"SetOwnKey", status);
  1732     }
  1733 
  1734     return S_OK;
  1735 }
  1736 
  1737 STDMETHODIMP CpEpEngine::TrustOwnKey(pEpIdentity * ident)
  1738 {
  1739     assert(ident);
  1740 
  1741     if (!ident)
  1742         return E_INVALIDARG;
  1743 
  1744     ::pEp_identity *_ident;
  1745     try {
  1746         _ident = new_identity(ident);
  1747     }
  1748     catch (bad_alloc&) {
  1749         return E_OUTOFMEMORY;
  1750     }
  1751     catch (exception& ex) {
  1752         return FAIL(ex.what());
  1753     }
  1754 
  1755     assert(_ident);
  1756     if (_ident == NULL)
  1757         return E_OUTOFMEMORY;
  1758 
  1759     PEP_STATUS status = ::trust_own_key(session(), _ident);
  1760 
  1761     ::free_identity(_ident);
  1762 
  1763     if (status == PEP_STATUS_OK)
  1764         return S_OK;
  1765     else if (status == PEP_OUT_OF_MEMORY)
  1766         return E_OUTOFMEMORY;
  1767     else
  1768         return FAIL(L"TrustOwnKey", status);
  1769 }
  1770 
  1771 STDMETHODIMP CpEpEngine::Startup()
  1772 {
  1773     try
  1774     {
  1775         startup<CpEpEngine>(messageToSend, notifyHandshake, this, &CpEpEngine::Startup_sync, &CpEpEngine::Shutdown_sync);
  1776     }
  1777     catch (bad_alloc&) {
  1778         return E_OUTOFMEMORY;
  1779     }
  1780     catch (exception& ex) {
  1781         return FAIL(ex.what());
  1782     }
  1783 
  1784     return S_OK;
  1785 }
  1786 
  1787 STDMETHODIMP CpEpEngine::GetKeyRating(BSTR fpr, pEpComType *commType)
  1788 {
  1789     assert(fpr);
  1790 
  1791     if (!fpr)
  1792         return E_INVALIDARG;
  1793 
  1794     string _fpr = utf8_string(fpr);
  1795 
  1796     PEP_comm_type _commType;
  1797     PEP_STATUS status = ::get_key_rating(session(), _fpr.c_str(), &_commType);
  1798     if (status != PEP_STATUS_OK)
  1799         return FAIL(L"cannot get key rating", status);
  1800 
  1801     *commType = (pEpComType)_commType;
  1802 
  1803     return S_OK;
  1804 }
  1805 
  1806 STDMETHODIMP CpEpEngine::GetKeyRatingForUser(BSTR userId, BSTR fpr, pEpRating *rating)
  1807 {
  1808     assert(userId);
  1809     assert(fpr);
  1810 
  1811     if (!(userId && fpr))
  1812         return E_INVALIDARG;
  1813 
  1814     string user_id = utf8_string(userId);
  1815     string _fpr = utf8_string(fpr);
  1816 
  1817     PEP_rating _rating;
  1818     PEP_STATUS status = ::get_key_rating_for_user(session(), user_id.c_str(), _fpr.c_str(), &_rating);
  1819     if (status != PEP_STATUS_OK)
  1820         return FAIL(L"cannot get key rating for user", status);
  1821 
  1822     *rating = (pEpRating)_rating;
  1823 
  1824     return S_OK;
  1825 }
  1826 
  1827 STDMETHODIMP CpEpEngine::DeliverHandshakeResult(enum SyncHandshakeResult result, SAFEARRAY *identities_sharing)
  1828 {
  1829     sync_handshake_result _result = (sync_handshake_result)result;
  1830     identity_list *_identities_sharing = NULL;
  1831     if (identities_sharing)
  1832     {
  1833         try {
  1834             _identities_sharing = identities(identities_sharing);
  1835         }
  1836         catch (bad_alloc&) {
  1837             return E_OUTOFMEMORY;
  1838         }
  1839     }
  1840 
  1841     PEP_STATUS status = ::deliverHandshakeResult(session(), _result, _identities_sharing);
  1842     free_identity_list(_identities_sharing);
  1843     switch (status) {
  1844     case PEP_STATUS_OK:
  1845         break;
  1846     case PEP_OUT_OF_MEMORY:
  1847         return E_OUTOFMEMORY;
  1848     default:
  1849         return FAIL(L"deliverHandshakeResult is reporting an error", status);
  1850     }
  1851 
  1852     return S_OK;
  1853 }
  1854 
  1855 STDMETHODIMP CpEpEngine::PERToXERSyncMessage(TextMessage *msg, BSTR * xer)
  1856 {
  1857     assert(msg);
  1858 
  1859     if (!msg)
  1860         return E_INVALIDARG;
  1861 
  1862     ::message *_msg = NULL;
  1863     try {
  1864         _msg = text_message_to_C(msg);
  1865     }
  1866     catch (bad_alloc&) {
  1867         return E_OUTOFMEMORY;
  1868     }
  1869     catch (exception& ex) {
  1870         return FAIL(ex.what());
  1871     }
  1872 
  1873     char* text;
  1874     char* val = _msg->attachments->value;
  1875 
  1876     PEP_STATUS status = ::PER_to_XER_Sync_msg(val, strlen(val), &text);
  1877     free_message(_msg);
  1878 
  1879     if (status != PEP_STATUS_OK)
  1880         return FAIL(L"cannot get XER", status);
  1881 
  1882     *xer = utf16_bstr(text);
  1883     pEp_free(text);
  1884 
  1885     return S_OK;
  1886 }
  1887 
  1888 STDMETHODIMP CpEpEngine::DisableIdentityForSync(struct pEpIdentity * ident)
  1889 {
  1890     assert(ident);
  1891 
  1892     if (!ident)
  1893         return E_INVALIDARG;
  1894 
  1895     ::pEp_identity *_ident;
  1896     try {
  1897         _ident = new_identity(ident);
  1898     }
  1899     catch (bad_alloc&) {
  1900         return E_OUTOFMEMORY;
  1901     }
  1902     catch (exception& ex) {
  1903         return FAIL(ex.what());
  1904     }
  1905 
  1906     assert(_ident);
  1907     if (_ident == NULL)
  1908         return E_OUTOFMEMORY;
  1909 
  1910     PEP_STATUS status = ::disable_identity_for_sync(session(), _ident);
  1911 
  1912     ::free_identity(_ident);
  1913 
  1914     if (status == PEP_STATUS_OK)
  1915         return S_OK;
  1916     else if (status == PEP_OUT_OF_MEMORY)
  1917         return E_OUTOFMEMORY;
  1918     else
  1919         return FAIL(L"DisableIdentityForSync", status);
  1920 }
  1921 
  1922 STDMETHODIMP CpEpEngine::EnableIdentityForSync(struct pEpIdentity * ident)
  1923 {
  1924     assert(ident);
  1925 
  1926     if (!ident)
  1927         return E_INVALIDARG;
  1928 
  1929     ::pEp_identity *_ident;
  1930     try {
  1931         _ident = new_identity(ident);
  1932     }
  1933     catch (bad_alloc&) {
  1934         return E_OUTOFMEMORY;
  1935     }
  1936     catch (exception& ex) {
  1937         return FAIL(ex.what());
  1938     }
  1939 
  1940     assert(_ident);
  1941     if (_ident == NULL)
  1942         return E_OUTOFMEMORY;
  1943 
  1944     PEP_STATUS status = ::enable_identity_for_sync(session(), _ident);
  1945 
  1946     ::free_identity(_ident);
  1947 
  1948     if (status == PEP_STATUS_OK)
  1949         return S_OK;
  1950     else if (status == PEP_OUT_OF_MEMORY)
  1951         return E_OUTOFMEMORY;
  1952     else
  1953         return FAIL(L"EnableIdentityForSync", status);
  1954 }
  1955 
  1956 STDMETHODIMP CpEpEngine::PerMachineDirectory(BSTR * directory)
  1957 {
  1958     assert(directory);
  1959 
  1960     if (!directory)
  1961         return E_INVALIDARG;
  1962 
  1963     const char *_directory = ::per_machine_directory();
  1964 
  1965     if (_directory == NULL)
  1966         return FAIL(L"PerMachineDirectory: _directory is NULL");
  1967 
  1968     *directory = utf16_bstr(_directory);
  1969 
  1970     return S_OK;
  1971 }
  1972 
  1973 STDMETHODIMP CpEpEngine::PerUserDirectory(BSTR * directory)
  1974 {
  1975     assert(directory);
  1976 
  1977     if (!directory)
  1978         return E_INVALIDARG;
  1979 
  1980     const char *_directory = ::per_user_directory();
  1981 
  1982     if (_directory == NULL)
  1983         return FAIL(L"PerUserDirectory: _directory is NULL");
  1984 
  1985     *directory = utf16_bstr(_directory);
  1986 
  1987     return S_OK;
  1988 }
  1989 
  1990 STDMETHODIMP CpEpEngine::RatingFromCommType(pEpComType commType, pEpRating * rating) 
  1991 {
  1992     PEP_comm_type _comm_type = (PEP_comm_type)commType;
  1993     PEP_rating _rating = ::rating_from_comm_type(_comm_type);
  1994 
  1995     *rating = (pEpRating)_rating;
  1996 
  1997     return S_OK;
  1998 }
  1999 
  2000 STDMETHODIMP CpEpEngine::GetIsSyncRunning(VARIANT_BOOL *running)
  2001 {
  2002     *running = pEp::Adapter::is_sync_running();
  2003 
  2004     return S_OK;
  2005 }
  2006 
  2007 STDMETHODIMP CpEpEngine::ShutDownSync()
  2008 {
  2009     pEp::Adapter::shutdown();
  2010 
  2011     return S_OK;
  2012 }