#include "./connection.h" #include "../alpm/manager.h" #include "../alpm/upgradelookup.h" #include "../alpm/suggestionslookup.h" #include "../alpm/packageinfolookup.h" #include "../alpm/config.h" #include #include #include #include #include #include using namespace std; namespace RepoIndex { Connection::Connection(Manager &manager, QWebSocket *socket, QObject *parent) : QObject(parent), m_socket(socket), m_manager(manager), m_repoInfoUpdatesRequested(false), m_groupInfoUpdatesRequested(false) { socket->setParent(this); connect(socket, &QWebSocket::textMessageReceived, this, &Connection::processTextMessage); connect(socket, &QWebSocket::binaryMessageReceived, this, &Connection::processBinaryMessage); connect(socket, &QWebSocket::disconnected, this, &Connection::socketDisconnected); connect(&manager, &Manager::updatesAvailable, this, &Connection::updatedAvailable); } void Connection::sendJson(const QJsonObject &obj) { m_socket->sendTextMessage(QJsonDocument(obj).toJson(QJsonDocument::Compact)); } void Connection::sendError(const QString &msg, const QJsonValue &id) { QJsonObject response; response.insert(QStringLiteral("class"), QStringLiteral("error")); response.insert(QStringLiteral("msg"), msg); if(!id.isNull() && !id.isUndefined()) { response.insert(QStringLiteral("id"), id); } sendJson(response); } void Connection::sendResult(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value) { QJsonObject response; response.insert(QStringLiteral("class"), QStringLiteral("results")); response.insert(QStringLiteral("what"), what); response.insert(QStringLiteral("value"), value); if(!id.isNull() && !id.isUndefined()) { response.insert("id", id); } sendJson(response); } void Connection::sendResults(const QJsonValue &what, const QJsonValue &id, const QJsonValue &values) { QJsonObject response; response.insert(QStringLiteral("class"), QStringLiteral("results")); response.insert(QStringLiteral("what"), what); response.insert(QStringLiteral("values"), values); if(!id.isNull() && !id.isUndefined()) { response.insert("id", id); } sendJson(response); } void Connection::updatedAvailable() { if(m_repoInfoUpdatesRequested) { sendResult(QStringLiteral("basicrepoinfo"), QJsonValue(), m_manager.basicRepoInfo()); } if(m_groupInfoUpdatesRequested) { sendResults(QStringLiteral("groupinfo"), QJsonValue(), m_manager.groupInfo()); } } template void Connection::performLookup(const QJsonObject &request, Args &&...args) { auto *lookup = new Lookup(forward(args)...); if(lookup->finished()) { // the lookup has already finished when constructing the lookup object // -> send the results immidiately sendResult(request.value(QStringLiteral("what")), request.value(QStringLiteral("id")), lookup->results()); } else if(!lookup->errors().isEmpty()) { // error occured during the construction of the lookup object // -> send the errors immidiately QJsonObject results; results.insert(QStringLiteral("errors"), lookup->errors()); sendResult(request.value(QStringLiteral("what")), request.value(QStringLiteral("id")), results); } else { // the lookup object has been created without errors but the lookup is not done yet // -> send results when available connect(lookup, &Lookup::resultsAvailable, this, &Connection::sendResult); } // in any case: the lookup object destroys itself } void Connection::handleQuery(const QJsonObject &obj) { const auto what = obj.value(QStringLiteral("what")).toString(); const auto id = obj.value(QStringLiteral("id")); if(what == QLatin1String("basicrepoinfo")) { m_repoInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_repoInfoUpdatesRequested); sendResult(what, id, m_manager.basicRepoInfo()); } else if(what == QLatin1String("basicpkginfo") || what == QLatin1String("pkgdetails") || what == QLatin1String("fullpkginfo")) { performLookup(obj, m_manager, obj); } else if(what == QLatin1String("groupinfo")) { m_groupInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_groupInfoUpdatesRequested); sendResults(what, id, m_manager.groupInfo()); } else if(what == QLatin1String("suggestions")) { performLookup(obj, m_manager, obj); } else if(what == QLatin1String("upgradelookup")) { performLookup(obj, m_manager, obj); } else if(what == QLatin1String("ping")) { sendResult(what, id, QStringLiteral("pong")); } else { sendError(QStringLiteral("unknown query"), id); } } void Connection::handleCmd(const QJsonObject &obj) { const auto what = obj.value(QStringLiteral("what")).toString(); const auto id = obj.value(QStringLiteral("id")); if(what == QLatin1String("stop")) { if(m_socket->peerAddress().isLoopback()) { cerr << shchar << "Info: Server stopped via web interface." << endl; QCoreApplication::quit(); } else { sendError(QStringLiteral("rejected"), id); } } else if(what == QLatin1String("reinitalpm")) { if(m_socket->peerAddress().isLoopback()) { cerr << shchar << "Info: Re-initializing ALPM databases (triggered via web interface) ..." << endl; m_manager.removeAllDatabases(); if(m_manager.config().isLocalDatabaseEnabled()) { m_manager.addLocalDatabase(); } if(m_manager.config().areReposFromPacmanConfEnabled()) { m_manager.addDataBasesFromPacmanConfig(); } m_manager.addDatabasesFromRepoIndexConfig(); m_manager.initAlpmDataBases(); } else { sendError(QStringLiteral("rejected"), id); } } else if(what == QLatin1String("updatealpm")) { if(m_socket->peerAddress().isLoopback()) { cerr << shchar << "Info: Forcing update of all ALPM databases (triggered via web interface) ..." << endl; m_manager.forceUpdateAlpmDatabases(); } else { sendError(QStringLiteral("rejected"), id); } } else { sendError(QStringLiteral("unknown command"), id); } } void Connection::processTextMessage(const QString &message) { QJsonParseError error; QByteArray jsonData; jsonData.append(message); const QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error); if(error.error == QJsonParseError::NoError) { const QJsonObject obj = doc.object(); const QString msgClass = obj.value(QStringLiteral("class")).toString(); if(msgClass == QLatin1String("query")) { handleQuery(obj); } else if(msgClass == QLatin1String("cmd")) { handleCmd(obj); } else { sendError(QStringLiteral("no message class specified")); } } else { sendError(QStringLiteral("unable to parse JSON: ") + error.errorString()); } } void Connection::processBinaryMessage(const QByteArray &) { sendError(QStringLiteral("unable to parse binary messages")); } void Connection::socketDisconnected() { deleteLater(); } }