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