diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 361 |
1 files changed, 349 insertions, 12 deletions
diff --git a/src/main.cpp b/src/main.cpp index 0836278..efb354f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,9 @@ #include "../include/GlobalContextMenu.hpp" #include "../include/StringUtils.hpp" #include "../include/ImagePreview.hpp" +#include "../include/Rpc.hpp" +#include <msgpack.hpp> +#include <sstream> #include <string> #include <SFML/Graphics.hpp> #include <cstring> @@ -94,6 +97,13 @@ static void channelChangeChannelName(Channel *channel, const StringView data, co fprintf(stderr, "Channel change name: user with public key %s not found in channel %s\n", userPublicKey.toString().c_str(), channel->getName().c_str()); return; } + + int userPermissionLevel = channel->getUserLowestPermissionLevel(user); + if(userPermissionLevel != odhtdb::PERMISSION_LEVEL_ADMIN) + { + fprintf(stderr, "Channel change name: attempted by user %s who is not an admin\n", user->getName().c_str()); + return; + } sibs::SafeDeserializer deserializer((const u8*)data.data, data.size); u16 channelNameLength = deserializer.extract<u16>(); @@ -107,6 +117,40 @@ static void channelChangeChannelName(Channel *channel, const StringView data, co // We dont care if there is more data to read (malicious packet), we already got all the data we need } +static void channelAddDiscordMessage(Channel *channel, const StringView data, const odhtdb::Signature::PublicKey &userPublicKey, u64 timestamp, const odhtdb::Hash &requestHash) +{ + auto bridgeOwner = channel->getUserByPublicKey(userPublicKey); + if(!bridgeOwner) + { + fprintf(stderr, "Channel add discord message: user with public key %s not found in channel %s\n", userPublicKey.toString().c_str(), channel->getName().c_str()); + return; + } + + int userPermissionLevel = channel->getUserLowestPermissionLevel(bridgeOwner); + if(userPermissionLevel != odhtdb::PERMISSION_LEVEL_ADMIN) + { + fprintf(stderr, "Channel add discord message: attempted by user %s who is not an admin\n", bridgeOwner->getName().c_str()); + return; + } + + sibs::SafeDeserializer deserializer((const u8*)data.data, data.size); + u64 discordUserId = deserializer.extract<u64>(); + u8 discordNameLength = deserializer.extract<u8>(); + if(discordNameLength == 0) return; + + string discordUserName; + discordUserName.resize(discordNameLength); + deserializer.extract((u8*)&discordUserName[0], discordNameLength); + + usize msgSize = deserializer.getSize(); + if(msgSize == 0) return; + string msg(deserializer.getBuffer(), deserializer.getBuffer() + deserializer.getSize()); + + auto timestampSeconds = ntp::NtpTimestamp::fromCombined(timestamp).seconds; + channel->addLocalDiscordMessage(discordUserName, discordUserId, msg, bridgeOwner, timestampSeconds, requestHash); + // We dont care if there is more data to read (malicious packet), we already got all the data we need +} + static void channelAddStoredMessage(Channel *channel, const odhtdb::Hash &requestHash, const odhtdb::Signature::PublicKey &creatorPublicKey, const StringView decryptedObject, u64 timestamp, bool loadedFromCache) { User *user = channel->getUserByPublicKey(creatorPublicKey); @@ -187,6 +231,18 @@ static void channelAddStoredMessage(Channel *channel, const odhtdb::Hash &reques } break; } + case ChannelDataType::ADD_DISCORD_MESSAGE: + { + try + { + channelAddDiscordMessage(channel, decryptedData, creatorPublicKey, timestamp, requestHash); + } + catch(sibs::DeserializeException &e) + { + fprintf(stderr, "Failed to deserialize channel add discord message\n"); + } + break; + } default: fprintf(stderr, "Got unexpected channel data type: %u\n", channelDataType); break; @@ -207,7 +263,7 @@ int main(int argc, char **argv) printf("Resource path set to: %s\n", resourcesPath.string().c_str()); } else - printf("Resource directory not defined, using currently directory"); + printf("Resource directory not defined, using currently directory\n"); const sf::Int64 FRAMERATE_FOCUSED = 144; const sf::Int64 FRAMERATE_NOT_FOCUSED = 10; @@ -239,6 +295,9 @@ int main(int argc, char **argv) odhtdb::Database *database = nullptr; odhtdb::DatabaseCallbackFuncs callbackFuncs; + + using LocalUserMessageCallback = function<void(const odhtdb::DatabaseAddNodeRequest &request)>; + LocalUserMessageCallback onMessageByLocalUser = nullptr; callbackFuncs.createNodeCallbackFunc = [&waitingToJoinChannels, &database, &channels, &channelMessageMutex, &waitingToJoin, &localNodeUsers, &lastFocusedTimer](const odhtdb::DatabaseCreateNodeRequest &request) { @@ -280,7 +339,7 @@ int main(int argc, char **argv) } }; - callbackFuncs.addNodeCallbackFunc = [&channels, &channelMessageMutex, &lastFocusedTimer](const odhtdb::DatabaseAddNodeRequest &request) + callbackFuncs.addNodeCallbackFunc = [&channels, &channelMessageMutex, &lastFocusedTimer, &onMessageByLocalUser](const odhtdb::DatabaseAddNodeRequest &request) { lock_guard<recursive_mutex> lock(channelMessageMutex); //printf("Add node callback func %s\n", request.requestHash->toString().c_str()); @@ -291,6 +350,12 @@ int main(int argc, char **argv) channelAddStoredMessage(channel, *request.requestHash, *request.creatorPublicKey, StringView((const char*)request.decryptedData.data, request.decryptedData.size), request.timestamp, request.loadedFromCache); if(channel == Channel::getCurrent()) lastFocusedTimer.restart(); + + if(!request.loadedFromCache && *request.creatorPublicKey == static_cast<OnlineLocalUser*>(channel->getLocalUser())->getPublicKey()) + { + if(onMessageByLocalUser) + onMessageByLocalUser(request); + } return; } } @@ -808,7 +873,7 @@ int main(int argc, char **argv) }); // Change name of the current channel - Command::add("channelname", [&loggedIn, &offlineChannel, addSystemMessage](const vector<string> &args) + Command::add("channelname", [&offlineChannel, addSystemMessage](const vector<string> &args) { if(args.size() != 1) { @@ -818,16 +883,17 @@ int main(int argc, char **argv) addSystemMessage(errMsg); return; } - - if(!loggedIn) + + Channel *currentChannel = Channel::getCurrent(); + if(currentChannel == &offlineChannel) { - addSystemMessage("You need to be logged in to change channel name"); + addSystemMessage("You need to be in a channel to change channel name"); return; } - if(Channel::getCurrent() == &offlineChannel) + if(!currentChannel->getLocalUser()->isOnlineUser()) { - addSystemMessage("You need to be in a channel to change channel name"); + addSystemMessage("You need to be logged in to change channel name"); return; } @@ -836,8 +902,15 @@ int main(int argc, char **argv) addSystemMessage("Channel name has to be between 1 and 32 bytes long"); return; } + + int localUserPermissionLevel = currentChannel->getUserLowestPermissionLevel(static_cast<OnlineLocalUser*>(currentChannel->getLocalUser())); + if(localUserPermissionLevel != odhtdb::PERMISSION_LEVEL_ADMIN) + { + addSystemMessage("You need to be admin to change channel name"); + return; + } - Channel::getCurrent()->setName(args[0]); + currentChannel->setName(args[0]); string msg = "Channel name has been changed to "; msg += args[0]; addSystemMessage(msg); @@ -855,10 +928,268 @@ int main(int argc, char **argv) commandsMsg += commandIt.first; } addSystemMessage(commandsMsg); + + odhtdb::MapHash<u64> myDiscordIdsByChannel; + + bool running = true; + thread rpcThread([&running, &onMessageByLocalUser, &channels, &lastFocusedTimer, &myDiscordIdsByChannel]() + { + Rpc rpc(5555); + mutex messageMutex; + vector<msgpack::sbuffer> messagesToSend; + onMessageByLocalUser = [&messagesToSend, &messageMutex](const odhtdb::DatabaseAddNodeRequest &request) + { + lock_guard<mutex> lock(messageMutex); + auto channelDataType = (ChannelDataType)static_cast<const char*>(request.decryptedData.data)[0]; + usize size = request.decryptedData.size - 1; + const char *data = (const char*)request.decryptedData.data + 1; + const char *action = nullptr; + if(channelDataType == ChannelDataType::ADD_MESSAGE) + action = "addMessage"; + + if(!action) return; + vector<string> msg = { action, string(data, data + size), request.nodeHash->toString() }; + msgpack::sbuffer buffer; + msgpack::pack(buffer, msg); + messagesToSend.emplace_back(move(buffer)); + }; + + while(running) + { + rpc.recv([&channels, &lastFocusedTimer, &myDiscordIdsByChannel](zmq::message_t *message) + { + try + { + msgpack::object_handle oh = msgpack::unpack((const char*)message->data(), message->size()); + auto deserialized = oh.get(); + vector<string> msg; + deserialized.convert(msg); + if(msg.size() < 2) + { + fprintf(stderr, "Rpc receive, data length expected to be at least 2, was %u\n", msg.size()); + return; + } + auto &action = msg[0]; + + string dchatChannelIdRaw = odhtdb::hex2bin(msg[1].c_str(), msg[1].size()); + odhtdb::Hash dchatChannelId; + memcpy(dchatChannelId.getData(), dchatChannelIdRaw.data(), dchatChannelIdRaw.size()); + Channel *bridgedChannel = nullptr; + for(Channel *channel : channels) + { + if(*channel->getNodeInfo().getRequestHash() == dchatChannelId) + { + bridgedChannel = channel; + break; + } + } + + if(!bridgedChannel) + { + fprintf(stderr, "Rcp addMessage, invalid dchat channel %s\n", msg[1].c_str()); + return; + } + + if(bridgedChannel == Channel::getCurrent()) + lastFocusedTimer.restart(); + + fprintf(stderr, "Received rpc, action: %s\n", action.c_str()); + if(action == "addMessage") + { + if((msg.size() - 2) % 4 != 0) + { + fprintf(stderr, "Rpc addMessage, request was malformed\n"); + return; + } + + for(size_t i = 2; i < msg.size(); i += 4) + { + auto &content = msg[i]; + auto &discordUserId = msg[i + 1]; + u64 discordUserIdNumber = 0; + auto &discordUserName = msg[i + 2]; + auto &messageTimestampMillisec = msg[i + 3]; + u64 messageTimestampSecondsNumber = 0; + + try + { + discordUserIdNumber = stoull(discordUserId); + } + catch(...) + { + fprintf(stderr, "Rpc receive, failed to convert discord id to uint64_t: %s\n", discordUserId.c_str()); + return; + } + + try + { + messageTimestampSecondsNumber = stoull(messageTimestampMillisec) / 1000; + } + catch(...) + { + fprintf(stderr, "Rpc receive, failed to convert discord message timestamp to uint64_t: %s\n", messageTimestampMillisec.c_str()); + return; + } + + auto myDiscordIdIt = myDiscordIdsByChannel.find(dchatChannelId); + if(myDiscordIdIt != myDiscordIdsByChannel.end() && myDiscordIdIt->second == discordUserIdNumber) + { + auto &channelMessages = bridgedChannel->getMessageBoard().getMessages(); + Message *myLatestMessage = nullptr; + for(auto it = channelMessages.rbegin(), end = channelMessages.rend(); it != end; ++it) + { + if((*it)->user == bridgedChannel->getLocalUser()) + { + myLatestMessage = *it; + break; + } + } + + if(myLatestMessage && (i64)messageTimestampSecondsNumber - (i64)myLatestMessage->timestampSeconds <= 3) + { + return; + /* + auto myMessageUtf8 = myLatestMessage->text.getString().toUtf8(); + odhtdb::Hash myMessageHash(myMessageUtf8.data(), myMessageUtf8.size()); + odhtdb::Hash myDiscordMessageHash(content.data(), content.size()); + if(myMessageHash == myDiscordMessageHash) + return; + */ + } + } + + bridgedChannel->addLocalDiscordMessage(discordUserName, discordUserIdNumber, content, bridgedChannel->getLocalUser(), messageTimestampSecondsNumber, odhtdb::Hash(messageTimestampMillisec.c_str(), messageTimestampMillisec.size())); + } + } + else if(action == "addUser") + { + if((msg.size() - 2) % 4 != 0) + { + fprintf(stderr, "Rpc addUser, request was malformed\n"); + return; + } + + for(size_t i = 2; i < msg.size(); i += 4) + { + auto &discordUsername = msg[i]; + auto &discordUserId = msg[i + 1]; + auto &userStatus = msg[i + 2]; + auto &avatarURL = msg[i + 3]; + + try + { + u64 discordUserIdNumber = stoull(discordUserId); + bool online = (userStatus != "offline"); + printf("Rpc, adding user %s with status %s\n", discordUsername.c_str(), userStatus.c_str()); + DiscordServiceUser *discordUser = bridgedChannel->getDiscordService()->getUserById(discordUserIdNumber); + if(discordUser) + { + discordUser->connected = online; + } + else + { + discordUser = new DiscordServiceUser(discordUsername, discordUserIdNumber, online); + bridgedChannel->getDiscordService()->addUser(discordUser); + } + discordUser->avatarUrl = avatarURL; + } + catch(...) + { + fprintf(stderr, "Warning: Rpc receive, failed to convert discord id to uint64_t: %s\n", discordUserId.c_str()); + // Ignore for now.. should we really handle this error other than showing warning? + } + } + } + else if(action == "removeUser") + { + for(size_t i = 2; i < msg.size(); ++i) + { + auto &discordUserId = msg[i]; + try + { + u64 discordUserIdNumber = stoull(discordUserId); + bridgedChannel->getDiscordService()->removeUser(discordUserIdNumber); + } + catch(...) + { + fprintf(stderr, "Warning: Rpc receive, failed to convert discord id to uint64_t: %s\n", discordUserId.c_str()); + // Ignore for now.. should we really handle this error other than showing warning? + } + } + } + else if(action == "statusChange") + { + if((msg.size() - 2) != 2) + { + fprintf(stderr, "Rpc statusChange, request was malformed\n"); + return; + } + + auto &discordUserId = msg[2]; + auto &userStatus = msg[3]; + + try + { + u64 discordUserIdNumber = stoull(discordUserId); + DiscordServiceUser *discordUser = bridgedChannel->getDiscordService()->getUserById(discordUserIdNumber); + if(discordUser) + { + discordUser->connected = (userStatus != "offline"); + printf("Rcp statusChange, changed user %s (%s) status to %s\n", discordUserId.c_str(), discordUser->getName().c_str(), userStatus.c_str()); + } + } + catch(...) + { + fprintf(stderr, "Warning: Rpc receive, failed to convert discord id to uint64_t: %s\n", discordUserId.c_str()); + // Ignore for now.. should we really handle this error other than showing warning? + } + } + else if(action == "addMe") + { + if((msg.size() - 2) != 1) + { + fprintf(stderr, "Rpc addMe, request was malformed\n"); + return; + } + + auto &myDiscordId = msg[2]; + try + { + u64 myDiscordIdNumber = stoull(myDiscordId); + myDiscordIdsByChannel[dchatChannelId] = myDiscordIdNumber; + } + catch(...) + { + fprintf(stderr, "Warning: Rpc receive, failed to convert discord id to uint64_t: %s\n", myDiscordId.c_str()); + // Ignore for now.. should we really handle this error other than showing warning? + } + } + else + { + fprintf(stderr, "Rcp received unknown action %s\n", action.c_str()); + } + } + catch(msgpack::type_error &e) + { + fprintf(stderr, "Failed to deserialize received rpc, error: %s\n", e.what()); + } + }); + { + lock_guard<mutex> lock(messageMutex); + for(auto &messageToSend : messagesToSend) + { + fprintf(stderr, "Rpc, sending message\n"); + rpc.send(messageToSend.data(), messageToSend.size()); + } + messagesToSend.clear(); + } + this_thread::sleep_for(chrono::milliseconds(50)); + } + }); sf::Clock frameTimer; - while (window.isOpen()) + while (running) { frameTimer.restart(); Channel *currentChannel = Channel::getCurrent(); @@ -867,11 +1198,14 @@ int main(int argc, char **argv) while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) + { window.close(); + running = false; + } else if(event.type == sf::Event::Resized) { sf::FloatRect viewRect(0.0f, 0.0f, event.size.width, event.size.height); - /* // TODO: Use xlib to set window minimum size instead + /* // TODO: Use xlib/xcb to set window minimum size instead const int minWidth = 800; if(event.size.width < minWidth) { @@ -916,7 +1250,7 @@ int main(int argc, char **argv) channel->update(); } - if(lastFocusedTimer.getElapsedTime().asMilliseconds() > 5000) + if((!windowFocused || !focused) && lastFocusedTimer.getElapsedTime().asMilliseconds() > 5000) { this_thread::sleep_for(chrono::milliseconds(250)); continue; @@ -948,6 +1282,9 @@ int main(int argc, char **argv) //video.draw(window); window.display(); } + + onMessageByLocalUser = nullptr; + rpcThread.join(); for(Channel *channel : channels) { |