CpEpEngine.cpp
author Markus Schaber <markus@pep-security.net>
Tue, 04 Oct 2016 23:07:42 +0200
branchkeysync
changeset 178 9f30e2a5f72c
parent 177 4d197f1c3abb
child 180 51f843f18dd2
permissions -rw-r--r--
COM-30 COM-24: Refactorings
Drop unused enum.
     1 // CpEpEngine.cpp : Implementation of CpEpEngine
     2 
     3 #include "stdafx.h"
     4 #include "CpEpEngine.h"
     5 
     6 using namespace std;
     7 using namespace pEp::utility;
     8 
     9 // CpEpEngine
    10 
    11 STDMETHODIMP CpEpEngine::InterfaceSupportsErrorInfo(REFIID riid)
    12 {
    13 	static const IID* const arr[] =
    14 	{
    15 		&IID_IpEpEngine
    16 	};
    17 
    18 	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    19 	{
    20 		if (InlineIsEqualGUID(*arr[i], riid))
    21 			return S_OK;
    22 	}
    23 	return S_FALSE;
    24 }
    25 
    26 #define FAIL(msg) error(msg)
    27 
    28 STDMETHODIMP CpEpEngine::VerboseLogging(VARIANT_BOOL enable)
    29 {
    30 	verbose_mode = enable != VARIANT_FALSE;
    31 	return S_OK;
    32 }
    33 
    34 STDMETHODIMP CpEpEngine::PassiveMode(VARIANT_BOOL enable)
    35 {
    36 	::config_passive_mode(get_session(), enable != VARIANT_FALSE);
    37 	return S_OK;
    38 }
    39 
    40 STDMETHODIMP CpEpEngine::UnencryptedSubject(VARIANT_BOOL enable)
    41 {
    42 	::config_unencrypted_subject(get_session(), enable != VARIANT_FALSE);
    43 	return S_OK;
    44 }
    45 
    46 STDMETHODIMP CpEpEngine::Log(BSTR title, BSTR entity, BSTR description, BSTR comment)
    47 {
    48 	string _title;
    49 	string _entity;
    50 	string _description;
    51 	string _comment;
    52 	HRESULT result = S_OK;
    53 
    54 	assert(title);
    55 	if (title)
    56 		_title = utf8_string(title);
    57 	else
    58 		result = E_INVALIDARG;
    59 
    60 	assert(entity);
    61 	if (entity)
    62 		_entity = utf8_string(entity);
    63 	else
    64 		result = E_INVALIDARG;
    65 
    66 	if (description)
    67 		_description = utf8_string(description);
    68 
    69 	if (comment)
    70 		_comment = utf8_string(comment);
    71 
    72 	if (result != S_OK)
    73 		return result;
    74 
    75 	PEP_STATUS _status = ::log_event(get_session(), _title.c_str(), _entity.c_str(), _description.c_str(), _comment.c_str());
    76 	assert(_status == PEP_STATUS_OK);
    77 	if (_status != PEP_STATUS_OK)
    78 		return FAIL(L"log_event");
    79 	else
    80 		return S_OK;
    81 }
    82 
    83 STDMETHODIMP CpEpEngine::TrustWords(BSTR fpr, BSTR lang, LONG max_words, BSTR * words)
    84 {
    85 	assert(fpr);
    86 	assert(max_words >= 0);
    87 	assert(words);
    88 
    89 	HRESULT result = S_OK;
    90 
    91 	string _fpr;
    92 	if (fpr)
    93 		_fpr = utf8_string(fpr);
    94 	else
    95 		result = E_INVALIDARG;
    96 
    97 	string _lang;
    98 	if (lang) {
    99 		_lang = utf8_string(lang);
   100 		if (_lang.length()) {
   101 			if (_lang.length() != 2)
   102 				result = E_INVALIDARG;
   103 		}
   104 		else
   105 			_lang = "en";
   106 	}
   107 	else
   108 		_lang = "en";
   109 
   110 	if (max_words < 0)
   111 		result = E_INVALIDARG;
   112 
   113 	if (words == NULL)
   114 		result = E_INVALIDARG;
   115 
   116 	if (result != S_OK)
   117 		return result;
   118 
   119 	char *_words = NULL;
   120 	size_t _wsize = 0;
   121 
   122 	PEP_STATUS status = ::trustwords(get_session(), _fpr.c_str(), _lang.c_str(), &_words, &_wsize, max_words);
   123 	assert(status != PEP_OUT_OF_MEMORY);
   124 	if (status == PEP_OUT_OF_MEMORY)
   125 		return E_OUTOFMEMORY;
   126 
   127 	if (_words == NULL) {
   128 		*words = NULL;
   129 		return FAIL(L"TrustWords");
   130 	}
   131 	else {
   132 		*words = utf16_bstr(_words);
   133 		pEp_free(_words);
   134 		return S_OK;
   135 	}
   136 }
   137 
   138 STDMETHODIMP CpEpEngine::GetCrashdumpLog(LONG maxlines, BSTR * log)
   139 {
   140 	assert(maxlines >= 0);
   141 	assert(log);
   142 
   143 	if (!(maxlines >= 0 && log))
   144 		return E_INVALIDARG;
   145 
   146 	char *_log;
   147 	PEP_STATUS status = ::get_crashdump_log(get_session(), (int)maxlines, &_log);
   148 	assert(status == PEP_STATUS_OK);
   149 	if (status == PEP_OUT_OF_MEMORY)
   150 		return E_OUTOFMEMORY;
   151 	if (status != PEP_STATUS_OK || _log == NULL)
   152 		return FAIL(L"GetCrashdumpLog");
   153 
   154 	*log = utf16_bstr(_log);
   155 	pEp_free(_log);
   156 	return S_OK;
   157 }
   158 
   159 STDMETHODIMP CpEpEngine::GetEngineVersion(BSTR * engine_version)
   160 {
   161 	assert(engine_version);
   162 
   163 	if (!engine_version)
   164 		return E_INVALIDARG;
   165 
   166 	const char *_engine_version = ::get_engine_version();
   167 
   168 	if (_engine_version == NULL)
   169 		return FAIL(L"GetEngineVersion");
   170 
   171 	*engine_version = utf16_bstr(_engine_version);
   172 
   173 	return S_OK;
   174 }
   175 
   176 STDMETHODIMP CpEpEngine::GetLanguageList(BSTR * languages)
   177 {
   178 	assert(languages);
   179 
   180 	if (!languages)
   181 		return E_INVALIDARG;
   182 
   183 	char *_languages;
   184 	PEP_STATUS status = ::get_languagelist(get_session(), &_languages);
   185 	assert(status == PEP_STATUS_OK);
   186 	if (status == PEP_OUT_OF_MEMORY)
   187 		return E_OUTOFMEMORY;
   188 	if (status != PEP_STATUS_OK || _languages == NULL)
   189 		return FAIL(L"GetLanguageList");
   190 
   191 	*languages = utf16_bstr(_languages);
   192 	pEp_free(_languages);
   193 	return S_OK;
   194 }
   195 
   196 STDMETHODIMP CpEpEngine::StartKeyserverLookup()
   197 {
   198 	if (identity_queue.load())
   199 		return S_OK;
   200 
   201 	identity_queue.store(new identity_queue_t());
   202 	keymanagement_thread = new thread(::do_keymanagement, retrieve_next_identity, (void *)identity_queue.load());
   203 
   204 	return S_OK;
   205 }
   206 
   207 STDMETHODIMP CpEpEngine::StopKeyserverLookup()
   208 {
   209 	if (identity_queue.load() == NULL)
   210 		return S_OK;
   211 
   212 	identity_queue_t *_iq = identity_queue.load();
   213 	identity_queue.store(NULL);
   214 
   215 	pEp_identity_cpp shutdown;
   216 	_iq->push_front(shutdown);
   217 
   218 	keymanagement_thread->join();
   219 	delete keymanagement_thread;
   220 	keymanagement_thread = NULL;
   221 
   222 	delete _iq;
   223 
   224 	return S_OK;
   225 }
   226 
   227 STDMETHODIMP CpEpEngine::Myself(struct pEpIdentity *ident, struct pEpIdentity *result)
   228 {
   229 	assert(ident);
   230 	assert(result);
   231 
   232 	if (ident == NULL || result == NULL)
   233 		return E_INVALIDARG;
   234 
   235 	::pEp_identity *_ident = new_identity(ident);
   236 	assert(_ident);
   237 	if (_ident == NULL)
   238 		return E_OUTOFMEMORY;
   239 
   240 	// DEBUG CODE - REMOVE BEFORE RELEASE!
   241 	// SyncHandshakeResult handshakeResult;
   242 	//
   243 	// HRESULT res = Fire_ShowHandshake(ident, result, &handshakeResult);
   244 	// 
   245 	// HRESULT res2 = Fire_TestEvent(15, _bstr_t( "hallo"));
   246 
   247 	PEP_STATUS status = ::myself(get_session(), _ident);
   248 
   249 	if (status == PEP_STATUS_OK) {
   250 		assert(_ident->fpr);
   251 		copy_identity(result, _ident);
   252 		::free_identity(_ident);
   253 		return S_OK;
   254 	}
   255 	else {
   256 		::free_identity(_ident);
   257 		if (status == PEP_OUT_OF_MEMORY)
   258 			return E_OUTOFMEMORY;
   259 		else
   260 			return FAIL(L"myself");
   261 	}
   262 }
   263 
   264 STDMETHODIMP CpEpEngine::UpdateIdentity(struct pEpIdentity *ident, struct pEpIdentity *result)
   265 {
   266 	assert(ident);
   267 	assert(result);
   268 
   269 	if (ident == NULL || result == NULL)
   270 		return E_INVALIDARG;
   271 
   272 	::pEp_identity *_ident = new_identity(ident);
   273 	assert(_ident);
   274 	if (_ident == NULL)
   275 		return E_OUTOFMEMORY;
   276 
   277 	PEP_STATUS status = ::update_identity(get_session(), _ident);
   278 
   279 	if (status == PEP_STATUS_OK) {
   280 		assert(_ident->fpr);
   281 		copy_identity(result, _ident);
   282 		::free_identity(_ident);
   283 		return S_OK;
   284 	}
   285 	else {
   286 		::free_identity(_ident);
   287 		if (status == PEP_OUT_OF_MEMORY)
   288 			return E_OUTOFMEMORY;
   289 		else
   290 			return FAIL(L"UpdateIdentity");
   291 	}
   292 }
   293 
   294 STDMETHODIMP CpEpEngine::KeyMistrusted(struct pEpIdentity *ident)
   295 {
   296 	::pEp_identity *_ident;
   297 
   298 	assert(ident);
   299 
   300 	try {
   301 		_ident = new_identity(ident);
   302 	}
   303 	catch (bad_alloc&) {
   304 		return E_OUTOFMEMORY;
   305 	}
   306 	catch (exception&) {
   307 		return E_FAIL;
   308 	}
   309 
   310 	PEP_STATUS status = ::key_mistrusted(get_session(), _ident);
   311 	free_identity(_ident);
   312 
   313 	if (status == PEP_OUT_OF_MEMORY)
   314 		return E_OUTOFMEMORY;
   315 
   316 	if (status == PEP_KEY_NOT_FOUND)
   317 		return FAIL(L"key not found");
   318 
   319 	if (status != ::PEP_STATUS_OK)
   320 		return FAIL(L"cannot revoke compromized key");
   321 
   322 	return S_OK;
   323 }
   324 
   325 STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
   326 {
   327 	::pEp_identity *_ident;
   328 
   329 	assert(ident);
   330 
   331 	try {
   332 		_ident = new_identity(ident);
   333 	}
   334 	catch (bad_alloc&) {
   335 		return E_OUTOFMEMORY;
   336 	}
   337 	catch (exception&) {
   338 		return E_FAIL;
   339 	}
   340 
   341 	PEP_STATUS status = ::key_reset_trust(get_session(), _ident);
   342 	free_identity(_ident);
   343 
   344 	if (status == PEP_OUT_OF_MEMORY)
   345 		return E_OUTOFMEMORY;
   346 
   347 	if (status == PEP_KEY_NOT_FOUND)
   348 		return FAIL(L"key not found");
   349 
   350 	if (status != ::PEP_STATUS_OK)
   351 		return FAIL(L"cannot reset trust");
   352 
   353 	return S_OK;
   354 }
   355 
   356 int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
   357 {
   358 	assert(ident);
   359 	assert(management);
   360 	if (!(ident && management))
   361 		return -1;
   362 
   363 	CpEpEngine *me = (CpEpEngine *)management;
   364 
   365 	if (me->identity_queue.load() == NULL)
   366 		return 0;
   367 
   368 	try {
   369 		me->identity_queue.load()->push_back(ident);
   370 	}
   371 	catch (exception&) {
   372 		return -1;
   373 	}
   374 
   375 	return 0;
   376 }
   377 
   378 ::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
   379 {
   380 	assert(management);
   381 	identity_queue_t *iq = (identity_queue_t *)management;
   382 
   383 	do /* poll queue */ {
   384 		if (iq->size())
   385 			break;
   386 		::Sleep(100);
   387 	} while (true);
   388 
   389 	::pEp_identity *_ident;
   390 	pEp_identity_cpp& ident = iq->front();
   391 
   392 	if (ident.address.size() == 0)
   393 		return NULL;
   394 
   395 	_ident = ident.to_pEp_identity();
   396 	iq->pop_front();
   397 
   398 	return _ident;
   399 }
   400 
   401 PEP_STATUS CpEpEngine::messageToSend(void * obj, message *msg)
   402 {
   403 	assert(msg);
   404 	if (msg == NULL)
   405 		return PEP_ILLEGAL_VALUE;
   406 
   407 	TextMessage _msg;
   408 	memset(&_msg, 0, sizeof(TextMessage));
   409 
   410 	text_message_from_C(&_msg, msg);
   411 	CpEpEngine *me = (CpEpEngine *)obj;
   412 	HRESULT r = me->Fire_MessageToSend(&_msg);
   413 	assert(r == S_OK);
   414 	clear_text_message(&_msg);
   415 	if (r == E_OUTOFMEMORY)
   416 		return PEP_OUT_OF_MEMORY;
   417 	if (r != S_OK)
   418 		return PEP_UNKNOWN_ERROR;
   419 
   420 	return PEP_STATUS_OK;
   421 }
   422 
   423 PEP_STATUS CpEpEngine::showHandshake(void * obj, pEp_identity *self, pEp_identity *partner)
   424 {
   425 	assert(self && partner);
   426 	if (!(self && partner))
   427 		return PEP_ILLEGAL_VALUE;
   428 
   429 	pEpIdentity _self;
   430 	copy_identity(&_self, self);
   431 	pEpIdentity _partner;
   432 	copy_identity(&_partner, partner);
   433 	CpEpEngine *me = (CpEpEngine *)obj;
   434 	SyncHandshakeResult _result;
   435 	HRESULT r = me->Fire_ShowHandshake(&_self, &_partner, &_result);
   436 	assert(r == S_OK);
   437 	clear_identity_s(_self);
   438 	clear_identity_s(_partner);
   439 	if (r == E_OUTOFMEMORY)
   440 		return PEP_OUT_OF_MEMORY;
   441 	if (r != S_OK)
   442 		return PEP_UNKNOWN_ERROR;
   443 
   444 	PEP_STATUS status = deliverHandshakeResult(me->get_session(), partner, (sync_handshake_result)(int)_result);
   445 	return status;
   446 }
   447 
   448 STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
   449 {
   450 	assert(fpr);
   451 
   452 	string _fpr = utf8_string(fpr);
   453 	PEP_STATUS status = ::blacklist_add(get_session(), _fpr.c_str());
   454 	assert(status == PEP_STATUS_OK);
   455 	if (status != PEP_STATUS_OK)
   456 		return FAIL(L"blacklist_add failed in pEp engine");
   457 
   458 	return S_OK;
   459 }
   460 
   461 STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
   462 {
   463 	assert(fpr);
   464 
   465 	string _fpr = utf8_string(fpr);
   466 	PEP_STATUS status = ::blacklist_delete(get_session(), _fpr.c_str());
   467 	assert(status == PEP_STATUS_OK);
   468 	if (status != PEP_STATUS_OK)
   469 		return FAIL(L"blacklist_delete failed in pEp engine");
   470 
   471 	return S_OK;
   472 }
   473 
   474 STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
   475 {
   476 	assert(fpr);
   477 	assert(listed);
   478 
   479 	string _fpr = utf8_string(fpr);
   480 	bool result;
   481 	PEP_STATUS status = ::blacklist_is_listed(get_session(), _fpr.c_str(), &result);
   482 	assert(status == PEP_STATUS_OK);
   483 	if (status != PEP_STATUS_OK)
   484 		return FAIL(L"blacklist_is_listed failed in pEp engine");
   485 
   486 	*listed = result ? VARIANT_TRUE : VARIANT_FALSE;
   487 	return S_OK;
   488 }
   489 
   490 STDMETHODIMP CpEpEngine::BlacklistRetreive(SAFEARRAY **blacklist)
   491 {
   492 	assert(blacklist);
   493 
   494 	::stringlist_t *_blacklist = NULL;
   495 	PEP_STATUS status = ::blacklist_retrieve(get_session(), &_blacklist);
   496 	assert(status == PEP_STATUS_OK);
   497 	if (status != PEP_STATUS_OK)
   498 		return FAIL(L"blacklist_retrieve failed in pEp engine");
   499 	assert(_blacklist);
   500 
   501 	*blacklist = string_array(_blacklist);
   502 	return S_OK;
   503 }
   504 
   505 HRESULT CpEpEngine::error(_bstr_t msg)
   506 {
   507 	_bstr_t helpFile = L"";
   508 	_bstr_t source = L"pEp COM Adapter";
   509 
   510 	ICreateErrorInfo *cei;
   511 	if (SUCCEEDED(CreateErrorInfo(&cei))) {
   512 		cei->SetDescription(msg);
   513 		cei->SetGUID(__uuidof(IpEpEngine));
   514 		cei->SetHelpContext(0);
   515 		cei->SetHelpFile(helpFile);
   516 		cei->SetSource(source);
   517 
   518 		IErrorInfo *errinfo;
   519 		if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
   520 			SetErrorInfo(0, errinfo);
   521 			errinfo->Release();
   522 		}
   523 		cei->Release();
   524 	}
   525 	return E_FAIL;
   526 }
   527 
   528 STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags)
   529 {
   530 	assert(src);
   531 	assert(dst);
   532 
   533 	::message *_src = text_message_to_C(src);
   534 	::message *msg_dst;
   535 	::stringlist_t *_extra = new_stringlist(extra);
   536 
   537 	// _PEP_enc_format is intentionally hardcoded to PEP_enc_PEP:
   538 	// 2016-10-02 14:10 < fdik> schabi: actually, all adapters now must use PEP_enc_PEP
   539 	PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t) flags;
   540 	PEP_STATUS status = ::encrypt_message(get_session(), _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
   541 	::free_stringlist(_extra);
   542 
   543 	if (status == PEP_STATUS_OK)
   544 		text_message_from_C(dst, msg_dst);
   545 	else
   546 		text_message_from_C(dst, _src);
   547 
   548 	::free_message(msg_dst);
   549 	::free_message(_src);
   550 
   551 	if (status == PEP_OUT_OF_MEMORY)
   552 		return E_OUTOFMEMORY;
   553 
   554 	return S_OK;
   555 }
   556 
   557 STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
   558 {
   559 	assert(src);
   560 	assert(dst);
   561 	assert(keylist);
   562 	assert(rating);
   563 
   564 	*keylist = NULL;
   565 	*rating = pEpRatingUndefined;
   566 
   567 	::message *_src = text_message_to_C(src);
   568 	::message *msg_dst = NULL;
   569 	::stringlist_t *_keylist;
   570 	::PEP_rating _rating;
   571 
   572 	PEP_decrypt_flags_t engineflags = 0;
   573 	PEP_STATUS status = ::decrypt_message(get_session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
   574 
   575 	*flags = (pEpDecryptFlags)engineflags;
   576 
   577 	if (msg_dst)
   578 		text_message_from_C(dst, msg_dst);
   579 
   580 	::free_message(_src);
   581 	::free_message(msg_dst);
   582 
   583 	if (_keylist) {
   584 		*keylist = string_array(_keylist);
   585 		free_stringlist(_keylist);
   586 	}
   587 
   588 	*rating = (pEpRating)_rating;
   589 
   590 	return S_OK;
   591 }
   592 
   593 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
   594 {
   595 	assert(msg);
   596 	assert(pVal);
   597 
   598 	::message *_msg = text_message_to_C(msg);
   599 
   600 	PEP_rating _rating;
   601 	PEP_STATUS status = ::outgoing_message_rating(get_session(), _msg, &_rating);
   602 	if (status != PEP_STATUS_OK)
   603 		return FAIL(L"cannot get message rating");
   604 
   605 	*pVal = (pEpRating)_rating;
   606 	return S_OK;
   607 }
   608 
   609 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
   610 {
   611 	::pEp_identity *_ident;
   612 
   613 	assert(ident);
   614 	assert(pVal);
   615 
   616 	try {
   617 		_ident = new_identity(ident);
   618 	}
   619 	catch (bad_alloc&) {
   620 		return E_OUTOFMEMORY;
   621 	}
   622 	catch (exception&) {
   623 		return E_FAIL;
   624 	}
   625 
   626 	PEP_rating _rating;
   627 	PEP_STATUS status = ::identity_rating(get_session(), _ident, &_rating);
   628 	free_identity(_ident);
   629 	if (status != PEP_STATUS_OK)
   630 		return FAIL(L"cannot get message color");
   631 
   632 	*pVal = (pEpRating)_rating;
   633 	return S_OK;
   634 }
   635 
   636 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
   637 {
   638 	assert(pVal);
   639 
   640 	PEP_rating engineRating = (PEP_rating)rating;
   641 	PEP_color _color = ::color_from_rating(engineRating);
   642 
   643 	*pVal = (pEpColor)_color;
   644 
   645 	return S_OK;
   646 }
   647 
   648 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
   649 {
   650 	::pEp_identity *_ident;
   651 
   652 	assert(ident);
   653 	assert(result);
   654 
   655 	try {
   656 		_ident = new_identity(ident);
   657 	}
   658 	catch (bad_alloc&) {
   659 		return E_OUTOFMEMORY;
   660 	}
   661 	catch (exception&) {
   662 		return E_FAIL;
   663 	}
   664 
   665 	if (verbose_mode) {
   666 		stringstream ss;
   667 		ss << "TrustPersonalKey called with ";
   668 		ss << utf8_string(ident->address);
   669 		ss << L": ";
   670 		ss << ident->commType;
   671 		verbose(ss.str());
   672 	}
   673 
   674 	PEP_STATUS status = ::trust_personal_key(get_session(), _ident);
   675 
   676 	if (verbose_mode) {
   677 		stringstream ss;
   678 		ss << "result ";
   679 		ss << status;
   680 		ss << " for ";
   681 		ss << _ident->address;
   682 		ss << L": ";
   683 		ss << _ident->comm_type;
   684 		verbose(ss.str());
   685 	}
   686 
   687 	if (status == PEP_STATUS_OK)
   688 		copy_identity(result, _ident);
   689 
   690 	free_identity(_ident);
   691 	if (status == PEP_OUT_OF_MEMORY)
   692 		return E_OUTOFMEMORY;
   693 	else if (status != PEP_STATUS_OK)
   694 		return FAIL(L"failure while executing TrustPersonalKey()");
   695 
   696 	return S_OK;
   697 }
   698 
   699 // keysync api
   700 
   701 void CpEpEngine::start_keysync()
   702 {
   703 	// acquire the lock
   704 	std::unique_lock<std::mutex> lock(keysync_mutex);
   705 
   706 	// Check if we're already running.
   707 	if (keysync_thread_running) 
   708 	{
   709 		// If we have pending aborts, we need to wake up those threads
   710 		// and cancel the pending abort.
   711 		if (keysync_abort_requested) 
   712 		{
   713 			keysync_abort_requested = false;
   714 			keysync_condition.notify_all();
   715 		}
   716 		return;
   717 	}
   718 
   719 	// Ensure we are not aborting the new thread due to a
   720 	// left over flag.
   721 	keysync_abort_requested = false;
   722 
   723 	// Init our keysync session
   724 	PEP_STATUS status = ::init(&keysync_session);
   725 	::register_sync_callbacks(keysync_session, (void*)this, messageToSend, showHandshake, inject_sync_msg, retreive_next_sync_msg);
   726 	assert(status == PEP_STATUS_OK);
   727 
   728     attach_sync_session(get_session(), keysync_session);
   729 
   730 	// Star the keysync thread
   731 	keysync_thread = new thread(::do_sync_protocol, keysync_session, this);
   732 
   733 	// flag to signal we're running
   734 	keysync_thread_running = true;
   735 }
   736 
   737 void CpEpEngine::stop_keysync()
   738 {
   739 	// acquire the lock
   740 	std::unique_lock<std::mutex> lock(keysync_mutex);
   741 
   742 	// check whether we're not running, or there's a concurrent abort
   743 	if (keysync_abort_requested || !keysync_thread_running)
   744 		return;
   745 
   746 	// signal that we're gonna abort
   747 	keysync_abort_requested = true;
   748 
   749 	// Notify the keysync thread
   750 	keysync_condition.notify_all();
   751 
   752 	// Wait for the other thread to finish and clean up
   753 	while (keysync_thread_running && keysync_abort_requested)
   754 		keysync_condition.wait(lock);
   755 
   756 	if (!keysync_abort_requested)
   757 		return; // someone called start_keysync() while we were trying to stop it...
   758 
   759     detach_sync_session(get_session());
   760 
   761 	// wait for the thread to end
   762 	keysync_thread->join();
   763 
   764 	// clean up
   765 	delete keysync_thread;
   766 	keysync_thread = NULL;
   767 	::unregister_sync_callbacks(keysync_session);
   768 	release(keysync_session);
   769     keysync_session == NULL;
   770 	keysync_abort_requested = false;
   771 }
   772 
   773 int CpEpEngine::inject_sync_msg(void * msg, void * management)
   774 {
   775 	// check argument
   776 	if (!msg)
   777 		return E_INVALIDARG;
   778 	if (!management)
   779 		return ERROR_INVALID_HANDLE;
   780 
   781 	CpEpEngine* me = (CpEpEngine*)management;
   782 
   783 	// acquire the lock
   784 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
   785 
   786 	// check whether we're running:
   787 	if (me->keysync_abort_requested || !me->keysync_thread_running)
   788 		return E_ASYNC_OPERATION_NOT_STARTED;
   789 
   790 	// queue the message
   791 	me->keysync_queue.push(msg);
   792 
   793 	// notify the receivers
   794 	me->keysync_condition.notify_all();
   795 
   796     return S_OK;
   797 }
   798 
   799 void * CpEpEngine::retreive_next_sync_msg(void * management)
   800 {
   801 	// sanity check
   802 	assert(management);
   803 
   804 	CpEpEngine* me = (CpEpEngine*)management;
   805 
   806 	// acquire the lock
   807 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
   808 
   809 	// as long as we're supposed to be active 
   810 	// (we won't wait for the queue to empty currently...)
   811 	while (!me->keysync_abort_requested)
   812 	{
   813 		// If the queue is empty, wait for a signal, and try again.
   814 		if (me->keysync_queue.empty())
   815 		{
   816 			me->keysync_condition.wait(lock);
   817 			continue;
   818 		}
   819 
   820 		// Pop the message and return it.
   821 		void* msg = me->keysync_queue.front();
   822 		assert(msg);
   823 
   824 		me->keysync_queue.pop();
   825 
   826 		return msg;
   827 	}
   828 
   829 	// we acknowledge that we're quitting...
   830 	me->keysync_thread_running = false;
   831 
   832 	// We signal the main thread that we got his signal
   833 	// so it can gain the mutex again and call join() on us.
   834 	me->keysync_condition.notify_all();
   835 
   836 	// and tell the pep engine we're done.
   837 	return NULL;
   838 }
   839 
   840 
   841 // Event callbacks
   842 
   843 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
   844 {
   845 	callbacks cbs = get_callbacks();
   846 	vector<IpEpEngineCallbacks*>& vec = cbs;
   847 
   848 	vec.push_back(new_callbacks);
   849 	new_callbacks->AddRef();
   850 	start_keysync();
   851 
   852 	return S_OK;
   853 }
   854 
   855 STDMETHODIMP CpEpEngine::UnregisterCallbacks(IpEpEngineCallbacks* obsolete_callbacks)
   856 {
   857 	callbacks cbs = get_callbacks();
   858 	vector<IpEpEngineCallbacks*>& vec = cbs;
   859 
   860 	auto position = std::find(vec.begin(), vec.end(), obsolete_callbacks);
   861 	if (position != vec.end()) {
   862 		vec.erase(position);
   863 		obsolete_callbacks->Release();
   864 		if (vec.empty())
   865 			stop_keysync();
   866 
   867 		return S_OK;
   868 	}
   869 
   870 	return S_FALSE;
   871 }
   872 
   873 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
   874 	assert(keyinfo_list);
   875 
   876 	if (keyinfo_list == NULL)
   877 		return E_INVALIDARG;
   878 
   879 	string _pattern = "";
   880 	if (search_pattern)
   881 		_pattern = utf8_string(search_pattern);
   882 	::stringpair_list_t* _keyinfo_list = NULL;
   883 
   884 	PEP_STATUS status = ::OpenPGP_list_keyinfo(get_session(), _pattern.c_str(), &_keyinfo_list);
   885 	assert(status != PEP_OUT_OF_MEMORY);
   886 	if (status == PEP_OUT_OF_MEMORY)
   887 		return E_OUTOFMEMORY;
   888 
   889 	if (status != ::PEP_STATUS_OK)
   890 		return FAIL(L"OpenPGP_list_keyinfo");
   891 
   892 	if (_keyinfo_list && _keyinfo_list->value) {
   893 		::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
   894 	}
   895 	else {
   896 		::free_stringpair_list(_keyinfo_list);
   897 		return FAIL(L"OpenPGP_list_keyinfo: no keys found");
   898 	}
   899 
   900 	::free_stringpair_list(_keyinfo_list);
   901 	return S_OK;
   902 
   903 }
   904 
   905 HRESULT CpEpEngine::Fire_MessageToSend(TextMessage * msg)
   906 {
   907 	callbacks cbs = get_callbacks();
   908 	vector<IpEpEngineCallbacks*>& vec = cbs;
   909 
   910 	assert(msg);
   911 
   912 	for (auto it = vec.begin(); it != vec.end(); ++it)
   913 	{
   914 		auto res = (*it)->MessageToSend(msg);
   915 		if (res != S_OK)
   916 			return res;
   917 	}
   918 	return S_OK;
   919 }
   920 
   921 HRESULT CpEpEngine::Fire_ShowHandshake(pEpIdentity * self, pEpIdentity * partner, SyncHandshakeResult * result)
   922 {
   923 	callbacks cbs = get_callbacks();
   924 	vector<IpEpEngineCallbacks*>& vec = cbs;
   925 
   926 	assert(self);
   927 	assert(partner);
   928 	assert(result);
   929 
   930 	for (auto it = vec.begin(); it != vec.end(); ++it)
   931 	{
   932 		auto res = (*it)->ShowHandshake(self, partner, result);
   933 		if (res != S_OK)
   934 			return res;
   935 	}
   936 	return S_OK;
   937 }