diff --git a/src/Cutter.cpp b/src/Cutter.cpp index 15a14084..35ec9303 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -1696,9 +1696,6 @@ QString CutterCore::getVersionInformation() return ret; } - - - QJsonArray CutterCore::getOpenedFiles() { QJsonDocument files = cmdj("oj"); @@ -1729,3 +1726,13 @@ void CutterCore::deleteTask(RCoreTask *task) { r_core_task_del(core_, task->id); } + +void CutterCore::setCutterPlugins(QList plugins) +{ + this->plugins = plugins; +} + +QList CutterCore::getCutterPlugins() +{ + return plugins; +} diff --git a/src/Cutter.h b/src/Cutter.h index 58a96ef9..47eac268 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -45,6 +45,8 @@ typedef ut64 RVA; #define RVA_INVALID RVA_MAX class AsyncTaskManager; +class CutterCore; +#include "plugins/CutterPlugin.h" class RCoreLocked { @@ -506,6 +508,9 @@ public: void joinTask(RCoreTask *task); void deleteTask(RCoreTask *task); + void setCutterPlugins(QList plugins); + QList getCutterPlugins(); + RCoreLocked core() const; signals: @@ -548,6 +553,8 @@ private: RCore *core_; AsyncTaskManager *asyncTaskManager; + + QList plugins; }; class ccClass : public CutterCore diff --git a/src/Cutter.pro b/src/Cutter.pro index ec3072ad..8bf2a549 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -55,6 +55,9 @@ macx { unix:exists(/usr/local/include/libr) { INCLUDEPATH += /usr/local/include/libr } +unix { + QMAKE_LFLAGS += -rdynamic # Export dynamic symbols for plugins +} # Libraries include(lib_radare2.pri) @@ -253,7 +256,8 @@ HEADERS += \ utils/StringsTask.h \ utils/FunctionsTask.h \ utils/CommandTask.h \ - utils/ProgressIndicator.h + utils/ProgressIndicator.h \ + plugins/CutterPlugin.h FORMS += \ dialogs/AboutDialog.ui \ diff --git a/src/CutterApplication.cpp b/src/CutterApplication.cpp index 406cbe15..0ecd0664 100644 --- a/src/CutterApplication.cpp +++ b/src/CutterApplication.cpp @@ -8,10 +8,13 @@ #include #include #include +#include +#include #ifdef CUTTER_ENABLE_JUPYTER #include "utils/JupyterConnection.h" #endif +#include "plugins/CutterPlugin.h" CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv) { @@ -96,6 +99,9 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc } else { // filename specified as positional argument mainWindow->openNewFile(args[0], analLevelSpecified ? analLevel : -1); } + + // Load plugins + loadPlugins(); } CutterApplication::~CutterApplication() @@ -126,3 +132,33 @@ bool CutterApplication::event(QEvent *e) } return QApplication::event(e); } + +void CutterApplication::loadPlugins() +{ + QList plugins; + QDir pluginsDir(qApp->applicationDirPath()); + #if defined(Q_OS_WIN) + if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") + pluginsDir.cdUp(); + #elif defined(Q_OS_MAC) + if (pluginsDir.dirName() == "MacOS") { + pluginsDir.cdUp(); + pluginsDir.cdUp(); + pluginsDir.cdUp(); + } + #endif + pluginsDir.cd("plugins"); + foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { + QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); + QObject *plugin = pluginLoader.instance(); + if (plugin) { + CutterPlugin *cutterPlugin = qobject_cast(plugin); + if (cutterPlugin) { + cutterPlugin->setupPlugin(Core()); + plugins.append(cutterPlugin); + } + } + } + + Core()->setCutterPlugins(plugins); +} diff --git a/src/CutterApplication.h b/src/CutterApplication.h index 0edb625c..703b4619 100644 --- a/src/CutterApplication.h +++ b/src/CutterApplication.h @@ -3,6 +3,7 @@ #include #include +#include #include "MainWindow.h" @@ -20,6 +21,8 @@ public: return mainWindow; } + void loadPlugins(); + protected: bool event(QEvent *e); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index caaf3e06..34815751 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -239,6 +239,13 @@ void MainWindow::initUI() updateTasksIndicator(); connect(core->getAsyncTaskManager(), &AsyncTaskManager::tasksChanged, this, &MainWindow::updateTasksIndicator); + + /* Load plugins */ + QList plugins = Core()->getCutterPlugins(); + for (auto plugin : plugins) { + CutterDockWidget *pluginDock = plugin->setupInterface(this); + tabifyDockWidget(dashboardDock, pluginDock); + } } void MainWindow::updateTasksIndicator() diff --git a/src/dialogs/R2PluginsDialog.cpp b/src/dialogs/R2PluginsDialog.cpp index 518722fc..d50174e0 100644 --- a/src/dialogs/R2PluginsDialog.cpp +++ b/src/dialogs/R2PluginsDialog.cpp @@ -50,6 +50,15 @@ R2PluginsDialog::R2PluginsDialog(QWidget *parent) : ui->RAsmTreeWidget->addTopLevelItem(item); } qhelpers::adjustColumns(ui->RAsmTreeWidget, 0); + + for (CutterPlugin *plugin : Core()->getCutterPlugins()) { + QTreeWidgetItem *item = new QTreeWidgetItem(); + item->setText(0, plugin->name); + item->setText(1, plugin->description); + item->setText(2, plugin->version); + item->setText(3, plugin->author); + ui->CutterTreeWidget->addTopLevelItem(item); + } } R2PluginsDialog::~R2PluginsDialog() diff --git a/src/dialogs/R2PluginsDialog.ui b/src/dialogs/R2PluginsDialog.ui index 780127b8..e7990f01 100644 --- a/src/dialogs/R2PluginsDialog.ui +++ b/src/dialogs/R2PluginsDialog.ui @@ -2,12 +2,23 @@ R2PluginsDialog + + + 0 + 0 + 296 + 310 + + radare2 plugin information + + 4 + RBin @@ -168,6 +179,44 @@ + + + Cutter + + + + + + Cutter plugins + + + + + + + + Name + + + + + Description + + + + + Version + + + + + Author + + + + + + diff --git a/src/plugins/CutterPlugin.h b/src/plugins/CutterPlugin.h new file mode 100644 index 00000000..2585169a --- /dev/null +++ b/src/plugins/CutterPlugin.h @@ -0,0 +1,31 @@ +#ifndef CUTTERPLUGIN_H +#define CUTTERPLUGIN_H + +class CutterPlugin; +class MainWindow; + +#include "Cutter.h" +#include "widgets/CutterDockWidget.h" + +class CutterPlugin +{ +public: + virtual ~CutterPlugin() {} + virtual void setupPlugin(CutterCore *core) = 0; + virtual CutterDockWidget* setupInterface(MainWindow *main, QAction *action = nullptr) = 0; + + QString name; + QString description; + QString version; + QString author; + +protected: + CutterCore *core; + CutterDockWidget *dockable; +}; + +#define CutterPlugin_iid "org.radare.cutter.plugins.CutterPlugin" + +Q_DECLARE_INTERFACE(CutterPlugin, CutterPlugin_iid) + +#endif // CUTTERPLUGIN_H diff --git a/src/plugins/CutterSamplePlugin.cpp b/src/plugins/CutterSamplePlugin.cpp new file mode 100644 index 00000000..29680cec --- /dev/null +++ b/src/plugins/CutterSamplePlugin.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +#include "CutterSamplePlugin.h" +#include "utils/TempConfig.h" +#include "utils/Configuration.h" + +void CutterSamplePlugin::setupPlugin(CutterCore *core) +{ + this->core = core; + this->name = "SamplePlugin"; + this->description = "Just a sample plugin."; + this->version = "1.0"; + this->author = "xarkes"; +} + +CutterDockWidget* CutterSamplePlugin::setupInterface(MainWindow *main, QAction* actions) +{ + // Instantiate dock widget + dockable = new CutterSamplePluginWidget(main, actions); + return dockable; +} + +CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action) +{ + this->setObjectName("CutterSamplePluginWidget"); + this->setWindowTitle("Sample Plugin"); + QWidget *content = new QWidget(); + this->setWidget(content); + + QVBoxLayout *layout = new QVBoxLayout(this); + content->setLayout(layout); + text = new QLabel(content); + text->setFont(Config()->getFont()); + text->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + layout->addWidget(text); + + QPushButton *button = new QPushButton(content); + button->setText("Want a fortune?"); + button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + button->setMaximumHeight(50); + button->setMaximumWidth(200); + layout->addWidget(button); + layout->setAlignment(button, Qt::AlignHCenter); + + connect(Core(), &CutterCore::seekChanged, this, &CutterSamplePluginWidget::on_seekChanged); + connect(button, &QPushButton::clicked, this, &CutterSamplePluginWidget::on_buttonClicked); +} + +void CutterSamplePluginWidget::on_seekChanged(RVA addr) +{ + Q_UNUSED(addr); + QString res; + { + TempConfig tempConfig; + tempConfig.set("scr.color", 0); + res = Core()->cmd("?E `pi 1`"); + } + text->setText(res); +} + +void CutterSamplePluginWidget::on_buttonClicked() +{ + QString res = Core()->cmd("?E `fo`"); + text->setText(res); +} diff --git a/src/plugins/CutterSamplePlugin.h b/src/plugins/CutterSamplePlugin.h new file mode 100644 index 00000000..557abb61 --- /dev/null +++ b/src/plugins/CutterSamplePlugin.h @@ -0,0 +1,35 @@ +#ifndef CUTTERSAMPLEPLUGIN_H +#define CUTTERSAMPLEPLUGIN_H + +#include +#include +#include "CutterPlugin.h" + +class CutterSamplePlugin : public QObject, CutterPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.radare.cutter.plugins.CutterPlugin") + Q_INTERFACES(CutterPlugin) + +public: + void setupPlugin(CutterCore *core) override; + CutterDockWidget* setupInterface(MainWindow *main, QAction *action = nullptr) override; +}; + +class CutterSamplePluginWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit CutterSamplePluginWidget(MainWindow *main, QAction *action); + +private: + QLabel* text; + +private slots: + void on_seekChanged(RVA addr); + void on_buttonClicked(); +}; + + +#endif // CUTTERSAMPLEPLUGIN_H diff --git a/src/plugins/PluginSample.pro b/src/plugins/PluginSample.pro new file mode 100644 index 00000000..12771154 --- /dev/null +++ b/src/plugins/PluginSample.pro @@ -0,0 +1,13 @@ +HEADERS += CutterSamplePlugin.h CutterPlugin.h +INCLUDEPATH += ../ +SOURCES += CutterSamplePlugin.cpp + +# Needed for r_core include TODO cross platform +unix:exists(/usr/include/libr) { + INCLUDEPATH += /usr/include/libr +} + +TEMPLATE = lib +CONFIG += plugin +QT += widgets +TARGET = PluginSample