+ else {
+ // Treat the SSL error as a warning, continue to connect anyways
+ QString sslErrorMessage = tr("Encrypted connection couldn't be verified, continuing "
+ "since verification is not required");
+ if (!sslErrors.empty()) {
+ // Add the error reason if known
+ sslErrorMessage.append(tr(" (Reason: %1)").arg(sslErrors.first().errorString()));
+ }
+ showMessage(Message::Info, BufferInfo::StatusBuffer, "", sslErrorMessage);
+
+ // Proceed with the connection
+ socket.ignoreSslErrors();
+ }
+}
+
+#endif // HAVE_SSL
+
+void CoreNetwork::checkTokenBucket()
+{
+ if (_skipMessageRates) {
+ if (_msgQueue.size() == 0) {
+ // Message queue emptied; stop the timer and bail out
+ _tokenBucketTimer.stop();
+ return;
+ }
+ // Otherwise, we're emptying the queue, continue on as normal
+ }
+
+ // Process whatever messages are pending
+ fillBucketAndProcessQueue();
+}
+
+void CoreNetwork::fillBucketAndProcessQueue()
+{
+ // If there's less tokens than burst size, refill the token bucket by 1
+ if (_tokenBucket < _burstSize) {
+ _tokenBucket++;
+ }
+
+ // As long as there's tokens available and messages remaining, sending messages from the queue
+ while (_msgQueue.size() > 0 && _tokenBucket > 0) {
+ writeToSocket(_msgQueue.takeFirst());
+ }
+}
+
+void CoreNetwork::writeToSocket(const QByteArray& data)
+{
+ // Log the message if enabled and network ID matches or allows all
+ if (_debugLogRawIrc && (_debugLogRawNetId == -1 || networkId().toInt() == _debugLogRawNetId)) {
+ // Include network ID
+ qDebug() << "IRC net" << networkId() << ">>" << data;
+ }
+ socket.write(data);
+ socket.write("\r\n");
+ if (!_skipMessageRates) {
+ // Only subtract from the token bucket if message rate limiting is enabled
+ _tokenBucket--;
+ }
+}
+
+Network::Server CoreNetwork::usedServer() const
+{
+ if (_lastUsedServerIndex < serverList().count())
+ return serverList()[_lastUsedServerIndex];
+
+ if (!serverList().isEmpty())
+ return serverList()[0];
+
+ return Network::Server();
+}
+
+void CoreNetwork::requestConnect() const
+{
+ if (_shuttingDown) {
+ return;
+ }
+ if (connectionState() != Disconnected) {
+ qWarning() << "Requesting connect while already being connected!";
+ return;
+ }
+ QMetaObject::invokeMethod(const_cast<CoreNetwork*>(this), "connectToIrc", Qt::QueuedConnection);
+}
+
+void CoreNetwork::requestDisconnect() const
+{
+ if (_shuttingDown) {
+ return;
+ }
+ if (connectionState() == Disconnected) {
+ qWarning() << "Requesting disconnect while not being connected!";
+ return;
+ }
+ userInputHandler()->handleQuit(BufferInfo(), QString());
+}
+
+void CoreNetwork::requestSetNetworkInfo(const NetworkInfo& info)
+{
+ Network::Server currentServer = usedServer();
+ setNetworkInfo(info);
+ Core::updateNetwork(coreSession()->user(), info);
+
+ // the order of the servers might have changed,
+ // so we try to find the previously used server
+ _lastUsedServerIndex = 0;
+ for (int i = 0; i < serverList().count(); i++) {
+ Network::Server server = serverList()[i];
+ if (server.host == currentServer.host && server.port == currentServer.port) {
+ _lastUsedServerIndex = i;
+ break;
+ }
+ }
+}
+
+QList<QList<QByteArray>> CoreNetwork::splitMessage(const QString& cmd,
+ const QString& message,
+ const std::function<QList<QByteArray>(QString&)>& cmdGenerator)
+{
+ QString wrkMsg(message);
+ QList<QList<QByteArray>> msgsToSend;
+
+ // do while (wrkMsg.size() > 0)
+ do {
+ // First, check to see if the whole message can be sent at once. The
+ // cmdGenerator function is passed in by the caller and is used to encode
+ // and encrypt (if applicable) the message, since different callers might
+ // want to use different encoding or encode different values.
+ int splitPos = wrkMsg.size();
+ QList<QByteArray> initialSplitMsgEnc = cmdGenerator(wrkMsg);
+ int initialOverrun = userInputHandler()->lastParamOverrun(cmd, initialSplitMsgEnc);
+
+ if (initialOverrun) {
+ // If the message was too long to be sent, first try splitting it along
+ // word boundaries with QTextBoundaryFinder.
+ QString splitMsg(wrkMsg);
+ QTextBoundaryFinder qtbf(QTextBoundaryFinder::Word, splitMsg);
+ qtbf.setPosition(initialSplitMsgEnc[1].size() - initialOverrun);
+ QList<QByteArray> splitMsgEnc;
+ int overrun = initialOverrun;
+
+ while (overrun) {
+ splitPos = qtbf.toPreviousBoundary();
+
+ // splitPos==-1 means the QTBF couldn't find a split point at all and
+ // splitPos==0 means the QTBF could only find a boundary at the beginning of
+ // the string. Neither one of these works for us.
+ if (splitPos > 0) {
+ // If a split point could be found, split the message there, calculate the
+ // overrun, and continue with the loop.
+ splitMsg = splitMsg.left(splitPos);
+ splitMsgEnc = cmdGenerator(splitMsg);
+ overrun = userInputHandler()->lastParamOverrun(cmd, splitMsgEnc);
+ }
+ else {
+ // If a split point could not be found (the beginning of the message
+ // is reached without finding a split point short enough to send) and we
+ // are still in Word mode, switch to Grapheme mode. We also need to restore
+ // the full wrkMsg to splitMsg, since splitMsg may have been cut down during
+ // the previous attempt to find a split point.
+ if (qtbf.type() == QTextBoundaryFinder::Word) {
+ splitMsg = wrkMsg;
+ splitPos = splitMsg.size();
+ QTextBoundaryFinder graphemeQtbf(QTextBoundaryFinder::Grapheme, splitMsg);
+ graphemeQtbf.setPosition(initialSplitMsgEnc[1].size() - initialOverrun);
+ qtbf = graphemeQtbf;
+ }
+ else {
+ // If the QTBF fails to find a split point in Grapheme mode, we give up.
+ // This should never happen, but it should be handled anyway.
+ qWarning() << "Unexpected failure to split message!";
+ return msgsToSend;
+ }
+ }
+ }
+
+ // Once a message of sendable length has been found, remove it from the wrkMsg and
+ // add it to the list of messages to be sent.
+ wrkMsg.remove(0, splitPos);
+ msgsToSend.append(splitMsgEnc);
+ }
+ else {
+ // If the entire remaining message is short enough to be sent all at once, remove
+ // it from the wrkMsg and add it to the list of messages to be sent.
+ wrkMsg.remove(0, splitPos);
+ msgsToSend.append(initialSplitMsgEnc);
+ }
+ } while (wrkMsg.size() > 0);
+
+ return msgsToSend;