COM-49: When system.db is missing, adapter keeps restaring in a loop with no error message.
- Move most initialization work from constructor to FinalConstruct(), where
we can (at least) return an error code.
- Fix null dereference crashes in destructor when FinalConstruct() failed.
- Generally preserve the PEP_STATUS via FACILITY_ITF HRESULTs (except OOM,
which results in an OutOfMemoryException via E_OUTOFMEMORY).
- Add pEpStatus enum to IDL file, so the client can (potentially) use it to make
more sense out of the HRESULTs.
1 // CpEpEngine.cpp : Implementation of CpEpEngine
4 #include "CpEpEngine.h"
8 using namespace pEp::utility;
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;
17 STDMETHODIMP CpEpEngine::InterfaceSupportsErrorInfo(REFIID riid)
19 static const IID* const arr[] =
25 for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
27 if (InlineIsEqualGUID(*arr[i], riid))
33 // The second argument is optional, and currently supports PEP_STATUS.
34 #define FAIL(msg, ...) error(msg, __VA_ARGS__)
36 STDMETHODIMP CpEpEngine::VerboseLogging(VARIANT_BOOL enable)
38 verbose_mode = enable != VARIANT_FALSE;
42 STDMETHODIMP CpEpEngine::PassiveMode(VARIANT_BOOL enable)
44 ::config_passive_mode(get_session(), enable != VARIANT_FALSE);
48 STDMETHODIMP CpEpEngine::UnencryptedSubject(VARIANT_BOOL enable)
50 ::config_unencrypted_subject(get_session(), enable != VARIANT_FALSE);
54 STDMETHODIMP CpEpEngine::ExportKey(BSTR fpr, BSTR * keyData)
59 if (!(fpr && keyData))
62 string _fpr = utf8_string(fpr);
63 char *_key_data = NULL;
66 ::PEP_STATUS status = ::export_key(get_session(), _fpr.c_str(), &_key_data, &_size);
67 assert(status != ::PEP_OUT_OF_MEMORY);
68 if (status == ::PEP_OUT_OF_MEMORY)
71 if (status != ::PEP_STATUS_OK)
72 return FAIL(L"export_key", status);
74 _bstr_t b_key_data(utf16_string(_key_data).c_str());
76 *keyData = b_key_data.Detach();
81 STDMETHODIMP CpEpEngine::Log(BSTR title, BSTR entity, BSTR description, BSTR comment)
87 HRESULT result = S_OK;
91 _title = utf8_string(title);
93 result = E_INVALIDARG;
97 _entity = utf8_string(entity);
99 result = E_INVALIDARG;
102 _description = utf8_string(description);
105 _comment = utf8_string(comment);
110 PEP_STATUS _status = ::log_event(get_session(), _title.c_str(), _entity.c_str(), _description.c_str(), _comment.c_str());
111 assert(_status == PEP_STATUS_OK);
112 if (_status != PEP_STATUS_OK)
113 return FAIL(L"log_event", _status);
118 STDMETHODIMP CpEpEngine::Trustwords(BSTR fpr, BSTR lang, LONG max_words, BSTR * words)
121 assert(max_words >= 0);
124 HRESULT result = S_OK;
128 _fpr = utf8_string(fpr);
130 result = E_INVALIDARG;
134 _lang = utf8_string(lang);
135 if (_lang.length()) {
136 if (_lang.length() != 2)
137 result = E_INVALIDARG;
146 result = E_INVALIDARG;
149 result = E_INVALIDARG;
157 PEP_STATUS status = ::trustwords(get_session(), _fpr.c_str(), _lang.c_str(), &_words, &_wsize, max_words);
158 assert(status != PEP_OUT_OF_MEMORY);
159 if (status == PEP_OUT_OF_MEMORY)
160 return E_OUTOFMEMORY;
162 if (_words == NULL) {
164 return FAIL(L"Trustwords: _words == NULL", status);
167 *words = utf16_bstr(_words);
173 STDMETHODIMP CpEpEngine::GetTrustwords(struct pEpIdentity *id1, struct pEpIdentity *id2, BSTR lang, VARIANT_BOOL full, BSTR *words)
179 if (!(id1 && id2 && words))
184 HRESULT result = S_OK;
186 pEp_identity* _id1 = NULL;
187 pEp_identity* _id2 = NULL;
192 _id1 = new_identity(id1);
193 _id2 = new_identity(id2);
196 _lang = utf8_string(lang);
197 if (_lang.length() == 0) {
200 else if (_lang.length() != 2) {
201 result = E_INVALIDARG;
209 result = E_OUTOFMEMORY;
211 catch (exception& ex) {
212 result = FAIL(ex.what());
217 if (result == S_OK) {
218 auto status = ::get_trustwords(get_session(), _id1, _id2, _lang.c_str(), &_words, &_size, full != 0 /* convert variant bool to C bool */);
220 if (status == PEP_OUT_OF_MEMORY) {
221 result = E_OUTOFMEMORY;
223 else if (status == PEP_TRUSTWORD_NOT_FOUND) {
224 result = FAIL(L"GetTrustwords: Trustword not found", status);
227 result = FAIL(L"GetTrustwords: _words == NULL", status);
230 *words = utf16_bstr(_words);
241 STDMETHODIMP CpEpEngine::GetMessageTrustwords(
242 /* [in] */ struct TextMessage *msg,
243 /* [in] */ struct pEpIdentity *receivedBy,
244 /* [in] */ SAFEARRAY *keylist,
245 /* [defaultvalue][in] */ BSTR lang,
246 /* [defaultvalue][in] */ VARIANT_BOOL full,
247 /* [retval][out] */ BSTR *words) {
252 if (!(msg && receivedBy && words))
257 HRESULT result = S_OK;
259 pEp_identity * _received_by = NULL;
260 ::message * _msg = NULL;
261 ::stringlist_t *_keylist = NULL;
266 _received_by = new_identity(receivedBy);
267 _msg = text_message_to_C(msg);
270 _keylist = new_stringlist(keylist);
274 _lang = utf8_string(lang);
275 if (_lang.length() == 0) {
278 else if (_lang.length() != 2) {
279 result = E_INVALIDARG;
287 result = E_OUTOFMEMORY;
289 catch (exception& ex) {
290 result = FAIL(ex.what());
294 if (result == S_OK) {
295 auto status = ::get_message_trustwords(
302 full != 0 /* convert variant bool to C bool */);
304 if (status == PEP_OUT_OF_MEMORY) {
305 result = E_OUTOFMEMORY;
307 else if (status == PEP_TRUSTWORD_NOT_FOUND) {
308 result = FAIL(L"GetTrustwords: Trustword not found", status);
311 result = FAIL(L"GetTrustwords: _words == NULL", status);
314 *words = utf16_bstr(_words);
319 ::free_message(_msg);
320 ::free_stringlist(_keylist);
321 ::free_identity(_received_by);
326 STDMETHODIMP CpEpEngine::GetCrashdumpLog(LONG maxlines, BSTR * log)
328 // COM-18: Currently, long == int on windows, so the check
329 // for INT_MAX is not strictly necessary. However, the code
330 // might get copy-pasted to other adapters in the future,
331 // so safety first...
332 assert(maxlines >= 0 && maxlines <= INT_MAX);
335 if (!(maxlines >= 0 && maxlines <= INT_MAX && log))
339 PEP_STATUS status = ::get_crashdump_log(get_session(), (int)maxlines, &_log);
340 assert(status == PEP_STATUS_OK);
341 if (status == PEP_OUT_OF_MEMORY)
342 return E_OUTOFMEMORY;
343 if (status != PEP_STATUS_OK)
344 return FAIL(L"GetCrashdumpLog", status);
346 return FAIL(L"GetCrashdumpLog: _log == NULL");
348 *log = utf16_bstr(_log);
353 STDMETHODIMP CpEpEngine::GetEngineVersion(BSTR * engine_version)
355 assert(engine_version);
360 const char *_engine_version = ::get_engine_version();
362 if (_engine_version == NULL)
363 return FAIL(L"GetEngineVersion: _engine_version == NULL");
365 *engine_version = utf16_bstr(_engine_version);
370 STDMETHODIMP CpEpEngine::GetLanguageList(BSTR * languages)
378 PEP_STATUS status = ::get_languagelist(get_session(), &_languages);
379 assert(status == PEP_STATUS_OK);
380 if (status == PEP_OUT_OF_MEMORY)
381 return E_OUTOFMEMORY;
382 if (status != PEP_STATUS_OK)
383 return FAIL(L"GetLanguageList", status);
384 if (_languages == NULL)
385 return FAIL(L"GetLanguageList: _languages == NULL");
387 *languages = utf16_bstr(_languages);
388 pEp_free(_languages);
392 STDMETHODIMP CpEpEngine::SetIdentityFlags(struct pEpIdentity *identity, pEpIdentityFlags flags)
398 ::pEp_identity *_ident = nullptr;
401 _ident = new_identity(identity);
404 return E_OUTOFMEMORY;
407 return E_OUTOFMEMORY;
409 catch (exception& ex) {
410 return FAIL(ex.what());;
413 PEP_STATUS status = ::set_identity_flags(get_session(), _ident, (identity_flags_t)flags);
414 ::free_identity(_ident);
415 if (status != PEP_STATUS_OK)
416 return FAIL(_T("SetIdentityFlags"), status);
421 STDMETHODIMP CpEpEngine::UnsetIdentityFlags(struct pEpIdentity *identity, pEpIdentityFlags flags)
427 ::pEp_identity *_ident = nullptr;
430 _ident = new_identity(identity);
433 return E_OUTOFMEMORY;
436 return E_OUTOFMEMORY;
438 catch (exception& ex) {
439 return FAIL(ex.what());;
442 PEP_STATUS status = ::unset_identity_flags(get_session(), _ident, (identity_flags_t)flags);
443 ::free_identity(_ident);
444 if (status != PEP_STATUS_OK)
445 return FAIL(_T("UnsetIdentityFlags"), status);
450 STDMETHODIMP CpEpEngine::StartKeyserverLookup()
452 if (identity_queue.load())
455 identity_queue.store(new identity_queue_t());
456 keymanagement_thread = new thread(::do_keymanagement, retrieve_next_identity, (void *)identity_queue.load());
461 STDMETHODIMP CpEpEngine::StopKeyserverLookup()
463 if (identity_queue.load() == NULL)
466 identity_queue_t *_iq = identity_queue.load();
467 identity_queue.store(NULL);
469 pEp_identity_cpp shutdown;
470 _iq->push_front(shutdown);
472 keymanagement_thread->join();
473 delete keymanagement_thread;
474 keymanagement_thread = NULL;
481 STDMETHODIMP CpEpEngine::Myself(struct pEpIdentity *ident, struct pEpIdentity *result)
486 if (!(ident && result))
489 ::pEp_identity *_ident = 0;
492 _ident = new_identity(ident);
495 return E_OUTOFMEMORY;
498 return E_OUTOFMEMORY;
500 catch (exception& ex) {
501 return FAIL(ex.what());;
505 // DEBUG CODE - REMOVE BEFORE RELEASE!
506 // SyncHandshakeResult handshakeResult;
508 // HRESULT res = Fire_NotifyHandshake(ident, result, signal, &handshakeResult);
510 // HRESULT res2 = Fire_TestEvent(15, _bstr_t( "hallo"));
512 PEP_STATUS status = ::myself(get_session(), _ident);
514 if (status == PEP_STATUS_OK) {
516 copy_identity(result, _ident);
517 ::free_identity(_ident);
521 ::free_identity(_ident);
522 if (status == PEP_OUT_OF_MEMORY)
523 return E_OUTOFMEMORY;
525 return FAIL(L"myself", status);
529 STDMETHODIMP CpEpEngine::UpdateIdentity(struct pEpIdentity *ident, struct pEpIdentity *result)
534 if (!(ident && result))
537 ::pEp_identity *_ident;
539 _ident = new_identity(ident);
542 return E_OUTOFMEMORY;
544 catch (exception& ex) {
545 return FAIL(ex.what());
550 return E_OUTOFMEMORY;
552 PEP_STATUS status = ::update_identity(get_session(), _ident);
554 if (status == PEP_STATUS_OK) {
555 assert(_ident->fpr); // Guaranteed not NULL, but possibly empty string.
556 copy_identity(result, _ident);
557 ::free_identity(_ident);
560 else if (status == PEP_GET_KEY_FAILED) {
562 pEp_free(_ident->fpr);
565 copy_identity(result, _ident);
567 ::free_identity(_ident);
571 ::free_identity(_ident);
572 if (status == PEP_OUT_OF_MEMORY)
573 return E_OUTOFMEMORY;
575 return FAIL(L"UpdateIdentity", status);
579 STDMETHODIMP CpEpEngine::KeyMistrusted(struct pEpIdentity *ident)
581 ::pEp_identity *_ident;
588 _ident = new_identity(ident);
591 return E_OUTOFMEMORY;
593 catch (exception& ex) {
594 return FAIL(ex.what());;
597 PEP_STATUS status = ::key_mistrusted(get_session(), _ident);
598 free_identity(_ident);
600 if (status == PEP_OUT_OF_MEMORY)
601 return E_OUTOFMEMORY;
603 if (status == PEP_KEY_NOT_FOUND)
604 return FAIL(L"key not found");
606 if (status != ::PEP_STATUS_OK)
607 return FAIL(L"cannot revoke compromized key", status);
612 STDMETHODIMP CpEpEngine::UndoLastMistrust()
614 PEP_STATUS status = ::undo_last_mistrust(get_session());
616 if (status == PEP_CANNOT_FIND_IDENTITY)
617 return FAIL(L"Cannot find identity!", status);
619 if (status != ::PEP_STATUS_OK)
620 return FAIL(L"cannot revoke compromized key", status);
625 STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
627 ::pEp_identity *_ident;
635 _ident = new_identity(ident);
638 return E_OUTOFMEMORY;
640 catch (exception& ex) {
641 return FAIL(ex.what());;
644 PEP_STATUS status = ::key_reset_trust(get_session(), _ident);
645 free_identity(_ident);
647 if (status == PEP_OUT_OF_MEMORY)
648 return E_OUTOFMEMORY;
650 if (status == PEP_KEY_NOT_FOUND)
651 return FAIL(L"key not found");
653 if (status != ::PEP_STATUS_OK)
654 return FAIL(L"cannot reset trust", status);
659 int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
663 if (!(ident && management))
666 CpEpEngine *me = (CpEpEngine *)management;
668 if (me->identity_queue.load() == NULL)
672 me->identity_queue.load()->push_back(ident);
681 ::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
687 identity_queue_t *iq = (identity_queue_t *)management;
689 do /* poll queue */ {
695 ::pEp_identity *_ident;
696 pEp_identity_cpp& ident = iq->front();
698 if (ident.address.size() == 0)
701 _ident = ident.to_pEp_identity();
707 PEP_STATUS CpEpEngine::messageToSend(void * obj, message *msg)
712 return PEP_ILLEGAL_VALUE;
715 memset(&_msg, 0, sizeof(TextMessage));
717 text_message_from_C(&_msg, msg);
718 CpEpEngine *me = (CpEpEngine *)obj;
719 HRESULT r = me->Fire_MessageToSend(&_msg);
721 clear_text_message(&_msg);
722 if (r == E_OUTOFMEMORY)
723 return PEP_OUT_OF_MEMORY;
725 return PEP_UNKNOWN_ERROR;
727 return PEP_STATUS_OK;
730 STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
736 string _fpr = utf8_string(fpr);
737 PEP_STATUS status = ::blacklist_add(get_session(), _fpr.c_str());
738 assert(status == PEP_STATUS_OK);
739 if (status != PEP_STATUS_OK)
740 return FAIL(L"blacklist_add failed in pEp engine", status);
745 STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
751 string _fpr = utf8_string(fpr);
752 PEP_STATUS status = ::blacklist_delete(get_session(), _fpr.c_str());
753 assert(status == PEP_STATUS_OK);
754 if (status != PEP_STATUS_OK)
755 return FAIL(L"blacklist_delete failed in pEp engine", status);
760 STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
765 if (!(fpr && listed))
768 string _fpr = utf8_string(fpr);
770 PEP_STATUS status = ::blacklist_is_listed(get_session(), _fpr.c_str(), &result);
771 assert(status == PEP_STATUS_OK);
772 if (status != PEP_STATUS_OK)
773 return FAIL(L"blacklist_is_listed failed in pEp engine", status);
775 *listed = result ? VARIANT_TRUE : VARIANT_FALSE;
779 STDMETHODIMP CpEpEngine::BlacklistRetrieve(SAFEARRAY **blacklist)
786 ::stringlist_t *_blacklist = NULL;
787 PEP_STATUS status = ::blacklist_retrieve(get_session(), &_blacklist);
788 assert(status == PEP_STATUS_OK);
789 if (status != PEP_STATUS_OK)
790 return FAIL(L"blacklist_retrieve failed in pEp engine", status);
793 *blacklist = string_array(_blacklist);
794 ::free_stringlist(_blacklist);
798 HRESULT CpEpEngine::error(_bstr_t msg)
800 _bstr_t helpFile = L"";
801 _bstr_t source = L"pEp COM Adapter";
803 ICreateErrorInfo *cei;
804 if (SUCCEEDED(CreateErrorInfo(&cei))) {
805 cei->SetDescription(msg);
806 cei->SetGUID(__uuidof(IpEpEngine));
807 cei->SetHelpContext(0);
808 cei->SetHelpFile(helpFile);
809 cei->SetSource(source);
812 if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
813 SetErrorInfo(0, errinfo);
821 HRESULT CpEpEngine::error(_bstr_t msg, PEP_STATUS status)
823 std::stringstream stream;
826 stream << std::hex << status;
828 error(stream.str().c_str());
830 if (status == ::PEP_OUT_OF_MEMORY)
831 return E_OUTOFMEMORY;
833 return MAKE_HRESULT(1, FACILITY_ITF, (0xFFFF & status));
836 STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags)
844 ::message *_src = text_message_to_C(src);
846 // COM-19: Initialize msg_dst to NULL, or we end up calling
847 // free_message() below with a pointer to random garbage in
848 // case of an error in encrypt_message().
849 ::message *msg_dst = NULL;
850 ::stringlist_t *_extra = new_stringlist(extra); // can cope with NULL
852 // _PEP_enc_format is intentionally hardcoded to PEP_enc_PEP:
853 // 2016-10-02 14:10 < fdik> schabi: actually, all adapters now must use PEP_enc_PEP
854 PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
855 PEP_STATUS status = ::encrypt_message(get_session(), _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
856 ::free_stringlist(_extra);
858 if (status == PEP_STATUS_OK)
859 text_message_from_C(dst, msg_dst);
861 text_message_from_C(dst, _src);
863 ::free_message(msg_dst);
864 ::free_message(_src);
866 if (status == PEP_OUT_OF_MEMORY)
867 return E_OUTOFMEMORY;
869 // COM-41: Enhanced PEP status handling
870 if ((status != PEP_STATUS_OK) && (status < PEP_UNENCRYPTED || status >= PEP_TRUSTWORD_NOT_FOUND))
871 return FAIL("Failure to encrypt message", status);
873 // Statii like PEP_UNENCRYPTED due to no private key
874 // should not be a catastrophic failure here. Using S_FALSE
875 // still allows clients to differentiate with S_OK,
876 // although this does not work out of the box with
877 // the standard .NET mapping of COM.
878 if (status != PEP_STATUS_OK)
885 STDMETHODIMP CpEpEngine::EncryptMessageForSelf(pEpIdentity * targetId, TextMessage * src, TextMessage * dst, pEpEncryptFlags flags)
891 if (!(targetId && src && dst))
894 PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
896 ::pEp_identity *_target_id = new_identity(targetId);
898 ::message *_src = text_message_to_C(src);
900 // COM-19: Initialize msg_dst to NULL, or we end up calling
901 // free_message() below with a pointer to random garbage in
902 // case of an error in encrypt_message_for_self().
903 ::message *msg_dst = NULL;
904 PEP_STATUS status = ::encrypt_message_for_self(get_session(), _target_id, _src, &msg_dst, PEP_enc_PEP, engineFlags);
906 if (status == PEP_STATUS_OK)
907 text_message_from_C(dst, msg_dst);
909 text_message_from_C(dst, _src);
911 ::free_message(msg_dst);
912 ::free_message(_src);
913 ::free_identity(_target_id);
915 if (status == PEP_OUT_OF_MEMORY)
916 return E_OUTOFMEMORY;
918 // Different to encrypt_message, this should never fail (we ought to always
919 // have a private key for ourself).#
920 if (status != PEP_STATUS_OK)
921 return FAIL("Failure to encrypt message", status);
926 STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
934 if (!(src && dst && keylist && flags && rating))
938 *rating = pEpRatingUndefined;
940 ::message *_src = text_message_to_C(src);
941 ::message *msg_dst = NULL;
942 ::stringlist_t *_keylist = NULL;
943 ::PEP_rating _rating;
945 PEP_decrypt_flags_t engineflags = 0;
946 PEP_STATUS status = ::decrypt_message(get_session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
948 *flags = (pEpDecryptFlags)engineflags;
951 text_message_from_C(dst, msg_dst);
953 ::free_message(_src);
954 ::free_message(msg_dst);
957 *keylist = string_array(_keylist);
958 free_stringlist(_keylist);
961 *rating = (pEpRating)_rating;
966 STDMETHODIMP CpEpEngine::ReEvaluateMessageRating(TextMessage * msg, SAFEARRAY * x_KeyList, pEpRating x_EncStatus, pEpRating *rating)
969 assert(x_EncStatus != PEP_rating_undefined);
972 if (!(msg && x_EncStatus != PEP_rating_undefined && rating))
975 *rating = pEpRatingUndefined;
977 ::message *_msg = text_message_to_C(msg);
978 ::stringlist_t *_keylist = new_stringlist(x_KeyList);
979 ::PEP_rating _rating = PEP_rating_undefined;
981 PEP_STATUS status = ::re_evaluate_message_rating(get_session(), _msg, _keylist, (PEP_rating)x_EncStatus, &_rating);
983 ::free_stringlist(_keylist);
984 ::free_message(_msg);
986 *rating = (pEpRating)_rating;
991 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
999 ::message *_msg = text_message_to_C(msg);
1002 PEP_STATUS status = ::outgoing_message_rating(get_session(), _msg, &_rating);
1003 if (status != PEP_STATUS_OK)
1004 return FAIL(L"cannot get message rating", status);
1006 *pVal = (pEpRating)_rating;
1010 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
1012 ::pEp_identity *_ident;
1017 if (!(ident && pVal))
1018 return E_INVALIDARG;
1021 _ident = new_identity(ident);
1023 catch (bad_alloc&) {
1024 return E_OUTOFMEMORY;
1026 catch (exception& ex) {
1027 return FAIL(ex.what());;
1031 PEP_STATUS status = ::identity_rating(get_session(), _ident, &_rating);
1032 free_identity(_ident);
1034 if (status != PEP_STATUS_OK)
1035 return FAIL(L"cannot get message color", status);
1037 *pVal = (pEpRating)_rating;
1041 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
1046 return E_INVALIDARG;
1048 PEP_rating engineRating = (PEP_rating)rating;
1049 PEP_color _color = ::color_from_rating(engineRating);
1051 *pVal = (pEpColor)_color;
1056 STDMETHODIMP CpEpEngine::OwnIdentitiesRetrieve(LPSAFEARRAY* ownIdentities)
1058 assert(ownIdentities);
1060 return E_INVALIDARG;
1062 *ownIdentities = nullptr;
1064 ::identity_list *il = nullptr;
1065 PEP_STATUS status = ::own_identities_retrieve(get_session(), &il);
1066 if (status == PEP_OUT_OF_MEMORY) {
1067 return E_OUTOFMEMORY;
1069 else if (status != PEP_STATUS_OK)
1071 return FAIL(_T("OwnIdentitiesRetrieve"), status);
1074 SAFEARRAY * _own_identities = nullptr;
1076 _own_identities = array_from_C<pEpIdentity, identity_list>(il);
1078 catch (exception& ex)
1080 ::free_identity_list(il);
1082 dynamic_cast<bad_alloc&>(ex);
1086 return FAIL(ex.what());
1088 return E_OUTOFMEMORY;
1090 free_identity_list(il);
1092 *ownIdentities = _own_identities;
1096 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
1098 ::pEp_identity *_ident;
1103 if (!ident || !result)
1104 return E_INVALIDARG;
1107 _ident = new_identity(ident);
1109 catch (bad_alloc&) {
1110 return E_OUTOFMEMORY;
1112 catch (exception& ex) {
1113 return FAIL(ex.what());;
1118 ss << "TrustPersonalKey called with ";
1119 ss << utf8_string(ident->Address);
1121 ss << ident->CommType;
1125 PEP_STATUS status = ::trust_personal_key(get_session(), _ident);
1132 ss << _ident->address;
1134 ss << _ident->comm_type;
1138 if (status == PEP_STATUS_OK)
1139 copy_identity(result, _ident);
1141 free_identity(_ident);
1142 if (status == PEP_OUT_OF_MEMORY)
1143 return E_OUTOFMEMORY;
1144 else if (status != PEP_STATUS_OK)
1145 return FAIL(L"failure while executing TrustPersonalKey()", status);
1152 void CpEpEngine::start_keysync()
1155 std::unique_lock<std::recursive_mutex> lock(keysync_mutex);
1157 // Assert if we're not already running.
1158 assert(!this->keysync_thread);
1160 // Ensure we are not aborting the new thread due to a
1162 keysync_abort_requested = false;
1164 // Init our keysync session
1165 { // begin lock scope
1166 std::lock_guard<std::mutex> lock(init_mutex);
1167 PEP_STATUS status = ::init(&keysync_session);
1168 ::register_sync_callbacks(keysync_session, (void*)this, messageToSend, notifyHandshake, inject_sync_msg, retrieve_next_sync_msg);
1169 assert(status == PEP_STATUS_OK);
1172 attach_sync_session(get_session(), keysync_session);
1174 // We need to marshal the callbacks to the keysync thread
1175 LPSTREAM marshaled_callbacks;
1177 auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
1178 assert(result == S_OK);
1180 // Star the keysync thread
1181 keysync_thread = new thread(do_keysync_in_thread, this, marshaled_callbacks);
1184 void CpEpEngine::do_keysync_in_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks)
1187 assert(marshaled_callbacks);
1189 // We need to initialize COM here for successfull delivery of the callbacks.
1190 // As we don't create any COM instances in our thread, the COMINIT value is
1191 // currently irrelevant, so we go with the safest value.
1192 auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1193 assert(res == S_OK);
1197 res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
1198 assert(SUCCEEDED(res));
1200 self->client_last_signalled_polling_state = false;
1201 self->client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
1203 res = self->client_callbacks_on_sync_thread->QueryInterface(
1204 &self->client_callbacks2_on_sync_thread);
1206 self->client_callbacks2_on_sync_thread = NULL;
1208 ::do_sync_protocol(self->keysync_session, self);
1210 self->client_callbacks_on_sync_thread->Release();
1212 self->client_callbacks_on_sync_thread = NULL;
1214 if (self->client_callbacks2_on_sync_thread)
1215 self->client_callbacks2_on_sync_thread->Release();
1216 self->client_callbacks2_on_sync_thread = NULL;
1221 void CpEpEngine::stop_keysync()
1224 std::unique_lock<std::recursive_mutex> lock(keysync_mutex);
1226 // Do nothing if keysync is not running.
1227 if (!keysync_thread)
1230 assert(!keysync_abort_requested);
1231 // signal that we're gonna abort
1232 keysync_abort_requested = true;
1234 // Notify the keysync thread
1235 keysync_condition.notify_all();
1237 // Wait for the other thread to finish and clean up
1238 while (keysync_abort_requested)
1239 keysync_condition.wait(lock);
1241 // collect the child thread for the thread to end
1242 keysync_thread->join();
1245 delete keysync_thread;
1246 keysync_thread = NULL;
1248 ::detach_sync_session(get_session());
1249 ::unregister_sync_callbacks(keysync_session);
1251 std::lock_guard<std::mutex> releaselock(init_mutex);
1252 release(keysync_session);
1253 keysync_session = NULL;
1256 int CpEpEngine::inject_sync_msg(void * msg, void * management)
1262 return E_INVALIDARG;
1264 return ERROR_INVALID_HANDLE;
1266 CpEpEngine* me = (CpEpEngine*)management;
1269 std::unique_lock<std::recursive_mutex> lock(me->keysync_mutex);
1271 // check whether we're in a valid state running:
1272 if (!me->keysync_thread)
1273 return E_ASYNC_OPERATION_NOT_STARTED;
1275 // queue the message
1276 me->keysync_queue.push(msg);
1278 // notify the receivers
1279 me->keysync_condition.notify_all();
1284 void * CpEpEngine::retrieve_next_sync_msg(void * management, time_t *timeout)
1291 CpEpEngine* me = (CpEpEngine*)management;
1293 if ((timeout && *timeout)
1294 && me->client_callbacks2_on_sync_thread
1295 && me->client_last_signalled_polling_state == false)
1297 me->client_callbacks2_on_sync_thread->NeedFastPolling(VARIANT_TRUE);
1298 me->client_last_signalled_polling_state = true;
1300 else if (!(timeout && *timeout)
1301 && me->client_callbacks2_on_sync_thread
1302 && me->client_last_signalled_polling_state == true)
1304 me->client_callbacks2_on_sync_thread->NeedFastPolling(VARIANT_FALSE);
1305 me->client_last_signalled_polling_state = false;
1309 std::unique_lock<std::recursive_mutex> lock(me->keysync_mutex);
1311 if (me->notify_handshake_finished)
1312 me->notify_handshake_deliver_result();
1314 if (timeout && *timeout) {
1315 std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now()
1316 + std::chrono::seconds(*timeout);
1318 while (me->keysync_queue.empty() && !me->keysync_abort_requested)
1320 auto status = me->keysync_condition.wait_until(lock, end_time);
1322 if (me->notify_handshake_finished)
1323 me->notify_handshake_deliver_result();
1325 if (status == std::cv_status::timeout)
1327 *timeout = 1; // Signal timeout
1332 std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
1336 *timeout = std::chrono::duration_cast<std::chrono::seconds>(end_time - now).count();
1347 while (me->keysync_queue.empty() && !me->keysync_abort_requested)
1349 me->keysync_condition.wait(lock);
1351 if (me->notify_handshake_finished)
1352 me->notify_handshake_deliver_result();
1356 if (me->keysync_abort_requested) {
1357 // we acknowledge that we're quitting...
1358 me->keysync_abort_requested = false;
1360 // We signal the main thread that we got his signal
1361 // so it can gain the mutex again and call join() on us.
1362 me->keysync_condition.notify_all();
1364 // and tell the pep engine we're done.
1366 *timeout = 0; // signal for termination.
1370 assert(!me->keysync_queue.empty());
1372 // Pop the message and return it.
1373 void* msg = me->keysync_queue.front();
1376 me->keysync_queue.pop();
1384 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
1386 // check for valid parameter
1388 return E_INVALIDARG;
1390 // don't allow double registration.
1391 if (this->client_callbacks)
1392 return E_ILLEGAL_STATE_CHANGE;
1394 this->client_callbacks = new_callbacks;
1395 new_callbacks->AddRef();
1402 STDMETHODIMP CpEpEngine::UnregisterCallbacks()
1404 // don't allow double deregistration.
1405 // S_FALSE still is no error (as double deregistration is not fatal).
1406 if (!this->client_callbacks)
1411 this->client_callbacks->Release();
1413 this->client_callbacks = NULL;
1418 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
1419 assert(keyinfo_list);
1421 if (keyinfo_list == NULL)
1422 return E_INVALIDARG;
1424 string _pattern = "";
1426 _pattern = utf8_string(search_pattern);
1427 ::stringpair_list_t* _keyinfo_list = NULL;
1429 PEP_STATUS status = ::OpenPGP_list_keyinfo(get_session(), _pattern.c_str(), &_keyinfo_list);
1430 assert(status != PEP_OUT_OF_MEMORY);
1431 if (status == PEP_OUT_OF_MEMORY)
1432 return E_OUTOFMEMORY;
1434 if (status != ::PEP_STATUS_OK)
1435 return FAIL(L"OpenPGP_list_keyinfo", status);
1437 if (_keyinfo_list && _keyinfo_list->value) {
1438 ::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
1441 ::free_stringpair_list(_keyinfo_list);
1442 return FAIL(L"OpenPGP_list_keyinfo: no keys found");
1445 ::free_stringpair_list(_keyinfo_list);
1450 HRESULT CpEpEngine::Fire_MessageToSend(TextMessage * msg)
1453 assert(this->client_callbacks_on_sync_thread);
1456 return E_INVALIDARG;
1458 if (!this->client_callbacks_on_sync_thread)
1459 return E_ILLEGAL_METHOD_CALL;
1461 auto result = this->client_callbacks_on_sync_thread->MessageToSend(msg);
1466 // This method is called from the keysync thread, and dispatches
1467 // the handshake asynchroneously to a background thread,
1468 // so the engine can continue working.
1469 PEP_STATUS CpEpEngine::notifyHandshake(void * obj, pEp_identity *self, pEp_identity *partner, sync_handshake_signal signal)
1471 assert(self && partner);
1472 if (!(self && partner))
1473 return PEP_ILLEGAL_VALUE;
1475 CpEpEngine *me = (CpEpEngine *)obj;
1477 if (me->notify_handshake_active) {
1478 // We don't support concurrent handshakes currently,
1479 // with the exception of an abort of the handshake,
1480 // which we deliver synchroneously (as it's non-blocking).
1481 if (signal == SYNC_NOTIFY_TIMEOUT) {
1482 pEpIdentity timeout_self;
1483 pEpIdentity timeout_partner;
1484 SyncHandshakeSignal timeout_signal = (SyncHandshakeSignal)signal;
1485 copy_identity(&timeout_self, self);
1486 copy_identity(&timeout_partner, partner);
1487 SyncHandshakeResult result;
1488 auto res = me->client_callbacks_on_sync_thread->NotifyHandshake(&timeout_self, &timeout_partner, timeout_signal, &result);
1490 clear_identity_s(timeout_self);
1491 clear_identity_s(timeout_partner);
1494 IErrorInfo* errorInfo = NULL;
1495 if (FAILED(GetErrorInfo(0, &errorInfo)))
1498 // The _com_error takes ownership of the errorInfo
1499 // and will Release() it. It can also cope with
1501 _com_error error(res, errorInfo);
1503 string _description = utf8_string(
1504 error.ErrorMessage());
1506 string _comment = utf8_string(error.Description());
1508 auto source = error.Source();
1509 if (source.length() > 0) {
1510 _comment += "\r\nSource: ";
1511 _comment += utf8_string(source);
1514 ::log_event(me->keysync_session,
1515 "Error on NotifyHandshakeTimeout",
1517 _description.c_str(),
1520 return PEP_UNKNOWN_ERROR;
1525 return PEP_STATUS_OK;
1528 ::log_event(me->keysync_session, "Reentrant notify_handshake call!", "pEp COM Adapter", NULL, NULL);
1529 return PEP_UNKNOWN_ERROR;
1532 assert(!(me->notify_handshake_active
1533 || me->notify_handshake_finished
1534 || me->notify_handshake_thread));
1536 me->notify_handshake_active = true;
1538 copy_identity(&me->notify_handshake_self, self);
1539 copy_identity(&me->notify_handshake_partner, partner);
1540 me->notify_handshake_signal = (SyncHandshakeSignal)signal;
1542 // We need to marshal the callbacks to the keysync thread
1543 LPSTREAM marshaled_callbacks;
1545 auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, me->client_callbacks_on_sync_thread, &marshaled_callbacks);
1546 assert(result == S_OK);
1548 me->notify_handshake_thread = new thread(notify_handshake_background_thread, me, marshaled_callbacks);
1550 return PEP_STATUS_OK;
1553 // This method also runs in the keysync thread, called by
1554 // retrieve_next_sync_msg() to deliver back the results
1555 // of the sync into the engine.
1556 void CpEpEngine::notify_handshake_deliver_result()
1558 assert(notify_handshake_active
1559 && notify_handshake_finished);
1560 if (!(notify_handshake_active
1561 && notify_handshake_finished))
1564 notify_handshake_thread->join();
1565 notify_handshake_thread = NULL;
1567 Identity partner = new_identity(¬ify_handshake_partner);
1569 if (FAILED(notify_handshake_error))
1571 IErrorInfo *errorInfo = NULL;
1573 if (notify_handshake_error_info) {
1575 auto res = CoGetInterfaceAndReleaseStream(notify_handshake_error_info, IID_IErrorInfo, &lp);
1577 if (SUCCEEDED(res) && lp)
1578 errorInfo = static_cast<IErrorInfo*>(lp);
1581 // The _com_error takes ownership of the errorInfo
1582 // and will Release() it. It can also cope with
1584 _com_error error(notify_handshake_error, errorInfo);
1586 string _description = utf8_string(
1587 error.ErrorMessage());
1589 string _comment = utf8_string(error.Description());
1591 auto source = error.Source();
1592 if (source.length() > 0) {
1593 _comment += "\r\nSource: ";
1594 _comment += utf8_string(source);
1597 ::log_event(keysync_session,
1598 "Notify Handshake Failed!",
1600 _description.c_str(),
1603 ::deliverHandshakeResult(keysync_session, partner, SYNC_HANDSHAKE_CANCEL);
1606 ::deliverHandshakeResult(
1609 (sync_handshake_result)notify_handshake_result);
1611 notify_handshake_error_info = NULL;
1613 clear_identity_s(notify_handshake_self);
1614 clear_identity_s(notify_handshake_partner);
1615 notify_handshake_active = false;
1616 notify_handshake_finished = false;
1619 // Method on the background thread, calling into Outlook to
1620 // trigger the Handshake notification, and then scheduling
1621 // the result back to the main thread.
1622 void CpEpEngine::notify_handshake_background_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks)
1626 // We need to initialize COM here for successfull delivery of the callbacks.
1627 // As we don't create any COM instances in our thread, the COMINIT value is
1628 // currently irrelevant, so we go with the safest value.
1629 auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1630 assert(res == S_OK);
1634 res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
1635 assert(SUCCEEDED(res));
1637 auto client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
1639 self->notify_handshake_error = client_callbacks_on_sync_thread->NotifyHandshake(
1640 &self->notify_handshake_self,
1641 &self->notify_handshake_partner,
1642 self->notify_handshake_signal,
1643 &self->notify_handshake_result);
1645 if (FAILED(self->notify_handshake_error)) {
1646 IErrorInfo* errorInfo = NULL;
1648 res = GetErrorInfo(0, &errorInfo);
1650 if (res = S_OK && errorInfo != NULL) {
1651 res = CoMarshalInterThreadInterfaceInStream(
1654 &self->notify_handshake_error_info);
1656 errorInfo->Release();
1660 // notify the keysync thread.
1661 self->notify_handshake_finished = true;
1662 self->keysync_condition.notify_all();