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 <QDebug> |
22 | #include <QStringList> |
23 | |
24 | #include "aliasmanager.h" |
25 | #include "network.h" |
26 | |
27 | INIT_SYNCABLE_OBJECT(AliasManager) |
28 | AliasManager &AliasManager::operator=(const AliasManager &other) |
29 | { |
30 | if (this == &other) |
31 | return *this; |
32 | |
33 | SyncableObject::operator=(other); |
34 | _aliases = other._aliases; |
35 | return *this; |
36 | } |
37 | |
38 | |
39 | int AliasManager::indexOf(const QString &name) const |
40 | { |
41 | for (int i = 0; i < _aliases.count(); i++) { |
42 | if (_aliases[i].name == name) |
43 | return i; |
44 | } |
45 | return -1; |
46 | } |
47 | |
48 | |
49 | QVariantMap AliasManager::initAliases() const |
50 | { |
51 | QVariantMap aliases; |
52 | QStringList names; |
53 | QStringList expansions; |
54 | |
55 | for (int i = 0; i < _aliases.count(); i++) { |
56 | names << _aliases[i].name; |
57 | expansions << _aliases[i].expansion; |
58 | } |
59 | |
60 | aliases["names" ] = names; |
61 | aliases["expansions" ] = expansions; |
62 | return aliases; |
63 | } |
64 | |
65 | |
66 | void AliasManager::initSetAliases(const QVariantMap &aliases) |
67 | { |
68 | QStringList names = aliases["names" ].toStringList(); |
69 | QStringList expansions = aliases["expansions" ].toStringList(); |
70 | |
71 | if (names.count() != expansions.count()) { |
72 | qWarning() << "AliasesManager::initSetAliases: received" << names.count() << "alias names but only" << expansions.count() << "expansions!" ; |
73 | return; |
74 | } |
75 | |
76 | _aliases.clear(); |
77 | for (int i = 0; i < names.count(); i++) { |
78 | _aliases << Alias(names[i], expansions[i]); |
79 | } |
80 | } |
81 | |
82 | |
83 | void AliasManager::addAlias(const QString &name, const QString &expansion) |
84 | { |
85 | if (contains(name)) { |
86 | return; |
87 | } |
88 | |
89 | _aliases << Alias(name, expansion); |
90 | |
91 | SYNC(ARG(name), ARG(expansion)) |
92 | } |
93 | |
94 | |
95 | AliasManager::AliasList AliasManager::defaults() |
96 | { |
97 | AliasList aliases; |
98 | aliases << Alias("j" , "/join $0" ) |
99 | << Alias("ns" , "/msg nickserv $0" ) |
100 | << Alias("nickserv" , "/msg nickserv $0" ) |
101 | << Alias("cs" , "/msg chanserv $0" ) |
102 | << Alias("chanserv" , "/msg chanserv $0" ) |
103 | << Alias("hs" , "/msg hostserv $0" ) |
104 | << Alias("hostserv" , "/msg hostserv $0" ) |
105 | << Alias("wii" , "/whois $0 $0" ) |
106 | << Alias("back" , "/quote away" ); |
107 | |
108 | #ifdef Q_OS_LINUX |
109 | // let's add aliases for scripts that only run on linux |
110 | aliases << Alias("inxi" , "/exec inxi $0" ) |
111 | << Alias("sysinfo" , "/exec inxi -d" ); |
112 | #endif |
113 | |
114 | return aliases; |
115 | } |
116 | |
117 | |
118 | AliasManager::CommandList AliasManager::processInput(const BufferInfo &info, const QString &msg) |
119 | { |
120 | CommandList result; |
121 | processInput(info, msg, result); |
122 | return result; |
123 | } |
124 | |
125 | |
126 | void AliasManager::processInput(const BufferInfo &info, const QString &msg_, CommandList &list) |
127 | { |
128 | QString msg = msg_; |
129 | |
130 | // leading slashes indicate there's a command to call unless there is another one in the first section (like a path /proc/cpuinfo) |
131 | int secondSlashPos = msg.indexOf('/', 1); |
132 | int firstSpacePos = msg.indexOf(' '); |
133 | if (!msg.startsWith('/') || (secondSlashPos != -1 && (secondSlashPos < firstSpacePos || firstSpacePos == -1))) { |
134 | if (msg.startsWith("//" )) |
135 | msg.remove(0, 1); // //asdf is transformed to /asdf |
136 | msg.prepend("/SAY " ); // make sure we only send proper commands to the core |
137 | } |
138 | else { |
139 | // check for aliases |
140 | QString cmd = msg.section(' ', 0, 0).remove(0, 1).toUpper(); |
141 | for (int i = 0; i < count(); i++) { |
142 | if ((*this)[i].name.toUpper() == cmd) { |
143 | expand((*this)[i].expansion, info, msg.section(' ', 1), list); |
144 | return; |
145 | } |
146 | } |
147 | } |
148 | |
149 | list.append(qMakePair(info, msg)); |
150 | } |
151 | |
152 | |
153 | void AliasManager::expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg, CommandList &list) |
154 | { |
155 | const Network *net = network(bufferInfo.networkId()); |
156 | if (!net) { |
157 | // FIXME send error as soon as we have a method for that! |
158 | return; |
159 | } |
160 | |
161 | QRegExp paramRangeR("\\$(\\d+)\\.\\.(\\d*)" ); |
162 | QStringList commands = alias.split(QRegExp("; ?" )); |
163 | QStringList params = msg.split(' '); |
164 | QStringList expandedCommands; |
165 | for (int i = 0; i < commands.count(); i++) { |
166 | QString command = commands[i]; |
167 | |
168 | // replace ranges like $1..3 |
169 | if (!params.isEmpty()) { |
170 | int pos; |
171 | while ((pos = paramRangeR.indexIn(command)) != -1) { |
172 | int start = paramRangeR.cap(1).toInt(); |
173 | bool ok; |
174 | int end = paramRangeR.cap(2).toInt(&ok); |
175 | if (!ok) { |
176 | end = params.count(); |
177 | } |
178 | if (end < start) |
179 | command = command.replace(pos, paramRangeR.matchedLength(), QString()); |
180 | else { |
181 | command = command.replace(pos, paramRangeR.matchedLength(), QStringList(params.mid(start - 1, end - start + 1)).join(" " )); |
182 | } |
183 | } |
184 | } |
185 | |
186 | for (int j = params.count(); j > 0; j--) { |
187 | IrcUser *ircUser = net->ircUser(params[j - 1]); |
188 | command = command.replace(QString("$%1:hostname" ).arg(j), ircUser ? ircUser->host() : QString("*" )); |
189 | command = command.replace(QString("$%1" ).arg(j), params[j - 1]); |
190 | } |
191 | command = command.replace("$0" , msg); |
192 | command = command.replace("$channelname" , bufferInfo.bufferName()); // legacy |
193 | command = command.replace("$channel" , bufferInfo.bufferName()); |
194 | command = command.replace("$currentnick" , net->myNick()); // legacy |
195 | command = command.replace("$nick" , net->myNick()); |
196 | expandedCommands << command; |
197 | } |
198 | |
199 | while (!expandedCommands.isEmpty()) { |
200 | QString command; |
201 | if (expandedCommands[0].trimmed().toLower().startsWith("/wait" )) { |
202 | command = expandedCommands.join("; " ); |
203 | expandedCommands.clear(); |
204 | } |
205 | else { |
206 | command = expandedCommands.takeFirst(); |
207 | } |
208 | list.append(qMakePair(bufferInfo, command)); |
209 | } |
210 | } |
211 | |