#include "yourlsclient.h" #include #include #include #include #include #include /** * @brief Constructs the client. * @param parent QObject parent. */ YourlsClient::YourlsClient (QObject *parent) : QObject (parent) , m_networkAccessManager (new QNetworkAccessManager (this)) { } /** * @brief Requests a short URL from the YOURLS API. * @param settings API endpoint and signature. * @param longUrl Source URL to shorten. */ void YourlsClient::shortenUrl (const YourlsSettings &settings, const QUrl &longUrl) { if (settings.apiUrl.trimmed().isEmpty()) { emit shortenFailed (QStringLiteral ("API URL is empty.")); return; } if (settings.signature.trimmed().isEmpty()) { emit shortenFailed (QStringLiteral ("API signature is empty.")); return; } if (!longUrl.isValid() || longUrl.scheme().isEmpty() || longUrl.host().isEmpty()) { emit shortenFailed (QStringLiteral ("Clipboard does not contain a valid URL.")); return; } QUrl requestUrl (settings.apiUrl); if (!requestUrl.isValid()) { emit shortenFailed (QStringLiteral ("Configured API URL is invalid: %1").arg (settings.apiUrl)); return; } QUrlQuery query; query.addQueryItem (QStringLiteral ("signature"), settings.signature); query.addQueryItem (QStringLiteral ("action"), QStringLiteral ("shorturl")); query.addQueryItem (QStringLiteral ("format"), QStringLiteral ("json")); query.addQueryItem (QStringLiteral ("url"), longUrl.toString (QUrl::FullyDecoded)); requestUrl.setQuery (query); QNetworkRequest request (requestUrl); request.setHeader (QNetworkRequest::UserAgentHeader, QStringLiteral ("yourls-ui/1.0")); QNetworkReply *reply = m_networkAccessManager->get (request); connect (reply, &QNetworkReply::finished, this, [this, reply]() { const QByteArray body = reply->readAll(); if (reply->error() != QNetworkReply::NoError) { const QString details = QStringLiteral ("Network error: %1\nHTTP status: %2\nResponse body:\n%3") .arg (reply->errorString(), reply->attribute (QNetworkRequest::HttpStatusCodeAttribute).toString(), QString::fromUtf8 (body)); reply->deleteLater(); emit shortenFailed (details); return; } QJsonParseError parseError; const QJsonDocument jsonDocument = QJsonDocument::fromJson (body, &parseError); if (parseError.error != QJsonParseError::NoError || !jsonDocument.isObject()) { const QString details = QStringLiteral ("Failed to parse YOURLS response as JSON: %1\nResponse body:\n%2") .arg (parseError.errorString(), QString::fromUtf8 (body)); reply->deleteLater(); emit shortenFailed (details); return; } const QJsonObject object = jsonDocument.object(); const QString status = object.value (QStringLiteral ("status")).toString(); const QString shortUrlText = object.value (QStringLiteral ("shorturl")).toString(); if (status.compare (QStringLiteral ("success"), Qt::CaseInsensitive) != 0 || shortUrlText.isEmpty()) { QStringList details; details << QStringLiteral ("YOURLS returned an error."); if (object.contains (QStringLiteral ("message"))) details << QStringLiteral ("Message: %1").arg (object.value (QStringLiteral ("message")).toString()); if (object.contains (QStringLiteral ("code"))) details << QStringLiteral ("Code: %1").arg (object.value (QStringLiteral ("code")).toVariant().toString()); details << QStringLiteral ("Raw response:\n%1").arg (QString::fromUtf8 (body)); reply->deleteLater(); emit shortenFailed (details.join (QStringLiteral ("\n"))); return; } const QUrl shortUrl (shortUrlText); if (!shortUrl.isValid()) { reply->deleteLater(); emit shortenFailed (QStringLiteral ("YOURLS returned an invalid short URL: %1").arg (shortUrlText)); return; } reply->deleteLater(); emit shortenSucceeded (shortUrl); }); }