#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; } }