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