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