cmake: avoid de-duplication of user's CXXFLAGS
[quassel.git] / src / common / ircdecoder.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2020 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 "ircdecoder.h"
22
23 #include <QDebug>
24
25 #include "irctag.h"
26
27 QString IrcDecoder::parseTagValue(const QString& value)
28 {
29     QString result;
30     bool escaped = false;
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
34         //
35         // See https://doc.qt.io/qt-5/qchar.html#unicode
36         if (escaped) {
37             switch (it->unicode()) {
38             case '\\':
39                 result.append('\\');
40                 break;
41             case 's':
42                 result.append(' ');
43                 break;
44             case ':':
45                 result.append(';');
46                 break;
47             case 'r':
48                 result.append('\r');
49                 break;
50             case 'n':
51                 result.append('\n');
52                 break;
53             default:
54                 result.append(*it);
55             }
56             escaped = false;
57         }
58         else if (it->unicode() == '\\') {
59             escaped = true;
60         }
61         else {
62             result.append(*it);
63         }
64     }
65     return result;
66 }
67
68 QByteArray IrcDecoder::extractFragment(const QByteArray& raw, int& start, int end, char prefix)
69 {
70     // Try to set find the end of the space-delimited fragment
71     if (end == -1) {
72         end = raw.indexOf(' ', start);
73     }
74     // If no space comes after this point, use the remainder of the string
75     if (end == -1) {
76         end = raw.length();
77     }
78     QByteArray fragment;
79     // If a prefix is set
80     if (prefix != 0) {
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);
85             start = end;
86         }
87     }
88     else {
89         // otherwise return the entire fragment
90         fragment = raw.mid(start, end - start);
91         start = end;
92     }
93     return fragment;
94 }
95
96 void IrcDecoder::skipEmptyParts(const QByteArray& raw, int& start)
97 {
98     while (start < raw.length() && raw[start] == ' ') {
99         start++;
100     }
101 }
102
103 QHash<IrcTagKey, QString> IrcDecoder::parseTags(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
104 {
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()) {
111             continue;
112         }
113
114         QString rawKey;
115         QString rawValue;
116         int index = rawTag.indexOf('=');
117         if (index == -1 || index == rawTag.length()) {
118             rawKey = rawTag;
119         }
120         else {
121             rawKey = rawTag.left(index);
122             rawValue = rawTag.mid(index + 1);
123         }
124
125         IrcTagKey key{};
126         key.clientTag = rawKey.startsWith('+');
127         if (key.clientTag) {
128             rawKey.remove(0, 1);
129         }
130
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);
135         }
136         else {
137             key.key = rawKey;
138         }
139         tags[key] = parseTagValue(rawValue);
140     }
141     return tags;
142 }
143
144 QString IrcDecoder::parsePrefix(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
145 {
146     return decode(extractFragment(raw, start, -1, ':'));
147 }
148
149 QString IrcDecoder::parseCommand(const std::function<QString(const QByteArray&)>& decode, const QByteArray& raw, int& start)
150 {
151     return decode(extractFragment(raw, start, -1));
152 }
153
154 QByteArray IrcDecoder::parseParameter(const QByteArray& raw, int& start)
155 {
156     if (start < raw.length() && raw[start] == ':') {
157         // Skip the prefix
158         start++;
159         return extractFragment(raw, start, raw.size());
160     }
161     else {
162         return extractFragment(raw, start);
163     }
164 }
165
166 void IrcDecoder::parseMessage(const std::function<QString(const QByteArray&)>& decode,
167                               const QByteArray& rawMsg,
168                               QHash<IrcTagKey, QString>& tags,
169                               QString& prefix,
170                               QString& command,
171                               QList<QByteArray>& parameters)
172 {
173     int start = 0;
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);
186     }
187     parameters = params;
188 }