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()) {
57 } else if (it->unicode() == '\\') {
66 QByteArray IrcDecoder::extractFragment(const QByteArray& raw, int& start, int end, char prefix)
68 // Try to set find the end of the space-delimited fragment
70 end = raw.indexOf(' ', start);
72 // If no space comes after this point, use the remainder of the string
79 // And the fragment starts with the prefix
80 if (start < raw.length() && raw[start] == prefix) {
81 // return the fragment without the prefix, advancing the string
82 fragment = raw.mid(start + 1, end - start - 1);
87 // otherwise return the entire fragment
88 fragment = raw.mid(start, end - start);
94 void IrcDecoder::skipEmptyParts(const QByteArray& raw, int& start)
96 while (start < raw.length() && raw[start] == ' ') {
101 QHash<IrcTagKey, QString> IrcDecoder::parseTags(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
103 QHash<IrcTagKey, QString> tags = {};
104 QString rawTagStr = decode(extractFragment(raw, start, -1, '@'));
105 // Tags are delimited with ; according to spec
106 QList<QString> rawTags = rawTagStr.split(';');
107 for (const QString& rawTag : rawTags) {
108 if (rawTag.isEmpty()) {
114 int index = rawTag.indexOf('=');
115 if (index == -1 || index == rawTag.length()) {
119 rawKey = rawTag.left(index);
120 rawValue = rawTag.mid(index + 1);
124 key.clientTag = rawKey.startsWith('+');
128 QList<QString> splitByVendorAndKey = rawKey.split('/');
129 if (!splitByVendorAndKey.isEmpty()) key.key = splitByVendorAndKey.takeLast();
130 if (!splitByVendorAndKey.isEmpty()) key.vendor = splitByVendorAndKey.takeLast();
131 tags[key] = parseTagValue(rawValue);
136 QString IrcDecoder::parsePrefix(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
138 return decode(extractFragment(raw, start, -1, ':'));
141 QString IrcDecoder::parseCommand(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
143 return decode(extractFragment(raw, start, -1));
146 QByteArray IrcDecoder::parseParameter(const QByteArray& raw, int& start)
148 if (start < raw.length() && raw[start] == ':') {
151 return extractFragment(raw, start, raw.size());
154 return extractFragment(raw, start);
158 void IrcDecoder::parseMessage(const std::function<QString(const QByteArray&)>& decode, const QByteArray& rawMsg, QHash<IrcTagKey, QString>& tags, QString& prefix, QString& command, QList<QByteArray>& parameters)
161 skipEmptyParts(rawMsg, start);
162 tags = parseTags(decode, rawMsg, start);
163 skipEmptyParts(rawMsg, start);
164 prefix = parsePrefix(decode, rawMsg, start);
165 skipEmptyParts(rawMsg, start);
166 command = parseCommand(decode, rawMsg, start);
167 skipEmptyParts(rawMsg, start);
168 QList<QByteArray> params;
169 while (start != rawMsg.length()) {
170 QByteArray param = parseParameter(rawMsg, start);
171 skipEmptyParts(rawMsg, start);
172 params.append(param);