diff --git a/src/alternativesmodel.cpp b/src/alternativesmodel.cpp index 78af424..d9aa315 100644 --- a/src/alternativesmodel.cpp +++ b/src/alternativesmodel.cpp @@ -1,341 +1,339 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "alternativesmodel.h" #include #include #include -#include #include #include -#include #include #include #include #include #include #include #include #include #include #include "helper.h" #include "configuration.h" #include "job.h" using namespace Purpose; static const QStringList s_defaultDisabledPlugins = {QStringLiteral("saveasplugin")}; typedef bool (*matchFunction)(const QString& constraint, const QJsonValue& value); static bool defaultMatch(const QString& constraint, const QJsonValue& value) { return value == QJsonValue(constraint); } static bool mimeTypeMatch(const QString& constraint, const QJsonValue& value) { if(value.isArray()) { const auto array = value.toArray(); for (const QJsonValue& val : array) { if (mimeTypeMatch(constraint, val)) return true; } return false; } else if(value.isObject()) { for(const QJsonValue& val : value.toObject()) { if (mimeTypeMatch(constraint, val)) return true; } return false; } else if(constraint.contains(QLatin1Char('*'))) { return QRegExp(constraint, Qt::CaseInsensitive, QRegExp::Wildcard).exactMatch(value.toString()); } else { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(value.toString()); return mime.inherits(constraint); } } static bool dbusMatch(const QString& constraint, const QJsonValue& value) { Q_UNUSED(value) return QDBusConnection::sessionBus().interface()->isServiceRegistered(constraint); } static bool executablePresent(const QString& constraint, const QJsonValue& value) { Q_UNUSED(value) return !QStandardPaths::findExecutable(constraint).isEmpty(); } static bool desktopFilePresent(const QString& constraint, const QJsonValue& value) { Q_UNUSED(value) return !QStandardPaths::locate(QStandardPaths::ApplicationsLocation, constraint).isEmpty(); } static QMap s_matchFunctions = { { QStringLiteral("mimeType"), mimeTypeMatch }, { QStringLiteral("dbus"), dbusMatch }, { QStringLiteral("application"), desktopFilePresent }, { QStringLiteral("exec"), executablePresent } }; class Purpose::AlternativesModelPrivate { public: QVector m_plugins; QJsonObject m_inputData; QString m_pluginType; QStringList m_disabledPlugins = s_defaultDisabledPlugins; QJsonObject m_pluginTypeData; const QRegularExpression constraintRx { QStringLiteral("(\\w+):(.*)") }; bool isPluginAcceptable(const KPluginMetaData &meta, const QStringList &disabledPlugins) const { const QJsonObject obj = meta.rawData(); if(!obj.value(QStringLiteral("X-Purpose-PluginTypes")).toArray().contains(m_pluginType)) { qDebug() << "discarding" << meta.name() << KPluginMetaData::readStringList(meta.rawData(), QStringLiteral("X-Purpose-PluginTypes")); return false; } if (disabledPlugins.contains(meta.pluginId()) || m_disabledPlugins.contains(meta.pluginId())) { //qDebug() << "disabled plugin" << meta.name() << meta.pluginId(); return false; } //All constraints must match const QJsonArray constraints = obj.value(QStringLiteral("X-Purpose-Constraints")).toArray(); for(const QJsonValue& constraint: constraints) { if (!constraintMatches(meta, constraint)) return false; } return true; } bool constraintMatches(const KPluginMetaData &meta, const QJsonValue &constraint) const { //Treat an array as an OR if (constraint.isArray()) { const QJsonArray options = constraint.toArray(); for (const auto &option: options) { if (constraintMatches(meta, option)) { return true; } } return false; } Q_ASSERT(constraintRx.isValid()); QRegularExpressionMatch match = constraintRx.match(constraint.toString()); if (!match.isValid() || !match.hasMatch()) { qWarning() << "wrong constraint" << constraint.toString(); return false; } const QString propertyName = match.captured(1); const QString constrainedValue = match.captured(2); const bool acceptable = s_matchFunctions.value(propertyName, defaultMatch)(constrainedValue, m_inputData.value(propertyName)); if (!acceptable) { // qDebug() << "not accepted" << meta.name() << propertyName << constrainedValue << m_inputData[propertyName]; } return acceptable; } }; AlternativesModel::AlternativesModel(QObject* parent) : QAbstractListModel(parent) , d_ptr(new AlternativesModelPrivate) { } AlternativesModel::~AlternativesModel() { Q_D(AlternativesModel); delete d; } QHash AlternativesModel::roleNames() const { QHash roles = QAbstractListModel::roleNames(); roles.unite({ { IconNameRole, "iconName" }, { PluginIdRole, "pluginId" }, { ActionDisplayRole, "actionDisplay" } }); return roles; } void AlternativesModel::setInputData(const QJsonObject &input) { Q_D(AlternativesModel); if (input == d->m_inputData) return; d->m_inputData = input; initializeModel(); Q_EMIT inputDataChanged(); } void AlternativesModel::setPluginType(const QString& pluginType) { Q_D(AlternativesModel); if (pluginType == d->m_pluginType) return; d->m_pluginTypeData = Purpose::readPluginType(pluginType); d->m_pluginType = pluginType; Q_ASSERT(d->m_pluginTypeData.isEmpty() == d->m_pluginType.isEmpty()); initializeModel(); Q_EMIT pluginTypeChanged(); } QStringList AlternativesModel::disabledPlugins() const { Q_D(const AlternativesModel); return d->m_disabledPlugins; } void AlternativesModel::setDisabledPlugins(const QStringList &pluginIds) { Q_D(AlternativesModel); if (pluginIds == d->m_disabledPlugins) return; d->m_disabledPlugins = pluginIds; initializeModel(); Q_EMIT disabledPluginsChanged(); } QString AlternativesModel::pluginType() const { Q_D(const AlternativesModel); return d->m_pluginType; } QJsonObject AlternativesModel::inputData() const { Q_D(const AlternativesModel); return d->m_inputData; } Purpose::Configuration* AlternativesModel::configureJob(int row) { Q_D(AlternativesModel); const KPluginMetaData pluginData = d->m_plugins.at(row); return new Configuration(d->m_inputData, d->m_pluginType, d->m_pluginTypeData, pluginData); } int AlternativesModel::rowCount(const QModelIndex& parent) const { Q_D(const AlternativesModel); return parent.isValid() ? 0 : d->m_plugins.count(); } QVariant AlternativesModel::data(const QModelIndex& index, int role) const { Q_D(const AlternativesModel); if (!index.isValid() || index.row()>d->m_plugins.count()) return QVariant(); KPluginMetaData data = d->m_plugins[index.row()]; switch (role) { case Qt::DisplayRole: return data.name(); case Qt::ToolTip: return data.description(); case IconNameRole: return data.iconName(); case Qt::DecorationRole: return QIcon::fromTheme(data.iconName()); case PluginIdRole: return data.pluginId(); case ActionDisplayRole: { QString action = data.rawData()[QStringLiteral("KPlugin")].toObject()[QStringLiteral("X-Purpose-ActionDisplay")].toString(); return action.isEmpty() ? data.name() : action; } } return QVariant(); } static QVector findScriptedPackages(std::function filter) { QVector ret; QSet addedPlugins; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kpackage/Purpose"), QStandardPaths::LocateDirectory); for (const QString &dir : dirs) { QDirIterator dirIt(dir, QDir::Dirs | QDir::NoDotAndDotDot); for(; dirIt.hasNext(); ) { QDir dir(dirIt.next()); Q_ASSERT(dir.exists()); if (!dir.exists(QStringLiteral("metadata.json"))) continue; const KPluginMetaData info = Purpose::createMetaData(dir.absoluteFilePath(QStringLiteral("metadata.json"))); if (!addedPlugins.contains(info.pluginId()) && filter(info)) { addedPlugins << info.pluginId(); ret += info; } } } return ret; } void AlternativesModel::initializeModel() { Q_D(AlternativesModel); if (d->m_pluginType.isEmpty()) { return; } const QJsonArray inbound = d->m_pluginTypeData.value(QStringLiteral("X-Purpose-InboundArguments")).toArray(); for (const QJsonValue& arg : inbound) { if(!d->m_inputData.contains(arg.toString())) { qWarning() << "Cannot initialize model with data" << d->m_inputData << ". missing:" << arg; return; } } const auto config = KSharedConfig::openConfig(QStringLiteral("purposerc")); const auto group = config->group("plugins"); const QStringList disabledPlugins = group.readEntry("disabled", QStringList()); auto pluginAcceptable = [d, disabledPlugins](const KPluginMetaData& meta) { return d->isPluginAcceptable(meta, disabledPlugins); }; beginResetModel(); d->m_plugins.clear(); const auto plugins = KPluginLoader::findPlugins(QStringLiteral("kf5/purpose")); QSet addedPlugins; for (const auto &metaData : plugins) { if (!addedPlugins.contains(metaData.pluginId()) && pluginAcceptable(metaData)) { addedPlugins << metaData.pluginId(); d->m_plugins << metaData; } } d->m_plugins += findScriptedPackages(pluginAcceptable); endResetModel(); } diff --git a/src/externalprocess/purposeprocess_main.cpp b/src/externalprocess/purposeprocess_main.cpp index 40c40d1..79a38d0 100644 --- a/src/externalprocess/purposeprocess_main.cpp +++ b/src/externalprocess/purposeprocess_main.cpp @@ -1,162 +1,161 @@ /* Copyright 2015 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include #include -#include #include #include "helper.h" #include "purpose_external_process_debug.h" #include #include static QString pluginType; static KPluginMetaData md; class Communication : public QObject { Q_OBJECT public: Communication(const QString &serverName) { int pcIdx = metaObject()->indexOfSlot("propertyChanged()"); Q_ASSERT(pcIdx>=0); const QMetaMethod propertyChangedMethod = metaObject()->method(pcIdx); m_socket.setServerName(serverName); m_socket.connectToServer(QIODevice::ReadWrite); connect(&m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(error())); bool b = m_socket.waitForConnected(); Q_ASSERT(b); b = m_socket.waitForReadyRead(); Q_ASSERT(b); Q_ASSERT(m_socket.canReadLine()); QByteArray byteLine = m_socket.readLine(); byteLine.chop(1); // Drop \n const qint64 bytes = byteLine.toLongLong(); // QByteArray and QJsonDocument::from* can only handle int size. // If the payload is bigger we are screwed. Q_ASSERT(bytes <= std::numeric_limits::max()); QByteArray dataArray; dataArray.resize(bytes); int pos = 0; bool couldRead = false; while ((pos < bytes) && (couldRead = (m_socket.bytesAvailable() || m_socket.waitForReadyRead()))) { pos += m_socket.read(dataArray.data() + pos, qMin(m_socket.bytesAvailable(), bytes-pos)); } Q_ASSERT(couldRead); // false if we hit a timeout before read-end. Q_ASSERT(pos == bytes); Purpose::Configuration config(QJsonDocument::fromBinaryData(dataArray).object(), pluginType, md); config.setUseSeparateProcess(false); Q_ASSERT(config.isReady()); m_job = config.createJob(); m_job->start(); const QMetaObject* m = m_job->metaObject(); for(int i = 0, c = m->propertyCount(); iproperty(i); if (prop.hasNotifySignal() && prop.isReadable()) { connect(m_job, prop.notifySignal(), this, propertyChangedMethod, Qt::UniqueConnection); } } } private Q_SLOTS: void error() { qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "socket error:" << m_socket.error(); } void propertyChanged() { const int idx = senderSignalIndex(); const QMetaObject* m = m_job->metaObject(); QJsonObject toSend; for(int i = 0, c = m->propertyCount(); iproperty(i); if (prop.notifySignalIndex() == idx) { toSend[QString::fromLatin1(prop.name())] = fromVariant(prop.read(m_job)); } } send(toSend); } static QJsonValue fromVariant(const QVariant &var) { if (var.canConvert()) { return var.toJsonObject(); } else { return QJsonValue::fromVariant(var); } } private: void send(const QJsonObject &object) { const QByteArray data = QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'; // qCDebug(PURPOSE_EXTERNAL_PROCESS_LOG) << "sending..." << data; m_socket.write(data); } Purpose::Job* m_job = nullptr; QLocalSocket m_socket; }; int main(int argc, char** argv) { #pragma message("warning: make QGuiApplication, consider QCoreApplication?") QApplication app(argc, argv); QString serverName; { QCommandLineParser parser; parser.addOption(QCommandLineOption(QStringLiteral("server"), QStringLiteral("Named socket to connect to"), QStringLiteral("name"))); parser.addOption(QCommandLineOption(QStringLiteral("pluginPath"), QStringLiteral("Chosen plugin"), QStringLiteral("path"))); parser.addOption(QCommandLineOption(QStringLiteral("pluginType"), QStringLiteral("Plugin type name "), QStringLiteral("path"))); parser.addHelpOption(); parser.process(app); serverName = parser.value(QStringLiteral("server")); pluginType = parser.value(QStringLiteral("pluginType")); const QString path = parser.value(QStringLiteral("pluginPath")); if (path.endsWith(QLatin1String("/metadata.json"))) { QFileInfo fi(path); md = Purpose::createMetaData(path); } else { md = KPluginMetaData(path); Q_ASSERT(md.isValid()); } } Communication c(serverName); return app.exec(); } #include "purposeprocess_main.moc" diff --git a/src/helper.h b/src/helper.h index f64ad28..08724c1 100644 --- a/src/helper.h +++ b/src/helper.h @@ -1,33 +1,32 @@ /* Copyright 2015 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PURPOSE_HELPER_H #define PURPOSE_HELPER_H -#include #include #include namespace Purpose { QJsonObject readPluginType(const QString &pluginType); KPluginMetaData createMetaData(const QString &file); } #endif diff --git a/src/job.cpp b/src/job.cpp index c123776..8dd03e2 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -1,66 +1,64 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "job.h" -#include -#include using namespace Purpose; class Purpose::JobPrivate { public: QJsonObject m_data; QJsonObject m_output = { {QStringLiteral("uninitialized"), QStringLiteral("true") } }; }; Job::Job(QObject* parent) : KJob(parent) , d_ptr(new JobPrivate) { } Job::~Job() { } QJsonObject Job::data() const { Q_D(const Job); return d->m_data; } void Job::setData(const QJsonObject &data) { Q_D(Job); d->m_data = data; } QJsonObject Job::output() const { Q_D(const Job); return d->m_output; } void Job::setOutput(const QJsonObject& output) { Q_D(Job); if (d->m_output!=output) { d->m_output = output; Q_EMIT outputChanged(output); } } diff --git a/src/plugins/bluetooth/bluetoothplugin.cpp b/src/plugins/bluetooth/bluetoothplugin.cpp index 6f2d542..842d076 100644 --- a/src/plugins/bluetooth/bluetoothplugin.cpp +++ b/src/plugins/bluetooth/bluetoothplugin.cpp @@ -1,95 +1,96 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include +#include #include #include #include EXPORT_SHARE_VERSION class BluetoothJob : public Purpose::Job { Q_OBJECT public: BluetoothJob(QObject* parent) : Purpose::Job(parent) {} QStringList arrayToList(const QJsonArray& array) { QStringList ret; for (const QJsonValue& val : array) { QUrl url(val.toString()); if(url.isLocalFile()) { ret += url.toLocalFile(); } } return ret; } void start() override { QProcess* process = new QProcess(this); process->setProgram(QStringLiteral("bluedevil-sendfile")); QJsonArray urlsJson = data().value(QStringLiteral("urls")).toArray(); process->setArguments(QStringList(QStringLiteral("-u")) << data().value(QStringLiteral("device")).toString() << QStringLiteral("-f") << arrayToList(urlsJson)); connect(process, &QProcess::errorOccurred, this, &BluetoothJob::processError); connect(process, QOverload::of(&QProcess::finished), this, &BluetoothJob::jobFinished); connect(process, &QProcess::readyRead, this, [process](){ qDebug() << "bluedevil-sendfile output:" << process->readAll(); }); process->start(); } void processError(QProcess::ProcessError error) { QProcess* process = qobject_cast(sender()); qWarning() << "bluetooth share error:" << error << process->errorString(); setError(1 + error); setErrorText(process->errorString()); emitResult(); } void jobFinished(int code, QProcess::ExitStatus status) { if (status != QProcess::NormalExit) qWarning() << "bluedevil-sendfile crashed"; setError(code); setOutput( {{ QStringLiteral("url"), QString() }}); emitResult(); } private: }; class Q_DECL_EXPORT BluetoothPlugin : public Purpose::PluginBase { Q_OBJECT public: BluetoothPlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new BluetoothJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(BluetoothPlugin, "bluetoothplugin.json") #include "bluetoothplugin.moc" diff --git a/src/plugins/email/emailplugin.cpp b/src/plugins/email/emailplugin.cpp index a12674b..d5a87c0 100644 --- a/src/plugins/email/emailplugin.cpp +++ b/src/plugins/email/emailplugin.cpp @@ -1,158 +1,160 @@ /* Copyright 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include #include #include #include +#include +#include EXPORT_SHARE_VERSION namespace { class EmailJob : public Purpose::Job { Q_OBJECT public: explicit EmailJob(QObject *parent = nullptr) : Purpose::Job(parent) {} void start() override { // Use xdg-mime to figure out what is the user's default email client const auto xdgmime = QStandardPaths::findExecutable(QStringLiteral("xdg-mime")); if (xdgmime.isEmpty()) { // xdg-utils not available, let Qt figure what to do for us... launchMailto(); return; } auto xdgmimeProc = new QProcess(this); xdgmimeProc->setProgram(xdgmime); xdgmimeProc->setArguments({ QStringLiteral("query"), QStringLiteral("default"), QStringLiteral("x-scheme-handler/mailto") }); connect(xdgmimeProc, static_cast(&QProcess::finished), this, &EmailJob::xdgMimeFinished); xdgmimeProc->start(); } void xdgMimeFinished(int code, QProcess::ExitStatus status) { if (code != 0 || status != QProcess::NormalExit) { // Something went wrong, fallback to QDesktopServices launchMailto(); return; } const auto proc = qobject_cast(sender()); const auto mailService = proc->readAllStandardOutput(); qDebug() << "Default mailto handler:" << mailService; // Thunderbird is a special snowflake and cannot handle attachments via // the mailto schema, so we need to handle it ourselves if (mailService.contains("thunderbird")) { launchThunderbird(); } else { launchMailto(); } } void launchMailto() { const auto urls = data().value(QStringLiteral("urls")).toArray(); QUrlQuery query; for (const auto &att : urls) { QUrl url(att.toString()); if (url.isLocalFile()) { query.addQueryItem(QStringLiteral("attachment"), att.toString()); } else { query.addQueryItem(QStringLiteral("body"), att.toString()); query.addQueryItem(QStringLiteral("subject"), data().value(QStringLiteral("title")).toString()); } } QUrl url; url.setScheme(QStringLiteral("mailto")); url.setQuery(query); if (!QDesktopServices::openUrl(url)) { setError(KJob::UserDefinedError); setErrorText(i18n("Failed to launch email client")); } emitResult(); } void launchThunderbird() { // thunderbird -compose "attachment='file:///att1','file:///att2'" const auto tb = QStandardPaths::findExecutable(QStringLiteral("thunderbird")); if (tb.isEmpty()) { launchMailto(); return; } const auto urls = data().value(QStringLiteral("urls")).toArray(); QStringList attachments; QStringList args = QStringList{ QStringLiteral("-compose")}; QString message; for (const auto &att : urls) { QUrl url(att.toString()); if (url.isLocalFile()) { attachments.push_back(att.toString()); } else { message += QStringLiteral("body='%1',subject='%2',").arg(url.toString()).arg(data().value(QStringLiteral("title")).toString()); } } message +=(QStringLiteral("attachment='%1'").arg(attachments.join(QLatin1Char(',')))); args.append(message); if (!QProcess::startDetached(tb, args)) { setError(KJob::UserDefinedError); setErrorText(i18n("Failed to launch email client")); } emitResult(); } }; } class Q_DECL_EXPORT EmailPlugin : public Purpose::PluginBase { Q_OBJECT public: EmailPlugin(QObject *p, const QVariantList &) : Purpose::PluginBase(p) {} Purpose::Job *createJob() const override { return new EmailJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(EmailPlugin, "emailplugin.json") #include "emailplugin.moc" diff --git a/src/plugins/imgur/imgurplugin.cpp b/src/plugins/imgur/imgurplugin.cpp index d1fb24a..bf1e332 100644 --- a/src/plugins/imgur/imgurplugin.cpp +++ b/src/plugins/imgur/imgurplugin.cpp @@ -1,177 +1,175 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include "mpform.h" #include -#include #include #include #include #include -#include #include #include #include #include #include EXPORT_SHARE_VERSION Q_GLOBAL_STATIC_WITH_ARGS(const QUrl, imageImgurUrl, (QLatin1String("https://api.imgur.com/3/image"))) Q_GLOBAL_STATIC_WITH_ARGS(const QUrl, albumImgurUrl, (QLatin1String("https://api.imgur.com/3/album"))) // key associated with aleixpol@kde.org Q_GLOBAL_STATIC_WITH_ARGS(const QString, YOUR_CLIENT_ID, (QLatin1String("0bffa5b4ac8383c"))) class ImgurShareJob : public Purpose::Job { Q_OBJECT public: explicit ImgurShareJob(QObject* parent) : Purpose::Job(parent) , m_pendingJobs(0) {} void start() override { m_pendingJobs = 0; const QJsonArray urls = data().value(QStringLiteral("urls")).toArray(); if (urls.isEmpty()) { qWarning() << "no urls to share" << urls << data(); emitResult(); return; } if (urls.count()>1) { KIO::TransferJob *tJob = KIO::storedHttpPost("", *albumImgurUrl, KIO::HideProgressInfo); tJob->setMetaData(QMap{ { QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: Client-ID ") + *YOUR_CLIENT_ID } }); connect(tJob, &KJob::result, this, &ImgurShareJob::albumCreated); } else { startUploading(); } } QJsonObject processResponse(KJob* job) { KIO::StoredTransferJob *sjob = qobject_cast(job); QJsonParseError error; const QJsonObject resultMap = QJsonDocument::fromJson(sjob->data(), &error).object(); if (sjob->isErrorPage()) { setError(3); setErrorText(i18n("Error page returned")); } else if (job->error()) { setError(job->error()); setErrorText(job->errorText()); } else if (error.error) { setError(1); setErrorText(error.errorString()); } else if (!resultMap.value(QStringLiteral("success")).toBool()) { setError(2); const QJsonObject dataMap = resultMap[QStringLiteral("data")].toObject(); setErrorText(dataMap[QStringLiteral("error")].toString()); } else { return resultMap[QStringLiteral("data")].toObject(); } emitResult(); return {}; } void albumCreated(KJob* job) { const QJsonObject dataMap = processResponse(job); if (!dataMap.isEmpty()) { m_albumId = dataMap[QStringLiteral("id")].toString(); m_albumDeleteHash = dataMap[QStringLiteral("deletehash")].toString(); startUploading(); } } void startUploading() { Q_EMIT infoMessage(this, i18n("Uploading files to imgur...")); const QJsonArray urls = data().value(QStringLiteral("urls")).toArray(); for (const QJsonValue &val : urls) { QString u = val.toString(); KIO::StoredTransferJob* job = KIO::storedGet(QUrl(u)); connect(job, &KJob::finished, this, &ImgurShareJob::fileFetched); m_pendingJobs++; } } void fileFetched(KJob* j) { if (j->error()) { setError(j->error()); setErrorText(j->errorText()); emitResult(); qDebug() << "error:" << j->errorText() << j->errorString(); return; } MPForm form; KIO::StoredTransferJob* job = qobject_cast(j); form.addFile(QStringLiteral("image"), job->url(), job->data()); form.addPair(QStringLiteral("album"), m_albumDeleteHash, {}); form.finish(); KIO::StoredTransferJob *tJob = KIO::storedHttpPost(form.formData(), *imageImgurUrl, KIO::HideProgressInfo); tJob->setMetaData(QMap{ { QStringLiteral("content-type"), QString::fromLocal8Bit(form.contentType()) }, { QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: Client-ID ") + *YOUR_CLIENT_ID } }); connect(tJob, &KJob::result, this, &ImgurShareJob::imageUploaded); } void imageUploaded(KJob* job) { const QJsonObject dataMap = processResponse(job); if (!dataMap.isEmpty()) { const QString url = dataMap[QStringLiteral("link")].toString(); Q_EMIT infoMessage(this, url, QStringLiteral("%1").arg(url)); --m_pendingJobs; if (m_pendingJobs == 0) { const QString finalUrl = m_albumId.isEmpty() ? url : QStringLiteral("https://imgur.com/a/") + m_albumId; setOutput({ { QStringLiteral("url"), finalUrl } }); emitResult(); } } } private: QString m_albumId; QString m_albumDeleteHash; int m_pendingJobs; }; class Q_DECL_EXPORT ImgurPlugin : public Purpose::PluginBase { Q_OBJECT public: ImgurPlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new ImgurShareJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(ImgurPlugin, "imgurplugin.json") #include "imgurplugin.moc" diff --git a/src/plugins/imgur/mpform.cpp b/src/plugins/imgur/mpform.cpp index 8558bbf..771edd9 100644 --- a/src/plugins/imgur/mpform.cpp +++ b/src/plugins/imgur/mpform.cpp @@ -1,203 +1,202 @@ /* ============================================================ * * This file is a part of kipi-plugins project * http://www.kipi-plugins.org * * Date : 2005-07-07 * Description : a tool to export images to imgur.com * * Copyright (C) 2005-2008 by Vardhman Jain * Copyright (C) 2008-2013 by Gilles Caulier * Copyright (C) 2010-2012 by Marius Orcsik * * 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, or (at your option) any later version. * * 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. * * ============================================================ */ #include "mpform.h" // Qt includes #include #include -#include #include #include // KDE includes #include MPForm::MPForm() { m_boundary = "----------"; m_boundary += KRandom::randomString(42 + 13).toLatin1(); } MPForm::~MPForm() { } void MPForm::reset() { m_buffer.resize(0); } void MPForm::finish() { QByteArray str; str += "--"; str += m_boundary; str += "--"; m_buffer.append(str); } bool MPForm::addPair(const QString& name, const QString& value, const QString& contentType) { QByteArray str; QByteArray content_length = QByteArray::number(value.length()); str += "--"; str += m_boundary; str += "\r\n"; if (!name.isEmpty()) { str += "Content-Disposition: form-data; name=\""; str += name.toLatin1(); str += "\"\r\n"; } if (!contentType.isEmpty()) { str += "Content-Type: " + contentType.toLatin1(); str += "\r\n"; str += "Mime-version: 1.0 "; str += "\r\n"; } str += "Content-Length: "; str += content_length; str += "\r\n\r\n"; str += value.toUtf8(); m_buffer.append(str); m_buffer.append("\r\n"); return true; } bool MPForm::addFile(const QString& name, const QString& path) { QMimeDatabase db; QMimeType ptr = db.mimeTypeForUrl(QUrl::fromLocalFile(path)); QString mime = ptr.name(); if (mime.isEmpty()) { // if we ourselves can't determine the mime of the local file, // very unlikely the remote site will be able to identify it return false; } QFile imageFile(path); if (!imageFile.open(QIODevice::ReadOnly)) { qWarning() << "Couldn't open" << path; return false; } QByteArray imageData = imageFile.readAll(); QByteArray str; QByteArray file_size = QByteArray::number(imageFile.size()); imageFile.close(); str += "--"; str += m_boundary; str += "\r\n"; str += "Content-Disposition: form-data; name=\""; str += name.toLatin1(); str += "\"; "; str += "filename=\""; str += QFile::encodeName(imageFile.fileName()); str += "\"\r\n"; str += "Content-Length: "; str += file_size; str += "\r\n"; str += "Content-Type: "; str += mime.toLatin1(); str += "\r\n\r\n"; m_buffer.append(str); //int oldSize = m_buffer.size(); m_buffer.append(imageData); m_buffer.append("\r\n"); return true; } bool MPForm::addFile(const QString& name, const QUrl& fileUrl, const QByteArray& fileData) { QMimeDatabase db; QMimeType ptr = db.mimeTypeForUrl(fileUrl); QString mime = ptr.name(); if (mime.isEmpty()) { // if we ourselves can't determine the mime of the local file, // very unlikely the remote site will be able to identify it return false; } QByteArray str; QByteArray file_size = QByteArray::number(fileData.size()); str += "--"; str += m_boundary; str += "\r\n"; str += "Content-Disposition: form-data; name=\""; str += name.toLatin1(); str += "\"; "; str += "filename=\""; str += QFile::encodeName(fileUrl.fileName()); str += "\"\r\n"; str += "Content-Length: "; str += file_size; str += "\r\n"; str += "Content-Type: "; str += mime.toLatin1(); str += "\r\n\r\n"; m_buffer.append(str); //int oldSize = m_buffer.size(); m_buffer.append(fileData); m_buffer.append("\r\n"); return true; } QByteArray MPForm::contentType() const { return "Content-Type: multipart/form-data; boundary=" + m_boundary; } QByteArray MPForm::boundary() const { return m_boundary; } QByteArray MPForm::formData() const { return m_buffer; } diff --git a/src/plugins/kdeconnect/kdeconnectplugin.cpp b/src/plugins/kdeconnect/kdeconnectplugin.cpp index e3cb2b5..9fa87cc 100644 --- a/src/plugins/kdeconnect/kdeconnectplugin.cpp +++ b/src/plugins/kdeconnect/kdeconnectplugin.cpp @@ -1,95 +1,93 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include -#include -#include #include #include #include EXPORT_SHARE_VERSION class KDEConnectJob : public Purpose::Job { Q_OBJECT public: KDEConnectJob(QObject* parent) : Purpose::Job(parent) {} QStringList arrayToList(const QJsonArray& array) { QStringList ret; for (const QJsonValue& val : array) { ret += val.toString(); } return ret; } void start() override { QProcess* process = new QProcess(this); process->setProgram(QStringLiteral("kdeconnect-cli")); QJsonArray urlsJson = data().value(QStringLiteral("urls")).toArray(); process->setArguments(QStringList(QStringLiteral("--device")) << data().value(QStringLiteral("device")).toString() << QStringLiteral("--share") << arrayToList(urlsJson)); connect(process, &QProcess::errorOccurred, this, &KDEConnectJob::processError); connect(process, QOverload::of(&QProcess::finished), this, &KDEConnectJob::jobFinished); connect(process, &QProcess::readyRead, this, [process](){ qDebug() << "kdeconnect-cli output:" << process->readAll(); }); process->start(); } void processError(QProcess::ProcessError error) { QProcess* process = qobject_cast(sender()); qWarning() << "kdeconnect share error:" << error << process->errorString(); setError(1 + error); setErrorText(process->errorString()); emitResult(); } void jobFinished(int code, QProcess::ExitStatus status) { if (status != QProcess::NormalExit) qWarning() << "kdeconnect-cli crashed"; setError(code); setOutput( {{ QStringLiteral("url"), QString() }}); emitResult(); } private: }; class Q_DECL_EXPORT KDEConnectPlugin : public Purpose::PluginBase { Q_OBJECT public: KDEConnectPlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new KDEConnectJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(KDEConnectPlugin, "kdeconnectplugin.json") #include "kdeconnectplugin.moc" diff --git a/src/plugins/kdeconnect_sms/kdeconnectsmsplugin.cpp b/src/plugins/kdeconnect_sms/kdeconnectsmsplugin.cpp index 84810e1..4a53232 100644 --- a/src/plugins/kdeconnect_sms/kdeconnectsmsplugin.cpp +++ b/src/plugins/kdeconnect_sms/kdeconnectsmsplugin.cpp @@ -1,73 +1,70 @@ /* Copyright 2018 Nicolas Fella This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include -#include #include #include -#include -#include #include #include #include #include EXPORT_SHARE_VERSION class KDEConnectSMSJob : public Purpose::Job { Q_OBJECT public: KDEConnectSMSJob(QObject* parent) : Purpose::Job(parent) {} QStringList arrayToList(const QJsonArray& array) { QStringList ret; for (const QJsonValue& val : array) { ret += val.toString(); } return ret; } void start() override { QJsonArray urlsJson = data().value(QStringLiteral("urls")).toArray(); QString title = data().value(QStringLiteral("title")).toString(); QString message = i18n("%1 - %2").arg(title).arg(arrayToList(urlsJson).join(QLatin1Char(' '))); QProcess::startDetached(QStringLiteral("kdeconnect-sms"), QStringList(QStringLiteral("--message")) << message); QTimer::singleShot(0, this, &KDEConnectSMSJob::emitResult); } }; class Q_DECL_EXPORT KDEConnectSMSPlugin : public Purpose::PluginBase { Q_OBJECT public: KDEConnectSMSPlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new KDEConnectSMSJob(nullptr); } }; K_PLUGIN_FACTORY_WITH_JSON(KDEConnectSMS, "kdeconnectsmsplugin.json", registerPlugin();) #include "kdeconnectsmsplugin.moc" diff --git a/src/plugins/ktp-sendfile/ktpsendfileplugin.cpp b/src/plugins/ktp-sendfile/ktpsendfileplugin.cpp index d2d16ef..9bdc13f 100644 --- a/src/plugins/ktp-sendfile/ktpsendfileplugin.cpp +++ b/src/plugins/ktp-sendfile/ktpsendfileplugin.cpp @@ -1,87 +1,84 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include -#include #include -#include -#include #include #include #include #include EXPORT_SHARE_VERSION class KTpSendFileShareJob : public Purpose::Job { Q_OBJECT public: KTpSendFileShareJob(QObject* parent) : Purpose::Job(parent) {} QStringList arrayToList(const QJsonArray& array) { QStringList ret; for (const QJsonValue& val : array) { ret += val.toString(); } return ret; } void start() override { QString executable = QStandardPaths::findExecutable(QStringLiteral("ktp-send-file")); if (executable.isEmpty()) { setError(1); setErrorText(i18n("Couldn't find 'ktp-send-file' executable.")); emitResult(); return; } QProcess* process = new QProcess(this); process->setProgram(executable); QJsonArray urlsJson = data().value(QStringLiteral("urls")).toArray(); process->setArguments(arrayToList(urlsJson)); connect(process, static_cast(&QProcess::finished), this, &KTpSendFileShareJob::jobFinished); process->start(); } Q_SLOT void jobFinished(int code, QProcess::ExitStatus /*status*/) { setError(code); setOutput( {{ QStringLiteral("url"), QString() }}); emitResult(); } }; class Q_DECL_EXPORT KTpSendFilePlugin : public Purpose::PluginBase { Q_OBJECT public: KTpSendFilePlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new KTpSendFileShareJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(KTpSendFilePlugin, "ktpsendfileplugin.json") #include "ktpsendfileplugin.moc" diff --git a/src/plugins/pastebin/pastebinplugin.cpp b/src/plugins/pastebin/pastebinplugin.cpp index 72be61b..cba03a8 100644 --- a/src/plugins/pastebin/pastebinplugin.cpp +++ b/src/plugins/pastebin/pastebinplugin.cpp @@ -1,138 +1,135 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include -#include +#include #include -#include -#include #include #include #include #include -#include #include #include EXPORT_SHARE_VERSION // Taken from "share" Data Engine // key associated with plasma-devel@kde.org // thanks to Alan Schaaf of Pastebin (alan@pastebin.com) Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, apiKey, ("0c8b6add8e0f6d53f61fe5ce870a1afa")) class PastebinJob : public Purpose::Job { Q_OBJECT public: PastebinJob(QObject* parent) : Purpose::Job(parent) , m_pendingJobs(0) {} void start() override { const QJsonArray urls = data().value(QStringLiteral("urls")).toArray(); if (urls.isEmpty()) { qWarning() << "no urls to share" << urls << data(); emitResult(); return; } m_pendingJobs = 0; for (const QJsonValue &val : urls) { QString u = val.toString(); KIO::StoredTransferJob* job = KIO::storedGet(QUrl(u), KIO::NoReload, KIO::HideProgressInfo); connect(job, &KJob::finished, this, &PastebinJob::fileFetched); m_pendingJobs++; } Q_ASSERT(m_pendingJobs>0); } void fileFetched(KJob* j) { KIO::StoredTransferJob* job = qobject_cast(j); m_data += job->data(); --m_pendingJobs; if (job->error()) { setError(job->error()); setErrorText(job->errorString()); emitResult(); } else if (m_pendingJobs == 0) performUpload(); } void performUpload() { if (m_data.isEmpty()) { setError(1); setErrorText(i18n("No information to send")); emitResult(); return; } // qCDebug(PLUGIN_PASTEBIN) << "exporting patch to pastebin" << source->file(); QByteArray bytearray = "api_option=paste&api_paste_private=1&api_paste_name=kde-purpose-pastebin-plugin&api_paste_expire_date=1D&api_paste_format=diff&api_dev_key="+*apiKey+"&api_paste_code="; bytearray += QUrl::toPercentEncoding(QString::fromUtf8(m_data)); const QUrl url(QStringLiteral("https://pastebin.com/api/api_post.php")); KIO::TransferJob *tf = KIO::http_post(url, bytearray); tf->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: application/x-www-form-urlencoded")); connect(tf, &KIO::TransferJob::data, this, [this](KIO::Job*, const QByteArray& data) { m_resultData += data; }); connect(tf, &KJob::result, this, &PastebinJob::textUploaded); m_resultData.clear(); } void textUploaded(KJob* job) { if (job->error()) { setError(error()); setErrorText(job->errorText()); } else if (!m_resultData.startsWith("http")) { setError(1); setErrorText(QString::fromUtf8(m_resultData)); } else setOutput( { { QStringLiteral("url"), QString::fromUtf8(m_resultData) } }); emitResult(); } private: int m_pendingJobs; QByteArray m_data; QByteArray m_resultData; }; class Q_DECL_EXPORT PastebinPlugin : public Purpose::PluginBase { Q_OBJECT public: PastebinPlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new PastebinJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(PastebinPlugin, "pastebinplugin.json") #include "pastebinplugin.moc" diff --git a/src/plugins/phabricator/phabricatorjobs.cpp b/src/plugins/phabricator/phabricatorjobs.cpp index dc0afcb..c449d2c 100644 --- a/src/plugins/phabricator/phabricatorjobs.cpp +++ b/src/plugins/phabricator/phabricatorjobs.cpp @@ -1,269 +1,261 @@ /* * This file is part of KDevelop * Copyright 2017 René J.V. Bertin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "phabricatorjobs.h" #include "debug.h" -#include #include #include #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - +// // // // // // // #include #define COLOURCODES "\u001B[[0-9]*m" using namespace Phabricator; bool DifferentialRevision::buildArcCommand(const QString& workDir, const QString& patchFile, bool doBrowse) { bool ret; QString arc = QStandardPaths::findExecutable(QStringLiteral("arc")); if (!arc.isEmpty()) { QStringList args; args << QStringLiteral("diff"); if (m_id.isEmpty()) { // creating a new differential revision (review request) // the fact we skip "--create" means we'll be creating a new "differential diff" // which obliges the user to fill in the details we cannot provide through the plugin ATM. // TODO: grab the TARGET_GROUPS from .reviewboardrc and pass that via --reviewers } else { // updating an existing differential revision (review request) args << QStringLiteral("--update") << m_id; } args << QStringLiteral("--excuse") << QStringLiteral("patch submitted with the purpose/phabricator plugin"); if (m_commit.isEmpty()) { args << QStringLiteral("--raw"); } else { args << QStringLiteral("--allow-untracked") << QStringLiteral("--ignore-unsound-tests") << QStringLiteral("--nolint") << QStringLiteral("-nounit") << QStringLiteral("--verbatim") << m_commit; } if (doBrowse) { args << QStringLiteral("--browse"); } m_arcCmd.setProgram(arc); m_arcCmd.setArguments(args); if (!patchFile.isEmpty()) { m_arcCmd.setStandardInputFile(patchFile); m_arcInput = patchFile; } m_arcCmd.setWorkingDirectory(workDir); connect(&m_arcCmd, static_cast(&QProcess::finished), this, &DifferentialRevision::done); setPercent(33); ret = true; } else { qCWarning(PLUGIN_PHABRICATOR) << "Could not find 'arc' in the path"; setError(KJob::UserDefinedError + 3); setErrorText(i18n("Could not find the 'arc' command")); setErrorString(errorText()); ret = false; } return ret; } void DifferentialRevision::start() { if (!m_arcCmd.program().isEmpty()) { qCDebug(PLUGIN_PHABRICATOR) << "starting" << m_arcCmd.program() << m_arcCmd.arguments(); qCDebug(PLUGIN_PHABRICATOR) << "\twordDir=" << m_arcCmd.workingDirectory() << "stdin=" << m_arcInput; m_arcCmd.start(); if (m_arcCmd.waitForStarted(5000)) { setPercent(66); } } } void DifferentialRevision::setErrorString(const QString& msg) { QRegExp unwanted(QString::fromUtf8(COLOURCODES)); m_errorString = msg; m_errorString.replace(unwanted, QString()); } QString DifferentialRevision::scrubbedResult() { QString result = QString::fromUtf8(m_arcCmd.readAllStandardOutput()); // the return string can contain terminal text colour codes: remove them. QRegExp unwanted(QString::fromUtf8(COLOURCODES)); result.replace(unwanted, QString()); return result; } QStringList DifferentialRevision::scrubbedResultList() { QStringList result = QString::fromUtf8(m_arcCmd.readAllStandardOutput()).split(QChar::LineFeed); // the return string can contain terminal text colour codes: remove them. QRegExp unwanted(QString::fromUtf8(COLOURCODES)); result.replaceInStrings(unwanted, QString()); // remove all (now) empty strings result.removeAll(QString()); return result; } NewDiffRev::NewDiffRev(const QUrl& patch, const QString& projectPath, bool doBrowse, QObject* parent) : DifferentialRevision(QString(), parent) , m_patch(patch) , m_project(projectPath) { buildArcCommand(projectPath, patch.toLocalFile(), doBrowse); } void NewDiffRev::done(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit || exitCode) { setError(KJob::UserDefinedError + exitCode); setErrorText(i18n("Could not create the new \"differential diff\"")); setErrorString(QString::fromUtf8(m_arcCmd.readAllStandardError())); qCWarning(PLUGIN_PHABRICATOR) << "Could not create the new \"differential diff\":" << m_arcCmd.error() << ";" << errorString(); } else { setPercent(99); const QString arcOutput = scrubbedResult(); const char *diffOpCode = "Diff URI: "; int diffOffset = arcOutput.indexOf(QLatin1String(diffOpCode)); if (diffOffset >= 0) { m_diffURI = arcOutput.mid(diffOffset + strlen(diffOpCode)).split(QChar::LineFeed).at(0); } else { m_diffURI = arcOutput; } } emitResult(); } UpdateDiffRev::UpdateDiffRev(const QUrl& patch, const QString& basedir, const QString& id, const QString& updateComment, bool doBrowse, QObject* parent) : DifferentialRevision(id, parent) , m_patch(patch) , m_basedir(basedir) { buildArcCommand(m_basedir, m_patch.toLocalFile(), doBrowse); QStringList args = m_arcCmd.arguments(); if (updateComment.isEmpty()) { args << QStringLiteral("--message") << QStringLiteral(""); } else { args << QStringLiteral("--message") << updateComment; } m_arcCmd.setArguments(args); } void UpdateDiffRev::done(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit || exitCode) { setError(KJob::UserDefinedError + exitCode); setErrorText(i18n("Patch upload to Phabricator failed")); setErrorString(QString::fromUtf8(m_arcCmd.readAllStandardError())); qCWarning(PLUGIN_PHABRICATOR) << "Patch upload to Phabricator failed with exit code" << exitCode << ", error" << m_arcCmd.error() << ";" << errorString(); } else { const QString arcOutput = scrubbedResult(); const char *diffOpCode = "Revision URI: "; int diffOffset = arcOutput.indexOf(QLatin1String(diffOpCode)); if (diffOffset >= 0) { m_diffURI = arcOutput.mid(diffOffset + strlen(diffOpCode)).split(QChar::LineFeed).at(0); } else { m_diffURI = arcOutput; } } emitResult(); } DiffRevList::DiffRevList(const QString& projectDir, QObject* parent) : DifferentialRevision(QString(), parent) , m_projectDir(projectDir) { buildArcCommand(m_projectDir); } bool Phabricator::DiffRevList::buildArcCommand(const QString& workDir, const QString& unused, bool) { Q_UNUSED(unused) bool ret; QString arc = QStandardPaths::findExecutable(QStringLiteral("arc")); if (!arc.isEmpty()) { QStringList args; args << QStringLiteral("list"); m_arcCmd.setProgram(arc); m_arcCmd.setArguments(args); m_arcCmd.setWorkingDirectory(workDir); connect(&m_arcCmd, static_cast(&QProcess::finished), this, &DiffRevList::done); setPercent(33); ret = true; } else { qCWarning(PLUGIN_PHABRICATOR) << "Could not find 'arc' in the path"; setError(KJob::UserDefinedError + 3); setErrorText(i18n("Could not find the 'arc' command")); setErrorString(errorText()); ret = false; } return ret; } void DiffRevList::done(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit || exitCode) { setError(KJob::UserDefinedError + exitCode); setErrorText(i18n("Could not get list of differential revisions in %1", QDir::currentPath())); setErrorString(QString::fromUtf8(m_arcCmd.readAllStandardError())); qCWarning(PLUGIN_PHABRICATOR) << "Could not get list of differential revisions" << m_arcCmd.error() << ";" << errorString(); } else { setPercent(99); const QStringList reviews = scrubbedResultList(); qCDebug(PLUGIN_PHABRICATOR) << "arc list returned:" << reviews; for (auto rev : reviews) { QRegExp revIDExpr(QString::fromUtf8(" D[0-9][0-9]*: ")); int idStart = rev.indexOf(revIDExpr); if (idStart >= 0) { QString revID = rev.mid(idStart+1).split(QString::fromUtf8(": ")).at(0); QString revTitle = rev.section(revIDExpr, 1); if (rev.startsWith(QStringLiteral("* Accepted "))) { // append a Unicode "Heavy Check Mark" to signal accepted revisions revTitle += QStringLiteral(" ") + QString(QChar(0x2714)); m_statusMap[revTitle] = Accepted; } else if (rev.startsWith(QStringLiteral("* Needs Revision "))) { // append a Unicode "Heavy Ballot X" for lack of a Unicode glyph // resembling the icon used on the Phab site. revTitle += QStringLiteral(" ") + QString(QChar(0x2718)); m_statusMap[revTitle] = NeedsRevision; } else if (rev.startsWith(QStringLiteral("* Needs Review "))) { m_statusMap[revTitle] = NeedsReview; } m_reviews << qMakePair(revID, revTitle); m_revMap[revTitle] = revID; } } } emitResult(); } diff --git a/src/plugins/phabricator/phabricatorplugin.cpp b/src/plugins/phabricator/phabricatorplugin.cpp index bcfc3db..19f30dd 100644 --- a/src/plugins/phabricator/phabricatorplugin.cpp +++ b/src/plugins/phabricator/phabricatorplugin.cpp @@ -1,148 +1,144 @@ /* * This file is part of KDevelop * Copyright 2017 René J.V. Bertin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include #include -#include #include #include -#include #include #include -#include #include "phabricatorjobs.h" #include "debug.h" #include "purpose/job.h" #include "purpose/pluginbase.h" // FIXME: For some reason PLUGIN_PHABRICATOR isn't exported from the PhabricatorHelpers lib #undef qCDebug #define qCDebug(dum) qDebug() #undef qCWarning #define qCWarning(dum) qWarning() #undef qCCritical #define qCCritical(dum) qCritical() class PhabricatorJob : public Purpose::Job { Q_OBJECT public: PhabricatorJob(QObject* object = nullptr) : Purpose::Job(object) {} void start() override { const QString localBaseDir(data().value(QStringLiteral("localBaseDir")).toString()); const QUrl sourceFile(data().value(QStringLiteral("urls")).toArray().first().toString()); const QString updateDR = data().value(QStringLiteral("updateDR")).toString(); const bool doBrowse = data().value(QStringLiteral("doBrowse")).toBool(); const QString baseDir = QUrl(localBaseDir).toLocalFile(); if (QFileInfo(sourceFile.toLocalFile()).size() <= 0) { setError(KJob::UserDefinedError+1); setErrorText(i18n("Phabricator refuses empty patchfiles")); emit PhabricatorJob::warning(this, errorString(), QString()); qCCritical(PLUGIN_PHABRICATOR) << errorString(); emitResult(); return; } else if (updateDR.localeAwareCompare(i18n("unknown")) == 0) { setError(KJob::UserDefinedError+1); setErrorText(i18n("Please choose between creating a new revision or updating an existing one")); emit PhabricatorJob::warning(this, errorString(), QString()); qCCritical(PLUGIN_PHABRICATOR) << errorString(); emitResult(); return; } m_drTitle = data().value(QStringLiteral("drTitle")).toString(); KJob* job; if (!updateDR.isEmpty()) { const QString updateComment = data().value(QStringLiteral("updateComment")).toString(); job=new Phabricator::UpdateDiffRev(sourceFile, baseDir, updateDR, updateComment, doBrowse, this); connect(job, &KJob::finished, this, &PhabricatorJob::diffUpdated); } else { job=new Phabricator::NewDiffRev(sourceFile, baseDir, true, this); connect(job, &KJob::finished, this, &PhabricatorJob::diffCreated); } job->start(); emit PhabricatorJob::infoMessage(this, QStringLiteral("upload job started"), QString()); } void diffCreatedOrUpdated(KJob* j, bool created) { if(j->error()!=0) { setError(j->error()); setErrorText(j->errorString()); emit PhabricatorJob::warning(this, j->errorString(), QString()); qCCritical(PLUGIN_PHABRICATOR) << "Could not upload the patch" << j->errorString(); emitResult(); return; } if (created) { Phabricator::NewDiffRev const * job = qobject_cast(j); qCWarning(PLUGIN_PHABRICATOR) <<"new diff:" << job->diffURI(); setOutput({{ QStringLiteral("url"), job->diffURI() }}); } else { Phabricator::UpdateDiffRev const * job = qobject_cast(j); qCWarning(PLUGIN_PHABRICATOR) << "updated diff" << job->requestId() << ":" << job->diffURI(); setOutput({{ QStringLiteral("url"), job->diffURI() }}); emit PhabricatorJob::infoMessage(this, QStringLiteral("updated diff %1: %2").arg(job->requestId()).arg(job->diffURI()), QString()); } emitResult(); } void diffCreated(KJob* j) { diffCreatedOrUpdated(j, true); } void diffUpdated(KJob* j) { diffCreatedOrUpdated(j, false); } QString m_drTitle; }; class Q_DECL_EXPORT PhabricatorPlugin : public Purpose::PluginBase { Q_OBJECT public: PhabricatorPlugin(QObject* parent, const QList& /*args*/) : PluginBase(parent) {} ~PhabricatorPlugin() override {} Purpose::Job* createJob() const override { return new PhabricatorJob; } }; K_PLUGIN_CLASS_WITH_JSON(PhabricatorPlugin, "phabricatorplugin.json") #include "phabricatorplugin.moc" diff --git a/src/plugins/phabricator/quick/difflistmodel.h b/src/plugins/phabricator/quick/difflistmodel.h index 7c6d6bc..baeac0a 100644 --- a/src/plugins/phabricator/quick/difflistmodel.h +++ b/src/plugins/phabricator/quick/difflistmodel.h @@ -1,72 +1,70 @@ /* * Copyright 2017 René J.V. Bertin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DIFFLISTMODEL_H #define DIFFLISTMODEL_H #include -#include #include #include -#include class KJob; class QTemporaryDir; class DiffListModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QString status READ status WRITE setStatus) public: DiffListModel(QObject* parent = nullptr); void refresh(); QHash roleNames() const override; QVariant data(const QModelIndex &idx, int role) const override; int rowCount(const QModelIndex & parent) const override; QString status() const { return m_status; } void setStatus(const QString &status); void receivedDiffRevs(KJob* job); Q_SCRIPTABLE QVariant get(int row, const QByteArray &role); private: struct Value { QVariant summary; QVariant id; QVariant status; #ifndef QT_NO_DEBUG_STREAM operator QString() const { QString ret = QStringLiteral("DiffListModel::Value{summary=\"%1\" id=\"%2\" status=\"%3\"}"); return ret.arg(this->summary.toString()).arg(this->id.toString()).arg(this->status.toInt()); } #endif }; QVector m_values; QString m_status; QString m_initialDir; QTemporaryDir *m_tempDir; }; #endif diff --git a/src/plugins/phabricator/quick/phabricatorrc.cpp b/src/plugins/phabricator/quick/phabricatorrc.cpp index 853b6a1..8b0c7d8 100644 --- a/src/plugins/phabricator/quick/phabricatorrc.cpp +++ b/src/plugins/phabricator/quick/phabricatorrc.cpp @@ -1,39 +1,36 @@ /* * Copyright 2017 René J.V. Bertin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "phabricatorrc.h" -#include -#include -#include PhabricatorRC::PhabricatorRC(QObject* parent) : QObject(parent) {} void PhabricatorRC::setPath(const QUrl &filePath) { if (filePath == m_path || !filePath.isLocalFile()) return; m_path = filePath; //.arcconfig files are JSON files // TODO figure out the if/what/how of .arcconfig file contents emit dataChanged(); } diff --git a/src/plugins/phabricator/quick/phabricatorrc.h b/src/plugins/phabricator/quick/phabricatorrc.h index f6ecb33..1ed0870 100644 --- a/src/plugins/phabricator/quick/phabricatorrc.h +++ b/src/plugins/phabricator/quick/phabricatorrc.h @@ -1,46 +1,45 @@ /* * Copyright 2017 René J.V. Bertin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PHABRICATORRC_H #define PHABRICATORRC_H #include #include -#include class PhabricatorRC : public QObject { Q_OBJECT Q_PROPERTY(QUrl path READ path WRITE setPath NOTIFY dataChanged) public: PhabricatorRC(QObject* parent = nullptr); void setPath(const QUrl &path); QUrl path() const { return m_path; } Q_SIGNALS: void dataChanged(); private: QUrl m_path; }; #endif diff --git a/src/plugins/reviewboard/reviewboardplugin.cpp b/src/plugins/reviewboard/reviewboardplugin.cpp index e079105..3bd66cb 100644 --- a/src/plugins/reviewboard/reviewboardplugin.cpp +++ b/src/plugins/reviewboard/reviewboardplugin.cpp @@ -1,128 +1,124 @@ /* * This file is part of KDevelop * Copyright 2010 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include -#include #include #include -#include #include -#include #include "reviewboardjobs.h" #include "debug.h" #include "purpose/job.h" #include "purpose/pluginbase.h" class TheReviewboardJob : public Purpose::Job { Q_OBJECT public: TheReviewboardJob(QObject* object = nullptr) : Purpose::Job(object) {} void start() override { const QString baseDir(data().value(QStringLiteral("baseDir")).toString()); const QString repository(data().value(QStringLiteral("repository")).toString()); const QUrl sourceFile(data().value(QStringLiteral("urls")).toArray().first().toString()); const QString updateRR = data().value(QStringLiteral("updateRR")).toString(); const QJsonObject extraData = data().value(QStringLiteral("extraData")).toObject(); const QString username = data().value(QStringLiteral("username")).toString(); const QString password = data().value(QStringLiteral("password")).toString(); QUrl server(data().value(QStringLiteral("server")).toString()); server.setUserInfo(username + QLatin1Char(':') + password); KJob* job; if (!updateRR.isEmpty()) { job=new ReviewBoard::SubmitPatchRequest(server, sourceFile, baseDir, updateRR); connect(job, &KJob::finished, this, &TheReviewboardJob::reviewDone); } else { job=new ReviewBoard::NewRequest(server, repository); job->setProperty("extraData", extraData); connect(job, &KJob::finished, this, &TheReviewboardJob::reviewCreated); } job->setProperty("baseDir", baseDir); job->start(); } void reviewCreated(KJob* j) { if (j->error()!=0) { setError(j->error()); setErrorText(j->errorString()); emitResult(); return; } ReviewBoard::NewRequest const * job = qobject_cast(j); //This will provide things like groups and users for review from .reviewboardrc QVariantMap extraData = job->property("extraData").toMap(); if (!extraData.isEmpty()) { KJob* updateJob = new ReviewBoard::UpdateRequest(job->server(), job->requestId(), extraData); updateJob->start(); } // for git projects, m_source will be a VCSDiffPatchSource instance const QUrl sourceFile(data().value(QStringLiteral("urls")).toArray().first().toString()); ReviewBoard::SubmitPatchRequest* submitPatchJob=new ReviewBoard::SubmitPatchRequest(job->server(), sourceFile, j->property("baseDir").toString(), job->requestId()); connect(submitPatchJob, &ReviewBoard::SubmitPatchRequest::finished, this, &TheReviewboardJob::reviewDone); submitPatchJob->start(); } void reviewDone(KJob* j) { if(j->error()!=0) { setError(j->error()); setErrorText(j->errorString()); emitResult(); return; } ReviewBoard::SubmitPatchRequest const * job = qobject_cast(j); QUrl url = job->server(); url.setUserInfo(QString()); QString requrl = QStringLiteral("%1/r/%2/").arg(url.toDisplayString(QUrl::PreferLocalFile), job->requestId()); setOutput({{ QStringLiteral("url"), requrl }}); emitResult(); } }; class Q_DECL_EXPORT ReviewBoardPlugin : public Purpose::PluginBase { Q_OBJECT public: ReviewBoardPlugin(QObject* parent, const QList& /*args*/) : PluginBase(parent) {} ~ReviewBoardPlugin() override {} Purpose::Job* createJob() const override { return new TheReviewboardJob; } }; K_PLUGIN_CLASS_WITH_JSON(ReviewBoardPlugin, "reviewboardplugin.json") #include "reviewboardplugin.moc" diff --git a/src/plugins/saveas/saveasplugin.cpp b/src/plugins/saveas/saveasplugin.cpp index 49af011..0b46ebf 100644 --- a/src/plugins/saveas/saveasplugin.cpp +++ b/src/plugins/saveas/saveasplugin.cpp @@ -1,127 +1,124 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include -#include -#include #include #include -#include #include #include #include #include EXPORT_SHARE_VERSION static QUrl addPathToUrl(const QUrl &url, const QString &relPath) { QString path = url.path(); if (!path.endsWith(QLatin1Char('/'))) { path += QLatin1Char('/'); } path += relPath; QUrl u(url); u.setPath(path); return u; } class SaveAsShareJob : public Purpose::Job { Q_OBJECT public: SaveAsShareJob(QObject* parent) : Purpose::Job(parent) { setCapabilities(Killable | Suspendable); } void start() override { const QJsonArray inputUrls = data().value(QStringLiteral("urls")).toArray(); if (inputUrls.isEmpty()) { setErrorText(i18n("No URLs to save")); setError(1); emitResult(); return; } QList urls; bool containsData = false; for (const QJsonValue &val : inputUrls) { urls.append(QUrl(val.toString())); containsData |= urls.last().scheme() == QLatin1String("data"); } m_dest = QUrl(data().value(QStringLiteral("destinationPath")).toString()); if (containsData && !(urls.count() == 1 && m_dest.isLocalFile() && !QFileInfo(m_dest.toLocalFile()).isDir())) { for(const QUrl& url: urls) { QUrl dest = addPathToUrl(m_dest, QStringLiteral("data")); auto job = KIO::copy(url, dest); connect(job, &KJob::finished, this, &SaveAsShareJob::fileCopied); m_jobs.insert(job); } } else { auto job = KIO::copy(urls, m_dest); connect(job, &KJob::finished, this, &SaveAsShareJob::fileCopied); m_jobs.insert(job); } } bool doKill() override { bool killed = true; for(KJob* job: m_jobs) killed &= job->kill(); return killed; } void fileCopied(KJob* job) { auto r = m_jobs.remove(job); Q_ASSERT(r); setError(error() + job->error()); setErrorText(errorText() + QLatin1Char(' ') + job->errorText()); if (job->error()==0 && m_jobs.isEmpty()) { setOutput({ { QStringLiteral("url"), m_dest.toString() } }); } emitResult(); } private: QUrl m_dest; QSet m_jobs; }; class Q_DECL_EXPORT SaveAsPlugin : public Purpose::PluginBase { Q_OBJECT public: SaveAsPlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new SaveAsShareJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(SaveAsPlugin, "saveasplugin.json") #include "saveasplugin.moc" diff --git a/src/plugins/telegram/telegramplugin.cpp b/src/plugins/telegram/telegramplugin.cpp index 05f0308..2f60fce 100644 --- a/src/plugins/telegram/telegramplugin.cpp +++ b/src/plugins/telegram/telegramplugin.cpp @@ -1,129 +1,128 @@ /* Copyright 2018 Nicolas Fella This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include -#include -#include #include #include #include #include #include #include #include #include +#include #include "debug.h" Q_LOGGING_CATEGORY(PLUGIN_TELEGRAM, "org.kde.purpose.plugin.telegram") EXPORT_SHARE_VERSION class TelegramJob : public Purpose::Job { Q_OBJECT public: TelegramJob(QObject* parent) : Purpose::Job(parent) {} QStringList arrayToList(const QJsonArray& array) { QStringList ret; for (const QJsonValue& val : array) { QUrl url(val.toString()); if(url.isLocalFile()) { ret += KShell::quoteArg(url.toLocalFile()); } } return ret; } void start() override { for (const QString &desktopFile: {QStringLiteral("org.telegram.desktop.desktop"), QStringLiteral("telegramdesktop.desktop") }) { const auto path = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, desktopFile); if (!path.isEmpty()) { useDesktopFile(path); return; } } //Failed to find the application QTimer::singleShot(0, this, [this]() { setError(1); setErrorText(i18n("Could not find telegram")); setOutput( {{ QStringLiteral("url"), QString() }}); emitResult(); }); } void useDesktopFile(const QString &path) { const KDesktopFile file(path); const KConfigGroup desktopEntryGroup = file.group("Desktop Entry"); QString execLine = desktopEntryGroup.readEntry("Exec"); execLine.replace(QLatin1String("%u"), arrayToList(data().value(QStringLiteral("urls")).toArray()).join(QLatin1Char(' '))); execLine.replace(QLatin1String("@@u"), QLatin1String("@@")); execLine.replace(QLatin1String(" -- "), QLatin1String(" -sendpath ")); QStringList args = KShell::splitArgs(execLine); QProcess* process = new QProcess(this); process->setProgram(args.takeFirst()); process->setArguments(args); connect(process, &QProcess::errorOccurred, this, &TelegramJob::processError); connect(process, &QProcess::readyRead, this, [process](){ qDebug() << process->program() << "output:" << process->readAll(); }); process->start(); QTimer::singleShot(500, this, &TelegramJob::jobFinished); } void processError(QProcess::ProcessError error) { QProcess* process = qobject_cast(sender()); qWarning() << "telegram share error:" << error << process->errorString(); setError(1 + error); setErrorText(process->errorString()); emitResult(); } void jobFinished() { setOutput( {{ QStringLiteral("url"), QString() }}); emitResult(); } private: }; class Q_DECL_EXPORT TelegramPlugin : public Purpose::PluginBase { Q_OBJECT public: TelegramPlugin(QObject* p, const QVariantList& ) : Purpose::PluginBase(p) {} Purpose::Job* createJob() const override { return new TelegramJob(nullptr); } }; K_PLUGIN_CLASS_WITH_JSON(TelegramPlugin, "telegramplugin.json") #include "telegramplugin.moc" diff --git a/src/plugins/youtube/youtubejob.cpp b/src/plugins/youtube/youtubejob.cpp index b3d45e7..ea8cdcf 100644 --- a/src/plugins/youtube/youtubejob.cpp +++ b/src/plugins/youtube/youtubejob.cpp @@ -1,135 +1,128 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "youtubejob.h" #include -#include #include #include -#include -#include -#include #include -#include #include -#include #include -#include static const QUrl apiUrl(QStringLiteral("https://www.googleapis.com/upload/youtube/v3/videos?part=snippet%2Cstatus&uploadType=resumable")); static const QString watchUrl = QStringLiteral("https://www.youtube.com/watch?v="); YoutubeJob::YoutubeJob(const QUrl& url, const QByteArray &accessToken, const QString& title, const QStringList& tags, const QString& description, QObject* parent) : KJob(parent), m_url(url), m_token(accessToken) { m_metadata = QByteArray("{ " "\"snippet\": {" "\"title\": \"" + title.toUtf8() + "\", " "\"categoryId\": \"22\", " "\"description\": \"" + description.toUtf8() + "\", " "\"tags\": [ \"" + tags.join(QStringLiteral("\", \"")).toUtf8() + "\" ] " "}, " "\"status\": { " "\"privacyStatus\": \"public\" " "} " "}"); } void YoutubeJob::start() { createLocation(); } void YoutubeJob::fileFetched(KJob* j) { if (j->error()) { setError(j->error()); setErrorText(j->errorText()); emitResult(); } KIO::StoredTransferJob* job = qobject_cast(j); uploadVideo(job->data()); } void YoutubeJob::createLocation() { QNetworkRequest req(apiUrl); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json; charset=UTF-8")); req.setRawHeader("Authorization", "Bearer "+m_token); req.setRawHeader("X-Upload-Content-Type", "video/*"); auto reply = m_manager.post(req, m_metadata); connect(reply, &QNetworkReply::finished, this, &YoutubeJob::locationCreated); connect(reply, static_cast(&QNetworkReply::error), [](QNetworkReply::NetworkError e){ qDebug() << "creation error" << e; }); } void YoutubeJob::locationCreated() { QNetworkReply* reply = qobject_cast(sender()); if (reply->error()) { setError(reply->error()); setErrorText(reply->errorString()); qWarning() << "couldn't upload file" << reply->readAll(); emitResult(); return; } Q_ASSERT(reply->atEnd()); Q_ASSERT(reply->hasRawHeader("Location")); m_uploadUrl = QUrl::fromEncoded(reply->rawHeader("Location")); KIO::StoredTransferJob* job = KIO::storedGet(m_url); connect(job, &KJob::finished, this, &YoutubeJob::fileFetched); } void YoutubeJob::uploadVideo(const QByteArray& data) { QNetworkRequest req(m_uploadUrl); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("video/*")); req.setRawHeader("X-Upload-Content-Length", QByteArray::number(data.size())); req.setRawHeader("Authorization", "Bearer "+m_token); setTotalAmount(Bytes, data.size()); auto reply = m_manager.post(req, data); connect(reply, &QNetworkReply::finished, this, &YoutubeJob::videoUploaded); connect(reply, &QNetworkReply::uploadProgress, this, [this](quint64 bytesSent, quint64 bytesTotal) { setProcessedAmount(Bytes, bytesSent); setPercent(bytesTotal == 0 ? 0 : (bytesSent*100)/bytesTotal); }); connect(reply, static_cast(&QNetworkReply::error), [](QNetworkReply::NetworkError e){ qDebug() << "upload error" << e; }); } void YoutubeJob::videoUploaded() { QNetworkReply* reply = qobject_cast(sender()); if (reply->error()) { setError(reply->error()); setErrorText(reply->errorString()); qWarning() << "couldn't finish upload" << reply->readAll(); emitResult(); return; } auto doc = QJsonDocument::fromJson(reply->readAll()).object(); m_output = watchUrl+doc.value(QStringLiteral("id")).toString(); emitResult(); } diff --git a/src/plugins/youtube/youtubejob.h b/src/plugins/youtube/youtubejob.h index 15f763e..097cc9c 100644 --- a/src/plugins/youtube/youtubejob.h +++ b/src/plugins/youtube/youtubejob.h @@ -1,53 +1,49 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef YOUTUBEJOB_H #define YOUTUBEJOB_H -#include #include -#include -#include #include -#include #include #include class YoutubeJob : public KJob { Q_OBJECT public: YoutubeJob(const QUrl& url, const QByteArray &token, const QString& title, const QStringList& tags, const QString& description, QObject* parent = nullptr); void start() override; QString outputUrl() const { return m_output; } private: void fileFetched(KJob*); void createLocation(); void locationCreated(); void uploadVideo(const QByteArray& data); void videoUploaded(); QUrl m_url; QByteArray m_token; QString m_output; QNetworkAccessManager m_manager; QByteArray m_metadata; QUrl m_uploadUrl; }; #endif /* YOUTUBEJOB_H */ diff --git a/src/plugins/youtube/youtubejobcomposite.cpp b/src/plugins/youtube/youtubejobcomposite.cpp index 8a27dbb..7df1433 100644 --- a/src/plugins/youtube/youtubejobcomposite.cpp +++ b/src/plugins/youtube/youtubejobcomposite.cpp @@ -1,103 +1,102 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "youtubejobcomposite.h" #include "youtubejob.h" #include #include #include -#include #include #include #include #include #include #include QDebug operator<<(QDebug s, const Accounts::Service& service) { s.nospace() << qPrintable(service.displayName()) << ',' << qPrintable(service.name()) << '\n'; return s; } QDebug operator<<(QDebug s, const Accounts::Provider& provider) { s.nospace() << "Provider(" << qPrintable(provider.displayName()) << ',' << qPrintable(provider.name()) << ")\n"; return s; } YoutubeJobComposite::YoutubeJobComposite() : Purpose::Job() { } void YoutubeJobComposite::start() { const QJsonValue jsonId = data().value(QStringLiteral("accountId")); if (jsonId.isNull() || jsonId.isUndefined()) { setError(1); setErrorText(i18n("No YouTube account configured in your accounts.")); emitResult(); return; } const Accounts::AccountId id = jsonId.toInt(); //TODO: make async QByteArray accessToken; { auto job = new GetCredentialsJob(id, this); bool b = job->exec(); if (!b) { qWarning() << "Couldn't fetch credentials"; setError(job->error()); setErrorText(job->errorText()); emitResult(); return; } accessToken = job->credentialsData().value(QStringLiteral("AccessToken")).toByteArray(); } m_pendingJobs = 0; const QJsonArray urls = data().value(QStringLiteral("urls")).toArray(); for (const QJsonValue& url : urls) { YoutubeJob* job = new YoutubeJob(QUrl(url.toString()), accessToken, data().value(QStringLiteral("videoTitle")).toString(), data().value(QStringLiteral("videoTags")).toString().split(QLatin1Char(',')), data().value(QStringLiteral("videoDesc")).toString(), this); connect(job, &KJob::finished, this, &YoutubeJobComposite::subjobFinished); job->start(); m_pendingJobs++; } } void YoutubeJobComposite::subjobFinished(KJob* subjob) { m_pendingJobs--; if (subjob->error()) { setError(subjob->error()); setErrorText(subjob->errorText()); emitResult(); return; } if (m_pendingJobs==0) { if (!error()) { const QJsonValue url = qobject_cast(subjob)->outputUrl(); setOutput({{ QStringLiteral("url"), url.toString() }}); } emitResult(); } } diff --git a/src/plugins/youtube/youtubeplugin.cpp b/src/plugins/youtube/youtubeplugin.cpp index 9e8d58c..111153b 100644 --- a/src/plugins/youtube/youtubeplugin.cpp +++ b/src/plugins/youtube/youtubeplugin.cpp @@ -1,45 +1,44 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include "youtubejobcomposite.h" #include "youtubejob.h" #include -#include class YoutubePlugin : public Purpose::PluginBase { Q_OBJECT public: YoutubePlugin(QObject* parent, const QVariantList& args) : Purpose::PluginBase(parent) { Q_UNUSED(args); } Purpose::Job* createJob() const override { return new YoutubeJobComposite; } }; K_PLUGIN_CLASS_WITH_JSON(YoutubePlugin, "youtubeplugin.json") EXPORT_SHARE_VERSION #include "youtubeplugin.moc" diff --git a/src/quick/purposequickplugin.cpp b/src/quick/purposequickplugin.cpp index 98d57b8..b4e2f83 100644 --- a/src/quick/purposequickplugin.cpp +++ b/src/quick/purposequickplugin.cpp @@ -1,34 +1,33 @@ /* Copyright 2014 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "purposequickplugin.h" #include "alternativesmodel.h" #include "job.h" #include "configuration.h" #include -#include void PurposeQuickPlugin::registerTypes(const char* uri) { qmlRegisterType(uri, 1, 0, "PurposeAlternativesModel"); qmlRegisterUncreatableType(uri, 1, 0, "PurposeJob", QStringLiteral("You're not supposed to instantiate jobs")); qmlRegisterUncreatableType(uri, 1, 0, "PurposeConfiguration", QStringLiteral("You're not supposed to instantiate configurations")); qmlRegisterType(QUrl(QStringLiteral("qrc:/org/kde/purpose/PurposeWizard.qml")), uri, 1, 0, "PurposeWizard"); qmlRegisterType(QUrl(QStringLiteral("qrc:/org/kde/purpose/AlternativesView.qml")), uri, 1, 0, "AlternativesView"); qmlRegisterType(QUrl(QStringLiteral("qrc:/org/kde/purpose/RunningJob.qml")), uri, 1, 0, "RunningJob"); }