From dd381ac71adfcd9bcad028b277fe65e13fecaf15 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Fri, 13 Mar 2009 00:56:08 +0100 Subject: [PATCH] Introduce /exec support for running simple client-side scripts /exec myscript [param1 param2 ...] runs an executable named "myscript" in one of the acceptable script dirs ($configdir/scripts, $datadir/scripts) and pipes its stdout into IRC. This is not much, but enough to run stuff like infobash/inxi or the infamous now-playing stuff :P It's recommended to use aliases for invoking scripts, e.g. /audio could expand to /exec nowplaying, where nowplaying is your fancy script that queries your Amarok for the current song. Now keep 'em scripts coming, good ones will be included in our repo :) --- src/client/CMakeLists.txt | 2 + src/client/clientuserinputhandler.cpp | 26 +++++++- src/client/execwrapper.cpp | 93 +++++++++++++++++++++++++++ src/client/execwrapper.h | 58 +++++++++++++++++ 4 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/client/execwrapper.cpp create mode 100644 src/client/execwrapper.h diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 4780de05..5277844e 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -22,6 +22,7 @@ set(SOURCES clientsettings.cpp clientsyncer.cpp clientuserinputhandler.cpp + execwrapper.cpp irclistmodel.cpp messagefilter.cpp messagemodel.cpp @@ -43,6 +44,7 @@ set(MOC_HDRS clientirclisthelper.h clientuserinputhandler.h clientsyncer.h + execwrapper.h irclistmodel.h messagefilter.h messagemodel.h diff --git a/src/client/clientuserinputhandler.cpp b/src/client/clientuserinputhandler.cpp index 049b9d3d..a02bc189 100644 --- a/src/client/clientuserinputhandler.cpp +++ b/src/client/clientuserinputhandler.cpp @@ -24,6 +24,7 @@ #include "clientaliasmanager.h" #include "clientuserinputhandler.h" #include "clientsettings.h" +#include "execwrapper.h" #include "ircuser.h" #include "network.h" @@ -54,8 +55,27 @@ void ClientUserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const } } - AliasManager::CommandList clist = _aliasManager.processInput(bufferInfo, msg); + AliasManager::CommandList clist = Client::aliasManager()->processInput(bufferInfo, msg); + + for(int i = 0; i < clist.count(); i++) { + QString cmd = clist.at(i).second.section(' ', 0, 0).remove(0, 1).toUpper(); + if(cmd == "EXEC") + handleExec(clist.at(i).first, clist.at(i).second.section(' ', 1)); + else + emit sendInput(clist.at(i).first, clist.at(i).second); + } +} + +void ClientUserInputHandler::handleExec(const BufferInfo &bufferInfo, const QString &execString) { + QString script; + QStringList params; + if(execString.contains(' ')) { + script = execString.section(' ', 0, 0); + params = execString.section(' ', 1).trimmed().split(' '); // FIXME handle args properly, including quoted strings etc + } else + script = execString; + + ExecWrapper *exec = new ExecWrapper(this); + exec->start(bufferInfo, script, params); - for(int i = 0; i < clist.count(); i++) - emit sendInput(clist.at(i).first, clist.at(i).second); } diff --git a/src/client/execwrapper.cpp b/src/client/execwrapper.cpp new file mode 100644 index 00000000..8ef81943 --- /dev/null +++ b/src/client/execwrapper.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** +* 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 + +#include "execwrapper.h" + +#include "client.h" +#include "messagemodel.h" +#include "quassel.h" + +ExecWrapper::ExecWrapper(QObject* parent) : QObject(parent) { + connect(&_process, SIGNAL(readyReadStandardOutput()), SLOT(processReadStdout())); + connect(&_process, SIGNAL(readyReadStandardError()), SLOT(processReadStderr())); + connect(&_process, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus))); + connect(&_process, SIGNAL(error(QProcess::ProcessError)), SLOT(processError(QProcess::ProcessError))); + + connect(this, SIGNAL(stdout(QString)), SLOT(postStdout(QString))); + connect(this, SIGNAL(stderr(QString)), SLOT(postStderr(QString))); +} + +void ExecWrapper::start(const BufferInfo &info, const QString &scriptName, const QStringList& params) { + _bufferInfo = info; + _scriptName = scriptName; + foreach(QString scriptDir, Quassel::scriptDirPaths()) { + QString fileName = scriptDir + '/' + scriptName; + if(!QFile::exists(fileName)) + continue; + _process.start(fileName, params); + return; + } + emit stderr(tr("Could not find script \"%1\"").arg(scriptName)); + deleteLater(); +} + +void ExecWrapper::postStdout(const QString &msg) { + if(_bufferInfo.isValid()) + Client::userInput(_bufferInfo, msg); +} + +void ExecWrapper::postStderr(const QString &msg) { + if(_bufferInfo.isValid()) + Client::messageModel()->insertErrorMessage(_bufferInfo, msg); +} + +void ExecWrapper::processFinished(int exitCode, QProcess::ExitStatus status) { + if(status == QProcess::CrashExit) { + emit stderr(tr("Script \"%1\" crashed with exit code %2.").arg(_scriptName).arg(exitCode)); + } + + // TODO empty buffers + + deleteLater(); +} + +void ExecWrapper::processError(QProcess::ProcessError error) { + emit stderr(tr("Script \"%1\" caused error %2.").arg(_scriptName).arg(error)); +} + +void ExecWrapper::processReadStdout() { + _stdoutBuffer.append(_process.readAllStandardOutput()); + int idx; + while((idx = _stdoutBuffer.indexOf('\n')) >= 0) { + emit stdout(_stdoutBuffer.left(idx)); + _stdoutBuffer = _stdoutBuffer.mid(idx + 1); + } +} + +void ExecWrapper::processReadStderr() { + _stderrBuffer.append(_process.readAllStandardError()); + int idx; + while((idx = _stderrBuffer.indexOf('\n')) >= 0) { + emit stdout(_stderrBuffer.left(idx)); + _stderrBuffer = _stderrBuffer.mid(idx + 1); + } +} diff --git a/src/client/execwrapper.h b/src/client/execwrapper.h new file mode 100644 index 00000000..b0c52bef --- /dev/null +++ b/src/client/execwrapper.h @@ -0,0 +1,58 @@ +/*************************************************************************** +* 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 EXECWRAPPER_H_ +#define EXECWRAPPER_H_ + +#include + +#include "bufferinfo.h" + +class ExecWrapper : public QObject { + Q_OBJECT + +public: + ExecWrapper(QObject *parent = 0); + +public slots: + void start(const BufferInfo &info, const QString &scriptName, const QStringList ¶ms = QStringList()); + +signals: + void stderr(const QString &errorMsg); + void stdout(const QString &out); + +private slots: + void processReadStdout(); + void processReadStderr(); + void processFinished(int exitCode, QProcess::ExitStatus exitStatus); + void processError(QProcess::ProcessError); + + void postStdout(const QString &); + void postStderr(const QString &); + +private: + QProcess _process; + BufferInfo _bufferInfo; + QString _scriptName; + QString _stdoutBuffer; + QString _stderrBuffer; +}; + +#endif -- 2.20.1