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