1 #include "IrcServer.hpp"
2
3 #include <cassert>
4 #include <cstdlib>
5
6 #include "common/QLogging.hpp"
7 #include "messages/Message.hpp"
8 #include "providers/irc/Irc2.hpp"
9 #include "providers/irc/IrcChannel2.hpp"
10 #include "providers/irc/IrcMessageBuilder.hpp"
11 #include "singletons/Settings.hpp"
12 #include "util/IrcHelpers.hpp"
13 #include "util/QObjectRef.hpp"
14
15 #include <QMetaEnum>
16
17 namespace chatterino {
18
IrcServer(const IrcServerData & data)19 IrcServer::IrcServer(const IrcServerData &data)
20 : data_(new IrcServerData(data))
21 {
22 this->initializeIrc();
23
24 this->connect();
25 }
26
IrcServer(const IrcServerData & data,const std::vector<std::weak_ptr<Channel>> & restoreChannels)27 IrcServer::IrcServer(const IrcServerData &data,
28 const std::vector<std::weak_ptr<Channel>> &restoreChannels)
29 : IrcServer(data)
30 {
31 for (auto &&weak : restoreChannels)
32 {
33 if (auto shared = weak.lock())
34 {
35 this->channels[shared->getName()] = weak;
36 }
37 }
38 }
39
~IrcServer()40 IrcServer::~IrcServer()
41 {
42 delete this->data_;
43 }
44
id()45 int IrcServer::id()
46 {
47 return this->data_->id;
48 }
49
user()50 const QString &IrcServer::user()
51 {
52 return this->data_->user;
53 }
54
nick()55 const QString &IrcServer::nick()
56 {
57 return this->data_->nick.isEmpty() ? this->data_->user : this->data_->nick;
58 }
59
initializeConnectionSignals(IrcConnection * connection,ConnectionType type)60 void IrcServer::initializeConnectionSignals(IrcConnection *connection,
61 ConnectionType type)
62 {
63 assert(type == Both);
64
65 QObject::connect(
66 connection, &Communi::IrcConnection::socketError, this,
67 [this](QAbstractSocket::SocketError error) {
68 static int index =
69 QAbstractSocket::staticMetaObject.indexOfEnumerator(
70 "SocketError");
71
72 std::lock_guard lock(this->channelMutex);
73
74 for (auto &&weak : this->channels)
75 {
76 if (auto shared = weak.lock())
77 {
78 shared->addMessage(makeSystemMessage(
79 QStringLiteral("Socket error: ") +
80 QAbstractSocket::staticMetaObject.enumerator(index)
81 .valueToKey(error)));
82 }
83 }
84 });
85
86 QObject::connect(connection, &Communi::IrcConnection::nickNameRequired,
87 this, [](const QString &reserved, QString *result) {
88 *result = reserved + (std::rand() % 100);
89 });
90
91 QObject::connect(connection, &Communi::IrcConnection::noticeMessageReceived,
92 this, [this](Communi::IrcNoticeMessage *message) {
93 // XD PAJLADA
94 MessageBuilder builder;
95
96 builder.emplace<TimestampElement>(
97 calculateMessageTimestamp(message));
98 builder.emplace<TextElement>(
99 message->nick(), MessageElementFlag::Username);
100 builder.emplace<TextElement>(
101 "-> you:", MessageElementFlag::Username);
102 builder.emplace<TextElement>(message->content(),
103 MessageElementFlag::Text);
104
105 auto msg = builder.release();
106
107 for (auto &&weak : this->channels)
108 if (auto shared = weak.lock())
109 shared->addMessage(msg);
110 });
111 }
112
initializeConnection(IrcConnection * connection,ConnectionType type)113 void IrcServer::initializeConnection(IrcConnection *connection,
114 ConnectionType type)
115 {
116 assert(type == Both);
117
118 connection->setSecure(this->data_->ssl);
119 connection->setHost(this->data_->host);
120 connection->setPort(this->data_->port);
121
122 connection->setUserName(this->data_->user);
123 connection->setNickName(this->data_->nick.isEmpty() ? this->data_->user
124 : this->data_->nick);
125 connection->setRealName(this->data_->real.isEmpty() ? this->data_->user
126 : this->data_->nick);
127
128 if (getSettings()->enableExperimentalIrc)
129 {
130 switch (this->data_->authType)
131 {
132 case IrcAuthType::Sasl:
133 connection->setSaslMechanism("PLAIN");
134 [[fallthrough]];
135 case IrcAuthType::Pass:
136 this->data_->getPassword(
137 this, [conn = new QObjectRef(connection) /* can't copy */,
138 this](const QString &password) mutable {
139 if (*conn)
140 {
141 (*conn)->setPassword(password);
142 this->open(Both);
143 }
144
145 delete conn;
146 });
147 break;
148 default:
149 this->open(Both);
150 }
151 }
152 }
153
createChannel(const QString & channelName)154 std::shared_ptr<Channel> IrcServer::createChannel(const QString &channelName)
155 {
156 return std::make_shared<IrcChannel>(channelName, this);
157 }
158
hasSeparateWriteConnection() const159 bool IrcServer::hasSeparateWriteConnection() const
160 {
161 return false;
162 }
163
onReadConnected(IrcConnection * connection)164 void IrcServer::onReadConnected(IrcConnection *connection)
165 {
166 {
167 std::lock_guard lock(this->channelMutex);
168
169 for (auto &&command : this->data_->connectCommands)
170 {
171 connection->sendRaw(command + "\r\n");
172 }
173 }
174
175 AbstractIrcServer::onReadConnected(connection);
176 }
177
privateMessageReceived(Communi::IrcPrivateMessage * message)178 void IrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
179 {
180 auto target = message->target();
181 target = target.startsWith('#') ? target.mid(1) : target;
182
183 if (auto channel = this->getChannelOrEmpty(target); !channel->isEmpty())
184 {
185 MessageParseArgs args;
186 IrcMessageBuilder builder(channel.get(), message, args);
187
188 if (!builder.isIgnored())
189 {
190 builder.triggerHighlights();
191 channel->addMessage(builder.build());
192 }
193 else
194 {
195 qCDebug(chatterinoIrc) << "message ignored :rage:";
196 }
197 }
198 }
199
readConnectionMessageReceived(Communi::IrcMessage * message)200 void IrcServer::readConnectionMessageReceived(Communi::IrcMessage *message)
201 {
202 AbstractIrcServer::readConnectionMessageReceived(message);
203
204 switch (message->type())
205 {
206 case Communi::IrcMessage::Join: {
207 auto x = static_cast<Communi::IrcJoinMessage *>(message);
208
209 if (auto it =
210 this->channels.find(this->cleanChannelName(x->channel()));
211 it != this->channels.end())
212 {
213 if (auto shared = it->lock())
214 {
215 if (message->nick() == this->data_->nick)
216 {
217 shared->addMessage(makeSystemMessage("joined"));
218 }
219 else
220 {
221 if (auto c =
222 dynamic_cast<ChannelChatters *>(shared.get()))
223 c->addJoinedUser(x->nick());
224 }
225 }
226 }
227 return;
228 }
229
230 case Communi::IrcMessage::Part: {
231 auto x = static_cast<Communi::IrcPartMessage *>(message);
232
233 if (auto it =
234 this->channels.find(this->cleanChannelName(x->channel()));
235 it != this->channels.end())
236 {
237 if (auto shared = it->lock())
238 {
239 if (message->nick() == this->data_->nick)
240 {
241 shared->addMessage(makeSystemMessage("parted"));
242 }
243 else
244 {
245 if (auto c =
246 dynamic_cast<ChannelChatters *>(shared.get()))
247 c->addPartedUser(x->nick());
248 }
249 }
250 }
251 return;
252 }
253
254 case Communi::IrcMessage::Pong:
255 case Communi::IrcMessage::Notice:
256 case Communi::IrcMessage::Private:
257 return;
258
259 default:
260 if (getSettings()->showUnhandledIrcMessages)
261 {
262 MessageBuilder builder;
263
264 builder.emplace<TimestampElement>(
265 calculateMessageTimestamp(message));
266 builder.emplace<TextElement>(message->toData(),
267 MessageElementFlag::Text);
268 builder->flags.set(MessageFlag::Debug);
269
270 auto msg = builder.release();
271
272 for (auto &&weak : this->channels)
273 {
274 if (auto shared = weak.lock())
275 shared->addMessage(msg);
276 }
277 };
278 }
279 }
280
281 } // namespace chatterino
282