CpEpEngine.cpp
author Markus Schaber <markus@pep-security.net>
Wed, 07 Feb 2018 19:42:45 +0100
branchCOM-74
changeset 272 0cd9b4cde17c
parent 271 92866cd8b0c4
child 273 30be98685afa
permissions -rw-r--r--
COM-74: Expose _PEP_enc_format to app for EncryptMessage

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