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