#include "../include/Channel.hpp" #include #include #include #include using namespace std; namespace dchat { static Channel *currentChannel; Channel::Channel(const string &_name, const odhtdb::DatabaseNode &_databaseNodeInfo, User *_localUser, odhtdb::Database *_database) : database(_database), databaseNodeInfo(_databaseNodeInfo), name(_name), messageBoard(this), localUser(_localUser ? _localUser : new OfflineUser("You")) { addUserLocally(localUser); //addLocalMessage(u8"[emoji](https://discordemoji.com/assets/emoji/PepeDab.gif) deaf [emoji](https://discordemoji.com/assets/emoji/COGGERS.gif)", &systemUser, 0, odhtdb::Hash()); //addLocalMessage(u8"[emoji](https://discordemoji.com/assets/emoji/PepeDab.gif)[emoji](https://discordemoji.com/assets/emoji/COGGERS.gif)", &systemUser, 0, odhtdb::Hash()); //addLocalMessage(u8"pepedab https://discordemoji.com/assets/emoji/PepeDab.gif coggers https://discordemoji.com/assets/emoji/COGGERS.gif check out this url http://www.grandtournation.com/6808/start-date-of-the-grand-tour-season-3-confirmed-mark-your-calendars/ owo", &systemUser, 0, odhtdb::Hash()); // addLocalMessage(u8"ht clic", &systemUser, 0, odhtdb::Hash()); if(database) { database->seed(databaseNodeInfo); pingKey = odhtdb::DhtKey(*databaseNodeInfo.getRequestHash()).getPingKey(); // TODO: Ban peers that spam this key (take in account that creator of packets can be forged) pingListener = database->receiveCustomMessage(pingKey, [this](const void *data, usize size) { sibs::SafeSerializer result; try { sibs::SafeDeserializer deserializer((const u8*)data, size); u8 userPublicKeyRaw[odhtdb::PUBLIC_KEY_NUM_BYTES]; deserializer.extract(userPublicKeyRaw, odhtdb::PUBLIC_KEY_NUM_BYTES); odhtdb::Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, odhtdb::PUBLIC_KEY_NUM_BYTES); auto user = getUserByPublicKey(userPublicKey); if(!user) { // TODO: Ban peer if this happens too often return result; } string unsignedData = userPublicKey.unsign(odhtdb::DataView((void*)deserializer.getBuffer(), deserializer.getSize())); sibs::SafeDeserializer unsignedDeserializer((const u8*)unsignedData.data(), unsignedData.size()); u32 pingTimestampSec = unsignedDeserializer.extract(); if(pingTimestampSec > user->pingTimestampSec) { user->pingTimestampSec = pingTimestampSec; } } catch(std::exception &e) { fprintf(stderr, "Failed while deseralizing ping\n"); } return result; }); sendPing(database->getSyncedTimestampUtc().seconds); } } Channel::~Channel() { if(database) { database->cancelNodeListener(pingKey, pingListener); database->stopSeeding(*databaseNodeInfo.getRequestHash()); sendPing(0); } for(User *user : users) { delete user; } } User* Channel::getLocalUser() { return localUser; } SystemUser* Channel::getSystemUser() { return &systemUser; } MessageBoard& Channel::getMessageBoard() { return messageBoard; } const string& Channel::getName() const { return name; } const vector Channel::getUsers() const { return users; } OnlineUser* Channel::getUserByPublicKey(const odhtdb::Signature::PublicKey &publicKey) { auto userIt = publicKeyOnlineUsersMap.find(publicKey); if(userIt != publicKeyOnlineUsersMap.end()) return userIt->second; return nullptr; } const odhtdb::DatabaseNode& Channel::getNodeInfo() const { return databaseNodeInfo; } void Channel::addLocalMessage(const string &msg, User *owner, u64 timestampSeconds) { addLocalMessage(msg, owner, timestampSeconds, odhtdb::Hash()); } void Channel::addLocalMessage(const string &msg, User *owner, u64 timestampSeconds, const odhtdb::Hash &id) { assert(owner); if(timestampSeconds == 0) { timestampSeconds = time(NULL); } messageBoard.addMessage(new Message(owner, msg, timestampSeconds), id); } 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) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::ADD_MESSAGE); serializer.add((const u8*)msg.data(), msg.size()); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } else addLocalMessage(msg, localUser, 0, odhtdb::Hash()); } void Channel::deleteLocalMessage(const odhtdb::Hash &id, const odhtdb::Signature::PublicKey &requestedByUser) { messageBoard.deleteMessage(id, requestedByUser); } void Channel::deleteMessage(const odhtdb::Hash &id, const odhtdb::Signature::PublicKey &requestedByUser) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::DELETE_MESSAGE); serializer.add((const u8*)id.getData(), odhtdb::HASH_BYTE_SIZE); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } else deleteLocalMessage(id, requestedByUser); } void Channel::addUserLocally(User *user) { users.push_back(user); user->avatarUrl = "https://discordemoji.com/assets/emoji/HanekawaSmug.gif"; if(user->isOnlineUser()) { auto onlineUser = static_cast(user); publicKeyOnlineUsersMap[onlineUser->getPublicKey()] = onlineUser; } } bool Channel::addUser(const odhtdb::Signature::PublicKey &userId, const odhtdb::DataView &groupId) { assert(database); if(!database || localUser->type != User::Type::ONLINE_LOCAL_USER) return false; if(groupId.size != odhtdb::GROUP_ID_LENGTH) { fprintf(stderr, "Group id is wrong size. Expected to be %u bytes, was %u byte(s)\n", odhtdb::GROUP_ID_LENGTH, groupId.size); return false; } auto onlineLocalUser = static_cast(localUser); try { database->addUser(databaseNodeInfo, onlineLocalUser->keyPair, userId, groupId); return true; } catch(std::exception &e) { fprintf(stderr, "Group with id %s does not exist in channel %s or you do not have permission to add user to that group\nError: %s\n", odhtdb::bin2hex((const char*)groupId.data, groupId.size).c_str(), databaseNodeInfo.getRequestHash()->toString().c_str(), e.what()); return false; } } void Channel::replaceLocalUser(OnlineLocalUser *newOnlineLocalUser) { for(vector::iterator it = users.begin(); it != users.end(); ++it) { if(*it == localUser) { users.erase(it); delete localUser; break; } } localUser = newOnlineLocalUser; addUserLocally(newOnlineLocalUser); } void Channel::changeNick(const string &newNick) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::NICKNAME_CHANGE); serializer.add((u8)newNick.size()); serializer.add((const u8*)newNick.data(), newNick.size()); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } } void Channel::setAvatar(const string &newAvatarUrl) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(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(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); messageBoard.processEvent(event, cache); } void Channel::draw(sf::RenderWindow &window, Cache &cache) { messageBoard.draw(window, cache); chatbar.draw(window, cache); } void Channel::update() { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER && pingTimer.getElapsedTime().asMilliseconds() > 5000) { pingTimer.restart(); sendPing(database->getSyncedTimestampUtc().seconds); } } void Channel::sendPing(u32 pingTimestampSec) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { //printf("Sending ping, counter: %u\n", pingCounter); auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add((const u8*)onlineLocalUser->getPublicKey().getData(), onlineLocalUser->getPublicKey().getSize()); sibs::SafeSerializer signedSerializer; signedSerializer.add(pingTimestampSec); string signedData = onlineLocalUser->keyPair.getPrivateKey().sign(odhtdb::DataView(signedSerializer.getBuffer().data(), signedSerializer.getBuffer().size())); serializer.add((const u8*)signedData.data(), signedData.size()); database->sendCustomMessage(pingKey, move(serializer.getBuffer())); } } u32 Channel::getSyncedTimestampUtcInSec() { if(!database) return 0; return database->getSyncedTimestampUtc().seconds; } void Channel::setCurrent(Channel *channel) { currentChannel = channel; } Channel* Channel::getCurrent() { return currentChannel; } }