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