aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/Cache.hpp4
-rw-r--r--include/Chatbar.hpp6
-rw-r--r--include/FileUtil.hpp3
-rw-r--r--src/Cache.cpp48
-rw-r--r--src/Chatbar.cpp58
-rw-r--r--src/FileUtil.cpp22
-rw-r--r--src/main.cpp82
7 files changed, 222 insertions, 1 deletions
diff --git a/include/Cache.hpp b/include/Cache.hpp
index 429f652..19b811a 100644
--- a/include/Cache.hpp
+++ b/include/Cache.hpp
@@ -3,6 +3,7 @@
#include <boost/filesystem/path.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <string>
+#include <unordered_map>
#include <thread>
#include <mutex>
#include <vector>
@@ -49,6 +50,9 @@ namespace dchat
// Creates directory if it doesn't exist (recursively). Throws boost exception on failure
static boost::filesystem::path getDchatDir();
+ static void loadBindsFromFile();
+ static void replaceBindsInFile(const std::unordered_map<std::string, std::string> &binds);
+
// Get cached image or downloads it.
// Default download file limit is 12MB
// Returns ImageByUrlResult describing texture status.
diff --git a/include/Chatbar.hpp b/include/Chatbar.hpp
index 6ae2190..27b57f9 100644
--- a/include/Chatbar.hpp
+++ b/include/Chatbar.hpp
@@ -6,6 +6,8 @@
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/System/Clock.hpp>
+#include <string>
+#include <unordered_map>
namespace dchat
{
@@ -32,6 +34,10 @@ namespace dchat
void draw(sf::RenderWindow &window);
static float getHeight();
+
+ static bool addBind(const std::string &key, const std::string &value, bool updateFile = true);
+ static bool removeBind(const std::string &key, bool updateFile = true);
+ static const std::unordered_map<std::string, std::string>& getBinds();
private:
void processChatCommand(const StringView &cmd);
private:
diff --git a/include/FileUtil.hpp b/include/FileUtil.hpp
index 0cfc808..097b607 100644
--- a/include/FileUtil.hpp
+++ b/include/FileUtil.hpp
@@ -15,4 +15,7 @@ namespace dchat
// Throws FileException on error.
// Returned value is allocated with malloc and should be free'd by caller.
StringView getFileContent(const boost::filesystem::path &filepath);
+
+ // Throws FileException on error
+ void fileReplace(const boost::filesystem::path &filepath, const StringView data);
}
diff --git a/src/Cache.cpp b/src/Cache.cpp
index d402d36..f3c5830 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -3,10 +3,13 @@
#include "../include/ResourceCache.hpp"
#include "../include/FileUtil.hpp"
#include "../include/Gif.hpp"
+#include "../include/Chatbar.hpp"
#include <boost/filesystem/convenience.hpp>
#include <unordered_map>
#include <process.hpp>
#include <odhtdb/Hash.hpp>
+#include <sibs/SafeSerializer.hpp>
+#include <sibs/SafeDeserializer.hpp>
#if OS_FAMILY == OS_FAMILY_POSIX
#include <pwd.h>
@@ -60,6 +63,51 @@ namespace dchat
return dchatHomeDir;
}
+ void Cache::loadBindsFromFile()
+ {
+ StringView fileContent;
+ try
+ {
+ fileContent = getFileContent(getDchatDir() / "binds");
+ sibs::SafeDeserializer deserializer((const u8*)fileContent.data, fileContent.size);
+
+ while(!deserializer.empty())
+ {
+ u8 keySize = deserializer.extract<u8>();
+ string key;
+ key.resize(keySize);
+ deserializer.extract((u8*)&key[0], keySize);
+
+ u8 valueSize = deserializer.extract<u8>();
+ string value;
+ value.resize(valueSize);
+ deserializer.extract((u8*)&value[0], valueSize);
+
+ Chatbar::addBind(key, value, false);
+ }
+ }
+ catch(FileException &e)
+ {
+ fprintf(stderr, "Failed to read binds from file, reason: %s\n", e.what());
+ }
+
+ delete fileContent.data;
+ }
+
+ void Cache::replaceBindsInFile(const unordered_map<string, string> &binds)
+ {
+ sibs::SafeSerializer serializer;
+ for(auto &it : binds)
+ {
+ serializer.add((u8)it.first.size());
+ serializer.add((const u8*)it.first.data(), it.first.size());
+
+ serializer.add((u8)it.second.size());
+ serializer.add((const u8*)it.second.data(), it.second.size());
+ }
+ fileReplace(getDchatDir() / "binds", StringView((const char*)serializer.getBuffer().data(), serializer.getBuffer().size()));
+ }
+
ImageByUrlResult loadImageFromFile(const boost::filesystem::path &filepath)
{
try
diff --git a/src/Chatbar.cpp b/src/Chatbar.cpp
index cd2daa4..dd5a925 100644
--- a/src/Chatbar.cpp
+++ b/src/Chatbar.cpp
@@ -6,6 +6,7 @@
#include "../include/UsersSidePanel.hpp"
#include "../include/Command.hpp"
#include "../include/ColorScheme.hpp"
+#include "../include/Cache.hpp"
#include <cmath>
#include <cstring>
#include <process.hpp>
@@ -25,6 +26,8 @@ namespace dchat
const float LINE_PADDING_SIDE = 20.0f;
const float LINE_HEIGHT = 1.0f;
+ unordered_map<string, string> binds;
+
Chatbar::Chatbar() :
text("", *ResourceCache::getFont("fonts/Roboto-Regular.ttf"), FONT_SIZE * Settings::getScaling()),
caretIndex(0),
@@ -204,7 +207,7 @@ namespace dchat
}
}
- string getClipboard()
+ static string getClipboard()
{
string result;
TinyProcessLib::Process process("xsel -o -b", "", [&result](const char *bytes, size_t n)
@@ -216,6 +219,28 @@ namespace dchat
return result;
}
+ static void findReplaceAll(string &str, const string &substrToReplace, const string &stringToReplaceWith)
+ {
+ size_t findOffset = 0;
+ while(findOffset < (size_t)str.size())
+ {
+ findOffset = str.find(substrToReplace, findOffset);
+ if(findOffset != string::npos)
+ {
+ string substr = str.replace(findOffset, substrToReplace.size(), stringToReplaceWith);
+ findOffset += substrToReplace.size() + stringToReplaceWith.size();
+ }
+ }
+ }
+
+ static void replaceBinds(string &msg)
+ {
+ for(auto &it : binds)
+ {
+ findReplaceAll(msg, it.first, it.second);
+ }
+ }
+
void Chatbar::processEvent(const sf::Event &event, Channel *channel)
{
if(!focused) return;
@@ -241,7 +266,10 @@ namespace dchat
if(msg[0] == '/')
processChatCommand(StringView(msg.data() + 1, msg.size() - 1));
else
+ {
+ replaceBinds(msg);
channel->addMessage(msg);
+ }
clear();
}
}
@@ -314,4 +342,32 @@ namespace dchat
const float fontHeight = ResourceCache::getFont("fonts/Roboto-Regular.ttf")->getLineSpacing(fontSize);
return PADDING_TOP * Settings::getScaling() + floor(fontHeight * 1.7f + BOX_PADDING_Y * Settings::getScaling() * 2.0f) + PADDING_BOTTOM * Settings::getScaling();
}
+
+ bool Chatbar::addBind(const std::string &key, const std::string &value, bool updateFile)
+ {
+ if(binds.find(key) != binds.end())
+ return false;
+
+ binds[key] = value;
+ if(updateFile)
+ Cache::replaceBindsInFile(binds);
+ return true;
+ }
+
+ bool Chatbar::removeBind(const std::string &key, bool updateFile)
+ {
+ auto it = binds.find(key);
+ if(it == binds.end())
+ return false;
+
+ binds.erase(it);
+ if(updateFile)
+ Cache::replaceBindsInFile(binds);
+ return true;
+ }
+
+ const unordered_map<string, string>& Chatbar::getBinds()
+ {
+ return binds;
+ }
}
diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp
index 53687dd..c2746ed 100644
--- a/src/FileUtil.cpp
+++ b/src/FileUtil.cpp
@@ -32,4 +32,26 @@ namespace dchat
fclose(file);
return { fileData, fileSize };
}
+
+ void fileReplace(const boost::filesystem::path &filepath, const StringView data)
+ {
+#if OS_FAMILY == OS_FAMILY_POSIX
+ FILE *file = fopen(filepath.string().c_str(), "wb+");
+#else
+ FILE *file = _wfopen(filepath.wstring().c_str(), L"wb+");
+#endif
+ if(!file)
+ {
+ int error = errno;
+ string errMsg = "Failed to replace file: ";
+ errMsg += filepath.string();
+ errMsg += ", reason: ";
+ errMsg += strerror(error);
+ throw FileException(errMsg);
+ }
+
+ setbuf(file, NULL);
+ fwrite(data.data, 1, data.size, file);
+ fclose(file);
+ }
}
diff --git a/src/main.cpp b/src/main.cpp
index 014c9ff..59b89a5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -103,6 +103,7 @@ int main(int argc, char **argv)
//Video video(500, 500, "https://www.youtube.com/watch?v=bs0-EX9mJmg");
Cache cache;
+ Cache::loadBindsFromFile();
Channel offlineChannel("Offline");
ChannelSidePanel::addChannel(&offlineChannel);
@@ -422,6 +423,87 @@ int main(int argc, char **argv)
printf("UI scaling set to %f\n", scaling);
});
+ Command::add("addbind", [](const vector<string> &args)
+ {
+ if(args.size() != 2)
+ {
+ string errMsg = "Expected 2 arguments for command addbind, got %u argument(";
+ errMsg += to_string(args.size());
+ errMsg += ")";
+ Channel::getCurrent()->addLocalMessage(errMsg, Channel::getCurrent()->getSystemUser());
+ return;
+ }
+
+ string key = ":";
+ key += args[0];
+ key += ":";
+
+ if(key.size() > 255)
+ {
+ // 253 = bind + two colons
+ Channel::getCurrent()->addLocalMessage("Bind is too long. Max size is 253 bytes", Channel::getCurrent()->getSystemUser());
+ return;
+ }
+
+ bool bindAdded = Chatbar::addBind(key, args[1]);
+ if(bindAdded)
+ Channel::getCurrent()->addLocalMessage("Bind added", Channel::getCurrent()->getSystemUser());
+ else
+ Channel::getCurrent()->addLocalMessage("Bind already exists. Remove it first if you want to replace it", Channel::getCurrent()->getSystemUser());
+ });
+
+ Command::add("removebind", [](const vector<string> &args)
+ {
+ if(args.size() != 1)
+ {
+ string errMsg = "Expected 1 argument for command removebind, got %u argument(";
+ errMsg += to_string(args.size());
+ errMsg += ")";
+ Channel::getCurrent()->addLocalMessage(errMsg, Channel::getCurrent()->getSystemUser());
+ return;
+ }
+
+ string key = ":";
+ key += args[0];
+ key += ":";
+
+ if(key.size() > 255)
+ {
+ // 253 = bind + two colons
+ Channel::getCurrent()->addLocalMessage("Bind is too long. Max size is 253 bytes", Channel::getCurrent()->getSystemUser());
+ return;
+ }
+
+ bool bindRemoved = Chatbar::removeBind(key);
+ if(bindRemoved)
+ Channel::getCurrent()->addLocalMessage("Bind removed", Channel::getCurrent()->getSystemUser());
+ else
+ Channel::getCurrent()->addLocalMessage("Bind doesn't exist, nothing was removed", Channel::getCurrent()->getSystemUser());
+ });
+
+ Command::add("binds", [](const vector<string> &args)
+ {
+ if(args.size() != 0)
+ {
+ string errMsg = "Expected 0 arguments for command removebind, got %u argument(";
+ errMsg += to_string(args.size());
+ errMsg += ")";
+ Channel::getCurrent()->addLocalMessage(errMsg, Channel::getCurrent()->getSystemUser());
+ return;
+ }
+
+ string msg = "Binds:";
+ auto binds = Chatbar::getBinds();
+ for(auto &bind : binds)
+ {
+ msg += "\n";
+ msg += bind.first;
+ msg += " ";
+ msg += bind.second;
+ }
+ Channel::getCurrent()->addLocalMessage(msg, Channel::getCurrent()->getSystemUser());
+ });
+
// Get username and id (public key)
Command::add("whoami", [&currentUserKeyPair, &currentUserName](const vector<string> &args)
{