From 88916c50b83bfad1ec1c5dd5f4e4f5d345851cb0 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 1 Nov 2018 07:16:38 +0100 Subject: Move code from dchat_gtk to generic place --- src/Room.cpp | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/Room.cpp (limited to 'src') 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 +#include +#include +#include + +// TODO: Remove error checks when odhtdb has been improved to take care of such errors +namespace dchat +{ + Room::Room(std::shared_ptr _database, std::shared_ptr _id, std::shared_ptr _encryptionKey) : + database(_database), + id(_id), + encryptionKey(_encryptionKey), + userdata(nullptr) + { + + } + + std::shared_ptr 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(userPublicKey); + userByPublicKey[userPublicKey] = user; + return user; + } + + std::shared_ptr 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(*request.nodeHash); + auto room = std::make_shared(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(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(); + 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(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 -- cgit v1.2.3