Skip to content

[WIP] Expose a full unsorted list of conversations #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 93 additions & 9 deletions include/session/config/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1407,37 +1407,121 @@ class ConfigBase : public ConfigSig {
}
};

struct IInternalsBase {
virtual ~IInternalsBase() = default;
virtual ConfigBase* getConfigBasePtr() = 0;
virtual const ConfigBase* getConfigBasePtr() const = 0;
};

// The C++ struct we hold opaquely inside the C internals struct. This is designed so that any
// internals<T> has the same layout so that it doesn't matter whether we unbox to an
// internals<ConfigBase> or internals<SubType>.
template <
typename ConfigT = ConfigBase,
std::enable_if_t<std::is_base_of_v<ConfigBase, ConfigT>, int> = 0>
struct internals final {
std::unique_ptr<ConfigBase> config;
std::string error;
struct internals final : public IInternalsBase {
private:
std::variant<std::unique_ptr<ConfigBase>, ConfigBase*> m_config_holder;

public:
std::string error;

template <typename... Args>
explicit internals(std::in_place_type_t<std::unique_ptr<ConfigBase>>, Args&&... args)
: m_config_holder(std::make_unique<ConfigT>(std::forward<Args>(args)...)), error() {}

explicit internals(ConfigBase* config_ptr_managed_externally)
: m_config_holder(config_ptr_managed_externally), error() {
if (!config_ptr_managed_externally) {
throw std::invalid_argument("Externally managed config pointer cannot be null.");
}
}

~internals() override {
// std::variant handles destruction of unique_ptr if it holds one and the raw pointer needs no special cleanup here
}

internals(const internals&) = delete;
internals& operator=(const internals&) = delete;
internals(internals&&) = delete;
internals& operator=(internals&&) = delete;

private:
ConfigBase* getConfigBasePtr() override {
return std::visit(
[](auto&& arg) -> ConfigBase* {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, std::unique_ptr<ConfigBase>>) {
return arg.get();
} else if constexpr (std::is_same_v<T, ConfigBase*>) {
return arg;
}
return nullptr; // Should not happen
},
m_config_holder);
}

const ConfigBase* getConfigBasePtr() const override {
return std::visit(
[](auto&& arg) -> const ConfigBase* {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, std::unique_ptr<ConfigBase>>) {
return arg.get();
} else if constexpr (std::is_same_v<T, ConfigBase*>) {
return arg;
}
return nullptr; // Should not happen
},
m_config_holder);
}

public:

/// Dereferencing falls through to the ConfigBase object
ConfigT* operator->() {
ConfigBase* base_ptr = getConfigBasePtr();
if (!base_ptr) {
assert(false && "ConfigBase pointer is null in internals::operator->");
return nullptr;
}

if constexpr (std::is_same_v<ConfigT, ConfigBase>)
return config.get();
return static_cast<ConfigT*>(base_ptr);
else {
auto* c = dynamic_cast<ConfigT*>(config.get());
auto* c = dynamic_cast<ConfigT*>(base_ptr);
assert(c);
return c;
}
}
const ConfigT* operator->() const {
const ConfigBase* base_ptr = getConfigBasePtr();
if (!base_ptr) {
assert(false && "ConfigBase pointer is null in internals::operator-> const");
return nullptr;
}

if constexpr (std::is_same_v<ConfigT, ConfigBase>)
return config.get();
return static_cast<const ConfigT*>(base_ptr);
else {
auto* c = dynamic_cast<ConfigT*>(config.get());
auto* c = dynamic_cast<const ConfigT*>(base_ptr);
assert(c);
return c;
}
}
ConfigT& operator*() { return *operator->(); }
const ConfigT& operator*() const { return *operator->(); }
ConfigT& operator*() {
ConfigT* ptr = operator->();
if (!ptr) {
throw std::runtime_error("Attempted to dereference a null config pointer via internals::operator*");
}
return *ptr;
}
const ConfigT& operator*() const {
const ConfigT* ptr = operator->();
if (!ptr) {
throw std::runtime_error("Attempted to dereference a null config pointer via internals::operator* const");
}
return *ptr;
}
};

template <typename T = ConfigBase, std::enable_if_t<std::is_base_of_v<ConfigBase, T>, int> = 0>
Expand Down
144 changes: 144 additions & 0 deletions include/session/config/config_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "../config.h"
#include "../export.h"
#include "base.h"
#include "notify.h"
#include "groups/keys.h"

// Config manager base type: this type holds the internal object and is initialized by the various
// config-dependent settings (e.g. config_manager_init) then passed to the various functions.
typedef struct config_manager {
// Internal opaque object pointer; calling code should leave this alone.
void* internals;
} config_manager;

typedef enum CONVERSATION_TYPE {
CONVERSATION_TYPE_ONE_TO_ONE = 0,
CONVERSATION_TYPE_COMMUNITY = 1,
CONVERSATION_TYPE_GROUP = 2,
CONVERSATION_TYPE_BLINDED_ONE_TO_ONE = 3,
CONVERSATION_TYPE_LEGACY_GROUP = 100,
} CONVERSATION_TYPE;

typedef struct config_convo {
char id[409]; // needs to be large enough to fit a full community url (base_url: 267, '/': 1, room: 64, '?public_key=': 12, pubkey_hex: 64, null terminator: 1 ) with null terminator.
CONVERSATION_TYPE type;
char name[101];

// display_pic pic; // TODO: Need to add a C API version

int64_t first_active; // ms since unix epoch
int64_t last_active; // ms since unix epoch
int priority;
CONVO_NOTIFY_MODE notifications;
int64_t mute_until;

union {
struct {
bool is_message_request;
bool is_blocked;
} one_to_one;

struct {
bool is_message_request;
} group;

struct {
char base_url[268]; // null-terminated (max length 267), normalized (i.e. always lower-case,
// only has port if non-default, has trailing / removed)
unsigned char pubkey[32]; // 32 bytes (not terminated, can contain nulls)
bool legacy_blinding;
} blinded_one_to_one;
} specific_data;
} config_convo;

/// API: config_manager/config_manager_init
///
/// Constructs a config manager object and sets a pointer to it in `manager`.
///
/// When done with the object the `config_manager` must be destroyed by passing the pointer to
/// config_manager_free().
///
/// Declaration:
/// ```cpp
/// BOOL config_manager_init(
/// [out] config_manager** manager,
/// [in] const unsigned char* ed25519_secretkey,
/// [out] char* error
/// );
/// ```
///
/// Inputs:
/// - `conf` -- [out] Pointer to the config object
/// - `ed25519_secretkey` -- [in] must be the 32-byte secret key seed value. (You can also pass the
/// pointer to the beginning of the 64-byte value libsodium calls the "secret key" as the first 32
/// bytes of that are the seed). This field cannot be null.
/// - `error` -- [out] the pointer to a buffer in which we will write an error string if an error
/// occurs; error messages are discarded if this is given as NULL. If non-NULL this must be a
/// buffer of at least 256 bytes.
///
/// Outputs:
/// - `bool` -- Returns true on success; returns false and writes the exception message as a
/// C-string into `error` (if not NULL) on failure.
LIBSESSION_EXPORT bool config_manager_init(
config_manager** manager, const unsigned char* ed25519_secretkey_bytes, char* error);

/// API: base/config_manager_free
///
/// Frees a config manager object created with config_manager_init. This will also free any configs
/// the manager is storing so any previously returned `config_object` or `config_group_keys` will be
/// invalid once this is called.
///
/// Declaration:
/// ```cpp
/// VOID config_free(
/// [in, out] config_manager* manager
/// );
/// ```
///
/// Inputs:
/// - `manager` -- [in] Pointer to config_manager object
LIBSESSION_EXPORT void config_manager_free(config_manager* manager);

LIBSESSION_EXPORT bool config_manager_load(
config_manager* manager,
int16_t namespace_,
const char* group_ed25519_pubkey,
const unsigned char* dump,
size_t dumplen,
char* error);

LIBSESSION_EXPORT bool config_manager_get_config(
config_manager* manager,
const uint16_t namespace_,
const char* pubkey_hex,
config_object** config,
char* error);

LIBSESSION_EXPORT bool config_manager_get_keys_config(
config_manager* manager,
const char* pubkey_hex,
config_group_keys** config,
char* error);

LIBSESSION_EXPORT void config_manager_free_config(config_object* config);
LIBSESSION_EXPORT void config_manager_free_keys_config(config_group_keys* config);

LIBSESSION_EXPORT bool config_manager_get_conversations(
const config_manager* manager,
config_convo** conversations,
size_t* conversations_len,
char* error);

#ifdef __cplusplus
} // extern "C"
#endif
Loading