We need alias handling both client (for /exec) and core (for perform) side.
Thus, we need it in mod_common. AliasManager is now able to process an input
line, providing a list of resulting commands. In doing this, it also ensures that
privmsgs are always prefixed by /SAY.
The client and core input handlers both pipe commands through AliasManager now. At
some point, we can think about client-only and core-only aliases, maybe.
buffermodel.cpp
buffersettings.cpp
client.cpp
+ clientaliasmanager.cpp
clientbacklogmanager.cpp
clientbufferviewconfig.cpp
clientbufferviewmanager.cpp
abstractui.h
buffermodel.h
client.h
+ clientaliasmanager.h
clientbacklogmanager.h
clientbufferviewconfig.h
clientbufferviewmanager.h
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-09 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "clientaliasmanager.h"
+
+#include "client.h"
+
+ClientAliasManager::ClientAliasManager(QObject *parent)
+ : AliasManager(parent)
+{
+
+}
+
+const Network *ClientAliasManager::network(NetworkId id) const {
+ return Client::network(id);
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-09 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef CLIENTALIASMANAGER_H
+#define CLIENTALIASMANAGER_H
+
+#include "aliasmanager.h"
+
+class ClientAliasManager : public AliasManager {
+ Q_OBJECT
+
+public:
+ explicit ClientAliasManager(QObject *parent = 0);
+
+ inline virtual const QMetaObject *syncMetaObject() const { return &AliasManager::staticMetaObject; }
+
+protected:
+ const Network *network(NetworkId) const;
+
+};
+
+#endif //CLIENTALIASMANAGER_H
}
void ClientUserInputHandler::clientConnected() {
- _aliasManager = AliasManager();
+ _aliasManager = ClientAliasManager();
Client::signalProxy()->synchronize(&_aliasManager);
connect(&_aliasManager, SIGNAL(initDone()), SLOT(initDone()));
}
void ClientUserInputHandler::clientDisconnected() {
// clear alias manager
- _aliasManager = AliasManager();
+ _aliasManager = ClientAliasManager();
_initialized = false;
}
}
// this would be the place for a client-side hook
-void ClientUserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QString &msg_) {
- QString msg = msg_;
-
+void ClientUserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QString &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;
- }
+ 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());
}
}
- // 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;
- }
+ AliasManager::CommandList clist = _aliasManager.processInput(bufferInfo, msg);
- while(!expandedCommands.isEmpty()) {
- QString command;
- if(expandedCommands[0].trimmed().toLower().startsWith("/wait")) {
- command = expandedCommands.join("; ");
- expandedCommands.clear();
- } else {
- command = expandedCommands.takeFirst();
- }
- handleUserInput(bufferInfo, command);
- }
+ for(int i = 0; i < clist.count(); i++)
+ emit sendInput(clist.at(i).first, clist.at(i).second);
}
#include <QPair>
-#include "aliasmanager.h"
+#include "clientaliasmanager.h"
#include "bufferinfo.h"
class ClientUserInputHandler : public QObject {
void completionSuffixChanged(const QVariant &);
private:
- void expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg);
-
bool _initialized;
QRegExp _nickRx;
- AliasManager _aliasManager;
- QList<QPair<BufferInfo, QString> > _inputBuffer;
+ ClientAliasManager _aliasManager;
+ AliasManager::CommandList _inputBuffer;
};
#endif
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
-#include "aliasmanager.h"
#include <QDebug>
#include <QStringList>
+#include "aliasmanager.h"
+#include "network.h"
+
AliasManager &AliasManager::operator=(const AliasManager &other) {
if(this == &other)
return *this;
-
+
SyncableObject::operator=(other);
_aliases = other._aliases;
return *this;
AliasManager::AliasList AliasManager::defaults() {
AliasList aliases;
aliases << Alias("j", "/join $0")
- << Alias("ns", "/msg nickserv $0")
- << Alias("nickserv", "/msg nickserv $0")
- << Alias("cs", "/msg chanserv $0")
- << Alias("chanserv", "/msg chanserv $0")
- << Alias("hs", "/msg hostserv $0")
- << Alias("hostserv", "/msg hostserv $0")
- << Alias("back", "/quote away");
+ << Alias("ns", "/msg nickserv $0")
+ << Alias("nickserv", "/msg nickserv $0")
+ << Alias("cs", "/msg chanserv $0")
+ << Alias("chanserv", "/msg chanserv $0")
+ << Alias("hs", "/msg hostserv $0")
+ << Alias("hostserv", "/msg hostserv $0")
+ << Alias("back", "/quote away");
return aliases;
}
+
+AliasManager::CommandList AliasManager::processInput(const BufferInfo &info, const QString &msg) {
+ CommandList result;
+ processInput(info, msg, result);
+ return result;
+}
+
+void AliasManager::processInput(const BufferInfo &info, const QString &msg_, CommandList &list) {
+ 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)
+ 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
+ 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 < count(); i++) {
+ if((*this)[i].name.toUpper() == cmd) {
+ expand((*this)[i].expansion, info, msg.section(' ', 1), list);
+ return;
+ }
+ }
+ }
+
+ list.append(qMakePair(info, msg));
+}
+
+void AliasManager::expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg, CommandList &list) {
+ const Network *net = network(bufferInfo.networkId());
+ if(!net) {
+ // 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 = net->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()); // legacy
+ command = command.replace("$channel", bufferInfo.bufferName());
+ command = command.replace("$currentnick", net->myNick()); // legacy
+ command = command.replace("$nick", net->myNick());
+ expandedCommands << command;
+ }
+
+ while(!expandedCommands.isEmpty()) {
+ QString command;
+ if(expandedCommands[0].trimmed().toLower().startsWith("/wait")) {
+ command = expandedCommands.join("; ");
+ expandedCommands.clear();
+ } else {
+ command = expandedCommands.takeFirst();
+ }
+ list.append(qMakePair(bufferInfo, command));
+ }
+}
#ifndef ALIASMANAGER_H
#define ALIASMANAGER_H
+#include <QVariantMap>
+
+#include "bufferinfo.h"
#include "syncableobject.h"
-#include <QVariantMap>
+class Network;
class AliasManager : public SyncableObject {
Q_OBJECT
public:
inline AliasManager(QObject *parent = 0) : SyncableObject(parent) { setAllowClientUpdates(true); }
AliasManager &operator=(const AliasManager &other);
-
+
struct Alias {
QString name;
QString expansion;
inline int count() const { return _aliases.count(); }
inline void removeAt(int index) { _aliases.removeAt(index); }
inline Alias &operator[](int i) { return _aliases[i]; }
- inline const Alias &operator[](int i) const { return _aliases[i]; }
+ inline const Alias &operator[](int i) const { return _aliases.at(i); }
inline const AliasList &aliases() const { return _aliases; }
static AliasList defaults();
+ typedef QList<QPair<BufferInfo, QString> > CommandList;
+
+ CommandList processInput(const BufferInfo &info, const QString &message);
+
public slots:
virtual QVariantMap initAliases() const;
virtual void initSetAliases(const QVariantMap &aliases);
virtual void addAlias(const QString &name, const QString &expansion);
-
+
protected:
void setAliases(const QList<Alias> &aliases) { _aliases = aliases; }
+ virtual const Network *network(NetworkId) const = 0; // core and client require different access
signals:
void aliasAdded(const QString &name, const QString &expansion);
-
+
private:
+ void processInput(const BufferInfo &info, const QString &message, CommandList &previousCommands);
+ void expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg, CommandList &previousCommands);
+
AliasList _aliases;
};
#include "corealiasmanager.h"
#include "core.h"
+#include "corenetwork.h"
#include "coresession.h"
CoreAliasManager::CoreAliasManager(CoreSession *parent)
return;
}
- QVariantMap aliases = Core::getUserSetting(session->user(), "Aliases").toMap();
initSetAliases(Core::getUserSetting(session->user(), "Aliases").toMap());
if(isEmpty())
loadDefaults();
}
-
CoreAliasManager::~CoreAliasManager() {
CoreSession *session = qobject_cast<CoreSession *>(parent());
if(!session) {
Core::setUserSetting(session->user(), "Aliases", initAliases());
}
+const Network *CoreAliasManager::network(NetworkId id) const {
+ return qobject_cast<CoreSession *>(parent())->network(id);
+}
void CoreAliasManager::loadDefaults() {
foreach(Alias alias, AliasManager::defaults()) {
Q_OBJECT
public:
- CoreAliasManager(CoreSession *parent = 0);
+ explicit CoreAliasManager(CoreSession *parent);
~CoreAliasManager();
inline virtual const QMetaObject *syncMetaObject() const { return &AliasManager::staticMetaObject; }
+protected:
+ const Network *network(NetworkId) const;
+
private:
void loadDefaults();
-
+
};
#endif //COREALIASMANAGER_H
{
}
-void UserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QString &msg_) {
- if(msg_.isEmpty())
+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)
- 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
- cmd = QString("SAY");
- } else {
- cmd = msg.section(' ', 0, 0).remove(0, 1).toUpper();
- msg = msg.section(' ', 1);
+ AliasManager::CommandList list = coreSession()->aliasManager().processInput(bufferInfo, msg);
+
+ for(int i = 0; i < list.count(); i++) {
+ QString cmd = list.at(i).second.section(' ', 0, 0).remove(0, 1).toUpper();
+ QString payload = list.at(i).second.section(' ', 1);
+ handle(cmd, Q_ARG(BufferInfo, list.at(i).first), Q_ARG(QString, payload));
}
- handle(cmd, Q_ARG(BufferInfo, bufferInfo), Q_ARG(QString, msg));
}
// ====================
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()) {
- expand(coreSession()->aliasManager()[i].expansion, bufferInfo, msg);
- return;
- }
- }
+ Q_UNUSED(bufferInfo);
emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", QString("Error: %1 %2").arg(cmd, msg));
}
-void UserInputHandler::expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg) {
- 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);
- }
-}
-
-
void UserInputHandler::putPrivmsg(const QByteArray &target, const QByteArray &message) {
static const char *cmd = "PRIVMSG";
int overrun = lastParamOverrun(cmd, QList<QByteArray>() << message);
void timerEvent(QTimerEvent *event);
private:
- void expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg);
void banOrUnban(const BufferInfo &bufferInfo, const QString &text, bool ban);
void putPrivmsg(const QByteArray &target, const QByteArray &message);
int lastParamOverrun(const QString &cmd, const QList<QByteArray> ¶ms);
" - <b>$i..</b> represents all parameters from i on separated by spaces.<br />"
" - <b>$i:hostname</b> represents the hostname of the user identified by the i'th parameter or a * if unknown.<br />"
" - <b>$0</b> the whole string.<br />"
- " - <b>$currentnick</b> your current nickname<br />"
- " - <b>$channelname</b> the name of the selected channel<br /><br />"
+ " - <b>$nick</b> your current nickname<br />"
+ " - <b>$channel</b> the name of the selected channel<br /><br />"
"Multiple commands can be separated with semicolons<br /><br />"
"<b>Example:</b> \"Test $1; Test $2; Test All $0\" will be expanded to three separate messages \"Test 1\", \"Test 2\" and \"Test All 1 2 3\" when called like /test 1 2 3";
default:
QString newValue = value.toString();
if(newValue.isEmpty())
return false;
-
+
switch(index.column()) {
case 0:
if(aliasManager().contains(newValue)) {
}
void AliasesModel::clientConnected() {
- _aliasManager = AliasManager();
+ _aliasManager = ClientAliasManager();
Client::signalProxy()->synchronize(&_aliasManager);
connect(&_aliasManager, SIGNAL(initDone()), this, SLOT(initDone()));
connect(&_aliasManager, SIGNAL(updated(const QVariantMap &)), this, SLOT(revert()));
void AliasesModel::clientDisconnected() {
// clear alias managers
- _aliasManager = AliasManager();
- _clonedAliasManager = AliasManager();
+ _aliasManager = ClientAliasManager();
+ _clonedAliasManager = ClientAliasManager();
reset();
emit modelReady(false);
}
#include <QAbstractItemModel>
#include <QPointer>
-#include "aliasmanager.h"
+#include "clientaliasmanager.h"
class AliasesModel : public QAbstractItemModel {
Q_OBJECT
signals:
void configChanged(bool);
void modelReady(bool);
-
+
private:
- AliasManager _aliasManager;
- AliasManager _clonedAliasManager;
+ ClientAliasManager _aliasManager;
+ ClientAliasManager _clonedAliasManager;
bool _configChanged;
const AliasManager &aliasManager() const;