diff options
Diffstat (limited to 'src/mumble/PluginConfig.cpp')
-rw-r--r-- | src/mumble/PluginConfig.cpp | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/mumble/PluginConfig.cpp b/src/mumble/PluginConfig.cpp new file mode 100644 index 000000000..e70877067 --- /dev/null +++ b/src/mumble/PluginConfig.cpp @@ -0,0 +1,247 @@ +// Copyright 2021 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at <https://www.mumble.info/LICENSE>. + +#include "PluginConfig.h" + +#include "Log.h" +#include "MainWindow.h" +#include "Message.h" +#include "ServerHandler.h" +#include "WebFetch.h" +#include "MumbleApplication.h" +#include "ManualPlugin.h" +#include "Utils.h" +#include "PluginInstaller.h" +#include "PluginManager.h" + +#include <QtWidgets/QMessageBox> +#include <QtCore/QUrl> +#include <QtCore/QDir> +#include <QtCore/QStandardPaths> +#include <QtWidgets/QFileDialog> +#include "Global.h" + +const QString PluginConfig::name = QLatin1String("PluginConfig"); + +static ConfigWidget *PluginConfigDialogNew(Settings &st) { + return new PluginConfig(st); +} + +static ConfigRegistrar registrarPluginConfig(5000, PluginConfigDialogNew); + + +PluginConfig::PluginConfig(Settings &st) : ConfigWidget(st) { + setupUi(this); + + qtwPlugins->header()->setSectionResizeMode(0, QHeaderView::Stretch); + qtwPlugins->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + qtwPlugins->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + qtwPlugins->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); + + qpbUnload->setEnabled(false); + + refillPluginList(); +} + +QString PluginConfig::title() const { + return tr("Plugins"); +} + +const QString &PluginConfig::getName() const { + return PluginConfig::name; +} + +QIcon PluginConfig::icon() const { + return QIcon(QLatin1String("skin:config_plugin.png")); +} + +void PluginConfig::load(const Settings &r) { + loadCheckBox(qcbTransmit, r.bTransmitPosition); +} + +void PluginConfig::on_qpbInstallPlugin_clicked() { + QString pluginFile = QFileDialog::getOpenFileName(this, tr("Install plugin..."), QDir::homePath()); + + if (pluginFile.isEmpty()) { + return; + } + + try { + PluginInstaller installer(pluginFile, this); + if (installer.exec() == QDialog::Accepted) { + // Reload plugins so the new one actually shows up + on_qpbReload_clicked(); + + QMessageBox::information(this, "Mumble", tr("The plugin was installed successfully"), QMessageBox::Ok, QMessageBox::NoButton); + } + } catch (const PluginInstallException &e) { + QMessageBox::critical(this, "Mumble", e.getMessage(), QMessageBox::Ok, QMessageBox::NoButton); + } +} + +void PluginConfig::save() const { + s.bTransmitPosition = qcbTransmit->isChecked(); + s.qhPluginSettings.clear(); + + if (!s.bTransmitPosition) { + // Make sure that if posData is currently running, it gets reset + // The setting will prevent the system from reactivating + Global::get().pluginManager->unlinkPositionalData(); + } + + constexpr int enableCol = 1; + constexpr int positionalDataCol = 2; + constexpr int keyboardMonitorCol = 3; + + QList<QTreeWidgetItem *> list = qtwPlugins->findItems(QString(), Qt::MatchContains); + for(QTreeWidgetItem *i : list) { + + bool enable = (i->checkState(enableCol) == Qt::Checked); + bool positionalDataEnabled = (i->checkState(positionalDataCol) == Qt::Checked); + bool keyboardMonitoringEnabled = (i->checkState(keyboardMonitorCol) == Qt::Checked); + + const_plugin_ptr_t plugin = pluginForItem(i); + if (plugin) { + // insert plugin to settings + Global::get().pluginManager->enablePositionalDataFor(plugin->getID(), positionalDataEnabled); + Global::get().pluginManager->allowKeyboardMonitoringFor(plugin->getID(), keyboardMonitoringEnabled); + + if (enable) { + if (Global::get().pluginManager->loadPlugin(plugin->getID())) { + // potentially deactivate plugin features + // A plugin's feature is considered to be enabled by default after loading. Thus we only need to + // deactivate the ones we don't want + uint32_t featuresToDeactivate = FEATURE_NONE; + const uint32_t pluginFeatures = plugin->getFeatures(); + + if (!positionalDataEnabled && (pluginFeatures & FEATURE_POSITIONAL)) { + // deactivate this feature only if it is available in the first place + featuresToDeactivate |= FEATURE_POSITIONAL; + } + + if (featuresToDeactivate != FEATURE_NONE) { + uint32_t remainingFeatures = Global::get().pluginManager->deactivateFeaturesFor(plugin->getID(), featuresToDeactivate); + + if (remainingFeatures != FEATURE_NONE) { + Global::get().l->log(Log::Warning, tr("Unable to deactivate all requested features for plugin \"%1\"").arg(plugin->getName())); + } + } + } else { + // loading failed + enable = false; + Global::get().l->log(Log::Warning, tr("Unable to load plugin \"%1\"").arg(plugin->getName())); + } + } else { + Global::get().pluginManager->unloadPlugin(plugin->getID()); + } + + QString pluginKey = QLatin1String(QCryptographicHash::hash(plugin->getFilePath().toUtf8(), QCryptographicHash::Sha1).toHex()); + s.qhPluginSettings.insert(pluginKey, { plugin->getFilePath(), enable, positionalDataEnabled, keyboardMonitoringEnabled }); + } + } +} + +const_plugin_ptr_t PluginConfig::pluginForItem(QTreeWidgetItem *i) const { + if (i) { + return Global::get().pluginManager->getPlugin(i->data(0, Qt::UserRole).toUInt()); + } + + return nullptr; +} + +void PluginConfig::on_qpbConfig_clicked() { + const_plugin_ptr_t plugin = pluginForItem(qtwPlugins->currentItem()); + + if (plugin) { + if (!plugin->showConfigDialog(this)) { + // if the plugin doesn't support showing such a dialog, we'll show a default one + QMessageBox::information(this, QLatin1String("Mumble"), tr("Plugin has no configure function."), QMessageBox::Ok, QMessageBox::NoButton); + } + } +} + +void PluginConfig::on_qpbAbout_clicked() { + const_plugin_ptr_t plugin = pluginForItem(qtwPlugins->currentItem()); + + if (plugin) { + if (!plugin->showAboutDialog(this)) { + // if the plugin doesn't support showing such a dialog, we'll show a default one + QMessageBox::information(this, QLatin1String("Mumble"), tr("Plugin has no about function."), QMessageBox::Ok, QMessageBox::NoButton); + } + } +} + +void PluginConfig::on_qpbReload_clicked() { + Global::get().pluginManager->rescanPlugins(); + refillPluginList(); +} + +void PluginConfig::on_qpbUnload_clicked() { + QTreeWidgetItem *currentItem = qtwPlugins->currentItem(); + if (!currentItem) { + return; + } + + const_plugin_ptr_t plugin = pluginForItem(currentItem); + if (!plugin) { + return; + } + + if (Global::get().pluginManager->clearPlugin(plugin->getID())) { + // Plugin was successfully cleared + currentItem = qtwPlugins->takeTopLevelItem(qtwPlugins->indexOfTopLevelItem(currentItem)); + + delete currentItem; + } else { + qWarning("PluginConfig.cpp: Failed to delete unloaded plugin entry"); + } +} + +void PluginConfig::refillPluginList() { + qtwPlugins->clear(); + + // get plugins already sorted according to their name + const QVector<const_plugin_ptr_t > plugins = Global::get().pluginManager->getPlugins(true); + + foreach(const_plugin_ptr_t currentPlugin, plugins) { + QTreeWidgetItem *i = new QTreeWidgetItem(qtwPlugins); + i->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + i->setCheckState(1, currentPlugin->isLoaded() ? Qt::Checked : Qt::Unchecked); + + if (currentPlugin->getFeatures() & FEATURE_POSITIONAL) { + i->setCheckState(2, currentPlugin->isPositionalDataEnabled() ? Qt::Checked : Qt::Unchecked); + i->setToolTip(2, tr("Whether the positional audio feature of this plugin should be enabled")); + } else { + i->setToolTip(2, tr("This plugin does not provide support for positional audio")); + } + + i->setCheckState(3, currentPlugin->isKeyboardMonitoringAllowed() ? Qt::Checked : Qt::Unchecked); + i->setToolTip(3, tr("Whether this plugin has the permission to be listening to all keyboard events that occur while Mumble has focus")); + + i->setText(0, currentPlugin->getName()); + i->setToolTip(0, currentPlugin->getDescription().toHtmlEscaped()); + i->setToolTip(1, tr("Whether this plugin should be enabled")); + i->setData(0, Qt::UserRole, currentPlugin->getID()); + } + + qtwPlugins->setCurrentItem(qtwPlugins->topLevelItem(0)); + on_qtwPlugins_currentItemChanged(qtwPlugins->topLevelItem(0), NULL); +} + +void PluginConfig::on_qtwPlugins_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *) { + const_plugin_ptr_t plugin = pluginForItem(current); + + if (plugin) { + qpbAbout->setEnabled(plugin->providesAboutDialog()); + + qpbConfig->setEnabled(plugin->providesConfigDialog()); + + qpbUnload->setEnabled(true); + } else { + qpbAbout->setEnabled(false); + qpbConfig->setEnabled(false); + qpbUnload->setEnabled(false); + } +} |