aboutsummaryrefslogtreecommitdiff
path: root/src/Room.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Room.cpp')
-rw-r--r--src/Room.cpp264
1 files changed, 240 insertions, 24 deletions
diff --git a/src/Room.cpp b/src/Room.cpp
index 95e5818..7a51760 100644
--- a/src/Room.cpp
+++ b/src/Room.cpp
@@ -1,9 +1,13 @@
#include "../include/dchat/Room.hpp"
#include "../include/dchat/Cache.hpp"
+#include "../include/dchat/FileUtil.hpp"
#include "../include/dchat/RoomDataType.hpp"
#include <odhtdb/Database.hpp>
#include <odhtdb/Log.hpp>
+#include <odhtdb/bin2hex.hpp>
+#include <odhtdb/hex2bin.hpp>
#include <sibs/SafeSerializer.hpp>
+#include <sibs/SafeDeserializer.hpp>
#include <thread>
// TODO: Remove error checks when odhtdb has been improved to take care of such errors
@@ -14,13 +18,23 @@ namespace dchat
id(_id),
userdata(nullptr)
{
+
+ }
+ void Room::addUser(const odhtdb::Signature::PublicKey &userPublicKey, std::shared_ptr<Group> group)
+ {
+ assert(localUser);
+ assert(publicKeyToKeyPairMap.find(localUser->publicKey) != publicKeyToKeyPairMap.end());
+ assert(encryptionKey);
+ auto keyPair = publicKeyToKeyPairMap[localUser->publicKey];
+ odhtdb::DatabaseNode roomNode(encryptionKey, id);
+ rooms->database->addUser(roomNode, *keyPair, userPublicKey, odhtdb::DataView(group->id, odhtdb::GROUP_ID_LENGTH));
}
- std::shared_ptr<User> Room::addUser(const odhtdb::Signature::PublicKey &userPublicKey, const odhtdb::DataView groupId)
+ std::shared_ptr<User> Room::addUserLocally(const odhtdb::Signature::PublicKey &userPublicKey, std::shared_ptr<Group> group)
{
- auto it = userByPublicKey.find(userPublicKey);
- if(it != userByPublicKey.end())
+ auto existingUser = getUserByPublicKey(userPublicKey);
+ if(existingUser)
{
odhtdb::Log::error("The user %s already exists in the room %s", userPublicKey.toString().c_str(), id->toString().c_str());
assert(false);
@@ -28,10 +42,28 @@ namespace dchat
}
auto user = std::make_shared<User>(userPublicKey);
+ user->groups.push_back(group);
userByPublicKey[userPublicKey] = user;
return user;
}
+ std::shared_ptr<Group> Room::addGroupLocally(const odhtdb::DataView groupId)
+ {
+ auto existingGroup = getGroupById(groupId);
+ if(existingGroup)
+ {
+ odhtdb::Log::error("The group %s already exists in the room %s", groupId.toString().c_str(), id->toString().c_str());
+ assert(false);
+ return nullptr;
+ }
+
+ auto group = std::make_shared<Group>();
+ assert(groupId.size == odhtdb::GROUP_ID_LENGTH);
+ memcpy(group->id, groupId.data, groupId.size);
+ groups.push_back(group);
+ return group;
+ }
+
std::shared_ptr<User> Room::getUserByPublicKey(const odhtdb::Signature::PublicKey &userPublicKey)
{
auto userIt = userByPublicKey.find(userPublicKey);
@@ -40,6 +72,88 @@ namespace dchat
return nullptr;
}
+ std::shared_ptr<Group> Room::getGroupById(const odhtdb::DataView groupId)
+ {
+ for(std::shared_ptr<Group> &group : groups)
+ {
+ if(odhtdb::DataView(group->id, odhtdb::GROUP_ID_LENGTH) == groupId)
+ return group;
+ }
+ return nullptr;
+ }
+
+ static std::string getInviteKeyChecksum(const std::string &inviteKey)
+ {
+ u8 checksum = 0x3d;
+ for(char c : inviteKey)
+ {
+ checksum ^= c;
+ }
+ return odhtdb::bin2hex((const char*)&checksum, 1);
+ }
+
+ void Room::setLocalUser(std::shared_ptr<User> user, std::shared_ptr<odhtdb::Signature::KeyPair> keyPair)
+ {
+ if(localUser) return;
+ assert(user->publicKey == keyPair->getPublicKey());
+ assert(encryptionKey);
+ localUser = user;
+ publicKeyToKeyPairMap[user->publicKey] = keyPair;
+ odhtdb::Log::debug("Local user set to %s for room %s", user->publicKey.toString().c_str(), id->toString().c_str());
+
+ // Room id + room encryption key + invite key checksum (to verify invite key is not types incorrectly)
+ inviteKey = id->toString() + odhtdb::bin2hex((const char*)encryptionKey->data, encryptionKey->size);
+ inviteKey += getInviteKeyChecksum(inviteKey);
+ odhtdb::InfoHash inviteInfoHash = odhtdb::InfoHash::generateHash((const u8*)inviteKey.data(), inviteKey.size());
+ rooms->database->receiveCustomMessage(inviteInfoHash, [this](const void *data, usize size)
+ {
+ sibs::SafeSerializer serializer;
+ try
+ {
+ odhtdb::Log::debug("Received request to invite user to room");
+ sibs::SafeDeserializer deserializer((const u8*)data, size);
+ u8 nonce[odhtdb::ENCRYPTION_NONCE_BYTE_SIZE];
+ deserializer.extract(nonce, sizeof(nonce));
+ odhtdb::Decryption decryptedData(
+ { (void*)deserializer.getBuffer(), deserializer.getSize() },
+ { nonce, sizeof(nonce) },
+ { (void*)encryptionKey->data, encryptionKey->size });
+
+ sibs::SafeDeserializer deserializerMsg((const u8*)decryptedData.getDecryptedText().data, decryptedData.getDecryptedText().size);
+ odhtdb::Signature::PublicKey userPublicKey;
+ deserializerMsg.extract((u8*)userPublicKey.getData(), userPublicKey.getSize());
+ auto user = getUserByPublicKey(userPublicKey);
+ if(user)
+ {
+ odhtdb::Log::warn("User %s wanted us to add them to the room %s but they are already a member of it", userPublicKey.toString().c_str(), id->toString().c_str());
+ // TODO: Return error message to user, so they can get a nice error message (or deny message)
+ return serializer;
+ }
+
+ std::string userMsg;
+ if(!deserializerMsg.empty())
+ {
+ userMsg.resize(deserializerMsg.getSize());
+ deserializerMsg.extract((u8*)&userMsg[0], deserializerMsg.getSize());
+ }
+
+ if(rooms->callbackFuncs.receiveInviteUserCallbackFunc)
+ {
+ InviteUserRequest request;
+ request.room = std::shared_ptr<Room>(this);
+ request.userPublicKey = std::move(userPublicKey);
+ request.message = std::move(userMsg);
+ rooms->callbackFuncs.receiveInviteUserCallbackFunc(request);
+ }
+ }
+ catch(std::exception &e)
+ {
+ odhtdb::Log::warn("Failed to deserialize request to invite user to room");
+ }
+ return serializer;
+ });
+ }
+
void Room::publishMessage(const std::string &msg)
{
assert(localUser);
@@ -48,7 +162,7 @@ namespace dchat
sibs::SafeSerializer serializer;
serializer.add(RoomDataType::ADD_MESSAGE);
serializer.add((const u8*)msg.data(), msg.size());
- auto &keyPair = publicKeyToKeyPairMap[localUser->publicKey];
+ auto keyPair = publicKeyToKeyPairMap[localUser->publicKey];
odhtdb::DatabaseNode roomNode(encryptionKey, id);
rooms->database->addData(roomNode, *keyPair, { serializer.getBuffer().data(), serializer.getSize() });
}
@@ -71,7 +185,6 @@ namespace dchat
if(roomIt != roomById.end())
{
odhtdb::Log::error("Room %s has already been created once", request.nodeHash->toString().c_str());
- assert(false);
return;
}
@@ -83,16 +196,28 @@ namespace dchat
roomById[*request.nodeHash] = room;
if(callbackFuncs.createRoomCallbackFunc)
callbackFuncs.createRoomCallbackFunc(room);
-
- auto user = room->addUser(*request.creatorPublicKey, request.groupId);
+
+ auto group = room->addGroupLocally(request.groupId);
+ auto user = room->addUserLocally(*request.creatorPublicKey, group);
+ bool isLocalUser = false;
auto localUserIt = roomLocalUser.find(*request.nodeHash);
if(localUserIt != roomLocalUser.end())
{
- room->localUser = user;
- room->publicKeyToKeyPairMap[user->publicKey] = localUserIt->second;
+ room->setLocalUser(user, localUserIt->second);
+ isLocalUser = true;
}
+
if(callbackFuncs.addUserCallbackFunc)
- callbackFuncs.addUserCallbackFunc(room, user);
+ {
+ RoomAddUserRequest callbackRequest;
+ callbackRequest.room = room;
+ callbackRequest.user = user;
+ callbackRequest.addedByUser = user;
+ callbackRequest.timestampSeconds = ntp::NtpTimestamp::fromCombined(request.timestamp).seconds;
+ callbackRequest.loadedFromCache = request.loadedFromCache;
+ callbackRequest.isLocalUser = isLocalUser;
+ callbackFuncs.addUserCallbackFunc(callbackRequest);
+ }
}
void Rooms::addNodeCallbackFunc(const odhtdb::DatabaseAddNodeRequest &request)
@@ -102,7 +227,6 @@ namespace dchat
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;
@@ -111,7 +235,6 @@ namespace dchat
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;
}
@@ -207,20 +330,61 @@ namespace dchat
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);
+ 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());
return;
}
- auto user = roomIt->second->addUser(*request.userToAddPublicKey, request.groupToAddUserTo);
- auto localUserIt = roomLocalUser.find(*request.nodeHash);
- if(localUserIt != roomLocalUser.end())
+ auto room = roomIt->second;
+ auto group = room->getGroupById(request.groupToAddUserTo);
+ if(!group)
+ {
+ odhtdb::Log::error("User %s was added to node %s in a non-existing group %s",
+ request.userToAddPublicKey->toString().c_str(),
+ request.nodeHash->toString().c_str(),
+ request.groupToAddUserTo.toString().c_str());
+ return;
+ }
+
+ auto addedByUser = room->getUserByPublicKey(*request.creatorPublicKey);
+ if(!addedByUser)
{
- roomIt->second->localUser = user;
- roomIt->second->publicKeyToKeyPairMap[user->publicKey] = localUserIt->second;
+ odhtdb::Log::error("User was added to room by admin that doesn't exist in room.... (bug?)");
+ return;
}
+
+ auto user = room->addUserLocally(*request.userToAddPublicKey, group);
+ std::lock_guard<std::recursive_mutex> waitingToJoinRoomLock(waitingToJoinRoomMutex);
+ bool isLocalUser = false;
+ auto waitingToJoinRoomIt = waitingToJoinRoom.find(*request.nodeHash);
+ if(waitingToJoinRoomIt != waitingToJoinRoom.end())
+ {
+ room->setLocalUser(user, waitingToJoinRoomIt->second);
+ waitingToJoinRoom.erase(waitingToJoinRoomIt);
+ isLocalUser = true;
+ }
+ else
+ {
+ auto localUserIt = roomLocalUser.find(*request.nodeHash);
+ if(localUserIt != roomLocalUser.end())
+ {
+ room->setLocalUser(user, localUserIt->second);
+ isLocalUser = true;
+ }
+ }
+
if(callbackFuncs.addUserCallbackFunc)
- callbackFuncs.addUserCallbackFunc(roomIt->second, user);
+ {
+ RoomAddUserRequest callbackRequest;
+ callbackRequest.room = room;
+ callbackRequest.user = user;
+ callbackRequest.addedByUser = addedByUser;
+ callbackRequest.timestampSeconds = ntp::NtpTimestamp::fromCombined(request.timestamp).seconds;
+ callbackRequest.loadedFromCache = request.loadedFromCache;
+ callbackRequest.isLocalUser = isLocalUser;
+ callbackFuncs.addUserCallbackFunc(callbackRequest);
+ }
}
void Rooms::connect(const char *address, u16 port, RoomCallbackFuncs callbackFuncs)
@@ -273,24 +437,24 @@ namespace dchat
currentUserPassword = password;
}
- void Rooms::createRoom(const std::string &name)
+ std::shared_ptr<Room> Rooms::createRoom(const std::string &name)
{
std::lock_guard<std::recursive_mutex> lock(roomModifyMutex);
if(!loggedIn)
- throw std::runtime_error("You need to be logged in to create a room ");
+ throw std::runtime_error("You need to be logged in to create a room");
auto newNode = database->create();
roomLocalUser[*newNode->getRequestHash()] = newNode->getNodeAdminKeyPair();
roomEncryptionKey[*newNode->getRequestHash()] = newNode->getNodeEncryptionKey();
auto roomIt = roomById.find(*newNode->getRequestHash());
+ assert(roomIt != roomById.end());
if(roomIt != roomById.end())
{
roomIt->second->encryptionKey = newNode->getNodeEncryptionKey();
auto user = roomIt->second->getUserByPublicKey(newNode->getNodeAdminKeyPair()->getPublicKey());
if(user)
{
- roomIt->second->localUser = user;
- roomIt->second->publicKeyToKeyPairMap[user->publicKey] = newNode->getNodeAdminKeyPair();
+ roomIt->second->setLocalUser(user, newNode->getNodeAdminKeyPair());
}
}
@@ -301,5 +465,57 @@ namespace dchat
serializer.add((u16)name.size());
serializer.add(name.data(), name.size());
database->addData(nodeInfo, *newNode->getNodeAdminKeyPair(), { serializer.getBuffer().data(), serializer.getSize() });
+ return roomIt->second;
+ }
+
+ void Rooms::requestJoinRoom(const std::string &inviteKey, const std::string &message)
+ {
+ if(!loggedIn)
+ throw std::runtime_error("You need to be logged in to join a room");
+
+ assert(inviteKey.size() == 130);
+ std::lock_guard<std::recursive_mutex> waitingToJoinRoomLock(waitingToJoinRoomMutex);
+ std::string roomIdHex = inviteKey.substr(0, 64);
+ std::string roomIdBin = odhtdb::hex2bin(roomIdHex.c_str(), roomIdHex.size());
+ auto roomId = std::make_shared<odhtdb::Hash>();
+ memcpy(roomId->getData(), roomIdBin.data(), roomIdBin.size());
+ if(waitingToJoinRoom.find(*roomId) != waitingToJoinRoom.end())
+ {
+ fprintf(stderr, "You have already requested to join room %s\n", roomIdHex.c_str());
+ return;
+ }
+ if(roomById.find(*roomId) != roomById.end())
+ {
+ fprintf(stderr, "You are already a member of the room %s\n", roomIdHex.c_str());
+ return;
+ }
+ std::string roomEncryptionKeyHex = inviteKey.substr(64, 64);
+ std::string roomEncryptionKeyStr = odhtdb::hex2bin(roomEncryptionKeyHex.c_str(), roomEncryptionKeyHex.size());
+ auto roomEncryptionKey = std::make_shared<odhtdb::OwnedByteArray>(new u8[odhtdb::ENCRYPTION_KEY_BYTE_SIZE], odhtdb::ENCRYPTION_KEY_BYTE_SIZE);
+ memcpy(roomEncryptionKey->data, roomEncryptionKeyStr.data(), roomEncryptionKeyStr.size());
+ std::string inviteKeyChecksum = inviteKey.substr(128, 2);
+ if(inviteKeyChecksum != getInviteKeyChecksum(inviteKey.substr(0, 128)))
+ {
+ fprintf(stderr, "Invalid invite key, checksum is incorrect. Did you type the invite key incorrectly?\n");
+ return;
+ }
+
+ auto ourNewUser = std::make_shared<odhtdb::Signature::KeyPair>();
+ sibs::SafeSerializer serializer;
+ serializer.add(ourNewUser->getPublicKey().getData(), ourNewUser->getPublicKey().getSize());
+ serializer.add(message.data(), message.size());
+ odhtdb::DataView encryptionData { serializer.getBuffer().data(), serializer.getSize() };
+ odhtdb::DataView encryptionKey { (void*)roomEncryptionKey->data, roomEncryptionKey->size };
+ odhtdb::Encryption encryption(encryptionData, encryptionKey);
+
+ sibs::SafeSerializer serializerFinish;
+ serializerFinish.add((const u8*)encryption.getNonce().data, encryption.getNonce().size);
+ serializerFinish.add((const u8*)encryption.getCipherText().data, encryption.getCipherText().size);
+ odhtdb::InfoHash inviteInfoHash = odhtdb::InfoHash::generateHash((const u8*)inviteKey.data(), inviteKey.size());
+ database->sendCustomMessage(inviteInfoHash, serializerFinish.getBuffer().data(), serializerFinish.getSize());
+ waitingToJoinRoom[*roomId] = ourNewUser;
+ odhtdb::DatabaseNode roomNode(roomEncryptionKey, roomId);
+ database->seed(roomNode);
+ database->storeNodeInfoForUserEncrypted(roomNode, currentUsername, currentUserPassword, *ourNewUser);
}
}