aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/Channel.hpp8
-rw-r--r--include/Command.hpp3
-rw-r--r--include/Message.hpp2
-rw-r--r--src/Channel.cpp49
-rw-r--r--src/Command.cpp6
-rw-r--r--src/Message.cpp4
-rw-r--r--src/MessageBoard.cpp2
-rw-r--r--src/Text.cpp6
-rw-r--r--src/main.cpp174
9 files changed, 241 insertions, 13 deletions
diff --git a/include/Channel.hpp b/include/Channel.hpp
index 26b5286..5f5699e 100644
--- a/include/Channel.hpp
+++ b/include/Channel.hpp
@@ -26,7 +26,9 @@ namespace dchat
ADD_MESSAGE,
EDIT_MESSAGE,
DELETE_MESSAGE,
- NICKNAME_CHANGE
+ NICKNAME_CHANGE,
+ CHANGE_AVATAR,
+ CHANGE_CHANNEL_NAME
};
class Channel
@@ -49,6 +51,7 @@ namespace dchat
// If timestamp is 0, then current time is used
void addLocalMessage(const std::string &msg, User *owner, u64 timestampSeconds = 0);
void addLocalMessage(const std::string &msg, User *owner, u64 timestampSeconds, const odhtdb::Hash &id);
+ void addSystemMessage(const std::string &msg, bool plainText = true);
void addMessage(const std::string &msg);
void deleteLocalMessage(const odhtdb::Hash &id, const odhtdb::Signature::PublicKey &requestedByUser);
void deleteMessage(const odhtdb::Hash &id, const odhtdb::Signature::PublicKey &requestedByUser);
@@ -57,6 +60,9 @@ namespace dchat
bool addUser(const odhtdb::Signature::PublicKey &userId, const odhtdb::DataView &groupId);
void replaceLocalUser(OnlineLocalUser *newOnlineLocalUser);
void changeNick(const std::string &newNick);
+ void setAvatar(const std::string &newAvatarUrl);
+ void setNameLocally(const std::string &name);
+ void setName(const std::string &name);
void processEvent(const sf::Event &event, Cache &cache);
void draw(sf::RenderWindow &window, Cache &cache);
diff --git a/include/Command.hpp b/include/Command.hpp
index fe8a947..fe08664 100644
--- a/include/Command.hpp
+++ b/include/Command.hpp
@@ -3,6 +3,7 @@
#include <vector>
#include <string>
#include <functional>
+#include <unordered_map>
namespace dchat
{
@@ -13,5 +14,7 @@ namespace dchat
public:
static bool add(const std::string &cmd, CommandHandlerFunc handlerFunc);
static bool call(const std::string &cmd, const std::vector<std::string> &args);
+
+ static const std::unordered_map<std::string, CommandHandlerFunc>& getCommands();
};
}
diff --git a/include/Message.hpp b/include/Message.hpp
index a2d67e8..2327a0b 100644
--- a/include/Message.hpp
+++ b/include/Message.hpp
@@ -18,7 +18,7 @@ namespace dchat
};
// If timestamp is 0, then timestamp is not used
- Message(User *user, const std::string &text, u64 timestampSeconds = 0);
+ Message(User *user, const std::string &text, u64 timestampSeconds = 0, bool plainText = false);
const User *user;
Text text;
diff --git a/src/Channel.cpp b/src/Channel.cpp
index 1ac34c0..5015975 100644
--- a/src/Channel.cpp
+++ b/src/Channel.cpp
@@ -118,12 +118,12 @@ namespace dchat
return databaseNodeInfo;
}
- void Channel::addLocalMessage(const std::string &msg, User *owner, u64 timestampSeconds)
+ void Channel::addLocalMessage(const string &msg, User *owner, u64 timestampSeconds)
{
addLocalMessage(msg, owner, timestampSeconds, odhtdb::Hash());
}
- void Channel::addLocalMessage(const std::string &msg, User *owner, u64 timestampSeconds, const odhtdb::Hash &id)
+ void Channel::addLocalMessage(const string &msg, User *owner, u64 timestampSeconds, const odhtdb::Hash &id)
{
assert(owner);
if(timestampSeconds == 0)
@@ -133,7 +133,13 @@ namespace dchat
messageBoard.addMessage(new Message(owner, msg, timestampSeconds), id);
}
- void Channel::addMessage(const std::string &msg)
+ void Channel::addSystemMessage(const string &msg, bool plainText)
+ {
+ u64 timestampSeconds = time(NULL);
+ messageBoard.addMessage(new Message(&systemUser, msg, timestampSeconds, plainText), odhtdb::Hash());
+ }
+
+ void Channel::addMessage(const string &msg)
{
if(database && localUser->type == User::Type::ONLINE_LOCAL_USER)
{
@@ -222,7 +228,7 @@ namespace dchat
addUserLocally(newOnlineLocalUser);
}
- void Channel::changeNick(const std::string &newNick)
+ void Channel::changeNick(const string &newNick)
{
if(database && localUser->type == User::Type::ONLINE_LOCAL_USER)
{
@@ -237,6 +243,41 @@ namespace dchat
}
}
+ void Channel::setAvatar(const string &newAvatarUrl)
+ {
+ if(database && localUser->type == User::Type::ONLINE_LOCAL_USER)
+ {
+ auto onlineLocalUser = static_cast<OnlineLocalUser*>(localUser);
+
+ sibs::SafeSerializer serializer;
+ serializer.add(ChannelDataType::CHANGE_AVATAR);
+ serializer.add((u16)newAvatarUrl.size());
+ serializer.add((const u8*)newAvatarUrl.data(), newAvatarUrl.size());
+
+ database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
+ }
+ }
+
+ void Channel::setNameLocally(const string &name)
+ {
+ this->name = name;
+ }
+
+ void Channel::setName(const string &name)
+ {
+ if(database && localUser->type == User::Type::ONLINE_LOCAL_USER)
+ {
+ auto onlineLocalUser = static_cast<OnlineLocalUser*>(localUser);
+
+ sibs::SafeSerializer serializer;
+ serializer.add(ChannelDataType::CHANGE_CHANNEL_NAME);
+ serializer.add((u16)name.size());
+ serializer.add((const u8*)name.data(), name.size());
+
+ database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
+ }
+ }
+
void Channel::processEvent(const sf::Event &event, Cache &cache)
{
chatbar.processEvent(event, cache, this);
diff --git a/src/Command.cpp b/src/Command.cpp
index a28cd42..fb4c0cb 100644
--- a/src/Command.cpp
+++ b/src/Command.cpp
@@ -1,5 +1,4 @@
#include "../include/Command.hpp"
-#include <unordered_map>
using namespace std;
@@ -34,4 +33,9 @@ namespace dchat
}
return false;
}
+
+ const std::unordered_map<std::string, CommandHandlerFunc>& Command::getCommands()
+ {
+ return commandHandlerFuncs;
+ }
}
diff --git a/src/Message.cpp b/src/Message.cpp
index a408aee..1b42626 100644
--- a/src/Message.cpp
+++ b/src/Message.cpp
@@ -7,9 +7,9 @@ using namespace std;
namespace dchat
{
- Message::Message(User *_user, const std::string &_text, u64 _timestampSeconds) :
+ Message::Message(User *_user, const std::string &_text, u64 _timestampSeconds, bool plainText) :
user(_user),
- text(sf::String::fromUtf8(_text.begin(), _text.end()), ResourceCache::getFont("fonts/Roboto-Regular.ttf"), 18 * Settings::getScaling(), 0.0f, false),
+ text(sf::String::fromUtf8(_text.begin(), _text.end()), ResourceCache::getFont("fonts/Roboto-Regular.ttf"), 18 * Settings::getScaling(), 0.0f, plainText),
timestampSeconds(_timestampSeconds),
type(Type::REGULAR)
{
diff --git a/src/MessageBoard.cpp b/src/MessageBoard.cpp
index c75573a..0152cb5 100644
--- a/src/MessageBoard.cpp
+++ b/src/MessageBoard.cpp
@@ -326,7 +326,7 @@ namespace dchat
if(onlineLocalUser && event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Button::Right)
openContextMenu = true;
- if(visibleMessageStartIndex != -1)
+ if(visibleMessageStartIndex != -1 && visibleMessageStartIndex < messages.size() && visibleMessageEndIndex < messages.size())
{
//printf("visibleMessageStartIndex: %u, visibleMessageEndIndex: %u\n", visibleMessageStartIndex, visibleMessageEndIndex);
for(usize i = visibleMessageStartIndex; i <= visibleMessageEndIndex; ++i)
diff --git a/src/Text.cpp b/src/Text.cpp
index 0e24e73..5c8db41 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -170,14 +170,20 @@ namespace dchat
size_t urlStart = textElementStr.find(StringViewUtf32(urlStr.getData(), urlStr.getSize()), offset);
if(urlStart != -1)
{
+ bool foundDot = false;
offset = urlStart + urlStr.getSize();
while(offset < textElementStr.size)
{
if(isspace(textElementStr[offset]))
break;
+ else if(textElementStr[offset] == '.')
+ foundDot = true;
++offset;
}
+ if(!foundDot)
+ return -1;
+
StringViewUtf32 beforeUrlStr(textElementStr.data + stringStart, urlStart - stringStart);
newTextElements.push_back({ beforeUrlStr, TextElement::Type::TEXT });
diff --git a/src/main.cpp b/src/main.cpp
index 8c13c80..ecfd058 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,6 +47,65 @@ static void channelChangeUserNickname(Channel *channel, const StringView data, c
// We dont care if there is more data to read (malicious packet), we already got all the data we need
}
+static bool looksLikeUrl(const string &str)
+{
+ if(str.find('.') == string::npos)
+ return false;
+
+ if(str.size() >= 7 && strncmp(str.c_str(), "http://", 7) == 0)
+ return true;
+
+ if(str.size() >= 8 && strncmp(str.c_str(), "https://", 8) == 0)
+ return true;
+
+ return false;
+}
+
+static void channelChangeUserAvatar(Channel *channel, const StringView data, const odhtdb::Signature::PublicKey &userPublicKey)
+{
+ auto user = channel->getUserByPublicKey(userPublicKey);
+ if(!user)
+ {
+ fprintf(stderr, "Avatar change: user with public key %s not found in channel %s\n", userPublicKey.toString().c_str(), channel->getName().c_str());
+ return;
+ }
+
+ sibs::SafeDeserializer deserializer((const u8*)data.data, data.size);
+ u16 avatarLength = deserializer.extract<u16>();
+ if(avatarLength >= 10 && avatarLength <= 512)
+ {
+ string avatarUrl;
+ avatarUrl.resize(avatarLength);
+ deserializer.extract((u8*)&avatarUrl[0], avatarLength);
+ if(looksLikeUrl(avatarUrl))
+ {
+ user->avatarUrl = move(avatarUrl);
+ }
+ }
+ // We dont care if there is more data to read (malicious packet), we already got all the data we need
+}
+
+static void channelChangeChannelName(Channel *channel, const StringView data, const odhtdb::Signature::PublicKey &userPublicKey)
+{
+ auto user = channel->getUserByPublicKey(userPublicKey);
+ if(!user)
+ {
+ 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;
+ }
+
+ sibs::SafeDeserializer deserializer((const u8*)data.data, data.size);
+ u16 channelNameLength = deserializer.extract<u16>();
+ if(channelNameLength > 0 && channelNameLength <= 32)
+ {
+ string channelName;
+ channelName.resize(channelNameLength);
+ deserializer.extract((u8*)&channelName[0], channelNameLength);
+ channel->setNameLocally(channelName);
+ }
+ // 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)
{
User *user = channel->getUserByPublicKey(creatorPublicKey);
@@ -97,6 +156,30 @@ static void channelAddStoredMessage(Channel *channel, const odhtdb::Hash &reques
}
break;
}
+ case ChannelDataType::CHANGE_AVATAR:
+ {
+ try
+ {
+ channelChangeUserAvatar(channel, decryptedData, creatorPublicKey);
+ }
+ catch(sibs::DeserializeException &e)
+ {
+ fprintf(stderr, "Failed to deserialize avatar change\n");
+ }
+ break;
+ }
+ case ChannelDataType::CHANGE_CHANNEL_NAME:
+ {
+ try
+ {
+ channelChangeChannelName(channel, decryptedData, creatorPublicKey);
+ }
+ catch(sibs::DeserializeException &e)
+ {
+ fprintf(stderr, "Failed to deserialize channel name change\n");
+ }
+ break;
+ }
default:
fprintf(stderr, "Got unexpected channel data type: %u\n", channelDataType);
break;
@@ -242,9 +325,9 @@ int main(int argc, char **argv)
};
database = new odhtdb::Database("bootstrap.ring.cx", 4222, Cache::getDchatDir(), callbackFuncs);
- auto addSystemMessage = [&lastFocusedTimer](const std::string &msg)
+ auto addSystemMessage = [&lastFocusedTimer](const std::string &msg, bool plainText = true)
{
- Channel::getCurrent()->addLocalMessage(msg, Channel::getCurrent()->getSystemUser());
+ Channel::getCurrent()->addSystemMessage(msg, plainText);
lastFocusedTimer.restart();
};
@@ -634,7 +717,7 @@ int main(int argc, char **argv)
msg += " ";
msg += bind.second;
}
- addSystemMessage(msg);
+ addSystemMessage(msg, false);
});
// Change nick of current user in current channel
@@ -673,11 +756,96 @@ int main(int argc, char **argv)
addSystemMessage(msg);
});
+ // Change avatar of current user in current channel
+ Command::add("avatar", [&loggedIn, &offlineChannel, addSystemMessage](const vector<string> &args)
+ {
+ if(args.size() != 1)
+ {
+ string errMsg = "Expected 1 argument for command avatar, got ";
+ errMsg += to_string(args.size());
+ errMsg += " argument(s)";
+ addSystemMessage(errMsg);
+ return;
+ }
+
+ if(!loggedIn)
+ {
+ addSystemMessage("You need to be logged in to change your avatar");
+ return;
+ }
+
+ if(Channel::getCurrent() == &offlineChannel)
+ {
+ addSystemMessage("You need to be in a channel to change your avatar");
+ return;
+ }
+
+ if(args[0].size() < 10 || args[0].size() > 512)
+ {
+ addSystemMessage("Invalid avatar url size, expected to be between 10 and 512 bytes");
+ return;
+ }
+
+ if(looksLikeUrl(args[0]))
+ {
+ Channel::getCurrent()->setAvatar(args[0]);
+ addSystemMessage("Your avatar has been changed (Note: max avatar size is 1 Mb, if your avatar is larger then it will not be visible)");
+ }
+ else
+ {
+ addSystemMessage("Avatar url needs to start with either http:// or https:// and include a dot");
+ }
+ });
+
+ // Change name of the current channel
+ Command::add("channelname", [&loggedIn, &offlineChannel, addSystemMessage](const vector<string> &args)
+ {
+ if(args.size() != 1)
+ {
+ string errMsg = "Expected 1 argument for command channelname, got ";
+ errMsg += to_string(args.size());
+ errMsg += " argument(s)";
+ addSystemMessage(errMsg);
+ return;
+ }
+
+ if(!loggedIn)
+ {
+ addSystemMessage("You need to be logged in to change channel name");
+ return;
+ }
+
+ if(Channel::getCurrent() == &offlineChannel)
+ {
+ addSystemMessage("You need to be in a channel to change channel name");
+ return;
+ }
+
+ if(args[0].size() == 0 || args[0].size() > 32)
+ {
+ addSystemMessage("Channel name has to be between 1 and 32 bytes long");
+ return;
+ }
+
+ Channel::getCurrent()->setName(args[0]);
+ string msg = "Channel name has been changed to ";
+ msg += args[0];
+ addSystemMessage(msg);
+ });
+
Command::add("clearcache", [&database](const vector<string> &args)
{
printf("Cleared cache (%d bytes)\n", database->clearCache());
});
+ string commandsMsg = "Available commands: ";
+ for(const auto &commandIt : Command::getCommands())
+ {
+ commandsMsg += "\n/";
+ commandsMsg += commandIt.first;
+ }
+ addSystemMessage(commandsMsg);
+
sf::Clock frameTimer;
while (window.isOpen())