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