1 /***************************************************************************
2 * Copyright (C) 2005-2019 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "ircdecoder.h"
27 QString IrcDecoder::parseTagValue(const QString& value)
31 for (auto it = value.begin(); it < value.end(); it++) {
32 // Check if it's on the list of special wildcard characters, converting to Unicode for use
33 // in the switch statement
35 // See https://doc.qt.io/qt-5/qchar.html#unicode
37 switch (it->unicode()) {
57 } else if (it->unicode() == '\\') {
67 * Extracts a space-delimited fragment from an IRC message
68 * @param raw Raw Message
69 * @param start Current index into the message, will be advanced automatically
70 * @param end End of fragment, if already known. Default is -1, in which case it will be set to the next whitespace
71 * character or the end of the string
72 * @param prefix Required prefix. Default is 0. If set, this only parses a fragment if it starts with the given prefix.
75 QByteArray extractFragment(const QByteArray& raw, int& start, int end = -1, char prefix = 0)
77 // Try to set find the end of the space-delimited fragment
79 end = raw.indexOf(' ', start);
81 // If no space comes after this point, use the remainder of the string
88 // And the fragment starts with the prefix
89 if (start < raw.length() && raw[start] == prefix) {
90 // return the fragment without the prefix, advancing the string
91 fragment = raw.mid(start + 1, end - start - 1);
96 // otherwise return the entire fragment
97 fragment = raw.mid(start, end - start);
104 * Skips empty parts in the message
105 * @param raw Raw Message
106 * @param start Current index into the message, will be advanced automatically
108 void skipEmptyParts(const QByteArray& raw, int& start)
110 while (start < raw.length() && raw[start] == ' ') {
115 QHash<IrcTagKey, QString> IrcDecoder::parseTags(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
117 QHash<IrcTagKey, QString> tags = {};
118 QString rawTagStr = decode(extractFragment(raw, start, -1, '@'));
119 // Tags are delimited with ; according to spec
120 QList<QString> rawTags = rawTagStr.split(';');
121 for (const QString& rawTag : rawTags) {
122 if (rawTag.isEmpty()) {
128 int index = rawTag.indexOf('=');
129 if (index == -1 || index == rawTag.length()) {
133 rawKey = rawTag.left(index);
134 rawValue = rawTag.mid(index + 1);
138 key.clientTag = rawKey.startsWith('+');
142 QList<QString> splitByVendorAndKey = rawKey.split('/');
143 if (!splitByVendorAndKey.isEmpty()) key.key = splitByVendorAndKey.takeLast();
144 if (!splitByVendorAndKey.isEmpty()) key.vendor = splitByVendorAndKey.takeLast();
145 tags[key] = parseTagValue(rawValue);
150 QString IrcDecoder::parsePrefix(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
152 return decode(extractFragment(raw, start, -1, ':'));
155 QString IrcDecoder::parseCommand(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
157 return decode(extractFragment(raw, start, -1));
160 QByteArray IrcDecoder::parseParameter(const QByteArray& raw, int& start)
162 if (start < raw.length() && raw[start] == ':') {
165 return extractFragment(raw, start, raw.size());
168 return extractFragment(raw, start);
172 void IrcDecoder::parseMessage(const std::function<QString(const QByteArray&)>& decode, const QByteArray& rawMsg, QHash<IrcTagKey, QString>& tags, QString& prefix, QString& command, QList<QByteArray>& parameters)
175 skipEmptyParts(rawMsg, start);
176 tags = parseTags(decode, rawMsg, start);
177 skipEmptyParts(rawMsg, start);
178 prefix = parsePrefix(decode, rawMsg, start);
179 skipEmptyParts(rawMsg, start);
180 command = parseCommand(decode, rawMsg, start);
181 skipEmptyParts(rawMsg, start);
182 QList<QByteArray> params;
183 while (start != rawMsg.length()) {
184 QByteArray param = parseParameter(rawMsg, start);
185 skipEmptyParts(rawMsg, start);
186 params.append(param);