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