diff options
author | dec05eba <dec05eba@protonmail.com> | 2018-04-28 11:52:04 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2018-04-28 11:53:18 +0200 |
commit | 3b03f87070d91f63f0dc3c7152723727781dcccf (patch) | |
tree | 7b06163f34248d1e632c032cfaf306787675e585 /src | |
parent | b0bfb8b8d1479502bd5adf17e6a1b94ec00c63ca (diff) |
Add commands, users side panel, improve image download
start using odhtdb
Diffstat (limited to 'src')
-rw-r--r-- | src/Cache.cpp | 22 | ||||
-rw-r--r-- | src/Channel.cpp | 10 | ||||
-rw-r--r-- | src/ChannelSidePanel.cpp | 22 | ||||
-rw-r--r-- | src/Chatbar.cpp | 135 | ||||
-rw-r--r-- | src/Command.cpp | 37 | ||||
-rw-r--r-- | src/MessageBoard.cpp | 12 | ||||
-rw-r--r-- | src/UsersSidePanel.cpp | 47 | ||||
-rw-r--r-- | src/main.cpp | 165 |
8 files changed, 420 insertions, 30 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp index accd0c4..abfa1dd 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -80,6 +80,7 @@ namespace dchat texture->generateMipmap(); return { texture, ImageByUrlResult::Type::CACHED }; } + delete texture; delete fileContent.data; } } @@ -100,7 +101,6 @@ namespace dchat { while(true) { - imageDownloadMutex.lock(); for(vector<ImageDownloadInfo>::iterator it = imageDownloadProcesses.begin(); it != imageDownloadProcesses.end();) { int exitStatus; @@ -114,7 +114,9 @@ namespace dchat filepath /= urlHash.toString(); ImageByUrlResult imageByUrlResult = loadImageFromFile(filepath); + imageDownloadMutex.lock(); imageUrlCache[it->url] = imageByUrlResult; + imageDownloadMutex.unlock(); switch(imageByUrlResult.type) { case ImageByUrlResult::Type::CACHED: @@ -130,11 +132,21 @@ namespace dchat else ++it; } - imageDownloadMutex.unlock(); - while(imageDownloadProcesses.empty()) + while(imageDownloadProcesses.empty() && imageDownloadProcessesQueue.empty()) this_thread::sleep_for(chrono::milliseconds(20)); + if(!imageDownloadProcessesQueue.empty()) + { + imageDownloadMutex.lock(); + for(auto imageDownloadInfo : imageDownloadProcessesQueue) + { + imageDownloadProcesses.push_back(imageDownloadInfo); + } + imageDownloadProcessesQueue.clear(); + imageDownloadMutex.unlock(); + } + this_thread::sleep_for(chrono::milliseconds(20)); } }); @@ -169,10 +181,10 @@ namespace dchat Process::string_type cmd = "curl -L --silent -o '"; cmd += filepath.native(); cmd += "' --max-filesize " + downloadLimitBytesStr + " --range 0-" + downloadLimitBytesStr + " --url '" + url + "'"; - // certutil.exe -urlcache -split -f "https://url/to/file" path/and/name/to/save/as/file + // 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); ImageDownloadInfo imageDownloadInfo { process, url }; - imageDownloadProcesses.emplace_back(imageDownloadInfo); + imageDownloadProcessesQueue.emplace_back(imageDownloadInfo); return result; } } diff --git a/src/Channel.cpp b/src/Channel.cpp index d7d6dc5..f02b97a 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -1,4 +1,6 @@ #include "../include/Channel.hpp" +#include "../include/UsersSidePanel.hpp" +#include "../include/ChannelSidePanel.hpp" #include <cstring> using namespace std; @@ -72,9 +74,11 @@ namespace dchat messageBoard.processEvent(event); } - void Channel::draw(sf::RenderWindow &window, const sf::Vector2f &position, Cache &cache) + void Channel::draw(sf::RenderWindow &window, Cache &cache) { - messageBoard.draw(window, position, cache); - chatbar.draw(window, position); + ChannelSidePanel::draw(window); + messageBoard.draw(window, cache); + chatbar.draw(window); + UsersSidePanel::draw(window); } } diff --git a/src/ChannelSidePanel.cpp b/src/ChannelSidePanel.cpp index 3958376..a85dbdc 100644 --- a/src/ChannelSidePanel.cpp +++ b/src/ChannelSidePanel.cpp @@ -4,15 +4,16 @@ #include "../include/Channel.hpp" #include <SFML/Graphics/RectangleShape.hpp> #include <SFML/Graphics/Text.hpp> +#include <vector> #include <cmath> +using namespace std; + namespace dchat { - ChannelSidePanel::ChannelSidePanel(float _width) : - width(floor(_width)) - { - - } + vector<Channel*> channels; + const float width = 200.0f; + const unsigned int FONT_SIZE = 20; void ChannelSidePanel::addChannel(Channel *channel) { @@ -27,16 +28,21 @@ namespace dchat window.draw(rect); const sf::Font &font = ResourceCache::getFont("fonts/Roboto-Regular.ttf"); - sf::Vector2f position; + sf::Vector2f position(10.0f, 10.0f); for(Channel *channel : channels) { // TODO: Remove this shit sf::String str = "# "; str += sf::String::fromUtf8(channel->getName().begin(), channel->getName().end()); - sf::Text text(str, font, 24 * Settings::getScaling()); + sf::Text text(str, font, FONT_SIZE * Settings::getScaling()); text.setPosition(position); window.draw(text); - position.y += font.getLineSpacing(24 * Settings::getScaling()); + position.y += font.getLineSpacing(FONT_SIZE * Settings::getScaling()); } } + + float ChannelSidePanel::getWidth() + { + return width; + } } diff --git a/src/Chatbar.cpp b/src/Chatbar.cpp index fcfec36..51ab9f5 100644 --- a/src/Chatbar.cpp +++ b/src/Chatbar.cpp @@ -2,8 +2,12 @@ #include "../include/ResourceCache.hpp" #include "../include/Settings.hpp" #include "../include/Channel.hpp" +#include "../include/ChannelSidePanel.hpp" +#include "../include/UsersSidePanel.hpp" +#include "../include/Command.hpp" #include <cmath> #include <cstring> +#include <process.hpp> using namespace std; @@ -14,6 +18,7 @@ namespace dchat const float BOX_PADDING_Y = 5.0f; const int BLINK_TIME_VISIBLE_MS = 500; const int BLINK_TIME_INVISIBLE_MS = 500; + const float PADDING_SIDE = 20.0f; Chatbar::Chatbar() : text("", ResourceCache::getFont("fonts/Roboto-Regular.ttf"), FONT_SIZE * Settings::getScaling()), @@ -34,6 +39,17 @@ namespace dchat blinkTimer.restart(); } + void Chatbar::addString(const string &strToAdd) + { + if(strToAdd.empty()) return; + auto str = text.getString(); + str.insert(caretIndex, sf::String::fromUtf8(strToAdd.begin(), strToAdd.end())); + text.setString(str); + caretIndex += strToAdd.size(); + caretOffset = text.findCharacterPos(caretIndex) - text.getPosition(); + blinkTimer.restart(); + } + const sf::String& Chatbar::getString() const { return text.getString(); @@ -92,6 +108,108 @@ namespace dchat return focused; } + class ArgParseException : public std::runtime_error + { + public: + ArgParseException(const string &errMsg) : std::runtime_error(errMsg) {} + }; + + string stringRemoveQuotes(const StringView &str) + { + string result; + result.reserve(str.size); + for(usize i = 0; i < str.size; ++i) + { + char c = str[i]; + if(c != '\'' && c != '"') + result += c; + } + return result; + } + + vector<string> splitCommandArgs(const StringView &str) + { + vector<string> result; + ssize offset = 0; + char quoteChar = '\0'; + + for(ssize i = 0; i < str.size; ++i) + { + char c = str[i]; + if(c == '\'' || c == '"') + { + if(c == quoteChar) + quoteChar = '\0'; + else + quoteChar = c; + } + else if(quoteChar == '\0' && c == ' ') + { + ssize substrLength = i - offset; + if(substrLength > 0) + { + StringView substrView(str.data + offset, substrLength); + string substr = stringRemoveQuotes(substrView); + if(!substr.empty()) + result.emplace_back(move(substr)); + } + offset = i + 1; + } + } + + if(quoteChar != '\0') + { + string errMsg = "Reached end of command before end of quote ("; + errMsg += quoteChar; + errMsg += ")"; + throw ArgParseException(errMsg); + } + + ssize strLeft = str.size - offset; + if(strLeft > 0) + { + StringView substrView(str.data + offset, strLeft); + string substr = stringRemoveQuotes(substrView); + if(!substr.empty()) + result.emplace_back(move(substr)); + } + return result; + } + + void Chatbar::processChatCommand(const StringView &cmd) + { + vector<string> args; + try + { + args = splitCommandArgs(cmd); + } + catch(ArgParseException &e) + { + fprintf(stderr, "Failed to parse command arguments, reason: %s\n", e.what()); + return; + } + + if(!args.empty()) + { + string command = args.front(); + args.erase(args.begin()); + if(!Command::call(command, args)) + fprintf(stderr, "No such command: %s\n", command.c_str()); + } + } + + string getClipboard() + { + string result; + TinyProcessLib::Process process("xsel -o -b", "", [&result](const char *bytes, size_t n) + { + result.append(bytes, n); + }); + if(process.get_exit_status() != 0) + fprintf(stderr, "Failed to get clipboard content\n"); + return result; + } + void Chatbar::processEvent(const sf::Event &event, Channel *channel) { if(!focused) return; @@ -114,7 +232,10 @@ namespace dchat string msg; msg.resize(chatbarMsgUtf8.size()); memcpy(&msg[0], chatbarMsgUtf8.data(), chatbarMsgUtf8.size()); - channel->getMessageBoard().addMessage(new Message(channel->getLocalUser(), msg)); + if(msg[0] == '/') + processChatCommand(StringView(msg.data() + 1, msg.size() - 1)); + else + channel->getMessageBoard().addMessage(new Message(channel->getLocalUser(), msg)); clear(); } } @@ -123,6 +244,12 @@ namespace dchat { removeNextChar(); } + else if(event.text.unicode == 22) // ctrl+v + { + // TODO: Instead of calling external xsel, use sfml clipboard functionality (in new sfml version) + string clipboard = getClipboard(); + addString(clipboard); + } else { addChar(event.text.unicode); @@ -137,12 +264,12 @@ namespace dchat } } - void Chatbar::draw(sf::RenderWindow &window, const sf::Vector2f &position) + void Chatbar::draw(sf::RenderWindow &window) { auto windowSize = window.getSize(); - sf::Vector2f backgroundSize(floor(windowSize.x * 0.7f), floor(text.getCharacterSize() * 1.7f + BOX_PADDING_Y * 2.0f)); - sf::Vector2f backgroundPos(floor(position.x), floor(position.y + windowSize.y - backgroundSize.y - 20.0f)); + sf::Vector2f backgroundSize(floor(windowSize.x - ChannelSidePanel::getWidth() - UsersSidePanel::getWidth() - PADDING_SIDE * 2.0f), floor(text.getCharacterSize() * 1.7f + BOX_PADDING_Y * 2.0f)); + sf::Vector2f backgroundPos(floor(ChannelSidePanel::getWidth() + PADDING_SIDE), floor(windowSize.y - backgroundSize.y - 20.0f)); background.setSize(backgroundSize); background.setPosition(backgroundPos); text.setPosition(floor(backgroundPos.x + BOX_PADDING_X), floor(backgroundPos.y + backgroundSize.y * 0.5f - text.getCharacterSize() * 0.5f)); diff --git a/src/Command.cpp b/src/Command.cpp new file mode 100644 index 0000000..a074930 --- /dev/null +++ b/src/Command.cpp @@ -0,0 +1,37 @@ +#include "../include/Command.hpp" +#include <unordered_map> + +using namespace std; + +namespace dchat +{ + unordered_map<string, CommandHandlerFunc> commandHandlerFuncs; + + bool Command::add(const string &cmd, CommandHandlerFunc handlerFunc) + { + auto it = commandHandlerFuncs.find(cmd); + if(it != commandHandlerFuncs.end()) + return false; + + commandHandlerFuncs[cmd] = handlerFunc; + return true; + } + + bool Command::call(const string &cmd, const vector<string> &args) + { + auto it = commandHandlerFuncs.find(cmd); + if(it != commandHandlerFuncs.end()) + { + try + { + it->second(args); + } + catch(exception &e) + { + fprintf(stderr, "Failed while executing command %s, reason: %s\n", cmd.c_str(), e.what()); + } + return true; + } + return false; + } +} diff --git a/src/MessageBoard.cpp b/src/MessageBoard.cpp index 4cab31d..a0d1ab2 100644 --- a/src/MessageBoard.cpp +++ b/src/MessageBoard.cpp @@ -2,6 +2,8 @@ #include "../include/Settings.hpp" #include "../include/ResourceCache.hpp" #include "../include/Gif.hpp" +#include "../include/ChannelSidePanel.hpp" +#include "../include/UsersSidePanel.hpp" #include <SFML/Graphics/RectangleShape.hpp> #include <SFML/Graphics/Sprite.hpp> #include <SFML/Window/Mouse.hpp> @@ -17,6 +19,8 @@ namespace dchat const float USERNAME_PADDING_BOTTOM = 0.0f; const float MESSAGE_PADDING_BOTTOM = 20.0f; + const float PADDING_SIDE = 20.0f; + MessageBoard::MessageBoard(const sf::Vector2u &size) : selectingText(false), leftMouseButtonPressed(false), @@ -93,10 +97,10 @@ namespace dchat } } - void MessageBoard::draw(sf::RenderWindow &window, const sf::Vector2f &pos, Cache &cache) + void MessageBoard::draw(sf::RenderWindow &window, Cache &cache) { auto windowSize = window.getSize(); - sf::Vector2u backgroundSize(floor(windowSize.x - pos.x * 2.0f), floor(windowSize.y)); + sf::Vector2u backgroundSize(floor(windowSize.x - ChannelSidePanel::getWidth() - UsersSidePanel::getWidth() - PADDING_SIDE * 2.0f), floor(windowSize.y)); //if(backgroundSize != staticContentTexture.getSize()) // updateStaticContentTexture(backgroundSize); @@ -116,8 +120,8 @@ namespace dchat if(dirty) { - sf::Vector2f position = pos; - position.x += 20.0f; + sf::Vector2f position; + position.x = ChannelSidePanel::getWidth() + PADDING_SIDE; position.y += scroll; for(Message *message : messages) { diff --git a/src/UsersSidePanel.cpp b/src/UsersSidePanel.cpp new file mode 100644 index 0000000..5af01bc --- /dev/null +++ b/src/UsersSidePanel.cpp @@ -0,0 +1,47 @@ +#include "../include/UsersSidePanel.hpp" +#include "../include/ResourceCache.hpp" +#include "../include/Settings.hpp" +#include <SFML/Graphics/RectangleShape.hpp> +#include <SFML/Graphics/Text.hpp> +#include <vector> +#include <cmath> + +using namespace std; + +namespace dchat +{ + vector<User*> users; + const float width = 200.0f; + const unsigned int FONT_SIZE = 20; + + void UsersSidePanel::addUser(User *user) + { + users.push_back(user); + } + + void UsersSidePanel::draw(sf::RenderWindow &window) + { + auto windowSize = window.getSize(); + sf::RectangleShape rect(sf::Vector2f(width, windowSize.y)); + rect.setFillColor(sf::Color(30, 30, 30)); + rect.setPosition(windowSize.x - width, 0.0f); + window.draw(rect); + + const sf::Font &font = ResourceCache::getFont("fonts/Roboto-Regular.ttf"); + sf::Vector2f position(rect.getPosition().x + 10.0f, 10.0f); + for(User *user : users) + { + // TODO: Remove this shit + sf::String str = sf::String::fromUtf8(user->getName().begin(), user->getName().end()); + sf::Text text(str, font, FONT_SIZE * Settings::getScaling()); + text.setPosition(position); + window.draw(text); + position.y += font.getLineSpacing(FONT_SIZE * Settings::getScaling()); + } + } + + float UsersSidePanel::getWidth() + { + return width; + } +} diff --git a/src/main.cpp b/src/main.cpp index 3cebfe7..52a19b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,48 @@ #include "../include/Channel.hpp" #include "../include/ChannelSidePanel.hpp" +#include "../include/UsersSidePanel.hpp" #include "../include/Cache.hpp" #include "../include/ResourceCache.hpp" #include "../include/Video.hpp" +#include "../include/Command.hpp" #include <string> #include <SFML/Graphics.hpp> #include <cstring> -#include <X11/Xlib.h> #include <boost/filesystem/path.hpp> +#include <odhtdb/Database.hpp> +#include <odhtdb/Signature.hpp> +#include <odhtdb/bin2hex.hpp> +#include <odhtdb/hex2bin.hpp> +#include <X11/Xlib.h> using namespace std; using namespace dchat; using namespace TinyProcessLib; +string createChannelJoinKey(const unique_ptr<odhtdb::DatabaseCreateResponse> &databaseCreateResponse) +{ + string result; + result += databaseCreateResponse->getRequestHash()->toString(); + result += "&"; + result += odhtdb::bin2hex((const char*)databaseCreateResponse->getNodeEncryptionKey()->data, databaseCreateResponse->getNodeEncryptionKey()->size); + return result; +} + +odhtdb::DatabaseNode createDatabaseNodeFromJoinKey(const string &joinKey) +{ + odhtdb::DatabaseNode result; + + string nodeHashStr = odhtdb::hex2bin(joinKey.c_str(), 64); + memcpy(result.getRequestHash()->getData(), nodeHashStr.data(), nodeHashStr.size()); + + string nodeEncryptionKeyStr = odhtdb::hex2bin(joinKey.c_str() + 65, 64); + char *nodeEncryptionKeyRaw = new char[nodeEncryptionKeyStr.size()]; + result.getNodeEncryptionKey()->data = nodeEncryptionKeyRaw; + result.getNodeEncryptionKey()->size = nodeEncryptionKeyStr.size(); + + return result; +} + int main(int argc, char **argv) { /* @@ -27,14 +57,138 @@ int main(int argc, char **argv) window.setVerticalSyncEnabled(false); window.setFramerateLimit(60); - //odhtdb::Database database("bootstrap.ring.cx", 4222, Cache::getDchatDir()); + odhtdb::Database database("bootstrap.ring.cx", 4222, Cache::getDchatDir()); + + database.setOnCreateNodeCallback([](const odhtdb::DatabaseCreateNodeRequest &request) + { + + }); + + database.setOnAddNodeCallback([](const odhtdb::DatabaseAddNodeRequest &request) + { + + }); + + database.setOnAddUserCallback([](const odhtdb::DatabaseAddUserRequest &request) + { + + }); //Video video(500, 500, "https://www.youtube.com/watch?v=bs0-EX9mJmg"); Cache cache; Channel channel("latenightshiendjs"); - ChannelSidePanel channelSidePanel(300.0f); - channelSidePanel.addChannel(&channel); + ChannelSidePanel::addChannel(&channel); + UsersSidePanel::addUser(channel.getLocalUser()); + + odhtdb::Signature::KeyPair *currentUserKeyPair = nullptr; + vector<odhtdb::NodeLocalUser> localNodeUsers; + string currentUserName; + string currentUserPassword; + + Command::add("login", [¤tUserKeyPair, ¤tUserName, ¤tUserPassword, &localNodeUsers, &database](const vector<string> &args) + { + if(args.size() != 2) + { + fprintf(stderr, "Expected 2 arguments for command login (username and password), got %u argument(s)\n", args.size()); + return; + } + + try + { + odhtdb::Signature::KeyPair keyPair = database.getStorage().decryptLocalEncryptedUser(args[0], args[1]); + localNodeUsers = database.getStorage().getLocalNodeUsers(keyPair); + + for(auto localNodeUser : localNodeUsers) + { + //const odhtdb::DatabaseStorageObjectList *nodeStorage = database.getStorage().getStorage(localNodeUser.nodeHash); + //Channel *channel = new Channel() + } + + printf("Successfully logged into user %s\n", args[0].c_str()); + if(currentUserKeyPair) + delete currentUserKeyPair; + currentUserKeyPair = new odhtdb::Signature::KeyPair(keyPair); + + currentUserName = args[0]; + currentUserPassword = args[1]; + } + catch(odhtdb::DatabaseStorageException &e) + { + fprintf(stderr, "Failed to login, reason: %s\n", e.what()); + } + }); + + Command::add("register", [¤tUserKeyPair, ¤tUserName, ¤tUserPassword, &database](const vector<string> &args) + { + if(args.size() != 2) + { + fprintf(stderr, "Expected 2 arguments for command register (username and password), got %u argument(s)\n", args.size()); + return; + } + + odhtdb::Signature::KeyPair keyPair; + if(!database.getStorage().storeLocalUser(args[0], keyPair, args[1])) + { + fprintf(stderr, "User with name %s already exists in storage\n", args[0].c_str()); + return; + } + + printf("Registered user %s, public key: %s, private key: %s\n", args[0].c_str(), keyPair.getPublicKey().toString().c_str(), keyPair.getPrivateKey().toString().c_str()); + printf("Successfully logged into user %s\n", args[0].c_str()); + + if(currentUserKeyPair) + delete currentUserKeyPair; + currentUserKeyPair = new odhtdb::Signature::KeyPair(keyPair); + + currentUserName = args[0]; + currentUserPassword = args[1]; + }); + + Command::add("cc", [¤tUserKeyPair, ¤tUserName, &database](const vector<string> &args) + { + if(args.size() != 1) + { + fprintf(stderr, "Expected 1 argument for command cc (channel name), got %u argument(s)\n", args.size()); + return; + } + + if(!currentUserKeyPair) + { + fprintf(stderr, "You are not logged in. Please login before creating a channel\n"); + return; + } + + auto createResponse = database.create(currentUserName, *currentUserKeyPair, args[0]); + database.commit(); + odhtdb::DatabaseNode databaseNode(createResponse->getNodeEncryptionKey(), createResponse->getRequestHash()); + database.seed(databaseNode); + printf("Created database '%s', join key: '%s'\n", args[0].c_str(), createChannelJoinKey(createResponse).c_str()); + }); + + Command::add("jc", [¤tUserKeyPair, &database](const vector<string> &args) + { + if(args.size() != 1) + { + fprintf(stderr, "Expected 1 argument for command jc (channel join key), got %u argument(s)\n", args.size()); + return; + } + + if(args[0].size() != 129) + { + fprintf(stderr, "Expected join key to be 129 characters, was %u character(s)\n", args[0].size()); + return; + } + + if(!currentUserKeyPair) + { + fprintf(stderr, "You are not logged in. Please login before joining a channel\n"); + return; + } + + odhtdb::DatabaseNode databaseNode = createDatabaseNodeFromJoinKey(args[0]); + database.seed(databaseNode); + }); sf::Event event; while (window.isOpen()) @@ -60,8 +214,7 @@ int main(int argc, char **argv) } window.clear(sf::Color(40, 40, 40)); - channel.draw(window, sf::Vector2f(channelSidePanel.width, 0.0f), cache); - channelSidePanel.draw(window); + channel.draw(window, cache); //video.draw(window); window.display(); } |