aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2018-11-01 07:16:38 +0100
committerdec05eba <dec05eba@protonmail.com>2018-11-01 07:16:39 +0100
commit88916c50b83bfad1ec1c5dd5f4e4f5d345851cb0 (patch)
tree8a318bf838f1be74e1957aef15b9e5c10ff068d9
parent07c9e3d393db4b4d59262f7d9898be169ac2f927 (diff)
Move code from dchat_gtk to generic place
m---------depends/odhtdb0
-rw-r--r--include/dchat/Room.hpp102
-rw-r--r--include/dchat/RoomDataType.hpp16
-rw-r--r--include/dchat/User.hpp16
-rw-r--r--src/Room.cpp226
5 files changed, 360 insertions, 0 deletions
diff --git a/depends/odhtdb b/depends/odhtdb
-Subproject e2eb4f72050a297668850deed91cc88860b6ad4
+Subproject 88149764207c7719b6a979c5eb4dea3269bdd3e
diff --git a/include/dchat/Room.hpp b/include/dchat/Room.hpp
new file mode 100644
index 0000000..4422a6f
--- /dev/null
+++ b/include/dchat/Room.hpp
@@ -0,0 +1,102 @@
+#pragma once
+
+#include "utils.hpp"
+#include "types.hpp"
+#include "StringView.hpp"
+#include "User.hpp"
+#include <string>
+#include <memory>
+#include <vector>
+#include <functional>
+#include <odhtdb/Database.hpp>
+
+namespace dchat
+{
+ struct RoomMessage
+ {
+ odhtdb::Hash id;
+ std::shared_ptr<User> creator;
+ uint32_t timestampSeconds;
+ std::string text;
+ };
+
+ class Room
+ {
+ DISABLE_COPY(Room)
+ public:
+ Room(std::shared_ptr<odhtdb::Database> database, std::shared_ptr<odhtdb::Hash> id, std::shared_ptr<odhtdb::OwnedByteArray> encryptionKey);
+ std::shared_ptr<User> addUser(const odhtdb::Signature::PublicKey &userPublicKey, const odhtdb::DataView groupId);
+ // Returns null if user doesn't exist in room
+ std::shared_ptr<User> getUserByPublicKey(const odhtdb::Signature::PublicKey &userPublicKey);
+ void publishMessage(const std::string &msg);
+
+ std::shared_ptr<odhtdb::Database> database;
+ std::shared_ptr<odhtdb::Hash> id;
+ std::shared_ptr<odhtdb::OwnedByteArray> encryptionKey;
+ std::string name;
+ odhtdb::Signature::MapPublicKey<std::shared_ptr<User>> userByPublicKey;
+ std::vector<RoomMessage> messages;
+ std::shared_ptr<User> localUser;
+ // Used for local users
+ odhtdb::Signature::MapPublicKey<std::shared_ptr<odhtdb::Signature::KeyPair>> publicKeyToKeyPairMap;
+ void *userdata;
+ };
+
+ class Rooms;
+
+ struct RoomAddMessageRequest
+ {
+ std::shared_ptr<Room> room;
+ bool loadedFromCache;
+ RoomMessage message;
+ };
+
+ struct UserChangeNicknameRequest
+ {
+ std::shared_ptr<Room> room;
+ std::shared_ptr<User> user;
+ uint32_t timestampSeconds;
+ bool loadedFromCache;
+ std::string newNickname;
+ };
+
+ // if connection failed then @rooms is null and errMsg contains the error
+ using ConnectBoostrapNodeCallbackFunc = std::function<void(std::shared_ptr<Rooms> rooms, const char *errMsg)>;
+ using CreateRoomCallbackFunc = std::function<void(std::shared_ptr<Room> room)>;
+ using RoomAddUserCallbackFunc = std::function<void(std::shared_ptr<Room> room, std::shared_ptr<User> user)>;
+ using RoomAddMessageCallbackFunc = std::function<void(const RoomAddMessageRequest &request)>;
+ using UserChangeNicknameCallbackFunc = std::function<void(const UserChangeNicknameRequest &request)>;
+ struct RoomCallbackFuncs
+ {
+ ConnectBoostrapNodeCallbackFunc connectCallbackFunc;
+ CreateRoomCallbackFunc createRoomCallbackFunc;
+ RoomAddUserCallbackFunc addUserCallbackFunc;
+ RoomAddMessageCallbackFunc addMessageCallbackFunc;
+ UserChangeNicknameCallbackFunc userChangeNicknameCallbackFunc;
+ };
+
+ class Rooms
+ {
+ DISABLE_COPY(Rooms)
+ public:
+ // @callbackFuncs.connectCallbackFunc can't be null
+ static void connect(const char *address, u16 port, RoomCallbackFuncs callbackFuncs);
+ // Throws on failure
+ void loginUser(const std::string &username, const std::string &password);
+ // Throws on failure
+ void registerUser(const std::string &username, const std::string &password);
+
+ std::shared_ptr<odhtdb::Database> database;
+ private:
+ Rooms(const char *address, u16 port, RoomCallbackFuncs callbackFuncs);
+ void createNodeCallbackFunc(const odhtdb::DatabaseCreateNodeRequest &request);
+ void addNodeCallbackFunc(const odhtdb::DatabaseAddNodeRequest &request);
+ void addUserCallbackFunc(const odhtdb::DatabaseAddUserRequest &request);
+ private:
+ odhtdb::MapHash<std::shared_ptr<Room>> roomById;
+ RoomCallbackFuncs callbackFuncs;
+ bool loggedIn;
+ odhtdb::MapHash<std::shared_ptr<odhtdb::Signature::KeyPair>> roomLocalUser;
+ odhtdb::MapHash<std::shared_ptr<odhtdb::OwnedByteArray>> roomEncryptionKey;
+ };
+} \ No newline at end of file
diff --git a/include/dchat/RoomDataType.hpp b/include/dchat/RoomDataType.hpp
new file mode 100644
index 0000000..cef2a8a
--- /dev/null
+++ b/include/dchat/RoomDataType.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "types.hpp"
+
+namespace dchat
+{
+ enum class RoomDataType : u8
+ {
+ ADD_MESSAGE,
+ EDIT_MESSAGE,
+ DELETE_MESSAGE,
+ NICKNAME_CHANGE,
+ CHANGE_AVATAR,
+ CHANGE_ROOM_NAME,
+ };
+} \ No newline at end of file
diff --git a/include/dchat/User.hpp b/include/dchat/User.hpp
new file mode 100644
index 0000000..4021c78
--- /dev/null
+++ b/include/dchat/User.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <string>
+#include <odhtdb/Signature.hpp>
+
+namespace dchat
+{
+ class User
+ {
+ public:
+ User(const odhtdb::Signature::PublicKey &_publicKey) : publicKey(_publicKey), userdata(nullptr) {}
+ const odhtdb::Signature::PublicKey publicKey;
+ std::string nickname;
+ void *userdata;
+ };
+} \ No newline at end of file
diff --git a/src/Room.cpp b/src/Room.cpp
new file mode 100644
index 0000000..cd6476d
--- /dev/null
+++ b/src/Room.cpp
@@ -0,0 +1,226 @@
+#include "../include/dchat/Room.hpp"
+#include "../include/dchat/Cache.hpp"
+#include "../include/dchat/RoomDataType.hpp"
+#include <odhtdb/Database.hpp>
+#include <odhtdb/Log.hpp>
+#include <sibs/SafeSerializer.hpp>
+#include <thread>
+
+// TODO: Remove error checks when odhtdb has been improved to take care of such errors
+namespace dchat
+{
+ Room::Room(std::shared_ptr<odhtdb::Database> _database, std::shared_ptr<odhtdb::Hash> _id, std::shared_ptr<odhtdb::OwnedByteArray> _encryptionKey) :
+ database(_database),
+ id(_id),
+ encryptionKey(_encryptionKey),
+ userdata(nullptr)
+ {
+
+ }
+
+ std::shared_ptr<User> Room::addUser(const odhtdb::Signature::PublicKey &userPublicKey, const odhtdb::DataView groupId)
+ {
+ auto it = userByPublicKey.find(userPublicKey);
+ if(it != userByPublicKey.end())
+ {
+ odhtdb::Log::error("The user %s already exists in the room %s", userPublicKey.toString().c_str(), id->toString().c_str());
+ assert(false);
+ return nullptr;
+ }
+
+ auto user = std::make_shared<User>(userPublicKey);
+ userByPublicKey[userPublicKey] = user;
+ return user;
+ }
+
+ std::shared_ptr<User> Room::getUserByPublicKey(const odhtdb::Signature::PublicKey &userPublicKey)
+ {
+ auto userIt = userByPublicKey.find(userPublicKey);
+ if(userIt != userByPublicKey.end())
+ return userIt->second;
+ return nullptr;
+ }
+
+ void Room::publishMessage(const std::string &msg)
+ {
+ sibs::SafeSerializer serializer;
+ serializer.add(RoomDataType::ADD_MESSAGE);
+ serializer.add((const u8*)msg.data(), msg.size());
+ auto &keyPair = publicKeyToKeyPairMap[localUser->publicKey];
+ odhtdb::DatabaseNode roomNode(encryptionKey, id);
+ database->addData(roomNode, *keyPair, { serializer.getBuffer().data(), serializer.getSize() });
+ }
+
+ Rooms::Rooms(const char *address, u16 port, RoomCallbackFuncs _callbackFuncs) :
+ callbackFuncs(_callbackFuncs),
+ loggedIn(false)
+ {
+ odhtdb::DatabaseCallbackFuncs databaseCallbackFuncs;
+ databaseCallbackFuncs.createNodeCallbackFunc = std::bind(&Rooms::createNodeCallbackFunc, this, std::placeholders::_1);
+ databaseCallbackFuncs.addNodeCallbackFunc = std::bind(&Rooms::addNodeCallbackFunc, this, std::placeholders::_1);
+ databaseCallbackFuncs.addUserCallbackFunc = std::bind(&Rooms::addUserCallbackFunc, this, std::placeholders::_1);
+ database = odhtdb::Database::connect(address, port, Cache::getDchatDir(), databaseCallbackFuncs).get();
+ }
+
+ void Rooms::createNodeCallbackFunc(const odhtdb::DatabaseCreateNodeRequest &request)
+ {
+ auto roomIt = roomById.find(*request.nodeHash);
+ if(roomIt != roomById.end())
+ {
+ odhtdb::Log::error("Room %s has already been created once", request.nodeHash->toString().c_str());
+ assert(false);
+ return;
+ }
+
+ auto encryptionKey = roomEncryptionKey[*request.nodeHash];
+ auto roomId = std::make_shared<odhtdb::Hash>(*request.nodeHash);
+ auto room = std::make_shared<Room>(database, roomId, encryptionKey);
+ roomById[*request.nodeHash] = room;
+ if(callbackFuncs.createRoomCallbackFunc)
+ callbackFuncs.createRoomCallbackFunc(room);
+
+ auto user = room->addUser(*request.creatorPublicKey, request.groupId);
+ if(callbackFuncs.addUserCallbackFunc)
+ callbackFuncs.addUserCallbackFunc(room, user);
+ }
+
+ void Rooms::addNodeCallbackFunc(const odhtdb::DatabaseAddNodeRequest &request)
+ {
+ auto roomIt = roomById.find(*request.nodeHash);
+ if(roomIt == roomById.end())
+ {
+ odhtdb::Log::error("Attempting to add data to node %s but the node has not been created", request.nodeHash->toString().c_str());
+ assert(false);
+ return;
+ }
+ auto room = roomIt->second;
+
+ auto user = room->getUserByPublicKey(*request.creatorPublicKey);
+ if(!user)
+ {
+ odhtdb::Log::error("Attempting to add data to node %s but the the user %s doesn't exist in the node %s", request.creatorPublicKey->toString().c_str(), request.nodeHash->toString().c_str());
+ assert(false);
+ return;
+ }
+
+ if(request.decryptedData.size == 0)
+ return;
+
+ uint32_t timestampSeconds = ntp::NtpTimestamp::fromCombined(request.timestamp).seconds;
+
+ RoomDataType roomDataType = (RoomDataType)static_cast<const char*>(request.decryptedData.data)[0];
+ try
+ {
+ switch(roomDataType)
+ {
+ case RoomDataType::ADD_MESSAGE:
+ {
+ RoomMessage message;
+ message.id = *request.requestHash;
+ message.creator = user;
+ message.timestampSeconds = timestampSeconds;
+ message.text = std::string((const char*)request.decryptedData.data + 1, request.decryptedData.size - 1);
+
+ RoomAddMessageRequest roomRequest;
+ roomRequest.room = room;
+ roomRequest.loadedFromCache = request.loadedFromCache;
+ roomRequest.message = std::move(message);
+ if(callbackFuncs.addMessageCallbackFunc)
+ callbackFuncs.addMessageCallbackFunc(roomRequest);
+
+ room->messages.push_back(std::move(roomRequest.message));
+ break;
+ }
+ case RoomDataType::NICKNAME_CHANGE:
+ {
+ sibs::SafeDeserializer deserializer((const u8*)request.decryptedData.data + 1, request.decryptedData.size - 1);
+ u8 nameLength = deserializer.extract<u8>();
+ if(nameLength > 0)
+ {
+ std::string nickname;
+ nickname.resize(nameLength);
+ deserializer.extract((u8*)&nickname[0], nameLength);
+
+ UserChangeNicknameRequest roomRequest;
+ roomRequest.room = room;
+ roomRequest.user = user;
+ roomRequest.timestampSeconds = timestampSeconds;
+ roomRequest.loadedFromCache = request.loadedFromCache;
+ roomRequest.newNickname = nickname;
+ if(callbackFuncs.userChangeNicknameCallbackFunc)
+ callbackFuncs.userChangeNicknameCallbackFunc(roomRequest);
+ user->nickname = std::move(nickname);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ catch(std::exception &e)
+ {
+ odhtdb::Log::warn("Failed to process add node request, reason: %s\n", e.what());
+ }
+ }
+
+ void Rooms::addUserCallbackFunc(const odhtdb::DatabaseAddUserRequest &request)
+ {
+ auto roomIt = roomById.find(*request.nodeHash);
+ if(roomIt == roomById.end())
+ {
+ odhtdb::Log::error("User %s was added to node %s but the node has not been created", request.userToAddPublicKey->toString().c_str(), request.nodeHash->toString().c_str());
+ assert(false);
+ return;
+ }
+
+ auto user = roomIt->second->addUser(*request.userToAddPublicKey, request.groupToAddUserTo);
+ auto localUserIt = roomLocalUser.find(*request.nodeHash);
+ if(localUserIt != roomLocalUser.end())
+ {
+ roomIt->second->localUser = user;
+ roomIt->second->publicKeyToKeyPairMap[user->publicKey] = localUserIt->second;
+ }
+ if(callbackFuncs.addUserCallbackFunc)
+ callbackFuncs.addUserCallbackFunc(roomIt->second, user);
+ }
+
+ void Rooms::connect(const char *address, u16 port, RoomCallbackFuncs callbackFuncs)
+ {
+ assert(callbackFuncs.connectCallbackFunc);
+ std::thread([](const char *address, u16 port, RoomCallbackFuncs callbackFuncs)
+ {
+ try
+ {
+ callbackFuncs.connectCallbackFunc(std::shared_ptr<Rooms>(new Rooms(address, port, callbackFuncs)), nullptr);
+ }
+ catch(std::exception &e)
+ {
+ callbackFuncs.connectCallbackFunc(nullptr, e.what());
+ }
+ }, address, port, callbackFuncs).detach();
+ }
+
+ void Rooms::loginUser(const std::string &username, const std::string &password)
+ {
+ if(loggedIn)
+ throw std::runtime_error(std::string("You are already logged in as ") + username);
+
+ auto storedNodes = database->getStoredNodeUserInfoDecrypted(username, password);
+ loggedIn = true;
+ for(auto &nodeInfo : storedNodes)
+ {
+ roomLocalUser[nodeInfo.first] = nodeInfo.second.userKeyPair;
+ roomEncryptionKey[nodeInfo.first] = nodeInfo.second.nodeEncryptionKey;
+ database->loadNode(nodeInfo.first);
+ }
+ }
+
+ void Rooms::registerUser(const std::string &username, const std::string &password)
+ {
+ if(loggedIn)
+ throw std::runtime_error(std::string("You are already logged in as ") + username);
+
+ database->storeUserWithoutNodes(username, password);
+ loggedIn = true;
+ }
+} \ No newline at end of file