1 /*
2 Copyright (C) 2008-2020 The Communi Project
3
4 You may use this file under the terms of BSD license as follows:
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 * Neither the name of the copyright holder nor the names of its
14 contributors may be used to endorse or promote products derived
15 from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "ircmessagecomposer_p.h"
30 #include "ircmessage.h"
31 #include "irccore_p.h"
32 #include "irc.h"
33 #include <memory>
34
35 IRC_BEGIN_NAMESPACE
36
37 #ifndef IRC_DOXYGEN
IrcMessageComposer(IrcConnection * connection)38 IrcMessageComposer::IrcMessageComposer(IrcConnection* connection)
39 {
40 d.connection = connection;
41 }
42
isComposed(int code)43 bool IrcMessageComposer::isComposed(int code)
44 {
45 switch (code) {
46 case Irc::RPL_MOTDSTART:
47 case Irc::RPL_MOTD:
48 case Irc::RPL_ENDOFMOTD:
49 case Irc::RPL_NAMREPLY:
50 case Irc::RPL_ENDOFNAMES:
51 case Irc::RPL_TOPIC:
52 case Irc::RPL_NOTOPIC:
53 case Irc::RPL_INVITING:
54 case Irc::RPL_INVITED:
55 case Irc::RPL_WHOREPLY:
56 case Irc::RPL_ENDOFWHO:
57 case Irc::RPL_CHANNELMODEIS:
58 case Irc::RPL_AWAY:
59 case Irc::RPL_UNAWAY:
60 case Irc::RPL_NOWAWAY:
61 case Irc::RPL_WHOISUSER:
62 case Irc::RPL_WHOWASUSER:
63 case Irc::RPL_WHOISSERVER:
64 case Irc::RPL_WHOISACCOUNT:
65 case Irc::RPL_WHOISHOST:
66 case Irc::RPL_WHOISIDLE:
67 case Irc::RPL_WHOISSECURE:
68 case Irc::RPL_WHOISCHANNELS:
69 case Irc::RPL_ENDOFWHOIS:
70 case Irc::RPL_ENDOFWHOWAS:
71 return true;
72 default:
73 return false;
74 }
75 }
76
composeMessage(IrcNumericMessage * message)77 void IrcMessageComposer::composeMessage(IrcNumericMessage* message)
78 {
79 switch (message->code()) {
80 case Irc::RPL_MOTDSTART:
81 d.messages.push(new IrcMotdMessage(d.connection));
82 d.messages.top()->setPrefix(message->prefix());
83 d.messages.top()->setParameters(QStringList(message->parameters().value(0)));
84 break;
85 case Irc::RPL_MOTD:
86 d.messages.top()->setParameters(d.messages.top()->parameters() << message->parameters().value(1));
87 break;
88 case Irc::RPL_ENDOFMOTD:
89 finishCompose(message);
90 break;
91
92 case Irc::RPL_NAMREPLY: {
93 if (d.messages.empty() || d.messages.top()->type() != IrcMessage::Names)
94 d.messages.push(new IrcNamesMessage(d.connection));
95 d.messages.top()->setPrefix(message->prefix());
96 int count = message->parameters().count();
97 QString channel = message->parameters().value(count - 2);
98 QStringList names = d.messages.top()->parameters().mid(1);
99 names += message->parameters().value(count - 1).split(QLatin1Char(' '), Qt::SkipEmptyParts);
100 d.messages.top()->setParameters(QStringList() << channel << names);
101 break;
102 }
103 case Irc::RPL_ENDOFNAMES:
104 finishCompose(message);
105 break;
106
107 case Irc::RPL_TOPIC:
108 case Irc::RPL_NOTOPIC:
109 d.messages.push(new IrcTopicMessage(d.connection));
110 d.messages.top()->setPrefix(message->prefix());
111 d.messages.top()->setCommand(QString::number(message->code()));
112 d.messages.top()->setParameters(QStringList() << message->parameters().value(1) << message->parameters().value(2));
113 finishCompose(message);
114 break;
115
116 case Irc::RPL_INVITING:
117 case Irc::RPL_INVITED:
118 d.messages.push(new IrcInviteMessage(d.connection));
119 d.messages.top()->setPrefix(message->prefix());
120 d.messages.top()->setCommand(QString::number(message->code()));
121 d.messages.top()->setParameters(QStringList() << message->parameters().value(1) << message->parameters().value(2));
122 finishCompose(message);
123 break;
124
125 case Irc::RPL_WHOREPLY: {
126 d.messages.push(new IrcWhoReplyMessage(d.connection));
127 d.messages.top()->setPrefix(message->parameters().value(5) // nick
128 + QLatin1Char('!') + message->parameters().value(2) // ident
129 + QLatin1Char('@') + message->parameters().value(3)); // host
130 d.messages.top()->setCommand(QString::number(message->code()));
131 d.messages.top()->setParameters(QStringList() << message->parameters().value(1) // mask
132 << message->parameters().value(4) // server
133 << message->parameters().value(6)); // status
134 QString last = message->parameters().value(7);
135 int index = last.indexOf(QLatin1Char(' ')); // ignore hopcount
136 if (index != -1)
137 d.messages.top()->setParameters(d.messages.top()->parameters() << last.mid(index + 1)); // real name
138 finishCompose(message);
139 break;
140 }
141
142 case Irc::RPL_CHANNELMODEIS:
143 d.messages.push(new IrcModeMessage(d.connection));
144 d.messages.top()->setPrefix(message->prefix());
145 d.messages.top()->setCommand(QString::number(message->code()));
146 d.messages.top()->setParameters(message->parameters().mid(1));
147 finishCompose(message);
148 break;
149
150 case Irc::RPL_AWAY:
151 if (!d.messages.isEmpty() && d.messages.top()->type() == IrcMessage::Whois) {
152 replaceParam(9, message->parameters().value(2)); // away reason
153 break;
154 }
155 Q_FALLTHROUGH();
156 case Irc::RPL_UNAWAY:
157 Q_FALLTHROUGH();
158 case Irc::RPL_NOWAWAY:
159 d.messages.push(new IrcAwayMessage(d.connection));
160 d.messages.top()->setCommand(QString::number(message->code()));
161 if (message->code() == Irc::RPL_AWAY) {
162 d.messages.top()->setPrefix(message->parameters().value(1));
163 d.messages.top()->setParameters(message->parameters().mid(2));
164 } else {
165 d.messages.top()->setPrefix(message->parameters().value(0));
166 d.messages.top()->setParameters(message->parameters().mid(1));
167 }
168 finishCompose(message);
169 break;
170
171 case Irc::RPL_WHOISUSER:
172 d.messages.push(new IrcWhoisMessage(d.connection));
173 d.messages.top()->setPrefix(message->parameters().value(1)
174 + "!" + message->parameters().value(2)
175 + "@" + message->parameters().value(3));
176 d.messages.top()->setParameters(QStringList() << message->parameters().value(5)
177 << QString() // server
178 << QString() // info
179 << QString() // account
180 << QString() // address
181 << QString() // since
182 << QString() // idle
183 << QString() // secure
184 << QString() // channels
185 << QString()); // away reason
186 break;
187
188 case Irc::RPL_WHOWASUSER:
189 d.messages.push(new IrcWhowasMessage(d.connection));
190 d.messages.top()->setPrefix(message->parameters().value(1)
191 + "!" + message->parameters().value(2)
192 + "@" + message->parameters().value(3));
193 d.messages.top()->setParameters(QStringList() << message->parameters().value(5)
194 << QString() // server
195 << QString() // info
196 << QString() // account
197 << QString() // address
198 << QString() // since
199 << QString() // idle
200 << QString() // secure
201 << QString()); // channels
202 break;
203
204 case Irc::RPL_WHOISSERVER:
205 replaceParam(1, message->parameters().value(2)); // server
206 replaceParam(2, message->parameters().value(3)); // info
207 break;
208
209 case Irc::RPL_WHOISACCOUNT:
210 replaceParam(3, message->parameters().value(2));
211 break;
212
213 case Irc::RPL_WHOISHOST:
214 replaceParam(4, QStringList(message->parameters().mid(2)).join(QLatin1String(" ")));
215 break;
216
217 case Irc::RPL_WHOISIDLE:
218 replaceParam(5, message->parameters().value(3)); // since
219 replaceParam(6, message->parameters().value(2)); // idle
220 break;
221
222 case Irc::RPL_WHOISSECURE:
223 replaceParam(7, "using a secure connection");
224 break;
225
226 case Irc::RPL_WHOISCHANNELS:
227 replaceParam(8, message->parameters().value(2)); // channels
228 break;
229
230 case Irc::RPL_ENDOFWHOIS:
231 case Irc::RPL_ENDOFWHOWAS:
232 finishCompose(message);
233 break;
234 }
235 }
236
finishCompose(IrcMessage * message)237 void IrcMessageComposer::finishCompose(IrcMessage* message)
238 {
239 if (!d.messages.isEmpty()) {
240 IrcMessage* composed = d.messages.pop();
241 composed->setTimeStamp(message->timeStamp());
242 if (message->testFlag(IrcMessage::Implicit))
243 composed->setFlag(IrcMessage::Implicit);
244 emit messageComposed(composed);
245 }
246 }
247
replaceParam(int index,const QString & param)248 void IrcMessageComposer::replaceParam(int index, const QString& param)
249 {
250 if (!d.messages.isEmpty()) {
251 QStringList params = d.messages.top()->parameters();
252 if (index < params.count())
253 params.replace(index, param);
254 d.messages.top()->setParameters(params);
255 }
256 }
257 #endif // IRC_DOXYGEN
258
259 #include "moc_ircmessagecomposer_p.cpp"
260
261 IRC_END_NAMESPACE
262