File indexing completed on 2024-12-15 04:51:47

0001 /*******************************************************************
0002  KNotes -- Notes for the KDE project
0003 
0004  SPDX-FileCopyrightText: 2003 Daniel Martin <daniel.martin@pirack.com>
0005  SPDX-FileCopyrightText: 2004, 2006 Michael Brade <brade@kde.org>
0006  SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel.org>
0007 
0008  SPDX-License-Identifier: GPL-2.0-or-later
0009 *******************************************************************/
0010 
0011 #include "notesnetworkreceiver.h"
0012 
0013 #include "noteshared_debug.h"
0014 #include "noteutils.h"
0015 #include <QDateTime>
0016 #include <QHostAddress>
0017 #include <QLocale>
0018 #include <QStringDecoder>
0019 #include <QTcpSocket>
0020 #include <QTimer>
0021 
0022 // Maximum note size in chars we are going to accept,
0023 // to prevent "note floods".
0024 #define MAXBUFFER 4096
0025 
0026 // Maximum time we are going to wait between data receptions,
0027 // to prevent memory and connection floods. In milliseconds.
0028 #define MAXTIME 10000
0029 
0030 // Small buffer's size
0031 #define SBSIZE 512
0032 using namespace NoteShared;
0033 
0034 class NoteShared::NotesNetworkReceiverPrivate
0035 {
0036 public:
0037     NotesNetworkReceiverPrivate(QTcpSocket *s)
0038         : m_buffer(new QByteArray())
0039         , m_sock(s)
0040     {
0041     }
0042 
0043     ~NotesNetworkReceiverPrivate()
0044     {
0045         delete m_buffer;
0046         delete m_sock;
0047     }
0048 
0049     QTimer *m_timer = nullptr; // to avoid memory and connection floods
0050 
0051     QByteArray *const m_buffer;
0052     QTcpSocket *const m_sock;
0053 
0054     QString m_titleAddon;
0055 };
0056 
0057 NotesNetworkReceiver::NotesNetworkReceiver(QTcpSocket *s)
0058     : QObject()
0059     , d(new NoteShared::NotesNetworkReceiverPrivate(s))
0060 {
0061     const QString date = QLocale().toString(QDateTime::currentDateTime(), QLocale::ShortFormat);
0062 
0063     // Add the remote IP or hostname and the date to the title, to help the
0064     // user guess who wrote it.
0065     d->m_titleAddon = QStringLiteral(" [%1, %2]").arg(d->m_sock->peerAddress().toString(), date);
0066 
0067     // Setup the communications
0068     connect(d->m_sock, &QTcpSocket::readyRead, this, &NotesNetworkReceiver::slotDataAvailable);
0069     connect(d->m_sock, &QTcpSocket::disconnected, this, &NotesNetworkReceiver::slotConnectionClosed);
0070     connect(d->m_sock, qOverload<QAbstractSocket::SocketError>(&QTcpSocket::errorOccurred), this, &NotesNetworkReceiver::slotError);
0071     // Setup the timer
0072     d->m_timer = new QTimer(this);
0073     d->m_timer->setSingleShot(true);
0074     connect(d->m_timer, &QTimer::timeout, this, &NotesNetworkReceiver::slotReceptionTimeout);
0075     d->m_timer->start(MAXTIME);
0076 }
0077 
0078 NotesNetworkReceiver::~NotesNetworkReceiver() = default;
0079 
0080 void NotesNetworkReceiver::slotDataAvailable()
0081 {
0082     char smallBuffer[SBSIZE];
0083     int smallBufferLen;
0084 
0085     do {
0086         // Append to "big buffer" only if we have some space left.
0087         int curLen = d->m_buffer->size();
0088 
0089         smallBufferLen = d->m_sock->read(smallBuffer, SBSIZE);
0090 
0091         // Limit max transfer over buffer, to avoid overflow.
0092         smallBufferLen = qMin(smallBufferLen, MAXBUFFER - curLen);
0093 
0094         if (smallBufferLen > 0) {
0095             d->m_buffer->resize(curLen + smallBufferLen);
0096             memcpy(d->m_buffer->data() + curLen, smallBuffer, smallBufferLen);
0097         }
0098     } while (smallBufferLen == SBSIZE);
0099 
0100     // If we are overflowing, close connection.
0101     if (d->m_buffer->size() == MAXBUFFER) {
0102         d->m_sock->close();
0103     } else {
0104         d->m_timer->start(MAXTIME);
0105     }
0106 }
0107 
0108 void NotesNetworkReceiver::slotReceptionTimeout()
0109 {
0110     d->m_sock->close();
0111 }
0112 
0113 void NotesNetworkReceiver::slotConnectionClosed()
0114 {
0115     QStringDecoder codec(QStringDecoder::System);
0116 
0117     if (d->m_timer->isActive()) {
0118         const QString noteText = QString(codec.decode(*d->m_buffer)).trimmed();
0119         NoteUtils utils;
0120         const NoteUtils::NoteText result = utils.extractNoteText(noteText, d->m_titleAddon);
0121         if (!result.noteText.isEmpty()) {
0122             Q_EMIT sigNoteReceived(result.noteTitle, result.noteText);
0123         }
0124     }
0125 
0126     deleteLater();
0127 }
0128 
0129 void NotesNetworkReceiver::slotError(QAbstractSocket::SocketError error)
0130 {
0131     qCWarning(NOTESHARED_LOG) << "error type :" << static_cast<int>(error) << " error string : " << d->m_sock->errorString();
0132 }
0133 
0134 #include "moc_notesnetworkreceiver.cpp"