Skip to content

Commit

Permalink
Merge pull request #67 from jagerman/group-info-desc
Browse files Browse the repository at this point in the history
Add description to group info
  • Loading branch information
jagerman committed Oct 26, 2023
2 parents 4305c78 + 156c997 commit 50f0cde
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 6 deletions.
36 changes: 36 additions & 0 deletions include/session/config/groups/info.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ extern "C" {
#include "../profile_pic.h"
#include "../util.h"

LIBSESSION_EXPORT extern const size_t GROUP_INFO_NAME_MAX_LENGTH;
LIBSESSION_EXPORT extern const size_t GROUP_INFO_DESCRIPTION_MAX_LENGTH;

/// API: groups/groups_info_init
///
/// Constructs a group info config object and sets a pointer to it in `conf`.
Expand Down Expand Up @@ -56,6 +59,9 @@ LIBSESSION_EXPORT const char* groups_info_get_name(const config_object* conf);
/// Sets the group's name to the null-terminated C string. Returns 0 on success, non-zero on
/// error (and sets the config_object's error string).
///
/// If the given name is longer than GROUP_INFO_NAME_MAX_LENGTH (100) bytes then it will be
/// truncated.
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
/// - `name` -- [in] Pointer to the name as a null-terminated C string
Expand All @@ -64,6 +70,36 @@ LIBSESSION_EXPORT const char* groups_info_get_name(const config_object* conf);
/// - `int` -- Returns 0 on success, non-zero on error
LIBSESSION_EXPORT int groups_info_set_name(config_object* conf, const char* name);

/// API: groups_info/groups_info_get_description
///
/// Returns a pointer to the currently-set description (null-terminated), or NULL if there is no
/// description at all. Should be copied right away as the pointer may not remain valid beyond
/// other API calls.
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
///
/// Outputs:
/// - `char*` -- Pointer to the currently-set description as a null-terminated string, or NULL if
/// there is no description
LIBSESSION_EXPORT const char* groups_info_get_description(const config_object* conf);

/// API: groups_info/groups_info_set_description
///
/// Sets the group's description to the null-terminated C string. Returns 0 on success, non-zero on
/// error (and sets the config_object's error string).
///
/// If the given description is longer than GROUP_INFO_DESCRIPTION_MAX_LENGTH (2000) bytes then it
/// will be truncated.
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
/// - `description` -- [in] Pointer to the description as a null-terminated C string
///
/// Outputs:
/// - `int` -- Returns 0 on success, non-zero on error
LIBSESSION_EXPORT int groups_info_set_description(config_object* conf, const char* description);

/// API: groups_info/groups_info_get_pic
///
/// Obtains the current profile pic. The pointers in the returned struct will be NULL if a profile
Expand Down
35 changes: 30 additions & 5 deletions include/session/config/groups/info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ using namespace std::literals;
/// D - delete attachments before - same as above, but specific to attachments.
/// E - disappearing message timer (seconds) if the delete-after-send disappearing messages mode is
/// enabled for the group. Omitted if disappearing messages is disabled.
/// n - utf8 group name (human-readable)
/// n - utf8 group name (human-readable); may not contain nulls, max length 100.
/// o - utf8 group description (human-readable); may not contain nulls, max length 2000.
/// p - group profile url
/// q - group profile decryption key (binary)

class Info final : public ConfigBase {

public:
/// Limits for the name & description strings, in bytes. If longer, we truncate to these
/// lengths:
static constexpr size_t NAME_MAX_LENGTH = 100; // same as base_group_info::NAME_MAX_LENGTH
static constexpr size_t DESCRIPTION_MAX_LENGTH = 2000;

// No default constructor
Info() = delete;

Expand Down Expand Up @@ -98,15 +104,34 @@ class Info final : public ConfigBase {
///
/// Sets the group name; if given an empty string then the name is removed.
///
/// Declaration:
/// ```cpp
/// void set_name(std::string_view new_name);
/// ```
/// If given a name longer than `Info::NAME_MAX_LENGTH` (100) bytes it will be truncated.
///
/// Inputs:
/// - `new_name` -- The name to be put into the group Info
void set_name(std::string_view new_name);

/// API: groups/Info::get_description
///
/// Returns the group description, or std::nullopt if there is no group description set.
///
/// If given a description longer than `Info::DESCRIPTION_MAX_LENGTH` (2000) bytes it will be
/// truncated.
///
/// Inputs: None
///
/// Outputs:
/// - `std::optional<std::string_view>` - Returns the group description if it is set
std::optional<std::string_view> get_description() const;

/// API: groups/Info::set_description
///
/// Sets the optional group description; if given an empty string then an existing description
/// is removed.
///
/// Inputs:
/// - `new_desc` -- The new description to be put into the group Info
void set_description(std::string_view new_desc);

/// API: groups/Info::get_profile_pic
///
/// Gets the group's current profile pic URL and decryption key. The returned object will
Expand Down
56 changes: 56 additions & 0 deletions src/config/groups/info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,23 @@ std::optional<std::string_view> Info::get_name() const {
}

void Info::set_name(std::string_view new_name) {
if (new_name.size() > NAME_MAX_LENGTH)
new_name = new_name.substr(0, NAME_MAX_LENGTH);
set_nonempty_str(data["n"], new_name);
}

std::optional<std::string_view> Info::get_description() const {
if (auto* s = data["o"].string(); s && !s->empty())
return *s;
return std::nullopt;
}

void Info::set_description(std::string_view new_desc) {
if (new_desc.size() > DESCRIPTION_MAX_LENGTH)
new_desc = new_desc.substr(0, DESCRIPTION_MAX_LENGTH);
set_nonempty_str(data["o"], new_desc);
}

profile_pic Info::get_profile_pic() const {
profile_pic pic{};
if (auto* url = data["p"].string(); url && !url->empty())
Expand Down Expand Up @@ -105,6 +119,10 @@ bool Info::is_destroyed() const {
using namespace session;
using namespace session::config;

LIBSESSION_C_API const size_t GROUP_INFO_NAME_MAX_LENGTH = groups::Info::NAME_MAX_LENGTH;
LIBSESSION_C_API const size_t GROUP_INFO_DESCRIPTION_MAX_LENGTH =
groups::Info::DESCRIPTION_MAX_LENGTH;

LIBSESSION_C_API int groups_info_init(
config_object** conf,
const unsigned char* ed25519_pubkey,
Expand Down Expand Up @@ -153,6 +171,44 @@ LIBSESSION_C_API int groups_info_set_name(config_object* conf, const char* name)
return 0;
}

/// API: groups_info/groups_info_get_description
///
/// Returns a pointer to the currently-set description (null-terminated), or NULL if there is
/// no description at all. Should be copied right away as the pointer may not remain valid
/// beyond other API calls.
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
///
/// Outputs:
/// - `char*` -- Pointer to the currently-set description as a null-terminated string, or NULL
/// if there is no description
LIBSESSION_C_API const char* groups_info_get_description(const config_object* conf) {
if (auto s = unbox<groups::Info>(conf)->get_description())
return s->data();
return nullptr;
}

/// API: groups_info/groups_info_set_description
///
/// Sets the group's description to the null-terminated C string. Returns 0 on success, non-zero on
/// error (and sets the config_object's error string).
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
/// - `description` -- [in] Pointer to the description as a null-terminated C string
///
/// Outputs:
/// - `int` -- Returns 0 on success, non-zero on error
LIBSESSION_C_API int groups_info_set_description(config_object* conf, const char* description) {
try {
unbox<groups::Info>(conf)->set_description(description);
} catch (const std::exception& e) {
return set_error(conf, SESSION_ERR_BAD_VALUE, e);
}
return 0;
}

/// API: groups_info/groups_info_get_pic
///
/// Obtains the current profile pic. The pointers in the returned struct will be NULL if a profile
Expand Down
16 changes: 15 additions & 1 deletion tests/test_group_keys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ TEST_CASE("Group Keys - C++ API", "[config][groups][keys][cpp]") {

// change group info, re-key, distribute
admin1.info.set_name("tomatosauce"s);
admin1.info.set_description("this is where you go to play in the tomato sauce, I guess");

CHECK(admin1.info.needs_push());

Expand All @@ -258,6 +259,8 @@ TEST_CASE("Group Keys - C++ API", "[config][groups][keys][cpp]") {
CHECK(a.info.merge(info_configs) == std::vector{{"fakehash3"s}});
CHECK(a.members.merge(mem_configs) == std::vector{{"fakehash3"s}});
CHECK(a.info.get_name() == "tomatosauce"s);
CHECK(a.info.get_description() ==
"this is where you go to play in the tomato sauce, I guess"s);
CHECK(a.keys.current_hashes() ==
std::unordered_set{{"keyhash1"s, "keyhash2"s, "keyhash3"s}});
}
Expand All @@ -268,6 +271,8 @@ TEST_CASE("Group Keys - C++ API", "[config][groups][keys][cpp]") {
CHECK(m.info.merge(info_configs) == std::vector{{"fakehash3"s}});
CHECK(m.members.merge(mem_configs) == std::vector{{"fakehash3"s}});
CHECK(m.info.get_name() == "tomatosauce"s);
CHECK(m.info.get_description() ==
"this is where you go to play in the tomato sauce, I guess"s);
CHECK(m.keys.current_hashes() == std::unordered_set{{"keyhash2"s, "keyhash3"s}});
}

Expand Down Expand Up @@ -533,12 +538,21 @@ TEST_CASE("Group Keys - C++ API", "[config][groups][keys][cpp]") {
admin1.info.dump(),
admin1.members.dump(),
admin1.keys.dump()};
admin1b.info.set_name("Test New Name");
admin1b.info.set_name(
"Test New Name Really long "
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
admin1b.info.set_description(std::string(2050, 'z'));
CHECK_NOTHROW(admin1b.info.push());
admin1b.members.set(
admin1b.members.get_or_construct("05124076571076017981235497801235098712093870981273590"
"8746387172343"));
CHECK_NOTHROW(admin1b.members.push());

// Test truncation
CHECK(admin1b.info.get_name() ==
"Test New Name Really long "
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuv");
CHECK(admin1b.info.get_description() == std::string(2000, 'z'));
}

TEST_CASE("Group Keys - C API", "[config][groups][keys][c]") {
Expand Down

0 comments on commit 50f0cde

Please sign in to comment.