CpEpEngine.cpp
author Markus Schaber <markus@pep-security.net>
Fri, 16 Jun 2017 23:49:49 +0200
branchENGINE-179
changeset 254 70e5127cfb62
parent 252 abfbe0f74ce1
child 257 9300cce7cf7b
permissions -rw-r--r--
ENGINE-179

Fix freeing of wrong variable.
Move method to the end of IpEpEngine2 (preserve COM backwards compatibility
for existing clients. Forward compatibility does not matter yet, as we install
plugin and adapter together, and there are no 3rd party clients yet).
Fix some naming conventions of parameters.
     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_KeyList);
   951     assert(x_EncStatus != PEP_rating_undefined);
   952     assert(rating);
   953 
   954     if (!(msg && x_KeyList && x_EncStatus != PEP_rating_undefined && rating))
   955         return E_INVALIDARG;
   956 
   957     *rating = pEpRatingUndefined;
   958 
   959     ::message *_msg = text_message_to_C(msg);
   960     ::stringlist_t *_keylist = new_stringlist(x_KeyList);
   961     ::PEP_rating _rating = PEP_rating_undefined;
   962 
   963     PEP_STATUS status = ::re_evaluate_message_rating(get_session(), _msg, _keylist, (PEP_rating)x_EncStatus, &_rating);
   964 
   965     ::free_stringlist(_keylist);
   966     ::free_message(_msg);
   967 
   968     *rating = (pEpRating)_rating;
   969 
   970     return S_OK;
   971 }
   972 
   973 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
   974 {
   975     assert(msg);
   976     assert(pVal);
   977 
   978     if (!(msg  && pVal))
   979         return E_INVALIDARG;
   980 
   981     ::message *_msg = text_message_to_C(msg);
   982 
   983     PEP_rating _rating;
   984     PEP_STATUS status = ::outgoing_message_rating(get_session(), _msg, &_rating);
   985     if (status != PEP_STATUS_OK)
   986         return FAIL(L"cannot get message rating", status);
   987 
   988     *pVal = (pEpRating)_rating;
   989     return S_OK;
   990 }
   991 
   992 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
   993 {
   994     ::pEp_identity *_ident;
   995 
   996     assert(ident);
   997     assert(pVal);
   998 
   999     if (!(ident  && pVal))
  1000         return E_INVALIDARG;
  1001 
  1002     try {
  1003         _ident = new_identity(ident);
  1004     }
  1005     catch (bad_alloc&) {
  1006         return E_OUTOFMEMORY;
  1007     }
  1008     catch (exception& ex) {
  1009         return FAIL(ex.what());;
  1010     }
  1011 
  1012     PEP_rating _rating;
  1013     PEP_STATUS status = ::identity_rating(get_session(), _ident, &_rating);
  1014     free_identity(_ident);
  1015 
  1016     if (status != PEP_STATUS_OK)
  1017         return FAIL(L"cannot get message color", status);
  1018 
  1019     *pVal = (pEpRating)_rating;
  1020     return S_OK;
  1021 }
  1022 
  1023 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
  1024 {
  1025     assert(pVal);
  1026 
  1027     if (!pVal)
  1028         return E_INVALIDARG;
  1029 
  1030     PEP_rating engineRating = (PEP_rating)rating;
  1031     PEP_color _color = ::color_from_rating(engineRating);
  1032 
  1033     *pVal = (pEpColor)_color;
  1034 
  1035     return S_OK;
  1036 }
  1037 
  1038 STDMETHODIMP CpEpEngine::OwnIdentitiesRetrieve(LPSAFEARRAY* ownIdentities)
  1039 {
  1040     assert(ownIdentities);
  1041     if (!ownIdentities)
  1042         return E_INVALIDARG;
  1043 
  1044     *ownIdentities = nullptr;
  1045 
  1046     ::identity_list *il = nullptr;
  1047     PEP_STATUS status = ::own_identities_retrieve(get_session(), &il);
  1048     if (status == PEP_OUT_OF_MEMORY) {
  1049         return E_OUTOFMEMORY;
  1050     }
  1051     else if (status != PEP_STATUS_OK)
  1052     {
  1053         return FAIL(_T("OwnIdentitiesRetrieve"), status);
  1054     }
  1055 
  1056     SAFEARRAY * _own_identities = nullptr;
  1057     try {
  1058         _own_identities = array_from_C<pEpIdentity, identity_list>(il);
  1059     }
  1060     catch (exception& ex)
  1061     {
  1062         ::free_identity_list(il);
  1063         try {
  1064             dynamic_cast<bad_alloc&>(ex);
  1065         }
  1066         catch (bad_cast&)
  1067         {
  1068             return FAIL(ex.what());
  1069         }
  1070         return E_OUTOFMEMORY;
  1071     }
  1072     free_identity_list(il);
  1073 
  1074     *ownIdentities = _own_identities;
  1075     return S_OK;
  1076 }
  1077 
  1078 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
  1079 {
  1080     ::pEp_identity *_ident;
  1081 
  1082     assert(ident);
  1083     assert(result);
  1084 
  1085     if (!ident || !result)
  1086         return E_INVALIDARG;
  1087 
  1088     try {
  1089         _ident = new_identity(ident);
  1090     }
  1091     catch (bad_alloc&) {
  1092         return E_OUTOFMEMORY;
  1093     }
  1094     catch (exception& ex) {
  1095         return FAIL(ex.what());;
  1096     }
  1097 
  1098     if (verbose_mode) {
  1099         stringstream ss;
  1100         ss << "TrustPersonalKey called with ";
  1101         ss << utf8_string(ident->Address);
  1102         ss << L": ";
  1103         ss << ident->CommType;
  1104         verbose(ss.str());
  1105     }
  1106 
  1107     PEP_STATUS status = ::trust_personal_key(get_session(), _ident);
  1108 
  1109     if (verbose_mode) {
  1110         stringstream ss;
  1111         ss << "result ";
  1112         ss << status;
  1113         ss << " for ";
  1114         ss << _ident->address;
  1115         ss << L": ";
  1116         ss << _ident->comm_type;
  1117         verbose(ss.str());
  1118     }
  1119 
  1120     if (status == PEP_STATUS_OK)
  1121         copy_identity(result, _ident);
  1122 
  1123     free_identity(_ident);
  1124     if (status == PEP_OUT_OF_MEMORY)
  1125         return E_OUTOFMEMORY;
  1126     else if (status != PEP_STATUS_OK)
  1127         return FAIL(L"failure while executing TrustPersonalKey()", status);
  1128 
  1129     return S_OK;
  1130 }
  1131 
  1132 // keysync api
  1133 
  1134 void CpEpEngine::start_keysync()
  1135 {
  1136     // acquire the lock
  1137     std::unique_lock<std::recursive_mutex> lock(keysync_mutex);
  1138 
  1139     // Assert if we're not already running.
  1140     assert(!this->keysync_thread);
  1141 
  1142     // Ensure we are not aborting the new thread due to a
  1143     // left over flag.
  1144     keysync_abort_requested = false;
  1145 
  1146     // Init our keysync session
  1147     PEP_STATUS status = ::init(&keysync_session);
  1148     ::register_sync_callbacks(keysync_session, (void*)this, messageToSend, notifyHandshake, inject_sync_msg, retrieve_next_sync_msg);
  1149     assert(status == PEP_STATUS_OK);
  1150 
  1151     attach_sync_session(get_session(), keysync_session);
  1152 
  1153     // We need to marshal the callbacks to the keysync thread
  1154     LPSTREAM marshaled_callbacks;
  1155 
  1156     auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
  1157     assert(result == S_OK);
  1158 
  1159     // Star the keysync thread
  1160     keysync_thread = new thread(do_keysync_in_thread, this, marshaled_callbacks);
  1161 }
  1162 
  1163 void CpEpEngine::do_keysync_in_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks)
  1164 {
  1165     assert(self);
  1166     assert(marshaled_callbacks);
  1167 
  1168     // We need to initialize COM here for successfull delivery of the callbacks.
  1169     // As we don't create any COM instances in our thread, the COMINIT value is
  1170     // currently irrelevant, so we go with the safest value.
  1171     auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  1172     assert(res == S_OK);
  1173 
  1174     LPVOID vp;
  1175 
  1176     res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
  1177     assert(SUCCEEDED(res));
  1178 
  1179     self->client_last_signalled_polling_state = false;
  1180     self->client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
  1181 
  1182     res = self->client_callbacks_on_sync_thread->QueryInterface(
  1183         &self->client_callbacks2_on_sync_thread);
  1184     if (res != S_OK)
  1185         self->client_callbacks2_on_sync_thread = NULL;
  1186 
  1187     ::do_sync_protocol(self->keysync_session, self);
  1188 
  1189     self->client_callbacks_on_sync_thread->Release();
  1190 
  1191     self->client_callbacks_on_sync_thread = NULL;
  1192 
  1193     if (self->client_callbacks2_on_sync_thread)
  1194         self->client_callbacks2_on_sync_thread->Release();
  1195     self->client_callbacks2_on_sync_thread = NULL;
  1196 
  1197     CoUninitialize();
  1198 }
  1199 
  1200 void CpEpEngine::stop_keysync()
  1201 {
  1202     // acquire the lock
  1203     std::unique_lock<std::recursive_mutex> lock(keysync_mutex);
  1204 
  1205     // Do nothing if keysync is not running.
  1206     if (!keysync_thread)
  1207         return;
  1208 
  1209     assert(!keysync_abort_requested);
  1210     // signal that we're gonna abort
  1211     keysync_abort_requested = true;
  1212 
  1213     // Notify the keysync thread
  1214     keysync_condition.notify_all();
  1215 
  1216     // Wait for the other thread to finish and clean up
  1217     while (keysync_abort_requested)
  1218         keysync_condition.wait(lock);
  1219 
  1220     // collect the child thread for the thread to end
  1221     keysync_thread->join();
  1222 
  1223     // clean up
  1224     delete keysync_thread;
  1225     keysync_thread = NULL;
  1226 
  1227     ::detach_sync_session(get_session());
  1228     ::unregister_sync_callbacks(keysync_session);
  1229     release(keysync_session);
  1230     keysync_session = NULL;
  1231 }
  1232 
  1233 int CpEpEngine::inject_sync_msg(void * msg, void * management)
  1234 {
  1235     assert(msg);
  1236     assert(management);
  1237     // check argument
  1238     if (!msg)
  1239         return E_INVALIDARG;
  1240     if (!management)
  1241         return ERROR_INVALID_HANDLE;
  1242 
  1243     CpEpEngine* me = (CpEpEngine*)management;
  1244 
  1245     // acquire the lock
  1246     std::unique_lock<std::recursive_mutex> lock(me->keysync_mutex);
  1247 
  1248     // check whether we're in a valid state running:
  1249     if (!me->keysync_thread)
  1250         return E_ASYNC_OPERATION_NOT_STARTED;
  1251 
  1252     // queue the message
  1253     me->keysync_queue.push(msg);
  1254 
  1255     // notify the receivers
  1256     me->keysync_condition.notify_all();
  1257 
  1258     return S_OK;
  1259 }
  1260 
  1261 void * CpEpEngine::retrieve_next_sync_msg(void * management, time_t *timeout)
  1262 {
  1263     // sanity check
  1264     assert(management);
  1265     if (!(management))
  1266         return NULL;
  1267 
  1268     CpEpEngine* me = (CpEpEngine*)management;
  1269 
  1270     if ((timeout && *timeout)
  1271         && me->client_callbacks2_on_sync_thread
  1272         && me->client_last_signalled_polling_state == false)
  1273     {
  1274         me->client_callbacks2_on_sync_thread->NeedFastPolling(VARIANT_TRUE);
  1275         me->client_last_signalled_polling_state = true;
  1276     }
  1277     else if (!(timeout && *timeout)
  1278         && me->client_callbacks2_on_sync_thread
  1279         && me->client_last_signalled_polling_state == true)
  1280     {
  1281         me->client_callbacks2_on_sync_thread->NeedFastPolling(VARIANT_FALSE);
  1282         me->client_last_signalled_polling_state = false;
  1283     }
  1284 
  1285     // acquire the lock
  1286     std::unique_lock<std::recursive_mutex> lock(me->keysync_mutex);
  1287 
  1288     if (me->notify_handshake_finished)
  1289         me->notify_handshake_deliver_result();
  1290 
  1291     if (timeout && *timeout) {
  1292         std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now()
  1293             + std::chrono::seconds(*timeout);
  1294 
  1295         while (me->keysync_queue.empty() && !me->keysync_abort_requested)
  1296         {
  1297             auto status = me->keysync_condition.wait_until(lock, end_time);
  1298 
  1299             if (me->notify_handshake_finished)
  1300                 me->notify_handshake_deliver_result();
  1301 
  1302             if (status == std::cv_status::timeout)
  1303             {
  1304                 *timeout = 1; // Signal timeout
  1305                 return NULL;
  1306             } 
  1307             else 
  1308             {
  1309                 std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
  1310 
  1311                 if (now < end_time) 
  1312                 {
  1313                     *timeout = std::chrono::duration_cast<std::chrono::seconds>(end_time - now).count();
  1314                 } 
  1315                 else 
  1316                 {
  1317                     *timeout = 0;
  1318                 }
  1319             }
  1320         }
  1321     }
  1322     else
  1323     {
  1324         while (me->keysync_queue.empty() && !me->keysync_abort_requested)
  1325         {
  1326             me->keysync_condition.wait(lock);
  1327 
  1328             if (me->notify_handshake_finished)
  1329                 me->notify_handshake_deliver_result();
  1330         }
  1331     }
  1332 
  1333     if (me->keysync_abort_requested) {
  1334         // we acknowledge that we're quitting...
  1335         me->keysync_abort_requested = false;
  1336 
  1337         // We signal the main thread that we got his signal
  1338         // so it can gain the mutex again and call join() on us.
  1339         me->keysync_condition.notify_all();
  1340 
  1341         // and tell the pep engine we're done.
  1342         if (timeout)
  1343             *timeout = 0; // signal for termination.
  1344         return NULL;
  1345     }
  1346 
  1347     assert(!me->keysync_queue.empty());
  1348 
  1349     // Pop the message and return it.
  1350     void* msg = me->keysync_queue.front();
  1351     assert(msg);
  1352 
  1353     me->keysync_queue.pop();
  1354 
  1355     return msg;
  1356 }
  1357 
  1358 
  1359 // Event callbacks
  1360 
  1361 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
  1362 {
  1363     // check for valid parameter
  1364     if (!new_callbacks)
  1365         return E_INVALIDARG;
  1366 
  1367     // don't allow double registration.
  1368     if (this->client_callbacks)
  1369         return E_ILLEGAL_STATE_CHANGE;
  1370 
  1371     this->client_callbacks = new_callbacks;
  1372     new_callbacks->AddRef();
  1373 
  1374     start_keysync();
  1375 
  1376     return S_OK;
  1377 }
  1378 
  1379 STDMETHODIMP CpEpEngine::UnregisterCallbacks()
  1380 {
  1381     // don't allow double deregistration.
  1382     // S_FALSE still is no error (as double deregistration is not fatal).
  1383     if (!this->client_callbacks)
  1384         return S_FALSE;
  1385 
  1386     stop_keysync();
  1387 
  1388     this->client_callbacks->Release();
  1389 
  1390     this->client_callbacks = NULL;
  1391 
  1392     return S_OK;
  1393 }
  1394 
  1395 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
  1396     assert(keyinfo_list);
  1397 
  1398     if (keyinfo_list == NULL)
  1399         return E_INVALIDARG;
  1400 
  1401     string _pattern = "";
  1402     if (search_pattern)
  1403         _pattern = utf8_string(search_pattern);
  1404     ::stringpair_list_t* _keyinfo_list = NULL;
  1405 
  1406     PEP_STATUS status = ::OpenPGP_list_keyinfo(get_session(), _pattern.c_str(), &_keyinfo_list);
  1407     assert(status != PEP_OUT_OF_MEMORY);
  1408     if (status == PEP_OUT_OF_MEMORY)
  1409         return E_OUTOFMEMORY;
  1410 
  1411     if (status != ::PEP_STATUS_OK)
  1412         return FAIL(L"OpenPGP_list_keyinfo", status);
  1413 
  1414     if (_keyinfo_list && _keyinfo_list->value) {
  1415         ::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
  1416     }
  1417     else {
  1418         ::free_stringpair_list(_keyinfo_list);
  1419         return FAIL(L"OpenPGP_list_keyinfo: no keys found");
  1420     }
  1421 
  1422     ::free_stringpair_list(_keyinfo_list);
  1423     return S_OK;
  1424 
  1425 }
  1426 
  1427 HRESULT CpEpEngine::Fire_MessageToSend(TextMessage * msg)
  1428 {
  1429     assert(msg);
  1430     assert(this->client_callbacks_on_sync_thread);
  1431 
  1432     if (!msg)
  1433         return E_INVALIDARG;
  1434 
  1435     if (!this->client_callbacks_on_sync_thread)
  1436         return E_ILLEGAL_METHOD_CALL;
  1437 
  1438     auto result = this->client_callbacks_on_sync_thread->MessageToSend(msg);
  1439 
  1440     return result;
  1441 }
  1442 
  1443 // This method is called from the keysync thread, and dispatches
  1444 // the handshake asynchroneously to a background thread,
  1445 // so the engine can continue working.
  1446 PEP_STATUS CpEpEngine::notifyHandshake(void * obj, pEp_identity *self, pEp_identity *partner, sync_handshake_signal signal)
  1447 {
  1448     assert(self && partner);
  1449     if (!(self && partner))
  1450         return PEP_ILLEGAL_VALUE;
  1451 
  1452     CpEpEngine *me = (CpEpEngine *)obj;
  1453 
  1454     if (me->notify_handshake_active) {
  1455         // We don't support concurrent handshakes currently, 
  1456         // with the exception of an abort of the handshake, 
  1457         // which we deliver synchroneously (as it's non-blocking).
  1458         if (signal == SYNC_NOTIFY_TIMEOUT) {
  1459             pEpIdentity timeout_self;
  1460             pEpIdentity timeout_partner;
  1461             SyncHandshakeSignal timeout_signal = (SyncHandshakeSignal)signal;
  1462             copy_identity(&timeout_self, self);
  1463             copy_identity(&timeout_partner, partner);
  1464             SyncHandshakeResult result;
  1465             auto res = me->client_callbacks_on_sync_thread->NotifyHandshake(&timeout_self, &timeout_partner, timeout_signal, &result);
  1466 
  1467             clear_identity_s(timeout_self);
  1468             clear_identity_s(timeout_partner);
  1469 
  1470             if (FAILED(res)) {
  1471                 IErrorInfo* errorInfo = NULL;
  1472                 if (FAILED(GetErrorInfo(0, &errorInfo)))
  1473                     errorInfo = NULL;
  1474 
  1475                 // The _com_error takes ownership of the errorInfo
  1476                 // and will Release() it. It can also cope with
  1477                 // NULL errorInfos.
  1478                 _com_error error(res, errorInfo);
  1479 
  1480                 string _description = utf8_string(
  1481                     error.ErrorMessage());
  1482 
  1483                 string _comment = utf8_string(error.Description());
  1484 
  1485                 auto source = error.Source();
  1486                 if (source.length() > 0) {
  1487                     _comment += "\r\nSource: ";
  1488                     _comment += utf8_string(source);
  1489                 }
  1490 
  1491                 ::log_event(me->keysync_session,
  1492                     "Error on NotifyHandshakeTimeout",
  1493                     "pEp COM Adapter",
  1494                     _description.c_str(),
  1495                     _comment.c_str());
  1496 
  1497                 return PEP_UNKNOWN_ERROR;
  1498             }
  1499 
  1500             if (res != S_OK)
  1501 
  1502             return PEP_STATUS_OK;
  1503         }
  1504 
  1505         ::log_event(me->keysync_session, "Reentrant notify_handshake call!", "pEp COM Adapter", NULL, NULL);
  1506         return PEP_UNKNOWN_ERROR;
  1507     }
  1508 
  1509     assert(!(me->notify_handshake_active
  1510         || me->notify_handshake_finished
  1511         || me->notify_handshake_thread));
  1512 
  1513     me->notify_handshake_active = true;
  1514 
  1515     copy_identity(&me->notify_handshake_self, self);
  1516     copy_identity(&me->notify_handshake_partner, partner);
  1517     me->notify_handshake_signal = (SyncHandshakeSignal)signal;
  1518 
  1519     // We need to marshal the callbacks to the keysync thread
  1520     LPSTREAM marshaled_callbacks;
  1521 
  1522     auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, me->client_callbacks_on_sync_thread, &marshaled_callbacks);
  1523     assert(result == S_OK);
  1524 
  1525     me->notify_handshake_thread = new thread(notify_handshake_background_thread, me, marshaled_callbacks);
  1526 
  1527     return PEP_STATUS_OK;
  1528 }
  1529 
  1530 // This method also runs in the keysync thread, called by
  1531 // retrieve_next_sync_msg() to deliver back the results
  1532 // of the sync into the engine.
  1533 void CpEpEngine::notify_handshake_deliver_result()
  1534 {
  1535     assert(notify_handshake_active
  1536         && notify_handshake_finished);
  1537     if (!(notify_handshake_active
  1538         && notify_handshake_finished))
  1539         return;
  1540 
  1541     notify_handshake_thread->join();
  1542     notify_handshake_thread = NULL;
  1543 
  1544     Identity partner = new_identity(&notify_handshake_partner);
  1545 
  1546     if (FAILED(notify_handshake_error))
  1547     {
  1548         IErrorInfo *errorInfo = NULL;
  1549 
  1550         if (notify_handshake_error_info) {
  1551             LPVOID lp = NULL;
  1552             auto res = CoGetInterfaceAndReleaseStream(notify_handshake_error_info, IID_IErrorInfo, &lp);
  1553 
  1554             if (SUCCEEDED(res) && lp)
  1555                 errorInfo = static_cast<IErrorInfo*>(lp);
  1556         }
  1557 
  1558         // The _com_error takes ownership of the errorInfo
  1559         // and will Release() it. It can also cope with
  1560         // NULL errorInfos.
  1561         _com_error error(notify_handshake_error, errorInfo);
  1562 
  1563         string _description = utf8_string(
  1564             error.ErrorMessage());
  1565 
  1566         string _comment = utf8_string(error.Description());
  1567 
  1568         auto source = error.Source();
  1569         if (source.length() > 0) {
  1570             _comment += "\r\nSource: ";
  1571             _comment += utf8_string(source);
  1572         }
  1573 
  1574         ::log_event(keysync_session,
  1575             "Notify Handshake Failed!",
  1576             "pEp COM Adapter",
  1577             _description.c_str(),
  1578             _comment.c_str());
  1579 
  1580         ::deliverHandshakeResult(keysync_session, partner, SYNC_HANDSHAKE_CANCEL);
  1581     }
  1582     else {
  1583         ::deliverHandshakeResult(
  1584             keysync_session,
  1585             partner,
  1586             (sync_handshake_result)notify_handshake_result);
  1587     }
  1588     notify_handshake_error_info = NULL;
  1589 
  1590     clear_identity_s(notify_handshake_self);
  1591     clear_identity_s(notify_handshake_partner);
  1592     notify_handshake_active = false;
  1593     notify_handshake_finished = false;
  1594 }
  1595 
  1596 // Method on the background thread, calling into Outlook to
  1597 // trigger the Handshake notification, and then scheduling
  1598 // the result back to the main thread.
  1599 void CpEpEngine::notify_handshake_background_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks)
  1600 {
  1601     assert(self);
  1602 
  1603     // We need to initialize COM here for successfull delivery of the callbacks.
  1604     // As we don't create any COM instances in our thread, the COMINIT value is
  1605     // currently irrelevant, so we go with the safest value.
  1606     auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  1607     assert(res == S_OK);
  1608 
  1609     LPVOID vp;
  1610 
  1611     res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
  1612     assert(SUCCEEDED(res));
  1613 
  1614     auto client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
  1615 
  1616     self->notify_handshake_error = client_callbacks_on_sync_thread->NotifyHandshake(
  1617         &self->notify_handshake_self,
  1618         &self->notify_handshake_partner,
  1619         self->notify_handshake_signal,
  1620         &self->notify_handshake_result);
  1621 
  1622     if (FAILED(self->notify_handshake_error)) {
  1623         IErrorInfo* errorInfo = NULL;
  1624 
  1625         res = GetErrorInfo(0, &errorInfo);
  1626 
  1627         if (res = S_OK && errorInfo != NULL) {
  1628             res = CoMarshalInterThreadInterfaceInStream(
  1629                 IID_IErrorInfo,
  1630                 errorInfo,
  1631                 &self->notify_handshake_error_info);
  1632 
  1633             errorInfo->Release();
  1634         }
  1635     }
  1636 
  1637     // notify the keysync thread.
  1638     self->notify_handshake_finished = true;
  1639     self->keysync_condition.notify_all();
  1640 }