aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Room.cpp226
1 files changed, 226 insertions, 0 deletions
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