1 /***************************************************************************
2 * Copyright (C) 2005-2020 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()) {
58 else if (it->unicode() == '\\') {
68 QByteArray IrcDecoder::extractFragment(const QByteArray& raw, int& start, int end, char prefix)
70 // Try to set find the end of the space-delimited fragment
72 end = raw.indexOf(' ', start);
74 // If no space comes after this point, use the remainder of the string
81 // And the fragment starts with the prefix
82 if (start < raw.length() && raw[start] == prefix) {
83 // return the fragment without the prefix, advancing the string
84 fragment = raw.mid(start + 1, end - start - 1);
89 // otherwise return the entire fragment
90 fragment = raw.mid(start, end - start);
96 void IrcDecoder::skipEmptyParts(const QByteArray& raw, int& start)
98 while (start < raw.length() && raw[start] == ' ') {
103 QHash<IrcTagKey, QString> IrcDecoder::parseTags(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
105 QHash<IrcTagKey, QString> tags = {};
106 QString rawTagStr = decode(extractFragment(raw, start, -1, '@'));
107 // Tags are delimited with ; according to spec
108 QList<QString> rawTags = rawTagStr.split(';');
109 for (const QString& rawTag : rawTags) {
110 if (rawTag.isEmpty()) {
116 int index = rawTag.indexOf('=');
117 if (index == -1 || index == rawTag.length()) {
121 rawKey = rawTag.left(index);
122 rawValue = rawTag.mid(index + 1);
126 key.clientTag = rawKey.startsWith('+');
131 int splitIndex = rawKey.lastIndexOf('/');
132 if (splitIndex > 0 && splitIndex + 1 < rawKey.length()) {
133 key.key = rawKey.mid(splitIndex + 1);
134 key.vendor = rawKey.left(splitIndex);
139 tags[key] = parseTagValue(rawValue);
144 QString IrcDecoder::parsePrefix(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
146 return decode(extractFragment(raw, start, -1, ':'));
149 QString IrcDecoder::parseCommand(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
151 return decode(extractFragment(raw, start, -1));
154 QByteArray IrcDecoder::parseParameter(const QByteArray& raw, int& start)
156 if (start < raw.length() && raw[start] == ':') {
159 return extractFragment(raw, start, raw.size());
162 return extractFragment(raw, start);
166 void IrcDecoder::parseMessage(const std::function<QString(const QByteArray&)>& decode,
167 const QByteArray& rawMsg,
168 QHash<IrcTagKey, QString>& tags,
171 QList<QByteArray>& parameters)
174 skipEmptyParts(rawMsg, start);
175 tags = parseTags(decode, rawMsg, start);
176 skipEmptyParts(rawMsg, start);
177 prefix = parsePrefix(decode, rawMsg, start);
178 skipEmptyParts(rawMsg, start);
179 command = parseCommand(decode, rawMsg, start);
180 skipEmptyParts(rawMsg, start);
181 QList<QByteArray> params;
182 while (start != rawMsg.length()) {
183 QByteArray param = parseParameter(rawMsg, start);
184 skipEmptyParts(rawMsg, start);
185 params.append(param);