Merge branch 'LIB-11'

# Conflicts:
#	test/Makefile
pull/8/head
heck 2 years ago
commit ce136d408d

@ -1,6 +1,6 @@
BasedOnStyle: LLVM
Language: Cpp
Standard: c++14
Standard: c++11
DerivePointerAlignment: true
SortIncludes: false
ReflowComments: false
@ -38,4 +38,5 @@ SpaceAfterTemplateKeyword: false
AccessModifierOffset: -4
AllowShortBlocksOnASingleLine: Always
IndentPPDirectives: BeforeHash
Cpp11BracedListStyle: false
Cpp11BracedListStyle: false
BreakStringLiterals: false

31
.gitignore vendored

@ -2,6 +2,7 @@
*.a
*.d
*.swp
.DS_Store
ws
test_adapter
.gnupg
@ -24,3 +25,33 @@ test_library
test_message_cache
test_passphrase_cache
test_semaphore
test_lm_dummy
test_sqlite3
test_pEpsqlite
test_listmanager_dummy
test_tofu_react
test_pEpLog_basic
/test/pitytest_data
*.db
/test/pitytest11/test/test_transport
/test/pitytest11/test/test_processdirs
/test/pitytest11/test/test_execmodes
/test/pitytest11/test/test_linear
/test/pitytest11/test/test_model
/test/pitytest11/pitytest_data/*
/test/pitytest11/test/test_swarm
/test/test_update_ident
/test/test_template_swarm_single
/test/test_template_swarm_multi
/test/pitytest11/test/test_copy
/test/pitytest11/test/test_pitytree
/test/test_suite_all
/test/test_sync_init
/test/test_tofu2
/test/pitytest11/test/test_assert
/test/test_tofu
/test/test_tofu_one2many
/test/test_swarm_group
/test/pitytest11/test/pitytest_data/

@ -8,6 +8,9 @@
#include "status_to_string.hh"
#include "pEpLog.hh"
#include "passphrase_cache.hh"
#include "callback_dispatcher.hh"
#include "group_manager_api.h"
#include <iostream>
using namespace std;
@ -36,18 +39,12 @@ namespace pEp {
throw RuntimeError(_status, status);
}
RuntimeError::RuntimeError(const std::string &_text, ::PEP_STATUS _status)
: std::runtime_error(_text.c_str()), text(_text), status(_status)
RuntimeError::RuntimeError(const std::string &_text, ::PEP_STATUS _status) :
std::runtime_error(_text.c_str()), text(_text), status(_status)
{
}
namespace Adapter {
// private
SyncModes _sync_mode = SyncModes::Async;
::messageToSend_t _messageToSend = nullptr;
::notifyHandshake_t _notifyHandshake = nullptr;
bool _adapter_manages_sync_thread = false;
::inject_sync_event_t _inject_action = _inject_sync_event;
std::thread _sync_thread;
::utility::locked_queue<SYNC_EVENT, ::free_Sync_event> sync_evt_q;
std::mutex mut;
@ -58,67 +55,6 @@ namespace pEp {
return _sync_thread.get_id();
}
// public
void sync_initialize(
SyncModes mode,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
bool adapter_manages_sync_thread)
{
_messageToSend = messageToSend;
_notifyHandshake = notifyHandshake;
_adapter_manages_sync_thread = adapter_manages_sync_thread;
set_sync_mode(mode);
return;
}
// public
void set_sync_mode(SyncModes mode)
{
// std::lock_guard<mutex> lock(mut);
_sync_mode = mode;
if (_sync_mode == SyncModes::Sync) {
// init session with inject_sync = process
// stop sync
session(release);
_inject_action = _process_sync_event;
session(init);
::register_sync_callbacks(session(), nullptr, _notifyHandshake, _retrieve_next_sync_event);
if(!_adapter_manages_sync_thread) {
shutdown();
} else {
// The adapter need to shutdown sync thread
}
}
if (_sync_mode == SyncModes::Async) {
// init session with inject_sync = queue
// start sync thread
session(release);
_inject_action = _inject_sync_event;
session(init);
if(!_adapter_manages_sync_thread) {
if (!is_sync_running()) {
startup<void>(_messageToSend, _notifyHandshake, nullptr, nullptr);
}
} else {
// The adapter need to do sync thread start up
}
}
if (_sync_mode == SyncModes::Off) {
// init sesssion with inject_sync = null
// stop sync thread
if(!_adapter_manages_sync_thread) {
shutdown();
} else {
// Adapter needs to shutdown sync thread
}
session(release);
_inject_action = _inject_sync_event;
session(init);
}
return;
}
// private
int _process_sync_event(::SYNC_EVENT ev, void *management)
{
@ -171,34 +107,104 @@ namespace pEp {
return _sync_thread.get_id() == this_thread::get_id();
}
// public
::PEP_SESSION Session::operator()(session_action action)
// ---------------------------------------------------------------------------------------
Session::Session() :
_messageToSend{ nullptr }, _notifyHandshake{ nullptr }, _sync_mode{ SyncModes::Async },
_adapter_manages_sync_thread{ false }
{
}
void Session::initialize(SyncModes sync_mode, bool adapter_manages_sync_thread)
{
pEpLog("Initializing session with CallbackDispatcher...");
_init(
pEp::CallbackDispatcher::messageToSend,
pEp::CallbackDispatcher::notifyHandshake,
sync_mode,
adapter_manages_sync_thread);
}
void Session::initialize(
SyncModes sync_mode,
bool adapter_manages_sync_thread,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake)
{
pEpLog("Initializing session...");
_init(messageToSend, notifyHandshake, sync_mode, adapter_manages_sync_thread);
}
void Session::_init(
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
SyncModes sync_mode,
bool adapter_manages_sync_thread)
{
// cache the values for sync-thread session creation
_messageToSend = messageToSend;
_notifyHandshake = notifyHandshake;
_sync_mode = sync_mode;
_adapter_manages_sync_thread = adapter_manages_sync_thread;
refresh();
::adapter_group_init();
}
void Session::refresh()
{
std::lock_guard<mutex> lock(mut);
release();
::PEP_STATUS status = ::PEP_STATUS_OK;
switch (action) {
case release:
if (_session.get()) {
_session = nullptr;
}
break;
case init:
if (!_session.get()) {
::PEP_SESSION session_;
status = ::init(&session_, _messageToSend, _inject_action, _ensure_passphrase);
throw_status(status);
_session = SessionPtr{session_, ::release};
}
break;
default:
status = ::PEP_ILLEGAL_VALUE;
// Switch to mode "Sync" ensures the sync thread to be shutdown
if (_sync_mode == SyncModes::Sync) {
// process the event directly
_inject_action = _process_sync_event;
if (!_adapter_manages_sync_thread) {
stop_sync();
} else {
// The adapter needs to shutdown sync thread
}
}
// Switch to mode "ASync", sync thread needs to be started using start_sync
if (_sync_mode == SyncModes::Async) {
// put the event on queue
_inject_action = _inject_sync_event;
}
// create
::PEP_SESSION session_;
::PEP_STATUS status;
status = ::init(&session_, _messageToSend, _inject_action, _ensure_passphrase);
throw_status(status);
return _session.get();
status = ::register_sync_callbacks(
session_,
nullptr,
_notifyHandshake,
_retrieve_next_sync_event);
if (status != PEP_STATUS_OK) {
pEpLog("libpEpAdapter: WARNING - session is initialized but without sync/callbacks. "
"This is normal if there are no own identities yet. Call session.init() again to "
"re-initialize the session after creating an own identity.");
}
// store
_session = SessionPtr{ session_, ::release };
}
void Session::release()
{
if (_session.get()) {
_session = nullptr;
}
}
// public
::PEP_SESSION Session::operator()()
{
if (!_session.get()) {
throw std::runtime_error(
"libpEpAdapter: No session! Before use, call session.initialize() for each thread");
} else {
return _session.get();
}
}
// public
@ -207,21 +213,30 @@ namespace pEp {
_inject_sync_event(nullptr, nullptr);
}
void start_sync()
{
startup<pEp::CallbackDispatcher>(
&callback_dispatcher,
&::pEp::CallbackDispatcher::on_startup,
&::pEp::CallbackDispatcher::on_shutdown);
}
// public
void shutdown()
void stop_sync()
{
pEpLog("called");
if (_sync_thread.joinable()) {
pEpLog("sync_is_running - injecting null event");
inject_sync_shutdown();
_sync_thread.join();
pEp::callback_dispatcher.notifyHandshake(nullptr, nullptr, SYNC_NOTIFY_STOP);
}
}
// public
bool is_sync_running()
{
if(!_adapter_manages_sync_thread) {
if (!session._adapter_manages_sync_thread) {
return _sync_thread.joinable();
} else {
return false;

@ -11,6 +11,7 @@
#include <thread>
#include <pEp/sync_api.h>
#include "callback_dispatcher.hh"
namespace pEp {
@ -29,28 +30,19 @@ namespace pEp {
// public
enum class SyncModes
{
Off,
Sync,
Async
};
void sync_initialize(
SyncModes mode,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
bool adapter_manages_sync_thread);
void set_sync_mode(SyncModes mode);
int _inject_sync_event(::SYNC_EVENT ev, void *management);
int _process_sync_event(::SYNC_EVENT ev, void *management);
::PEP_STATUS _ensure_passphrase(::PEP_SESSION session, const char *fpr);
void start_sync();
template<class T = void>
void startup(
messageToSend_t messageToSend,
notifyHandshake_t notifyHandshake,
T *obj = nullptr,
std::function<void(T *)> _startup = nullptr,
std::function<void(T *)> _shutdown = nullptr);
@ -61,18 +53,53 @@ namespace pEp {
// returns the thread id of the sync thread
std::thread::id sync_thread_id();
enum session_action
{
init,
release,
};
class Session {
public:
// TODO: needed because libpEpAdapter provides a static instance
// the session needs to be initialized in order to be usable.
Session();
// Init using CallbackDispatcher
// CAUTION: This may result in a partially initialized session.
// If there are any problem with register_sync_callbacks(), it will still
// succeed. (e.g. due to no own identities yet)
// BUT
// * Sync will not work
// * Group Encryption will not work
// TODO: This needs to be resolved in the engine, new func register_callbacks()
// that is not sync specific, and move the sync-checks to "start-sync()"
void initialize(SyncModes sync_mode = SyncModes::Async, bool adapter_manages_sync_thread = false);
// Arbitrary callbacks
void initialize(
SyncModes sync_mode,
bool adapter_manages_sync_thread,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake);
// re-creates the session using same values
void refresh();
// Not copyable
Session(const Session &) = delete;
Session operator=(const Session&) = delete;
void release();
PEP_SESSION operator()();
SyncModes _sync_mode;
::messageToSend_t _messageToSend;
::notifyHandshake_t _notifyHandshake;
bool _adapter_manages_sync_thread;
::inject_sync_event_t _inject_action;
private:
void _init(
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
SyncModes sync_mode,
bool adapter_manages_sync_thread);
using SessionPtr = std::unique_ptr<_pEpSession, std::function<void(PEP_SESSION)>>;
SessionPtr _session = nullptr;
public:
PEP_SESSION operator()(session_action action = init);
};
extern thread_local Session session;
@ -84,7 +111,7 @@ namespace pEp {
// injects a NULL event into sync_event_queue to denote sync thread to
// shutdown, and joins & removes the sync thread
void shutdown();
void stop_sync();
bool is_sync_running();
bool in_shutdown();

@ -14,8 +14,6 @@ namespace pEp {
namespace Adapter {
using std::function;
extern ::messageToSend_t _messageToSend;
extern ::notifyHandshake_t _notifyHandshake;
extern std::thread _sync_thread;
extern ::utility::locked_queue<::SYNC_EVENT, ::free_Sync_event> sync_evt_q;
@ -38,12 +36,10 @@ namespace pEp {
*/
// private
template<class T>
void sync_thread(T *obj, function<void(T *)> _startup, function<void(T *)> _shutdown)
void sync_thread(Session *rhs, T *obj, function<void(T *)> _startup, function<void(T *)> _shutdown)
{
pEpLog("called");
_ex = nullptr;
assert(_messageToSend);
assert(_notifyHandshake);
// 1. Execute registered startup function
if (obj && _startup) {
@ -52,29 +48,18 @@ namespace pEp {
pEpLog("creating session for the sync thread");
// 2. Create session for the sync thread
session();
// 3. register_sync_callbacks()
{
// TODO: Do we need to use a passphraseWrap here???
pEpLog("register_sync_callbacks()");
::PEP_STATUS status = ::register_sync_callbacks(
session(),
nullptr,
_notifyHandshake,
_retrieve_next_sync_event);
pEpLog("register_sync_callbacks() return:" << status);
// Convert status into exception and store it
// set register_done AFTER that
try {
throw_status(status);
register_done.store(true);
} catch (...) {
_ex = std::current_exception();
register_done.store(true);
return;
}
// 3. register_sync_callbacks() (in session.initialize())
try {
session.initialize(
rhs->_sync_mode,
rhs->_adapter_manages_sync_thread,
rhs->_messageToSend,
rhs->_notifyHandshake);
register_done.store(true);
} catch (...) {
_ex = std::current_exception();
register_done.store(true);
return;
}
pEpLog("sync protocol loop started");
@ -86,8 +71,7 @@ namespace pEp {
unregister_sync_callbacks(session());
// 6. Release the session
// TODO: Maybe do that AFTER shutdown?
session(release);
session.release();
// 7. Execute registered shutdown function
if (obj && _shutdown) {
@ -97,38 +81,25 @@ namespace pEp {
/*
* Sync Thread Startup
* 1. ensure session for the main thread (registers: messageToSend, _inject_sync_event, _ensure_passphrase)
* 1. throw if main thread session is not initialized
* 2. Start the sync thread
* 3. Defer execution until sync thread register_sync_callbacks() has returned
* 4. Throw pending exception from the sync thread
*/
// private
template<class T>
void startup(
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
T *obj,
function<void(T *)> _startup,
function<void(T *)> _shutdown)
void startup(T *obj, std::function<void(T *)> _startup, std::function<void(T *)> _shutdown)
{
pEpLog("called");
if (messageToSend) {
_messageToSend = messageToSend;
}
if (notifyHandshake) {
_notifyHandshake = notifyHandshake;
}
pEpLog("ensure session for the main thread");
// 1. re-initialize session for the main thread (registers: messageToSend, _inject_sync_event, _ensure_passphrase)
session(release);
session(init);
// refresh the session
// due to partially initialized session, see session.initialize()
session.refresh();
if (!_sync_thread.joinable()) {
register_done.store(false);
pEpLog("creating sync-thread");
// 2. Start the sync thread
_sync_thread = std::thread(sync_thread<T>, obj, _startup, _shutdown);
_sync_thread = std::thread(sync_thread<T>, &session, obj, _startup, _shutdown);
// 3. Defer execution until sync thread register_sync_callbacks() has returned
while (register_done.load() == false) {
pEpLog("waiting for sync-thread to init...");

@ -5,10 +5,13 @@
include ../Makefile.conf
SOURCE=$(wildcard *.cc)
SRC_CXX=$(wildcard *.cc)
SRC_C=$(wildcard *.c)
HEADERS=$(wildcard *.hh *.hxx *.h)
OBJECTS=$(subst .cc,.o,$(SOURCE))
DEPENDS=$(subst .cc,.d,$(SOURCE))
OBJ_CXX=$(subst .cc,.o,$(SRC_CXX))
OBJ_C=$(subst .c,.o,$(SRC_C))
OBJECTS=$(OBJ_CXX) $(OBJ_C)
DEPENDS=$(subst .cc,.d,$(SRC_CXX))
CXXFLAGS+= -MMD -MP
ifneq ($(MAKECMDGOALS),clean)

@ -1,96 +0,0 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
#include <pEp/group.h>
#include "adapter_group.h"
#include "pEpLog.hh"
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************************************
* Group management functions
*************************************************************************************************/
DYNAMIC_API PEP_STATUS adapter_group_create(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
identity_list *memberlist,
pEp_group **group)
{
pEpLog("called");
return ::group_create(session, group_identity, manager, memberlist, group);
}
DYNAMIC_API PEP_STATUS
adapter_group_join(PEP_SESSION session, pEp_identity *group_identity, pEp_identity *as_member)
{
pEpLog("called");
return ::group_join(session, group_identity, as_member);
}
DYNAMIC_API PEP_STATUS
adapter_group_dissolve(PEP_SESSION session, pEp_identity *group_identity, pEp_identity *manager)
{
pEpLog("called");
return ::group_dissolve(session, group_identity, manager);
}
DYNAMIC_API PEP_STATUS adapter_group_invite_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member)
{
pEpLog("called");
return ::group_invite_member(session, group_identity, group_member);
}
PEP_STATUS adapter_group_remove_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member)
{
pEpLog("called");
return ::group_remove_member(session, group_identity, group_member);
}
DYNAMIC_API PEP_STATUS adapter_group_rating(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
PEP_rating *rating)
{
pEpLog("called");
return ::group_rating(session, group_identity, manager, rating);
}
/*************************************************************************************************
* Group query functions
*************************************************************************************************/
//DYNAMIC_API PEP_STATUS group_query_groups(PEP_SESSION session, identity_list **groups)
//{
// pEpLog("called");
// return PEP_STATUS_OK;
//}
//
//DYNAMIC_API PEP_STATUS
//group_query_manager(PEP_SESSION session, const pEp_identity *const group, pEp_identity **manager)
//{
// pEpLog("called");
// return PEP_STATUS_OK;
//}
//
//DYNAMIC_API PEP_STATUS
//group_query_members(PEP_SESSION session, const pEp_identity *const group, identity_list **members)
//{
// pEpLog("called");
// return PEP_STATUS_OK;
//}
#ifdef __cplusplus
}
#endif

@ -5,6 +5,7 @@
#include "passphrase_cache.hh"
#include <stdexcept>
#include <cassert>
#include "Adapter.hh"
pEp::CallbackDispatcher pEp::callback_dispatcher;
@ -51,7 +52,7 @@ namespace pEp {
}
if (targets.empty()) {
stop_sync();
Adapter::stop_sync();
}
}
@ -73,39 +74,6 @@ namespace pEp {
}
}
void CallbackDispatcher::start_sync()
{
pEpLog("called");
callback_dispatcher.semaphore.go();
pEp::Adapter::startup<CallbackDispatcher>(
CallbackDispatcher::messageToSend,
CallbackDispatcher::notifyHandshake,
&callback_dispatcher,
&CallbackDispatcher::on_startup,
&CallbackDispatcher::on_shutdown);
pEpLog("all targets signal: SYNC_NOTIFY_START");
for (auto target : callback_dispatcher.targets) {
if (target.notifyHandshake) {
target.notifyHandshake(nullptr, nullptr, SYNC_NOTIFY_START);
}
}
}
void CallbackDispatcher::stop_sync()
{
callback_dispatcher.semaphore.stop();
Adapter::shutdown();
callback_dispatcher.semaphore.go();
for (auto target : callback_dispatcher.targets) {
if (target.notifyHandshake) {
target.notifyHandshake(nullptr, nullptr, SYNC_NOTIFY_STOP);
}
}
}
PEP_STATUS CallbackDispatcher::_messageToSend(::message *msg)
{
if (Adapter::on_sync_thread() && !msg) {

@ -7,10 +7,10 @@
#include <vector>
#include <functional>
#include <mutex>
#include "Adapter.hh"
#include "Semaphore.hh"
#include "passphrase_cache.hh"
#include <pEp/sync_api.h>
namespace pEp {
// use this class when implementing a desktop adapter
// register different interfaces with add()
@ -37,18 +37,15 @@ namespace pEp {
proc on_shutdown = nullptr);
void remove(::messageToSend_t messageToSend);
static void start_sync();
static void stop_sync();
static PEP_STATUS messageToSend(::message *msg);
static PEP_STATUS notifyHandshake(
::pEp_identity *me,
::pEp_identity *partner,
::sync_handshake_signal signal);
protected:
void on_startup();
void on_shutdown();
protected:
PEP_STATUS _messageToSend(::message *msg);
PEP_STATUS _notifyHandshake(

@ -0,0 +1,146 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
//#include <pEp/group.h>
// clang-format off
#include "group_manager_api.h"
#include "grp_driver_replicator.hh"
#include "pEpLog.hh"
#include "grp_driver_dummy.hh"
#include "grp_driver_engine.hh"
#ifdef __cplusplus
extern "C" {
#endif
using namespace std;
using namespace pEp;
Adapter::GroupDriverReplicator adapter_grp_manager{};
shared_ptr<Adapter::GroupDriverDummy> grp_drv_dummy;
shared_ptr<Adapter::GroupDriverEngine> grp_drv_engine;
DYNAMIC_API PEP_STATUS adapter_group_init()
{
PEP_STATUS status;
try {
const string lm_dummy_db_filename = "groups.db";
#ifdef WIN32
const string lm_dummy_db_path = string(::per_user_directory()) + "\\" + lm_dummy_db_filename;
#else
const string lm_dummy_db_path = string(::per_user_directory()) + "/" + lm_dummy_db_filename;
#endif
if(!grp_drv_dummy) {
grp_drv_dummy = make_shared<Adapter::GroupDriverDummy>(lm_dummy_db_path);
}
if(!grp_drv_engine) {
grp_drv_engine = make_shared<Adapter::GroupDriverEngine>();
}
adapter_grp_manager.set_replication_source(*grp_drv_dummy.get());
adapter_grp_manager.set_replication_destination(*grp_drv_engine.get());
} catch (const std::exception &e) {
pEpLog(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLog("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
return status;
}
/*************************************************************************************************
* Group management functions
*************************************************************************************************/
DYNAMIC_API PEP_STATUS adapter_group_create(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
identity_list *memberlist)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.adapter_group_create(
session,
group_identity,
manager,
memberlist);
return status;
}
DYNAMIC_API PEP_STATUS adapter_group_dissolve(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.adapter_group_dissolve(session, group_identity, manager);
return status;
}
DYNAMIC_API PEP_STATUS adapter_group_invite_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.adapter_group_invite_member(
session,
group_identity,
group_member);
return status;
}
DYNAMIC_API PEP_STATUS adapter_group_remove_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.adapter_group_remove_member(
session,
group_identity,
group_member);
return status;
}
DYNAMIC_API PEP_STATUS adapter_group_join(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *as_member)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.adapter_group_join(session, group_identity, as_member);
return status;
}
/*************************************************************************************************
* Group query functions
*************************************************************************************************/
DYNAMIC_API PEP_STATUS adapter_group_query_groups(PEP_SESSION session, identity_list **groups)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.group_query_groups(session, groups);
return status;
}
DYNAMIC_API PEP_STATUS
adapter_group_query_manager(PEP_SESSION session, const pEp_identity *const group, pEp_identity **manager)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.group_query_manager(session, group, manager);
return status;
}
DYNAMIC_API PEP_STATUS adapter_group_query_members(
PEP_SESSION session,
const pEp_identity *const group,
identity_list **members)
{
pEpLog("called");
PEP_STATUS status = adapter_grp_manager.group_query_members(session, group, members);
return status;
}
#ifdef __cplusplus
}
#endif

@ -1,16 +1,16 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
#ifndef LIBPEPADAPTER_GROUP_HH
#define LIBPEPADAPTER_GROUP_HH
#ifndef LIBPEPADAPTER_GROUP_MANAGER_API_H
#define LIBPEPADAPTER_GROUP_MANAGER_API_H
#include <pEp/message_api.h>
#include <pEp/group.h>
#ifdef __cplusplus
extern "C" {
#endif
DYNAMIC_API PEP_STATUS adapter_group_init();
/*************************************************************************************************
* Group management functions
@ -37,9 +37,6 @@ extern "C" {
* a user_id and address, and there must be a default key for the manager
* present in the database
* @param[in,out] member_ident_list list of group member identities
* @param[in,out] group Optional reference for pointer to group object
* representing the created group.
* (When input is NULL, no object is created)
*
* @retval PEP_STATUS_OK on success
* error on failure
@ -58,37 +55,8 @@ DYNAMIC_API PEP_STATUS adapter_group_create(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
identity_list *memberlist,
pEp_group **group
);
identity_list *memberlist);
/**
* <!-- adapter_group_join() -->
*
* @brief Join a group for which we have received an invitation, marking
* our own membership in the database for the group and sending the manager
* a confirmation of the acceptance of the invitation
*
* @param[in] session associated session object
* @param[in] group_identity the pEp_identity object representing the group. Must contain at least
* a user_id and address
* @param[in] as_member the pEp_identity object representing the own identity we want to use to
* join the group. This must match the identity which was invited to the group.
* Must contain a user_id and address.
*
* @retval PEP_STATUS_OK on success
* error on failure
*
* @ownership FIXME
*
*
*/
DYNAMIC_API PEP_STATUS adapter_group_join(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *as_member
);
/**
* <!-- adapter_group_dissolve() -->
@ -111,11 +79,8 @@ DYNAMIC_API PEP_STATUS adapter_group_join(
* @warning For recipients to accept the dissolution, the sender/manager key used must be a key that they
* have a trust entry for.
*/
DYNAMIC_API PEP_STATUS adapter_group_dissolve(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager
);
DYNAMIC_API PEP_STATUS
adapter_group_dissolve(PEP_SESSION session, pEp_identity *group_identity, pEp_identity *manager);
/**
* <!-- adapter_group_invite_member() -->
@ -140,10 +105,9 @@ DYNAMIC_API PEP_STATUS adapter_group_dissolve(
*
*/
DYNAMIC_API PEP_STATUS adapter_group_invite_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member
);
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member);
/**
* <!-- adapter_group_remove_member() -->
@ -165,46 +129,46 @@ DYNAMIC_API PEP_STATUS adapter_group_invite_member(
* @todo Revamp implementation and execute key reset
*
*/
PEP_STATUS adapter_group_remove_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member
);
DYNAMIC_API PEP_STATUS adapter_group_remove_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member);
/**
* <!-- adapter_group_rating() -->
* <!-- group_join() -->
*
* @brief Get the rating for this group - if the caller is the manager, this will return the aggregate rating
* of group members. For members, this will return the rating of the group_identity
* @brief Join a group for which we have received an invitation, marking
* our own membership in the database for the group and sending the manager
* a confirmation of the acceptance of the invitation
*
* @param[in] session associated session object
* @param[in] group_identity the pEp_identity object representing the group. Must contain at least
* a user_id and address
* @param[in] manager the pEp_identity object representing the member to remove. Must contain
* a user_id and address
* @param[out] rating the group rating
* @param[in] as_member the pEp_identity object representing the own identity we want to use to
* join the group. This must match the identity which was invited to the group.
* Must contain a user_id and address.
*
* @retval PEP_STATUS_OK on success
* error on failure
*
* @ownership FIXME
*
*
*/
DYNAMIC_API PEP_STATUS adapter_group_rating(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
PEP_rating *rating
DYNAMIC_API PEP_STATUS adapter_group_join(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *as_member
);
/*************************************************************************************************
* Group query functions
*************************************************************************************************/
/**
* <!-- group_query_groups() -->
* <!-- adapter_group_query_groups() -->
*
* @brief queries the list manager which groups currently exist.
*
@ -219,13 +183,10 @@ DYNAMIC_API PEP_STATUS adapter_group_rating(
*
*/
//DYNAMIC_API PEP_STATUS group_query_groups(
// PEP_SESSION session,
// identity_list **groups
//);
DYNAMIC_API PEP_STATUS adapter_group_query_groups(PEP_SESSION session, identity_list **groups);
/**
* <!-- group_query_manager() -->
* <!-- adapter_group_query_manager() -->
*
* @brief queries the list manager for the group manager of a given group.
*
@ -240,14 +201,10 @@ DYNAMIC_API PEP_STATUS adapter_group_rating(
*
*/
//DYNAMIC_API PEP_STATUS group_query_manager(
// PEP_SESSION session,
// const pEp_identity *const group,
// pEp_identity **manager
//);
DYNAMIC_API PEP_STATUS adapter_group_query_manager(PEP_SESSION session, const pEp_identity *const group, pEp_identity **manager);
/**
* <!-- group_query_members() -->
* <!-- adapter_group_query_members() -->
*
* @brief queries the list manager for all members of a given group.
*
@ -262,15 +219,11 @@ DYNAMIC_API PEP_STATUS adapter_group_rating(
*
*/
//DYNAMIC_API PEP_STATUS group_query_members(
// PEP_SESSION session,
// const pEp_identity *const group,
// identity_list **members
//);
DYNAMIC_API PEP_STATUS adapter_group_query_members(PEP_SESSION session, const pEp_identity *const group, identity_list **members);
#ifdef __cplusplus
};
#endif
#endif //LIBPEPADAPTER_GROUP_HH
#endif //LIBPEPADAPTER_GROUP_MANAGER_API_H

@ -0,0 +1,383 @@
#include "grp_driver_dummy.hh"
#include "pEpLog.hh"
#include "utils.hh"
#include "std_utils.hh"
#include <pEp/message_api.h>
#include "listmanager_dummy.hh"
using namespace std;
namespace pEp {
namespace Adapter {
bool GroupDriverDummy::log_enabled = false;
GroupDriverDummy::GroupDriverDummy(const std::string &db_path) :
lmd(ListManagerDummy(db_path))
{
pEpLogClass("called");
}
PEP_STATUS GroupDriverDummy::adapter_group_create(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
identity_list *memberlist) noexcept
{
pEpLogClass("called");
PEP_STATUS status = PEP_UNKNOWN_ERROR;
if (!group_identity || !manager) {
status = PEP_ILLEGAL_VALUE;
} else {
if (Utils::is_c_str_empty(group_identity->address) ||
Utils::is_c_str_empty(manager->address)) {
status = PEP_ILLEGAL_VALUE;
} else {
const string addr_list{ group_identity->address };
const string addr_manager{ manager->address };
try {
lmd.list_add(addr_list, addr_manager);
status = PEP_STATUS_OK;
} catch (const AlreadyExistsException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_GROUP_EXISTS;
} catch (const exception &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLogClass("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
if (status == PEP_STATUS_OK) {
// Add the memberlist (if given)
// Fail totally on the first member_invite() that fails
const vector<pEp_identity *> cxx_memberlist = Utils::to_cxx(*memberlist);
for (pEp_identity *const member : cxx_memberlist) {
status = this->adapter_group_invite_member(session, group_identity, member);
if (status != PEP_STATUS_OK) {
status = status;
break;
}
}
}
}
}
return status;
}
// The engine checks if the manager is correct for the group given
// But the list manager does not require that
// So, we verify that first, too. using moderator()
PEP_STATUS GroupDriverDummy::adapter_group_dissolve(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager) noexcept
{
pEpLogClass("called");
PEP_STATUS status = PEP_UNKNOWN_ERROR;
if (!group_identity || !manager) {
status = PEP_ILLEGAL_VALUE;
} else {
if (Utils::is_c_str_empty(group_identity->address) ||
Utils::is_c_str_empty(manager->address)) {
status = PEP_ILLEGAL_VALUE;
} else {
const string addr_list{ group_identity->address };
const string addr_manager{ manager->address };
// Check if given manager is correct for the given group
string addr_manager_queried;
try {
addr_manager_queried = lmd.moderator(addr_list);
status = PEP_STATUS_OK;
} catch (const ListDoesNotExistException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_GROUP_NOT_FOUND;
} catch (const exception &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLogClass("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
if (status == PEP_STATUS_OK) {
if (addr_manager_queried != addr_manager) {
status = PEP_CANNOT_DISABLE_GROUP;
} else {
try {
lmd.list_delete(addr_list);
status = PEP_STATUS_OK;
} catch (const MemberDoesNotExistException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
// TODO: Silently succeed???
status = PEP_STATUS_OK;
} catch (const ListDoesNotExistException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_GROUP_NOT_FOUND;
} catch (const exception &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLogClass("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
}
}
}
}
return status;
}
PEP_STATUS GroupDriverDummy::adapter_group_invite_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member) noexcept
{
pEpLogClass("called");
PEP_STATUS status = PEP_UNKNOWN_ERROR;
if (!group_identity || !group_member) {
status = PEP_ILLEGAL_VALUE;
} else {
if (Utils::is_c_str_empty(group_identity->address) ||
Utils::is_c_str_empty(group_member->address)) {
status = PEP_ILLEGAL_VALUE;
} else {
const string addr_list{ group_identity->address };
const string addr_member{ group_member->address };
try {
lmd.member_add(addr_list, addr_member);
status = PEP_STATUS_OK;
} catch (const AlreadyExistsException &e) {
// TODO: Silently succeed???
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_STATUS_OK;
} catch (const ListDoesNotExistException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
return PEP_GROUP_NOT_FOUND;
} catch (const exception &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLogClass("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
}
}
return status;
}
PEP_STATUS GroupDriverDummy::adapter_group_remove_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member) noexcept
{
pEpLogClass("called");
PEP_STATUS status = PEP_UNKNOWN_ERROR;
if (!group_identity || !group_member) {
status = PEP_ILLEGAL_VALUE;
} else {
if (Utils::is_c_str_empty(group_identity->address) ||
Utils::is_c_str_empty(group_member->address)) {
status = PEP_ILLEGAL_VALUE;
} else {
const string addr_list{ group_identity->address };
const string addr_member{ group_member->address };
try {
lmd.member_remove(addr_list, addr_member);
status = PEP_STATUS_OK;
} catch (const MemberDoesNotExistException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
// TODO: Silently succeed???
status = PEP_STATUS_OK;
} catch (const ListDoesNotExistException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_GROUP_NOT_FOUND;
} catch (const exception &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLogClass("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
}
}
return status;
}
PEP_STATUS GroupDriverDummy::adapter_group_join(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *as_member) noexcept
{
pEpLogClass("called");
// TODO: listmanager member db list_join()
// PEP_STATUS status = PEP_UNKNOWN_ERROR;
// if (!group_identity || !group_member) {
// status = PEP_ILLEGAL_VALUE;
// } else {
// if (Utils::is_c_str_empty(group_identity->address) ||
// Utils::is_c_str_empty(group_member->address)) {
// status = PEP_ILLEGAL_VALUE;
// } else {
// const string addr_list{ group_identity->address };
// const string addr_member{ group_member->address };
//
// try {
// lmd.member_remove(addr_list, addr_member);
// status = PEP_STATUS_OK;
// } catch (const MemberDoesNotExistException &e) {
// pEpLogClass(Utils::nested_exception_to_string(e));
// // TODO: Silently succeed???
// status = PEP_STATUS_OK;
// } catch (const ListDoesNotExistException &e) {
// pEpLogClass(Utils::nested_exception_to_string(e));
// status = PEP_GROUP_NOT_FOUND;
// } catch (const exception &e) {
// pEpLogClass(Utils::nested_exception_to_string(e));
// status = PEP_UNKNOWN_ERROR;
// } catch (...) {
// pEpLogClass("unknown exception");
// status = PEP_UNKNOWN_ERROR;
// }
// }
// }
return PEP_STATUS_OK;
}
PEP_STATUS GroupDriverDummy::group_query_groups(PEP_SESSION session, identity_list **groups) noexcept
{
pEpLogClass("called");
PEP_STATUS status = PEP_UNKNOWN_ERROR;
if (!session) {
status = PEP_ILLEGAL_VALUE;
} else {
vector<string> lists_queried;
try {
lists_queried = lmd.lists();
status = PEP_STATUS_OK;
} catch (const exception &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLogClass("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
if (status == PEP_STATUS_OK) {
::identity_list *idl_groups = ::new_identity_list(nullptr);
for (const string &addr_list : lists_queried) {
::pEp_identity *grp_ident = ::new_identity(
addr_list.c_str(),
nullptr,
nullptr,
nullptr);
::update_identity(session, grp_ident);
identity_list_add(idl_groups, grp_ident);
}
*groups = idl_groups;
}
}
return status;
}
PEP_STATUS GroupDriverDummy::group_query_manager(
PEP_SESSION session,
const pEp_identity *const group,
pEp_identity **manager) noexcept
{
pEpLogClass("called");
PEP_STATUS status = PEP_UNKNOWN_ERROR;
if (!session || !group) {
status = PEP_ILLEGAL_VALUE;
} else {
const string addr_list{ group->address };
string addr_manager{};
try {
addr_manager = lmd.moderator(addr_list);
status = PEP_STATUS_OK;
} catch (const ListDoesNotExistException &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_GROUP_NOT_FOUND;
} catch (const exception &e) {
pEpLogClass(Utils::nested_exception_to_string(e));
status = PEP_UNKNOWN_ERROR;
} catch (...) {
pEpLogClass("unknown exception");
status = PEP_UNKNOWN_ERROR;
}
if (status == PEP_STATUS_OK) {
::pEp_identity *manager_queried = ::new_identity(
addr_manager.c_str(),
nullptr,
nullptr,
nullptr);
::update_identity(session, manager_queried);
*manager = manager_queried;
}
}
return status;
}