/* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2014 Eugene Shalygin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "longurl.h" #include #include #include #include #include #include #include "postwidget.h" #include "shortenmanager.h" K_PLUGIN_FACTORY_WITH_JSON(LongUrlFactory, "choqok_longurl.json", registerPlugin < LongUrl > ();) const QString baseLongUrlDorComUrl = QLatin1String("http://api.longurl.org/v2/"); LongUrl::LongUrl(QObject *parent, const QList< QVariant > &) : Choqok::Plugin(QLatin1String("choqok_longurl"), parent) , state(Stopped), mServicesAreFetched(false) { sheduleSupportedServicesFetch(); connect(Choqok::UI::Global::SessionManager::self(), SIGNAL(newPostWidgetAdded(Choqok::UI::PostWidget*,Choqok::Account*,QString)), this, SLOT(slotAddNewPostWidget(Choqok::UI::PostWidget*))); } LongUrl::~LongUrl() { suspendJobs(); mData.clear(); mShortUrls.clear(); for (KJob *job: mParsingList.keys()) { job->kill(); } mParsingList.clear(); } void LongUrl::parse(QPointer< Choqok::UI::PostWidget > postToParse) { if (!postToParse) { return; } QStringList redirectList, pureList = postToParse->urls(); QString content = postToParse->currentPost()->content; for (int i = 0; i < pureList.count(); ++i) { if (pureList[i].length() > 30) { continue; } if (!pureList[i].startsWith(QLatin1String("http"), Qt::CaseInsensitive)) { pureList[i].prepend(QLatin1String("http://")); } redirectList << pureList[i]; } for (const QString &url: redirectList) { KJob *job = sheduleParsing(url); if (job) { mParsingList.insert(job, postToParse); job->start(); } } } void LongUrl::processJobResults(KJob *job) { const QJsonDocument json = QJsonDocument::fromJson(mData[job]); if (json.isNull()) { return; } const QVariantMap m = json.toVariant().toMap(); const QUrl longUrl = m.value(QLatin1String("long-url")).toUrl(); replaceUrl(takeJob(job), QUrl(mShortUrls.take(job)), longUrl); } void LongUrl::startParsing() { int i = 8; while (!postsQueue.isEmpty() && i > 0) { parse(postsQueue.dequeue()); --i; } if (postsQueue.isEmpty()) { state = Stopped; } else { QTimer::singleShot(500, this, SLOT(startParsing())); } } void LongUrl::replaceUrl(LongUrl::PostWidgetPointer post, const QUrl &fromUrl, const QUrl &toUrl) { if (post) { QString content = post->content(); QString fromUrlStr = fromUrl.url(); content.replace(QRegExp(QLatin1String("title='") + fromUrlStr + QLatin1Char('\'')), QLatin1String("title='") + toUrl.url() + QLatin1Char('\'')); content.replace(QRegExp(QLatin1String("href='") + fromUrlStr + QLatin1Char('\'')), QLatin1String("href='") + toUrl.url() + QLatin1Char('\'')); post->setContent(content); Choqok::ShortenManager::self()->emitNewUnshortenedUrl(post, fromUrl, toUrl); } } void LongUrl::sheduleSupportedServicesFetch() { mServicesAreFetched = true; mServicesData = QSharedPointer(new QByteArray()); KIO::TransferJob *job = KIO::get(QUrl(baseLongUrlDorComUrl + QLatin1String("services?format=json")), KIO::NoReload, KIO::HideProgressInfo); connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(servicesDataReceived(KIO::Job*,QByteArray))); connect(job, SIGNAL(result(KJob*)), SLOT(servicesJobResult(KJob*))); } void LongUrl::servicesDataReceived(KIO::Job *job, QByteArray data) { Q_UNUSED(job); mServicesData->append(data); } void LongUrl::servicesJobResult(KJob *job) { if (!job->error()) { const QJsonDocument json = QJsonDocument::fromJson(*mServicesData); if (!json.isNull()) { supportedServices = json.toVariant().toMap().uniqueKeys(); } } else { qCritical() << "Job Error:" << job->errorString(); } mServicesAreFetched = false; mServicesData.clear(); } bool LongUrl::isServiceSupported(const QString &host) { return supportedServices.contains(host); } KJob *LongUrl::sheduleParsing(const QString &shortUrl) { QUrl url(shortUrl); if (isServiceSupported(url.host())) { QUrl request = QUrl(baseLongUrlDorComUrl + QLatin1String("expand")); QUrlQuery requestQuery; requestQuery.addQueryItem(QLatin1String("url"), url.url()); requestQuery.addQueryItem(QLatin1String("format"), QLatin1String("json")); requestQuery.addQueryItem(QLatin1String("user-agent"), QLatin1String("Choqok")); request.setQuery(requestQuery); KIO::TransferJob *job = KIO::get(request, KIO::NoReload, KIO::HideProgressInfo); mData.insert(job, QByteArray()); mShortUrls.insert(job, shortUrl); connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(dataReceived(KIO::Job*,QByteArray))); connect(job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*))); return job; } return 0; } void LongUrl::dataReceived(KIO::Job *job, QByteArray data) { mData[job].append(data); } void LongUrl::jobResult(KJob *job) { if (!job->error()) { processJobResults(job); } mData.remove(job); mShortUrls.remove(job); mParsingList.remove(job); } void LongUrl::slotAddNewPostWidget(Choqok::UI::PostWidget *newWidget) { postsQueue.enqueue(newWidget); if (state == Stopped && !mServicesAreFetched) { state = Running; QTimer::singleShot(1000, this, SLOT(startParsing())); } } void LongUrl::aboutToUnload() { suspendJobs(); base::aboutToUnload(); } void LongUrl::suspendJobs() { for (KJob *job: mParsingList.keys()) { job->suspend(); } } #include "longurl.moc"