Skip to content

Commit

Permalink
Protobuf integration
Browse files Browse the repository at this point in the history
- protobuf added as external dependency
- SessionProtos.pro and WebSocketResources.proto looped in from session-ios codebase
- cmake target implemented s.t. wrapping/unwrapping functions can be defined and implemented in protos.{cpp,hpp}
  • Loading branch information
dr7ana committed Sep 6, 2023
1 parent 364f8d3 commit 0652ad1
Show file tree
Hide file tree
Showing 25 changed files with 722 additions and 36 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)

add_subdirectory(external)
add_subdirectory(src)
add_subdirectory(proto)

if(STATIC_BUNDLE)
include(combine_archives)
Expand Down
69 changes: 46 additions & 23 deletions include/session/config/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "base.h"
#include "namespaces.hpp"
#include "protos.hpp"

namespace session::config {

Expand Down Expand Up @@ -200,6 +201,12 @@ class ConfigBase : public ConfigSig {
void set_verifier(ConfigMessage::verify_callable v) override;
void set_signer(ConfigMessage::sign_callable s) override;

// Virtual method to be overloaded by derived classes. Protobuf wrapped messages are used
// for legacy types, so we need different logic depending on the class in question. All new
// types will reject the protobuf and directly handle the binary data. Old types will try
// protobuf parsing on incoming messages and handle all outgoing messages directly in binary
virtual bool accepts_protobuf() const { return false; }

public:
// class for proxying subfield access; this class should never be stored but only used
// ephemerally (most of its methods are rvalue-qualified). This lets constructs such as
Expand Down Expand Up @@ -745,6 +752,36 @@ class ConfigBase : public ConfigSig {
};

protected:
/// API: base/ConfigBase::_merge
///
/// Internal implementation of merge. This takes all of the messages pulled down from the server
/// and does whatever is necessary to merge (or replace) the current values.
///
/// Values are pairs of the message hash (as provided by the server) and the raw message body.
///
/// After this call the caller should check `needs_push()` to see if the data on hand was
/// updated and needs to be pushed to the server again (for example, because the data contained
/// conflicts that required another update to resolve).
///
/// Returns the number of the given config messages that were successfully parsed.
///
/// Will throw on serious error (i.e. if neither the current nor any of the given configs are
/// parseable). This should not happen (the current config, at least, should always be
/// re-parseable).
///
/// Declaration:
/// ```cpp
/// int _merge(const std::vector<std::pair<std::string, ustring_view>>& configs);
/// int _merge(const std::vector<std::pair<std::string, ustring>>& configs);
/// ```
///
/// Inputs:
/// - `configs` -- vector of pairs containing the message hash and the raw message body
///
/// Outputs:
/// - `int` -- Returns how many config messages that were successfully parsed
int _merge(const std::vector<std::pair<std::string, ustring_view>>& configs);

/// API: base/ConfigBase::extra_data
///
/// Called when dumping to obtain any extra data that a subclass needs to store to reconstitute
Expand Down Expand Up @@ -849,37 +886,23 @@ class ConfigBase : public ConfigSig {

/// API: base/ConfigBase::merge
///
/// This takes all of the messages pulled down from the server and does whatever is necessary to
/// merge (or replace) the current values.
///
/// Values are pairs of the message hash (as provided by the server) and the raw message body.
///
/// After this call the caller should check `needs_push()` to see if the data on hand was
/// updated and needs to be pushed to the server again (for example, because the data contained
/// conflicts that required another update to resolve).
///
/// Returns the number of the given config messages that were successfully parsed.
///
/// Will throw on serious error (i.e. if neither the current nor any of the given configs are
/// parseable). This should not happen (the current config, at least, should always be
/// re-parseable).
///
/// Declaration:
/// ```cpp
/// int merge(const std::vector<std::pair<std::string, ustring_view>>& configs);
/// int merge(const std::vector<std::pair<std::string, ustring>>& configs);
/// ```
/// Wrapper around ConfigBase::_merge to handle protobuf parsing. Currently, in the transition
/// from legacy to new config groups, legacy groups using protobuf must be compatible with
/// new config groups handling raw binary I/O. As a result, this method checks the
/// accepts_protobuf() override for the given class, and either passes the parsed protobuf or
/// the original binary data to _merge
///
/// Inputs:
/// - `configs` -- vector of pairs containing the message hash and the raw message body
///
/// Outputs:
/// - `int` -- Returns how many config messages that were successfully parsed
virtual int merge(const std::vector<std::pair<std::string, ustring_view>>& configs);

// Same as merge (above )but takes the values as ustring's as sometimes that is more convenient.
int merge(const std::vector<std::pair<std::string, ustring>>& configs);

// Same as above, but passes the ustring_views to the overload of protos::handle_incoming that
// takes ustring_views
int merge(const std::vector<std::pair<std::string, ustring_view>>& configs);

/// API: base/ConfigBase::is_dirty
///
/// Returns true if we are currently dirty (i.e. have made changes that haven't been serialized
Expand Down
2 changes: 2 additions & 0 deletions include/session/config/contacts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ class Contacts : public ConfigBase {
/// - `bool` - Returns true if the contact list is empty
bool empty() const { return size() == 0; }

bool accepts_protobuf() const override { return true; }

struct iterator;
/// API: contacts/contacts::begin
///
Expand Down
2 changes: 2 additions & 0 deletions include/session/config/convo_info_volatile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,8 @@ class ConvoInfoVolatile : public ConfigBase {
/// - `bool` -- Returns true if the convesation list is empty
bool empty() const { return size() == 0; }

bool accepts_protobuf() const override { return true; }

struct iterator;
/// API: convo_info_volatile/ConvoInfoVolatile::begin
///
Expand Down
2 changes: 2 additions & 0 deletions include/session/config/user_groups.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ class UserGroups : public ConfigBase {
/// - `bool` - Returns true if the contact list is empty
bool empty() const { return size() == 0; }

bool accepts_protobuf() const override { return true; }

struct iterator;
/// API: user_groups/UserGroups::begin
///
Expand Down
2 changes: 2 additions & 0 deletions include/session/config/user_profile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ class UserProfile final : public ConfigBase {
/// not, and `std::nullopt` to drop the setting from the config (and thus use the client's
/// default).
void set_blinded_msgreqs(std::optional<bool> enabled);

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

} // namespace session::config
7 changes: 7 additions & 0 deletions include/session/util.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#pragma once
#include <sodium/randombytes.h>

#include <cassert>
#include <chrono>
#include <cstring>
#include <iterator>
#include <memory>
Expand Down Expand Up @@ -39,6 +42,10 @@ inline std::string_view from_unsigned_sv(const std::vector<T, A>& v) {
return {from_unsigned(v.data()), v.size()};
}

inline uint64_t get_timestamp() {
return std::chrono::steady_clock::now().time_since_epoch().count();
}

/// Returns true if the first string is equal to the second string, compared case-insensitively.
inline bool string_iequal(std::string_view s1, std::string_view s2) {
return std::equal(s1.begin(), s1.end(), s2.begin(), s2.end(), [](char a, char b) {
Expand Down
51 changes: 51 additions & 0 deletions proto/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

function(check_target target)
if (NOT TARGET ${target})
message(FATAL_ERROR "Project failed to compile required target: ${target}")
endif()
endfunction()

include(FetchContent)

FetchContent_Declare(
protobuf
GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git
GIT_TAG v4.24.2 # apparently this must be a tag (not hash) for git_shallow to work?
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)

set(protobuf_INSTALL OFF CACHE BOOL "" FORCE)
set(protobuf_WITH_ZLIB OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(protobuf_ABSL_PROVIDER "module" CACHE STRING "" FORCE)
set(protobuf_BUILD_PROTOC_BINARIES ON CACHE BOOL "" FORCE)
set(protobuf_BUILD_PROTOBUF_BINARIES ON CACHE BOOL "" FORCE)

message(STATUS "Pulling protobuf repository...")

FetchContent_MakeAvailable(protobuf)

check_target(protobuf::libprotobuf-lite)
check_target(protobuf::libprotobuf)
check_target(protobuf::libprotoc)
check_target(protobuf::protoc)

set(Protobuf_INCLUDE_DIR "${protobuf_SOURCE_DIR}/src" CACHE INTERNAL "")
set(Protobuf_LIBRARIES protobuf::libprotobuf CACHE INTERNAL "")
find_package(Protobuf REQUIRED) # makes protobuf_generate_cpp available

include_directories(${CMAKE_CURRENT_BINARY_DIR})

add_library(protos protos.cpp)
target_link_libraries(protos PUBLIC ${Protobuf_LIBRARIES} libsession::config)

foreach(pfile SessionProtos WebSocketResources)
protobuf_generate_cpp(${pfile}_PROTO_SRCS ${pfile}_PROTO_HDRS ${pfile}.proto)
target_sources(protos PUBLIC ${${pfile}_PROTO_SRCS})
endforeach()

add_executable(test_proto test_proto.cpp)
target_link_libraries(test_proto PUBLIC protos)

0 comments on commit 0652ad1

Please sign in to comment.