CpEpEngine.cpp
author Markus Schaber <markus@pep-security.net>
Sat, 12 Nov 2016 00:29:25 +0100
changeset 204 21b7749b5beb
parent 203 b398e3da1b99
child 216 6a08f0334e8a
permissions -rw-r--r--
COM-41: Preliminary fix for the exception althouhg we still have
some discussion ongoing.
     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 // The second argument is optional, and currently supports PEP_STATUS.
    27 #define FAIL(msg, ...) error(msg, __VA_ARGS__)
    28 
    29 STDMETHODIMP CpEpEngine::VerboseLogging(VARIANT_BOOL enable)
    30 {
    31 	verbose_mode = enable != VARIANT_FALSE;
    32 	return S_OK;
    33 }
    34 
    35 STDMETHODIMP CpEpEngine::PassiveMode(VARIANT_BOOL enable)
    36 {
    37 	::config_passive_mode(get_session(), enable != VARIANT_FALSE);
    38 	return S_OK;
    39 }
    40 
    41 STDMETHODIMP CpEpEngine::UnencryptedSubject(VARIANT_BOOL enable)
    42 {
    43 	::config_unencrypted_subject(get_session(), enable != VARIANT_FALSE);
    44 	return S_OK;
    45 }
    46 
    47 STDMETHODIMP CpEpEngine::ExportKey(BSTR fpr, BSTR * keyData)
    48 {
    49     assert(fpr);
    50     assert(keyData);
    51 
    52     if (!(fpr && keyData))
    53         return E_INVALIDARG;
    54 
    55     string _fpr = utf8_string(fpr);
    56     char *_key_data = NULL;
    57     size_t _size = 0;
    58 
    59     ::PEP_STATUS status = ::export_key(get_session(), _fpr.c_str(), &_key_data, &_size);
    60     assert(status != ::PEP_OUT_OF_MEMORY);
    61     if (status == ::PEP_OUT_OF_MEMORY)
    62         return E_OUTOFMEMORY;
    63 
    64     if (status != ::PEP_STATUS_OK)
    65         return FAIL(L"export_key", status);
    66 
    67     _bstr_t b_key_data(utf16_string(_key_data).c_str());
    68     pEp_free(_key_data);
    69     * keyData = b_key_data.Detach();
    70 
    71     return S_OK;
    72 }
    73 
    74 STDMETHODIMP CpEpEngine::Log(BSTR title, BSTR entity, BSTR description, BSTR comment)
    75 {
    76 	string _title;
    77 	string _entity;
    78 	string _description;
    79 	string _comment;
    80 	HRESULT result = S_OK;
    81 
    82 	assert(title);
    83 	if (title)
    84 		_title = utf8_string(title);
    85 	else
    86 		result = E_INVALIDARG;
    87 
    88 	assert(entity);
    89 	if (entity)
    90 		_entity = utf8_string(entity);
    91 	else
    92 		result = E_INVALIDARG;
    93 
    94 	if (description)
    95 		_description = utf8_string(description);
    96 
    97 	if (comment)
    98 		_comment = utf8_string(comment);
    99 
   100 	if (result != S_OK)
   101 		return result;
   102 
   103 	PEP_STATUS _status = ::log_event(get_session(), _title.c_str(), _entity.c_str(), _description.c_str(), _comment.c_str());
   104 	assert(_status == PEP_STATUS_OK);
   105 	if (_status != PEP_STATUS_OK)
   106 		return FAIL(L"log_event", _status);
   107 	else
   108 		return S_OK;
   109 }
   110 
   111 STDMETHODIMP CpEpEngine::Trustwords(BSTR fpr, BSTR lang, LONG max_words, BSTR * words)
   112 {
   113 	assert(fpr);
   114 	assert(max_words >= 0);
   115 	assert(words);
   116 
   117 	HRESULT result = S_OK;
   118 
   119 	string _fpr;
   120 	if (fpr)
   121 		_fpr = utf8_string(fpr);
   122 	else
   123 		result = E_INVALIDARG;
   124 
   125 	string _lang;
   126 	if (lang) {
   127 		_lang = utf8_string(lang);
   128 		if (_lang.length()) {
   129 			if (_lang.length() != 2)
   130 				result = E_INVALIDARG;
   131 		}
   132 		else
   133 			_lang = "en";
   134 	}
   135 	else
   136 		_lang = "en";
   137 
   138 	if (max_words < 0)
   139 		result = E_INVALIDARG;
   140 
   141 	if (words == NULL)
   142 		result = E_INVALIDARG;
   143 
   144 	if (result != S_OK)
   145 		return result;
   146 
   147 	char *_words = NULL;
   148 	size_t _wsize = 0;
   149 
   150 	PEP_STATUS status = ::trustwords(get_session(), _fpr.c_str(), _lang.c_str(), &_words, &_wsize, max_words);
   151 	assert(status != PEP_OUT_OF_MEMORY);
   152 	if (status == PEP_OUT_OF_MEMORY)
   153 		return E_OUTOFMEMORY;
   154 
   155 	if (_words == NULL) {
   156 		*words = NULL;
   157 		return FAIL(L"Trustwords: _words == NULL", status);
   158 	}
   159 	else {
   160 		*words = utf16_bstr(_words);
   161 		pEp_free(_words);
   162 		return S_OK;
   163 	}
   164 }
   165 
   166 STDMETHODIMP CpEpEngine::GetTrustwords(struct pEpIdentity *id1, struct pEpIdentity *id2, BSTR lang, VARIANT_BOOL full, BSTR *words)
   167 {
   168     assert(id1);
   169     assert(id2);
   170     assert(words);
   171 
   172     if (!(id1 && id2 && words))
   173     {
   174         return E_INVALIDARG;
   175     }
   176 
   177     HRESULT result = S_OK;
   178 
   179     pEp_identity* _id1 = NULL;
   180     pEp_identity* _id2 = NULL;
   181     string _lang;
   182     *words = NULL;
   183 
   184     try {
   185         _id1 = new_identity(id1);
   186         _id2 = new_identity(id2);
   187 
   188         if (!lang) {
   189             _lang = utf8_string(lang);
   190             if (_lang.length() == 0) {
   191                 _lang = "en";
   192             } else if (_lang.length() != 2) {
   193                 result = E_INVALIDARG;
   194             }
   195         } else {
   196             _lang = "en";
   197         }
   198     } catch (bad_alloc&) {
   199         result = E_OUTOFMEMORY;
   200     } catch (exception& ex) {
   201         result = FAIL(ex.what());
   202     }
   203 
   204     char* _words;
   205     size_t _size;
   206     if (result == S_OK) {
   207         auto status = ::get_trustwords(get_session(), _id1, _id2, _lang.c_str(), &_words, &_size, full != 0 /* convert variant bool to C bool */);
   208 
   209         if (status == PEP_OUT_OF_MEMORY) {
   210             result = E_OUTOFMEMORY;
   211         }
   212         else if (status == PEP_TRUSTWORD_NOT_FOUND) {
   213             result = FAIL(L"GetTrustwords: Trustword not found", status);
   214         }
   215         else if (!words) {
   216             result = FAIL(L"GetTrustwords: _words == NULL", status);
   217         }
   218         else {
   219             *words = utf16_bstr(_words);
   220             pEp_free(_words);
   221         }
   222     }
   223 
   224     free_identity(_id1);
   225     free_identity(_id2);
   226 
   227     return result;
   228 }
   229 
   230 
   231 STDMETHODIMP CpEpEngine::GetCrashdumpLog(LONG maxlines, BSTR * log)
   232 {
   233 	// COM-18: Currently, long == int on windows, so the check
   234 	// for INT_MAX is not strictly necessary. However, the code
   235 	// might get copy-pasted to other adapters in the future,
   236 	// so safety first...
   237 	assert(maxlines >= 0 && maxlines <= INT_MAX);
   238 	assert(log);
   239 
   240 	if (!(maxlines >= 0 && maxlines <= INT_MAX && log))
   241 		return E_INVALIDARG;
   242 
   243 	char *_log;
   244 	PEP_STATUS status = ::get_crashdump_log(get_session(), (int)maxlines, &_log);
   245 	assert(status == PEP_STATUS_OK);
   246 	if (status == PEP_OUT_OF_MEMORY)
   247 		return E_OUTOFMEMORY;
   248 	if (status != PEP_STATUS_OK)
   249 		return FAIL(L"GetCrashdumpLog", status);
   250     if (_log == NULL)
   251         return FAIL(L"GetCrashdumpLog: _log == NULL");
   252 
   253 	*log = utf16_bstr(_log);
   254 	pEp_free(_log);
   255 	return S_OK;
   256 }
   257 
   258 STDMETHODIMP CpEpEngine::GetEngineVersion(BSTR * engine_version)
   259 {
   260 	assert(engine_version);
   261 
   262 	if (!engine_version)
   263 		return E_INVALIDARG;
   264 
   265 	const char *_engine_version = ::get_engine_version();
   266 
   267 	if (_engine_version == NULL)
   268 		return FAIL(L"GetEngineVersion: _engine_version == NULL");
   269 
   270 	*engine_version = utf16_bstr(_engine_version);
   271 
   272 	return S_OK;
   273 }
   274 
   275 STDMETHODIMP CpEpEngine::GetLanguageList(BSTR * languages)
   276 {
   277 	assert(languages);
   278 
   279 	if (!languages)
   280 		return E_INVALIDARG;
   281 
   282 	char *_languages;
   283 	PEP_STATUS status = ::get_languagelist(get_session(), &_languages);
   284 	assert(status == PEP_STATUS_OK);
   285 	if (status == PEP_OUT_OF_MEMORY)
   286 		return E_OUTOFMEMORY;
   287     if (status != PEP_STATUS_OK)
   288         return FAIL(L"GetLanguageList", status);
   289 	if (_languages == NULL)
   290 		return FAIL(L"GetLanguageList: _languages == NULL");
   291 
   292 	*languages = utf16_bstr(_languages);
   293 	pEp_free(_languages);
   294 	return S_OK;
   295 }
   296 
   297 STDMETHODIMP CpEpEngine::StartKeyserverLookup()
   298 {
   299 	if (identity_queue.load())
   300 		return S_OK;
   301 
   302 	identity_queue.store(new identity_queue_t());
   303 	keymanagement_thread = new thread(::do_keymanagement, retrieve_next_identity, (void *)identity_queue.load());
   304 
   305 	return S_OK;
   306 }
   307 
   308 STDMETHODIMP CpEpEngine::StopKeyserverLookup()
   309 {
   310 	if (identity_queue.load() == NULL)
   311 		return S_OK;
   312 
   313 	identity_queue_t *_iq = identity_queue.load();
   314 	identity_queue.store(NULL);
   315 
   316 	pEp_identity_cpp shutdown;
   317 	_iq->push_front(shutdown);
   318 
   319 	keymanagement_thread->join();
   320 	delete keymanagement_thread;
   321 	keymanagement_thread = NULL;
   322 
   323 	delete _iq;
   324 
   325 	return S_OK;
   326 }
   327 
   328 STDMETHODIMP CpEpEngine::Myself(struct pEpIdentity *ident, struct pEpIdentity *result)
   329 {
   330 	assert(ident);
   331 	assert(result);
   332 
   333 	if (!(ident && result))
   334 		return E_INVALIDARG;
   335 
   336 	::pEp_identity *_ident = 0;
   337 	
   338 	try {
   339 		_ident = new_identity(ident);
   340 		assert(_ident);
   341 		if (_ident == NULL)
   342 			return E_OUTOFMEMORY;
   343 	}
   344 	catch (bad_alloc&) {
   345 		return E_OUTOFMEMORY;
   346 	}
   347 	catch (exception& ex) {
   348 		return FAIL(ex.what());;
   349 	}
   350 
   351 
   352 	// DEBUG CODE - REMOVE BEFORE RELEASE!
   353 	// SyncHandshakeResult handshakeResult;
   354 	//
   355 	// HRESULT res = Fire_ShowHandshake(ident, result, &handshakeResult);
   356 	// 
   357 	// HRESULT res2 = Fire_TestEvent(15, _bstr_t( "hallo"));
   358 
   359 	PEP_STATUS status = ::myself(get_session(), _ident);
   360 
   361 	if (status == PEP_STATUS_OK) {
   362 		assert(_ident->fpr);
   363 		copy_identity(result, _ident);
   364 		::free_identity(_ident);
   365 		return S_OK;
   366 	}
   367 	else {
   368 		::free_identity(_ident);
   369 		if (status == PEP_OUT_OF_MEMORY)
   370 			return E_OUTOFMEMORY;
   371 		else
   372 			return FAIL(L"myself", status);
   373 	}
   374 }
   375 
   376 STDMETHODIMP CpEpEngine::UpdateIdentity(struct pEpIdentity *ident, struct pEpIdentity *result)
   377 {
   378 	assert(ident);
   379 	assert(result);
   380 
   381 	if (!(ident && result))
   382 		return E_INVALIDARG;
   383 
   384 	::pEp_identity *_ident;
   385 	try {
   386 		_ident = new_identity(ident);
   387 	}
   388 	catch (bad_alloc&) {
   389 		return E_OUTOFMEMORY;
   390 	}
   391 	catch (exception& ex) {
   392 		return FAIL(ex.what());
   393 	}
   394 
   395 	assert(_ident);
   396 	if (_ident == NULL)
   397 		return E_OUTOFMEMORY;
   398 
   399 	PEP_STATUS status = ::update_identity(get_session(), _ident);
   400 
   401 	if (status == PEP_STATUS_OK) {
   402 		assert(_ident->fpr); // Guaranteed not NULL, but possibly empty string.
   403 		copy_identity(result, _ident);
   404 		::free_identity(_ident);
   405 		return S_OK;
   406 	}
   407 	else {
   408 		::free_identity(_ident);
   409 		if (status == PEP_OUT_OF_MEMORY)
   410 			return E_OUTOFMEMORY;
   411 		else
   412 			return FAIL(L"UpdateIdentity", status);
   413 	}
   414 }
   415 
   416 STDMETHODIMP CpEpEngine::KeyMistrusted(struct pEpIdentity *ident)
   417 {
   418 	::pEp_identity *_ident;
   419 
   420 	assert(ident);
   421 	if (!ident)
   422 		return E_INVALIDARG;
   423 
   424 	try {
   425 		_ident = new_identity(ident);
   426 	}
   427 	catch (bad_alloc&) {
   428 		return E_OUTOFMEMORY;
   429 	}
   430 	catch (exception& ex) {
   431 		return FAIL(ex.what());;
   432 	}
   433 
   434 	PEP_STATUS status = ::key_mistrusted(get_session(), _ident);
   435 	free_identity(_ident);
   436 
   437 	if (status == PEP_OUT_OF_MEMORY)
   438 		return E_OUTOFMEMORY;
   439 
   440 	if (status == PEP_KEY_NOT_FOUND)
   441 		return FAIL(L"key not found");
   442 
   443 	if (status != ::PEP_STATUS_OK)
   444 		return FAIL(L"cannot revoke compromized key", status);
   445 
   446 	return S_OK;
   447 }
   448 
   449 STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
   450 {
   451 	::pEp_identity *_ident;
   452 
   453 	assert(ident); 
   454 	
   455 	if (!ident)
   456 		return E_INVALIDARG;
   457 
   458 	try {
   459 		_ident = new_identity(ident);
   460 	}
   461 	catch (bad_alloc&) {
   462 		return E_OUTOFMEMORY;
   463 	}
   464 	catch (exception& ex) {
   465 		return FAIL(ex.what());;
   466 	}
   467 
   468 	PEP_STATUS status = ::key_reset_trust(get_session(), _ident);
   469 	free_identity(_ident);
   470 
   471 	if (status == PEP_OUT_OF_MEMORY)
   472 		return E_OUTOFMEMORY;
   473 
   474 	if (status == PEP_KEY_NOT_FOUND)
   475 		return FAIL(L"key not found");
   476 
   477 	if (status != ::PEP_STATUS_OK)
   478 		return FAIL(L"cannot reset trust", status);
   479 
   480 	return S_OK;
   481 }
   482 
   483 int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
   484 {
   485 	assert(ident);
   486 	assert(management);
   487 	if (!(ident && management))
   488 		return -1;
   489 
   490 	CpEpEngine *me = (CpEpEngine *)management;
   491 
   492 	if (me->identity_queue.load() == NULL)
   493 		return 0;
   494 
   495 	try {
   496 		me->identity_queue.load()->push_back(ident);
   497 	}
   498 	catch (exception&) {
   499 		return -1;
   500 	}
   501 
   502 	return 0;
   503 }
   504 
   505 ::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
   506 {
   507 	assert(management);
   508 	if (!management)
   509 		return NULL;
   510 
   511 	identity_queue_t *iq = (identity_queue_t *)management;
   512 
   513 	do /* poll queue */ {
   514 		if (iq->size())
   515 			break;
   516 		::Sleep(100);
   517 	} while (true);
   518 
   519 	::pEp_identity *_ident;
   520 	pEp_identity_cpp& ident = iq->front();
   521 
   522 	if (ident.address.size() == 0)
   523 		return NULL;
   524 
   525 	_ident = ident.to_pEp_identity();
   526 	iq->pop_front();
   527 
   528 	return _ident;
   529 }
   530 
   531 PEP_STATUS CpEpEngine::messageToSend(void * obj, message *msg)
   532 {
   533 	assert(msg);
   534 	assert(obj);
   535 	if (!(msg && obj))
   536 		return PEP_ILLEGAL_VALUE;
   537 
   538 	TextMessage _msg;
   539 	memset(&_msg, 0, sizeof(TextMessage));
   540 
   541 	text_message_from_C(&_msg, msg);
   542 	CpEpEngine *me = (CpEpEngine *)obj;
   543 	HRESULT r = me->Fire_MessageToSend(&_msg);
   544 	assert(r == S_OK);
   545 	clear_text_message(&_msg);
   546 	if (r == E_OUTOFMEMORY)
   547 		return PEP_OUT_OF_MEMORY;
   548 	if (r != S_OK)
   549 		return PEP_UNKNOWN_ERROR;
   550 
   551 	return PEP_STATUS_OK;
   552 }
   553 
   554 PEP_STATUS CpEpEngine::showHandshake(void * obj, pEp_identity *self, pEp_identity *partner)
   555 {
   556 	assert(self && partner);
   557 	if (!(self && partner))
   558 		return PEP_ILLEGAL_VALUE;
   559 
   560 	pEpIdentity _self;
   561 	copy_identity(&_self, self);
   562 	pEpIdentity _partner;
   563 	copy_identity(&_partner, partner);
   564 	CpEpEngine *me = (CpEpEngine *)obj;
   565 	SyncHandshakeResult _result;
   566 	HRESULT r = me->Fire_ShowHandshake(&_self, &_partner, &_result);
   567 	assert(r == S_OK);
   568 	clear_identity_s(_self);
   569 	clear_identity_s(_partner);
   570 	if (r == E_OUTOFMEMORY)
   571 		return PEP_OUT_OF_MEMORY;
   572 	if (r != S_OK)
   573 		return PEP_UNKNOWN_ERROR;
   574 
   575 	PEP_STATUS status = deliverHandshakeResult(me->get_session(), partner, (sync_handshake_result)(int)_result);
   576 	return status;
   577 }
   578 
   579 STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
   580 {
   581 	assert(fpr);
   582 	if (!fpr)
   583 		return E_INVALIDARG;
   584 
   585 	string _fpr = utf8_string(fpr);
   586 	PEP_STATUS status = ::blacklist_add(get_session(), _fpr.c_str());
   587 	assert(status == PEP_STATUS_OK);
   588 	if (status != PEP_STATUS_OK)
   589 		return FAIL(L"blacklist_add failed in pEp engine", status);
   590 
   591 	return S_OK;
   592 }
   593 
   594 STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
   595 {
   596 	assert(fpr);
   597 	if (!fpr)
   598 		return E_INVALIDARG;
   599 
   600 	string _fpr = utf8_string(fpr);
   601 	PEP_STATUS status = ::blacklist_delete(get_session(), _fpr.c_str());
   602 	assert(status == PEP_STATUS_OK);
   603 	if (status != PEP_STATUS_OK)
   604 		return FAIL(L"blacklist_delete failed in pEp engine", status);
   605 
   606 	return S_OK;
   607 }
   608 
   609 STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
   610 {
   611 	assert(fpr);
   612 	assert(listed);
   613 
   614 	if (!(fpr && listed))
   615 		return E_INVALIDARG;
   616 
   617 	string _fpr = utf8_string(fpr);
   618 	bool result;
   619 	PEP_STATUS status = ::blacklist_is_listed(get_session(), _fpr.c_str(), &result);
   620 	assert(status == PEP_STATUS_OK);
   621 	if (status != PEP_STATUS_OK)
   622 		return FAIL(L"blacklist_is_listed failed in pEp engine", status);
   623 
   624 	*listed = result ? VARIANT_TRUE : VARIANT_FALSE;
   625 	return S_OK;
   626 }
   627 
   628 STDMETHODIMP CpEpEngine::BlacklistRetrieve(SAFEARRAY **blacklist)
   629 {
   630 	assert(blacklist);
   631 
   632 	if (!blacklist)
   633 		return E_INVALIDARG;
   634 
   635 	::stringlist_t *_blacklist = NULL;
   636 	PEP_STATUS status = ::blacklist_retrieve(get_session(), &_blacklist);
   637 	assert(status == PEP_STATUS_OK);
   638 	if (status != PEP_STATUS_OK)
   639 		return FAIL(L"blacklist_retrieve failed in pEp engine", status);
   640 	assert(_blacklist);
   641 
   642 	*blacklist = string_array(_blacklist);
   643 	::free_stringlist(_blacklist);
   644 	return S_OK;
   645 }
   646 
   647 HRESULT CpEpEngine::error(_bstr_t msg)
   648 {
   649 	_bstr_t helpFile = L"";
   650 	_bstr_t source = L"pEp COM Adapter";
   651 
   652 	ICreateErrorInfo *cei;
   653 	if (SUCCEEDED(CreateErrorInfo(&cei))) {
   654 		cei->SetDescription(msg);
   655 		cei->SetGUID(__uuidof(IpEpEngine));
   656 		cei->SetHelpContext(0);
   657 		cei->SetHelpFile(helpFile);
   658 		cei->SetSource(source);
   659 
   660 		IErrorInfo *errinfo;
   661 		if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
   662 			SetErrorInfo(0, errinfo);
   663 			errinfo->Release();
   664 		}
   665 		cei->Release();
   666 	}
   667 	return E_FAIL;
   668 }
   669 
   670 HRESULT CpEpEngine::error(_bstr_t msg, PEP_STATUS status)
   671 {
   672     std::stringstream stream;
   673     stream << msg;
   674     stream << ": ";
   675     stream << std::hex << status;
   676     
   677     error(stream.str().c_str());
   678 
   679     if (status == ::PEP_OUT_OF_MEMORY)
   680         return E_OUTOFMEMORY;
   681 
   682     return E_FAIL;
   683 }
   684 
   685 STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags)
   686 {
   687 	assert(src);
   688 	assert(dst);
   689 
   690 	if (!(src && dst))
   691 		return E_INVALIDARG;
   692 
   693 	::message *_src = text_message_to_C(src);
   694 
   695 	// COM-19: Initialize msg_dst to NULL, or we end up calling
   696 	// free_message() below with a pointer to random garbage in
   697 	// case of an error in encrypt_message().
   698 	::message *msg_dst = NULL;
   699 	::stringlist_t *_extra = new_stringlist(extra); // can cope with NULL
   700 
   701 	// _PEP_enc_format is intentionally hardcoded to PEP_enc_PEP:
   702 	// 2016-10-02 14:10 < fdik> schabi: actually, all adapters now must use PEP_enc_PEP
   703 	PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t) flags;
   704 	PEP_STATUS status = ::encrypt_message(get_session(), _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
   705 	::free_stringlist(_extra);
   706 
   707 	if (status == PEP_STATUS_OK)
   708 		text_message_from_C(dst, msg_dst);
   709 	else
   710 		text_message_from_C(dst, _src);
   711 
   712 	::free_message(msg_dst);
   713 	::free_message(_src);
   714 
   715 	if (status == PEP_OUT_OF_MEMORY)
   716 		return E_OUTOFMEMORY;
   717 
   718 	// COM-41: Enhanced PEP status handling
   719 	if ((status > PEP_STATUS_OK && status < PEP_UNENCRYPTED) ||
   720 		status < PEP_STATUS_OK ||
   721 		status >= PEP_TRUSTWORD_NOT_FOUND)
   722 		return FAIL("Failure to encrypt message", status);
   723 
   724 	// Statii like PEP_UNENCRYPTED due to no private key
   725 	// should not be a catastrophic failure here. Using S_FALSE
   726 	// still allows clients to differentiate with S_OK,
   727 	// although this does not work out of the box with
   728 	// the standard .NET mapping of COM.
   729 	if (status != PEP_STATUS_OK)
   730 		return S_FALSE;
   731 
   732 	return S_OK;
   733 }
   734 
   735 STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
   736 {
   737 	assert(src);
   738 	assert(dst);
   739 	assert(keylist);
   740 	assert(flags);
   741 	assert(rating);
   742 
   743 	if (!(src && dst && keylist && flags && rating))
   744 		return E_INVALIDARG;
   745 
   746 	*keylist = NULL;
   747 	*rating = pEpRatingUndefined;
   748 
   749 	::message *_src = text_message_to_C(src);
   750 	::message *msg_dst = NULL;
   751 	::stringlist_t *_keylist = NULL;
   752 	::PEP_rating _rating;
   753 
   754 	PEP_decrypt_flags_t engineflags = 0;
   755 	PEP_STATUS status = ::decrypt_message(get_session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
   756 
   757 	*flags = (pEpDecryptFlags)engineflags;
   758 
   759 	if (msg_dst)
   760 		text_message_from_C(dst, msg_dst);
   761 
   762 	::free_message(_src);
   763 	::free_message(msg_dst);
   764 
   765 	if (_keylist) {
   766 		*keylist = string_array(_keylist);
   767 		free_stringlist(_keylist);
   768 	}
   769 
   770 	*rating = (pEpRating)_rating;
   771 
   772 	return S_OK;
   773 }
   774 
   775 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
   776 {
   777 	assert(msg);
   778 	assert(pVal);
   779 
   780 	if (!(msg  && pVal))
   781 		return E_INVALIDARG;
   782 
   783 	::message *_msg = text_message_to_C(msg);
   784 
   785 	PEP_rating _rating;
   786 	PEP_STATUS status = ::outgoing_message_rating(get_session(), _msg, &_rating);
   787 	if (status != PEP_STATUS_OK)
   788 		return FAIL(L"cannot get message rating", status);
   789 
   790 	*pVal = (pEpRating)_rating;
   791 	return S_OK;
   792 }
   793 
   794 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
   795 {
   796 	::pEp_identity *_ident;
   797 
   798 	assert(ident);
   799 	assert(pVal);
   800 
   801 	if (!(ident  && pVal))
   802 		return E_INVALIDARG;
   803 
   804 	try {
   805 		_ident = new_identity(ident);
   806 	}
   807 	catch (bad_alloc&) {
   808 		return E_OUTOFMEMORY;
   809 	}
   810 	catch (exception& ex) {
   811 		return FAIL(ex.what());;
   812 	}
   813 
   814 	PEP_rating _rating;
   815 	PEP_STATUS status = ::identity_rating(get_session(), _ident, &_rating);
   816 	free_identity(_ident);
   817 
   818 	if (status != PEP_STATUS_OK)
   819 		return FAIL(L"cannot get message color", status);
   820 
   821 	*pVal = (pEpRating)_rating;
   822 	return S_OK;
   823 }
   824 
   825 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
   826 {
   827 	assert(pVal);
   828 
   829 	if (!pVal)
   830 		return E_INVALIDARG;
   831 
   832 	PEP_rating engineRating = (PEP_rating)rating;
   833 	PEP_color _color = ::color_from_rating(engineRating);
   834 
   835 	*pVal = (pEpColor)_color;
   836 
   837 	return S_OK;
   838 }
   839 
   840 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
   841 {
   842 	::pEp_identity *_ident;
   843 
   844 	assert(ident);
   845 	assert(result);
   846 
   847 	if (!ident || !result)
   848 		return E_INVALIDARG;
   849 
   850 	try {
   851 		_ident = new_identity(ident);
   852 	}
   853 	catch (bad_alloc&) {
   854 		return E_OUTOFMEMORY;
   855 	}
   856 	catch (exception& ex) {
   857 		return FAIL(ex.what());;
   858 	}
   859 
   860 	if (verbose_mode) {
   861 		stringstream ss;
   862 		ss << "TrustPersonalKey called with ";
   863 		ss << utf8_string(ident->Address);
   864 		ss << L": ";
   865 		ss << ident->CommType;
   866 		verbose(ss.str());
   867 	}
   868 
   869 	PEP_STATUS status = ::trust_personal_key(get_session(), _ident);
   870 
   871 	if (verbose_mode) {
   872 		stringstream ss;
   873 		ss << "result ";
   874 		ss << status;
   875 		ss << " for ";
   876 		ss << _ident->address;
   877 		ss << L": ";
   878 		ss << _ident->comm_type;
   879 		verbose(ss.str());
   880 	}
   881 
   882 	if (status == PEP_STATUS_OK)
   883 		copy_identity(result, _ident);
   884 
   885 	free_identity(_ident);
   886 	if (status == PEP_OUT_OF_MEMORY)
   887 		return E_OUTOFMEMORY;
   888 	else if (status != PEP_STATUS_OK)
   889 		return FAIL(L"failure while executing TrustPersonalKey()", status);
   890 
   891 	return S_OK;
   892 }
   893 
   894 // keysync api
   895 
   896 void CpEpEngine::start_keysync()
   897 {
   898 	// acquire the lock
   899 	std::unique_lock<std::mutex> lock(keysync_mutex);
   900 
   901 	// Assert if we're not already running.
   902     assert(!this->keysync_thread);
   903 
   904 	// Ensure we are not aborting the new thread due to a
   905 	// left over flag.
   906 	keysync_abort_requested = false;
   907 
   908 	// Init our keysync session
   909 	PEP_STATUS status = ::init(&keysync_session);
   910 	::register_sync_callbacks(keysync_session, (void*)this, messageToSend, showHandshake, inject_sync_msg, retrieve_next_sync_msg);
   911 	assert(status == PEP_STATUS_OK);
   912 
   913     attach_sync_session(get_session(), keysync_session);
   914 
   915     // We need to marshal the callbacks to the keysync thread
   916     LPSTREAM marshaled_callbacks;
   917 
   918     auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
   919     assert(result == S_OK);
   920 
   921 	// Star the keysync thread
   922 	keysync_thread = new thread(do_keysync_in_thread, this, marshaled_callbacks);
   923 }
   924 
   925 void CpEpEngine::do_keysync_in_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks) 
   926 {
   927     assert(self);
   928 	assert(marshaled_callbacks);
   929 
   930     // We need to initialize COM here for successfull delivery of the callbacks.
   931     // As we don't create any COM instances in our thread, the COMINIT value is
   932     // currently irrelevant, so we go with the safest value.
   933     auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
   934     assert(res == S_OK);
   935 
   936     LPVOID vp;
   937 
   938     res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
   939     assert(SUCCEEDED(res));
   940 
   941     self->client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
   942 
   943     ::do_sync_protocol(self->keysync_session, self);
   944 
   945     self->client_callbacks_on_sync_thread->Release();
   946 
   947     self->client_callbacks_on_sync_thread = NULL;
   948 
   949     CoUninitialize();
   950 }
   951 
   952 void CpEpEngine::stop_keysync()
   953 {
   954 	// acquire the lock
   955 	std::unique_lock<std::mutex> lock(keysync_mutex);
   956 
   957     // Do nothing if keysync is not running.
   958     if (!keysync_thread)
   959         return;
   960 
   961     assert(!keysync_abort_requested);
   962 	// signal that we're gonna abort
   963 	keysync_abort_requested = true;
   964 
   965 	// Notify the keysync thread
   966 	keysync_condition.notify_all();
   967 
   968 	// Wait for the other thread to finish and clean up
   969 	while (keysync_abort_requested)
   970 		keysync_condition.wait(lock);
   971 
   972 	// collect the child thread for the thread to end
   973 	keysync_thread->join();
   974 
   975 	// clean up
   976 	delete keysync_thread;
   977 	keysync_thread = NULL;
   978 
   979     ::detach_sync_session(get_session());
   980 	::unregister_sync_callbacks(keysync_session);
   981 	release(keysync_session);
   982     keysync_session = NULL;
   983 }
   984 
   985 int CpEpEngine::inject_sync_msg(void * msg, void * management)
   986 {
   987 	assert(msg);
   988 	assert(management);
   989 	// check argument
   990 	if (!msg)
   991 		return E_INVALIDARG;
   992 	if (!management)
   993 		return ERROR_INVALID_HANDLE;
   994 
   995 	CpEpEngine* me = (CpEpEngine*)management;
   996 
   997 	// acquire the lock
   998 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
   999 
  1000 	// check whether we're in a valid state running:
  1001 	if (!me->keysync_thread)
  1002 		return E_ASYNC_OPERATION_NOT_STARTED;
  1003 
  1004 	// queue the message
  1005 	me->keysync_queue.push(msg);
  1006 
  1007 	// notify the receivers
  1008 	me->keysync_condition.notify_all();
  1009 
  1010     return S_OK;
  1011 }
  1012 
  1013 void * CpEpEngine::retrieve_next_sync_msg(void * management)
  1014 {
  1015 	// sanity check
  1016 	assert(management);
  1017 	if (!management)
  1018 		return NULL;
  1019 
  1020 	CpEpEngine* me = (CpEpEngine*)management;
  1021 
  1022 	// acquire the lock
  1023 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
  1024 
  1025 	// as long as we're supposed to be active 
  1026 	// (we won't wait for the queue to empty currently...)
  1027 	while (!me->keysync_abort_requested)
  1028 	{
  1029 		// If the queue is empty, wait for a signal, and try again.
  1030 		if (me->keysync_queue.empty())
  1031 		{
  1032 			me->keysync_condition.wait(lock);
  1033 			continue;
  1034 		}
  1035 
  1036 		// Pop the message and return it.
  1037 		void* msg = me->keysync_queue.front();
  1038 		assert(msg);
  1039 
  1040 		me->keysync_queue.pop();
  1041 
  1042 		return msg;
  1043 	}
  1044 
  1045 	// we acknowledge that we're quitting...
  1046 	me->keysync_abort_requested = false;
  1047 
  1048 	// We signal the main thread that we got his signal
  1049 	// so it can gain the mutex again and call join() on us.
  1050 	me->keysync_condition.notify_all();
  1051 
  1052 	// and tell the pep engine we're done.
  1053 	return NULL;
  1054 }
  1055 
  1056 
  1057 // Event callbacks
  1058 
  1059 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
  1060 {
  1061     // check for valid parameter
  1062     if (!new_callbacks)
  1063         return E_INVALIDARG;
  1064 
  1065     // don't allow double registration.
  1066     if (this->client_callbacks)
  1067         return E_ILLEGAL_STATE_CHANGE;
  1068 
  1069     this->client_callbacks = new_callbacks;
  1070     new_callbacks->AddRef();
  1071 
  1072 	start_keysync();
  1073 
  1074 	return S_OK;
  1075 }
  1076 
  1077 STDMETHODIMP CpEpEngine::UnregisterCallbacks()
  1078 {
  1079     // don't allow double deregistration.
  1080     // S_FALSE still is no error (as double deregistration is not fatal).
  1081     if (!this->client_callbacks)
  1082         return S_FALSE;
  1083 
  1084     stop_keysync();
  1085 
  1086     this->client_callbacks->Release();
  1087 
  1088     this->client_callbacks = NULL;
  1089 
  1090     return S_OK;
  1091 }
  1092 
  1093 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
  1094 	assert(keyinfo_list);
  1095 
  1096 	if (keyinfo_list == NULL)
  1097 		return E_INVALIDARG;
  1098 
  1099 	string _pattern = "";
  1100 	if (search_pattern)
  1101 		_pattern = utf8_string(search_pattern);
  1102 	::stringpair_list_t* _keyinfo_list = NULL;
  1103 
  1104 	PEP_STATUS status = ::OpenPGP_list_keyinfo(get_session(), _pattern.c_str(), &_keyinfo_list);
  1105 	assert(status != PEP_OUT_OF_MEMORY);
  1106 	if (status == PEP_OUT_OF_MEMORY)
  1107 		return E_OUTOFMEMORY;
  1108 
  1109 	if (status != ::PEP_STATUS_OK)
  1110 		return FAIL(L"OpenPGP_list_keyinfo", status);
  1111 
  1112 	if (_keyinfo_list && _keyinfo_list->value) {
  1113 		::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
  1114 	}
  1115 	else {
  1116 		::free_stringpair_list(_keyinfo_list);
  1117 		return FAIL(L"OpenPGP_list_keyinfo: no keys found");
  1118 	}
  1119 
  1120 	::free_stringpair_list(_keyinfo_list);
  1121 	return S_OK;
  1122 
  1123 }
  1124 
  1125 HRESULT CpEpEngine::Fire_MessageToSend(TextMessage * msg)
  1126 {
  1127 	assert(msg);
  1128     assert(this->client_callbacks_on_sync_thread);
  1129 
  1130 	if (!msg)
  1131 		return E_INVALIDARG;
  1132 
  1133 	if (!this->client_callbacks_on_sync_thread)
  1134 		return E_ILLEGAL_METHOD_CALL;
  1135 
  1136     auto result = this->client_callbacks_on_sync_thread->MessageToSend(msg);
  1137 
  1138 	return result;
  1139 }
  1140 
  1141 HRESULT CpEpEngine::Fire_ShowHandshake(pEpIdentity * self, pEpIdentity * partner, SyncHandshakeResult * result)
  1142 {
  1143 	assert(self);
  1144 	assert(partner);
  1145 	assert(result);
  1146     assert(this->client_callbacks_on_sync_thread);
  1147 
  1148 	if (!(self && partner && result))
  1149 		return E_INVALIDARG;
  1150 	if (!this->client_callbacks_on_sync_thread)
  1151 		return E_ILLEGAL_METHOD_CALL;
  1152     	
  1153 	auto res = this->client_callbacks_on_sync_thread->ShowHandshake(self, partner, result);
  1154 		
  1155 	return res;	
  1156 }