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