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