summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Mittendrein <git@maxmitti.tk>2016-05-05 21:14:32 +0200
committerMarkus Mittendrein <git@maxmitti.tk>2016-05-05 21:14:32 +0200
commit6eadd30b19903d395867b6b487ce6a7a7541237b (patch)
treec0a7f5fc08add51d3258958ab678284bd793bb3a
parent3f4853ebb4132e25fbc9c42e6cecc2791b67d52e (diff)
downloadmanager-6eadd30b19903d395867b6b487ce6a7a7541237b.tar.gz
manager-6eadd30b19903d395867b6b487ce6a7a7541237b.zip
Add DCC Chat ability
-rw-r--r--src/CRSMConfig.hpp14
-rw-r--r--src/ClientInfo.hpp2
-rw-r--r--src/crsm.cpp350
-rw-r--r--src/crsm.hpp22
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);