CpEpEngine.cpp
author Volker Birk <vb@pep-project.org>
Wed, 23 Nov 2016 16:02:58 +0100
changeset 216 6a08f0334e8a
parent 204 21b7749b5beb
child 218 ef80732697e6
permissions -rw-r--r--
fixing COM-43
     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 if (status == PEP_GET_KEY_FAILED) {
   408 		if (_ident->fpr) {
   409 			pEp_free(_ident->fpr);
   410 			_ident->fpr = NULL;
   411 		}
   412 		copy_identity(result, _ident);
   413 		result->Fpr = NULL;
   414 		::free_identity(_ident);
   415 		return S_OK;
   416 	}
   417 	else {
   418 		::free_identity(_ident);
   419 		if (status == PEP_OUT_OF_MEMORY)
   420 			return E_OUTOFMEMORY;
   421 		else
   422 			return FAIL(L"UpdateIdentity", status);
   423 	}
   424 }
   425 
   426 STDMETHODIMP CpEpEngine::KeyMistrusted(struct pEpIdentity *ident)
   427 {
   428 	::pEp_identity *_ident;
   429 
   430 	assert(ident);
   431 	if (!ident)
   432 		return E_INVALIDARG;
   433 
   434 	try {
   435 		_ident = new_identity(ident);
   436 	}
   437 	catch (bad_alloc&) {
   438 		return E_OUTOFMEMORY;
   439 	}
   440 	catch (exception& ex) {
   441 		return FAIL(ex.what());;
   442 	}
   443 
   444 	PEP_STATUS status = ::key_mistrusted(get_session(), _ident);
   445 	free_identity(_ident);
   446 
   447 	if (status == PEP_OUT_OF_MEMORY)
   448 		return E_OUTOFMEMORY;
   449 
   450 	if (status == PEP_KEY_NOT_FOUND)
   451 		return FAIL(L"key not found");
   452 
   453 	if (status != ::PEP_STATUS_OK)
   454 		return FAIL(L"cannot revoke compromized key", status);
   455 
   456 	return S_OK;
   457 }
   458 
   459 STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
   460 {
   461 	::pEp_identity *_ident;
   462 
   463 	assert(ident); 
   464 	
   465 	if (!ident)
   466 		return E_INVALIDARG;
   467 
   468 	try {
   469 		_ident = new_identity(ident);
   470 	}
   471 	catch (bad_alloc&) {
   472 		return E_OUTOFMEMORY;
   473 	}
   474 	catch (exception& ex) {
   475 		return FAIL(ex.what());;
   476 	}
   477 
   478 	PEP_STATUS status = ::key_reset_trust(get_session(), _ident);
   479 	free_identity(_ident);
   480 
   481 	if (status == PEP_OUT_OF_MEMORY)
   482 		return E_OUTOFMEMORY;
   483 
   484 	if (status == PEP_KEY_NOT_FOUND)
   485 		return FAIL(L"key not found");
   486 
   487 	if (status != ::PEP_STATUS_OK)
   488 		return FAIL(L"cannot reset trust", status);
   489 
   490 	return S_OK;
   491 }
   492 
   493 int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
   494 {
   495 	assert(ident);
   496 	assert(management);
   497 	if (!(ident && management))
   498 		return -1;
   499 
   500 	CpEpEngine *me = (CpEpEngine *)management;
   501 
   502 	if (me->identity_queue.load() == NULL)
   503 		return 0;
   504 
   505 	try {
   506 		me->identity_queue.load()->push_back(ident);
   507 	}
   508 	catch (exception&) {
   509 		return -1;
   510 	}
   511 
   512 	return 0;
   513 }
   514 
   515 ::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
   516 {
   517 	assert(management);
   518 	if (!management)
   519 		return NULL;
   520 
   521 	identity_queue_t *iq = (identity_queue_t *)management;
   522 
   523 	do /* poll queue */ {
   524 		if (iq->size())
   525 			break;
   526 		::Sleep(100);
   527 	} while (true);
   528 
   529 	::pEp_identity *_ident;
   530 	pEp_identity_cpp& ident = iq->front();
   531 
   532 	if (ident.address.size() == 0)
   533 		return NULL;
   534 
   535 	_ident = ident.to_pEp_identity();
   536 	iq->pop_front();
   537 
   538 	return _ident;
   539 }
   540 
   541 PEP_STATUS CpEpEngine::messageToSend(void * obj, message *msg)
   542 {
   543 	assert(msg);
   544 	assert(obj);
   545 	if (!(msg && obj))
   546 		return PEP_ILLEGAL_VALUE;
   547 
   548 	TextMessage _msg;
   549 	memset(&_msg, 0, sizeof(TextMessage));
   550 
   551 	text_message_from_C(&_msg, msg);
   552 	CpEpEngine *me = (CpEpEngine *)obj;
   553 	HRESULT r = me->Fire_MessageToSend(&_msg);
   554 	assert(r == S_OK);
   555 	clear_text_message(&_msg);
   556 	if (r == E_OUTOFMEMORY)
   557 		return PEP_OUT_OF_MEMORY;
   558 	if (r != S_OK)
   559 		return PEP_UNKNOWN_ERROR;
   560 
   561 	return PEP_STATUS_OK;
   562 }
   563 
   564 PEP_STATUS CpEpEngine::showHandshake(void * obj, pEp_identity *self, pEp_identity *partner)
   565 {
   566 	assert(self && partner);
   567 	if (!(self && partner))
   568 		return PEP_ILLEGAL_VALUE;
   569 
   570 	pEpIdentity _self;
   571 	copy_identity(&_self, self);
   572 	pEpIdentity _partner;
   573 	copy_identity(&_partner, partner);
   574 	CpEpEngine *me = (CpEpEngine *)obj;
   575 	SyncHandshakeResult _result;
   576 	HRESULT r = me->Fire_ShowHandshake(&_self, &_partner, &_result);
   577 	assert(r == S_OK);
   578 	clear_identity_s(_self);
   579 	clear_identity_s(_partner);
   580 	if (r == E_OUTOFMEMORY)
   581 		return PEP_OUT_OF_MEMORY;
   582 	if (r != S_OK)
   583 		return PEP_UNKNOWN_ERROR;
   584 
   585 	PEP_STATUS status = deliverHandshakeResult(me->get_session(), partner, (sync_handshake_result)(int)_result);
   586 	return status;
   587 }
   588 
   589 STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
   590 {
   591 	assert(fpr);
   592 	if (!fpr)
   593 		return E_INVALIDARG;
   594 
   595 	string _fpr = utf8_string(fpr);
   596 	PEP_STATUS status = ::blacklist_add(get_session(), _fpr.c_str());
   597 	assert(status == PEP_STATUS_OK);
   598 	if (status != PEP_STATUS_OK)
   599 		return FAIL(L"blacklist_add failed in pEp engine", status);
   600 
   601 	return S_OK;
   602 }
   603 
   604 STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
   605 {
   606 	assert(fpr);
   607 	if (!fpr)
   608 		return E_INVALIDARG;
   609 
   610 	string _fpr = utf8_string(fpr);
   611 	PEP_STATUS status = ::blacklist_delete(get_session(), _fpr.c_str());
   612 	assert(status == PEP_STATUS_OK);
   613 	if (status != PEP_STATUS_OK)
   614 		return FAIL(L"blacklist_delete failed in pEp engine", status);
   615 
   616 	return S_OK;
   617 }
   618 
   619 STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
   620 {
   621 	assert(fpr);
   622 	assert(listed);
   623 
   624 	if (!(fpr && listed))
   625 		return E_INVALIDARG;
   626 
   627 	string _fpr = utf8_string(fpr);
   628 	bool result;
   629 	PEP_STATUS status = ::blacklist_is_listed(get_session(), _fpr.c_str(), &result);
   630 	assert(status == PEP_STATUS_OK);
   631 	if (status != PEP_STATUS_OK)
   632 		return FAIL(L"blacklist_is_listed failed in pEp engine", status);
   633 
   634 	*listed = result ? VARIANT_TRUE : VARIANT_FALSE;
   635 	return S_OK;
   636 }
   637 
   638 STDMETHODIMP CpEpEngine::BlacklistRetrieve(SAFEARRAY **blacklist)
   639 {
   640 	assert(blacklist);
   641 
   642 	if (!blacklist)
   643 		return E_INVALIDARG;
   644 
   645 	::stringlist_t *_blacklist = NULL;
   646 	PEP_STATUS status = ::blacklist_retrieve(get_session(), &_blacklist);
   647 	assert(status == PEP_STATUS_OK);
   648 	if (status != PEP_STATUS_OK)
   649 		return FAIL(L"blacklist_retrieve failed in pEp engine", status);
   650 	assert(_blacklist);
   651 
   652 	*blacklist = string_array(_blacklist);
   653 	::free_stringlist(_blacklist);
   654 	return S_OK;
   655 }
   656 
   657 HRESULT CpEpEngine::error(_bstr_t msg)
   658 {
   659 	_bstr_t helpFile = L"";
   660 	_bstr_t source = L"pEp COM Adapter";
   661 
   662 	ICreateErrorInfo *cei;
   663 	if (SUCCEEDED(CreateErrorInfo(&cei))) {
   664 		cei->SetDescription(msg);
   665 		cei->SetGUID(__uuidof(IpEpEngine));
   666 		cei->SetHelpContext(0);
   667 		cei->SetHelpFile(helpFile);
   668 		cei->SetSource(source);
   669 
   670 		IErrorInfo *errinfo;
   671 		if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
   672 			SetErrorInfo(0, errinfo);
   673 			errinfo->Release();
   674 		}
   675 		cei->Release();
   676 	}
   677 	return E_FAIL;
   678 }
   679 
   680 HRESULT CpEpEngine::error(_bstr_t msg, PEP_STATUS status)
   681 {
   682     std::stringstream stream;
   683     stream << msg;
   684     stream << ": ";
   685     stream << std::hex << status;
   686     
   687     error(stream.str().c_str());
   688 
   689     if (status == ::PEP_OUT_OF_MEMORY)
   690         return E_OUTOFMEMORY;
   691 
   692     return E_FAIL;
   693 }
   694 
   695 STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags)
   696 {
   697 	assert(src);
   698 	assert(dst);
   699 
   700 	if (!(src && dst))
   701 		return E_INVALIDARG;
   702 
   703 	::message *_src = text_message_to_C(src);
   704 
   705 	// COM-19: Initialize msg_dst to NULL, or we end up calling
   706 	// free_message() below with a pointer to random garbage in
   707 	// case of an error in encrypt_message().
   708 	::message *msg_dst = NULL;
   709 	::stringlist_t *_extra = new_stringlist(extra); // can cope with NULL
   710 
   711 	// _PEP_enc_format is intentionally hardcoded to PEP_enc_PEP:
   712 	// 2016-10-02 14:10 < fdik> schabi: actually, all adapters now must use PEP_enc_PEP
   713 	PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t) flags;
   714 	PEP_STATUS status = ::encrypt_message(get_session(), _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
   715 	::free_stringlist(_extra);
   716 
   717 	if (status == PEP_STATUS_OK)
   718 		text_message_from_C(dst, msg_dst);
   719 	else
   720 		text_message_from_C(dst, _src);
   721 
   722 	::free_message(msg_dst);
   723 	::free_message(_src);
   724 
   725 	if (status == PEP_OUT_OF_MEMORY)
   726 		return E_OUTOFMEMORY;
   727 
   728 	// COM-41: Enhanced PEP status handling
   729 	if ((status > PEP_STATUS_OK && status < PEP_UNENCRYPTED) ||
   730 		status < PEP_STATUS_OK ||
   731 		status >= PEP_TRUSTWORD_NOT_FOUND)
   732 		return FAIL("Failure to encrypt message", status);
   733 
   734 	// Statii like PEP_UNENCRYPTED due to no private key
   735 	// should not be a catastrophic failure here. Using S_FALSE
   736 	// still allows clients to differentiate with S_OK,
   737 	// although this does not work out of the box with
   738 	// the standard .NET mapping of COM.
   739 	if (status != PEP_STATUS_OK)
   740 		return S_FALSE;
   741 
   742 	return S_OK;
   743 }
   744 
   745 STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
   746 {
   747 	assert(src);
   748 	assert(dst);
   749 	assert(keylist);
   750 	assert(flags);
   751 	assert(rating);
   752 
   753 	if (!(src && dst && keylist && flags && rating))
   754 		return E_INVALIDARG;
   755 
   756 	*keylist = NULL;
   757 	*rating = pEpRatingUndefined;
   758 
   759 	::message *_src = text_message_to_C(src);
   760 	::message *msg_dst = NULL;
   761 	::stringlist_t *_keylist = NULL;
   762 	::PEP_rating _rating;
   763 
   764 	PEP_decrypt_flags_t engineflags = 0;
   765 	PEP_STATUS status = ::decrypt_message(get_session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
   766 
   767 	*flags = (pEpDecryptFlags)engineflags;
   768 
   769 	if (msg_dst)
   770 		text_message_from_C(dst, msg_dst);
   771 
   772 	::free_message(_src);
   773 	::free_message(msg_dst);
   774 
   775 	if (_keylist) {
   776 		*keylist = string_array(_keylist);
   777 		free_stringlist(_keylist);
   778 	}
   779 
   780 	*rating = (pEpRating)_rating;
   781 
   782 	return S_OK;
   783 }
   784 
   785 STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
   786 {
   787 	assert(msg);
   788 	assert(pVal);
   789 
   790 	if (!(msg  && pVal))
   791 		return E_INVALIDARG;
   792 
   793 	::message *_msg = text_message_to_C(msg);
   794 
   795 	PEP_rating _rating;
   796 	PEP_STATUS status = ::outgoing_message_rating(get_session(), _msg, &_rating);
   797 	if (status != PEP_STATUS_OK)
   798 		return FAIL(L"cannot get message rating", status);
   799 
   800 	*pVal = (pEpRating)_rating;
   801 	return S_OK;
   802 }
   803 
   804 STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
   805 {
   806 	::pEp_identity *_ident;
   807 
   808 	assert(ident);
   809 	assert(pVal);
   810 
   811 	if (!(ident  && pVal))
   812 		return E_INVALIDARG;
   813 
   814 	try {
   815 		_ident = new_identity(ident);
   816 	}
   817 	catch (bad_alloc&) {
   818 		return E_OUTOFMEMORY;
   819 	}
   820 	catch (exception& ex) {
   821 		return FAIL(ex.what());;
   822 	}
   823 
   824 	PEP_rating _rating;
   825 	PEP_STATUS status = ::identity_rating(get_session(), _ident, &_rating);
   826 	free_identity(_ident);
   827 
   828 	if (status != PEP_STATUS_OK)
   829 		return FAIL(L"cannot get message color", status);
   830 
   831 	*pVal = (pEpRating)_rating;
   832 	return S_OK;
   833 }
   834 
   835 STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
   836 {
   837 	assert(pVal);
   838 
   839 	if (!pVal)
   840 		return E_INVALIDARG;
   841 
   842 	PEP_rating engineRating = (PEP_rating)rating;
   843 	PEP_color _color = ::color_from_rating(engineRating);
   844 
   845 	*pVal = (pEpColor)_color;
   846 
   847 	return S_OK;
   848 }
   849 
   850 STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
   851 {
   852 	::pEp_identity *_ident;
   853 
   854 	assert(ident);
   855 	assert(result);
   856 
   857 	if (!ident || !result)
   858 		return E_INVALIDARG;
   859 
   860 	try {
   861 		_ident = new_identity(ident);
   862 	}
   863 	catch (bad_alloc&) {
   864 		return E_OUTOFMEMORY;
   865 	}
   866 	catch (exception& ex) {
   867 		return FAIL(ex.what());;
   868 	}
   869 
   870 	if (verbose_mode) {
   871 		stringstream ss;
   872 		ss << "TrustPersonalKey called with ";
   873 		ss << utf8_string(ident->Address);
   874 		ss << L": ";
   875 		ss << ident->CommType;
   876 		verbose(ss.str());
   877 	}
   878 
   879 	PEP_STATUS status = ::trust_personal_key(get_session(), _ident);
   880 
   881 	if (verbose_mode) {
   882 		stringstream ss;
   883 		ss << "result ";
   884 		ss << status;
   885 		ss << " for ";
   886 		ss << _ident->address;
   887 		ss << L": ";
   888 		ss << _ident->comm_type;
   889 		verbose(ss.str());
   890 	}
   891 
   892 	if (status == PEP_STATUS_OK)
   893 		copy_identity(result, _ident);
   894 
   895 	free_identity(_ident);
   896 	if (status == PEP_OUT_OF_MEMORY)
   897 		return E_OUTOFMEMORY;
   898 	else if (status != PEP_STATUS_OK)
   899 		return FAIL(L"failure while executing TrustPersonalKey()", status);
   900 
   901 	return S_OK;
   902 }
   903 
   904 // keysync api
   905 
   906 void CpEpEngine::start_keysync()
   907 {
   908 	// acquire the lock
   909 	std::unique_lock<std::mutex> lock(keysync_mutex);
   910 
   911 	// Assert if we're not already running.
   912     assert(!this->keysync_thread);
   913 
   914 	// Ensure we are not aborting the new thread due to a
   915 	// left over flag.
   916 	keysync_abort_requested = false;
   917 
   918 	// Init our keysync session
   919 	PEP_STATUS status = ::init(&keysync_session);
   920 	::register_sync_callbacks(keysync_session, (void*)this, messageToSend, showHandshake, inject_sync_msg, retrieve_next_sync_msg);
   921 	assert(status == PEP_STATUS_OK);
   922 
   923     attach_sync_session(get_session(), keysync_session);
   924 
   925     // We need to marshal the callbacks to the keysync thread
   926     LPSTREAM marshaled_callbacks;
   927 
   928     auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
   929     assert(result == S_OK);
   930 
   931 	// Star the keysync thread
   932 	keysync_thread = new thread(do_keysync_in_thread, this, marshaled_callbacks);
   933 }
   934 
   935 void CpEpEngine::do_keysync_in_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks) 
   936 {
   937     assert(self);
   938 	assert(marshaled_callbacks);
   939 
   940     // We need to initialize COM here for successfull delivery of the callbacks.
   941     // As we don't create any COM instances in our thread, the COMINIT value is
   942     // currently irrelevant, so we go with the safest value.
   943     auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
   944     assert(res == S_OK);
   945 
   946     LPVOID vp;
   947 
   948     res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
   949     assert(SUCCEEDED(res));
   950 
   951     self->client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
   952 
   953     ::do_sync_protocol(self->keysync_session, self);
   954 
   955     self->client_callbacks_on_sync_thread->Release();
   956 
   957     self->client_callbacks_on_sync_thread = NULL;
   958 
   959     CoUninitialize();
   960 }
   961 
   962 void CpEpEngine::stop_keysync()
   963 {
   964 	// acquire the lock
   965 	std::unique_lock<std::mutex> lock(keysync_mutex);
   966 
   967     // Do nothing if keysync is not running.
   968     if (!keysync_thread)
   969         return;
   970 
   971     assert(!keysync_abort_requested);
   972 	// signal that we're gonna abort
   973 	keysync_abort_requested = true;
   974 
   975 	// Notify the keysync thread
   976 	keysync_condition.notify_all();
   977 
   978 	// Wait for the other thread to finish and clean up
   979 	while (keysync_abort_requested)
   980 		keysync_condition.wait(lock);
   981 
   982 	// collect the child thread for the thread to end
   983 	keysync_thread->join();
   984 
   985 	// clean up
   986 	delete keysync_thread;
   987 	keysync_thread = NULL;
   988 
   989     ::detach_sync_session(get_session());
   990 	::unregister_sync_callbacks(keysync_session);
   991 	release(keysync_session);
   992     keysync_session = NULL;
   993 }
   994 
   995 int CpEpEngine::inject_sync_msg(void * msg, void * management)
   996 {
   997 	assert(msg);
   998 	assert(management);
   999 	// check argument
  1000 	if (!msg)
  1001 		return E_INVALIDARG;
  1002 	if (!management)
  1003 		return ERROR_INVALID_HANDLE;
  1004 
  1005 	CpEpEngine* me = (CpEpEngine*)management;
  1006 
  1007 	// acquire the lock
  1008 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
  1009 
  1010 	// check whether we're in a valid state running:
  1011 	if (!me->keysync_thread)
  1012 		return E_ASYNC_OPERATION_NOT_STARTED;
  1013 
  1014 	// queue the message
  1015 	me->keysync_queue.push(msg);
  1016 
  1017 	// notify the receivers
  1018 	me->keysync_condition.notify_all();
  1019 
  1020     return S_OK;
  1021 }
  1022 
  1023 void * CpEpEngine::retrieve_next_sync_msg(void * management)
  1024 {
  1025 	// sanity check
  1026 	assert(management);
  1027 	if (!management)
  1028 		return NULL;
  1029 
  1030 	CpEpEngine* me = (CpEpEngine*)management;
  1031 
  1032 	// acquire the lock
  1033 	std::unique_lock<std::mutex> lock(me->keysync_mutex);
  1034 
  1035 	// as long as we're supposed to be active 
  1036 	// (we won't wait for the queue to empty currently...)
  1037 	while (!me->keysync_abort_requested)
  1038 	{
  1039 		// If the queue is empty, wait for a signal, and try again.
  1040 		if (me->keysync_queue.empty())
  1041 		{
  1042 			me->keysync_condition.wait(lock);
  1043 			continue;
  1044 		}
  1045 
  1046 		// Pop the message and return it.
  1047 		void* msg = me->keysync_queue.front();
  1048 		assert(msg);
  1049 
  1050 		me->keysync_queue.pop();
  1051 
  1052 		return msg;
  1053 	}
  1054 
  1055 	// we acknowledge that we're quitting...
  1056 	me->keysync_abort_requested = false;
  1057 
  1058 	// We signal the main thread that we got his signal
  1059 	// so it can gain the mutex again and call join() on us.
  1060 	me->keysync_condition.notify_all();
  1061 
  1062 	// and tell the pep engine we're done.
  1063 	return NULL;
  1064 }
  1065 
  1066 
  1067 // Event callbacks
  1068 
  1069 STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
  1070 {
  1071     // check for valid parameter
  1072     if (!new_callbacks)
  1073         return E_INVALIDARG;
  1074 
  1075     // don't allow double registration.
  1076     if (this->client_callbacks)
  1077         return E_ILLEGAL_STATE_CHANGE;
  1078 
  1079     this->client_callbacks = new_callbacks;
  1080     new_callbacks->AddRef();
  1081 
  1082 	start_keysync();
  1083 
  1084 	return S_OK;
  1085 }
  1086 
  1087 STDMETHODIMP CpEpEngine::UnregisterCallbacks()
  1088 {
  1089     // don't allow double deregistration.
  1090     // S_FALSE still is no error (as double deregistration is not fatal).
  1091     if (!this->client_callbacks)
  1092         return S_FALSE;
  1093 
  1094     stop_keysync();
  1095 
  1096     this->client_callbacks->Release();
  1097 
  1098     this->client_callbacks = NULL;
  1099 
  1100     return S_OK;
  1101 }
  1102 
  1103 STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
  1104 	assert(keyinfo_list);
  1105 
  1106 	if (keyinfo_list == NULL)
  1107 		return E_INVALIDARG;
  1108 
  1109 	string _pattern = "";
  1110 	if (search_pattern)
  1111 		_pattern = utf8_string(search_pattern);
  1112 	::stringpair_list_t* _keyinfo_list = NULL;
  1113 
  1114 	PEP_STATUS status = ::OpenPGP_list_keyinfo(get_session(), _pattern.c_str(), &_keyinfo_list);
  1115 	assert(status != PEP_OUT_OF_MEMORY);
  1116 	if (status == PEP_OUT_OF_MEMORY)
  1117 		return E_OUTOFMEMORY;
  1118 
  1119 	if (status != ::PEP_STATUS_OK)
  1120 		return FAIL(L"OpenPGP_list_keyinfo", status);
  1121 
  1122 	if (_keyinfo_list && _keyinfo_list->value) {
  1123 		::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
  1124 	}
  1125 	else {
  1126 		::free_stringpair_list(_keyinfo_list);
  1127 		return FAIL(L"OpenPGP_list_keyinfo: no keys found");
  1128 	}
  1129 
  1130 	::free_stringpair_list(_keyinfo_list);
  1131 	return S_OK;
  1132 
  1133 }
  1134 
  1135 HRESULT CpEpEngine::Fire_MessageToSend(TextMessage * msg)
  1136 {
  1137 	assert(msg);
  1138     assert(this->client_callbacks_on_sync_thread);
  1139 
  1140 	if (!msg)
  1141 		return E_INVALIDARG;
  1142 
  1143 	if (!this->client_callbacks_on_sync_thread)
  1144 		return E_ILLEGAL_METHOD_CALL;
  1145 
  1146     auto result = this->client_callbacks_on_sync_thread->MessageToSend(msg);
  1147 
  1148 	return result;
  1149 }
  1150 
  1151 HRESULT CpEpEngine::Fire_ShowHandshake(pEpIdentity * self, pEpIdentity * partner, SyncHandshakeResult * result)
  1152 {
  1153 	assert(self);
  1154 	assert(partner);
  1155 	assert(result);
  1156     assert(this->client_callbacks_on_sync_thread);
  1157 
  1158 	if (!(self && partner && result))
  1159 		return E_INVALIDARG;
  1160 	if (!this->client_callbacks_on_sync_thread)
  1161 		return E_ILLEGAL_METHOD_CALL;
  1162     	
  1163 	auto res = this->client_callbacks_on_sync_thread->ShowHandshake(self, partner, result);
  1164 		
  1165 	return res;	
  1166 }