1 | /*************************************************************************** |
2 | * Copyright (C) 2005-2014 by the Quassel Project * |
3 | * devel@quassel-irc.org * |
4 | * * |
5 | * This program is free software; you can redistribute it and/or modify * |
6 | * it under the terms of the GNU General Public License as published by * |
7 | * the Free Software Foundation; either version 2 of the License, or * |
8 | * (at your option) version 3. * |
9 | * * |
10 | * This program is distributed in the hope that it will be useful, * |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
13 | * GNU General Public License for more details. * |
14 | * * |
15 | * You should have received a copy of the GNU General Public License * |
16 | * along with this program; if not, write to the * |
17 | * Free Software Foundation, Inc., * |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
19 | ***************************************************************************/ |
20 | |
21 | #include <QFile> |
22 | #include <QTextCodec> |
23 | |
24 | #include "execwrapper.h" |
25 | |
26 | #include "client.h" |
27 | #include "messagemodel.h" |
28 | #include "quassel.h" |
29 | |
30 | ExecWrapper::ExecWrapper(QObject *parent) : QObject(parent) |
31 | { |
32 | connect(&_process, SIGNAL(readyReadStandardOutput()), SLOT(processReadStdout())); |
33 | connect(&_process, SIGNAL(readyReadStandardError()), SLOT(processReadStderr())); |
34 | connect(&_process, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus))); |
35 | connect(&_process, SIGNAL(error(QProcess::ProcessError)), SLOT(processError(QProcess::ProcessError))); |
36 | |
37 | connect(this, SIGNAL(output(QString)), SLOT(postStdout(QString))); |
38 | connect(this, SIGNAL(error(QString)), SLOT(postStderr(QString))); |
39 | } |
40 | |
41 | |
42 | void ExecWrapper::start(const BufferInfo &info, const QString &command) |
43 | { |
44 | _bufferInfo = info; |
45 | QString params; |
46 | |
47 | QRegExp rx("^\\s*(\\S+)(\\s+(.*))?$" ); |
48 | if (!rx.exactMatch(command)) { |
49 | emit error(tr("Invalid command string for /exec: %1" ).arg(command)); |
50 | } |
51 | else { |
52 | _scriptName = rx.cap(1); |
53 | params = rx.cap(3); |
54 | } |
55 | |
56 | // Make sure we don't execute something outside a script dir |
57 | if (_scriptName.contains("../" ) || _scriptName.contains("..\\" )) |
58 | emit error(tr("Name \"%1\" is invalid: ../ or ..\\ are not allowed!" ).arg(_scriptName)); |
59 | |
60 | else { |
61 | foreach(QString scriptDir, Quassel::scriptDirPaths()) { |
62 | QString fileName = scriptDir + _scriptName; |
63 | if (!QFile::exists(fileName)) |
64 | continue; |
65 | _process.setWorkingDirectory(scriptDir); |
66 | _process.start('"' + fileName + "\" " + params); |
67 | return; |
68 | } |
69 | emit error(tr("Could not find script \"%1\"" ).arg(_scriptName)); |
70 | } |
71 | |
72 | deleteLater(); // self-destruct |
73 | } |
74 | |
75 | |
76 | void ExecWrapper::postStdout(const QString &msg) |
77 | { |
78 | if (_bufferInfo.isValid()) |
79 | Client::userInput(_bufferInfo, msg); |
80 | } |
81 | |
82 | |
83 | void ExecWrapper::postStderr(const QString &msg) |
84 | { |
85 | if (_bufferInfo.isValid()) |
86 | Client::messageModel()->insertErrorMessage(_bufferInfo, msg); |
87 | } |
88 | |
89 | |
90 | void ExecWrapper::processFinished(int exitCode, QProcess::ExitStatus status) |
91 | { |
92 | if (status == QProcess::CrashExit) { |
93 | emit error(tr("Script \"%1\" crashed with exit code %2." ).arg(_scriptName).arg(exitCode)); |
94 | } |
95 | |
96 | // empty buffers |
97 | if (!_stdoutBuffer.isEmpty()) |
98 | foreach(QString msg, _stdoutBuffer.split('\n')) |
99 | emit output(msg); |
100 | if (!_stderrBuffer.isEmpty()) |
101 | foreach(QString msg, _stderrBuffer.split('\n')) |
102 | emit error(msg); |
103 | |
104 | deleteLater(); |
105 | } |
106 | |
107 | |
108 | void ExecWrapper::processError(QProcess::ProcessError err) |
109 | { |
110 | if (err == QProcess::FailedToStart) |
111 | emit error(tr("Script \"%1\" could not start." ).arg(_scriptName)); |
112 | else |
113 | emit error(tr("Script \"%1\" caused error %2." ).arg(_scriptName).arg(err)); |
114 | |
115 | if (_process.state() != QProcess::Running) |
116 | deleteLater(); |
117 | } |
118 | |
119 | |
120 | void ExecWrapper::processReadStdout() |
121 | { |
122 | QString str = QTextCodec::codecForLocale()->toUnicode(_process.readAllStandardOutput()); |
123 | str.replace(QRegExp("\r\n?" ), "\n" ); |
124 | _stdoutBuffer.append(str); |
125 | int idx; |
126 | while ((idx = _stdoutBuffer.indexOf('\n')) >= 0) { |
127 | emit output(_stdoutBuffer.left(idx)); |
128 | _stdoutBuffer = _stdoutBuffer.mid(idx + 1); |
129 | } |
130 | } |
131 | |
132 | |
133 | void ExecWrapper::processReadStderr() |
134 | { |
135 | QString str = QTextCodec::codecForLocale()->toUnicode(_process.readAllStandardError()); |
136 | str.replace(QRegExp("\r\n?" ), "\n" ); |
137 | _stderrBuffer.append(str); |
138 | int idx; |
139 | while ((idx = _stderrBuffer.indexOf('\n')) >= 0) { |
140 | emit error(_stderrBuffer.left(idx)); |
141 | _stderrBuffer = _stderrBuffer.mid(idx + 1); |
142 | } |
143 | } |
144 | |