CpEpEngine.cpp
author Markus Schaber <markus@pep-security.net>
Wed, 02 Nov 2016 23:30:50 +0100
changeset 190 8d1c4f057dea
parent 189 5361d677afdb
child 191 e6e934d5b62d
permissions -rw-r--r--
COM-38: Trustwords Function with Identities

Expose the get_trustwords function from ENGINE-109 via COM interface
as GetTrustWords().
     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 == NULL || keyData == NULL)
    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 == NULL || id2 == NULL || words == NULL)
   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 	assert(maxlines >= 0);
   234 	assert(log);
   235 
   236 	if (!(maxlines >= 0 && log))
   237 		return E_INVALIDARG;
   238 
   239 	char *_log;
   240 	PEP_STATUS status = ::get_crashdump_log(get_session(), (int)maxlines, &_log);
   241 	assert(status == PEP_STATUS_OK);
   242 	if (status == PEP_OUT_OF_MEMORY)
   243 		return E_OUTOFMEMORY;
   244 	if (status != PEP_STATUS_OK)
   245 		return FAIL(L"GetCrashdumpLog", status);
   246     if (_log == NULL)
   247         return FAIL(L"GetCrashdumpLog: _log == NULL");
   248 
   249 	*log = utf16_bstr(_log);
   250 	pEp_free(_log);
   251 	return S_OK;
   252 }
   253 
   254 STDMETHODIMP CpEpEngine::GetEngineVersion(BSTR * engine_version)
   255 {
   256 	assert(engine_version);
   257 
   258 	if (!engine_version)
   259 		return E_INVALIDARG;
   260 
   261 	const char *_engine_version = ::get_engine_version();
   262 
   263 	if (_engine_version == NULL)
   264 		return FAIL(L"GetEngineVersion: _engine_version == NULL");
   265 
   266 	*engine_version = utf16_bstr(_engine_version);
   267 
   268 	return S_OK;
   269 }
   270 
   271 STDMETHODIMP CpEpEngine::GetLanguageList(BSTR * languages)
   272 {
   273 	assert(languages);
   274 
   275 	if (!languages)
   276 		return E_INVALIDARG;
   277 
   278 	char *_languages;
   279 	PEP_STATUS status = ::get_languagelist(get_session(), &_languages);
   280 	assert(status == PEP_STATUS_OK);
   281 	if (status == PEP_OUT_OF_MEMORY)
   282 		return E_OUTOFMEMORY;
   283     if (status != PEP_STATUS_OK)
   284         return FAIL(L"GetLanguageList", status);
   285 	if (_languages == NULL)
   286 		return FAIL(L"GetLanguageList: _languages == NULL");
   287 
   288 	*languages = utf16_bstr(_languages);
   289 	pEp_free(_languages);
   290 	return S_OK;
   291 }
   292 
   293 STDMETHODIMP CpEpEngine::StartKeyserverLookup()
   294 {
   295 	if (identity_queue.load())
   296 		return S_OK;
   297 
   298 	identity_queue.store(new identity_queue_t());
   299 	keymanagement_thread = new thread(::do_keymanagement, retrieve_next_identity, (void *)identity_queue.load());
   300 
   301 	return S_OK;
   302 }
   303 
   304 STDMETHODIMP CpEpEngine::StopKeyserverLookup()
   305 {
   306 	if (identity_queue.load() == NULL)
   307 		return S_OK;
   308 
   309 	identity_queue_t *_iq = identity_queue.load();
   310 	identity_queue.store(NULL);
   311 
   312 	pEp_identity_cpp shutdown;
   313 	_iq->push_front(shutdown);
   314 
   315 	keymanagement_thread->join();
   316 	delete keymanagement_thread;
   317 	keymanagement_thread = NULL;
   318 
   319 	delete _iq;
   320 
   321 	return S_OK;
   322 }
   323 
   324 STDMETHODIMP CpEpEngine::Myself(struct pEpIdentity *ident, struct pEpIdentity *result)
   325 {
   326 	assert(ident);
   327 	assert(result);
   328 
   329 	if (ident == NULL || result == NULL)
   330 		return E_INVALIDARG;
   331 
   332 	::pEp_identity *_ident = new_identity(ident);
   333 	assert(_ident);
   334 	if (_ident == NULL)
   335 		return E_OUTOFMEMORY;
   336 
   337 	// DEBUG CODE - REMOVE BEFORE RELEASE!
   338 	// SyncHandshakeResult handshakeResult;
   339 	//
   340 	// HRESULT res = Fire_ShowHandshake(ident, result, &handshakeResult);
   341 	// 
   342 	// HRESULT res2 = Fire_TestEvent(15, _bstr_t( "hallo"));
   343 
   344 	PEP_STATUS status = ::myself(get_session(), _ident);
   345 
   346 	if (status == PEP_STATUS_OK) {
   347 		assert(_ident->fpr);
   348 		copy_identity(result, _ident);
   349 		::free_identity(_ident);
   350 		return S_OK;
   351 	}
   352 	else {
   353 		::free_identity(_ident);
   354 		if (status == PEP_OUT_OF_MEMORY)
   355 			return E_OUTOFMEMORY;
   356 		else
   357 			return FAIL(L"myself", status);
   358 	}
   359 }
   360 
   361 STDMETHODIMP CpEpEngine::UpdateIdentity(struct pEpIdentity *ident, struct pEpIdentity *result)
   362 {
   363 	assert(ident);
   364 	assert(result);
   365 
   366 	if (ident == NULL || result == NULL)
   367 		return E_INVALIDARG;
   368 
   369 	::pEp_identity *_ident = new_identity(ident);
   370 	assert(_ident);
   371 	if (_ident == NULL)
   372 		return E_OUTOFMEMORY;
   373 
   374 	PEP_STATUS status = ::update_identity(get_session(), _ident);
   375 
   376 	if (status == PEP_STATUS_OK) {
   377 		assert(_ident->fpr);
   378 		copy_identity(result, _ident);
   379 		::free_identity(_ident);
   380 		return S_OK;
   381 	}
   382 	else {
   383 		::free_identity(_ident);
   384 		if (status == PEP_OUT_OF_MEMORY)
   385 			return E_OUTOFMEMORY;
   386 		else
   387 			return FAIL(L"UpdateIdentity", status);
   388 	}
   389 }
   390 
   391 STDMETHODIMP CpEpEngine::KeyMistrusted(struct pEpIdentity *ident)
   392 {
   393 	::pEp_identity *_ident;
   394 
   395 	assert(ident);
   396 
   397 	try {
   398 		_ident = new_identity(ident);
   399 	}
   400 	catch (bad_alloc&) {
   401 		return E_OUTOFMEMORY;
   402 	}
   403 	catch (exception&) {
   404 		return E_FAIL;
   405 	}
   406 
   407 	PEP_STATUS status = ::key_mistrusted(get_session(), _ident);
   408 	free_identity(_ident);
   409 
   410 	if (status == PEP_OUT_OF_MEMORY)
   411 		return E_OUTOFMEMORY;
   412 
   413 	if (status == PEP_KEY_NOT_FOUND)
   414 		return FAIL(L"key not found");
   415 
   416 	if (status != ::PEP_STATUS_OK)
   417 		return FAIL(L"cannot revoke compromized key", status);
   418 
   419 	return S_OK;
   420 }
   421 
   422 STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
   423 {
   424 	::pEp_identity *_ident;
   425 
   426 	assert(ident);
   427 
   428 	try {
   429 		_ident = new_identity(ident);
   430 	}
   431 	catch (bad_alloc&) {
   432 		return E_OUTOFMEMORY;
   433 	}
   434 	catch (exception&) {
   435 		return E_FAIL;
   436 	}
   437 
   438 	PEP_STATUS status = ::key_reset_trust(get_session(), _ident);
   439 	free_identity(_ident);
   440 
   441 	if (status == PEP_OUT_OF_MEMORY)
   442 		return E_OUTOFMEMORY;
   443 
   444 	if (status == PEP_KEY_NOT_FOUND)
   445 		return FAIL(L"key not found");
   446 
   447 	if (status != ::PEP_STATUS_OK)
   448 		return FAIL(L"cannot reset trust", status);
   449 
   450 	return S_OK;
   451 }
   452 
   453 int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
   454 {
   455 	assert(ident);
   456 	assert(management);
   457 	if (!(ident && management))
   458 		return -1;
   459 
   460 	CpEpEngine *me = (CpEpEngine *)management;
   461 
   462 	if (me->identity_queue.load() == NULL)
   463 		return 0;
   464 
   465 	try {
   466 		me->identity_queue.load()->push_back(ident);
   467 	}
   468 	catch (exception&) {
   469 		return -1;
   470 	}
   471 
   472 	return 0;
   473 }
   474 
   475 ::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
   476 {
   477 	assert(management);
   478 	identity_queue_t *iq = (identity_queue_t *)management;
   479 
   480 	do /* poll queue */ {
   481 		if (iq->size())
   482 			break;
   483 		::Sleep(100);
   484 	} while (true);
   485 
   486 	::pEp_identity *_ident;
   487 	pEp_identity_cpp& ident = iq->front();
   488 
   489 	if (ident.address.size() == 0)
   490 		return NULL;
   491 
   492 	_ident = ident.to_pEp_identity();
   493 	iq->pop_front();
   494 
   495 	return _ident;
   496 }
   497 
   498 PEP_STATUS CpEpEngine::messageToSend(void * obj, message *msg)
   499 {
   500 	assert(msg);
   501 	if (msg == NULL)
   502 		return PEP_ILLEGAL_VALUE;
   503 
   504 	TextMessage _msg;
   505 	memset(&_msg, 0, sizeof(TextMessage));
   506 
   507 	text_message_from_C(&_msg, msg);
   508 	CpEpEngine *me = (CpEpEngine *)obj;
   509 	HRESULT r = me->Fire_MessageToSend(&_msg);
   510 	assert(r == S_OK);
   511 	clear_text_message(&_msg);
   512 	if (r == E_OUTOFMEMORY)
   513 		return PEP_OUT_OF_MEMORY;
   514 	if (r != S_OK)
   515 		return PEP_UNKNOWN_ERROR;
   516 
   517 	return PEP_STATUS_OK;
   518 }
   519 
   520 PEP_STATUS CpEpEngine::showHandshake(void * obj, pEp_identity *self, pEp_identity *partner)
   521 {
   522 	assert(self && partner);
   523 	if (!(self && partner))
   524 		return PEP_ILLEGAL_VALUE;
   525 
   526 	pEpIdentity _self;
   527 	copy_identity(&_self, self);
   528 	pEpIdentity _partner;
   529 	copy_identity(&_partner, partner);
   530 	CpEpEngine *me = (CpEpEngine *)obj;
   531 	SyncHandshakeResult _result;
   532 	HRESULT r = me->Fire_ShowHandshake(&_self, &_partner, &_result);
   533 	assert(r == S_OK);
   534 	clear_identity_s(_self);
   535 	clear_identity_s(_partner);
   536 	if (r == E_OUTOFMEMORY)
   537 		return PEP_OUT_OF_MEMORY;
   538 	if (r != S_OK)
   539 		return PEP_UNKNOWN_ERROR;
   540 
   541 	PEP_STATUS status = deliverHandshakeResult(me->get_session(), partner, (sync_handshake_result)(int)_result);
   542 	return status;
   543 }
   544 
   545 STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
   546 {
   547 	assert(fpr);
   548 
   549 	string _fpr = utf8_string(fpr);
   550 	PEP_STATUS status = ::blacklist_add(get_session(), _fpr.c_str());
   551 	assert(status == PEP_STATUS_OK);
   552 	if (status != PEP_STATUS_OK)
   553 		return FAIL(L"blacklist_add failed in pEp engine", status);
   554 
   555 	return S_OK;
   556 }
   557 
   558 STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
   559 {
   560 	assert(fpr);
   561 
   562 	string _fpr = utf8_string(fpr);
   563 	PEP_STATUS status = ::blacklist_delete(get_session(), _fpr.c_str());
   564 	assert(status == PEP_STATUS_OK);
   565 	if (status != PEP_STATUS_OK)
   566 		return FAIL(L"blacklist_delete failed in pEp engine", status);
   567 
   568 	return S_OK;
   569 }
   570 
   571 STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
   572 {
   573 	assert(fpr);
   574 	assert(listed);
   575 
   576 	string _fpr = utf8_string(fpr);
   577 	bool result;
   578 	PEP_STATUS status = ::blacklist_is_listed(get_session(), _fpr.c_str(), &result);
   579 	assert(status == PEP_STATUS_OK);
   580 	if (status != PEP_STATUS_OK)
   581 		return FAIL(L"blacklist_is_listed failed in pEp engine", status);
   582 
   583 	*listed = result ? VARIANT_TRUE : VARIANT_FALSE;
   584 	return S_OK;
   585 }
   586 
   587 STDMETHODIMP CpEpEngine::BlacklistRetrieve(SAFEARRAY **blacklist)
   588 {
   589 	assert(blacklist);
   590 
   591 	::stringlist_t *_blacklist = NULL;
   592 	PEP_STATUS status = ::blacklist_retrieve(get_session(), &_blacklist);
   593 	assert(status == PEP_STATUS_OK);
   594 	if (status != PEP_STATUS_OK)
   595 		return FAIL(L"blacklist_retrieve failed in pEp engine", status);
   596 	assert(_blacklist);
   597 
   598 	*blacklist = string_array(_blacklist);
   599 	return S_OK;
   600 }
   601 
   602 HRESULT CpEpEngine::error(_bstr_t msg)
   603 {
   604 	_bstr_t helpFile = L"";
   605 	_bstr_t source = L"pEp COM Adapter";
   606 
   607 	ICreateErrorInfo *cei;
   608 	if (SUCCEEDED(CreateErrorInfo(&cei))) {
   609 		cei->SetDescription(msg);
   610 		cei->SetGUID(__uuidof(IpEpEngine));
   611 		cei->SetHelpContext(0);
   612 		cei->SetHelpFile(helpFile);
   613 		cei->SetSource(source);
   614 
   615 		IErrorInfo *errinfo;
   616 		if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
   617 			SetErrorInfo(0, errinfo);
   618 			errinfo->Release();
   619 		}
   620 		cei->Release();
   621 	}
   622 	return E_FAIL;
   623 }
   624 
   625 HRESULT CpEpEngine::error(_bstr_t msg, PEP_STATUS status)
   626 {
   627     std::stringstream stream;
   628     stream << msg;
   629     stream << ": ";
   630     stream << std::hex << status;
   631     
   632     error(stream.str().c_str());
   633 
   634     if (status == ::PEP_OUT_OF_MEMORY)
   635         return E_OUTOFMEMORY;
   636 
   637     return E_FAIL;
   638 }
   639 
   640 STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags)
   641 {
   642 	assert(src);
   643 	assert(dst);
   644 
   645 	::message *_src = text_message_to_C(src);
   646 	::message *msg_dst;
   647 	::stringlist_t *_extra = new_stringlist(extra);
   648 
   649 	// _PEP_enc_format is intentionally hardcoded to PEP_enc_PEP:
   650 	// 2016-10-02 14:10 < fdik> schabi: actually, all adapters now must use PEP_enc_PEP
   651 	PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t) flags;
   652 	PEP_STATUS status = ::encrypt_message(get_session(), _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
   653 	::free_stringlist(_extra);
   654 
   655 	if (status == PEP_STATUS_OK)
   656 		text_message_from_C(dst, msg_dst);
   657 	else
   658 		text_message_from_C(dst, _src);
   659 
   660 	::free_message(msg_dst);
   661 	::free_message(_src);
   662 
   663 	if (status == PEP_OUT_OF_MEMORY)
   664 		return E_OUTOFMEMORY;
   665 
   666 	return S_OK;
   667 }
   668 
   669 STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
   670 {
   671 	assert(src);
   672 	assert(dst);
   673 	assert(keylist);
   674 	assert(rating);
   675 
   676 	*keylist = NULL;
   677 	*rating = pEpRatingUndefined;
   678 
   679 	::message *_src = text_message_to_C(src);
   680 	::message *msg_dst = NULL;
   681 	::stringlist_t *_keylist;
   682 	::PEP_rating _rating;
   683 
   684 	PEP_decrypt_flags_t engineflags = 0;
   685 	PEP_STATUS status = ::decrypt_message(get_session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
   686 
   687 	*flags = (pEpDecryptFlags)engineflags;
   688 
   689 	if (msg_dst)
   690 		text_message_from_C(dst, msg_dst);
   691 
   692 	::free_message(_src);
   693 	::free_message(msg_dst);
   694 
   695 	if (_keylist) {
   696 		*keylist = string_array(_keylist);
   697 		free_stringlist(_keylist);
   698 	}
   699 
   700 	*rating = (pEpRating)_rating;
   701 
   702 	return S_OK;
   703 }
   704 
   705 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
   706 {
   707 	assert(msg);
   708 	assert(pVal);
   709 
   710 	::message *_msg = text_message_to_C(msg);
   711 
   712 	PEP_rating _rating;
   713 	PEP_STATUS status = ::outgoing_message_rating(get_session(), _msg, &_rating);
   714 	if (status != PEP_STATUS_OK)
   715 		return FAIL(L"cannot get message rating", status);
   716 
   717 	*pVal = (pEpRating)_rating;
   718 	return S_OK;
   719 }
   720 
   721 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
   722 {
   723 	::pEp_identity *_ident;
   724 
   725 	assert(ident);
   726 	assert(pVal);
   727 
   728 	try {
   729 		_ident = new_identity(ident);
   730 	}
   731 	catch (bad_alloc&) {
   732 		return E_OUTOFMEMORY;
   733 	}
   734 	catch (exception&) {
   735 		return E_FAIL;
   736 	}
   737 
   738 	PEP_rating _rating;
   739 	PEP_STATUS status = ::identity_rating(get_session(), _ident, &_rating);
   740 	free_identity(_ident);
   741 	if (status != PEP_STATUS_OK)
   742 		return FAIL(L"cannot get message color", status);
   743 
   744 	*pVal = (pEpRating)_rating;
   745 	return S_OK;
   746 }
   747 
   748 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
   749 {
   750 	assert(pVal);
   751 
   752 	PEP_rating engineRating = (PEP_rating)rating;
   753 	PEP_color _color = ::color_from_rating(engineRating);
   754 
   755 	*pVal = (pEpColor)_color;
   756 
   757 	return S_OK;
   758 }
   759 
   760 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
   761 {
   762 	::pEp_identity *_ident;
   763 
   764 	assert(ident);
   765 	assert(result);
   766 
   767 	try {
   768 		_ident = new_identity(ident);
   769 	}
   770 	catch (bad_alloc&) {
   771 		return E_OUTOFMEMORY;
   772 	}
   773 	catch (exception&) {
   774 		return E_FAIL;
   775 	}
   776 
   777 	if (verbose_mode) {
   778 		stringstream ss;
   779 		ss << "TrustPersonalKey called with ";
   780 		ss << utf8_string(ident->Address);
   781 		ss << L": ";
   782 		ss << ident->CommType;
   783 		verbose(ss.str());
   784 	}
   785 
   786 	PEP_STATUS status = ::trust_personal_key(get_session(), _ident);
   787 
   788 	if (verbose_mode) {
   789 		stringstream ss;
   790 		ss << "result ";
   791 		ss << status;
   792 		ss << " for ";
   793 		ss << _ident->address;
   794 		ss << L": ";
   795 		ss << _ident->comm_type;
   796 		verbose(ss.str());
   797 	}
   798 
   799 	if (status == PEP_STATUS_OK)
   800 		copy_identity(result, _ident);
   801 
   802 	free_identity(_ident);
   803 	if (status == PEP_OUT_OF_MEMORY)
   804 		return E_OUTOFMEMORY;
   805 	else if (status != PEP_STATUS_OK)
   806 		return FAIL(L"failure while executing TrustPersonalKey()", status);
   807 
   808 	return S_OK;
   809 }
   810 
   811 // keysync api
   812 
   813 void CpEpEngine::start_keysync()
   814 {
   815 	// acquire the lock
   816 	std::unique_lock<std::mutex> lock(keysync_mutex);
   817 
   818 	// Assert if we're not already running.
   819     assert(!this->keysync_thread);
   820 
   821 	// Ensure we are not aborting the new thread due to a
   822 	// left over flag.
   823 	keysync_abort_requested = false;
   824 
   825 	// Init our keysync session
   826 	PEP_STATUS status = ::init(&keysync_session);
   827 	::register_sync_callbacks(keysync_session, (void*)this, messageToSend, showHandshake, inject_sync_msg, retrieve_next_sync_msg);
   828 	assert(status == PEP_STATUS_OK);
   829 
   830     attach_sync_session(get_session(), keysync_session);
   831 
   832     // We need to marshal the callbacks to the keysync thread
   833     LPSTREAM marshaled_callbacks;
   834 
   835     auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
   836     assert(result == S_OK);
   837 
   838 	// Star the keysync thread
   839 	keysync_thread = new thread(do_keysync_in_thread, this, marshaled_callbacks);
   840 }
   841 
   842 void CpEpEngine::do_keysync_in_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks) 
   843 {
   844     assert(self);
   845     // We need to initialize COM here for successfull delivery of the callbacks.
   846     // As we don't create any COM instances in our thread, the COMINIT value is
   847     // currently irrelevant, so we go with the safest value.
   848     auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
   849     assert(res == S_OK);
   850 
   851     LPVOID vp;
   852 
   853     res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
   854     assert(SUCCEEDED(res));
   855 
   856     self->client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
   857 
   858     ::do_sync_protocol(self->keysync_session, self);
   859 
   860     self->client_callbacks_on_sync_thread->Release();
   861 
   862     self->client_callbacks_on_sync_thread = NULL;
   863 
   864     CoUninitialize();
   865 }
   866 
   867 void CpEpEngine::stop_keysync()
   868 {
   869 	// acquire the lock
   870 	std::unique_lock<std::mutex> lock(keysync_mutex);
   871 
   872     // Do nothing if keysync is not running.
   873     if (!keysync_thread)
   874         return;
   875 
   876     assert(!keysync_abort_requested);
   877 	// signal that we're gonna abort
   878 	keysync_abort_requested = true;
   879 
   880 	// Notify the keysync thread
   881 	keysync_condition.notify_all();
   882 
   883 	// Wait for the other thread to finish and clean up
   884 	while (keysync_abort_requested)
   885 		keysync_condition.wait(lock);
   886 
   887 	// collect the child thread for the thread to end
   888 	keysync_thread->join();
   889 
   890 	// clean up
   891 	delete keysync_thread;
   892 	keysync_thread = NULL;
   893 
   894     ::detach_sync_session(get_session());
   895 	::unregister_sync_callbacks(keysync_session);
   896 	release(keysync_session);
   897     keysync_session = NULL;
   898 }
   899 
   900 int CpEpEngine::inject_sync_msg(void * msg, void * management)
   901 {
   902 	// check argument
   903 	if (!msg)
   904 		return E_INVALIDARG;
   905 	if (!management)
   906 		return ERROR_INVALID_HANDLE;
   907 
   908 	CpEpEngine* me = (CpEpEngine*)management;
   909 
   910 	// acquire the lock
   911 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
   912 
   913 	// check whether we're in a valid state running:
   914 	if (!me->keysync_thread)
   915 		return E_ASYNC_OPERATION_NOT_STARTED;
   916 
   917 	// queue the message
   918 	me->keysync_queue.push(msg);
   919 
   920 	// notify the receivers
   921 	me->keysync_condition.notify_all();
   922 
   923     return S_OK;
   924 }
   925 
   926 void * CpEpEngine::retrieve_next_sync_msg(void * management)
   927 {
   928 	// sanity check
   929 	assert(management);
   930 
   931 	CpEpEngine* me = (CpEpEngine*)management;
   932 
   933 	// acquire the lock
   934 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
   935 
   936 	// as long as we're supposed to be active 
   937 	// (we won't wait for the queue to empty currently...)
   938 	while (!me->keysync_abort_requested)
   939 	{
   940 		// If the queue is empty, wait for a signal, and try again.
   941 		if (me->keysync_queue.empty())
   942 		{
   943 			me->keysync_condition.wait(lock);
   944 			continue;
   945 		}
   946 
   947 		// Pop the message and return it.
   948 		void* msg = me->keysync_queue.front();
   949 		assert(msg);
   950 
   951 		me->keysync_queue.pop();
   952 
   953 		return msg;
   954 	}
   955 
   956 	// we acknowledge that we're quitting...
   957 	me->keysync_abort_requested = false;
   958 
   959 	// We signal the main thread that we got his signal
   960 	// so it can gain the mutex again and call join() on us.
   961 	me->keysync_condition.notify_all();
   962 
   963 	// and tell the pep engine we're done.
   964 	return NULL;
   965 }
   966 
   967 
   968 // Event callbacks
   969 
   970 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
   971 {
   972     // check for valid parameter
   973     if (!new_callbacks)
   974         return E_INVALIDARG;
   975 
   976     // don't allow double registration.
   977     if (this->client_callbacks)
   978         return E_ILLEGAL_STATE_CHANGE;
   979 
   980     this->client_callbacks = new_callbacks;
   981     new_callbacks->AddRef();
   982 
   983 	start_keysync();
   984 
   985 	return S_OK;
   986 }
   987 
   988 STDMETHODIMP CpEpEngine::UnregisterCallbacks()
   989 {
   990     // don't allow double deregistration.
   991     // S_FALSE still is no error (as double deregistration is not fatal).
   992     if (!this->client_callbacks)
   993         return S_FALSE;
   994 
   995     stop_keysync();
   996 
   997     this->client_callbacks->Release();
   998 
   999     this->client_callbacks = NULL;
  1000 
  1001     return S_OK;
  1002 }
  1003 
  1004 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
  1005 	assert(keyinfo_list);
  1006 
  1007 	if (keyinfo_list == NULL)
  1008 		return E_INVALIDARG;
  1009 
  1010 	string _pattern = "";
  1011 	if (search_pattern)
  1012 		_pattern = utf8_string(search_pattern);
  1013 	::stringpair_list_t* _keyinfo_list = NULL;
  1014 
  1015 	PEP_STATUS status = ::OpenPGP_list_keyinfo(get_session(), _pattern.c_str(), &_keyinfo_list);
  1016 	assert(status != PEP_OUT_OF_MEMORY);
  1017 	if (status == PEP_OUT_OF_MEMORY)
  1018 		return E_OUTOFMEMORY;
  1019 
  1020 	if (status != ::PEP_STATUS_OK)
  1021 		return FAIL(L"OpenPGP_list_keyinfo", status);
  1022 
  1023 	if (_keyinfo_list && _keyinfo_list->value) {
  1024 		::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
  1025 	}
  1026 	else {
  1027 		::free_stringpair_list(_keyinfo_list);
  1028 		return FAIL(L"OpenPGP_list_keyinfo: no keys found");
  1029 	}
  1030 
  1031 	::free_stringpair_list(_keyinfo_list);
  1032 	return S_OK;
  1033 
  1034 }
  1035 
  1036 HRESULT CpEpEngine::Fire_MessageToSend(TextMessage * msg)
  1037 {
  1038 	assert(msg);
  1039     assert(this->client_callbacks_on_sync_thread);
  1040 
  1041     auto result = this->client_callbacks_on_sync_thread->MessageToSend(msg);
  1042 
  1043 	return result;
  1044 }
  1045 
  1046 HRESULT CpEpEngine::Fire_ShowHandshake(pEpIdentity * self, pEpIdentity * partner, SyncHandshakeResult * result)
  1047 {
  1048 	assert(self);
  1049 	assert(partner);
  1050 	assert(result);
  1051     assert(this->client_callbacks_on_sync_thread);
  1052     	
  1053 	auto res = this->client_callbacks_on_sync_thread->ShowHandshake(self, partner, result);
  1054 		
  1055 	return res;	
  1056 }