Skip to content
Open
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
26 changes: 26 additions & 0 deletions include/session/config/user_profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,32 @@ LIBSESSION_EXPORT int64_t user_profile_get_pro_access_expiry(const config_object
LIBSESSION_EXPORT void user_profile_set_pro_access_expiry(
config_object* conf, int64_t access_expiry_ts);

/// API: user_profile/user_profile_get_pro_auto_renewing
///
/// Returns whether the account's current Session Pro subscription is auto-renewing. Backend-derived
/// (the `auto_renewing` field on /get_pro_status); set alongside the access expiry.
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
///
/// Outputs:
/// - `int` -- 1 if the subscription is known to be auto-renewing, otherwise 0 (terminal, unknown,
/// or not Pro).
LIBSESSION_EXPORT int user_profile_get_pro_auto_renewing(const config_object* conf);

/// API: user_profile/user_profile_set_pro_auto_renewing
///
/// Records whether the current Session Pro subscription is auto-renewing: nonzero stores the flag,
/// 0 clears it (which is also how it is cleared when the subscription lapses).
///
/// Inputs:
/// - `conf` -- [in] Pointer to the config object
/// - `auto_renewing` -- [in] nonzero if auto-renewing, 0 to clear
///
/// Outputs:
/// - `void`
LIBSESSION_EXPORT void user_profile_set_pro_auto_renewing(config_object* conf, int auto_renewing);

/// API: user_profile/user_profile_get_refund_requested
///
/// Retrieves the timestamp at which the user requested a refund of their current Session Pro
Expand Down
26 changes: 26 additions & 0 deletions include/session/config/user_profile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ using namespace std::literals;
/// flight"), so all the account's devices poll the backend to pull the entitlement through.
/// Inserted only when not already pro; cleared automatically when entitlement lands; values
/// more than a week in the past are ignored on read.
/// A - set to 1 when the current Session Pro subscription is auto-renewing; omitted when it is
/// terminal (will not renew), unknown, or the account isn't Pro. Backend-derived
/// (get_pro_status.auto_renewing) and synced across devices; the client sets it alongside `E`
/// and clears it (sets false) when the subscription lapses.
/// 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`).
Expand Down Expand Up @@ -339,6 +343,28 @@ class UserProfile : public ConfigBase {
/// will expire, or nullopt to remove the value.
void set_pro_access_expiry(std::optional<sys_seconds> access_expiry_ts);

/// API: user_profile/UserProfile::get_pro_auto_renewing
///
/// Returns whether the account's current Session Pro subscription is auto-renewing (true) or
/// terminal/unknown (false). Backend-derived (the `auto_renewing` field on /get_pro_status);
/// the client sets it alongside `set_pro_access_expiry`. Only a `true` value is stored, so an
/// account that isn't Pro, or whose renewal status has not been learned, reads as false.
///
/// Inputs: None
///
/// Outputs:
/// - `bool` -- true iff the subscription is known to be auto-renewing.
bool get_pro_auto_renewing() const;

/// API: user_profile/UserProfile::set_pro_auto_renewing
///
/// Records whether the current Session Pro subscription is auto-renewing. `true` stores the
/// flag; `false` erases it -- which is also how it is cleared when the subscription lapses.
///
/// Inputs:
/// - `auto_renewing` -- true if the subscription auto-renews; false to clear the flag.
void set_pro_auto_renewing(bool auto_renewing);

/// API: user_profile/UserProfile::get_refund_requested
///
/// Retrieves the timestamp at which the user requested a refund of their current Session Pro
Expand Down
19 changes: 19 additions & 0 deletions src/config/user_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,17 @@ void UserProfile::set_pro_access_expiry(std::optional<std::chrono::sys_seconds>
}
}

bool UserProfile::get_pro_auto_renewing() const {
return data["A"].integer_or(0) != 0;
}

void UserProfile::set_pro_auto_renewing(bool auto_renewing) {
// Presence-only: store 1 when auto-renewing, erase otherwise (absent == terminal/unknown). No
// t/T bump -- this is backend-derived pro state (like E/I/R), not a user-initiated profile
// edit.
set_nonzero_int(data["A"], auto_renewing);
}

std::optional<std::chrono::sys_seconds> UserProfile::get_refund_requested() const {
if (auto* R = data["R"].integer()) {
std::chrono::sys_seconds when{std::chrono::seconds{*R}};
Expand Down Expand Up @@ -541,6 +552,14 @@ LIBSESSION_C_API void user_profile_set_pro_access_expiry(
unbox<UserProfile>(conf)->set_pro_access_expiry(as_sys_seconds(access_expiry_ts));
}

LIBSESSION_C_API int user_profile_get_pro_auto_renewing(const config_object* conf) {
return unbox<UserProfile>(conf)->get_pro_auto_renewing() ? 1 : 0;
}

LIBSESSION_C_API void user_profile_set_pro_auto_renewing(config_object* conf, int auto_renewing) {
unbox<UserProfile>(conf)->set_pro_auto_renewing(auto_renewing != 0);
}

LIBSESSION_C_API int64_t user_profile_get_refund_requested(const config_object* conf) {
if (auto when = unbox<UserProfile>(conf)->get_refund_requested())
return epoch_seconds(*when);
Expand Down
16 changes: 16 additions & 0 deletions tests/test_config_userprofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,12 @@ TEST_CASE("user profile C API", "[config][user_profile][c]") {
CHECK(user_profile_get_blinded_msgreqs(conf2) == -1);
user_profile_set_blinded_msgreqs(conf2, 1);
CHECK(user_profile_get_blinded_msgreqs(conf2) == 1);

CHECK(user_profile_get_pro_auto_renewing(conf2) == 0);
user_profile_set_pro_auto_renewing(conf2, 1);
CHECK(user_profile_get_pro_auto_renewing(conf2) == 1);
user_profile_set_pro_auto_renewing(conf2, 0);
CHECK(user_profile_get_pro_auto_renewing(conf2) == 0);
UserProfileTester::set_profile_updated(conf2, std::chrono::sys_seconds{124s});

// Both have changes, so push need a push
Expand Down Expand Up @@ -654,6 +660,16 @@ TEST_CASE("UserProfile Pro Storage", "[config][user_profile][pro]") {
profile.set_pro_access_expiry(access_expiry);
CHECK(profile.get_pro_access_expiry() == access_expiry);

// Pro auto-renewing flag: presence-only, defaults to false, and (backend-derived state, not a
// user edit) does not stamp the profile-updated timestamp.
CHECK_FALSE(profile.get_pro_auto_renewing());
UserProfileTester::set_profile_updated(profile, std::chrono::sys_seconds{456s});
profile.set_pro_auto_renewing(true);
CHECK(profile.get_pro_auto_renewing());
CHECK(profile.get_profile_updated().time_since_epoch().count() == 456);
profile.set_pro_auto_renewing(false);
CHECK_FALSE(profile.get_pro_auto_renewing());

// Refund-requested flag (synced via config, not the Pro backend)
CHECK_FALSE(profile.get_refund_requested().has_value());

Expand Down