File indexing completed on 2024-05-12 08:57:58

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003 
0004     SPDX-FileCopyrightText: 2014 Eugene Shalygin <eugene.shalygin@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "longurl.h"
0010 
0011 #include <QDebug>
0012 #include <QJsonDocument>
0013 #include <QSharedPointer>
0014 #include <QTimer>
0015 
0016 #include <KIO/TransferJob>
0017 #include <KPluginFactory>
0018 
0019 #include "postwidget.h"
0020 #include "shortenmanager.h"
0021 
0022 K_PLUGIN_CLASS_WITH_JSON(LongUrl, "choqok_longurl.json")
0023 
0024 const QString baseLongUrlDorComUrl = QLatin1String("http://api.longurl.org/v2/");
0025 
0026 LongUrl::LongUrl(QObject *parent, const QList< QVariant > &)
0027     : Choqok::Plugin(QLatin1String("choqok_longurl"), parent)
0028     , state(Stopped), mServicesAreFetched(false)
0029 {
0030     sheduleSupportedServicesFetch();
0031     connect(Choqok::UI::Global::SessionManager::self(),
0032             SIGNAL(newPostWidgetAdded(Choqok::UI::PostWidget*,Choqok::Account*,QString)),
0033             this,
0034             SLOT(slotAddNewPostWidget(Choqok::UI::PostWidget*)));
0035 }
0036 
0037 LongUrl::~LongUrl()
0038 {
0039     suspendJobs();
0040     mData.clear();
0041     mShortUrls.clear();
0042     for (KJob *job: mParsingList.keys()) {
0043         job->kill();
0044     }
0045     mParsingList.clear();
0046 }
0047 
0048 void LongUrl::parse(QPointer< Choqok::UI::PostWidget > postToParse)
0049 {
0050     if (!postToParse) {
0051         return;
0052     }
0053     QStringList redirectList, pureList = postToParse->urls();
0054     QString content = postToParse->currentPost()->content;
0055     for (int i = 0; i < pureList.count(); ++i) {
0056         if (pureList[i].length() > 30) {
0057             continue;
0058         }
0059         if (!pureList[i].startsWith(QLatin1String("http"), Qt::CaseInsensitive)) {
0060             pureList[i].prepend(QLatin1String("http://"));
0061         }
0062         redirectList << pureList[i];
0063     }
0064     for (const QString &url: redirectList) {
0065         KJob *job = sheduleParsing(url);
0066         if (job) {
0067             mParsingList.insert(job, postToParse);
0068             job->start();
0069         }
0070     }
0071 }
0072 
0073 void LongUrl::processJobResults(KJob *job)
0074 {
0075     const QJsonDocument json = QJsonDocument::fromJson(mData[job]);
0076     if (json.isNull()) {
0077         return;
0078     }
0079     const QVariantMap m = json.toVariant().toMap();
0080     const QUrl longUrl = m.value(QLatin1String("long-url")).toUrl();
0081     replaceUrl(takeJob(job), QUrl(mShortUrls.take(job)), longUrl);
0082 }
0083 
0084 void LongUrl::startParsing()
0085 {
0086     int i = 8;
0087     while (!postsQueue.isEmpty() && i > 0) {
0088         parse(postsQueue.dequeue());
0089         --i;
0090     }
0091 
0092     if (postsQueue.isEmpty()) {
0093         state = Stopped;
0094     } else {
0095         QTimer::singleShot(500, this, SLOT(startParsing()));
0096     }
0097 }
0098 
0099 void LongUrl::replaceUrl(LongUrl::PostWidgetPointer post, const QUrl &fromUrl, const QUrl &toUrl)
0100 {
0101     if (post) {
0102         QString content = post->content();
0103         QString fromUrlStr = fromUrl.url();
0104         content.replace(QRegExp(QLatin1String("title='") + fromUrlStr + QLatin1Char('\'')), QLatin1String("title='") + toUrl.url() + QLatin1Char('\''));
0105         content.replace(QRegExp(QLatin1String("href='") + fromUrlStr + QLatin1Char('\'')), QLatin1String("href='") + toUrl.url() + QLatin1Char('\''));
0106         post->setContent(content);
0107         Choqok::ShortenManager::self()->emitNewUnshortenedUrl(post, fromUrl, toUrl);
0108     }
0109 }
0110 
0111 void LongUrl::sheduleSupportedServicesFetch()
0112 {
0113     mServicesAreFetched = true;
0114     mServicesData = QSharedPointer<QByteArray>(new QByteArray());
0115     KIO::TransferJob *job = KIO::get(QUrl(baseLongUrlDorComUrl + QLatin1String("services?format=json")), KIO::NoReload, KIO::HideProgressInfo);
0116     connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(servicesDataReceived(KIO::Job*,QByteArray)));
0117     connect(job, SIGNAL(result(KJob*)), SLOT(servicesJobResult(KJob*)));
0118 }
0119 
0120 void LongUrl::servicesDataReceived(KIO::Job *job, QByteArray data)
0121 {
0122     Q_UNUSED(job);
0123     mServicesData->append(data);
0124 }
0125 
0126 void LongUrl::servicesJobResult(KJob *job)
0127 {
0128     if (!job->error()) {
0129         const QJsonDocument json = QJsonDocument::fromJson(*mServicesData);
0130         if (!json.isNull()) {
0131             supportedServices = json.toVariant().toMap().uniqueKeys();
0132         }
0133     } else {
0134         qCritical() << "Job Error:" << job->errorString();
0135     }
0136     mServicesAreFetched = false;
0137     mServicesData.clear();
0138 }
0139 
0140 bool LongUrl::isServiceSupported(const QString &host)
0141 {
0142     return supportedServices.contains(host);
0143 }
0144 
0145 KJob *LongUrl::sheduleParsing(const QString &shortUrl)
0146 {
0147     QUrl url(shortUrl);
0148     if (isServiceSupported(url.host())) {
0149         QUrl request = QUrl(baseLongUrlDorComUrl + QLatin1String("expand"));
0150         QUrlQuery requestQuery;
0151         requestQuery.addQueryItem(QLatin1String("url"), url.url());
0152         requestQuery.addQueryItem(QLatin1String("format"), QLatin1String("json"));
0153         requestQuery.addQueryItem(QLatin1String("user-agent"), QLatin1String("Choqok"));
0154         request.setQuery(requestQuery);
0155 
0156         KIO::TransferJob *job = KIO::get(request, KIO::NoReload, KIO::HideProgressInfo);
0157         mData.insert(job, QByteArray());
0158         mShortUrls.insert(job, shortUrl);
0159         connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(dataReceived(KIO::Job*,QByteArray)));
0160         connect(job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*)));
0161         return job;
0162     }
0163     return 0;
0164 }
0165 
0166 void LongUrl::dataReceived(KIO::Job *job, QByteArray data)
0167 {
0168     mData[job].append(data);
0169 }
0170 
0171 void LongUrl::jobResult(KJob *job)
0172 {
0173     if (!job->error()) {
0174         processJobResults(job);
0175     }
0176     mData.remove(job);
0177     mShortUrls.remove(job);
0178     mParsingList.remove(job);
0179 }
0180 
0181 void LongUrl::slotAddNewPostWidget(Choqok::UI::PostWidget *newWidget)
0182 {
0183     postsQueue.enqueue(newWidget);
0184     if (state == Stopped && !mServicesAreFetched) {
0185         state = Running;
0186         QTimer::singleShot(1000, this, SLOT(startParsing()));
0187     }
0188 }
0189 
0190 void LongUrl::aboutToUnload()
0191 {
0192     suspendJobs();
0193     base::aboutToUnload();
0194 }
0195 
0196 void LongUrl::suspendJobs()
0197 {
0198     for (KJob *job: mParsingList.keys()) {
0199         job->suspend();
0200     }
0201 }
0202 
0203 #include "longurl.moc"
0204 #include "moc_longurl.cpp"