CpEpEngine.cpp
author Thomas
Thu, 14 Nov 2019 16:18:44 +0100
branchsync
changeset 373 5b10b2200c91
parent 371 228f5551c75b
child 374 ccc4c88f280e
permissions -rw-r--r--
Adjust HandshakeSignal enum to engine changes
     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     PEP_STATUS status = ::key_reset_all_own_keys(session());
   830 
   831     if (status == PEP_OUT_OF_MEMORY)
   832         return E_OUTOFMEMORY;
   833 
   834     if (status != PEP_STATUS_OK)
   835         return FAIL(L"cannot reset all own keys", status);
   836 
   837     return S_OK;
   838 }
   839 
   840 STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
   841 {
   842     ::pEp_identity *_ident;
   843 
   844     assert(ident);
   845 
   846     if (!ident)
   847         return E_INVALIDARG;
   848 
   849     try {
   850         _ident = new_identity(ident);
   851     }
   852     catch (bad_alloc&) {
   853         return E_OUTOFMEMORY;
   854     }
   855     catch (exception& ex) {
   856         return FAIL(ex.what());;
   857     }
   858 
   859     PEP_STATUS status = ::key_reset_trust(session(), _ident);
   860     free_identity(_ident);
   861 
   862     if (status == PEP_OUT_OF_MEMORY)
   863         return E_OUTOFMEMORY;
   864 
   865     if (status == PEP_KEY_NOT_FOUND)
   866         return FAIL(L"key not found");
   867 
   868     if (status != PEP_STATUS_OK)
   869         return FAIL(L"cannot reset trust", status);
   870 
   871     return S_OK;
   872 }
   873 
   874 int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
   875 {
   876     assert(ident);
   877     assert(management);
   878     if (!(ident && management))
   879         return -1;
   880 
   881     CpEpEngine *me = (CpEpEngine *)management;
   882 
   883     if (me->identity_queue.load() == NULL)
   884         return 0;
   885 
   886     try {
   887         me->identity_queue.load()->push_back(ident);
   888     }
   889     catch (exception&) {
   890         return -1;
   891     }
   892 
   893     return 0;
   894 }
   895 
   896 ::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
   897 {
   898     assert(management);
   899     if (!management)
   900         return NULL;
   901 
   902     identity_queue_t *iq = (identity_queue_t *)management;
   903 
   904     do /* poll queue */ {
   905         if (iq->size())
   906             break;
   907         ::Sleep(100);
   908     } while (true);
   909 
   910     ::pEp_identity *_ident;
   911     pEp_identity_cpp& ident = iq->front();
   912 
   913     if (ident.address.size() == 0)
   914         return NULL;
   915 
   916     _ident = ident.to_pEp_identity();
   917     iq->pop_front();
   918 
   919     return _ident;
   920 }
   921 
   922 static IpEpEngineCallbacks * _unmarshaled_consumer(CpEpEngine::callback_container::Container::iterator p)
   923 {
   924     if (!p->cdata && p->pdata && p->pdata->marshaled) {
   925         HRESULT r = CoGetInterfaceAndReleaseStream(p->pdata->marshaled, IID_IpEpEngineCallbacks, (LPVOID*) &p->cdata);
   926         if (!SUCCEEDED(r))
   927             throw runtime_error("_unmarshaled_consumer(): CoGetInterfaceAndReleaseStream() failed");
   928         p->pdata->marshaled = nullptr;
   929     }
   930     else if (p->cdata && !p->pdata) {
   931         p->cdata->Release();
   932         p->cdata = nullptr;
   933     }
   934 
   935     return p->cdata;
   936 }
   937 
   938 PEP_STATUS CpEpEngine::messageToSend(message *msg)
   939 {
   940     assert(msg);
   941     if (!msg)
   942         return PEP_ILLEGAL_VALUE;
   943 
   944     bool in_sync = on_sync_thread();
   945 
   946     for (auto p = sync_callbacks.begin(); p != sync_callbacks.end(); ++p) {
   947         IpEpEngineCallbacks *cb = in_sync ? _unmarshaled_consumer(p) : p->pdata->unmarshaled;
   948 
   949         if (cb) {
   950             TextMessage _msg;
   951             memset(&_msg, 0, sizeof(TextMessage));
   952 
   953             text_message_from_C(&_msg, msg);
   954             HRESULT r = cb->MessageToSend(&_msg);
   955             assert(r == S_OK);
   956             clear_text_message(&_msg);
   957             if (r == E_OUTOFMEMORY)
   958                 return PEP_OUT_OF_MEMORY;
   959             if (r != S_OK)
   960                 return PEP_UNKNOWN_ERROR;
   961         }
   962     }
   963 
   964     sync_callbacks.compact();
   965 
   966     return PEP_STATUS_OK;
   967 }
   968 
   969 PEP_STATUS CpEpEngine::notifyHandshake(::pEp_identity *self, ::pEp_identity *partner, sync_handshake_signal signal)
   970 {
   971     assert(self && partner);
   972     if (!(self && partner))
   973         return PEP_ILLEGAL_VALUE;
   974 
   975     bool in_sync = on_sync_thread();
   976 
   977     // fire all of them
   978     for (auto p = sync_callbacks.begin(); p != sync_callbacks.end(); ++p) {
   979         IpEpEngineCallbacks *cb = in_sync ? _unmarshaled_consumer(p) : p->pdata->unmarshaled;
   980 
   981         if (cb) {
   982             pEpIdentity _self;
   983             copy_identity(&_self, self);
   984             pEpIdentity _partner;
   985             copy_identity(&_partner, partner);
   986 
   987             SyncHandshakeSignal _signal = (SyncHandshakeSignal)signal;
   988             HRESULT r = cb->NotifyHandshake(&_self, &_partner, _signal);
   989             assert(r == S_OK);
   990             clear_identity_s(_self);
   991             clear_identity_s(_partner);
   992             if (r == E_OUTOFMEMORY)
   993                 return PEP_OUT_OF_MEMORY;
   994         }
   995     }
   996 
   997     sync_callbacks.compact();
   998 
   999     return PEP_STATUS_OK;
  1000 }
  1001 
  1002 STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
  1003 {
  1004     assert(fpr);
  1005     if (!fpr)
  1006         return E_INVALIDARG;
  1007 
  1008     string _fpr = utf8_string(fpr);
  1009     PEP_STATUS status = ::blacklist_add(session(), _fpr.c_str());
  1010     assert(status == PEP_STATUS_OK);
  1011     if (status != PEP_STATUS_OK)
  1012         return FAIL(L"blacklist_add failed in pEp engine", status);
  1013 
  1014     return S_OK;
  1015 }
  1016 
  1017 STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
  1018 {
  1019     assert(fpr);
  1020     if (!fpr)
  1021         return E_INVALIDARG;
  1022 
  1023     string _fpr = utf8_string(fpr);
  1024     PEP_STATUS status = ::blacklist_delete(session(), _fpr.c_str());
  1025     assert(status == PEP_STATUS_OK);
  1026     if (status != PEP_STATUS_OK)
  1027         return FAIL(L"blacklist_delete failed in pEp engine", status);
  1028 
  1029     return S_OK;
  1030 }
  1031 
  1032 STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
  1033 {
  1034     assert(fpr);
  1035     assert(listed);
  1036 
  1037     if (!(fpr && listed))
  1038         return E_INVALIDARG;
  1039 
  1040     string _fpr = utf8_string(fpr);
  1041     bool result;
  1042     PEP_STATUS status = ::blacklist_is_listed(session(), _fpr.c_str(), &result);
  1043     assert(status == PEP_STATUS_OK);
  1044     if (status != PEP_STATUS_OK)
  1045         return FAIL(L"blacklist_is_listed failed in pEp engine", status);
  1046 
  1047     *listed = result ? VARIANT_TRUE : VARIANT_FALSE;
  1048     return S_OK;
  1049 }
  1050 
  1051 STDMETHODIMP CpEpEngine::BlacklistRetrieve(SAFEARRAY **blacklist)
  1052 {
  1053     assert(blacklist);
  1054 
  1055     if (!blacklist)
  1056         return E_INVALIDARG;
  1057 
  1058     ::stringlist_t *_blacklist = NULL;
  1059     PEP_STATUS status = ::blacklist_retrieve(session(), &_blacklist);
  1060     assert(status == PEP_STATUS_OK);
  1061     if (status != PEP_STATUS_OK)
  1062         return FAIL(L"blacklist_retrieve failed in pEp engine", status);
  1063     assert(_blacklist);
  1064 
  1065     *blacklist = string_array(_blacklist);
  1066     ::free_stringlist(_blacklist);
  1067     return S_OK;
  1068 }
  1069 
  1070 HRESULT CpEpEngine::error(_bstr_t msg)
  1071 {
  1072     _bstr_t helpFile = L"";
  1073     _bstr_t source = L"pEp COM Adapter";
  1074 
  1075     ICreateErrorInfo *cei;
  1076     if (SUCCEEDED(CreateErrorInfo(&cei))) {
  1077         cei->SetDescription(msg);
  1078         cei->SetGUID(__uuidof(IpEpEngine));
  1079         cei->SetHelpContext(0);
  1080         cei->SetHelpFile(helpFile);
  1081         cei->SetSource(source);
  1082 
  1083         IErrorInfo *errinfo;
  1084         if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
  1085             SetErrorInfo(0, errinfo);
  1086             errinfo->Release();
  1087         }
  1088         cei->Release();
  1089     }
  1090     return E_FAIL;
  1091 }
  1092 
  1093 HRESULT CpEpEngine::error(_bstr_t msg, PEP_STATUS status)
  1094 {
  1095     std::stringstream stream;
  1096     stream << msg;
  1097     stream << ": ";
  1098     stream << pEp_status_to_string(status);
  1099     stream << std::hex << " (" << status << ")";
  1100 
  1101     error(stream.str().c_str());
  1102 
  1103     if (status == ::PEP_OUT_OF_MEMORY)
  1104         return E_OUTOFMEMORY;
  1105 
  1106     return MAKE_HRESULT(1, FACILITY_ITF, (0xFFFF & status));
  1107 }
  1108 
  1109 STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags, pEpEncFormat encFormat)
  1110 {
  1111     assert(src);
  1112     assert(dst);
  1113 
  1114     if (!(src && dst))
  1115         return E_INVALIDARG;
  1116 
  1117     ::message *_src = NULL;
  1118     try {
  1119         _src = text_message_to_C(src);
  1120     }
  1121     catch (bad_alloc&) {
  1122         return E_OUTOFMEMORY;
  1123     }
  1124     catch (exception& ex) {
  1125         return FAIL(ex.what());
  1126     }
  1127 
  1128     _PEP_enc_format _encFormat = (_PEP_enc_format)encFormat;
  1129 
  1130     // COM-19: Initialize msg_dst to NULL, or we end up calling
  1131     // free_message() below with a pointer to random garbage in
  1132     // case of an error in encrypt_message().
  1133     ::message *msg_dst = NULL;
  1134     ::stringlist_t *_extra = new_stringlist(extra); // can cope with NULL
  1135 
  1136     // _PEP_enc_format used to be intentionally hardcoded to PEP_enc_PEP:
  1137     // Since COM-74, this has been changed to an explicit parameter, to allow the engine to attach
  1138     // the keys and headers to outgoing, unencrypted messages.
  1139     PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
  1140     PEP_STATUS status = ::encrypt_message(session(), _src, _extra, &msg_dst, _encFormat, engineFlags);
  1141     ::free_stringlist(_extra);
  1142 
  1143     if (status == PEP_STATUS_OK)
  1144         text_message_from_C(dst, msg_dst);
  1145     else
  1146         text_message_from_C(dst, _src);
  1147 
  1148     ::free_message(msg_dst);
  1149     ::free_message(_src);
  1150 
  1151     if (status == PEP_OUT_OF_MEMORY)
  1152         return E_OUTOFMEMORY;
  1153 
  1154     // COM-41: Enhanced PEP status handling
  1155     if ((status != PEP_STATUS_OK) && (status < PEP_UNENCRYPTED || status >= PEP_TRUSTWORD_NOT_FOUND))
  1156         return FAIL("Failure to encrypt message", status);
  1157 
  1158     // Statii like PEP_UNENCRYPTED due to no private key
  1159     // should not be a catastrophic failure here. Using S_FALSE
  1160     // still allows clients to differentiate with S_OK,
  1161     // although this does not work out of the box with
  1162     // the standard .NET mapping of COM.
  1163     if (status != PEP_STATUS_OK)
  1164         return S_FALSE;
  1165 
  1166     return S_OK;
  1167 }
  1168 
  1169 STDMETHODIMP CpEpEngine::EncryptMessageAndAddPrivKey(TextMessage * src, TextMessage * dst, BSTR to_fpr, pEpEncryptFlags flags, pEpEncFormat encFormat)
  1170 {
  1171     assert(src);
  1172     assert(dst);
  1173     assert(to_fpr);
  1174 
  1175     if (!(src && dst))
  1176         return E_INVALIDARG;
  1177 
  1178     ::message *_src = NULL;
  1179     try {
  1180         _src = text_message_to_C(src);
  1181     }
  1182     catch (bad_alloc&) {
  1183         return E_OUTOFMEMORY;
  1184     }
  1185     catch (exception& ex) {
  1186         return FAIL(ex.what());
  1187     }
  1188 
  1189     _PEP_enc_format _encFormat = (_PEP_enc_format)encFormat;
  1190 
  1191     // COM-19: Initialize msg_dst to NULL, or we end up calling
  1192     // free_message() below with a pointer to random garbage in
  1193     // case of an error in encrypt_message().
  1194     ::message *msg_dst = NULL;
  1195 
  1196     string _to_fpr = utf8_string(to_fpr);
  1197                                                     // _PEP_enc_format used to be intentionally hardcoded to PEP_enc_PEP:
  1198                                                     // Since COM-74, this has been changed to an explicit parameter, to allow the engine to attach
  1199                                                     // the keys and headers to outgoing, unencrypted messages.
  1200     PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
  1201     PEP_STATUS status = ::encrypt_message_and_add_priv_key(session(), _src, &msg_dst, _to_fpr.c_str(), _encFormat, engineFlags);
  1202 
  1203     if (status == PEP_STATUS_OK)
  1204         text_message_from_C(dst, msg_dst);
  1205     else
  1206         text_message_from_C(dst, _src);
  1207 
  1208     ::free_message(msg_dst);
  1209     ::free_message(_src);
  1210 
  1211     if (status == PEP_OUT_OF_MEMORY)
  1212         return E_OUTOFMEMORY;
  1213 
  1214     // COM-41: Enhanced PEP status handling
  1215     if ((status != PEP_STATUS_OK) && (status < PEP_UNENCRYPTED || status >= PEP_TRUSTWORD_NOT_FOUND))
  1216         return FAIL("Failure to encrypt message", status);
  1217 
  1218     // Statii like PEP_UNENCRYPTED due to no private key
  1219     // should not be a catastrophic failure here. Using S_FALSE
  1220     // still allows clients to differentiate with S_OK,
  1221     // although this does not work out of the box with
  1222     // the standard .NET mapping of COM.
  1223     if (status != PEP_STATUS_OK)
  1224         return S_FALSE;
  1225 
  1226     return S_OK;
  1227 }
  1228 
  1229 STDMETHODIMP CpEpEngine::EncryptMessageForSelf(pEpIdentity * targetId, TextMessage * src,
  1230     /* [in] */ SAFEARRAY *extra, TextMessage * dst, pEpEncryptFlags flags)
  1231 {
  1232     assert(targetId);
  1233     assert(src);
  1234     assert(dst);
  1235 
  1236     if (!(targetId && src && dst))
  1237         return E_INVALIDARG;
  1238 
  1239     PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
  1240 
  1241     ::pEp_identity *_target_id = new_identity(targetId);
  1242 
  1243     ::message *_src = NULL;
  1244     try {
  1245         _src = text_message_to_C(src);
  1246     }
  1247     catch (bad_alloc&) {
  1248         return E_OUTOFMEMORY;
  1249     }
  1250     catch (exception& ex) {
  1251         return FAIL(ex.what());
  1252     }
  1253 
  1254     ::stringlist_t* _extra = NULL;
  1255     HRESULT result = S_OK;
  1256     ::message *msg_dst = NULL;
  1257     PEP_STATUS status = PEP_STATUS_OK;
  1258 
  1259     try {
  1260         if (extra) {
  1261             _extra = new_stringlist(extra);
  1262         }
  1263 
  1264         // COM-19: Initialize msg_dst to NULL, or we end up calling
  1265         // free_message() below with a pointer to random garbage in
  1266         // case of an error in encrypt_message_for_self().
  1267         status = ::encrypt_message_for_self(session(), _target_id, _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
  1268 
  1269         if (status == PEP_STATUS_OK)
  1270             text_message_from_C(dst, msg_dst);
  1271         else
  1272             text_message_from_C(dst, _src);
  1273     } catch (bad_alloc&) {
  1274         result = E_OUTOFMEMORY;
  1275     }
  1276     catch (exception& ex) {
  1277         result = FAIL(ex.what());
  1278     }
  1279 
  1280     ::free_message(msg_dst);
  1281     ::free_message(_src);
  1282     ::free_identity(_target_id);
  1283     ::free_stringlist(_extra);
  1284 
  1285     if (status == PEP_OUT_OF_MEMORY)
  1286         return E_OUTOFMEMORY;
  1287 
  1288     // Different to encrypt_message, this should never fail (we ought to always
  1289     // have a private key for ourself).#
  1290     if (status != PEP_STATUS_OK)
  1291         return FAIL("Failure to encrypt message", status);
  1292 
  1293     return result;
  1294 }
  1295 
  1296 STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
  1297 {
  1298     assert(src);
  1299     assert(dst);
  1300     assert(keylist);
  1301     assert(flags);
  1302     assert(rating);
  1303 
  1304     if (!(src && dst && keylist && flags && rating))
  1305         return E_INVALIDARG;
  1306 
  1307     *rating = pEpRatingUndefined;
  1308 
  1309     ::message *_src = NULL;
  1310     try {
  1311         _src = text_message_to_C(src);
  1312     }
  1313     catch (bad_alloc&) {
  1314         return E_OUTOFMEMORY;
  1315     }
  1316     catch (exception& ex) {
  1317         return FAIL(ex.what());
  1318     }
  1319     ::message *msg_dst = NULL;
  1320     ::stringlist_t *_keylist = new_stringlist(*keylist);
  1321     ::PEP_rating _rating;
  1322 
  1323     PEP_decrypt_flags_t engineflags = (PEP_decrypt_flags_t) *flags;
  1324     PEP_STATUS status = ::decrypt_message(session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
  1325 
  1326     *flags = (pEpDecryptFlags)engineflags;
  1327 
  1328     if (_src)
  1329         text_message_from_C(src, _src);
  1330 
  1331     if (msg_dst)
  1332         text_message_from_C(dst, msg_dst);
  1333 
  1334     ::free_message(_src);
  1335     ::free_message(msg_dst);
  1336 
  1337     if (_keylist) {
  1338         *keylist = string_array(_keylist);
  1339         free_stringlist(_keylist);
  1340     }
  1341 
  1342     *rating = (pEpRating)_rating;
  1343 
  1344     return S_OK;
  1345 }
  1346 
  1347 STDMETHODIMP CpEpEngine::ReEvaluateMessageRating(TextMessage * msg, SAFEARRAY * x_KeyList, pEpRating x_EncStatus, pEpRating *rating)
  1348 {
  1349     assert(msg);
  1350     assert(x_EncStatus != PEP_rating_undefined);
  1351     assert(rating);
  1352 
  1353     if (!(msg && x_EncStatus != PEP_rating_undefined && rating))
  1354         return E_INVALIDARG;
  1355 
  1356     *rating = pEpRatingUndefined;
  1357 
  1358     ::message *_msg = NULL;
  1359     try {
  1360         _msg = text_message_to_C(msg);
  1361     }
  1362     catch (bad_alloc&) {
  1363         return E_OUTOFMEMORY;
  1364     }
  1365     catch (exception& ex) {
  1366         return FAIL(ex.what());
  1367     }
  1368 
  1369     ::stringlist_t *_keylist = new_stringlist(x_KeyList);
  1370     ::PEP_rating _rating = PEP_rating_undefined;
  1371 
  1372     PEP_STATUS status = ::re_evaluate_message_rating(session(), _msg, _keylist, (PEP_rating)x_EncStatus, &_rating);
  1373 
  1374     ::free_stringlist(_keylist);
  1375     ::free_message(_msg);
  1376 
  1377     *rating = (pEpRating)_rating;
  1378 
  1379     return S_OK;
  1380 }
  1381 
  1382 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
  1383 {
  1384     assert(msg);
  1385     assert(pVal);
  1386 
  1387     if (!(msg  && pVal))
  1388         return E_INVALIDARG;
  1389 
  1390     ::message *_msg = NULL;
  1391     try {
  1392         _msg = text_message_to_C(msg);
  1393     }
  1394     catch (bad_alloc&) {
  1395         return E_OUTOFMEMORY;
  1396     }
  1397     catch (exception& ex) {
  1398         return FAIL(ex.what());
  1399     }
  1400 
  1401     PEP_rating _rating;
  1402     PEP_STATUS status = ::outgoing_message_rating(session(), _msg, &_rating);
  1403     if (status != PEP_STATUS_OK)
  1404         return FAIL(L"cannot get message rating", status);
  1405 
  1406     *pVal = (pEpRating)_rating;
  1407     return S_OK;
  1408 }
  1409 
  1410 STDMETHODIMP CpEpEngine::OutgoingMessageRatingPreview(TextMessage *msg, pEpRating * pVal)
  1411 {
  1412     assert(msg);
  1413     assert(pVal);
  1414 
  1415     if (!(msg  && pVal))
  1416         return E_INVALIDARG;
  1417 
  1418     ::message *_msg = NULL;
  1419     try {
  1420         _msg = text_message_to_C(msg);
  1421     }
  1422     catch (bad_alloc&) {
  1423         return E_OUTOFMEMORY;
  1424     }
  1425     catch (exception& ex) {
  1426         return FAIL(ex.what());
  1427     }
  1428 
  1429     PEP_rating _rating;
  1430     PEP_STATUS status = ::outgoing_message_rating_preview(session(), _msg, &_rating);
  1431     if (status != PEP_STATUS_OK)
  1432         return FAIL(L"cannot get message rating", status);
  1433 
  1434     *pVal = (pEpRating)_rating;
  1435     return S_OK;
  1436 }
  1437 
  1438 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
  1439 {
  1440     ::pEp_identity *_ident;
  1441 
  1442     assert(ident);
  1443     assert(pVal);
  1444 
  1445     if (!(ident  && pVal))
  1446         return E_INVALIDARG;
  1447 
  1448     try {
  1449         _ident = new_identity(ident);
  1450     }
  1451     catch (bad_alloc&) {
  1452         return E_OUTOFMEMORY;
  1453     }
  1454     catch (exception& ex) {
  1455         return FAIL(ex.what());;
  1456     }
  1457 
  1458     PEP_rating _rating;
  1459     PEP_STATUS status = ::identity_rating(session(), _ident, &_rating);
  1460     free_identity(_ident);
  1461 
  1462     if (status != PEP_STATUS_OK)
  1463         return FAIL(L"cannot get message color", status);
  1464 
  1465     *pVal = (pEpRating)_rating;
  1466     return S_OK;
  1467 }
  1468 
  1469 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
  1470 {
  1471     assert(pVal);
  1472 
  1473     if (!pVal)
  1474         return E_INVALIDARG;
  1475 
  1476     PEP_rating engineRating = (PEP_rating)rating;
  1477     PEP_color _color = ::color_from_rating(engineRating);
  1478 
  1479     *pVal = (pEpColor)_color;
  1480      
  1481     return S_OK;
  1482 }
  1483 
  1484 STDMETHODIMP CpEpEngine::OwnIdentitiesRetrieve(LPSAFEARRAY* ownIdentities)
  1485 {
  1486     assert(ownIdentities);
  1487     if (!ownIdentities)
  1488         return E_INVALIDARG;
  1489 
  1490     *ownIdentities = nullptr;
  1491 
  1492     ::identity_list *il = nullptr;
  1493     PEP_STATUS status = ::own_identities_retrieve(session(), &il);
  1494     if (status == PEP_OUT_OF_MEMORY) {
  1495         return E_OUTOFMEMORY;
  1496     }
  1497     else if (status != PEP_STATUS_OK)
  1498     {
  1499         return FAIL(_T("OwnIdentitiesRetrieve"), status);
  1500     }
  1501 
  1502     SAFEARRAY * _own_identities = nullptr;
  1503     try {
  1504         _own_identities = array_from_C<pEpIdentity, identity_list>(il);
  1505     }
  1506     catch (exception& ex)
  1507     {
  1508         ::free_identity_list(il);
  1509         try {
  1510             dynamic_cast<bad_alloc&>(ex);
  1511         }
  1512         catch (bad_cast&)
  1513         {
  1514             return FAIL(ex.what());
  1515         }
  1516         return E_OUTOFMEMORY;
  1517     }
  1518     free_identity_list(il);
  1519 
  1520     *ownIdentities = _own_identities;
  1521     return S_OK;
  1522 }
  1523 
  1524 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
  1525 {
  1526     ::pEp_identity *_ident;
  1527 
  1528     assert(ident);
  1529     assert(result);
  1530 
  1531     if (!ident || !result)
  1532         return E_INVALIDARG;
  1533 
  1534     try {
  1535         _ident = new_identity(ident);
  1536     }
  1537     catch (bad_alloc&) {
  1538         return E_OUTOFMEMORY;
  1539     }
  1540     catch (exception& ex) {
  1541         return FAIL(ex.what());;
  1542     }
  1543 
  1544     if (verbose_mode) {
  1545         stringstream ss;
  1546         ss << "TrustPersonalKey called with ";
  1547         ss << utf8_string(ident->Address);
  1548         ss << L": ";
  1549         ss << ident->CommType;
  1550         verbose(ss.str());
  1551     }
  1552 
  1553     PEP_STATUS status = ::trust_personal_key(session(), _ident);
  1554 
  1555     if (verbose_mode) {
  1556         stringstream ss;
  1557         ss << "result ";
  1558         ss << status;
  1559         ss << " for ";
  1560         ss << _ident->address;
  1561         ss << L": ";
  1562         ss << _ident->comm_type;
  1563         verbose(ss.str());
  1564     }
  1565 
  1566     if (status == PEP_STATUS_OK)
  1567         copy_identity(result, _ident);
  1568 
  1569     free_identity(_ident);
  1570     if (status == PEP_OUT_OF_MEMORY)
  1571         return E_OUTOFMEMORY;
  1572     else if (status != PEP_STATUS_OK)
  1573         return FAIL(L"failure while executing TrustPersonalKey()", status);
  1574 
  1575     return S_OK;
  1576 }
  1577 
  1578 // Force an update check now
  1579 STDMETHODIMP CpEpEngine::UpdateNow(BSTR productCode, VARIANT_BOOL *didUpdate)
  1580 {
  1581     BOOL result = FALSE;
  1582 
  1583     try
  1584     {
  1585         _bstr_t pc(productCode);
  1586         wstring _pc = pc;
  1587 
  1588         auto products = pEp::GateKeeper::gatekeeper()->registered_products();
  1589         for (auto p = products.begin(); p != products.end(); ++p) {
  1590             if (_pc == p->first) {
  1591                 result = pEp::GateKeeper::gatekeeper()->update_product(*p);
  1592                 break;
  1593             }
  1594         }
  1595     }
  1596     catch (bad_alloc&) {
  1597         return E_OUTOFMEMORY;
  1598     }
  1599     catch (exception& ex) {
  1600         return FAIL(ex.what());;
  1601     }
  1602 
  1603     *didUpdate = result;
  1604     return S_OK;
  1605 }
  1606 
  1607 // Event callbacks
  1608 
  1609 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
  1610 {
  1611     // check for valid parameter
  1612     if (!new_callbacks)
  1613         return E_INVALIDARG;
  1614 
  1615     // don't allow double registration.
  1616     if (this->client_callbacks)
  1617         return E_ILLEGAL_STATE_CHANGE;
  1618 
  1619     this->client_callbacks = new_callbacks;
  1620     new_callbacks->AddRef();
  1621 
  1622     // provide callbacks to sync
  1623     LPSTREAM marshaled_callbacks = nullptr;
  1624     auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
  1625     assert(SUCCEEDED(result));
  1626     assert(marshaled_callbacks);
  1627     sync_callbacks.insert(new MarshaledCallbacks({ this->client_callbacks, marshaled_callbacks }));
  1628 
  1629     return S_OK;
  1630 }
  1631 
  1632 STDMETHODIMP CpEpEngine::UnregisterCallbacks()
  1633 {
  1634     // don't allow double deregistration.
  1635     // S_FALSE still is no error (as double deregistration is not fatal).
  1636     if (!this->client_callbacks)
  1637         return S_FALSE;
  1638 
  1639     for (auto p = sync_callbacks.begin(); p != sync_callbacks.end(); ++p) {
  1640         if (p->pdata && p->pdata->unmarshaled == this->client_callbacks) {
  1641             if (p->pdata->marshaled)
  1642                 p->pdata->marshaled->Release();
  1643             if (p->pdata->unmarshaled)
  1644                 p->pdata->unmarshaled->Release();
  1645             delete p->pdata;
  1646             p->pdata = nullptr;
  1647             break;
  1648         }
  1649     }
  1650 
  1651     this->client_callbacks->Release();
  1652     this->client_callbacks = nullptr;
  1653 
  1654     return S_OK;
  1655 }
  1656 
  1657 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
  1658     assert(keyinfo_list);
  1659 
  1660     if (keyinfo_list == NULL)
  1661         return E_INVALIDARG;
  1662 
  1663     string _pattern = "";
  1664     if (search_pattern)
  1665         _pattern = utf8_string(search_pattern);
  1666     ::stringpair_list_t* _keyinfo_list = NULL;
  1667 
  1668     PEP_STATUS status = ::OpenPGP_list_keyinfo(session(), _pattern.c_str(), &_keyinfo_list);
  1669     assert(status != PEP_OUT_OF_MEMORY);
  1670     if (status == PEP_OUT_OF_MEMORY)
  1671         return E_OUTOFMEMORY;
  1672 
  1673     if (status != PEP_STATUS_OK)
  1674         return FAIL(L"OpenPGP_list_keyinfo", status);
  1675 
  1676     if (_keyinfo_list && _keyinfo_list->value) {
  1677         ::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
  1678     }
  1679     else {
  1680         ::free_stringpair_list(_keyinfo_list);
  1681         return FAIL(L"OpenPGP_list_keyinfo: no keys found");
  1682     }
  1683 
  1684     ::free_stringpair_list(_keyinfo_list);
  1685     return S_OK;
  1686 
  1687 }
  1688 
  1689 STDMETHODIMP CpEpEngine::SetOwnKey(pEpIdentity * ident, BSTR fpr, struct pEpIdentity *result)
  1690 {
  1691     assert(ident);
  1692     assert(result);
  1693     assert(fpr);
  1694 
  1695     if (!(ident && result))
  1696         return E_INVALIDARG;
  1697 
  1698     ::pEp_identity *_ident;
  1699     try {
  1700         _ident = new_identity(ident);
  1701     }
  1702     catch (bad_alloc&) {
  1703         return E_OUTOFMEMORY;
  1704     }
  1705     catch (exception& ex) {
  1706         return FAIL(ex.what());
  1707     }
  1708 
  1709     assert(_ident);
  1710     if (_ident == NULL)
  1711         return E_OUTOFMEMORY;
  1712 
  1713     string _fpr = utf8_string(fpr);
  1714     PEP_STATUS status = ::set_own_key(session(), _ident, _fpr.c_str());
  1715 
  1716     if (status == PEP_STATUS_OK) {
  1717         copy_identity(result, _ident);
  1718         ::free_identity(_ident);
  1719         return S_OK;
  1720     }
  1721     else {
  1722         ::free_identity(_ident);
  1723         if (status == PEP_OUT_OF_MEMORY)
  1724             return E_OUTOFMEMORY;
  1725         else
  1726             return FAIL(L"SetOwnKey", status);
  1727     }
  1728 
  1729     return S_OK;
  1730 }
  1731 
  1732 STDMETHODIMP CpEpEngine::TrustOwnKey(pEpIdentity * ident)
  1733 {
  1734     assert(ident);
  1735 
  1736     if (!ident)
  1737         return E_INVALIDARG;
  1738 
  1739     ::pEp_identity *_ident;
  1740     try {
  1741         _ident = new_identity(ident);
  1742     }
  1743     catch (bad_alloc&) {
  1744         return E_OUTOFMEMORY;
  1745     }
  1746     catch (exception& ex) {
  1747         return FAIL(ex.what());
  1748     }
  1749 
  1750     assert(_ident);
  1751     if (_ident == NULL)
  1752         return E_OUTOFMEMORY;
  1753 
  1754     PEP_STATUS status = ::trust_own_key(session(), _ident);
  1755 
  1756     ::free_identity(_ident);
  1757 
  1758     if (status == PEP_STATUS_OK)
  1759         return S_OK;
  1760     else if (status == PEP_OUT_OF_MEMORY)
  1761         return E_OUTOFMEMORY;
  1762     else
  1763         return FAIL(L"TrustOwnKey", status);
  1764 }
  1765 
  1766 STDMETHODIMP CpEpEngine::Startup()
  1767 {
  1768     try
  1769     {
  1770         startup<CpEpEngine>(messageToSend, notifyHandshake, this, &CpEpEngine::Startup_sync, &CpEpEngine::Shutdown_sync);
  1771     }
  1772     catch (bad_alloc&) {
  1773         return E_OUTOFMEMORY;
  1774     }
  1775     catch (exception& ex) {
  1776         return FAIL(ex.what());
  1777     }
  1778 
  1779     return S_OK;
  1780 }
  1781 
  1782 STDMETHODIMP CpEpEngine::GetKeyRating(BSTR fpr, pEpComType *commType)
  1783 {
  1784     assert(fpr);
  1785 
  1786     if (!fpr)
  1787         return E_INVALIDARG;
  1788 
  1789     string _fpr = utf8_string(fpr);
  1790 
  1791     PEP_comm_type _commType;
  1792     PEP_STATUS status = ::get_key_rating(session(), _fpr.c_str(), &_commType);
  1793     if (status != PEP_STATUS_OK)
  1794         return FAIL(L"cannot get key rating", status);
  1795 
  1796     *commType = (pEpComType)_commType;
  1797 
  1798     return S_OK;
  1799 }
  1800 
  1801 STDMETHODIMP CpEpEngine::GetKeyRatingForUser(BSTR userId, BSTR fpr, pEpRating *rating)
  1802 {
  1803     assert(userId);
  1804     assert(fpr);
  1805 
  1806     if (!(userId && fpr))
  1807         return E_INVALIDARG;
  1808 
  1809     string user_id = utf8_string(userId);
  1810     string _fpr = utf8_string(fpr);
  1811 
  1812     PEP_rating _rating;
  1813     PEP_STATUS status = ::get_key_rating_for_user(session(), user_id.c_str(), _fpr.c_str(), &_rating);
  1814     if (status != PEP_STATUS_OK)
  1815         return FAIL(L"cannot get key rating for user", status);
  1816 
  1817     *rating = (pEpRating)_rating;
  1818 
  1819     return S_OK;
  1820 }
  1821 
  1822 STDMETHODIMP CpEpEngine::DeliverHandshakeResult(enum SyncHandshakeResult result, SAFEARRAY *identities_sharing)
  1823 {
  1824     sync_handshake_result _result = (sync_handshake_result)result;
  1825     identity_list *_identities_sharing = NULL;
  1826     if (identities_sharing)
  1827     {
  1828         try {
  1829             _identities_sharing = identities(identities_sharing);
  1830         }
  1831         catch (bad_alloc&) {
  1832             return E_OUTOFMEMORY;
  1833         }
  1834     }
  1835 
  1836     PEP_STATUS status = ::deliverHandshakeResult(session(), _result, _identities_sharing);
  1837     free_identity_list(_identities_sharing);
  1838     switch (status) {
  1839     case PEP_STATUS_OK:
  1840         break;
  1841     case PEP_OUT_OF_MEMORY:
  1842         return E_OUTOFMEMORY;
  1843     default:
  1844         return FAIL(L"deliverHandshakeResult is reporting an error", status);
  1845     }
  1846 
  1847     return S_OK;
  1848 }
  1849 
  1850 STDMETHODIMP CpEpEngine::PERToXERSyncMessage(TextMessage *msg, BSTR * xer)
  1851 {
  1852     assert(msg);
  1853 
  1854     if (!msg)
  1855         return E_INVALIDARG;
  1856 
  1857     ::message *_msg = NULL;
  1858     try {
  1859         _msg = text_message_to_C(msg);
  1860     }
  1861     catch (bad_alloc&) {
  1862         return E_OUTOFMEMORY;
  1863     }
  1864     catch (exception& ex) {
  1865         return FAIL(ex.what());
  1866     }
  1867 
  1868     char* text;
  1869     char* val = _msg->attachments->value;
  1870 
  1871     PEP_STATUS status = ::PER_to_XER_Sync_msg(val, strlen(val), &text);
  1872     free_message(_msg);
  1873 
  1874     if (status != PEP_STATUS_OK)
  1875         return FAIL(L"cannot get XER", status);
  1876 
  1877     *xer = utf16_bstr(text);
  1878     pEp_free(text);
  1879 
  1880     return S_OK;
  1881 }
  1882 
  1883 STDMETHODIMP CpEpEngine::DisableIdentityForSync(struct pEpIdentity * ident)
  1884 {
  1885     assert(ident);
  1886 
  1887     if (!ident)
  1888         return E_INVALIDARG;
  1889 
  1890     ::pEp_identity *_ident;
  1891     try {
  1892         _ident = new_identity(ident);
  1893     }
  1894     catch (bad_alloc&) {
  1895         return E_OUTOFMEMORY;
  1896     }
  1897     catch (exception& ex) {
  1898         return FAIL(ex.what());
  1899     }
  1900 
  1901     assert(_ident);
  1902     if (_ident == NULL)
  1903         return E_OUTOFMEMORY;
  1904 
  1905     PEP_STATUS status = ::disable_identity_for_sync(session(), _ident);
  1906 
  1907     ::free_identity(_ident);
  1908 
  1909     if (status == PEP_STATUS_OK)
  1910         return S_OK;
  1911     else if (status == PEP_OUT_OF_MEMORY)
  1912         return E_OUTOFMEMORY;
  1913     else
  1914         return FAIL(L"DisableIdentityForSync", status);
  1915 }
  1916 
  1917 STDMETHODIMP CpEpEngine::EnableIdentityForSync(struct pEpIdentity * ident)
  1918 {
  1919     assert(ident);
  1920 
  1921     if (!ident)
  1922         return E_INVALIDARG;
  1923 
  1924     ::pEp_identity *_ident;
  1925     try {
  1926         _ident = new_identity(ident);
  1927     }
  1928     catch (bad_alloc&) {
  1929         return E_OUTOFMEMORY;
  1930     }
  1931     catch (exception& ex) {
  1932         return FAIL(ex.what());
  1933     }
  1934 
  1935     assert(_ident);
  1936     if (_ident == NULL)
  1937         return E_OUTOFMEMORY;
  1938 
  1939     PEP_STATUS status = ::enable_identity_for_sync(session(), _ident);
  1940 
  1941     ::free_identity(_ident);
  1942 
  1943     if (status == PEP_STATUS_OK)
  1944         return S_OK;
  1945     else if (status == PEP_OUT_OF_MEMORY)
  1946         return E_OUTOFMEMORY;
  1947     else
  1948         return FAIL(L"EnableIdentityForSync", status);
  1949 }
  1950 
  1951 STDMETHODIMP CpEpEngine::PerMachineDirectory(BSTR * directory)
  1952 {
  1953     assert(directory);
  1954 
  1955     if (!directory)
  1956         return E_INVALIDARG;
  1957 
  1958     const char *_directory = ::per_machine_directory();
  1959 
  1960     if (_directory == NULL)
  1961         return FAIL(L"PerMachineDirectory: _directory is NULL");
  1962 
  1963     *directory = utf16_bstr(_directory);
  1964 
  1965     return S_OK;
  1966 }
  1967 
  1968 STDMETHODIMP CpEpEngine::PerUserDirectory(BSTR * directory)
  1969 {
  1970     assert(directory);
  1971 
  1972     if (!directory)
  1973         return E_INVALIDARG;
  1974 
  1975     const char *_directory = ::per_user_directory();
  1976 
  1977     if (_directory == NULL)
  1978         return FAIL(L"PerUserDirectory: _directory is NULL");
  1979 
  1980     *directory = utf16_bstr(_directory);
  1981 
  1982     return S_OK;
  1983 }
  1984 
  1985 STDMETHODIMP CpEpEngine::RatingFromCommType(pEpComType commType, pEpRating * rating) 
  1986 {
  1987     PEP_comm_type _comm_type = (PEP_comm_type)commType;
  1988     PEP_rating _rating = ::rating_from_comm_type(_comm_type);
  1989 
  1990     *rating = (pEpRating)_rating;
  1991 
  1992     return S_OK;
  1993 }