aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------depends/odhtdb0
-rw-r--r--include/dchat/FileUtil.hpp2
-rw-r--r--include/dchat/Group.hpp15
-rw-r--r--include/dchat/Room.hpp40
-rw-r--r--include/dchat/User.hpp4
-rw-r--r--project.conf2
-rw-r--r--src/Cache.cpp25
-rw-r--r--src/Room.cpp264
8 files changed, 316 insertions, 36 deletions
diff --git a/depends/odhtdb b/depends/odhtdb
-Subproject 254fa05345462185c1c633a6c4b87a83cab68d2
+Subproject 6d520dafec98bb4ebb7e40bd5110a5cbe5ebb47
diff --git a/include/dchat/FileUtil.hpp b/include/dchat/FileUtil.hpp
index 097b607..6a7ff00 100644
--- a/include/dchat/FileUtil.hpp
+++ b/include/dchat/FileUtil.hpp
@@ -13,7 +13,7 @@ namespace dchat
};
// Throws FileException on error.
- // Returned value is allocated with malloc and should be free'd by caller.
+ // Returned value is allocated with `new[]` and should be `delete`[]d by caller.
StringView getFileContent(const boost::filesystem::path &filepath);
// Throws FileException on error
diff --git a/include/dchat/Group.hpp b/include/dchat/Group.hpp
new file mode 100644
index 0000000..8b47923
--- /dev/null
+++ b/include/dchat/Group.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "types.hpp"
+#include <odhtdb/Group.hpp>
+#include <string>
+
+namespace dchat
+{
+ class Group
+ {
+ public:
+ u8 id[odhtdb::GROUP_ID_LENGTH];
+ std::string name;
+ };
+} \ No newline at end of file
diff --git a/include/dchat/Room.hpp b/include/dchat/Room.hpp
index 5dd0e3f..a39f2f0 100644
--- a/include/dchat/Room.hpp
+++ b/include/dchat/Room.hpp
@@ -28,9 +28,17 @@ namespace dchat
DISABLE_COPY(Room)
public:
Room(Rooms *rooms, std::shared_ptr<odhtdb::Hash> id);
- std::shared_ptr<User> addUser(const odhtdb::Signature::PublicKey &userPublicKey, const odhtdb::DataView groupId);
+ // Throws exception on failure if we are not allowed to add the user to the group
+ void addUser(const odhtdb::Signature::PublicKey &userPublicKey, std::shared_ptr<Group> group);
+ // Returns null if the user already exists in the room
+ std::shared_ptr<User> addUserLocally(const odhtdb::Signature::PublicKey &userPublicKey, std::shared_ptr<Group> group);
+ // Returns null if the group already exists in the room
+ std::shared_ptr<Group> addGroupLocally(const odhtdb::DataView groupId);
// Returns null if user doesn't exist in room
std::shared_ptr<User> getUserByPublicKey(const odhtdb::Signature::PublicKey &userPublicKey);
+ // Returns null if group doesn't exist in room
+ std::shared_ptr<Group> getGroupById(const odhtdb::DataView groupId);
+ void setLocalUser(std::shared_ptr<User> user, std::shared_ptr<odhtdb::Signature::KeyPair> keyPair);
void publishMessage(const std::string &msg);
Rooms *rooms;
@@ -38,10 +46,12 @@ namespace dchat
std::shared_ptr<odhtdb::OwnedByteArray> encryptionKey;
std::string name;
odhtdb::Signature::MapPublicKey<std::shared_ptr<User>> userByPublicKey;
+ std::vector<std::shared_ptr<Group>> groups;
std::vector<RoomMessage> messages;
std::shared_ptr<User> localUser;
// Used for local users
odhtdb::Signature::MapPublicKey<std::shared_ptr<odhtdb::Signature::KeyPair>> publicKeyToKeyPairMap;
+ std::string inviteKey;
void *userdata;
};
@@ -70,13 +80,31 @@ namespace dchat
std::string newName;
};
+ struct InviteUserRequest
+ {
+ std::shared_ptr<Room> room;
+ odhtdb::Signature::PublicKey userPublicKey;
+ std::string message;
+ };
+
+ struct RoomAddUserRequest
+ {
+ std::shared_ptr<Room> room;
+ std::shared_ptr<User> user;
+ std::shared_ptr<User> addedByUser;
+ uint32_t timestampSeconds;
+ bool loadedFromCache;
+ bool isLocalUser;
+ };
+
// 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 RoomAddUserCallbackFunc = std::function<void(const RoomAddUserRequest &request)>;
using RoomAddMessageCallbackFunc = std::function<void(const RoomAddMessageRequest &request)>;
using UserChangeNicknameCallbackFunc = std::function<void(const UserChangeNicknameRequest &request)>;
using ChangeRoomNameCallbackFunc = std::function<void(const RoomChangeNameRequest &request)>;
+ using ReceiveInviteUserCallbackFunc = std::function<void(const InviteUserRequest &request)>;
struct RoomCallbackFuncs
{
ConnectBoostrapNodeCallbackFunc connectCallbackFunc;
@@ -85,10 +113,12 @@ namespace dchat
RoomAddMessageCallbackFunc addMessageCallbackFunc;
UserChangeNicknameCallbackFunc userChangeNicknameCallbackFunc;
ChangeRoomNameCallbackFunc changeRoomNameCallbackFunc;
+ ReceiveInviteUserCallbackFunc receiveInviteUserCallbackFunc;
};
class Rooms
{
+ friend Room;
DISABLE_COPY(Rooms)
public:
// @callbackFuncs.connectCallbackFunc can't be null
@@ -98,7 +128,8 @@ namespace dchat
// Throws on failure
void registerUser(const std::string &username, const std::string &password);
// Throws on failure
- void createRoom(const std::string &name);
+ std::shared_ptr<Room> createRoom(const std::string &name);
+ void requestJoinRoom(const std::string &inviteKey, const std::string &message);
std::shared_ptr<odhtdb::Database> database;
private:
@@ -116,5 +147,8 @@ namespace dchat
std::string currentUsername;
std::string currentUserPassword;
std::recursive_mutex roomModifyMutex;
+
+ odhtdb::MapHash<std::shared_ptr<odhtdb::Signature::KeyPair>> waitingToJoinRoom;
+ std::recursive_mutex waitingToJoinRoomMutex;
};
} \ No newline at end of file
diff --git a/include/dchat/User.hpp b/include/dchat/User.hpp
index 4021c78..245197f 100644
--- a/include/dchat/User.hpp
+++ b/include/dchat/User.hpp
@@ -1,7 +1,10 @@
#pragma once
+#include "Group.hpp"
#include <string>
+#include <vector>
#include <odhtdb/Signature.hpp>
+#include <memory>
namespace dchat
{
@@ -11,6 +14,7 @@ namespace dchat
User(const odhtdb::Signature::PublicKey &_publicKey) : publicKey(_publicKey), userdata(nullptr) {}
const odhtdb::Signature::PublicKey publicKey;
std::string nickname;
+ std::vector<std::shared_ptr<Group>> groups;
void *userdata;
};
} \ No newline at end of file
diff --git a/project.conf b/project.conf
index a705bed..1e684e4 100644
--- a/project.conf
+++ b/project.conf
@@ -8,7 +8,7 @@ platforms = ["any"]
expose_include_dirs = ["include"]
[dependencies]
-tiny-process = "2"
+tiny-process = "2.1.0"
boost-filesystem = "1.66"
libpreview = ">=0.2.0"
libgd = "2"
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 8657371..cf9aa8a 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -12,8 +12,18 @@
#if OS_FAMILY == OS_FAMILY_POSIX
#include <pwd.h>
+#define toNativeString(str) str
#else
#include <string>
+#include <userenv.h>
+#include <locale>
+#include <codecvt>
+
+static std::wstring toNativeString(const std::string &str)
+{
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
+ return converter.from_bytes(str);
+}
#endif
using namespace std;
@@ -41,12 +51,12 @@ namespace dchat
homeDir.resize(homeDirLen);
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
- return Result<FileString>::Err("Failed to open process token");
+ throw std::runtime_error("Failed to open process token");
if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen))
{
CloseHandle(hToken);
- return Result<FileString>::Err("Failed to get home directory");
+ throw std::runtime_error("Failed to get home directory");
}
CloseHandle(hToken);
@@ -143,7 +153,7 @@ namespace dchat
return false;
}
- bool success = (gdImageFile(newImgPtr, filepath.c_str()) == 0);
+ bool success = (gdImageFile(newImgPtr, filepath.string().c_str()) == 0);
gdImageDestroy(imgPtr);
gdImageDestroy(newImgPtr);
return success;
@@ -427,11 +437,12 @@ namespace dchat
string downloadLimitBytesStr = to_string(downloadLimitBytes);
- Process::string_type cmd = "curl -L --silent -o '";
- cmd += filepath.native();
- cmd += "' --max-filesize " + downloadLimitBytesStr + " --range 0-" + downloadLimitBytesStr + " --url '" + url + "'";
+ std::string cmdUtf8 = "curl -L --silent -o '";
+ cmdUtf8 += filepath.string();
+ cmdUtf8 += "' --max-filesize " + downloadLimitBytesStr + " --range 0-" + downloadLimitBytesStr + " --url '" + url + "'";
+ Process::string_type cmd = toNativeString(cmdUtf8);
// TODO: Use this instead of curl on windows: certutil.exe -urlcache -split -f "https://url/to/file" path/and/name/to/save/as/file
- Process *process = new Process(cmd, "", nullptr, nullptr, false);
+ Process *process = new Process(cmd, toNativeString(""), nullptr, nullptr, false);
ImageDownloadInfo imageDownloadInfo { process, url };
imageDownloadProcessesQueue.emplace_back(imageDownloadInfo);
return result;
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);
}
}