From 0cae593b94c4fdfe100516c7f08f75e40bc1e089 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 8 Apr 2019 21:02:51 +0200 Subject: Move room setName to its own function, escape commands --- include/dchat/Process.hpp | 8 ++++++ include/dchat/Room.hpp | 18 ++++++++----- src/Cache.cpp | 5 ++-- src/Process.cpp | 33 +++++++++++++++++++++++ src/Room.cpp | 68 ++++++++++++++++++++++++++++++++++------------- 5 files changed, 106 insertions(+), 26 deletions(-) create mode 100644 include/dchat/Process.hpp create mode 100644 src/Process.cpp diff --git a/include/dchat/Process.hpp b/include/dchat/Process.hpp new file mode 100644 index 0000000..18aa2d9 --- /dev/null +++ b/include/dchat/Process.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace dchat +{ + std::string escapeCommand(const std::string &cmd); +} \ No newline at end of file diff --git a/include/dchat/Room.hpp b/include/dchat/Room.hpp index 56b60e5..6681cdc 100644 --- a/include/dchat/Room.hpp +++ b/include/dchat/Room.hpp @@ -31,16 +31,16 @@ namespace dchat Room(Rooms *rooms, std::shared_ptr id); // 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); - // Returns null if the user already exists in the room - std::shared_ptr addUserLocally(const odhtdb::Signature::PublicKey &userPublicKey, std::shared_ptr group); - // Returns null if the group already exists in the room - std::shared_ptr addGroupLocally(const odhtdb::DataView groupId); + // Returns null if user doesn't exist in room std::shared_ptr getUserByPublicKey(const odhtdb::Signature::PublicKey &userPublicKey); + // Returns null if group doesn't exist in room std::shared_ptr getGroupById(const odhtdb::DataView groupId); + void setAvatarUrl(const std::string &url); - void setNickname(const std::string &nickname); + void setUserNickname(const std::string &nickname); + bool setName(const std::string &name); void publishMessage(const std::string &msg); const odhtdb::Signature::MapPublicKey>& getUsers() const { return userByPublicKey; } @@ -57,6 +57,10 @@ namespace dchat // TODO: Move to private when we have proper support for groups std::vector> groups; private: + // Returns null if the user already exists in the room + std::shared_ptr addUserLocally(const odhtdb::Signature::PublicKey &userPublicKey, std::shared_ptr group); + // Returns null if the group already exists in the room + std::shared_ptr addGroupLocally(const odhtdb::DataView groupId); void setLocalUser(std::shared_ptr user, std::shared_ptr keyPair); private: odhtdb::Signature::MapPublicKey> userByPublicKey; @@ -116,6 +120,7 @@ namespace dchat uint32_t timestampSeconds; bool loadedFromCache; bool isLocalUser; + bool waitedToJoin; }; // if connection failed then @rooms is null and errMsg contains the error @@ -153,7 +158,8 @@ namespace dchat void registerUser(const std::string &username, const std::string &password); // Throws on failure std::shared_ptr createRoom(const std::string &name); - void requestJoinRoom(const std::string &inviteKey, const std::string &message); + // Throws on failure, returns true on success, returns false on expected failures (such as already being a member of the room) + bool requestJoinRoom(const std::string &inviteKey, const std::string &message); std::shared_ptr database; diff --git a/src/Cache.cpp b/src/Cache.cpp index 82f31b9..bb87857 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -3,6 +3,7 @@ #include "../include/dchat/FileUtil.hpp" #include "../include/dchat/Gif.hpp" #include "../include/dchat/Storage.hpp" +#include "../include/dchat/Process.hpp" #include #include #include @@ -351,8 +352,8 @@ namespace dchat string downloadLimitBytesStr = to_string(downloadLimitBytes); std::string cmdUtf8 = "curl -L --silent -o '"; - cmdUtf8 += filepath.string(); - cmdUtf8 += "' --max-filesize " + downloadLimitBytesStr + " --range 0-" + downloadLimitBytesStr + " --url '" + url + "'"; + cmdUtf8 += escapeCommand(filepath.string()); + cmdUtf8 += "' --max-filesize " + downloadLimitBytesStr + " --range 0-" + downloadLimitBytesStr + " --url '" + escapeCommand(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, toNativeString(""), nullptr, nullptr, false); diff --git a/src/Process.cpp b/src/Process.cpp new file mode 100644 index 0000000..d82da42 --- /dev/null +++ b/src/Process.cpp @@ -0,0 +1,33 @@ +#include "../include/dchat/Process.hpp" + +namespace dchat +{ + std::string escapeCommand(const std::string &cmd) + { + std::string result; + result.reserve(cmd.size()); + bool escape = false; + + for(char c : cmd) + { + if(c == '\\') + escape = !escape; + else + { + if(escape) + result += "\\"; + + if(c == '"') + result += "\\\""; // \" + else if(c == '\'') + result += "\\'"; // \' + else + result += c; + + escape = false; + } + } + + return result; + } +} \ No newline at end of file diff --git a/src/Room.cpp b/src/Room.cpp index b558f87..bc87c05 100644 --- a/src/Room.cpp +++ b/src/Room.cpp @@ -105,7 +105,9 @@ namespace dchat 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()); - // TODO: Use database instead of custom message. Then both parties dont have to be online to add user to room + // TODO: Use database instead of custom message. Then both parties dont have to be online to add user to room. + // TODO: Only listen for invite request if we have the right to add users to the room. + // TODO: Make this optional (should be possible to turn off from gui/settings) rooms->database->receiveCustomMessage(inviteInfoHash, [this](const void *data, usize size) { sibs::SafeSerializer serializer; @@ -160,37 +162,62 @@ namespace dchat assert(localUser); assert(publicKeyToKeyPairMap.find(localUser->publicKey) != publicKeyToKeyPairMap.end()); assert(encryptionKey); + sibs::SafeSerializer serializer; serializer.add(RoomDataType::CHANGE_AVATAR); serializer.add((u16)url.size()); - serializer.add((const u8*)url.data(), url.size()); + serializer.add(url.data(), url.size()); auto keyPair = publicKeyToKeyPairMap[localUser->publicKey]; odhtdb::DatabaseNode roomNode(encryptionKey, id); rooms->database->addData(roomNode, *keyPair, { serializer.getBuffer().data(), serializer.getSize() }); } - void Room::setNickname(const std::string &nickname) + void Room::setUserNickname(const std::string &nickname) { assert(localUser); assert(publicKeyToKeyPairMap.find(localUser->publicKey) != publicKeyToKeyPairMap.end()); assert(encryptionKey); + sibs::SafeSerializer serializer; serializer.add(RoomDataType::NICKNAME_CHANGE); serializer.add((u8)nickname.size()); - serializer.add((const u8*)nickname.data(), nickname.size()); + serializer.add(nickname.data(), nickname.size()); auto keyPair = publicKeyToKeyPairMap[localUser->publicKey]; odhtdb::DatabaseNode roomNode(encryptionKey, id); rooms->database->addData(roomNode, *keyPair, { serializer.getBuffer().data(), serializer.getSize() }); } + bool Room::setName(const std::string &name) + { + assert(localUser); + assert(encryptionKey); + + int userPermissionLevel = rooms->database->getUserLowestPermissionLevel(*id, localUser->publicKey); + if(userPermissionLevel != odhtdb::PERMISSION_LEVEL_ADMIN) + { + odhtdb::Log::debug("Room change name: attempted by user %s who is not an admin (permission level: %d)\n", localUser->publicKey.toString().c_str(), userPermissionLevel); + return false; + } + + sibs::SafeSerializer serializer; + serializer.add(RoomDataType::CHANGE_ROOM_NAME); + serializer.add((u16)name.size()); + serializer.add(name.data(), name.size()); + auto keyPair = publicKeyToKeyPairMap[localUser->publicKey]; + odhtdb::DatabaseNode roomNode(encryptionKey, id); + rooms->database->addData(roomNode, *keyPair, { serializer.getBuffer().data(), serializer.getSize() }); + return true; + } + void Room::publishMessage(const std::string &msg) { assert(localUser); assert(publicKeyToKeyPairMap.find(localUser->publicKey) != publicKeyToKeyPairMap.end()); assert(encryptionKey); + sibs::SafeSerializer serializer; serializer.add(RoomDataType::ADD_MESSAGE); - serializer.add((const u8*)msg.data(), msg.size()); + serializer.add(msg.data(), msg.size()); auto keyPair = publicKeyToKeyPairMap[localUser->publicKey]; odhtdb::DatabaseNode roomNode(encryptionKey, id); rooms->database->addData(roomNode, *keyPair, { serializer.getBuffer().data(), serializer.getSize() }); @@ -248,6 +275,7 @@ namespace dchat callbackRequest.timestampSeconds = ntp::NtpTimestamp::fromCombined(request.timestamp).seconds; callbackRequest.loadedFromCache = request.loadedFromCache; callbackRequest.isLocalUser = isLocalUser; + callbackRequest.waitedToJoin = false; callbackFuncs.addUserCallbackFunc(callbackRequest); } } @@ -415,6 +443,7 @@ namespace dchat auto user = room->addUserLocally(*request.userToAddPublicKey, group); bool isLocalUser = false; + bool waitedToJoin = false; if(!room->localUser) { std::lock_guard waitingToJoinRoomLock(waitingToJoinRoomMutex); @@ -424,6 +453,7 @@ namespace dchat room->setLocalUser(user, waitingToJoinRoomIt->second); waitingToJoinRoom.erase(waitingToJoinRoomIt); isLocalUser = true; + waitedToJoin = true; } else { @@ -445,6 +475,7 @@ namespace dchat callbackRequest.timestampSeconds = ntp::NtpTimestamp::fromCombined(request.timestamp).seconds; callbackRequest.loadedFromCache = request.loadedFromCache; callbackRequest.isLocalUser = isLocalUser; + callbackRequest.waitedToJoin = waitedToJoin; callbackFuncs.addUserCallbackFunc(callbackRequest); } } @@ -523,20 +554,23 @@ namespace dchat odhtdb::DatabaseNode nodeInfo(newNode->getNodeEncryptionKey(), newNode->getRequestHash()); database->storeNodeInfoForUserEncrypted(nodeInfo, currentUsername, currentUserPassword, *newNode->getNodeAdminKeyPair()); - sibs::SafeSerializer serializer; - serializer.add(RoomDataType::CHANGE_ROOM_NAME); - serializer.add((u16)name.size()); - serializer.add(name.data(), name.size()); - database->addData(nodeInfo, *newNode->getNodeAdminKeyPair(), { serializer.getBuffer().data(), serializer.getSize() }); + roomIt->second->setName(name); return roomIt->second; } - void Rooms::requestJoinRoom(const std::string &inviteKey, const std::string &message) + bool 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); + if(inviteKey.size() != 130) + { + std::string errMsg = "Invalid invite key length. Expected to be 130 bytes, was "; + errMsg += std::to_string(inviteKey.size()); + errMsg += " byte(s)"; + throw std::runtime_error(errMsg); + } + std::lock_guard waitingToJoinRoomLock(waitingToJoinRoomMutex); std::string roomIdHex = inviteKey.substr(0, 64); std::string roomIdBin = odhtdb::hex2bin(roomIdHex.c_str(), roomIdHex.size()); @@ -545,12 +579,12 @@ namespace dchat if(waitingToJoinRoom.find(*roomId) != waitingToJoinRoom.end()) { fprintf(stderr, "You have already requested to join room %s\n", roomIdHex.c_str()); - return; + return false; } if(roomById.find(*roomId) != roomById.end()) { fprintf(stderr, "You are already a member of the room %s\n", roomIdHex.c_str()); - return; + return false; } std::string roomEncryptionKeyHex = inviteKey.substr(64, 64); std::string roomEncryptionKeyStr = odhtdb::hex2bin(roomEncryptionKeyHex.c_str(), roomEncryptionKeyHex.size()); @@ -558,10 +592,7 @@ namespace dchat memcpy(newEncryptionKey->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; - } + throw std::runtime_error("Invalid invite key, checksum is incorrect. Did you type the invite key incorrectly?"); auto ourNewUser = std::make_shared(); sibs::SafeSerializer serializer; @@ -582,5 +613,6 @@ namespace dchat odhtdb::DatabaseNode roomNode(newEncryptionKey, roomId); database->seed(roomNode); database->storeNodeInfoForUserEncrypted(roomNode, currentUsername, currentUserPassword, *ourNewUser); + return true; } } -- cgit v1.2.3