File indexing completed on 2024-05-19 05:06:55

0001 /*
0002     SPDX-FileCopyrightText: 2017 Thomas Baumgart <tbaumgart@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "webconnect.h"
0007 
0008 #include <QLocalSocket>
0009 #include <QLocalServer>
0010 #include <QStandardPaths>
0011 #include <QDataStream>
0012 #include <QUrl>
0013 
0014 Q_LOGGING_CATEGORY(WebConnectLog, "WebConnect")
0015 
0016 class WebConnect::Private
0017 {
0018     Q_DISABLE_COPY_MOVE(Private)
0019 
0020 public:
0021     explicit Private(WebConnect* parent)
0022         : q(parent)
0023         , clientSocket(new QLocalSocket(parent))
0024         , serverSocket(0)
0025         , server(new QLocalServer(parent))
0026         , serverFail(false)
0027         , blockSize(0)
0028     {
0029     }
0030 
0031     WebConnect* q;
0032 
0033     QString         socketName;
0034     QLocalSocket*   clientSocket;
0035     QLocalSocket*   serverSocket;
0036     QLocalServer*   server;
0037     bool            serverFail;
0038     quint32         blockSize;
0039 
0040     void startup()
0041     {
0042         // create a per user socket name
0043         socketName = QString("%1/KMyMoney-WebConnect").arg(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation));
0044         // try to find a server
0045         if (!connectToServer()) {
0046             // no other instance seems to be running, so we start the server
0047             if (!server->listen(socketName)) {
0048                 qCInfo(WebConnectLog) << "Starting server failed. Try to remove stale socket.";
0049                 server->removeServer(socketName);
0050                 if(!server->listen(socketName)) {
0051                     qCWarning(WebConnectLog) << "Starting server failed again. WebConnect not available.";
0052                     serverFail = true;
0053                 }
0054             }
0055             if (!serverFail) {
0056                 qCInfo(WebConnectLog) << "Running in server mode";
0057             } else {
0058                 qCWarning(WebConnectLog) << "Unable to start server mode";
0059             }
0060         } else {
0061             qCInfo(WebConnectLog) << "Running in client mode";
0062             clientSocket->disconnectFromServer();
0063         }
0064     }
0065 
0066     bool connectToServer()
0067     {
0068         // try to find a server
0069         qCDebug(WebConnectLog) << "Try to connect to WebConnect server";
0070         clientSocket->setServerName(socketName);
0071         clientSocket->connectToServer();
0072         bool rc = clientSocket->waitForConnected(200);
0073         qCDebug(WebConnectLog) << "Connect to server" << (rc ? "is ok" : "failed");
0074         return rc;
0075     }
0076 };
0077 
0078 WebConnect::WebConnect(QObject* parent)
0079     : QObject(parent)
0080     , d(new Private(this))
0081 {
0082     connect(d->clientSocket, &QLocalSocket::connected, this, &WebConnect::serverConnected);
0083     connect(d->clientSocket, &QLocalSocket::disconnected, this, &WebConnect::serverDisconnected);
0084     connect(d->server, &QLocalServer::newConnection, this, &WebConnect::clientConnected);
0085 
0086     d->startup();
0087 }
0088 
0089 WebConnect::~WebConnect()
0090 {
0091     if (d->server->isListening()) {
0092         d->server->close();
0093     }
0094     delete d;
0095 }
0096 
0097 void WebConnect::clientConnected()
0098 {
0099     qCDebug(WebConnectLog) << "Client connected";
0100     disconnect(d->server, &QLocalServer::newConnection, this, &WebConnect::clientConnected);
0101     if (!d->serverSocket) {
0102         qCDebug(WebConnectLog) << "Get next pending connection";
0103         // Reset to beginning of stream
0104         d->blockSize = 0;
0105         d->serverSocket = d->server->nextPendingConnection();
0106         connect(d->serverSocket, &QLocalSocket::disconnected, this, &WebConnect::clientDisconnected);
0107         connect(d->serverSocket, &QLocalSocket::readyRead, this, &WebConnect::dataAvailable);
0108     }
0109 }
0110 
0111 void WebConnect::clientDisconnected()
0112 {
0113     qCDebug(WebConnectLog) << "Client disconnected";
0114     d->serverSocket->deleteLater();
0115     d->serverSocket = 0;
0116     if (d->server->hasPendingConnections()) {
0117         qCDebug(WebConnectLog) << "Processing next pending connection";
0118         clientConnected();
0119     } else {
0120         qCDebug(WebConnectLog) << "Wait for next client";
0121         connect(d->server, &QLocalServer::newConnection, this, &WebConnect::clientConnected);
0122     }
0123 }
0124 
0125 void WebConnect::serverConnected()
0126 {
0127     qCDebug(WebConnectLog) << "Server connected";
0128 }
0129 
0130 void WebConnect::serverDisconnected()
0131 {
0132     qCDebug(WebConnectLog) << "Server disconnected";
0133 }
0134 
0135 void WebConnect::dataAvailable()
0136 {
0137     QDataStream in(d->serverSocket);
0138     in.setVersion(QDataStream::Qt_4_0);
0139 
0140     if (d->blockSize == 0) {
0141         // Relies on the fact that we put the length into the QDataStream
0142         if (d->serverSocket->bytesAvailable() < (int)sizeof(quint32)) {
0143             return;
0144         }
0145         in >> d->blockSize;
0146     }
0147 
0148     if (d->serverSocket->bytesAvailable() < (int)sizeof(quint32) || d->serverSocket->atEnd()) {
0149         return;
0150     }
0151     QUrl url;
0152     in >> url;
0153     qCDebug(WebConnectLog) << "Processing" << url;
0154     Q_EMIT gotUrl(url);
0155 }
0156 
0157 void WebConnect::loadFile(const QUrl& url)
0158 {
0159     if (d->connectToServer()) {
0160         qCDebug(WebConnectLog) << "Pass to server" << url;
0161         // transfer filename
0162         QByteArray block;
0163         QDataStream stream(&block, QIODevice::WriteOnly);
0164         stream.setVersion(QDataStream::Qt_4_0);
0165         stream << (quint32) 0;
0166         stream << url;
0167         stream.device()->seek(0);
0168         stream << (quint32)(block.size() - sizeof(quint32));
0169         d->clientSocket->write(block);
0170         d->clientSocket->flush();
0171         d->clientSocket->disconnectFromServer();
0172     } else {
0173         qCWarning(WebConnectLog) << "Webconnect loadfile connection failed on client side";
0174     }
0175 }
0176 
0177 bool WebConnect::isClient() const
0178 {
0179     return !d->server->isListening() && !d->serverFail;
0180 }