diff --git a/include/pro/types.hpp b/include/pro/types.hpp index aca78bf..ed717b8 100644 --- a/include/pro/types.hpp +++ b/include/pro/types.hpp @@ -69,11 +69,12 @@ struct toJs_impl { Napi::Object operator()(const Napi::Env& env, const session::DecodedPro decoded_pro) { auto obj = Napi::Object::New(env); - obj["proStatus"] = - toJs(env, - decoded_pro.status == ProStatus::InvalidProBackendSig ? "InvalidProBackendSig" - : decoded_pro.status == ProStatus::InvalidUserSig ? "InvalidUserSig" - : "Valid"); + obj["proStatus"] = toJs( + env, + decoded_pro.status == ProStatus::Valid || decoded_pro.status == ProStatus::Expired + ? "ValidOrExpired" + : decoded_pro.status == ProStatus::InvalidProBackendSig ? "InvalidProBackendSig" + : "InvalidUserSig"); obj["proProof"] = toJs(env, decoded_pro.proof); obj["proProfileBitset"] = proProfileBitsetToJS(env, decoded_pro.profile_bitset); obj["proMessageBitset"] = proMessageBitsetToJS(env, decoded_pro.msg_bitset); diff --git a/include/user_config.hpp b/include/user_config.hpp index b179a49..6af4579 100644 --- a/include/user_config.hpp +++ b/include/user_config.hpp @@ -40,9 +40,11 @@ class UserConfigWrapper : public ConfigBaseImpl, public Napi::ObjectWrap #include "oxen/log/catlogger.hpp" +#include "oxenc/base64.h" #include "oxenc/hex.h" #include "session/config/namespaces.hpp" #include "session/config/profile_pic.hpp" @@ -43,6 +44,7 @@ void assertInfoMinLength(const Napi::CallbackInfo& info, const int minLength); void assertIsStringOrNull(const Napi::Value& value, const std::string& identifier = ""); void assertIsNumber(const Napi::Value& value, const std::string& identifier); +void assertIsNumberOrNull(const Napi::Value& val, const std::string& identifier); void assertIsBigint(const Napi::Value& val, const std::string& identifier); void assertIsArray(const Napi::Value& value, const std::string& identifier); void assertIsObject(const Napi::Value& value); @@ -80,10 +82,15 @@ int64_t toCppInteger(Napi::Value x, const std::string& identifier, bool allowUnd int64_t toCppIntegerB(Napi::Value x, const std::string& identifier, bool allowUndefined = false); std::optional maybeNonemptyInt(Napi::Value x, const std::string& identifier); +std::optional maybeNonemptyIntB(Napi::Value x, const std::string& identifier); + std::optional maybeNonemptyBoolean(Napi::Value x, const std::string& identifier); std::optional maybeNonemptySysSeconds( Napi::Value x, const std::string& identifier); +std::optional> maybeNonemptyTimeMs( + Napi::Value x, const std::string& identifier); + std::chrono::sys_seconds toCppSysSeconds(Napi::Value x, const std::string& identifier); std::chrono::sys_time toCppSysMs( Napi::Value x, const std::string& identifier); @@ -425,6 +432,21 @@ std::array from_hex_to_array(std::string x) { return result; } +template +std::array from_base64_to_array(std::string x) { + std::string as_b64 = oxenc::from_base64(x); + if (as_b64.size() != N) { + throw std::invalid_argument(fmt::format( + "from_base64_to_array: Decoded v64 size mismatch: expected {}, got {}", + N, + as_b64.size())); + } + + std::array result; + std::memcpy(result.data(), as_b64.data(), N); + return result; +} + std::vector from_hex_to_vector(std::string_view x); std::span from_base64_to_span(std::string_view x); diff --git a/libsession-util b/libsession-util index 3d79bb2..a8c935f 160000 --- a/libsession-util +++ b/libsession-util @@ -1 +1 @@ -Subproject commit 3d79bb2d2b5b62d5240fa3307e0901a2e217192a +Subproject commit a8c935fe2a3ece8a686154abd0c6bb18378ce3f5 diff --git a/package.json b/package.json index 339fe64..87c514d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "main": "index.js", "name": "libsession_util_nodejs", "description": "Wrappers for the Session Util Library", - "version": "0.6.5", + "version": "0.6.6", "license": "GPL-3.0", "author": { "name": "Oxen Project", diff --git a/src/constants.cpp b/src/constants.cpp index 13c906b..9d8cfac 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -5,6 +5,7 @@ #include "session/config/groups/info.hpp" #include "session/config/user_groups.hpp" #include "session/pro_backend.h" +#include "session/session_protocol.h" #include "session/version.h" #include "utilities.hpp" #include "version.h" @@ -17,11 +18,11 @@ Napi::Object ConstantsWrapper::Init(Napi::Env env, Napi::Object exports) { const char* class_name = "CONSTANTS"; auto pro_urls = Napi::Object::New(env); - pro_urls["roadmap"] = toJs(env, SESSION_PRO_URLS.roadmap); - pro_urls["privacy_policy"] = toJs(env, SESSION_PRO_URLS.privacy_policy); - pro_urls["terms_of_service"] = toJs(env, SESSION_PRO_URLS.terms_of_service); - pro_urls["pro_access_not_found"] = toJs(env, SESSION_PRO_URLS.pro_access_not_found); - pro_urls["support_url"] = toJs(env, SESSION_PRO_URLS.support_url); + pro_urls["roadmap"] = toJs(env, SESSION_PROTOCOL_STRINGS.url_pro_roadmap); + pro_urls["privacy_policy"] = toJs(env, SESSION_PROTOCOL_STRINGS.url_privacy_policy); + pro_urls["terms_of_service"] = toJs(env, SESSION_PROTOCOL_STRINGS.url_terms_of_service); + pro_urls["pro_access_not_found"] = toJs(env, SESSION_PROTOCOL_STRINGS.url_pro_access_not_found); + pro_urls["support_url"] = toJs(env, SESSION_PROTOCOL_STRINGS.url_pro_support); auto pro_provider_nil = Napi::Object::New(env); pro_provider_nil["device"] = toJs( diff --git a/src/contacts_config.cpp b/src/contacts_config.cpp index 7bc241e..1633f8e 100644 --- a/src/contacts_config.cpp +++ b/src/contacts_config.cpp @@ -5,6 +5,7 @@ #include "profile_pic.hpp" #include "session/config/expiring.hpp" #include "session/types.hpp" +#include "utilities.hpp" namespace session::nodeapi { @@ -51,6 +52,7 @@ struct toJs_impl { obj["expirationMode"] = toJs(env, expiration_mode_string(contact.exp_mode)); obj["expirationTimerSeconds"] = toJs(env, contact.exp_timer.count()); obj["profilePicture"] = toJs(env, contact.profile_picture); + obj["proProfileBitset"] = proProfileBitsetToJS(env, contact.profile_bitset); return obj; } @@ -160,6 +162,11 @@ void ContactsConfigWrapper::set(const Napi::CallbackInfo& info) { contact.profile_picture.clear(); } + if (auto proProfileBitset = maybeNonemptyIntB( + obj.Get("proProfileBitset"), "ContactsConfigWrapper.set proProfileBitset")) { + contact.profile_bitset.data = *proProfileBitset; + } + config.set(contact); }); } diff --git a/src/convo_info_volatile_config.cpp b/src/convo_info_volatile_config.cpp index a736aa2..74dc6d1 100644 --- a/src/convo_info_volatile_config.cpp +++ b/src/convo_info_volatile_config.cpp @@ -1,11 +1,12 @@ #include "convo_info_volatile_config.hpp" +#include + #include #include "base_config.hpp" #include "community.hpp" #include "session/config/convo_info_volatile.hpp" -#include "session/types.hpp" #include "utilities.hpp" namespace session::nodeapi { @@ -14,6 +15,40 @@ namespace convo = config::convo; using config::ConvoInfoVolatile; +struct ParsedBaseValues { + int64_t lastReadTsMs; + bool forcedUnread; + + Napi::Object obj; +}; + +void addBaseValues(const Napi::Env& env, Napi::Object obj, const convo::base& base) { + obj["lastReadTsMs"] = toJs(env, base.last_read); + obj["forcedUnread"] = toJs(env, base.unread); +} + +ParsedBaseValues parseBaseValues( + const Napi::CallbackInfo& info, convo::base& base, const std::string fnName) { + assertInfoLength(info, 2); + auto baseObj = info[1]; + assertIsObject(baseObj); + + auto obj = baseObj.As(); + auto lastReadTsMsJs = obj.Get("lastReadTsMs"); + assertIsNumber(lastReadTsMsJs, fnName + "lastReadTsMs"); + auto lastReadTsMsCpp = toCppInteger(lastReadTsMsJs, fnName + "lastReadTsMs"); + + auto forcedUnreadJs = obj.Get("forcedUnread"); + assertIsBoolean(forcedUnreadJs, fnName + "forcedUnread"); + auto lastReadTsMs = toCppBoolean(forcedUnreadJs, fnName + "forcedUnread"); + + ParsedBaseValues result; + result.lastReadTsMs = lastReadTsMsCpp; + result.forcedUnread = lastReadTsMs; + result.obj = obj; + return result; +} + template <> struct toJs_impl { Napi::Object operator()(const Napi::Env& env, const convo::one_to_one& info_1o1) { @@ -21,8 +56,16 @@ struct toJs_impl { auto obj = Napi::Object::New(env); obj["pubkeyHex"] = toJs(env, info_1o1.session_id); - obj["unread"] = toJs(env, info_1o1.unread); - obj["lastRead"] = toJs(env, info_1o1.last_read); + addBaseValues(env, obj, info_1o1); + + if (info_1o1.pro_gen_index_hash->empty() || + !info_1o1.pro_expiry_unix_ts.time_since_epoch().count()) { + obj["proGenIndexHashB64"] = env.Null(); + obj["proExpiryTsMs"] = env.Null(); + } else { + obj["proGenIndexHashB64"] = toJs(env, to_base64(*info_1o1.pro_gen_index_hash)); + obj["proExpiryTsMs"] = toJs(env, info_1o1.pro_expiry_unix_ts); + } return obj; } @@ -34,8 +77,7 @@ struct toJs_impl { auto obj = Napi::Object::New(env); obj["pubkeyHex"] = toJs(env, info_legacy.id); - obj["unread"] = toJs(env, info_legacy.unread); - obj["lastRead"] = toJs(env, info_legacy.last_read); + addBaseValues(env, obj, info_legacy); return obj; } @@ -45,8 +87,8 @@ template <> struct toJs_impl : toJs_impl { Napi::Object operator()(const Napi::Env& env, const convo::community info_comm) { auto obj = toJs_impl::operator()(env, info_comm); - obj["unread"] = toJs(env, info_comm.unread); - obj["lastRead"] = toJs(env, info_comm.last_read); + addBaseValues(env, obj, info_comm); + return obj; } }; @@ -57,8 +99,7 @@ struct toJs_impl { auto obj = Napi::Object::New(env); obj["pubkeyHex"] = toJs(env, group_info.id); - obj["unread"] = toJs(env, group_info.unread); - obj["lastRead"] = toJs(env, group_info.last_read); + addBaseValues(env, obj, group_info); return obj; } @@ -122,22 +163,45 @@ Napi::Value ConvoInfoVolatileWrapper::getAll1o1(const Napi::CallbackInfo& info) void ConvoInfoVolatileWrapper::set1o1(const Napi::CallbackInfo& info) { wrapExceptions(info, [&] { - assertInfoLength(info, 3); + assertInfoLength(info, 2); auto first = info[0]; assertIsString(first); - auto second = info[1]; - assertIsNumber(second, "set1o1"); - - auto third = info[2]; - assertIsBoolean(third); - - auto convo = config.get_or_construct_1to1(toCppString(first, "convoInfo.set1o1")); - - if (auto last_read = toCppInteger(second, "convoInfo.set1o1_2"); - last_read > convo.last_read) - convo.last_read = last_read; - convo.unread = toCppBoolean(third, "convoInfo.set1o1_3"); + std::string fnName = "ConvoInfoVolatileWrapper::set1o1."; + auto convo = config.get_or_construct_1to1(toCppString(first, fnName + "convoInfo")); + + auto parsed = parseBaseValues(info, convo, fnName); + if (parsed.lastReadTsMs > convo.last_read) + convo.last_read = parsed.lastReadTsMs; + convo.unread = parsed.forcedUnread; + + // 1o1 also have a pro gen index hash & pro expiry + auto proGenIndexHashB64Js = parsed.obj.Get("proGenIndexHashB64"); + assertIsStringOrNull(proGenIndexHashB64Js, fnName + "proGenIndexHashB64Js"); + auto proGenIndexHashB64Cpp = + maybeNonemptyString(proGenIndexHashB64Js, fnName + "proGenIndexHashB64Cpp"); + + auto proExpiryUnixTsMsJs = parsed.obj.Get("proExpiryTsMs"); + assertIsNumberOrNull(proExpiryUnixTsMsJs, fnName + "proExpiryUnixTsMsJs"); + auto proExpiryUnixTsMsCpp = + maybeNonemptyInt(proExpiryUnixTsMsJs, fnName + "proExpiryUnixTsMsCpp"); + // Note: null is used to ignore an update. i.e. if the field is unset, we do not want to + // overwrite the current value. + // To reset it, set it to empty string + if (proGenIndexHashB64Cpp.has_value()) { + if (proGenIndexHashB64Cpp->empty()) { + // if the first is set, but empty, we want to reset the field + convo.pro_gen_index_hash = std::nullopt; + } else { + // this throws if the size is wrong + convo.pro_gen_index_hash = from_base64_to_array<32>(*proGenIndexHashB64Cpp); + } + } + if (proExpiryUnixTsMsCpp.has_value()) { + // if the field is set (not null), we want to write the change as is + convo.pro_expiry_unix_ts = std::chrono::sys_time( + std::chrono::milliseconds(*proExpiryUnixTsMsCpp)); + } config.set(convo); }); @@ -164,23 +228,17 @@ Napi::Value ConvoInfoVolatileWrapper::getAllLegacyGroups(const Napi::CallbackInf void ConvoInfoVolatileWrapper::setLegacyGroup(const Napi::CallbackInfo& info) { wrapExceptions(info, [&] { - assertInfoLength(info, 3); + assertInfoLength(info, 2); auto first = info[0]; assertIsString(first); - auto second = info[1]; - assertIsNumber(second, "setLegacyGroup"); - - auto third = info[2]; - assertIsBoolean(third); - - auto convo = config.get_or_construct_legacy_group( - toCppString(first, "convoInfo.SetLegacyGroup1")); - if (auto last_read = toCppInteger(second, "convoInfo.SetLegacyGroup2"); - last_read > convo.last_read) - convo.last_read = last_read; + std::string fnName = "ConvoInfoVolatileWrapper::setLegacyGroup."; + auto convo = config.get_or_construct_legacy_group(toCppString(first, fnName + "convoInfo")); - convo.unread = toCppBoolean(third, "convoInfo.SetLegacyGroup3"); + auto parsed = parseBaseValues(info, convo, fnName); + if (parsed.lastReadTsMs > convo.last_read) + convo.last_read = parsed.lastReadTsMs; + convo.unread = parsed.forcedUnread; config.set(convo); }); @@ -206,22 +264,17 @@ Napi::Value ConvoInfoVolatileWrapper::getAllGroups(const Napi::CallbackInfo& inf void ConvoInfoVolatileWrapper::setGroup(const Napi::CallbackInfo& info) { wrapExceptions(info, [&] { - assertInfoLength(info, 3); + assertInfoLength(info, 2); auto first = info[0]; assertIsString(first); - auto second = info[1]; - assertIsNumber(second, "setGroup"); - auto third = info[2]; - assertIsBoolean(third); + std::string fnName = "ConvoInfoVolatileWrapper::setGroup."; + auto convo = config.get_or_construct_group(toCppString(first, fnName + "convoInfo")); - auto convo = config.get_or_construct_group(toCppString(first, "convoInfo.setGroup1")); - - if (auto last_read = toCppInteger(second, "convoInfo.setGroup2"); - last_read > convo.last_read) - convo.last_read = last_read; - - convo.unread = toCppBoolean(third, "convoInfo.setGroup3"); + auto parsed = parseBaseValues(info, convo, fnName); + if (parsed.lastReadTsMs > convo.last_read) + convo.last_read = parsed.lastReadTsMs; + convo.unread = parsed.forcedUnread; config.set(convo); }); @@ -250,24 +303,18 @@ Napi::Value ConvoInfoVolatileWrapper::getAllCommunities(const Napi::CallbackInfo void ConvoInfoVolatileWrapper::setCommunityByFullUrl(const Napi::CallbackInfo& info) { wrapExceptions(info, [&] { - assertInfoLength(info, 3); + assertInfoLength(info, 2); auto first = info[0]; assertIsString(first); - auto second = info[1]; - assertIsNumber(second, "setCommunityByFullUrl"); - - auto third = info[2]; - assertIsBoolean(third); - - auto convo = config.get_or_construct_community( - toCppString(first, "convoInfo.SetCommunityByFullUrl1")); + std::string fnName = "ConvoInfoVolatileWrapper::setCommunityByFullUrl."; - if (auto last_read = toCppInteger(second, "convoInfo.SetCommunityByFullUrl2"); - last_read > convo.last_read) - convo.last_read = last_read; + auto convo = config.get_or_construct_community(toCppString(first, fnName + "convoInfo")); - convo.unread = toCppBoolean(third, "convoInfo.SetCommunityByFullUrl3"); + auto parsed = parseBaseValues(info, convo, fnName); + if (parsed.lastReadTsMs > convo.last_read) + convo.last_read = parsed.lastReadTsMs; + convo.unread = parsed.forcedUnread; // Note: we only keep the messages read when their timestamp is not older // than 30 days or so (see libsession util PRUNE constant). so this `set()` diff --git a/src/encrypt_decrypt/encrypt_decrypt.cpp b/src/encrypt_decrypt/encrypt_decrypt.cpp index 72c64bc..8c04352 100644 --- a/src/encrypt_decrypt/encrypt_decrypt.cpp +++ b/src/encrypt_decrypt/encrypt_decrypt.cpp @@ -678,7 +678,6 @@ Napi::Value MultiEncryptWrapper::decryptFor1o1(const Napi::CallbackInfo& info) { // "messageHash": string, // }], // second: { - // "nowMs": number, // "proBackendPubkeyHex": Hexstring, // "ed25519PrivateKeyHex": Hexstring, // } @@ -698,7 +697,6 @@ Napi::Value MultiEncryptWrapper::decryptFor1o1(const Napi::CallbackInfo& info) { if (second.IsEmpty()) throw std::invalid_argument("decryptFor1o1 second received empty"); - auto nowMs = extractNowSysMs(second, "decryptFor1o1.second.nowMs"); auto proBackendPubkeyHex = extractProBackendPubkeyHex(second, "decryptFor1o1.second.proBackendPubkeyHex"); @@ -727,8 +725,8 @@ Napi::Value MultiEncryptWrapper::decryptFor1o1(const Napi::CallbackInfo& info) { auto envelopePayload = extractEnvelopePayload(obj, "decryptFor1o1.obj.envelopePayload"); - decrypted.push_back(session::decode_envelope( - keys, envelopePayload, nowMs, proBackendPubkeyHex)); + decrypted.push_back( + session::decode_envelope(keys, envelopePayload, proBackendPubkeyHex)); decryptedMessageHashes.push_back(messageHash); } catch (const std::exception& e) { log::warning( @@ -764,7 +762,6 @@ Napi::Value MultiEncryptWrapper::decryptForGroup(const Napi::CallbackInfo& info) // "messageHash": string, // }], // second: { - // "nowMs": number, // "proBackendPubkeyHex": Hexstring, // "ed25519GroupPubkeyHex": Hexstring, // "groupEncKeys": Array, @@ -785,7 +782,6 @@ Napi::Value MultiEncryptWrapper::decryptForGroup(const Napi::CallbackInfo& info) if (second.IsEmpty()) throw std::invalid_argument("decryptForGroup second received empty"); - auto nowMs = extractNowSysMs(second, "decryptForGroup.second.nowMs"); auto proBackendPubkeyHex = extractProBackendPubkeyHex(second, "decryptForGroup.second.proBackendPubkeyHex"); @@ -830,8 +826,8 @@ Napi::Value MultiEncryptWrapper::decryptForGroup(const Napi::CallbackInfo& info) auto envelopePayload = extractEnvelopePayload(obj, "decryptForGroup.obj.envelopePayload"); - decrypted.push_back(session::decode_envelope( - keys, envelopePayload, nowMs, proBackendPubkeyHex)); + decrypted.push_back( + session::decode_envelope(keys, envelopePayload, proBackendPubkeyHex)); decryptedMessageHashes.push_back(messageHash); } catch (const std::exception& e) { log::warning( diff --git a/src/user_config.cpp b/src/user_config.cpp index 098ba8c..53f634d 100644 --- a/src/user_config.cpp +++ b/src/user_config.cpp @@ -106,6 +106,8 @@ void UserConfigWrapper::Init(Napi::Env env, Napi::Object exports) { InstanceMethod("removeProConfig", &UserConfigWrapper::removeProConfig), InstanceMethod("setProBadge", &UserConfigWrapper::setProBadge), InstanceMethod("setAnimatedAvatar", &UserConfigWrapper::setAnimatedAvatar), + InstanceMethod("setProAccessExpiry", &UserConfigWrapper::setProAccessExpiry), + InstanceMethod("getProAccessExpiry", &UserConfigWrapper::getProAccessExpiry), InstanceMethod("getProProfileBitset", &UserConfigWrapper::getProProfileBitset), InstanceMethod( "generateProMasterKey", &UserConfigWrapper::generateProMasterKey), @@ -296,6 +298,10 @@ Napi::Value UserConfigWrapper::getProProfileBitset(const Napi::CallbackInfo& inf info, [&] { return proProfileBitsetToJS(info.Env(), config.get_profile_bitset()); }); } +Napi::Value UserConfigWrapper::getProAccessExpiry(const Napi::CallbackInfo& info) { + return wrapResult(info, [&] { return toJs(info.Env(), config.get_pro_access_expiry()); }); +} + void UserConfigWrapper::setProBadge(const Napi::CallbackInfo& info) { wrapExceptions(info, [&] { assertInfoLength(info, 1); @@ -318,6 +324,18 @@ void UserConfigWrapper::setAnimatedAvatar(const Napi::CallbackInfo& info) { }); } +void UserConfigWrapper::setProAccessExpiry(const Napi::CallbackInfo& info) { + wrapExceptions(info, [&] { + assertInfoLength(info, 1); + assertIsNumberOrNull(info[0], "setProAccessExpiry"); + + auto proAccessExpiryMs = + maybeNonemptyTimeMs(info[0], "UserConfigWrapper::setProAccessExpiry"); + + config.set_pro_access_expiry(proAccessExpiryMs); + }); +} + Napi::Value UserConfigWrapper::generateProMasterKey(const Napi::CallbackInfo& info) { return wrapResult(info, [&] { assertInfoLength(info, 1); diff --git a/src/utilities.cpp b/src/utilities.cpp index 5940a6f..cdce84e 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -32,6 +32,12 @@ void assertIsNumber(const Napi::Value& val, const std::string& identifier) { std::string("Wrong arguments: expected number: " + identifier).c_str()); } +void assertIsNumberOrNull(const Napi::Value& val, const std::string& identifier) { + checkOrThrow( + val.IsNumber() || val.IsNull(), + std::string("Wrong arguments: expected number or null: " + identifier).c_str()); +} + void assertIsBigint(const Napi::Value& val, const std::string& identifier) { checkOrThrow( val.IsBigInt() && !val.IsEmpty() && !val.IsNull() && !val.IsUndefined(), @@ -151,6 +157,16 @@ int64_t toCppIntegerB(Napi::Value x, const std::string& identifier, bool allowUn throw std::invalid_argument{"Unsupported type for "s + identifier + ": expected a bigint"}; } +std::optional maybeNonemptyIntB(Napi::Value x, const std::string& identifier) { + if (x.IsNull() || x.IsUndefined()) + return std::nullopt; + if (x.IsBigInt()) { + return toCppIntegerB(x, identifier); + } + + throw std::invalid_argument{"maybeNonemptyInt with invalid type, called from " + identifier}; +} + std::optional maybeNonemptyInt(Napi::Value x, const std::string& identifier) { if (x.IsNull() || x.IsUndefined()) return std::nullopt; @@ -176,6 +192,19 @@ std::optional maybeNonemptySysSeconds( "maybeNonemptySysSeconds with invalid type, called from " + identifier}; } +std::optional> maybeNonemptyTimeMs( + Napi::Value x, const std::string& identifier) { + if (x.IsNull() || x.IsUndefined()) + return std::nullopt; + + if (x.IsNumber()) { + auto num = x.As().Int64Value(); + return std::chrono::sys_time{std::chrono::milliseconds{num}}; + } + + throw std::invalid_argument{"maybeNonemptySysMs with invalid type, called from " + identifier}; +} + std::chrono::sys_seconds toCppSysSeconds(Napi::Value x, const std::string& identifier) { if (x.IsNumber()) { diff --git a/types/multi_encrypt/multi_encrypt.d.ts b/types/multi_encrypt/multi_encrypt.d.ts index 37f187e..59dab49 100644 --- a/types/multi_encrypt/multi_encrypt.d.ts +++ b/types/multi_encrypt/multi_encrypt.d.ts @@ -173,12 +173,12 @@ declare module 'libsession_util_nodejs' { decryptFor1o1: ( first: Array, - second: WithNowMs & WithProBackendPubkey & WithEd25519PrivateKeyHex + second: WithProBackendPubkey & WithEd25519PrivateKeyHex ) => Array; decryptForGroup: ( first: Array, - second: WithNowMs & WithProBackendPubkey & WithEd25519GroupPubkeyHex & WithGroupEncryptionKeys + second: WithProBackendPubkey & WithEd25519GroupPubkeyHex & WithGroupEncryptionKeys ) => Array; }; diff --git a/types/pro/pro.d.ts b/types/pro/pro.d.ts index 60944d1..6a10cec 100644 --- a/types/pro/pro.d.ts +++ b/types/pro/pro.d.ts @@ -10,7 +10,7 @@ declare module 'libsession_util_nodejs' { proBackendPubkeyHex: string; }; - type ProStatus = 'InvalidProBackendSig' | 'InvalidUserSig' | 'Valid'; + type ProStatus = 'ValidOrExpired' | 'Invalid'; type WithProProfileBitset = { proProfileBitset: bigint }; type WithProMessageBitset = { proMessageBitset: bigint }; type WithGenIndexHash = { genIndexHashB64: string }; diff --git a/types/user/contacts.d.ts b/types/user/contacts.d.ts index a2eee0f..76a0fa8 100644 --- a/types/user/contacts.d.ts +++ b/types/user/contacts.d.ts @@ -51,12 +51,24 @@ declare module 'libsession_util_nodejs' { approved?: boolean; approvedMe?: boolean; blocked?: boolean; + /** + * The pro features bitset for this contact. + * see `SESSION_PROTOCOL_PRO_FEATURES`. + * To unset the pro features, set this to 0, if undefined or null no changes + */ + proProfileBitset?: bigint; }; export type ContactInfoGet = ContactInfoShared & { approved: boolean; approvedMe: boolean; blocked: boolean; + /** + * The bitset of pro features that this user currently has enabled. + * see `SESSION_PROTOCOL_PRO_FEATURES`. + * defaults to 0. + */ + proProfileBitset: bigint; }; export class ContactsConfigWrapperNode extends BaseConfigWrapperNode { diff --git a/types/user/convovolatile.d.ts b/types/user/convovolatile.d.ts index fd2cc1c..681de51 100644 --- a/types/user/convovolatile.d.ts +++ b/types/user/convovolatile.d.ts @@ -5,16 +5,33 @@ declare module 'libsession_util_nodejs' { export type ConvoVolatileType = '1o1' | UserGroupsType; export type BaseConvoInfoVolatile = { - lastRead: number; // defaults to 0, unixTimestamp in ms - unread: boolean; // defaults to false + lastReadTsMs: number; // defaults to 0, unixTimestamp in ms + forcedUnread: boolean; // defaults to false }; - type ConvoInfoVolatile1o1 = BaseConvoInfoVolatile & { pubkeyHex: string }; - type ConvoInfoVolatileLegacyGroup = BaseConvoInfoVolatile & { pubkeyHex: string }; - type ConvoInfoVolatileGroup = BaseConvoInfoVolatile & { pubkeyHex: GroupPubkeyType }; - type ConvoInfoVolatileCommunity = BaseConvoInfoVolatile & CommunityDetails; + type ConvoVolatile1o1GetExtra = { + pubkeyHex: string; + } & { proExpiryTsMs: number | null; genIndexHashB64: string | null }; - // type ConvoInfoVolatileCommunity = BaseConvoInfoVolatile & { pubkeyHex: string }; // we need a `set` with the full url but maybe not for the `get` + type ConvoVolatile1o1SetExtra = { + /** + * The timestamp of the proof expiry in milliseconds. + * If null, no changes will be made. + * To force the field to be reset, you need to provide 0 here + */ + proExpiryTsMs: number | null; + /** + * The base64 encoded `genIndexHash` of the proof (32 bytes) + * If null, no changes will be made. + * To force the field to be reset, you need to provide an empty string here + */ + proGenIndexHashB64: string | null; + }; + + type ConvoInfoVolatileGet1o1 = BaseConvoInfoVolatile & ConvoVolatile1o1GetExtra; + type ConvoInfoVolatileGetLegacyGroup = BaseConvoInfoVolatile & { pubkeyHex: string }; + type ConvoInfoVolatileGetGroup = BaseConvoInfoVolatile & { pubkeyHex: GroupPubkeyType }; + type ConvoInfoVolatileGetCommunity = BaseConvoInfoVolatile & CommunityDetails; type ConvoInfoVolatileWrapper = BaseConfigWrapper & { init: (secretKey: Uint8Array, dump: Uint8Array | null) => void; @@ -22,27 +39,27 @@ declare module 'libsession_util_nodejs' { free: () => void; // 1o1 related methods - get1o1: (pubkeyHex: string) => ConvoInfoVolatile1o1 | null; - getAll1o1: () => Array; - set1o1: (pubkeyHex: string, lastRead: number, unread: boolean) => void; + get1o1: (pubkeyHex: string) => ConvoInfoVolatileGet1o1 | null; + getAll1o1: () => Array; + set1o1: (pubkeyHex: string, args: BaseConvoInfoVolatile & ConvoVolatile1o1SetExtra) => void; erase1o1: (pubkeyHex: string) => void; // legacy group related methods - getLegacyGroup: (pubkeyHex: string) => ConvoInfoVolatileLegacyGroup | null; - getAllLegacyGroups: () => Array; - setLegacyGroup: (pubkeyHex: string, lastRead: number, unread: boolean) => void; + getLegacyGroup: (pubkeyHex: string) => ConvoInfoVolatileGetLegacyGroup | null; + getAllLegacyGroups: () => Array; + setLegacyGroup: (pubkeyHex: string, args: BaseConvoInfoVolatile) => void; eraseLegacyGroup: (pubkeyHex: string) => boolean; // group related methods - getGroup: (pubkeyHex: GroupPubkeyType) => ConvoInfoVolatileGroup | null; - getAllGroups: () => Array; - setGroup: (pubkeyHex: GroupPubkeyType, lastRead: number, unread: boolean) => void; + getGroup: (pubkeyHex: GroupPubkeyType) => ConvoInfoVolatileGetGroup | null; + getAllGroups: () => Array; + setGroup: (pubkeyHex: GroupPubkeyType, args: BaseConvoInfoVolatile) => void; eraseGroup: (pubkeyHex: GroupPubkeyType) => boolean; // communities related methods - getCommunity: (communityFullUrl: string) => ConvoInfoVolatileCommunity | null; // pubkey not required - getAllCommunities: () => Array; - setCommunityByFullUrl: (fullUrlWithPubkey: string, lastRead: number, unread: boolean) => void; + getCommunity: (communityFullUrl: string) => ConvoInfoVolatileGetCommunity | null; // pubkey not required + getAllCommunities: () => Array; + setCommunityByFullUrl: (fullUrlWithPubkey: string, args: BaseConvoInfoVolatile) => void; eraseCommunityByFullUrl: (fullUrlWithOrWithoutPubkey: string) => void; }; diff --git a/types/user/userconfig.d.ts b/types/user/userconfig.d.ts index d6cfed0..2790bbe 100644 --- a/types/user/userconfig.d.ts +++ b/types/user/userconfig.d.ts @@ -43,6 +43,7 @@ declare module 'libsession_util_nodejs' { removeProConfig: () => boolean; setAnimatedAvatar: (enabled: boolean) => void; + setProAccessExpiry: (expiryTsMs: number) => void; setProBadge: (enabled: boolean) => void; /** * @@ -50,6 +51,8 @@ declare module 'libsession_util_nodejs' { */ getProProfileBitset: () => bigint; + getProAccessExpiry: () => number | null; + generateProMasterKey: ({ ed25519SeedHex, }: { @@ -94,6 +97,8 @@ declare module 'libsession_util_nodejs' { public getProConfig: UserConfigWrapper['getProConfig']; public setProConfig: UserConfigWrapper['setProConfig']; public removeProConfig: UserConfigWrapper['removeProConfig']; + public getProAccessExpiry: UserConfigWrapper['getProAccessExpiry']; + public setProAccessExpiry: UserConfigWrapper['setProAccessExpiry']; public getProProfileBitset: UserConfigWrapper['getProProfileBitset']; public setAnimatedAvatar: UserConfigWrapper['setAnimatedAvatar']; public setProBadge: UserConfigWrapper['setProBadge']; @@ -128,6 +133,8 @@ declare module 'libsession_util_nodejs' { | MakeActionCall | MakeActionCall | MakeActionCall + | MakeActionCall + | MakeActionCall | MakeActionCall | MakeActionCall | MakeActionCall;