File indexing completed on 2024-05-12 16:42:16

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