Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if(CCACHE_PROGRAM)
endif()

project(libsession-util
VERSION 1.5.4
VERSION 1.5.5
DESCRIPTION "Session client utility library"
LANGUAGES ${LANGS})

Expand Down
38 changes: 36 additions & 2 deletions include/session/config/user_profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ LIBSESSION_EXPORT int user_profile_set_name(config_object* conf, const char* nam
///
/// Obtains the current profile pic. The pointers in the returned struct will be NULL if a profile
/// pic is not currently set, and otherwise should be copied right away (they will not be valid
/// beyond other API calls on this config object).
/// beyond other API calls on this config object). The returned value will be the latest profile
/// pic between when the user last set their profile and when it was last re-uploaded.
///
/// Declaration:
/// ```cpp
Expand All @@ -110,7 +111,7 @@ LIBSESSION_EXPORT user_profile_pic user_profile_get_pic(const config_object* con

/// API: user_profile/user_profile_set_pic
///
/// Sets a user profile
/// Sets a user profile pic
///
/// Declaration:
/// ```cpp
Expand All @@ -128,6 +129,26 @@ LIBSESSION_EXPORT user_profile_pic user_profile_get_pic(const config_object* con
/// - `int` -- Returns 0 on success, non-zero on error
LIBSESSION_EXPORT int user_profile_set_pic(config_object* conf, user_profile_pic pic);

/// API: user_profile/user_profile_set_reupload_pic
///
/// Sets a user profile pic when reuploading
///
/// Declaration:
/// ```cpp
/// INT user_profile_set_reupload_pic(
/// [in] config_object* conf,
/// [in] user_profile_pic pic
/// );
/// ```
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
/// - `pic` -- [in] Pointer to the pic
///
/// Outputs:
/// - `int` -- Returns 0 on success, non-zero on error
LIBSESSION_EXPORT int user_profile_set_reupload_pic(config_object* conf, user_profile_pic pic);

/// API: user_profile/user_profile_get_nts_priority
///
/// Gets the current note-to-self priority level. Will be negative for hidden, 0 for unpinned, and >
Expand Down Expand Up @@ -245,6 +266,19 @@ LIBSESSION_EXPORT int user_profile_get_blinded_msgreqs(const config_object* conf
/// - `void` -- Returns Nothing
LIBSESSION_EXPORT void user_profile_set_blinded_msgreqs(config_object* conf, int enabled);

/// API: user_profile/user_profile_get_profile_updated
///
/// Returns the timestamp that the user last updated their profile information; or `0` if it's
/// never been updated. This value will return the latest timestamp between when the user last
/// set their profile and when it was last re-uploaded.
///
/// Inputs: None
///
/// Outputs:
/// - `int64_t` - timestamp (unix seconds) that the user last updated their public profile
/// information. Will be `0` if it's never been updated.
LIBSESSION_EXPORT int64_t user_profile_get_profile_updated(config_object* conf);

#ifdef __cplusplus
} // extern "C"
#endif
48 changes: 45 additions & 3 deletions include/session/config/user_profile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ using namespace std::literals;
/// M - set to 1 if blinded message request retrieval is enabled, 0 if retrieval is *disabled*, and
/// omitted if the setting has not been explicitly set (or has been explicitly cleared for some
/// reason).
/// t - The unix timestamp (seconds) that the user last explicitly updated their profile information
/// (automatically updates when changing `name`, `profile_pic` or `set_blinded_msgreqs`).
/// P - user profile url after re-uploading (should take precedence over `p` when `T > t`).
/// Q - user profile decryption key (binary) after re-uploading (should take precedence over `q`
/// when `T > t`).
/// T - The unix timestamp (seconds) that the user last re-uploaded their profile information
/// (automatically updates when calling `set_reupload_profile_pic`).

class UserProfile : public ConfigBase {

public:
friend class UserProfileTester;

// No default constructor
UserProfile() = delete;

Expand Down Expand Up @@ -101,7 +110,8 @@ class UserProfile : public ConfigBase {
/// API: user_profile/UserProfile::get_profile_pic
///
/// Gets the user's current profile pic URL and decryption key. The returned object will
/// evaluate as false if the URL and/or key are not set.
/// evaluate as false if the URL and/or key are not set. The returned value will be the latest
/// profile pic between when the user last set their profile and when it was last re-uploaded.
///
/// Inputs: None
///
Expand All @@ -111,8 +121,8 @@ class UserProfile : public ConfigBase {

/// API: user_profile/UserProfile::set_profile_pic
///
/// Sets the user's current profile pic to a new URL and decryption key. Clears both if either
/// one is empty.
/// Sets the user's current profile pic to a new URL and decryption key. Clears both as well as
/// the reupload values if either one is empty.
///
/// Declaration:
/// ```cpp
Expand All @@ -129,6 +139,25 @@ class UserProfile : public ConfigBase {
void set_profile_pic(std::string_view url, std::span<const unsigned char> key);
void set_profile_pic(profile_pic pic);

/// API: user_profile/UserProfile::set_reupload_profile_pic
///
/// Sets the user's profile pic to a new URL and decryption key after reuploading.
///
/// Declaration:
/// ```cpp
/// void set_reupload_profile_pic(std::string_view url, std::span<const unsigned char> key);
/// void set_reupload_profile_pic(profile_pic pic);
/// ```
///
/// Inputs:
/// - First function:
/// - `url` -- URL pointing to the profile pic
/// - `key` -- Decryption key
/// - Second function:
/// - `pic` -- Profile pic object
void set_reupload_profile_pic(std::string_view url, std::span<const unsigned char> key);
void set_reupload_profile_pic(profile_pic pic);

/// API: user_profile/UserProfile::get_nts_priority
///
/// Gets the Note-to-self conversation priority. Negative means hidden; 0 means unpinned;
Expand Down Expand Up @@ -199,6 +228,19 @@ class UserProfile : public ConfigBase {
/// default).
void set_blinded_msgreqs(std::optional<bool> enabled);

/// API: user_profile/UserProfile::get_profile_updated
///
/// Returns the timestamp that the user last updated their profile information; or `0` if it's
/// never been updated. This value will return the latest timestamp between when the user last
/// set their profile and when it was last re-uploaded.
///
/// Inputs: None
///
/// Outputs:
/// - `std::chrono::sys_seconds` - timestamp that the user last updated their profile
/// information. Will be `0` if it's never been updated.
std::chrono::sys_seconds get_profile_updated() const;

bool accepts_protobuf() const override { return true; }
};

Expand Down
1 change: 1 addition & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@ ConfigMessage::ConfigMessage(
for (const auto& [seqno_hash, ptrs] : replay) {
const auto& [data, diff] = ptrs;
apply_diff(data_, *diff, *data);

lagged_diffs_.emplace_hint(lagged_diffs_.end(), seqno_hash, *diff);
}

Expand Down
69 changes: 67 additions & 2 deletions src/config/user_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,27 @@ void UserProfile::set_name(std::string_view new_name) {
if (new_name.size() > contact_info::MAX_NAME_LENGTH)
throw std::invalid_argument{"Invalid profile name: exceeds maximum length"};
set_nonempty_str(data["n"], new_name);

const auto target_timestamp = (data["t"].integer_or(0) >= data["T"].integer_or(0) ? "t" : "T");
data[target_timestamp] =
static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
}
void UserProfile::set_name_truncated(std::string new_name) {
set_name(utf8_truncate(std::move(new_name), contact_info::MAX_NAME_LENGTH));
}

profile_pic UserProfile::get_profile_pic() const {
profile_pic pic{};
if (auto* url = data["p"].string(); url && !url->empty())

const bool use_primary_keys = (data["t"].integer_or(0) >= data["T"].integer_or(0));
const auto url_key = (use_primary_keys ? "p" : "P");
const auto key_key = (use_primary_keys ? "q" : "Q");

if (auto* url = data[url_key].string(); url && !url->empty())
pic.url = *url;
if (auto* key = data["q"].string(); key && key->size() == 32)
if (auto* key = data[key_key].string(); key && key->size() == 32)
pic.key.assign(
reinterpret_cast<const unsigned char*>(key->data()),
reinterpret_cast<const unsigned char*>(key->data()) + 32);
Expand All @@ -46,12 +57,32 @@ profile_pic UserProfile::get_profile_pic() const {

void UserProfile::set_profile_pic(std::string_view url, std::span<const unsigned char> key) {
set_pair_if(!url.empty() && key.size() == 32, data["p"], url, data["q"], key);

// If the profile was removed then we should remove the "reupload" version as well
if (url.empty() || key.size() != 32)
set_reupload_profile_pic({});

data["t"] = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
}

void UserProfile::set_profile_pic(profile_pic pic) {
set_profile_pic(pic.url, pic.key);
}

void UserProfile::set_reupload_profile_pic(
std::string_view url, std::span<const unsigned char> key) {
set_pair_if(!url.empty() && key.size() == 32, data["P"], url, data["Q"], key);
data["T"] = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
}

void UserProfile::set_reupload_profile_pic(profile_pic pic) {
set_reupload_profile_pic(pic.url, pic.key);
}

void UserProfile::set_nts_priority(int priority) {
set_nonzero_int(data["+"], priority);
}
Expand All @@ -75,6 +106,12 @@ void UserProfile::set_blinded_msgreqs(std::optional<bool> value) {
data["M"].erase();
else
data["M"] = static_cast<int>(*value);

const auto target_timestamp = (data["t"].integer_or(0) >= data["T"].integer_or(0) ? "t" : "T");
data[target_timestamp] =
static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
}

std::optional<bool> UserProfile::get_blinded_msgreqs() const {
Expand All @@ -83,6 +120,15 @@ std::optional<bool> UserProfile::get_blinded_msgreqs() const {
return std::nullopt;
}

std::chrono::sys_seconds UserProfile::get_profile_updated() const {
if (auto* t = data["t"].integer(); t) {
if (auto* T = data["T"].integer(); T && T > t)
return std::chrono::sys_seconds{std::chrono::seconds{*T}};
return std::chrono::sys_seconds{std::chrono::seconds{*t}};
}
return std::chrono::sys_seconds{};
}

extern "C" {

using namespace session;
Expand Down Expand Up @@ -141,6 +187,21 @@ LIBSESSION_C_API int user_profile_set_pic(config_object* conf, user_profile_pic
static_cast<int>(SESSION_ERR_BAD_VALUE));
}

LIBSESSION_C_API int user_profile_set_reupload_pic(config_object* conf, user_profile_pic pic) {
std::string_view url{pic.url};
std::span<const unsigned char> key;
if (!url.empty())
key = {pic.key, 32};

return wrap_exceptions(
conf,
[&] {
unbox<UserProfile>(conf)->set_reupload_profile_pic(url, key);
return 0;
},
static_cast<int>(SESSION_ERR_BAD_VALUE));
}

LIBSESSION_C_API int user_profile_get_nts_priority(const config_object* conf) {
return unbox<UserProfile>(conf)->get_nts_priority();
}
Expand Down Expand Up @@ -170,4 +231,8 @@ LIBSESSION_C_API void user_profile_set_blinded_msgreqs(config_object* conf, int
unbox<UserProfile>(conf)->set_blinded_msgreqs(std::move(val));
}

LIBSESSION_C_API int64_t user_profile_get_profile_updated(config_object* conf) {
return unbox<UserProfile>(conf)->get_profile_updated().time_since_epoch().count();
}

} // extern "C"
Loading