From 62ff1c782104674e29df9e4744d9b41133b1eb7d Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Mon, 2 Mar 2009 08:50:49 +0100 Subject: [PATCH] Move alias handling into the client This allows for aliasing client-side commands such as the soon-to-exist /exec command. Note that the core-side code is still there, to be removed as soon as we break protocol again. --- src/client/clientuserinputhandler.cpp | 131 ++++++++++++++++++++++++-- src/client/clientuserinputhandler.h | 11 +++ src/core/userinputhandler.cpp | 3 + 3 files changed, 135 insertions(+), 10 deletions(-) diff --git a/src/client/clientuserinputhandler.cpp b/src/client/clientuserinputhandler.cpp index 296fc1cb..bb6e3ce2 100644 --- a/src/client/clientuserinputhandler.cpp +++ b/src/client/clientuserinputhandler.cpp @@ -26,10 +26,38 @@ #include "ircuser.h" #include "network.h" -ClientUserInputHandler::ClientUserInputHandler(QObject *parent) : QObject(parent) { +ClientUserInputHandler::ClientUserInputHandler(QObject *parent) +: QObject(parent), + _initialized(false) +{ TabCompletionSettings s; s.notify("CompletionSuffix", this, SLOT(completionSuffixChanged(QVariant))); completionSuffixChanged(s.completionSuffix()); + + // we need this signal for future connects to reset the data; + connect(Client::instance(), SIGNAL(connected()), SLOT(clientConnected())); + connect(Client::instance(), SIGNAL(disconnected()), SLOT(clientDisconnected())); + if(Client::isConnected()) + clientConnected(); +} + +void ClientUserInputHandler::clientConnected() { + _aliasManager = AliasManager(); + Client::signalProxy()->synchronize(&_aliasManager); + connect(&_aliasManager, SIGNAL(initDone()), SLOT(initDone())); +} + +void ClientUserInputHandler::clientDisconnected() { + // clear alias manager + _aliasManager = AliasManager(); + _initialized = false; +} + +void ClientUserInputHandler::initDone() { + _initialized = true; + for(int i = 0; i < _inputBuffer.count(); i++) + handleUserInput(_inputBuffer.at(i).first, _inputBuffer.at(i).second); + _inputBuffer.clear(); } void ClientUserInputHandler::completionSuffixChanged(const QVariant &v) { @@ -40,17 +68,100 @@ void ClientUserInputHandler::completionSuffixChanged(const QVariant &v) { } // this would be the place for a client-side hook -void ClientUserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QString &msg) { - // check if we addressed a user and update its timestamp in that case - if(bufferInfo.type() == BufferInfo::ChannelBuffer) { - if(!msg.startsWith('/')) { - if(_nickRx.indexIn(msg) == 0) { - const Network *net = Client::network(bufferInfo.networkId()); - IrcUser *user = net ? net->ircUser(_nickRx.cap(1)) : 0; - if(user) - user->setLastSpokenTo(bufferInfo.bufferId(), QDateTime::currentDateTime().toUTC()); +void ClientUserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QString &msg_) { + QString msg = msg_; + + if(!_initialized) { // aliases not yet synced + _inputBuffer.append(qMakePair(bufferInfo, msg)); + return; + } + + // leading slashes indicate there's a command to call unless there is another one in the first section (like a path /proc/cpuinfo) + int secondSlashPos = msg.indexOf('/', 1); + int firstSpacePos = msg.indexOf(' '); + if(!msg.startsWith('/') || (secondSlashPos != -1 && (secondSlashPos < firstSpacePos || firstSpacePos == -1))) { + if(msg.startsWith("//")) + msg.remove(0, 1); // //asdf is transformed to /asdf + + // check if we addressed a user and update its timestamp in that case + if(bufferInfo.type() == BufferInfo::ChannelBuffer) { + if(!msg.startsWith('/')) { + if(_nickRx.indexIn(msg) == 0) { + const Network *net = Client::network(bufferInfo.networkId()); + IrcUser *user = net ? net->ircUser(_nickRx.cap(1)) : 0; + if(user) + user->setLastSpokenTo(bufferInfo.bufferId(), QDateTime::currentDateTime().toUTC()); + } + } + } + msg.prepend("/SAY "); // make sure we only send proper commands to the core + + } else { + // check for aliases + QString cmd = msg.section(' ', 0, 0).remove(0, 1).toUpper(); + for(int i = 0; i < _aliasManager.count(); i++) { + if(_aliasManager[i].name.toLower() == cmd.toLower()) { + expand(_aliasManager[i].expansion, bufferInfo, msg.section(' ', 1)); + return; } } } + + // all clear, send off to core. emit sendInput(bufferInfo, msg); } + +void ClientUserInputHandler::expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg) { + const Network *network = Client::network(bufferInfo.networkId()); + if(!network) { + // FIXME send error as soon as we have a method for that! + return; + } + + QRegExp paramRangeR("\\$(\\d+)\\.\\.(\\d*)"); + QStringList commands = alias.split(QRegExp("; ?")); + QStringList params = msg.split(' '); + QStringList expandedCommands; + for(int i = 0; i < commands.count(); i++) { + QString command = commands[i]; + + // replace ranges like $1..3 + if(!params.isEmpty()) { + int pos; + while((pos = paramRangeR.indexIn(command)) != -1) { + int start = paramRangeR.cap(1).toInt(); + bool ok; + int end = paramRangeR.cap(2).toInt(&ok); + if(!ok) { + end = params.count(); + } + if(end < start) + command = command.replace(pos, paramRangeR.matchedLength(), QString()); + else { + command = command.replace(pos, paramRangeR.matchedLength(), QStringList(params.mid(start - 1, end - start + 1)).join(" ")); + } + } + } + + for(int j = params.count(); j > 0; j--) { + IrcUser *ircUser = network->ircUser(params[j - 1]); + command = command.replace(QString("$%1:hostname").arg(j), ircUser ? ircUser->host() : QString("*")); + command = command.replace(QString("$%1").arg(j), params[j - 1]); + } + command = command.replace("$0", msg); + command = command.replace("$channelname", bufferInfo.bufferName()); + command = command.replace("$currentnick", network->myNick()); + expandedCommands << command; + } + + while(!expandedCommands.isEmpty()) { + QString command; + if(expandedCommands[0].trimmed().toLower().startsWith("/wait")) { + command = expandedCommands.join("; "); + expandedCommands.clear(); + } else { + command = expandedCommands.takeFirst(); + } + handleUserInput(bufferInfo, command); + } +} diff --git a/src/client/clientuserinputhandler.h b/src/client/clientuserinputhandler.h index bc1b46cc..3b2749f3 100644 --- a/src/client/clientuserinputhandler.h +++ b/src/client/clientuserinputhandler.h @@ -21,6 +21,9 @@ #ifndef CLIENTUSERINPUTHANDLER_H_ #define CLIENTUSERINPUTHANDLER_H_ +#include + +#include "aliasmanager.h" #include "bufferinfo.h" class ClientUserInputHandler : public QObject { @@ -36,10 +39,18 @@ signals: void sendInput(const BufferInfo &, const QString &); private slots: + void clientConnected(); + void clientDisconnected(); + void initDone(); void completionSuffixChanged(const QVariant &); private: + void expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg); + + bool _initialized; QRegExp _nickRx; + AliasManager _aliasManager; + QList > _inputBuffer; }; #endif diff --git a/src/core/userinputhandler.cpp b/src/core/userinputhandler.cpp index 8fe87b03..87d37df1 100644 --- a/src/core/userinputhandler.cpp +++ b/src/core/userinputhandler.cpp @@ -36,6 +36,8 @@ UserInputHandler::UserInputHandler(CoreNetwork *parent) void UserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QString &msg_) { if(msg_.isEmpty()) return; + + // FIXME: With protocol >= v10, this is all done in the client -> remove QString cmd; QString msg = msg_; // leading slashes indicate there's a command to call unless there is another one in the first section (like a path /proc/cpuinfo) @@ -391,6 +393,7 @@ void UserInputHandler::handleWhowas(const BufferInfo &bufferInfo, const QString emit putCmd("WHOWAS", serverEncode(msg.split(' '))); } +// FIXME: Remove alias handling as soon as core protocol >= v10 void UserInputHandler::defaultHandler(QString cmd, const BufferInfo &bufferInfo, const QString &msg) { for(int i = 0; i < coreSession()->aliasManager().count(); i++) { if(coreSession()->aliasManager()[i].name.toLower() == cmd.toLower()) { -- 2.20.1