diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CRSMConfig.hpp | 14 | ||||
| -rw-r--r-- | src/ClientInfo.hpp | 2 | ||||
| -rw-r--r-- | src/crsm.cpp | 350 | ||||
| -rw-r--r-- | src/crsm.hpp | 22 |
4 files changed, 281 insertions, 107 deletions
diff --git a/src/CRSMConfig.hpp b/src/CRSMConfig.hpp index 22458e9..a566092 100644 --- a/src/CRSMConfig.hpp +++ b/src/CRSMConfig.hpp @@ -71,10 +71,16 @@ public: String IngameChannel = "#crsm-ingame"; String QuitMessage = "Dedicated Clonk server powered by CRSM"; Integer ReconnectDelay = 10; - String ScenListMessage = "A list of available scenarios is available ingame or in a lobby."; + String ScenListMessage = "A list of available scenarios is available over DCC, ingame or in a lobby."; Map(String, String) Moderators; } IRC; + struct { + Boolean Use = true; + Integer Port = 9373; + String Address = "0.0.0.0"; + } DCC; + struct { struct { @@ -161,6 +167,12 @@ public: + ConfigVal(DCC.Use), + ConfigVal(DCC.Address), + ConfigVal(DCC.Port), + + + ConfigVal(Auto.ProcessManager.ReattachId), diff --git a/src/ClientInfo.hpp b/src/ClientInfo.hpp index 5ab1160..cdd8ff4 100644 --- a/src/ClientInfo.hpp +++ b/src/ClientInfo.hpp @@ -24,6 +24,8 @@ public: ClientInterface interface = Clonk; QString nick = ""; + bool dcc = false; + int CUID = 0; QString pcName = ""; bool activated = false; diff --git a/src/crsm.cpp b/src/crsm.cpp index 1503d0e..bf46835 100644 --- a/src/crsm.cpp +++ b/src/crsm.cpp @@ -7,6 +7,7 @@ #include <QThread> #include <QTimer> #include <QRegularExpression> +#include <QHostInfo> #define MGMT_BUFFER_FILENAME "CRSM-MGMT-Buffer" @@ -30,6 +31,8 @@ CRSM::CRSM(QObject *parent) : connect(&managementServer, SIGNAL(newConnection()), this, SLOT(newManagementConnection())); managementServer.listen(QHostAddress::LocalHostIPv6, Config.CRSM.ManagementPort); + connect(&dccServer, SIGNAL(newConnection()), this, SLOT(newDCCConnection())); + listC4Folders(); readScenarios(); @@ -116,7 +119,7 @@ void CRSM::readServerOutput() foreach(const QString &mess, what.split("\n", QString::SkipEmptyParts)) foreach(const QString &mod, ircModIOList) { - sendIrcMessage(mess, mod, false, true); + sendIrcMessage(mess, mod, false, true, true); } } out(what.trimmed() + "\n"); @@ -396,92 +399,13 @@ void CRSM::ircMessageReceived(IrcMessage *message) else if(message->type() == IrcMessage::Private) { IrcPrivateMessage* privMessage = (IrcPrivateMessage*)message; - QString target = privMessage->target(); - Log.ircLog(privMessage->content(), privMessage->nick(), privMessage->isPrivate(), privMessage->target(), privMessage->isAction(), false); - if(message->isOwn()) - { - return; - } - Log.ircUserLog(privMessage->content(), ClientInfo::ircClient(privMessage->nick(), privMessage->target()), privMessage->isPrivate(), privMessage->target(), privMessage->isAction(), false); - if(target == connection->nickName()) - target = message->nick(); - QString mess = privMessage->content(); - const ClientInfo& client = ClientInfo::ircClient(message->nick(), target); - if(client == Session.IRC.Admin) + if(!privMessage->isRequest()) { - checkActivity(Session.IRC.Admin); - } - - QString command; - - if(target == Config.IRC.IngameChannel && Session.IRC.UseIngameChat) - { - if(privMessage->isAction()) - { - writeToServer("/me [IRC] " + message->nick() + " " + mess + "\n"); - } - else - { - writeToServer("[IRC]<" + message->nick() + "> " + mess + "\n"); - } - } - else if(!privMessage->isAction() && !(command = getCommand(mess)).isEmpty()) - { - // command is handled afterwards - } - else if(privMessage->isPrivate()) - { - bool handled = false; - if(ircMods.contains(target)) - { - if(aliasWishEditor == target) - { - editAliasWishes(mess); - handled = true; - } - else if(ircModIOList.contains(message->nick())) - { - QString writeMessage; - if(mess.at(0) == '\\' || mess.at(0) == '/') - { - mess[0] = '/'; - } - else - { - writeMessage = "[IRC]<" + message->nick() + "> "; - } - writeMessage += mess; - writeToServer(writeMessage + "\n"); - handled = true; - } - } - if(!handled) - { - command = mess; - } - } - - if(!command.isEmpty()) - { - if(!cmd(command, client)) - { - respond(client, "Unbekannter Befehl: \"" + command + "\"!"); - } - } - - if(ircModWatchList.length() > 0) - { - QString watchMsg = "<"; - if(!privMessage->isPrivate()) - { - watchMsg += target + ":"; - } - watchMsg += message->nick() + "> " + mess + "\n"; - - foreach(const ClientInfo& client, ircModWatchList) - { - respond(client, watchMsg, RespondType::PrivateNotice); - } + QString target = privMessage->target(); + if(target == connection->nickName()) + target = message->nick(); + const ClientInfo& client = ClientInfo::ircClient(message->nick(), target); + handleIrcMessage(client, privMessage->content(), privMessage->target(), privMessage->isPrivate(), privMessage->isAction(), message->isOwn()); } } else if(message->isOwn()) @@ -667,6 +591,61 @@ void CRSM::managementConnectionDisconnected() } } +// modified version of http://stackoverflow.com/a/18866593 +QString GetRandomString(int length = 5) +{ + static const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + + QString randomString; + for(int i=0; i<length; ++i) + { + int index = qrand() % possibleCharacters.length(); + QChar nextChar = possibleCharacters.at(index); + randomString.append(nextChar); + } + return randomString; +} + +void CRSM::newDCCData() +{ + QTcpSocket* socket = (QTcpSocket*)sender(); + const DCCConnection& connection = dccSocketConnections[socket]; + QString message = QString::fromUtf8(socket->readLine()); + if(message.endsWith('\n')) + { + message.chop(1); + } + handleIrcMessage(connection.client, message, Config.IRC.Nick, true, false); +} + +void CRSM::newDCCConnection() +{ + QTcpSocket* socket = dccServer.nextPendingConnection(); + QString identifier; + do + { + identifier = GetRandomString(); + } + while(dccConnectionIdentifiers.contains(identifier)); + + dccConnectionIdentifiers.insert(identifier, socket); + connect(socket, SIGNAL(disconnected()), this, SLOT(disconnectedDCCConnection())); + + socket->write(QString("Weise diese Verbindung deinem IRC-Nick zu, indem du diesen Befehl eingibst: /msg " + Config.IRC.Nick + " dcc identify " + identifier + "\n").toUtf8()); +} + +void CRSM::disconnectedDCCConnection() +{ + QTcpSocket* socket = (QTcpSocket*)sender(); + if(dccSocketConnections.contains(socket)) + { + dccNickConnections.remove(dccSocketConnections[socket].client.nick); + dccSocketConnections.remove(socket); + } + + dccConnectionIdentifiers.remove(dccConnectionIdentifiers.key(socket)); +} + void CRSM::updateNextAutoScens() { if(autolist.length() <= 0) return; @@ -1093,7 +1072,7 @@ void CRSM::informModsAboutAliasWish() { foreach(const QString& mod, ircMods) { - sendIrcMessage("Ein neuer Aliaswunsch ist verfügbar. Insgesamt verfügbar: " + QString::number(Config.Auto.Hosting.AliasWishes.size()), mod, false, true); + sendIrcMessage("Ein neuer Aliaswunsch ist verfügbar. Insgesamt verfügbar: " + QString::number(Config.Auto.Hosting.AliasWishes.size()), mod, false, true, true); } } @@ -1444,6 +1423,13 @@ void CRSM::setupCmds() addCommand("irc say", &CRSM::ircSay, IRC | Management, Moderator, "Schreibt die <Nachricht> an <Channel¦Nick>.", "<Channel¦Nick> <Nachricht>"); addCommand("irc watch", &CRSM::ircWatch, IRC | Management, Moderator, "Sendet alle Nachrichten, die der Server im IRC-Netzwerk aufschnappt, an Moderatoren, die watch aktiviert haben.", "<on¦off>"); addCommand("irc raw", &CRSM::ircRaw, IRC | Management, Moderator, "Sendet, analog zu /QUOTE oder /RAW, eine <Rohnachricht> an den IRC-Server.", "<Rohnachricht>"); + + if(Config.DCC.Use) + { + addCommandGroup("dcc", IRC, User, "Die dcc-Befehlsgruppe bietet die Möglichkeit zum Steuern eines DCC-Chats."); + addCommand("dcc connect", &CRSM::dccConnect, IRC, User, "Sendet eine DCC-Chat-Verbindungsanfrage."); + addCommand("dcc identify", &CRSM::dccIdentify, IRC, User, "Mit dem Befehl wird eine neu geöffnete DCC-Verbindung einem Benutzer zugewiesen.", "<Verbindungs-ID>"); + } } addCommandGroup("exit", Management, UserType::Max, "Beendet den Server Mananger.", "Beendet den Server Mananger und falls ein Clonk-Server läuft auch diesen. Die Unterbefehle ermöglichen außerdem diverse Zusatzvarienten des Beendens.", &CRSM::exit); @@ -1472,7 +1458,7 @@ void CRSM::setupCmds() addCommand("relist", &CRSM::relist, Management, UserType::Max, "Durchsucht veränderte Rundenordner erneut."); } -void CRSM::respond(const ClientInfo &client, const QString &message, const RespondType type) +void CRSM::respond(const ClientInfo &client, const QString &message, const RespondType type, bool allowDCCNotice) { switch(client.interface) { @@ -1490,7 +1476,7 @@ void CRSM::respond(const ClientInfo &client, const QString &message, const Respo break; case RespondType::Notice: case RespondType::PrivateNotice: - sendIrcMessage(line, type == Notice ? client.target : client.nick, false, true); + sendIrcMessage(line, type == Notice ? client.target : client.nick, false, true, allowDCCNotice); break; } } @@ -1785,6 +1771,7 @@ void CRSM::applyConfig() } else { + Config.DCC.Use = false; Config.IRC.UseIngameChat = false; if(connection != nullptr) { @@ -1792,6 +1779,18 @@ void CRSM::applyConfig() } } + if(Config.DCC.Use) + { + if(!dccServer.isListening()) + { + dccServer.listen(QHostAddress::Any, Config.DCC.Port); + } + } + else + { + dccServer.close(); + } + if(!Config.IRC.UseIngameChat) { Session.IRC.UseIngameChat = false; @@ -1937,32 +1936,43 @@ QString CRSM::clientModName(const ClientInfo &client) } } -void CRSM::sendIrcMessage(const QString& message, const QString& target, bool action, bool notice) +void CRSM::sendIrcMessage(const QString& message, const QString& target, bool action, bool notice, bool noticeOrDCC) { + QTcpSocket* dccSocket = nullptr; + notice = notice || noticeOrDCC; bool query = !isChannelName(target); Log.ircLog(message, (query ? target : connection->nickName()), query, target, action, notice, query); - /*if(cmd->type() == IrcCommand::Message || cmd->type() == IrcCommand::CtcpAction) - { - IrcPrivateMessage* messageCmd = (IrcPrivateMessage*)cmd; - Log.ircLog(messageCmd->content(), connection->nickName(), !isChannelName(messageCmd->parameter(0)), messageCmd->parameter(0), cmd->type() == IrcCommand::CtcpAction, false); - } - else if(cmd->type() == IrcCommand::Notice) - { - IrcNoticeMessage* noticeCmd = (IrcNoticeMessage*)cmd; - Log.ircLog(noticeCmd->content(), connection->nickName(), !isChannelName(noticeCmd->parameter(0)), noticeCmd->parameter(0), false, true); - } - connection->sendCommand(cmd);*/ if(action) { connection->sendCommand(IrcCommand::createCtcpAction(target, message)); } else if(notice) { - connection->sendCommand(IrcCommand::createNotice(target, message)); + if(noticeOrDCC && dccNickConnections.contains(target)) + { + dccSocket = dccNickConnections[target].socket; + } + else + { + connection->sendCommand(IrcCommand::createNotice(target, message)); + } } else { - connection->sendCommand(IrcCommand::createMessage(target, message)); + if(query && dccNickConnections.contains(target)) + { + dccSocket = dccNickConnections[target].socket; + } + else + { + connection->sendCommand(IrcCommand::createMessage(target, message)); + } + } + + if(dccSocket) + { + dccSocket->write(message.toUtf8()); + dccSocket->write("\n"); } } @@ -2014,6 +2024,96 @@ bool CRSM::greetAllowed(const ClientInfo &client) return false; } +void CRSM::handleIrcMessage(const ClientInfo &client, QString message, const QString &target, bool privateMessage, bool action, bool own) +{ + if(message.isEmpty()) + { + return; + } + Log.ircLog(message, client.nick, privateMessage, target, action, false); + if(own) + { + return; + } + Log.ircUserLog(message, client, true, target, action, false); + if(client == Session.IRC.Admin) + { + checkActivity(Session.IRC.Admin); + } + + QString command; + + if(target == Config.IRC.IngameChannel && Session.IRC.UseIngameChat) + { + if(action) + { + writeToServer("/me [IRC] " + client.nick + " " + message + "\n"); + } + else + { + writeToServer("[IRC]<" + client.nick + "> " + message + "\n"); + } + } + else if(!action && !(command = getCommand(message)).isEmpty()) + { + // command is handled afterwards + } + else if(privateMessage) + { + bool handled = false; + if(ircMods.contains(client.nick)) + { + if(aliasWishEditor == client.nick) + { + editAliasWishes(message); + handled = true; + } + else if(ircModIOList.contains(client.nick)) + { + QString writeMessage; + if(message.at(0) == '\\' || message.at(0) == '/') + { + message[0] = '/'; + } + else + { + writeMessage = "[IRC]<" + client.nick + "> "; + } + writeMessage += message; + writeToServer(writeMessage + "\n"); + handled = true; + } + } + if(!handled) + { + command = message; + } + } + + if(!command.isEmpty()) + { + if(!cmd(command, client)) + { + respond(client, "Unbekannter Befehl: \"" + command + "\"!"); + } + } + + if(ircModWatchList.length() > 0) + { + QString watchMsg = "<"; + if(!privateMessage) + { + watchMsg += target + ":"; + } + watchMsg += client.nick + "> " + message + "\n"; + + foreach(const ClientInfo& client, ircModWatchList) + { + respond(client, watchMsg, RespondType::PrivateNotice, true); + } + } +} + CMD_FUNCTION_IMPL(help) bool recursive = args.startsWith("recursive ") && client.interface == Management; ClientInterface interface = client.interface; @@ -2282,7 +2382,7 @@ CMD_FUNCTION_IMPL(host) } CMD_FUNCTION_IMPL(list) - if(client.interface == Clonk || client.interface == Management) + if(client.interface == Clonk || client.interface == Management || client.dcc) { respond(client, listScenarios(args)); } @@ -3087,6 +3187,48 @@ CMD_FUNCTION_IMPL(packsScenariosList) return Success; } +CMD_FUNCTION_IMPL(dccConnect) + if(dccNickConnections.contains(client.nick)) + { + respond(client, "Diesem Nick ist bereits eine DCC-Verbindung zugewiesen.\n"); + return Success; + } + QHostInfo info = QHostInfo::fromName(Config.DCC.Address); + connection->sendCommand(IrcCommand::createCtcpRequest(client.nick, "DCC CHAT chat " + QString::number(info.addresses().first().toIPv4Address()) + " " + QString::number(Config.DCC.Port))); + return Success; +} + +CMD_FUNCTION_IMPL(dccIdentify) + if(dccNickConnections.contains(client.nick)) + { + respond(client, "Diesem Nick ist bereits eine DCC-Verbindung zugewiesen.\n"); + return Success; + } + if(args.isEmpty()) + { + respond(client, "Es wird eine Verbindungs-ID benötigt.\n"); + return SyntaxFail; + } + QTcpSocket* socket = dccConnectionIdentifiers.value(args, nullptr); + if(socket == nullptr) + { + respond(client, "Unbekannte bzw. falsche Verbindungs-ID: \"" + args + "\"\n"); + } + else + { + dccConnectionIdentifiers.remove(args); + DCCConnection conn; + conn.client = client; + conn.socket = socket; + conn.client.dcc = true; + dccNickConnections.insert(client.nick, conn); + dccSocketConnections.insert(socket, conn); + respond(client, "Identifizierung erfolgreich!\n"); + connect(socket, SIGNAL(readyRead()), this, SLOT(newDCCData())); + } + return Success; +} + IRC_CHECK_CALLBACK_IMPL(ircSayQuery) if(status <= 0) { diff --git a/src/crsm.hpp b/src/crsm.hpp index 57a0cc9..0a23d7a 100644 --- a/src/crsm.hpp +++ b/src/crsm.hpp @@ -136,6 +136,10 @@ private slots: void newManagementData(); void managementConnectionDisconnected(); + void newDCCConnection(); + void newDCCData(); + void disconnectedDCCConnection(); + void updateNextAutoScens(); void reconnectIrc(); void enableAutoHosting(); @@ -230,6 +234,16 @@ private: bool ok = false; bool hostingIsErrorDeactivated = false; + struct DCCConnection { + ClientInfo client; + QTcpSocket* socket; + }; + + QMap<QTcpSocket*, DCCConnection> dccSocketConnections; + QMap<QString, DCCConnection> dccNickConnections; + QMap<QString, QTcpSocket*> dccConnectionIdentifiers; + QTcpServer dccServer; + void startScen(const ScenarioSettings& scen, QStringList); void readConfig(); void readScenarios(); @@ -263,7 +277,7 @@ private: void setupCmds(); - void respond(const ClientInfo& client, const QString& message, const RespondType type = Normal); + void respond(const ClientInfo& client, const QString& message, const RespondType type = Normal, bool allowDCCNotice = false); void ircCheckUserStatus(const ClientInfo& requester, const ClientInfo& subject, IrcCheckCallback callback); void setIngameAdmin(const ClientInfo &client, const QString& newAdmin); @@ -293,8 +307,9 @@ private: void removeCommandSuffixes(QString& command); void substituteCommandAlias(QString& command); QString clientModName(const ClientInfo& client); - void sendIrcMessage(const QString& message, const QString& target, bool action, bool notice); + void sendIrcMessage(const QString& message, const QString& target, bool action, bool notice, bool noticeOrDCC = false); bool greetAllowed(const ClientInfo& client); + void handleIrcMessage(const ClientInfo& client, QString message, const QString& target, bool privateMessage, bool action, bool own = false); CMD_FUNCTION(help); CMD_FUNCTION(passToClonk); @@ -360,6 +375,9 @@ private: CMD_FUNCTION(packsScenariosDelete); CMD_FUNCTION(packsScenariosList); + CMD_FUNCTION(dccConnect); + CMD_FUNCTION(dccIdentify); + IRC_CHECK_CALLBACK(ircSetAdmin); IRC_CHECK_CALLBACK(ircModCmd); |
