Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/Remmina/Remmina.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Winterhalter <2370037-ToolsDevler@users.noreply.gitlab.com>2022-04-18 02:43:50 +0300
committerAntenore Gatta (tmow) <antenore@simbiosi.org>2022-04-18 02:43:50 +0300
commit6092eca0b3576f89bd586e89b3e56ad477b8bb3c (patch)
tree217f4248fb0d4068b9b7363d9049133a7a1d5ff1
parent9333622adbaaa572822fcde486b74691d386b8eb (diff)
Python plugins
-rw-r--r--.gitignore2
-rw-r--r--flatpak/org.remmina.Remmina-local.json4
-rw-r--r--flatpak/org.remmina.Remmina.json2
m---------flatpak/shared-modules0
-rw-r--r--plugins/pyvnc/pyvnc.py97
-rw-r--r--plugins/secret/src/glibsecret_plugin.c14
-rw-r--r--plugins/tool_hello_world_python/toolsdevler.py72
-rw-r--r--src/CMakeLists.txt16
-rw-r--r--src/include/remmina/plugin.h26
-rw-r--r--src/include/remmina/types.h16
-rw-r--r--src/rcw.c2
-rw-r--r--src/remmina.c2
-rw-r--r--src/remmina_exec.c2
-rw-r--r--src/remmina_file.c14
-rw-r--r--src/remmina_file_manager.c2
-rw-r--r--src/remmina_main.c29
-rw-r--r--src/remmina_main.h1
-rw-r--r--src/remmina_mpchange.c2
-rw-r--r--src/remmina_plugin_manager.c6
-rw-r--r--src/remmina_plugin_native.c18
-rw-r--r--src/remmina_plugin_native.h2
-rw-r--r--src/remmina_plugin_python.c228
-rw-r--r--src/remmina_plugin_python.h29
-rw-r--r--src/remmina_plugin_python_common.c312
-rw-r--r--src/remmina_plugin_python_common.h279
-rw-r--r--src/remmina_plugin_python_entry.c101
-rw-r--r--src/remmina_plugin_python_entry.h61
-rw-r--r--src/remmina_plugin_python_file.c161
-rw-r--r--src/remmina_plugin_python_file.h (renamed from src/remmina_plugin_python_module.h)33
-rw-r--r--src/remmina_plugin_python_module.c281
-rw-r--r--src/remmina_plugin_python_pref.c104
-rw-r--r--src/remmina_plugin_python_pref.h61
-rw-r--r--src/remmina_plugin_python_protocol.c317
-rw-r--r--src/remmina_plugin_python_protocol.h107
-rw-r--r--src/remmina_plugin_python_protocol_widget.c507
-rw-r--r--src/remmina_plugin_python_protocol_widget.h25
-rw-r--r--src/remmina_plugin_python_remmina.c1556
-rw-r--r--src/remmina_plugin_python_remmina.h73
-rw-r--r--src/remmina_plugin_python_remmina_file.c211
-rw-r--r--src/remmina_plugin_python_remmina_file.h26
-rw-r--r--src/remmina_plugin_python_secret.c138
-rw-r--r--src/remmina_plugin_python_secret.h67
-rw-r--r--src/remmina_plugin_python_tool.c84
-rw-r--r--src/remmina_plugin_python_tool.h68
-rw-r--r--src/remmina_pref_dialog.c2
-rw-r--r--src/remmina_protocol_widget.c6
46 files changed, 3647 insertions, 1519 deletions
diff --git a/.gitignore b/.gitignore
index 272f81a3a..578139080 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
*~
build
builddir
+cmake-build-debug/
+cmake-build-release/
tags
data/desktop/org.remmina.Remmina.desktop
diff --git a/flatpak/org.remmina.Remmina-local.json b/flatpak/org.remmina.Remmina-local.json
index 99ee971df..c437fc40c 100644
--- a/flatpak/org.remmina.Remmina-local.json
+++ b/flatpak/org.remmina.Remmina-local.json
@@ -1,7 +1,7 @@
{
"app-id": "org.remmina.Remmina",
"runtime": "org.gnome.Platform",
- "runtime-version": "41",
+ "runtime-version": "42",
"sdk": "org.gnome.Sdk",
"command": "remmina",
"cleanup": [
@@ -66,7 +66,7 @@
],
"modules": [
"shared-modules/intltool/intltool-0.51.json",
- "shared-modules/dbus-glib/dbus-glib-0.110.json",
+ "shared-modules/dbus-glib/dbus-glib.json",
"shared-modules/libappindicator/libappindicator-gtk3-12.10.json",
"shared-modules/libusb/libusb.json",
{
diff --git a/flatpak/org.remmina.Remmina.json b/flatpak/org.remmina.Remmina.json
index e08480aff..99d0155e3 100644
--- a/flatpak/org.remmina.Remmina.json
+++ b/flatpak/org.remmina.Remmina.json
@@ -66,7 +66,7 @@
],
"modules": [
"shared-modules/intltool/intltool-0.51.json",
- "shared-modules/dbus-glib/dbus-glib-0.110.json",
+ "shared-modules/dbus-glib/dbus-glib.json",
"shared-modules/libappindicator/libappindicator-gtk3-12.10.json",
"shared-modules/libusb/libusb.json",
{
diff --git a/flatpak/shared-modules b/flatpak/shared-modules
-Subproject e71f3d4bb6d16088230464ffe33743a8cfa28b0
+Subproject 977feac6610e324a44e38fc2946b3d333e170a7
diff --git a/plugins/pyvnc/pyvnc.py b/plugins/pyvnc/pyvnc.py
deleted file mode 100644
index 04c6c24e1..000000000
--- a/plugins/pyvnc/pyvnc.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import sys
-import remmina
-import enum
-import gi
-gi.require_version("Gtk", "3.0")
-from gi.repository import Gtk, GLib
-import psutil
-
-class VncFeature:
- PrefQuality = 1
- PrefViewonly = 2
- PrefDisableserverinput = 3
- ToolRefresh = 4
- ToolChat = 5
- ToolSendCtrlAltDel = 6
- Scale = 7
- Unfocus = 8
-
-
-class Plugin:
- def __init__(self):
- self.name = "PyVNC"
- self.type = "protocol"
- self.description = "VNC but in Python!"
- self.version = "1.0"
- self.icon_name = "org.remmina.Remmina-vnc-symbolic"
- self.icon_name_ssh = "org.remmina.Remmina-vnc-ssh-symbolic"
- self.ssh_setting = remmina.PROTOCOL_SSH_SETTING_TUNNEL
-
- self.features = [
- remmina.Feature(
- type=remmina.PROTOCOL_FEATURE_TYPE_PREF,
- id=VncFeature.PrefQuality,
- opt1=remmina.PROTOCOL_FEATURE_PREF_RADIO,
- opt2="quality",
- opt3=None)
- ,remmina.Feature(remmina.PROTOCOL_FEATURE_TYPE_PREF, VncFeature.PrefViewonly, remmina.PROTOCOL_FEATURE_PREF_CHECK, "viewonly", None)
- ,remmina.Feature(remmina.PROTOCOL_FEATURE_TYPE_PREF, VncFeature.PrefDisableserverinput, remmina.PROTOCOL_SETTING_TYPE_CHECK, "disableserverinput", "Disable server input")
- ,remmina.Feature(remmina.PROTOCOL_FEATURE_TYPE_TOOL, VncFeature.ToolRefresh, "Refresh", None, None)
- ,remmina.Feature(remmina.PROTOCOL_FEATURE_TYPE_TOOL, VncFeature.ToolChat, "Open Chat…", "face-smile", None)
- ,remmina.Feature(remmina.PROTOCOL_FEATURE_TYPE_TOOL, VncFeature.ToolSendCtrlAltDel, "Send Ctrl+Alt+Delete", None, None)
- ,remmina.Feature(remmina.PROTOCOL_FEATURE_TYPE_SCALE, VncFeature.Scale, None, None, None)
- ,remmina.Feature(remmina.PROTOCOL_FEATURE_TYPE_UNFOCUS, VncFeature.Unfocus, None, None, None)
- ]
-
- colordepths = ("8", "256 colors (8 bpp)", "16", "High color (16 bpp)", "32", "True color (32 bpp)")
- print(type(colordepths))
- qualities = ("0", "Poor (fastest)", "1","Medium", "2","Good", "9","Best (slowest)")
- print(type(qualities))
- self.basic_settings = [
- remmina.Setting(type=remmina.PROTOCOL_SETTING_TYPE_SERVER, name="server", label="", compact=False, opt1="_rfb._tcp",opt2=None)
- , remmina.Setting(type=remmina.PROTOCOL_SETTING_TYPE_TEXT, name="proxy", label="Repeater", compact=False, opt1=None, opt2=None)
- , remmina.Setting(type=remmina.PROTOCOL_SETTING_TYPE_TEXT, name="username", label="Username", compact=False, opt1=None, opt2=None)
- , remmina.Setting(type=remmina.PROTOCOL_SETTING_TYPE_PASSWORD,name="password", label="User password",compact=False, opt1=None, opt2=None)
- , remmina.Setting(type=remmina.PROTOCOL_SETTING_TYPE_SELECT, name="colordepth",label="Color depth", compact=False, opt1=colordepths,opt2=None)
- , remmina.Setting(type=remmina.PROTOCOL_SETTING_TYPE_SELECT, name="quality", label="Quality", compact=False, opt1=qualities,opt2=None)
- , remmina.Setting(type=remmina.PROTOCOL_SETTING_TYPE_KEYMAP, name="keymap", label="", compact=False, opt1=None, opt2=None)
- ]
- self.advanced_settings = [
- remmina.Setting(remmina.PROTOCOL_SETTING_TYPE_CHECK, "showcursor", "Show remote cursor", True, None, None)
- , remmina.Setting(remmina.PROTOCOL_SETTING_TYPE_CHECK, "viewonly", "View only", False, None, None)
- , remmina.Setting(remmina.PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", "Disable clipboard sync", True, None, None)
- , remmina.Setting(remmina.PROTOCOL_SETTING_TYPE_CHECK, "disableencryption", "Disable encryption", False, None, None)
- , remmina.Setting(remmina.PROTOCOL_SETTING_TYPE_CHECK, "disableserverinput", "Disable server input", True, None, None)
- , remmina.Setting(remmina.PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", "Disable password storing", False, None, None)
- ]
-
- def init(self, gp):
- print("[PyVNC.init]: Called!")
- pass
-
- def open_connection(self, gp):
- print("[PyVNC.open_connection]: Called!")
- pass
-
- def close_connection(self, gp):
- print("[PyVNC.close_connection]: Called!")
- pass
-
- def query_feature(self, gp):
- print("[PyVNC.query_feature]: Called!")
- pass
-
- def call_feature(self, gp):
- print("[PyVNC.call_feature]: Called!")
- pass
-
- def keystroke(self, gp):
- print("[PyVNC.keystroke]: Called!")
- pass
-
- def screenshot(self, gp):
- print("[PyVNC.screenshot]: Called!")
- pass
-
-myPlugin = Plugin()
-remmina.register_plugin(myPlugin)
diff --git a/plugins/secret/src/glibsecret_plugin.c b/plugins/secret/src/glibsecret_plugin.c
index 5bd585762..dc6d8cc36 100644
--- a/plugins/secret/src/glibsecret_plugin.c
+++ b/plugins/secret/src/glibsecret_plugin.c
@@ -61,7 +61,7 @@ static SecretCollection* defaultcollection;
#endif
-gboolean remmina_plugin_glibsecret_is_service_available()
+gboolean remmina_plugin_glibsecret_is_service_available(RemminaSecretPlugin* plugin)
{
#ifdef LIBSECRET_VERSION_0_18
if (secretservice && defaultcollection)
@@ -73,7 +73,7 @@ gboolean remmina_plugin_glibsecret_is_service_available()
#endif
}
-static void remmina_plugin_glibsecret_unlock_secret_service()
+static void remmina_plugin_glibsecret_unlock_secret_service(RemminaSecretPlugin* plugin)
{
TRACE_CALL(__func__);
@@ -97,7 +97,7 @@ static void remmina_plugin_glibsecret_unlock_secret_service()
return;
}
-void remmina_plugin_glibsecret_store_password(RemminaFile *remminafile, const gchar *key, const gchar *password)
+void remmina_plugin_glibsecret_store_password(RemminaSecretPlugin* plugin, RemminaFile *remminafile, const gchar *key, const gchar *password)
{
TRACE_CALL(__func__);
GError *r = NULL;
@@ -118,7 +118,7 @@ void remmina_plugin_glibsecret_store_password(RemminaFile *remminafile, const gc
}
gchar*
-remmina_plugin_glibsecret_get_password(RemminaFile *remminafile, const gchar *key)
+remmina_plugin_glibsecret_get_password(RemminaSecretPlugin* plugin, RemminaFile *remminafile, const gchar *key)
{
TRACE_CALL(__func__);
GError *r = NULL;
@@ -138,7 +138,7 @@ remmina_plugin_glibsecret_get_password(RemminaFile *remminafile, const gchar *ke
}
}
-void remmina_plugin_glibsecret_delete_password(RemminaFile *remminafile, const gchar *key)
+void remmina_plugin_glibsecret_delete_password(RemminaSecretPlugin* plugin, RemminaFile *remminafile, const gchar *key)
{
TRACE_CALL(__func__);
GError *r = NULL;
@@ -152,7 +152,7 @@ void remmina_plugin_glibsecret_delete_password(RemminaFile *remminafile, const g
REMMINA_PLUGIN_DEBUG("password “%s” cannot be deleted for file %s", key, path);
}
-gboolean remmina_plugin_glibsecret_init()
+gboolean remmina_plugin_glibsecret_init(RemminaSecretPlugin* plugin)
{
#ifdef LIBSECRET_VERSION_0_18
GError *error;
@@ -173,7 +173,7 @@ gboolean remmina_plugin_glibsecret_init()
return FALSE;
}
- remmina_plugin_glibsecret_unlock_secret_service();
+ remmina_plugin_glibsecret_unlock_secret_service(plugin);
return TRUE;
#else
diff --git a/plugins/tool_hello_world_python/toolsdevler.py b/plugins/tool_hello_world_python/toolsdevler.py
deleted file mode 100644
index d47d86d5e..000000000
--- a/plugins/tool_hello_world_python/toolsdevler.py
+++ /dev/null
@@ -1,72 +0,0 @@
-import sys
-
-if not hasattr(sys, 'argv'):
- sys.argv = ['']
-
-import remmina
-import gi
-gi.require_version("Gtk", "3.0")
-from gi.repository import Gtk, GLib
-import psutil
-
-class HelloPlugin:
- def __init__(self):
- self.name = "Hello"
- self.type = "protocol"
- self.description = "Hello World!"
- self.version = "1.0"
- self.icon_name = "org.remmina.Remmina-tool-symbolic"
- self.icon_name_ssh = "org.remmina.Remmina-tool-symbolic"
- self.btn = Gtk.Button(label="Hello!")
- self.btn.connect("clicked", self.callback_add, "hello")
- pass
-
- def callback_add(self, widget, data):
- print("Click :)")
-
- def name(self):
- return "Hello"
-
- def init(self):
- print("Init!")
- return True
-
-
- def open_connection(self, viewport):
- print("open_connection!")
- def foreach_child(child):
- child.add(self.btn)
- self.btn.show()
- viewport.foreach(foreach_child)
- print("Connected!")
-
- remmina.log_print("[%s]: Plugin open connection\n" % self.name)
- return True
-
- def draw(self, widget, cr, color):
- cr.rectangle(0, 0, 100, 100)
- cr.set_source_rgb(color[0], color[1], color[2])
- cr.fill()
- cr.queue_draw_area(0, 0, 100, 100)
-
- return True
-
- def close_connection(self, viewport):
- print("close_connection!")
- remmina.log_print("[%s]: Plugin close connection\n" % self.name)
- return True
-
- def query_feature(self):
- pass
-
- def call_feature(self):
- pass
-
- def send_keystrokes(self):
- pass
-
- def get_plugin_screenshot(self):
- pass
-
-myPlugin = HelloPlugin()
-remmina.register_plugin(myPlugin)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ca002d95f..a743f5d9a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -158,8 +158,20 @@ if(WITH_PYTHONLIBS)
${SRCINDEX}
"remmina_plugin_python.c"
"remmina_plugin_python.h"
- "remmina_plugin_python_module.c"
- "remmina_plugin_python_module.h"
+ "remmina_plugin_python_protocol.c"
+ "remmina_plugin_python_protocol.h"
+ "remmina_plugin_python_entry.c"
+ "remmina_plugin_python_entry.h"
+ "remmina_plugin_python_file.c"
+ "remmina_plugin_python_file.h"
+ "remmina_plugin_python_secret.c"
+ "remmina_plugin_python_secret.h"
+ "remmina_plugin_python_tool.c"
+ "remmina_plugin_python_tool.h"
+ "remmina_plugin_python_pref.c"
+ "remmina_plugin_python_pref.h"
+ "remmina_plugin_python_common.c"
+ "remmina_plugin_python_common.h"
"remmina_plugin_python_remmina.c"
"remmina_plugin_python_remmina.h"
"remmina_plugin_python_protocol_widget.c"
diff --git a/src/include/remmina/plugin.h b/src/include/remmina/plugin.h
index a6ff980f3..c75289af8 100644
--- a/src/include/remmina/plugin.h
+++ b/src/include/remmina/plugin.h
@@ -70,7 +70,7 @@ typedef struct _RemminaProtocolPlugin {
const gchar * icon_name;
const gchar * icon_name_ssh;
const RemminaProtocolSetting * basic_settings;
- const RemminaProtocolSetting * advanced_settings;
+ const RemminaProtocolSetting * advanced_settings;
RemminaProtocolSSHSetting ssh_setting;
const RemminaProtocolFeature * features;
@@ -92,7 +92,7 @@ typedef struct _RemminaEntryPlugin {
const gchar * domain;
const gchar * version;
- void (*entry_func)(void);
+ void (*entry_func)(struct _RemminaEntryPlugin* instance);
} RemminaEntryPlugin;
typedef struct _RemminaFilePlugin {
@@ -102,10 +102,10 @@ typedef struct _RemminaFilePlugin {
const gchar * domain;
const gchar * version;
- gboolean (*import_test_func)(const gchar *from_file);
- RemminaFile * (*import_func)(const gchar * from_file);
- gboolean (*export_test_func)(RemminaFile *file);
- gboolean (*export_func)(RemminaFile *file, const gchar *to_file);
+ gboolean (*import_test_func)(struct _RemminaFilePlugin* instance, const gchar *from_file);
+ RemminaFile * (*import_func)(struct _RemminaFilePlugin* instance, const gchar * from_file);
+ gboolean (*export_test_func)(struct _RemminaFilePlugin* instance, RemminaFile *file);
+ gboolean (*export_func)(struct _RemminaFilePlugin* instance, RemminaFile *file, const gchar *to_file);
const gchar * export_hints;
} RemminaFilePlugin;
@@ -116,7 +116,7 @@ typedef struct _RemminaToolPlugin {
const gchar * domain;
const gchar * version;
- void (*exec_func)(void);
+ void (*exec_func)(GtkMenuItem* item, struct _RemminaToolPlugin* instance);
} RemminaToolPlugin;
typedef struct _RemminaPrefPlugin {
@@ -127,7 +127,7 @@ typedef struct _RemminaPrefPlugin {
const gchar * version;
const gchar * pref_label;
- GtkWidget * (*get_pref_body)(void);
+ GtkWidget * (*get_pref_body)(struct _RemminaPrefPlugin* instance);
} RemminaPrefPlugin;
typedef struct _RemminaSecretPlugin {
@@ -138,11 +138,11 @@ typedef struct _RemminaSecretPlugin {
const gchar * version;
int init_order;
- gboolean (*init)(void);
- gboolean (*is_service_available)(void);
- void (*store_password)(RemminaFile *remminafile, const gchar *key, const gchar *password);
- gchar * (*get_password)(RemminaFile * remminafile, const gchar *key);
- void (*delete_password)(RemminaFile *remminafile, const gchar *key);
+ gboolean (*init)(struct _RemminaSecretPlugin* instance);
+ gboolean (*is_service_available)(struct _RemminaSecretPlugin* instance);
+ void (*store_password)(struct _RemminaSecretPlugin* instance, RemminaFile *remminafile, const gchar *key, const gchar *password);
+ gchar * (*get_password)(struct _RemminaSecretPlugin* instance, RemminaFile * remminafile, const gchar *key);
+ void (*delete_password)(struct _RemminaSecretPlugin* instance, RemminaFile *remminafile, const gchar *key);
} RemminaSecretPlugin;
/* Plugin Service is a struct containing a list of function pointers,
diff --git a/src/include/remmina/types.h b/src/include/remmina/types.h
index 3f27691a8..4db5a5e17 100644
--- a/src/include/remmina/types.h
+++ b/src/include/remmina/types.h
@@ -37,6 +37,7 @@
#pragma once
#include <glib.h>
+#include <glib-object.h>
G_BEGIN_DECLS
@@ -56,12 +57,27 @@ typedef enum {
#define REMMINA_PROTOCOL_FEATURE_PREF_RADIO 1
#define REMMINA_PROTOCOL_FEATURE_PREF_CHECK 2
+typedef enum
+{
+ REMMINA_TYPEHINT_STRING,
+ REMMINA_TYPEHINT_SIGNED,
+ REMMINA_TYPEHINT_UNSIGNED,
+ REMMINA_TYPEHINT_BOOLEAN,
+ REMMINA_TYPEHINT_CPOINTER,
+ REMMINA_TYPEHINT_RAW,
+ REMMINA_TYPEHINT_TUPLE,
+ REMMINA_TYPEHINT_UNDEFINED,
+} RemminaTypeHint;
+
typedef struct _RemminaProtocolFeature {
RemminaProtocolFeatureType type;
gint id;
gpointer opt1;
gpointer opt2;
gpointer opt3;
+ RemminaTypeHint opt1_type_hint;
+ RemminaTypeHint opt2_type_hint;
+ RemminaTypeHint opt3_type_hint;
} RemminaProtocolFeature;
typedef struct _RemminaPluginScreenshotData {
diff --git a/src/rcw.c b/src/rcw.c
index 3424377a6..36df55306 100644
--- a/src/rcw.c
+++ b/src/rcw.c
@@ -3682,7 +3682,7 @@ static RemminaConnectionWindow *rcw_create_scrolled(gint width, gint height, gbo
gtk_widget_realize(GTK_WIDGET(cnnwin));
gtk_window_set_default_size(GTK_WINDOW(cnnwin), width, height);
- g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
+ g_object_set(settings, "gtk-application-prefer-dark-theme", &remmina_pref.dark_theme, NULL);
/* Create the toolbar */
toolbar = rcw_create_toolbar(cnnwin, SCROLLED_WINDOW_MODE);
diff --git a/src/remmina.c b/src/remmina.c
index c3eb55c46..9d658d73e 100644
--- a/src/remmina.c
+++ b/src/remmina.c
@@ -303,7 +303,7 @@ static void remmina_on_startup(GApplication *app)
if (!secret_plugin)
g_print("Warning: Remmina is running without a secret plugin. Passwords will be saved in a less secure way.\n");
else
- if (!secret_plugin->is_service_available())
+ if (!secret_plugin->is_service_available(secret_plugin))
g_print("Warning: Remmina is running with a secrecy plugin, but it cannot connect to a secrecy service.\n");
remmina_exec_command(REMMINA_COMMAND_AUTOSTART, NULL);
diff --git a/src/remmina_exec.c b/src/remmina_exec.c
index bc6067edf..6b2c5cbd9 100644
--- a/src/remmina_exec.c
+++ b/src/remmina_exec.c
@@ -486,7 +486,7 @@ void remmina_exec_command(RemminaCommandType command, const gchar* data)
case REMMINA_COMMAND_PLUGIN:
plugin = (RemminaEntryPlugin*)remmina_plugin_manager_get_plugin(REMMINA_PLUGIN_TYPE_ENTRY, data);
if (plugin) {
- plugin->entry_func();
+ plugin->entry_func(plugin);
}else {
widget = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
_("Plugin %s is not registered."), data);
diff --git a/src/remmina_file.c b/src/remmina_file.c
index 9dc38a516..06db1ebc0 100644
--- a/src/remmina_file.c
+++ b/src/remmina_file.c
@@ -387,7 +387,7 @@ remmina_file_load(const gchar *filename)
}
secret_plugin = remmina_plugin_manager_get_secret_plugin();
- secret_service_available = secret_plugin && secret_plugin->is_service_available();
+ secret_service_available = secret_plugin && secret_plugin->is_service_available(secret_plugin);
remminafile->filename = g_strdup(filename);
gsize nkeys = 0;
@@ -421,7 +421,7 @@ remmina_file_load(const gchar *filename)
}
#endif
if ((g_strcmp0(s, ".") == 0) && (secret_service_available)) {
- gchar *sec = secret_plugin->get_password(remminafile, key);
+ gchar *sec = secret_plugin->get_password(secret_plugin, remminafile, key);
remmina_file_set_string(remminafile, key, sec);
/* Annotate in spsettings that this value comes from secret_plugin */
g_hash_table_insert(remminafile->spsettings, g_strdup(key), NULL);
@@ -741,7 +741,7 @@ void remmina_file_save(RemminaFile *remminafile)
}
secret_plugin = remmina_plugin_manager_get_secret_plugin();
- secret_service_available = secret_plugin && secret_plugin->is_service_available();
+ secret_service_available = secret_plugin && secret_plugin->is_service_available(secret_plugin);
g_hash_table_iter_init(&iter, remminafile->settings);
while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) {
@@ -751,11 +751,11 @@ void remmina_file_save(RemminaFile *remminafile)
REMMINA_DEBUG("We have a secret and disablepasswordstoring=0");
if (value && value[0]) {
if (g_strcmp0(value, ".") != 0)
- secret_plugin->store_password(remminafile, key, value);
+ secret_plugin->store_password(secret_plugin, remminafile, key, value);
g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, ".");
} else {
g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, "");
- secret_plugin->delete_password(remminafile, key);
+ secret_plugin->delete_password(secret_plugin, remminafile, key);
}
} else {
REMMINA_DEBUG("We have a password and disablepasswordstoring=0");
@@ -771,7 +771,7 @@ void remmina_file_save(RemminaFile *remminafile)
if (value && value[0]) {
if (g_strcmp0(value, ".") != 0) {
REMMINA_DEBUG("Deleting the secret in the keyring as disablepasswordstoring=1");
- secret_plugin->delete_password(remminafile, key);
+ secret_plugin->delete_password(secret_plugin, remminafile, key);
g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, ".");
}
}
@@ -831,7 +831,7 @@ void remmina_file_store_secret_plugin_password(RemminaFile *remminafile, const g
if (g_hash_table_lookup_extended(remminafile->spsettings, g_strdup(key), NULL, NULL)) {
plugin = remmina_plugin_manager_get_secret_plugin();
- plugin->store_password(remminafile, key, value);
+ plugin->store_password(plugin, remminafile, key, value);
} else {
remmina_file_set_string(remminafile, key, value);
remmina_file_save(remminafile);
diff --git a/src/remmina_file_manager.c b/src/remmina_file_manager.c
index 0c87d6a97..1edf3e391 100644
--- a/src/remmina_file_manager.c
+++ b/src/remmina_file_manager.c
@@ -364,7 +364,7 @@ RemminaFile *remmina_file_manager_load_file(const gchar *filename)
} else {
plugin = remmina_plugin_manager_get_import_file_handler(filename);
if (plugin)
- remminafile = plugin->import_func(filename);
+ remminafile = plugin->import_func(plugin, filename);
}
return remminafile;
}
diff --git a/src/remmina_main.c b/src/remmina_main.c
index 8b1fc2657..b856b7df5 100644
--- a/src/remmina_main.c
+++ b/src/remmina_main.c
@@ -286,7 +286,7 @@ static void remmina_main_show_snap_welcome()
g_print(" but we can’t find the secret plugin inside the SNAP.\n");
need_snap_interface_connections = TRUE;
} else {
- if (!remmina_secret_plugin->is_service_available()) {
+ if (!remmina_secret_plugin->is_service_available(remmina_secret_plugin)) {
g_print(" but we can’t access a secret service. Secret service or SNAP interface connection is missing.\n");
need_snap_interface_connections = TRUE;
}
@@ -987,7 +987,7 @@ void remmina_main_on_action_application_preferences(GSimpleAction *action, GVari
gtk_widget_show_all(widget);
/* Switch to a dark theme if the user enabled it */
settings = gtk_settings_get_default();
- g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
+ g_object_set(settings, "gtk-application-prefer-dark-theme", &remmina_pref.dark_theme, NULL);
}
void remmina_main_on_action_application_default(GSimpleAction *action, GVariant *param, gpointer data)
@@ -1050,7 +1050,7 @@ static void remmina_main_import_file_list(GSList *files)
for (element = files; element; element = element->next) {
path = (gchar *)element->data;
plugin = remmina_plugin_manager_get_import_file_handler(path);
- if (plugin && (remminafile = plugin->import_func(path)) != NULL && remmina_file_get_string(remminafile, "name")) {
+ if (plugin && (remminafile = plugin->import_func(plugin, path)) != NULL && remmina_file_get_string(remminafile, "name")) {
remmina_file_generate_filename(remminafile);
remmina_file_save(remminafile);
imported = TRUE;
@@ -1119,7 +1119,7 @@ void remmina_main_on_action_tools_export(GSimpleAction *action, GVariant *param,
dialog = gtk_file_chooser_dialog_new(plugin->export_hints, remminamain->window,
GTK_FILE_CHOOSER_ACTION_SAVE, _("_Save"), GTK_RESPONSE_ACCEPT, NULL);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
- plugin->export_func(remminafile, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
+ plugin->export_func(plugin, remminafile, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
gtk_widget_destroy(dialog);
} else {
dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
@@ -1415,7 +1415,7 @@ static gboolean remmina_main_add_tool_plugin(gchar *name, RemminaPlugin *plugin,
gtk_widget_show(menuitem);
gtk_menu_shell_append(GTK_MENU_SHELL(remminamain->menu_popup_full), menuitem);
- g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(tool_plugin->exec_func), NULL);
+ g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(tool_plugin->exec_func), tool_plugin);
return FALSE;
}
@@ -1436,7 +1436,7 @@ static void remmina_main_init(void)
REMMINA_DEBUG("Initializing the Remmina main window");
/* Switch to a dark theme if the user enabled it */
settings = gtk_settings_get_default();
- g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
+ g_object_set(settings, "gtk-application-prefer-dark-theme", &remmina_pref.dark_theme, NULL);
REMMINA_DEBUG ("Initializing monitor");
remminamain->monitor = remmina_network_monitor_new();
@@ -1607,14 +1607,23 @@ void remmina_main_update_file_datetime(RemminaFile *file)
remmina_main_load_files();
}
-void remmina_main_show_warning_dialog(const gchar *message)
-{
+void remmina_main_show_dialog(GtkMessageType msg, GtkButtonsType buttons, const gchar* message) {
GtkWidget *dialog;
if (remminamain->window) {
- dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
- message, g_get_application_name());
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, msg, buttons, message);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
}
+
+void remmina_main_show_warning_dialog(const gchar *message) {
+ GtkWidget *dialog;
+
+ if (remminamain->window) {
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
+ message, g_get_application_name());
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ }
+}
diff --git a/src/remmina_main.h b/src/remmina_main.h
index b49844fca..e381cfad3 100644
--- a/src/remmina_main.h
+++ b/src/remmina_main.h
@@ -109,6 +109,7 @@ void remmina_main_destroy(void);
void remmina_main_on_destroy_event(void);
void remmina_main_save_before_destroy(void);
+void remmina_main_show_dialog(GtkMessageType msg, GtkButtonsType buttons, const gchar* message);
void remmina_main_show_warning_dialog(const gchar *message);
void remmina_main_on_action_application_about(GSimpleAction *action, GVariant *param, gpointer data);
void remmina_main_on_action_application_news(GSimpleAction *action, GVariant *param, gpointer data);
diff --git a/src/remmina_mpchange.c b/src/remmina_mpchange.c
index e05904b9e..6f777b584 100644
--- a/src/remmina_mpchange.c
+++ b/src/remmina_mpchange.c
@@ -326,7 +326,7 @@ static gboolean remmina_file_multipasswd_changer_mt(gpointer d)
if (secret_plugin == NULL) {
initerror = _("The multi password changer requires a secrecy plugin.\n");
}else {
- if (!secret_plugin->is_service_available()) {
+ if (!secret_plugin->is_service_available(secret_plugin)) {
initerror = _("The multi password changer requires a secrecy service.\n");
}
}
diff --git a/src/remmina_plugin_manager.c b/src/remmina_plugin_manager.c
index ea4cc057e..f61f236d4 100644
--- a/src/remmina_plugin_manager.c
+++ b/src/remmina_plugin_manager.c
@@ -355,7 +355,7 @@ void remmina_plugin_manager_init()
sple = secret_plugins;
while(sple != NULL) {
sp = (RemminaSecretPlugin*)sple->data;
- if (sp->init()) {
+ if (sp->init(sp)) {
g_print("The %s secret plugin has been initialized and it will be your default secret plugin\n",
sp->name);
remmina_secret_plugin = sp;
@@ -516,7 +516,7 @@ RemminaFilePlugin* remmina_plugin_manager_get_import_file_handler(const gchar *f
if (plugin->type != REMMINA_PLUGIN_TYPE_FILE)
continue;
- if (plugin->import_test_func(file)) {
+ if (plugin->import_test_func(plugin, file)) {
return plugin;
}
}
@@ -533,7 +533,7 @@ RemminaFilePlugin* remmina_plugin_manager_get_export_file_handler(RemminaFile *r
plugin = (RemminaFilePlugin*)g_ptr_array_index(remmina_plugin_table, i);
if (plugin->type != REMMINA_PLUGIN_TYPE_FILE)
continue;
- if (plugin->export_test_func(remminafile)) {
+ if (plugin->export_test_func(plugin, remminafile)) {
return plugin;
}
}
diff --git a/src/remmina_plugin_native.c b/src/remmina_plugin_native.c
index f2d2ba989..9961d5641 100644
--- a/src/remmina_plugin_native.c
+++ b/src/remmina_plugin_native.c
@@ -53,29 +53,33 @@
#include "remmina_masterthread_exec.h"
#include "remmina/remmina_trace_calls.h"
-gboolean remmina_plugin_native_load(RemminaPluginService* service, const char* name) {
- TRACE_CALL(__func__);
- GModule *module;
+gboolean remmina_plugin_native_load(RemminaPluginService* service, const char* name)
+{
+ TRACE_CALL(__func__);
+ GModule* module;
RemminaPluginEntryFunc entry;
module = g_module_open(name, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
- if (!module) {
+ if (!module)
+ {
g_print("Failed to load plugin: %s.\n", name);
g_print("Error: %s\n", g_module_error());
return FALSE;
}
- if (!g_module_symbol(module, "remmina_plugin_entry", (gpointer*)&entry)) {
+ if (!g_module_symbol(module, "remmina_plugin_entry", (gpointer*)&entry))
+ {
g_print("Failed to locate plugin entry: %s.\n", name);
return FALSE;
}
- if (!entry(service)) {
+ if (!entry(service))
+ {
g_print("Plugin entry returned false: %s.\n", name);
return FALSE;
}
- return TRUE;
+ return TRUE;
/* We don’t close the module because we will need it throughout the process lifetime */
}
diff --git a/src/remmina_plugin_native.h b/src/remmina_plugin_native.h
index 7d9d8a123..21406654c 100644
--- a/src/remmina_plugin_native.h
+++ b/src/remmina_plugin_native.h
@@ -38,7 +38,7 @@
G_BEGIN_DECLS
-typedef gboolean (*RemminaPluginMain)(gchar *name);
+typedef gboolean (* RemminaPluginMain)(gchar* name);
gboolean remmina_plugin_native_load(RemminaPluginService* service, const char* name);
diff --git a/src/remmina_plugin_python.c b/src/remmina_plugin_python.c
index eecb33f60..be83a4635 100644
--- a/src/remmina_plugin_python.c
+++ b/src/remmina_plugin_python.c
@@ -17,134 +17,160 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
- * In addition, as a special exception, the copyright holders give
- * permission to link the code of portions of this program with the
- * OpenSSL library under certain conditions as described in each
- * individual source file, and distribute linked combinations
- * including the two.
- * You must obey the GNU General Public License in all respects
- * for all of the code used other than OpenSSL. * If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. * If you
- * do not wish to do so, delete this exception statement from your
- * version. * If you delete this exception statement from all source
- * files in the program, then also delete it here.
- *
- */
-
-/**
- * @file remmina_plugin_python.c
- * @brief Remmina Python plugin loader.
- * @author Mathias Winterhalter
- * @date 14.10.2020
- *
- * When Remmina discovers Python scripts in the plugin root folder the plugin manager
- * passes the path to the Python plugin loader. There it gets executed and the plugin
- * classes get mapped to "real" Remmina plugin instances.
- *
- * For the communication between Remmina and Python the python module called 'remmina'
- * is initialized and loaded into the environment of the plugin script
- * (@see remmina_plugin_python_module.c).
- *
- * @see http://www.remmina.org/wp for more information.
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
*/
-#include <gtk/gtk.h>
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "config.h"
#include "remmina/remmina_trace_calls.h"
+#include "remmina_plugin_python_common.h"
#include "remmina_plugin_python.h"
-#include "remmina_plugin_python_module.h"
+#include "remmina_plugin_python_remmina.h"
#include "remmina_plugin_python_protocol_widget.h"
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * An null terminated array of commands that are executed after the initialization of the Python engine. Every entry
+ * represents a line of Python code.
+ */
+static const char
+ * python_init_commands[] = { "import sys", "sys.path.append('" REMMINA_RUNTIME_PLUGINDIR "')", NULL // Sentinel
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// U T I L S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
/**
- * @brief Extracts the filename without extension from a path.
+ * @brief Extracts the filename without extension from a path.
*
* @param in The string to extract the filename from
* @param out The resulting filename without extension (must point to allocated memory).
*
* @return The length of the filename extracted.
*/
-static int basename_no_ext(const char* in, char** out);
+static int basename_no_ext(const char* in, char** out)
+{
+ TRACE_CALL(__func__);
-/**
- *
- */
-void remmina_plugin_python_init(void) {
- TRACE_CALL(__FUNC__);
+ assert(in);
+ assert(out);
- remmina_plugin_python_module_init();
+ const char* base = strrchr(in, '/');
+ if (base)
+ {
+ base++;
+ }
- Py_Initialize();
-
- // Tell the Python engine where to look for Python scripts.
- PyRun_SimpleString("import sys");
- PyRun_SimpleString("sys.path.append('" REMMINA_RUNTIME_PLUGINDIR "')");
-
- remmina_plugin_python_protocol_widget_init();
-}
+ const char* base_end = strrchr(base, '.');
+ if (!base_end)
+ {
+ base_end = base + strlen(base);
+ }
-gboolean remmina_plugin_python_load(RemminaPluginService* service, const gchar* name) {
- TRACE_CALL(__FUNC__);
+ const int length = base_end - base;
+ const int memsize = sizeof(char*) * ((length) + 1);
- gchar* filename = NULL;
- if (basename_no_ext(name, &filename) == 0) {
- g_printerr("[%s:%s]: %s can not extract filename from name!\n", __FILE__, __LINE__, name);
- return FALSE;
- }
+ *out = (char*)remmina_plugin_python_malloc(memsize);
- PyObject *plugin_name = PyUnicode_DecodeFSDefault(filename);
- free(filename);
+ memset(*out, 0, memsize);
+ strncpy(*out, base, length);
+ (*out)[length] = '\0';
- if (!plugin_name) {
- g_printerr("[%s:%s]: Error converting plugin file name to PyUnicode!\n", __FILE__, __LINE__);
- return FALSE;
- }
+ return length;
+}
- wchar_t* program_name = NULL;
- Py_ssize_t len = PyUnicode_AsWideChar(plugin_name, program_name, 0);
- program_name = malloc(sizeof(wchar_t)*len);
- if (!program_name) {
- g_printerr("[%s:%s]: Failed allocating %d bytes!\n", __FILE__, __LINE__, (sizeof(wchar_t)*len));
- return FALSE;
- }
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- PyUnicode_AsWideChar(plugin_name, program_name, len);
+void remmina_plugin_python_init(void)
+{
+ TRACE_CALL(__func__);
- // This line works around the issue that in Python the sys.argv array is empty. This causes several problems since
- // many rely on the fact that the program name is set in sys.argv[0].
- PySys_SetArgv(1, &program_name);
+ remmina_plugin_python_module_init();
+ Py_Initialize();
- if (PyImport_Import(plugin_name)) {
- return TRUE;
- }
+ for (const char** ptr = python_init_commands; *ptr; ++ptr)
+ {
+ PyRun_SimpleString(*ptr);
+ }
- g_print("Failed to load python plugin file: “%s”\n", name);
- PyErr_Print();
- return FALSE;
+ remmina_plugin_python_protocol_widget_init();
}
-
-static int basename_no_ext(const char* in, char** out) {
- const char* base = strrchr(in, '/');
- if (base) {
- base++;
- }
-
- const char* base_end = strrchr(base, '.');
- if (!base_end) {
- base_end = base + strlen(base);
- }
-
- int length = base_end - base;
- int memsize = sizeof(char*) * ((length) + 1);
- *out = (char*)malloc(memsize);
- memset(*out, 0, memsize);
- strncpy(*out, base, length);
- (*out)[length] = '\0';
-
- return length;
+gboolean remmina_plugin_python_load(RemminaPluginService* service, const char* name)
+{
+ TRACE_CALL(__func__);
+
+ assert(service);
+ assert(name);
+
+ remmina_plugin_python_set_service(service);
+
+ char* filename = NULL;
+ if (basename_no_ext(name, &filename) == 0)
+ {
+ g_printerr("[%s:%d]: Can not extract filename from '%s'!\n", __FILE__, __LINE__, name);
+ return FALSE;
+ }
+
+ PyObject* plugin_name = PyUnicode_DecodeFSDefault(filename);
+
+ if (!plugin_name)
+ {
+ free(filename);
+ g_printerr("[%s:%d]: Error converting plugin filename to PyUnicode!\n", __FILE__, __LINE__);
+ return FALSE;
+ }
+
+ wchar_t* program_name = NULL;
+ Py_ssize_t len = PyUnicode_AsWideChar(plugin_name, program_name, 0);
+ if (len <= 0)
+ {
+ free(filename);
+ g_printerr("[%s:%d]: Failed allocating %lu bytes!\n", __FILE__, __LINE__, (sizeof(wchar_t) * len));
+ return FALSE;
+ }
+
+ program_name = (wchar_t*)remmina_plugin_python_malloc(sizeof(wchar_t) * len);
+ if (!program_name)
+ {
+ free(filename);
+ g_printerr("[%s:%d]: Failed allocating %lu bytes!\n", __FILE__, __LINE__, (sizeof(wchar_t) * len));
+ return FALSE;
+ }
+
+ PyUnicode_AsWideChar(plugin_name, program_name, len);
+
+ PySys_SetArgv(1, &program_name);
+
+ if (PyImport_Import(plugin_name))
+ {
+ free(filename);
+ return TRUE;
+ }
+
+ g_print("[%s:%d]: Failed to load python plugin file: '%s'\n", __FILE__, __LINE__, name);
+ PyErr_Print();
+ free(filename);
+
+ return FALSE;
}
diff --git a/src/remmina_plugin_python.h b/src/remmina_plugin_python.h
index 233a224ef..97f53768d 100644
--- a/src/remmina_plugin_python.h
+++ b/src/remmina_plugin_python.h
@@ -29,23 +29,46 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python.h
+ *
+ * @brief Declares the interface between the Python plugin implementation and Remmina covering the initialization of
+ * the implementation and the load function, that allows Remmina to load plugins into the application.
*
+ * @details When Remmina discovers Python scripts in the plugin root folder the plugin manager passes the path to the
+ * Python plugin loader. There it gets executed and the plugin classes get mapped to "real" Remmina plugin
+ * instances.
+ *
+ * For the communication between Remmina and Python the python module called 'remmina' is initialized and
+ * loaded into the environment of the plugin script (@see remmina_plugin_python_module.c).
+ *
+ * @see http://www.remmina.org/wp for more information.
*/
#pragma once
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
#include "remmina/plugin.h"
G_BEGIN_DECLS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
/**
- * @brief Initializes the Python plugin loaders.
- * @details This does not load any plugins but initializes globals and the Python engine itself.
+ * @brief Initializes the Python plugin loaders.
+ * @details This does not load any plugins but initializes the implementation (e.g. globals and the Python engine).
*/
void remmina_plugin_python_init(void);
/**
- * @brief Loads a plugin from the Remmina plugin folder with the given name.
+ * @brief Loads a plugin from the Remmina plugin folder with the given name.
*
* @param service The instance of the service providing an API between Remmina and its plugins.
* @param filename The filename of the plugin to load.
diff --git a/src/remmina_plugin_python_common.c b/src/remmina_plugin_python_common.c
new file mode 100644
index 000000000..527d96d6a
--- /dev/null
+++ b/src/remmina_plugin_python_common.c
@@ -0,0 +1,312 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina_plugin_python_common.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#include "pygobject.h"
+#pragma GCC diagnostic pop
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A cache to store the last result that has been returned by the Python code using CallPythonMethod
+ * (@see remmina_plugin_python_common.h)
+ */
+static PyObject* __last_result;
+static GPtrArray* plugin_map = NULL;
+
+static RemminaPluginService* remmina_plugin_service;
+
+const char* ATTR_NAME = "name";
+const char* ATTR_ICON_NAME = "icon_name";
+const char* ATTR_DESCRIPTION = "description";
+const char* ATTR_VERSION = "version";
+const char* ATTR_ICON_NAME_SSH = "icon_name_ssh";
+const char* ATTR_FEATURES = "features";
+const char* ATTR_BASIC_SETTINGS = "basic_settings";
+const char* ATTR_ADVANCED_SETTINGS = "advanced_settings";
+const char* ATTR_SSH_SETTING = "ssh_setting";
+const char* ATTR_EXPORT_HINTS = "export_hints";
+const char* ATTR_PREF_LABEL = "pref_label";
+const char* ATTR_INIT_ORDER = "init_order";
+
+/**
+ * To prevent some memory related attacks or accidental allocation of an excessive amount of byes, this limit should
+ * always be used to check for a sane amount of bytes to allocate.
+ */
+static const int REASONABLE_LIMIT_FOR_MALLOC = 1024 * 1024;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+PyObject* remmina_plugin_python_last_result(void)
+{
+ TRACE_CALL(__func__);
+
+ return __last_result;
+}
+
+PyObject* remmina_plugin_python_last_result_set(PyObject* last_result)
+{
+ TRACE_CALL(__func__);
+
+ return __last_result = last_result;
+}
+
+gboolean remmina_plugin_python_check_error(void)
+{
+ TRACE_CALL(__func__);
+
+ if (PyErr_Occurred())
+ {
+ PyErr_Print();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void remmina_plugin_python_log_method_call(PyObject* instance, const char* method)
+{
+ TRACE_CALL(__func__);
+
+ assert(instance);
+ assert(method);
+ g_print("Python@%ld: %s.%s(...) -> %s\n",
+ PyObject_Hash(instance),
+ instance->ob_type->tp_name,
+ method,
+ PyUnicode_AsUTF8(PyObject_Str(remmina_plugin_python_last_result())));
+}
+
+long remmina_plugin_python_get_attribute_long(PyObject* instance, const char* attr_name, long def)
+{
+ TRACE_CALL(__func__);
+
+ assert(instance);
+ assert(attr_name);
+ PyObject* attr = PyObject_GetAttrString(instance, attr_name);
+ if (attr && PyLong_Check(attr))
+ {
+ return PyLong_AsLong(attr);
+ }
+
+ return def;
+}
+
+gboolean remmina_plugin_python_check_attribute(PyObject* instance, const char* attr_name)
+{
+ TRACE_CALL(__func__);
+
+ assert(instance);
+ assert(attr_name);
+ if (PyObject_HasAttrString(instance, attr_name))
+ {
+ return TRUE;
+ }
+
+ g_printerr("Python plugin instance is missing member: %s\n", attr_name);
+ return FALSE;
+}
+
+void* remmina_plugin_python_malloc(int bytes)
+{
+ TRACE_CALL(__func__);
+
+ assert(bytes > 0);
+ assert(bytes <= REASONABLE_LIMIT_FOR_MALLOC);
+
+ void* result = malloc(bytes);
+
+ if (!result)
+ {
+ g_printerr("Unable to allocate %d bytes in memory!\n", bytes);
+ perror("malloc");
+ }
+
+ return result;
+}
+
+char* remmina_plugin_python_copy_string_from_python(PyObject* string, Py_ssize_t len)
+{
+ TRACE_CALL(__func__);
+
+ char* result = NULL;
+ if (len <= 0 || string == NULL)
+ {
+ return NULL;
+ }
+
+ const char* py_str = PyUnicode_AsUTF8(string);
+ if (py_str)
+ {
+ const int label_size = sizeof(char) * (len + 1);
+ result = (char*)remmina_plugin_python_malloc(label_size);
+ result[len] = '\0';
+ memcpy(result, py_str, len);
+ }
+
+ return result;
+}
+
+void remmina_plugin_python_set_service(RemminaPluginService* service)
+{
+ remmina_plugin_service = service;
+}
+
+RemminaPluginService* remmina_plugin_python_get_service(void)
+{
+ return remmina_plugin_service;
+}
+
+void remmina_plugin_python_add_plugin(PyPlugin* plugin)
+{
+ TRACE_CALL(__func__);
+
+ if (!plugin_map)
+ {
+ plugin_map = g_ptr_array_new();
+ }
+
+ PyPlugin* test = remmina_plugin_python_get_plugin(plugin->generic_plugin->name);
+ if (test)
+ {
+ g_printerr("A plugin named '%s' has already been registered! Skipping...", plugin->generic_plugin->name);
+ }
+ else
+ {
+ g_ptr_array_add(plugin_map, plugin);
+ }
+}
+
+RemminaTypeHint remmina_plugin_python_to_generic(PyObject* field, gpointer* target)
+{
+ TRACE_CALL(__func__);
+
+ if (PyUnicode_Check(field))
+ {
+ Py_ssize_t len = PyUnicode_GetLength(field);
+
+ if (len > 0)
+ {
+ *target = remmina_plugin_python_copy_string_from_python(field, len);
+ }
+ else
+ {
+ *target = "";
+ }
+
+ return REMMINA_TYPEHINT_STRING;
+ }
+ else if (PyBool_Check(field))
+ {
+ *target = remmina_plugin_python_malloc(sizeof(long));
+ long* long_target = (long*)target;
+ *long_target = PyLong_AsLong(field);
+ return REMMINA_TYPEHINT_BOOLEAN;
+ }
+ else if (PyLong_Check(field))
+ {
+ *target = remmina_plugin_python_malloc(sizeof(long));
+ long* long_target = (long*)target;
+ *long_target = PyLong_AsLong(field);
+ return REMMINA_TYPEHINT_SIGNED;
+ }
+ else if (PyTuple_Check(field))
+ {
+ Py_ssize_t len = PyTuple_Size(field);
+ if (len)
+ {
+ gpointer* dest = (gpointer*)remmina_plugin_python_malloc(sizeof(gpointer) * (len + 1));
+ memset(dest, 0, sizeof(gpointer) * (len + 1));
+
+ for (Py_ssize_t i = 0; i < len; ++i)
+ {
+ PyObject* item = PyTuple_GetItem(field, i);
+ remmina_plugin_python_to_generic(item, dest + i);
+ }
+
+ *target = dest;
+ }
+ return REMMINA_TYPEHINT_TUPLE;
+ }
+
+ *target = NULL;
+ return REMMINA_TYPEHINT_UNDEFINED;
+}
+
+PyPlugin* remmina_plugin_python_get_plugin(const char* name)
+{
+ TRACE_CALL(__func__);
+
+ assert(plugin_map);
+ assert(name);
+
+ for (gint i = 0; i < plugin_map->len; ++i)
+ {
+ PyPlugin* plugin = (PyPlugin*)g_ptr_array_index(plugin_map, i);
+ if (plugin->generic_plugin && plugin->generic_plugin->name && g_str_equal(name, plugin->generic_plugin->name))
+ {
+ return plugin;
+ }
+ }
+
+ return NULL;
+}
+
+void init_pygobject()
+{
+ pygobject_init(-1, -1, -1);
+}
+
+GtkWidget* new_pywidget(GObject* obj)
+{
+ return (GtkWidget*)pygobject_new(obj);
+}
+
+GtkWidget* get_pywidget(PyObject* obj)
+{
+ return (GtkWidget*)pygobject_get(obj);
+}
diff --git a/src/remmina_plugin_python_common.h b/src/remmina_plugin_python_common.h
new file mode 100644
index 000000000..21557bb65
--- /dev/null
+++ b/src/remmina_plugin_python_common.h
@@ -0,0 +1,279 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_common.h
+ *
+ * @brief Contains functions and constants that are commonly used throughout the Python plugin implementation.
+ *
+ * @details These functions should not be used outside of the Python plugin implementation, since everything is intended
+ * to be used with the Python engine.
+ */
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <Python.h>
+#include <glib.h>
+#include <Python.h>
+#include <structmember.h>
+
+#include "remmina/plugin.h"
+#include "config.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+// - Attribute names
+
+extern const char* ATTR_NAME;
+extern const char* ATTR_ICON_NAME;
+extern const char* ATTR_DESCRIPTION;
+extern const char* ATTR_VERSION;
+extern const char* ATTR_ICON_NAME_SSH;
+extern const char* ATTR_FEATURES;
+extern const char* ATTR_BASIC_SETTINGS;
+extern const char* ATTR_ADVANCED_SETTINGS;
+extern const char* ATTR_SSH_SETTING;
+extern const char* ATTR_EXPORT_HINTS;
+extern const char* ATTR_PREF_LABEL;
+extern const char* ATTR_INIT_ORDER;
+
+// You can enable this for debuggin purposes or specify it in the build.
+// #define WITH_PYTHON_TRACE_CALLS
+
+/**
+ * If WITH_PYTHON_TRACE_CALLS is defined, it logs the calls to the Python code and errors in case.
+ */
+#ifdef WITH_PYTHON_TRACE_CALLS
+#define CallPythonMethod(instance, name, params, ...) \
+ remmina_plugin_python_last_result_set(PyObject_CallMethod(instance, name, params, ##__VA_ARGS__)); \
+ remmina_plugin_python_log_method_call(instance, name); \
+ remmina_plugin_python_check_error()
+#else
+/**
+ * If WITH_TRACE_CALL is not defined, it still logs errors but doesn't print the call anymore.
+ */
+#define CallPythonMethod(instance, name, params, ...) \
+ PyObject_CallMethod(instance, name, params, ##__VA_ARGS__); \
+ remmina_plugin_python_check_error()
+#endif // WITH_PYTHON_TRACE_CALLS
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// T Y P E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief The Python abstraction of the protocol widget struct.
+ *
+ * @details This struct is responsible to provide the same accessibility to the protocol widget for Python as for
+ * native plugins.
+ */
+typedef struct
+{
+ PyObject_HEAD
+ RemminaProtocolWidget* gp;
+} PyRemminaProtocolWidget;
+
+/**
+ * @brief Maps an instance of a Python plugin to a Remmina one.
+ *
+ * @details This is used to map a Python plugin instance to the Remmina plugin one. Also instance specific data as the
+ * protocol widget are stored in this struct.
+ */
+typedef struct
+{
+ RemminaProtocolPlugin* protocol_plugin;
+ RemminaFilePlugin* file_plugin;
+ RemminaSecretPlugin* secret_plugin;
+ RemminaToolPlugin* tool_plugin;
+ RemminaEntryPlugin* entry_plugin;
+ RemminaPrefPlugin* pref_plugin;
+ RemminaPlugin* generic_plugin;
+ PyRemminaProtocolWidget* gp;
+ PyObject* instance;
+} PyPlugin;
+
+/**
+ * A struct used to communicate data between Python and C without strict data type.
+ */
+typedef struct
+{
+ PyObject_HEAD;
+ RemminaTypeHint type_hint;
+ gpointer raw;
+} PyGeneric;
+
+/**
+ * Checks if self is set and returns NULL if not.
+ */
+#define SELF_CHECK() if (!self) { \
+ g_printerr("[%s:%d]: self is null!\n", __FILE__, __LINE__); \
+ PyErr_SetString(PyExc_RuntimeError, "Method is not called from an instance (self is null)!"); \
+ return NULL; \
+ }
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Creates a new instance of PyGeneric.
+ */
+PyGeneric* remmina_plugin_python_generic_new(void);
+
+/**
+ * Registers the given plugin if no other plugin with the same name has been already registered.
+ */
+void remmina_plugin_python_add_plugin(PyPlugin* plugin);
+
+/**
+ * Sets the pointer to the plugin service of Remmina.
+ */
+void remmina_plugin_python_set_service(RemminaPluginService* service);
+
+/**
+ * Extracts data from a PyObject instance to a generic pointer and returns a type hint if it could be determined.
+ */
+RemminaTypeHint remmina_plugin_python_to_generic(PyObject* field, gpointer* target);
+
+/**
+ * Gets the result of the last python method call.
+ */
+PyObject* remmina_plugin_python_last_result(void);
+
+/**
+ * @brief Sets the result of the last python method call.
+ *
+ * @return Returns the passed result (it's done to be compatible with the CallPythonMethod macro).
+ */
+PyObject* remmina_plugin_python_last_result_set(PyObject* result);
+
+/**
+ * @brief Prints a log message to inform the user a python message has been called.
+ *
+ * @detail This method is called from the CALL_PYTHON macro if WITH_PYTHON_TRACE_CALLS is defined.
+ *
+ * @param instance The instance that contains the called method.
+ * @param method The name of the method called.
+ */
+void remmina_plugin_python_log_method_call(PyObject* instance, const char* method);
+
+/**
+ * @brief Checks if an error has occurred and prints it.
+ *
+ * @return Returns TRUE if an error has occurred.
+ */
+gboolean remmina_plugin_python_check_error(void);
+
+/**
+ * @brief Gets the attribute as long value.
+ *
+ * @param instance The instance of the object to get the attribute.
+ * @param constant_name The name of the attribute to get.
+ * @param def The value to return if the attribute doesn't exist or is not set.
+ *
+ * @return The value attribute as long.
+ */
+long remmina_plugin_python_get_attribute_long(PyObject* instance, const char* attr_name, long def);
+
+/**
+ * @brief Checks if a given attribute exists.
+ *
+ * @param instance The object to check for the attribute.
+ * @param attr_name The name of the attribute to check.
+ *
+ * @return Returns TRUE if the attribute exists.
+ */
+gboolean remmina_plugin_python_check_attribute(PyObject* instance, const char* attr_name);
+
+/**
+ * @brief Allocates memory and checks for errors before returning.
+ *
+ * @param bytes Amount of bytes to allocate.
+ *
+ * @return Address to the allocated memory.
+ */
+void* remmina_plugin_python_malloc(int bytes);
+
+/**
+ * @biref Copies a string from a Python object to a new point in memory.
+ *
+ * @param string The python object, containing the string to copy.
+ * @param len The length of the string to copy.
+ *
+ * @return A char pointer to the new copy of the string.
+ */
+char* remmina_plugin_python_copy_string_from_python(PyObject* string, Py_ssize_t len);
+
+/**
+ * @brief Tries to find the Python plugin matching to the given instance of RemminaPlugin.
+ *
+ * @param plugin_map An array of PyPlugin pointers to search.
+ * @param instance The RemminaPlugin instance to find the correct PyPlugin instance for.
+ *
+ * @return A pointer to a PyPlugin instance if successful. Otherwise NULL is returned.
+ */
+PyPlugin* remmina_plugin_python_get_plugin(const char* name);
+
+/**
+ * Creates a new GtkWidget
+ * @param obj
+ * @return
+ */
+GtkWidget* new_pywidget(GObject* obj);
+
+/**
+ * Extracts a GtkWidget from a PyObject instance.
+ * @param obj
+ * @return
+ */
+GtkWidget* get_pywidget(PyObject* obj);
+
+/**
+ * Initializes the pygobject library. This needs to be called before any Python plugin is being initialized.
+ */
+void init_pygobject(void);
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_entry.c b/src/remmina_plugin_python_entry.c
new file mode 100644
index 000000000..54a478b18
--- /dev/null
+++ b/src/remmina_plugin_python_entry.c
@@ -0,0 +1,101 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_entry.c
+ * @brief Contains the wiring of a Python pluing based on RemminaPluginProtocol.
+ * @author Mathias Winterhalter
+ * @date 07.04.2021
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina_plugin_python_common.h"
+#include "remmina_plugin_python_entry.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void remmina_plugin_python_entry_init(void)
+{
+ TRACE_CALL(__func__);
+}
+
+void remmina_plugin_python_entry_entry_func_wrapper(RemminaEntryPlugin* instance)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ if (plugin)
+ {
+ CallPythonMethod(plugin->instance, "entry_func", NULL);
+ }
+}
+
+RemminaPlugin* remmina_plugin_python_create_entry_plugin(PyPlugin* plugin)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* instance = plugin->instance;
+
+ if (!remmina_plugin_python_check_attribute(instance, ATTR_NAME)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_VERSION)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_DESCRIPTION))
+ {
+ g_printerr("Unable to create entry plugin. Aborting!\n");
+ return NULL;
+ }
+
+ RemminaEntryPlugin* remmina_plugin = (RemminaEntryPlugin*)remmina_plugin_python_malloc(sizeof(RemminaEntryPlugin));
+
+ remmina_plugin->type = REMMINA_PLUGIN_TYPE_ENTRY;
+ remmina_plugin->domain = GETTEXT_PACKAGE;
+ remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_NAME));
+ remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_VERSION));
+ remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_DESCRIPTION));
+ remmina_plugin->entry_func = remmina_plugin_python_entry_entry_func_wrapper;
+
+ plugin->entry_plugin = remmina_plugin;
+ plugin->generic_plugin = (RemminaPlugin*)remmina_plugin;
+
+ remmina_plugin_python_add_plugin(plugin);
+
+ return (RemminaPlugin*)remmina_plugin;
+}
diff --git a/src/remmina_plugin_python_entry.h b/src/remmina_plugin_python_entry.h
new file mode 100644
index 000000000..3b4c3ee38
--- /dev/null
+++ b/src/remmina_plugin_python_entry.h
@@ -0,0 +1,61 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_entry.h
+ *
+ * @brief Contains the specialisation of RemminaPluginEntry plugins in Python.
+ */
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+/**
+ * Initializes the Python plugin specialisation for entry plugins.
+ */
+void remmina_plugin_python_entry_init(void);
+
+/**
+ * @brief Creates a new instance of the RemminaPluginEntry, initializes its members and references the wrapper
+ * functions.
+ * @param instance The instance of the Python plugin.
+ * @return Returns a new instance of the RemminaPlugin (must be freed!).
+ */
+RemminaPlugin* remmina_plugin_python_create_entry_plugin(PyPlugin* instance);
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_file.c b/src/remmina_plugin_python_file.c
new file mode 100644
index 000000000..71a4dcd75
--- /dev/null
+++ b/src/remmina_plugin_python_file.c
@@ -0,0 +1,161 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina_plugin_python_common.h"
+#include "remmina_plugin_python_file.h"
+#include "remmina_plugin_python_remmina_file.h"
+#include "remmina_file.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void remmina_plugin_python_file_init(void)
+{
+ TRACE_CALL(__func__);
+}
+
+gboolean remmina_plugin_python_file_import_test_func_wrapper(RemminaFilePlugin* instance, const gchar* from_file)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* result = NULL;
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+
+ if (plugin)
+ {
+ result = CallPythonMethod(plugin->instance, "import_test_func", "s", from_file);
+ }
+
+ return result == Py_None || result != Py_False;
+}
+
+RemminaFile* remmina_plugin_python_file_import_func_wrapper(RemminaFilePlugin* instance, const gchar* from_file)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* result = NULL;
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ if (!plugin)
+ {
+ return NULL;
+ }
+
+ result = CallPythonMethod(plugin->instance, "import_func", "s", from_file);
+
+ if (result == Py_None || result == Py_False)
+ {
+ return NULL;
+ }
+
+ return ((PyRemminaFile*)result)->file;
+}
+
+gboolean remmina_plugin_python_file_export_test_func_wrapper(RemminaFilePlugin* instance, RemminaFile* file)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* result = NULL;
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ if (plugin)
+ {
+ result = CallPythonMethod(plugin->instance,
+ "export_test_func",
+ "O",
+ remmina_plugin_python_remmina_file_to_python(file));
+ }
+
+ return result == Py_None || result != Py_False;
+}
+
+gboolean
+remmina_plugin_python_file_export_func_wrapper(RemminaFilePlugin* instance, RemminaFile* file, const gchar* to_file)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* result = NULL;
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ if (plugin)
+ {
+ result = CallPythonMethod(plugin->instance, "export_func", "s", to_file);
+ }
+
+ return result == Py_None || result != Py_False;
+}
+
+RemminaPlugin* remmina_plugin_python_create_file_plugin(PyPlugin* plugin)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* instance = plugin->instance;
+ Py_IncRef(instance);
+
+ if (!remmina_plugin_python_check_attribute(instance, ATTR_NAME))
+ {
+ g_printerr("Unable to create file plugin. Aborting!\n");
+ return NULL;
+ }
+
+ RemminaFilePlugin* remmina_plugin = (RemminaFilePlugin*)remmina_plugin_python_malloc(sizeof(RemminaFilePlugin));
+
+ remmina_plugin->type = REMMINA_PLUGIN_TYPE_FILE;
+ remmina_plugin->domain = GETTEXT_PACKAGE;
+ remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_NAME));
+ remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_VERSION));
+ remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_DESCRIPTION));
+ remmina_plugin->export_hints = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_EXPORT_HINTS));
+
+ remmina_plugin->import_test_func = remmina_plugin_python_file_import_test_func_wrapper;
+ remmina_plugin->import_func = remmina_plugin_python_file_import_func_wrapper;
+ remmina_plugin->export_test_func = remmina_plugin_python_file_export_test_func_wrapper;
+ remmina_plugin->export_func = remmina_plugin_python_file_export_func_wrapper;
+
+ plugin->file_plugin = remmina_plugin;
+ plugin->generic_plugin = (RemminaPlugin*)remmina_plugin;
+
+ remmina_plugin_python_add_plugin(plugin);
+
+ return (RemminaPlugin*)remmina_plugin;
+}
diff --git a/src/remmina_plugin_python_module.h b/src/remmina_plugin_python_file.h
index ced1a83a0..b45326f7f 100644
--- a/src/remmina_plugin_python_module.h
+++ b/src/remmina_plugin_python_file.h
@@ -1,6 +1,6 @@
/*
* Remmina - The GTK+ Remote Desktop Client
- * Copyright (C) 2014-2022 Antenore Gatta, Giovanni Panozzo
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
*
* 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
@@ -29,14 +29,33 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_file.h
*
+ * @brief Contains the specialisation of RemminaPluginFile plugins in Python.
*/
#pragma once
-RemminaPlugin* remmina_plugin_python_create_protocol_plugin(PyObject* pluginInstance);
-RemminaPlugin* remmina_plugin_python_create_entry_plugin(PyObject* pluginInstance);
-RemminaPlugin* remmina_plugin_python_create_file_plugin(PyObject* pluginInstance);
-RemminaPlugin* remmina_plugin_python_create_tool_plugin(PyObject* pluginInstance);
-RemminaPlugin* remmina_plugin_python_create_pref_plugin(PyObject* pluginInstance);
-RemminaPlugin* remmina_plugin_python_create_secret_plugin(PyObject* pluginInstance);
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+/**
+ * Initializes the Python plugin specialisation for file plugins.
+ */
+void remmina_plugin_python_file_init(void);
+
+/**
+ * @brief Creates a new instance of the RemminaPluginFile, initializes its members and references the wrapper
+ * functions.
+ * @param instance The instance of the Python plugin.
+ * @return Returns a new instance of the RemminaPlugin (must be freed!).
+ */
+RemminaPlugin* remmina_plugin_python_create_file_plugin(PyPlugin* instance);
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_module.c b/src/remmina_plugin_python_module.c
deleted file mode 100644
index d2daf7d10..000000000
--- a/src/remmina_plugin_python_module.c
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Remmina - The GTK+ Remote Desktop Client
- * Copyright (C) 2014-2022 Antenore Gatta, Giovanni Panozzo
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) 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.
- *
- * In addition, as a special exception, the copyright holders give
- * permission to link the code of portions of this program with the
- * OpenSSL library under certain conditions as described in each
- * individual source file, and distribute linked combinations
- * including the two.
- * You must obey the GNU General Public License in all respects
- * for all of the code used other than OpenSSL. * If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. * If you
- * do not wish to do so, delete this exception statement from your
- * version. * If you delete this exception statement from all source
- * files in the program, then also delete it here.
- *
- */
-
-/**
- * @file remmina_plugin_python_module.c
- * @brief Implementation of the Python module 'remmina'.
- * @author Mathias Winterhalter
- * @date 14.10.2020
- *
- * This file acts as a broker between Remmina and the Python plugins. It abstracts the communication flow
- * over the RemminaPluginService and redirects calls to the correct Python plugin. The PyRemminaProtocolWidget
- * takes care of providing the API inside the Python script.
- *
- *
- * @see http://www.remmina.org/wp for more information.
- */
-
-#include <glib.h>
-#include <gtk/gtk.h>
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
-
-#include "config.h"
-#include "remmina_plugin_manager.h"
-#include "remmina_plugin_python_remmina.h"
-#include "remmina/plugin.h"
-#include "remmina_protocol_widget.h"
-
-/**
- * @brief Handles the initialization of the Python plugin.
- * @details This function prepares the plugin structure and calls the init method of the
- * plugin Python class.
- *
- * @param gp The protocol widget used by the plugin.
- */
-static void remmina_protocol_init_wrapper(RemminaProtocolWidget *gp)
-{
- TRACE_CALL(__func__);
- PyPlugin* py_plugin = remmina_plugin_python_module_get_plugin(gp);
- py_plugin->gp->gp = gp;
- PyObject_CallMethod(py_plugin, "init", "O", py_plugin->gp);
-}
-
-/**
- * @brief
- * @details
- *
- * @param gp The protocol widget used by the plugin.
- */
-static gboolean remmina_protocol_open_connection_wrapper(RemminaProtocolWidget *gp)
-{
- TRACE_CALL(__func__);
- remmina_plugin_manager_service.protocol_plugin_signal_connection_opened(gp);
- PyPlugin* py_plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(py_plugin, "open_connection", "O", py_plugin->gp);
- return result == Py_True;
-}
-
-/**
- * @brief
- * @details
- *
- * @param gp The protocol widget used by the plugin.
- */
-static gboolean remmina_protocol_close_connection_wrapper(RemminaProtocolWidget *gp)
-{
- TRACE_CALL(__func__);
- PyPlugin* py_plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(py_plugin, "close_connection", "O", py_plugin->gp);
- return result == Py_True;
-}
-
-/**
- * @brief
- * @details
- *
- * @param gp The protocol widget used by the plugin.
- */
-static gboolean remmina_protocol_query_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
-{
- TRACE_CALL(__func__);
- PyPlugin* py_plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(py_plugin, "query_feature", "O", py_plugin->gp);
- return result == Py_True;
-}
-
-/**
- * @brief
- * @details
- *
- * @param gp The protocol widget used by the plugin.
- */
-static void remmina_protocol_call_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
-{
- TRACE_CALL(__func__);
- PyPlugin* py_plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(py_plugin, "call_feature", "O", py_plugin->gp);
-}
-
-/**
- * @brief
- * @details
- *
- * @param gp The protocol widget used by the plugin.
- */
-static void remmina_protocol_send_keytrokes_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
-{
- TRACE_CALL(__func__);
- PyPlugin* py_plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(py_plugin, "send_keystrokes", "O", py_plugin->gp);
-}
-
-/**
- * @brief
- * @details
- *
- * @param gp The protocol widget used by the plugin.
- */
-static gboolean remmina_protocol_get_plugin_screenshot_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
-{
- TRACE_CALL(__func__);
- PyPlugin* py_plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(py_plugin, "get_plugin_screenshot", "O", py_plugin->gp);
- return result == Py_True;
-}
-
-
-static long GetEnumOrDefault(PyObject* instance, gchar* constant_name, long def) {
- PyObject* attr = PyObject_GetAttrString(instance, constant_name);
- if (attr && PyLong_Check(attr)) {
- return PyLong_AsLong(attr);
- } else {
- return def;
- }
-}
-
-RemminaPlugin* remmina_plugin_python_create_protocol_plugin(PyObject* pluginInstance)
-{
- RemminaProtocolPlugin* remmina_plugin = (RemminaProtocolPlugin*)malloc(sizeof(RemminaProtocolPlugin));
-
- if(!PyObject_HasAttrString(pluginInstance, "icon_name_ssh")) {
- g_printerr("Error creating Remmina plugin. Python plugin instance is missing member: icon_name_ssh\n");
- return NULL;
- }
- else if(!PyObject_HasAttrString(pluginInstance, "icon_name")) {
- g_printerr("Error creating Remmina plugin. Python plugin instance is missing member: icon_name\n");
- return NULL;
- }
- else if(!PyObject_HasAttrString(pluginInstance, "features")) {
- g_printerr("Error creating Remmina plugin. Python plugin instance is missing member: features\n");
- return NULL;
- }
- else if(!PyObject_HasAttrString(pluginInstance, "basic_settings")) {
- g_printerr("Error creating Remmina plugin. Python plugin instance is missing member: basic_settings\n");
- return NULL;
- }
- else if(!PyObject_HasAttrString(pluginInstance, "advanced_settings")) {
- g_printerr("Error creating Remmina plugin. Python plugin instance is missing member: advanced_settings\n");
- return NULL;
- }
- else if(!PyObject_HasAttrString(pluginInstance, "ssh_setting")) {
- g_printerr("Error creating Remmina plugin. Python plugin instance is missing member: ssh_setting\n");
- return NULL;
- }
-
- remmina_plugin->type = REMMINA_PLUGIN_TYPE_PROTOCOL;
- remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "name")); // Name
- remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "description")); // Description
- remmina_plugin->domain = GETTEXT_PACKAGE; // Translation domain
- remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "version")); // Version number
- remmina_plugin->icon_name = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "icon_name")); // Icon for normal connection
- remmina_plugin->icon_name_ssh = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "icon_name_ssh")); // Icon for SSH connection
-
- PyObject* basic_settings_list = PyObject_GetAttrString(pluginInstance, "basic_settings");
- Py_ssize_t len = PyList_Size(basic_settings_list);
- if (len) {
- remmina_plugin->basic_settings = (RemminaProtocolSetting*)malloc(sizeof(RemminaProtocolSetting) * len);
- memset(&remmina_plugin->basic_settings[len], 0, sizeof(RemminaProtocolSetting));
-
- for (Py_ssize_t i = 0; i < len; ++i) {
- RemminaProtocolSetting* dest = remmina_plugin->basic_settings + i;
- ToRemminaProtocolSetting(dest, PyList_GetItem(basic_settings_list, i));
- }
- } else {
- remmina_plugin->basic_settings = NULL;
- }
-
- PyObject* advanced_settings_list = PyObject_GetAttrString(pluginInstance, "advanced_settings");
- len = PyList_Size(advanced_settings_list);
- if (len) {
- remmina_plugin->advanced_settings = (RemminaProtocolSetting*)malloc(sizeof(RemminaProtocolSetting) * (len+1));
- memset(&remmina_plugin->advanced_settings[len], 0, sizeof(RemminaProtocolSetting));
-
- for (Py_ssize_t i = 0; i < len; ++i) {
- RemminaProtocolSetting* dest = remmina_plugin->advanced_settings + i;
- ToRemminaProtocolSetting(dest, PyList_GetItem(advanced_settings_list, i));
- }
- } else {
- remmina_plugin->advanced_settings = NULL;
- }
-
-
- PyObject* features_list = PyObject_GetAttrString(pluginInstance, "features");
- len = PyList_Size(features_list);
- if (len) {
- remmina_plugin->features = (RemminaProtocolFeature*)malloc(sizeof(RemminaProtocolFeature) * (len+1));
- memset(&remmina_plugin->features[len], 0, sizeof(RemminaProtocolFeature));
-
- for (Py_ssize_t i = 0; i < len; ++i) {
- RemminaProtocolFeature* dest = remmina_plugin->features + i;
- ToRemminaProtocolFeature(dest, PyList_GetItem(features_list, i));
- }
- } else {
- remmina_plugin->features = NULL;
- }
-
- remmina_plugin->ssh_setting = (RemminaProtocolSSHSetting)GetEnumOrDefault(pluginInstance, "ssh_setting", REMMINA_PROTOCOL_SSH_SETTING_NONE);
-
- remmina_plugin->init = remmina_protocol_init_wrapper ; // Plugin initialization
- remmina_plugin->open_connection = remmina_protocol_open_connection_wrapper ; // Plugin open connection
- remmina_plugin->close_connection = remmina_protocol_close_connection_wrapper ; // Plugin close connection
- remmina_plugin->query_feature = remmina_protocol_query_feature_wrapper ; // Query for available features
- remmina_plugin->call_feature = remmina_protocol_call_feature_wrapper ; // Call a feature
- remmina_plugin->send_keystrokes = remmina_protocol_send_keytrokes_wrapper; // Send a keystroke
- remmina_plugin->get_plugin_screenshot = remmina_protocol_get_plugin_screenshot_wrapper; // Screenshot support unavailable
-
- return remmina_plugin;
-}
-
-RemminaPlugin* remmina_plugin_python_create_entry_plugin(pluginInstance)
-{
-
-}
-RemminaPlugin* remmina_plugin_python_create_file_plugin(pluginInstance)
-{
-
-}
-RemminaPlugin* remmina_plugin_python_create_tool_plugin(pluginInstance)
-{
-
-}
-RemminaPlugin* remmina_plugin_python_create_pref_plugin(pluginInstance)
-{
-
-}
-RemminaPlugin* remmina_plugin_python_create_secret_plugin(pluginInstance)
-{
-
-}
diff --git a/src/remmina_plugin_python_pref.c b/src/remmina_plugin_python_pref.c
new file mode 100644
index 000000000..b799d105a
--- /dev/null
+++ b/src/remmina_plugin_python_pref.c
@@ -0,0 +1,104 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina_plugin_python_common.h"
+#include "remmina_plugin_python_pref.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void remmina_plugin_python_pref_init(void)
+{
+ TRACE_CALL(__func__);
+
+}
+
+/**
+ * @brief
+ */
+GtkWidget* remmina_plugin_python_pref_get_pref_body_wrapper(RemminaPrefPlugin* instance)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+
+ PyObject* result = CallPythonMethod(plugin->instance, "get_pref_body", NULL, NULL);
+ if (result == Py_None || result == NULL)
+ {
+ return NULL;
+ }
+
+ return get_pywidget(result);
+}
+
+RemminaPlugin* remmina_plugin_python_create_pref_plugin(PyPlugin* plugin)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* instance = plugin->instance;
+
+ if (!remmina_plugin_python_check_attribute(instance, ATTR_NAME)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_VERSION)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_DESCRIPTION)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_PREF_LABEL))
+ {
+ g_printerr("Unable to create pref plugin. Aborting!\n");
+ return NULL;
+ }
+
+ RemminaPrefPlugin* remmina_plugin = (RemminaPrefPlugin*)remmina_plugin_python_malloc(sizeof(RemminaPrefPlugin));
+
+ remmina_plugin->type = REMMINA_PLUGIN_TYPE_PREF;
+ remmina_plugin->domain = GETTEXT_PACKAGE;
+ remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_NAME));
+ remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_VERSION));
+ remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_DESCRIPTION));
+ remmina_plugin->pref_label = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_PREF_LABEL));
+ remmina_plugin->get_pref_body = remmina_plugin_python_pref_get_pref_body_wrapper;
+
+ plugin->pref_plugin = remmina_plugin;
+ plugin->generic_plugin = (RemminaPlugin*)remmina_plugin;
+
+ remmina_plugin_python_add_plugin(plugin);
+
+ return (RemminaPlugin*)remmina_plugin;
+}
diff --git a/src/remmina_plugin_python_pref.h b/src/remmina_plugin_python_pref.h
new file mode 100644
index 000000000..ede1357ab
--- /dev/null
+++ b/src/remmina_plugin_python_pref.h
@@ -0,0 +1,61 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_pref.h
+ *
+ * @brief Contains the specialisation of RemminaPluginFile plugins in Python.
+ */
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+/**
+ * Initializes the Python plugin specialisation for preferences plugins.
+ */
+void remmina_plugin_python_pref_init(void);
+
+/**
+ * @brief Creates a new instance of the RemminaPluginPref, initializes its members and references the wrapper
+ * functions.
+ * @param instance The instance of the Python plugin.
+ * @return Returns a new instance of the RemminaPlugin (must be freed!).
+ */
+RemminaPlugin* remmina_plugin_python_create_pref_plugin(PyPlugin* instance);
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_protocol.c b/src/remmina_plugin_python_protocol.c
new file mode 100644
index 000000000..d01b2a657
--- /dev/null
+++ b/src/remmina_plugin_python_protocol.c
@@ -0,0 +1,317 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2022 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_common.c
+ * @brief
+ * @author Mathias Winterhalter
+ * @date 07.04.2021
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina_plugin_python_common.h"
+#include "remmina_plugin_python_protocol.h"
+#include "remmina_plugin_python_remmina.h"
+
+#include "remmina_protocol_widget.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void remmina_plugin_python_protocol_init(void)
+{
+ TRACE_CALL(__func__);
+}
+
+void remmina_protocol_init_wrapper(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ PyPlugin* py_plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ py_plugin->gp->gp = gp;
+ CallPythonMethod(py_plugin->instance, "init", "O", py_plugin->gp);
+}
+
+gboolean remmina_protocol_open_connection_wrapper(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ PyPlugin* py_plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ if (py_plugin)
+ {
+ PyObject* result = CallPythonMethod(py_plugin->instance, "open_connection", "O", py_plugin->gp);
+ return result == Py_True;
+ }
+ else
+ {
+ return gtk_false();
+ }
+}
+
+gboolean remmina_protocol_close_connection_wrapper(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ PyPlugin* py_plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyObject* result = CallPythonMethod(py_plugin->instance, "close_connection", "O", py_plugin->gp);
+ return result == Py_True;
+}
+
+gboolean remmina_protocol_query_feature_wrapper(RemminaProtocolWidget* gp,
+ const RemminaProtocolFeature* feature)
+{
+ TRACE_CALL(__func__);
+ PyPlugin* py_plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyRemminaProtocolFeature* pyFeature = remmina_plugin_python_protocol_feature_new();
+ pyFeature->type = (gint)feature->type;
+ pyFeature->id = feature->id;
+ pyFeature->opt1 = remmina_plugin_python_generic_new();
+ pyFeature->opt1->raw = feature->opt1;
+ pyFeature->opt2 = remmina_plugin_python_generic_new();
+ pyFeature->opt2->raw = feature->opt2;
+ pyFeature->opt3 = remmina_plugin_python_generic_new();
+ pyFeature->opt3->raw = feature->opt3;
+
+ PyObject* result = CallPythonMethod(py_plugin->instance, "query_feature", "OO", py_plugin->gp, pyFeature);
+ Py_DecRef((PyObject*)pyFeature);
+ Py_DecRef((PyObject*)pyFeature->opt1);
+ Py_DecRef((PyObject*)pyFeature->opt2);
+ Py_DecRef((PyObject*)pyFeature->opt3);
+ return result == Py_True;
+}
+
+void remmina_protocol_call_feature_wrapper(RemminaProtocolWidget* gp, const RemminaProtocolFeature* feature)
+{
+ TRACE_CALL(__func__);
+ PyPlugin* py_plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyRemminaProtocolFeature* pyFeature = remmina_plugin_python_protocol_feature_new();
+ pyFeature->type = (gint)feature->type;
+ pyFeature->id = feature->id;
+ pyFeature->opt1 = remmina_plugin_python_generic_new();
+ pyFeature->opt1->raw = feature->opt1;
+ pyFeature->opt1->type_hint = feature->opt1_type_hint;
+ pyFeature->opt2 = remmina_plugin_python_generic_new();
+ pyFeature->opt2->raw = feature->opt2;
+ pyFeature->opt2->type_hint = feature->opt2_type_hint;
+ pyFeature->opt3 = remmina_plugin_python_generic_new();
+ pyFeature->opt3->raw = feature->opt3;
+ pyFeature->opt3->type_hint = feature->opt3_type_hint;
+
+ CallPythonMethod(py_plugin->instance, "call_feature", "OO", py_plugin->gp, pyFeature);
+ Py_DecRef((PyObject*)pyFeature);
+ Py_DecRef((PyObject*)pyFeature->opt1);
+ Py_DecRef((PyObject*)pyFeature->opt2);
+ Py_DecRef((PyObject*)pyFeature->opt3);
+}
+
+void remmina_protocol_send_keytrokes_wrapper(RemminaProtocolWidget* gp,
+ const guint keystrokes[],
+ const gint keylen)
+{
+ TRACE_CALL(__func__);
+ PyPlugin* py_plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyObject* obj = PyList_New(keylen);
+ Py_IncRef(obj);
+ for (int i = 0; i < keylen; ++i)
+ {
+ PyList_SetItem(obj, i, PyLong_FromLong(keystrokes[i]));
+ }
+ CallPythonMethod(py_plugin->instance, "send_keystrokes", "OO", py_plugin->gp, obj);
+ Py_DecRef(obj);
+}
+
+gboolean remmina_protocol_get_plugin_screenshot_wrapper(RemminaProtocolWidget* gp,
+ RemminaPluginScreenshotData* rpsd)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* py_plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyRemminaPluginScreenshotData* data = remmina_plugin_python_screenshot_data_new();
+ Py_IncRef((PyObject*)data);
+ PyObject* result = CallPythonMethod(py_plugin->instance, "get_plugin_screenshot", "OO", py_plugin->gp, data);
+ if (result == Py_True)
+ {
+ if (!PyByteArray_Check((PyObject*)data->buffer))
+ {
+ g_printerr("Unable to parse screenshot data. 'buffer' needs to be an byte array!");
+ return 0;
+ }
+ Py_ssize_t buffer_len = PyByteArray_Size((PyObject*)data->buffer);
+
+ // Is being freed by Remmina!
+ rpsd->buffer = (unsigned char*)remmina_plugin_python_malloc(sizeof(unsigned char) * buffer_len);
+ if (!rpsd->buffer)
+ {
+ return 0;
+ }
+ memcpy(rpsd->buffer, PyByteArray_AsString((PyObject*)data->buffer), sizeof(unsigned char) * buffer_len);
+ rpsd->bytesPerPixel = data->bytesPerPixel;
+ rpsd->bitsPerPixel = data->bitsPerPixel;
+ rpsd->height = data->height;
+ rpsd->width = data->width;
+ }
+ Py_DecRef((PyObject*)data->buffer);
+ Py_DecRef((PyObject*)data);
+ return result == Py_True;
+}
+
+gboolean remmina_protocol_map_event_wrapper(RemminaProtocolWidget* gp)
+{
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyObject* result = CallPythonMethod(plugin->instance, "map_event", "O", plugin->gp);
+ return PyBool_Check(result) && result == Py_True;
+}
+
+gboolean remmina_protocol_unmap_event_wrapper(RemminaProtocolWidget* gp)
+{
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyObject* result = CallPythonMethod(plugin->instance, "unmap_event", "O", plugin->gp);
+ return PyBool_Check(result) && result == Py_True;
+}
+
+RemminaPlugin* remmina_plugin_python_create_protocol_plugin(PyPlugin* plugin)
+{
+ PyObject* instance = plugin->instance;
+
+ if (!remmina_plugin_python_check_attribute(instance, ATTR_ICON_NAME_SSH)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_ICON_NAME)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_FEATURES)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_BASIC_SETTINGS)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_ADVANCED_SETTINGS)
+ || !remmina_plugin_python_check_attribute(instance, ATTR_SSH_SETTING))
+ {
+ g_printerr("Unable to create protocol plugin. Aborting!\n");
+ return NULL;
+ }
+
+ RemminaProtocolPlugin* remmina_plugin = (RemminaProtocolPlugin*)remmina_plugin_python_malloc(sizeof(RemminaProtocolPlugin));
+
+ remmina_plugin->type = REMMINA_PLUGIN_TYPE_PROTOCOL;
+ remmina_plugin->domain = GETTEXT_PACKAGE;
+ remmina_plugin->basic_settings = NULL;
+ remmina_plugin->advanced_settings = NULL;
+ remmina_plugin->features = NULL;
+
+ remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_NAME));
+ remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_DESCRIPTION));
+ remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_VERSION));
+ remmina_plugin->icon_name = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_ICON_NAME));
+ remmina_plugin->icon_name_ssh = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_ICON_NAME_SSH));
+
+ PyObject* list = PyObject_GetAttrString(instance, "basic_settings");
+ Py_ssize_t len = PyList_Size(list);
+ if (len)
+ {
+ RemminaProtocolSetting* basic_settings = (RemminaProtocolSetting*)remmina_plugin_python_malloc(
+ sizeof(RemminaProtocolSetting) * (len + 1));
+ memset(basic_settings, 0, sizeof(RemminaProtocolSetting) * (len + 1));
+
+ for (Py_ssize_t i = 0; i < len; ++i)
+ {
+ RemminaProtocolSetting* dest = basic_settings + i;
+ remmina_plugin_python_to_protocol_setting(dest, PyList_GetItem(list, i));
+ }
+ RemminaProtocolSetting* dest = basic_settings + len;
+ dest->type = REMMINA_PROTOCOL_SETTING_TYPE_END;
+ remmina_plugin->basic_settings = basic_settings;
+ }
+
+ list = PyObject_GetAttrString(instance, "advanced_settings");
+ len = PyList_Size(list);
+ if (len)
+ {
+ RemminaProtocolSetting* advanced_settings = (RemminaProtocolSetting*)remmina_plugin_python_malloc(
+ sizeof(RemminaProtocolSetting) * (len + 1));
+ memset(advanced_settings, 0, sizeof(RemminaProtocolSetting) * (len + 1));
+
+ for (Py_ssize_t i = 0; i < len; ++i)
+ {
+ RemminaProtocolSetting* dest = advanced_settings + i;
+ remmina_plugin_python_to_protocol_setting(dest, PyList_GetItem(list, i));
+ }
+
+ RemminaProtocolSetting* dest = advanced_settings + len;
+ dest->type = REMMINA_PROTOCOL_SETTING_TYPE_END;
+
+ remmina_plugin->advanced_settings = advanced_settings;
+ }
+
+ list = PyObject_GetAttrString(instance, "features");
+ len = PyList_Size(list);
+ if (len)
+ {
+ RemminaProtocolFeature* features = (RemminaProtocolFeature*)remmina_plugin_python_malloc(
+ sizeof(RemminaProtocolFeature) * (len + 1));
+ memset(features, 0, sizeof(RemminaProtocolFeature) * (len + 1));
+
+ for (Py_ssize_t i = 0; i < len; ++i)
+ {
+ RemminaProtocolFeature* dest = features + i;
+ remmina_plugin_python_to_protocol_feature(dest, PyList_GetItem(list, i));
+ }
+
+ RemminaProtocolFeature* dest = features + len;
+ dest->type = REMMINA_PROTOCOL_FEATURE_TYPE_END;
+
+ remmina_plugin->features = features;
+ }
+
+ remmina_plugin->ssh_setting = (RemminaProtocolSSHSetting)remmina_plugin_python_get_attribute_long(instance,
+ ATTR_SSH_SETTING,
+ REMMINA_PROTOCOL_SSH_SETTING_NONE);
+
+ remmina_plugin->init = remmina_protocol_init_wrapper; // Plugin initialization
+ remmina_plugin->open_connection = remmina_protocol_open_connection_wrapper; // Plugin open connection
+ remmina_plugin->close_connection = remmina_protocol_close_connection_wrapper; // Plugin close connection
+ remmina_plugin->query_feature = remmina_protocol_query_feature_wrapper; // Query for available features
+ remmina_plugin->call_feature = remmina_protocol_call_feature_wrapper; // Call a feature
+ remmina_plugin->send_keystrokes =
+ remmina_protocol_send_keytrokes_wrapper; // Send a keystroke
+ remmina_plugin->get_plugin_screenshot =
+ remmina_protocol_get_plugin_screenshot_wrapper; // Screenshot support unavailable
+
+ remmina_plugin->map_event = remmina_protocol_map_event_wrapper;
+ remmina_plugin->unmap_event = remmina_protocol_unmap_event_wrapper;
+
+ plugin->protocol_plugin = remmina_plugin;
+ plugin->generic_plugin = (RemminaPlugin*)remmina_plugin;
+
+ remmina_plugin_python_add_plugin(plugin);
+
+ return (RemminaPlugin*)remmina_plugin;
+} \ No newline at end of file
diff --git a/src/remmina_plugin_python_protocol.h b/src/remmina_plugin_python_protocol.h
new file mode 100644
index 000000000..42724dcc6
--- /dev/null
+++ b/src/remmina_plugin_python_protocol.h
@@ -0,0 +1,107 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2022 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_protocol.h
+ *
+ * @brief Contains the specialisation of RemminaPluginFile plugins in Python.
+ */
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina/plugin.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+/**
+ * Wrapper for a Python object that contains a pointer to an instance of RemminaProtocolFeature.
+ */
+typedef struct
+{
+ PyObject_HEAD
+ RemminaProtocolFeatureType type;
+ gint id;
+ PyGeneric* opt1;
+ PyGeneric* opt2;
+ PyGeneric* opt3;
+} PyRemminaProtocolFeature;
+
+/**
+ *
+ */
+typedef struct
+{
+ PyObject_HEAD
+ PyByteArrayObject* buffer;
+ int bitsPerPixel;
+ int bytesPerPixel;
+ int width;
+ int height;
+} PyRemminaPluginScreenshotData;
+
+/**
+ * Initializes the Python plugin specialisation for protocol plugins.
+ */
+void remmina_plugin_python_protocol_init(void);
+
+/**
+ * @brief Creates a new instance of the RemminaPluginProtocol, initializes its members and references the wrapper
+ * functions.
+ *
+ * @param instance The instance of the Python plugin.
+ *
+ * @return Returns a new instance of the RemminaPlugin (must be freed!).
+ */
+RemminaPlugin* remmina_plugin_python_create_protocol_plugin(PyPlugin* plugin);
+
+/**
+ *
+ * @return
+ */
+PyRemminaProtocolFeature* remmina_plugin_python_protocol_feature_new(void);
+
+/**
+ *
+ * @return
+ */
+PyRemminaPluginScreenshotData* remmina_plugin_python_screenshot_data_new(void);
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_protocol_widget.c b/src/remmina_plugin_python_protocol_widget.c
index 7ae208883..eceb22422 100644
--- a/src/remmina_plugin_python_protocol_widget.c
+++ b/src/remmina_plugin_python_protocol_widget.c
@@ -29,7 +29,6 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
- *
*/
/**
@@ -83,49 +82,47 @@
* @see http://www.remmina.org/wp for more information.
*/
-#include <glib.h>
-#include <gtk/gtk.h>
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N L U C E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#include "config.h"
-#include "pygobject.h"
-#include "remmina_plugin_manager.h"
+#include "remmina_plugin_python_common.h"
#include "remmina/plugin.h"
#include "remmina_protocol_widget.h"
-#include "remmina_file.h"
-#include "remmina_plugin_python_remmina.h"
+#include "remmina/types.h"
#include "remmina_plugin_python_remmina_file.h"
-
#include "remmina_plugin_python_protocol_widget.h"
+#include "remmina_plugin_python_protocol.h"
// -- Python Type -> RemminaWidget
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
static PyObject* protocol_widget_get_viewport(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_get_width(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_set_width(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_set_width(PyRemminaProtocolWidget* self, PyObject* var_width);
static PyObject* protocol_widget_get_height(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_set_height(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_set_height(PyRemminaProtocolWidget* self, PyObject* var_height);
static PyObject* protocol_widget_get_current_scale_mode(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_get_expand(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_set_expand(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_set_expand(PyRemminaProtocolWidget* self, PyObject* var_expand);
static PyObject* protocol_widget_has_error(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_set_error(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_set_error(PyRemminaProtocolWidget* self, PyObject* var_msg);
static PyObject* protocol_widget_is_closed(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_get_file(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_emit_signal(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_register_hostkey(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_emit_signal(PyRemminaProtocolWidget* self, PyObject* var_signal);
+static PyObject* protocol_widget_register_hostkey(PyRemminaProtocolWidget* self, PyObject* var_widget);
static PyObject* protocol_widget_start_direct_tunnel(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_start_reverse_tunnel(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_start_reverse_tunnel(PyRemminaProtocolWidget* self, PyObject* var_local_port);
static PyObject* protocol_widget_start_xport_tunnel(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_set_display(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_set_display(PyRemminaProtocolWidget* self, PyObject* var_display);
static PyObject* protocol_widget_signal_connection_closed(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_signal_connection_opened(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_update_align(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_unlock_dynres(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_desktop_resize(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_panel_auth(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_panel_new_certificate(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_panel_changed_certificate(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_get_username(PyRemminaProtocolWidget* self, PyObject* args);
@@ -143,90 +140,126 @@ static PyObject* protocol_widget_panel_show_retry(PyRemminaProtocolWidget* self,
static PyObject* protocol_widget_panel_show(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_panel_hide(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_ssh_exec(PyRemminaProtocolWidget* self, PyObject* args);
-static PyObject* protocol_widget_chat_open(PyRemminaProtocolWidget* self, PyObject* args);
+static PyObject* protocol_widget_chat_open(PyRemminaProtocolWidget* self, PyObject* var_name);
static PyObject* protocol_widget_chat_close(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_chat_receive(PyRemminaProtocolWidget* self, PyObject* args);
static PyObject* protocol_widget_send_keys_signals(PyRemminaProtocolWidget* self, PyObject* args);
-static PyMethodDef python_protocol_widget_type_methods[] = {
- {"get_viewport", (PyCFunction)protocol_widget_get_viewport, METH_NOARGS, "" },
- {"get_width", (PyCFunction)protocol_widget_get_width, METH_NOARGS, "" },
- {"set_width", (PyCFunction)protocol_widget_set_width, METH_VARARGS, "" },
- {"get_height", (PyCFunction)protocol_widget_get_height, METH_VARARGS, "" },
- {"set_height", (PyCFunction)protocol_widget_set_height, METH_VARARGS, "" },
- {"get_current_scale_mode", (PyCFunction)protocol_widget_get_current_scale_mode, METH_VARARGS, "" },
- {"get_expand", (PyCFunction)protocol_widget_get_expand, METH_VARARGS, "" },
- {"set_expand", (PyCFunction)protocol_widget_set_expand, METH_VARARGS, "" },
- {"has_error", (PyCFunction)protocol_widget_has_error, METH_VARARGS, "" },
- {"set_error", (PyCFunction)protocol_widget_set_error, METH_VARARGS, "" },
- {"is_closed", (PyCFunction)protocol_widget_is_closed, METH_VARARGS, "" },
- {"get_file", (PyCFunction)protocol_widget_get_file, METH_VARARGS, "" },
- {"emit_signal", (PyCFunction)protocol_widget_emit_signal, METH_VARARGS, "" },
- {"register_hostkey", (PyCFunction)protocol_widget_register_hostkey, METH_VARARGS, "" },
- {"start_direct_tunnel", (PyCFunctionWithKeywords)protocol_widget_start_direct_tunnel, METH_VARARGS | METH_KEYWORDS, "" },
- {"start_reverse_tunnel", (PyCFunction)protocol_widget_start_reverse_tunnel, METH_VARARGS, "" },
- {"start_xport_tunnel", (PyCFunction)protocol_widget_start_xport_tunnel, METH_VARARGS, "" },
- {"set_display", (PyCFunction)protocol_widget_set_display, METH_VARARGS, "" },
- {"signal_connection_closed", (PyCFunction)protocol_widget_signal_connection_closed, METH_VARARGS, "" },
- {"signal_connection_opened", (PyCFunction)protocol_widget_signal_connection_opened, METH_VARARGS, "" },
- {"update_align", (PyCFunction)protocol_widget_update_align, METH_VARARGS, "" },
- {"unlock_dynres", (PyCFunction)protocol_widget_unlock_dynres, METH_VARARGS, "" },
- {"desktop_resize", (PyCFunction)protocol_widget_desktop_resize, METH_VARARGS, "" },
- {"panel_auth", (PyCFunction)protocol_widget_panel_auth, METH_VARARGS | METH_KEYWORDS, "" },
- {"panel_new_certificate", (PyCFunction)protocol_widget_panel_new_certificate, METH_VARARGS | METH_KEYWORDS, "" },
- {"panel_changed_certificate", (PyCFunction)protocol_widget_panel_changed_certificate, METH_VARARGS | METH_KEYWORDS, "" },
- {"get_username", (PyCFunction)protocol_widget_get_username, METH_VARARGS, "" },
- {"get_password", (PyCFunction)protocol_widget_get_password, METH_VARARGS, "" },
- {"get_domain", (PyCFunction)protocol_widget_get_domain, METH_VARARGS, "" },
- {"get_savepassword", (PyCFunction)protocol_widget_get_savepassword, METH_VARARGS, "" },
- {"panel_authx509", (PyCFunction)protocol_widget_panel_authx509, METH_VARARGS, "" },
- {"get_cacert", (PyCFunction)protocol_widget_get_cacert, METH_VARARGS, "" },
- {"get_cacrl", (PyCFunction)protocol_widget_get_cacrl, METH_VARARGS, "" },
- {"get_clientcert", (PyCFunction)protocol_widget_get_clientcert, METH_VARARGS, "" },
- {"get_clientkey", (PyCFunction)protocol_widget_get_clientkey, METH_VARARGS, "" },
- {"save_cred", (PyCFunction)protocol_widget_save_cred, METH_VARARGS, "" },
- {"panel_show_listen", (PyCFunction)protocol_widget_panel_show_listen, METH_VARARGS, "" },
- {"panel_show_retry", (PyCFunction)protocol_widget_panel_show_retry, METH_VARARGS, "" },
- {"panel_show", (PyCFunction)protocol_widget_panel_show, METH_VARARGS, "" },
- {"panel_hide", (PyCFunction)protocol_widget_panel_hide, METH_VARARGS, "" },
- {"ssh_exec", (PyCFunction)protocol_widget_ssh_exec, METH_VARARGS | METH_KEYWORDS, "" },
- {"chat_open", (PyCFunction)protocol_widget_chat_open, METH_VARARGS, "" },
- {"chat_close", (PyCFunction)protocol_widget_chat_close, METH_VARARGS, "" },
- {"chat_receive", (PyCFunction)protocol_widget_chat_receive, METH_VARARGS, "" },
- {"send_keys_signals", (PyCFunction)protocol_widget_send_keys_signals, METH_VARARGS | METH_KEYWORDS, "" }
-};
+static struct PyMethodDef python_protocol_widget_type_methods[] =
+ {{ "get_viewport", (PyCFunction)protocol_widget_get_viewport, METH_NOARGS, "" },
+ { "get_width", (PyCFunction)protocol_widget_get_width, METH_NOARGS, "" },
+ { "set_width", (PyCFunction)protocol_widget_set_width, METH_VARARGS, "" },
+ { "get_height", (PyCFunction)protocol_widget_get_height, METH_VARARGS, "" },
+ { "set_height", (PyCFunction)protocol_widget_set_height, METH_VARARGS, "" },
+ { "get_current_scale_mode", (PyCFunction)protocol_widget_get_current_scale_mode, METH_VARARGS, "" },
+ { "get_expand", (PyCFunction)protocol_widget_get_expand, METH_VARARGS, "" },
+ { "set_expand", (PyCFunction)protocol_widget_set_expand, METH_VARARGS, "" },
+ { "has_error", (PyCFunction)protocol_widget_has_error, METH_VARARGS, "" },
+ { "set_error", (PyCFunction)protocol_widget_set_error, METH_VARARGS, "" },
+ { "is_closed", (PyCFunction)protocol_widget_is_closed, METH_VARARGS, "" },
+ { "get_file", (PyCFunction)protocol_widget_get_file, METH_NOARGS, "" },
+ { "emit_signal", (PyCFunction)protocol_widget_emit_signal, METH_VARARGS, "" },
+ { "register_hostkey", (PyCFunction)protocol_widget_register_hostkey, METH_VARARGS, "" },
+ { "start_direct_tunnel", (PyCFunction)protocol_widget_start_direct_tunnel, METH_VARARGS | METH_KEYWORDS, "" },
+ { "start_reverse_tunnel", (PyCFunction)protocol_widget_start_reverse_tunnel, METH_VARARGS, "" },
+ { "start_xport_tunnel", (PyCFunction)protocol_widget_start_xport_tunnel, METH_VARARGS, "" },
+ { "set_display", (PyCFunction)protocol_widget_set_display, METH_VARARGS, "" },
+ { "signal_connection_closed", (PyCFunction)protocol_widget_signal_connection_closed, METH_VARARGS, "" },
+ { "signal_connection_opened", (PyCFunction)protocol_widget_signal_connection_opened, METH_VARARGS, "" },
+ { "update_align", (PyCFunction)protocol_widget_update_align, METH_VARARGS, "" },
+ { "unlock_dynres", (PyCFunction)protocol_widget_unlock_dynres, METH_VARARGS, "" },
+ { "desktop_resize", (PyCFunction)protocol_widget_desktop_resize, METH_VARARGS, "" },
+ { "panel_new_certificate", (PyCFunction)protocol_widget_panel_new_certificate, METH_VARARGS | METH_KEYWORDS, "" },
+ { "panel_changed_certificate", (PyCFunction)protocol_widget_panel_changed_certificate,
+ METH_VARARGS | METH_KEYWORDS, "" },
+ { "get_username", (PyCFunction)protocol_widget_get_username, METH_VARARGS, "" },
+ { "get_password", (PyCFunction)protocol_widget_get_password, METH_VARARGS, "" },
+ { "get_domain", (PyCFunction)protocol_widget_get_domain, METH_VARARGS, "" },
+ { "get_savepassword", (PyCFunction)protocol_widget_get_savepassword, METH_VARARGS, "" },
+ { "panel_authx509", (PyCFunction)protocol_widget_panel_authx509, METH_VARARGS, "" },
+ { "get_cacert", (PyCFunction)protocol_widget_get_cacert, METH_VARARGS, "" },
+ { "get_cacrl", (PyCFunction)protocol_widget_get_cacrl, METH_VARARGS, "" },
+ { "get_clientcert", (PyCFunction)protocol_widget_get_clientcert, METH_VARARGS, "" },
+ { "get_clientkey", (PyCFunction)protocol_widget_get_clientkey, METH_VARARGS, "" },
+ { "save_cred", (PyCFunction)protocol_widget_save_cred, METH_VARARGS, "" },
+ { "panel_show_listen", (PyCFunction)protocol_widget_panel_show_listen, METH_VARARGS, "" },
+ { "panel_show_retry", (PyCFunction)protocol_widget_panel_show_retry, METH_VARARGS, "" },
+ { "panel_show", (PyCFunction)protocol_widget_panel_show, METH_VARARGS, "" },
+ { "panel_hide", (PyCFunction)protocol_widget_panel_hide, METH_VARARGS, "" },
+ { "ssh_exec", (PyCFunction)protocol_widget_ssh_exec, METH_VARARGS | METH_KEYWORDS, "" },
+ { "chat_open", (PyCFunction)protocol_widget_chat_open, METH_VARARGS, "" },
+ { "chat_close", (PyCFunction)protocol_widget_chat_close, METH_VARARGS, "" },
+ { "chat_receive", (PyCFunction)protocol_widget_chat_receive, METH_VARARGS, "" },
+ { "send_keys_signals", (PyCFunction)protocol_widget_send_keys_signals, METH_VARARGS | METH_KEYWORDS, "" },
+ { NULL }};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static PyObject* python_protocol_feature_new(PyTypeObject* type, PyObject* kws, PyObject* args)
+{
+ TRACE_CALL(__func__);
+ PyRemminaProtocolWidget* self;
+ self = (PyRemminaProtocolWidget*)type->tp_alloc(type, 0);
+ if (!self)
+ {
+ return NULL;
+ }
+
+ return (PyObject*)self;
+}
+
+static int python_protocol_feature_init(PyObject* self, PyObject* args, PyObject* kwds)
+{
+ return 0;
+}
static PyTypeObject python_protocol_widget_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "remmina.RemminaWidget",
- .tp_doc = "Remmina protocol widget",
- .tp_basicsize = sizeof(PyRemminaProtocolWidget),
- .tp_itemsize = 0,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_new = PyType_GenericNew,
- .tp_methods = python_protocol_widget_type_methods
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "remmina.RemminaProtocolWidget",
+ .tp_doc = "RemminaProtocolWidget",
+ .tp_basicsize = sizeof(PyRemminaProtocolWidget),
+ .tp_itemsize = 0,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = python_protocol_feature_new,
+ .tp_init = python_protocol_feature_init,
+ .tp_methods = python_protocol_widget_type_methods
};
-typedef struct {
- PyObject_HEAD
- PyDictObject* settings;
- PyDictObject* spsettings;
-} PyRemminaFile;
+PyRemminaProtocolWidget* remmina_plugin_python_protocol_widget_create(void)
+{
+ TRACE_CALL(__func__);
+
+ PyRemminaProtocolWidget* result = PyObject_NEW(PyRemminaProtocolWidget, &python_protocol_widget_type);
+ assert(result);
-#define SELF_CHECK() if (!self) { \
- g_printerr("[%s:%d]: self is null!\n", __FILE__, __LINE__); \
- PyErr_SetString(PyExc_RuntimeError, "Method is not called from an instance (self is null)!"); \
- return NULL; \
- }
+ PyErr_Print();
+ Py_INCREF(result);
+ result->gp = NULL;
+ return result;
+}
-void remmina_plugin_python_protocol_widget_init(void) {
- pygobject_init(-1, -1, -1);
+void remmina_plugin_python_protocol_widget_init(void)
+{
+ init_pygobject();
+}
+
+void remmina_plugin_python_protocol_widget_type_ready(void)
+{
+ TRACE_CALL(__func__);
+ if (PyType_Ready(&python_protocol_widget_type) < 0)
+ {
+ g_printerr("Error initializing remmina.RemminaWidget!\n");
+ PyErr_Print();
+ }
}
static PyObject* protocol_widget_get_viewport(PyRemminaProtocolWidget* self, PyObject* args)
{
TRACE_CALL(__func__);
- return pygobject_new(G_OBJECT(remmina_protocol_widget_gtkviewport(self->gp)));
+ SELF_CHECK();
+ return (PyObject*)new_pywidget(G_OBJECT(remmina_protocol_widget_gtkviewport(self->gp)));
}
static PyObject* protocol_widget_get_width(PyRemminaProtocolWidget* self, PyObject* args)
@@ -242,14 +275,21 @@ static PyObject* protocol_widget_set_width(PyRemminaProtocolWidget* self, PyObje
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_width && PyLong_Check(var_width)) {
- gint width = (gint)PyLong_AsLong(var_width);
- remmina_protocol_widget_set_height(self->gp, width);
- } else {
- g_printerr("set_width(val): Error parsing arguments!\n");
- PyErr_Print();
+ if (!var_width)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ if (PyLong_Check(var_width))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type Long!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
+ gint width = (gint)PyLong_AsLong(var_width);
+ remmina_protocol_widget_set_height(self->gp, width);
+
return Py_None;
}
@@ -266,14 +306,21 @@ static PyObject* protocol_widget_set_height(PyRemminaProtocolWidget* self, PyObj
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_height && PyLong_Check(var_height)) {
- gint height = (gint)PyLong_AsLong(var_height);
- remmina_protocol_widget_set_height(self->gp, height);
- } else {
- g_printerr("set_height(val): Error parsing arguments!\n");
- PyErr_Print();
+ if (!var_height)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
+ if (PyLong_Check(var_height))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type Long!\n", __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ gint height = (gint)PyLong_AsLong(var_height);
+ remmina_protocol_widget_set_height(self->gp, height);
+
return Py_None;
}
@@ -298,12 +345,20 @@ static PyObject* protocol_widget_set_expand(PyRemminaProtocolWidget* self, PyObj
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_expand && PyBool_Check(var_expand)) {
- remmina_protocol_widget_set_expand(self->gp, PyObject_IsTrue(var_expand));
- } else {
- g_printerr("set_expand(val): Error parsing arguments!\n");
- PyErr_Print();
+ if (!var_expand)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ if (PyBool_Check(var_expand))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type Boolean!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
+
+ remmina_protocol_widget_set_expand(self->gp, PyObject_IsTrue(var_expand));
+
return Py_None;
}
@@ -320,13 +375,21 @@ static PyObject* protocol_widget_set_error(PyRemminaProtocolWidget* self, PyObje
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_msg && PyUnicode_Check(var_msg)) {
- gchar* msg = PyUnicode_AsUTF8(var_msg);
- remmina_protocol_widget_set_error(self->gp, msg);
- } else {
- g_printerr("set_error(msg): Error parsing arguments!\n");
- PyErr_Print();
+ if (!var_msg)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ if (PyUnicode_Check(var_msg))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type String!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
+
+ const gchar* msg = PyUnicode_AsUTF8(var_msg);
+ remmina_protocol_widget_set_error(self->gp, msg);
+
return Py_None;
}
@@ -344,7 +407,7 @@ static PyObject* protocol_widget_get_file(PyRemminaProtocolWidget* self, PyObjec
SELF_CHECK();
RemminaFile* file = remmina_protocol_widget_get_file(self->gp);
- return remmina_plugin_python_remmina_file_to_python(file);
+ return (PyObject*)remmina_plugin_python_remmina_file_to_python(file);
}
static PyObject* protocol_widget_emit_signal(PyRemminaProtocolWidget* self, PyObject* var_signal)
@@ -352,12 +415,20 @@ static PyObject* protocol_widget_emit_signal(PyRemminaProtocolWidget* self, PyOb
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_signal && PyUnicode_Check(var_signal)) {
- remmina_protocol_widget_set_error(self->gp, PyUnicode_AsUTF8(var_signal));
- } else {
- g_printerr("emit_signal(signal): Error parsing arguments!\n");
- PyErr_Print();
+ if (!var_signal)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ if (PyUnicode_Check(var_signal))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type String!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
+
+ remmina_protocol_widget_set_error(self->gp, PyUnicode_AsUTF8(var_signal));
+
return Py_None;
}
@@ -366,12 +437,14 @@ static PyObject* protocol_widget_register_hostkey(PyRemminaProtocolWidget* self,
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_widget) {
- remmina_protocol_widget_register_hostkey(self->gp, pygobject_get(var_widget));
- } else {
- g_printerr("register_hostkey(widget): Error parsing arguments!\n");
- PyErr_Print();
+ if (!var_widget)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
+
+ remmina_protocol_widget_register_hostkey(self->gp, get_pywidget(var_widget));
+
return Py_None;
}
@@ -383,11 +456,19 @@ static PyObject* protocol_widget_start_direct_tunnel(PyRemminaProtocolWidget* se
gint default_port;
gboolean port_plus;
- if (args && PyArg_ParseTuple(args, "ii", &default_port, &port_plus)) {
+ if (!args)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ }
+
+ if (PyArg_ParseTuple(args, "ii", &default_port, &port_plus))
+ {
return Py_BuildValue("s", remmina_protocol_widget_start_direct_tunnel(self->gp, default_port, port_plus));
- } else {
- g_printerr("start_direct_tunnel(default_port, port_plus): Error parsing arguments!\n");
+ }
+ else
+ {
PyErr_Print();
+ return NULL;
}
return Py_None;
}
@@ -397,19 +478,26 @@ static PyObject* protocol_widget_start_reverse_tunnel(PyRemminaProtocolWidget* s
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_local_port && PyLong_Check(var_local_port)) {
- return Py_BuildValue("p", remmina_protocol_widget_start_reverse_tunnel(self->gp, (gint)PyLong_AsLong(var_local_port)));
- } else {
- g_printerr("start_direct_tunnel(local_port): Error parsing arguments!\n");
- PyErr_Print();
+ if (!PyLong_Check(var_local_port))
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
- return Py_None;
+
+ if (!PyLong_Check(var_local_port))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type Long!\n", __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ return Py_BuildValue("p", remmina_protocol_widget_start_reverse_tunnel(self->gp, (gint)PyLong_AsLong(var_local_port)));
}
-static gboolean xport_tunnel_init(RemminaProtocolWidget *gp, gint remotedisplay, const gchar *server, gint port)
+static gboolean xport_tunnel_init(RemminaProtocolWidget* gp, gint remotedisplay, const gchar* server, gint port)
{
- PyPlugin* plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(plugin, "xport_tunnel_init", "Oisi", gp, remotedisplay, server, port);
+ TRACE_CALL(__func__);
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyObject* result = PyObject_CallMethod(plugin->instance, "xport_tunnel_init", "Oisi", gp, remotedisplay, server, port);
return PyObject_IsTrue(result);
}
@@ -426,12 +514,20 @@ static PyObject* protocol_widget_set_display(PyRemminaProtocolWidget* self, PyOb
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_display && PyLong_Check(var_display)) {
- remmina_protocol_widget_set_display(self->gp, (gint)PyLong_AsLong(var_display));
- } else {
- g_printerr("set_display(display): Error parsing arguments!\n");
- PyErr_Print();
+ if (!var_display)
+ {
+ g_printerr("[%s:%d@%s]: Argument is null!\n", __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ if (!PyLong_Check(var_display))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type Long!\n", __FILE__, __LINE__, __func__);
+ return NULL;
}
+
+ remmina_protocol_widget_set_display(self->gp, (gint)PyLong_AsLong(var_display));
+
return Py_None;
}
@@ -480,42 +576,20 @@ static PyObject* protocol_widget_desktop_resize(PyRemminaProtocolWidget* self, P
return Py_None;
}
-static PyObject* protocol_widget_panel_auth(PyRemminaProtocolWidget* self, PyObject* args)
-{
- TRACE_CALL(__func__);
- SELF_CHECK();
-
- gint pflags = 0;
- gchar* title, default_username, default_password, default_domain, password_prompt;
-
- if (PyArg_ParseTuple(args, "isssss", &pflags, &title, &default_username, &default_password, &default_domain, &password_prompt)) {
- if (pflags != REMMINA_MESSAGE_PANEL_FLAG_USERNAME
- && pflags != REMMINA_MESSAGE_PANEL_FLAG_USERNAME_READONLY
- && pflags != REMMINA_MESSAGE_PANEL_FLAG_DOMAIN
- && pflags != REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD) {
- g_printerr("panel_auth(pflags, title, default_username, default_password, default_domain, password_prompt): "
- "%d is not a known value for RemminaMessagePanelFlags!\n", pflags);
- } else {
- remmina_protocol_widget_panel_auth(self->gp, pflags, title, default_username, default_password, default_domain, password_prompt);
- }
- } else {
- g_printerr("panel_auth(pflags, title, default_username, default_password, default_domain, password_prompt): Error parsing arguments!\n");
- PyErr_Print();
- }
- return Py_None;
-}
-
static PyObject* protocol_widget_panel_new_certificate(PyRemminaProtocolWidget* self, PyObject* args)
{
TRACE_CALL(__func__);
SELF_CHECK();
- gchar* subject, issuer, fingerprint;
+ gchar* subject, * issuer, * fingerprint;
- if (PyArg_ParseTuple(args, "sss", &subject, &issuer, &fingerprint)) {
+ if (PyArg_ParseTuple(args, "sss", &subject, &issuer, &fingerprint))
+ {
remmina_protocol_widget_panel_new_certificate(self->gp, subject, issuer, fingerprint);
- } else {
- g_printerr("panel_new_certificate(subject, issuer, fingerprint): Error parsing arguments!\n");
+ }
+ else
+ {
PyErr_Print();
+ return NULL;
}
return Py_None;
}
@@ -524,13 +598,16 @@ static PyObject* protocol_widget_panel_changed_certificate(PyRemminaProtocolWidg
{
TRACE_CALL(__func__);
SELF_CHECK();
- gchar* subject, issuer, new_fingerprint, old_fingerprint;
+ gchar* subject, * issuer, * new_fingerprint, * old_fingerprint;
- if (PyArg_ParseTuple(args, "sss", &subject, &issuer, &new_fingerprint, &old_fingerprint)) {
+ if (PyArg_ParseTuple(args, "sss", &subject, &issuer, &new_fingerprint, &old_fingerprint))
+ {
remmina_protocol_widget_panel_changed_certificate(self->gp, subject, issuer, new_fingerprint, old_fingerprint);
- } else {
- g_printerr("panel_changed_certificate(subject, issuer, new_fingerprint, old_fingerprint): Error parsing arguments!\n");
+ }
+ else
+ {
PyErr_Print();
+ return NULL;
}
return Py_None;
}
@@ -622,11 +699,14 @@ static PyObject* protocol_widget_panel_show_listen(PyRemminaProtocolWidget* self
SELF_CHECK();
gint port = 0;
- if (PyArg_ParseTuple(args, "i", &port)) {
+ if (PyArg_ParseTuple(args, "i", &port))
+ {
remmina_protocol_widget_panel_show_listen(self->gp, port);
- } else {
- g_printerr("panel_show_listen(port): Error parsing arguments!\n");
+ }
+ else
+ {
PyErr_Print();
+ return NULL;
}
return Py_None;
}
@@ -665,27 +745,28 @@ static PyObject* protocol_widget_ssh_exec(PyRemminaProtocolWidget* self, PyObjec
gboolean wait;
gchar* cmd;
- if (PyArg_ParseTuple(args, "ps", &wait, &cmd)) {
+ if (PyArg_ParseTuple(args, "ps", &wait, &cmd))
+ {
remmina_protocol_widget_ssh_exec(self->gp, wait, cmd);
- } else {
- g_printerr("ssh_exec(wait, cmd): Error parsing arguments!\n");
+ }
+ else
+ {
PyErr_Print();
+ return NULL;
}
return Py_None;
}
-static gboolean _on_send_callback_wrapper(RemminaProtocolWidget *gp, const gchar *text)
+static void _on_send_callback_wrapper(RemminaProtocolWidget* gp, const gchar* text)
{
- PyPlugin* plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(plugin, "on_send", "Os", gp, text);
- return PyObject_IsTrue(result);
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyObject_CallMethod(plugin->instance, "on_send", "Os", gp, text);
}
-static gboolean _on_destroy_callback_wrapper(RemminaProtocolWidget *gp)
+static void _on_destroy_callback_wrapper(RemminaProtocolWidget* gp)
{
- PyPlugin* plugin = remmina_plugin_python_module_get_plugin(gp);
- PyObject* result = PyObject_CallMethod(plugin, "on_destroy", "O", gp);
- return PyObject_IsTrue(result);
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(gp->plugin->name);
+ PyObject_CallMethod(plugin->instance, "on_destroy", "O", gp);
}
static PyObject* protocol_widget_chat_open(PyRemminaProtocolWidget* self, PyObject* var_name)
@@ -693,13 +774,16 @@ static PyObject* protocol_widget_chat_open(PyRemminaProtocolWidget* self, PyObje
TRACE_CALL(__func__);
SELF_CHECK();
- if (var_name, PyUnicode_Check(var_name)) {
- remmina_protocol_widget_chat_open(self->gp, PyUnicode_AsUTF8(var_name), _on_send_callback_wrapper, _on_destroy_callback_wrapper);
- } else {
- g_printerr("chat_open(name): Error parsing arguments!\n");
- PyErr_Print();
+ if (!PyUnicode_Check(var_name))
+ {
+ g_printerr("[%s:%d@%s]: Argument is not of type String!\n", __FILE__, __LINE__, __func__);
}
+ remmina_protocol_widget_chat_open(self->gp,
+ PyUnicode_AsUTF8(var_name),
+ _on_send_callback_wrapper,
+ _on_destroy_callback_wrapper);
+
return Py_None;
}
@@ -718,11 +802,14 @@ static PyObject* protocol_widget_chat_receive(PyRemminaProtocolWidget* self, PyO
SELF_CHECK();
gchar* text;
- if (PyArg_ParseTuple(args, "s", &text)) {
+ if (PyArg_ParseTuple(args, "s", &text))
+ {
remmina_protocol_widget_chat_receive(self->gp, text);
- } else {
- g_printerr("chat_receive(text): Error parsing arguments!\n");
+ }
+ else
+ {
PyErr_Print();
+ return NULL;
}
return Py_None;
@@ -732,22 +819,28 @@ static PyObject* protocol_widget_send_keys_signals(PyRemminaProtocolWidget* self
{
TRACE_CALL(__func__);
SELF_CHECK();
- gchar* keyvals;
+ guint* keyvals;
int length;
GdkEventType event_type;
-
- if (PyArg_ParseTuple(args, "sii", &keyvals, &length, &event_type)) {
- if (event_type < GDK_NOTHING || event_type >= GDK_EVENT_LAST) {
- g_printerr("send_keys_signals(keyvals, length, event_type): "
- "%d is not a known value for GdkEventType!\n", event_type);
- } else {
- remmina_protocol_widget_send_keys_signals(self->gp, keyvals, length, event_type);
+ PyObject* widget;
+
+ if (PyArg_ParseTuple(args, "Osii", &widget, &keyvals, &length, &event_type) && widget && keyvals)
+ {
+ if (event_type < GDK_NOTHING || event_type >= GDK_EVENT_LAST)
+ {
+ g_printerr("[%s:%d@%s]: %d is not a known value for GdkEventType!\n", __FILE__, __LINE__, __func__, event_type);
+ return NULL;
+ }
+ else
+ {
+ remmina_protocol_widget_send_keys_signals((GtkWidget*)widget, keyvals, length, event_type);
}
- } else {
- g_printerr("send_keys_signals(keyvals): Error parsing arguments!\n");
+ }
+ else
+ {
PyErr_Print();
+ return NULL;
}
return Py_None;
}
-
diff --git a/src/remmina_plugin_python_protocol_widget.h b/src/remmina_plugin_python_protocol_widget.h
index 93bd38fbb..dcbbc8093 100644
--- a/src/remmina_plugin_python_protocol_widget.h
+++ b/src/remmina_plugin_python_protocol_widget.h
@@ -29,10 +29,35 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_protocol_widget.h
*
+ * @brief Contains the implementation of the widget handling used from the protocol plugin.
*/
#pragma once
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+/**
+ * Initializes the widget backend of the protocol plugin implementation.
+ */
void remmina_plugin_python_protocol_widget_init(void);
+/**
+ * Initializes Python types used for protocol widgets.
+ */
+void remmina_plugin_python_protocol_widget_type_ready(void);
+
+/**
+ * Creates a new instance of PyRemminaProtocolWidget and initializes its fields.
+ */
+PyRemminaProtocolWidget* remmina_plugin_python_protocol_widget_create();
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_remmina.c b/src/remmina_plugin_python_remmina.c
index 453421605..ad0ebc7a4 100644
--- a/src/remmina_plugin_python_remmina.c
+++ b/src/remmina_plugin_python_remmina.c
@@ -29,280 +29,213 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
- *
*/
-#include <glib.h>
-#include <gtk/gtk.h>
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#include "config.h"
+#include "remmina_plugin_python_common.h"
+#include "remmina_main.h"
#include "remmina_plugin_manager.h"
#include "remmina/plugin.h"
+#include "remmina/types.h"
#include "remmina_protocol_widget.h"
+#include "remmina_log.h"
#include "remmina_plugin_python_remmina.h"
+#include "remmina_plugin_python_protocol_widget.h"
-/**
- * @brief Holds pairs of Python and Remmina plugin instances (PyPlugin).
- */
-GPtrArray *remmina_plugin_registry = NULL;
+#include "remmina_plugin_python_entry.h"
+#include "remmina_plugin_python_file.h"
+#include "remmina_plugin_python_protocol.h"
+#include "remmina_plugin_python_tool.h"
+#include "remmina_plugin_python_secret.h"
+#include "remmina_plugin_python_pref.h"
-/**
- *
- */
-gboolean remmina_plugin_python_check_mandatory_member(PyObject* instance, const gchar* member);
+#include "remmina_pref.h"
+#include "remmina_ssh.h"
+#include "remmina_file_manager.h"
+#include "remmina_widget_pool.h"
+#include "remmina_public.h"
+#include "remmina_masterthread_exec.h"
-/**
- * @brief Wraps the log_printf function of RemminaPluginService.
- *
- * @details This function is only called by the Python engine! There is only one argument
- * since Python offers an inline string formatting which renders any formatting capabilities
- * redundant.
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param msg The message to print.
- */
-static PyObject* remmina_plugin_python_log_printf_wrapper(PyObject* self, PyObject* msg);
+#include "rcw.h"
+#include "remmina_plugin_python_remmina_file.h"
-/**
- * @brief Accepts an instance of a Python class representing a Remmina plugin.
- *
- * @details This function is only called by the Python engine!
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin An instance of a Python plugin class.
- */
-static PyObject* remmina_register_plugin_wrapper(PyObject* self, PyObject* plugin);
+#include <string.h>
-/**
- * @brief Retrieves the viewport of the current connection window if available.
- *
- * @details This function is only called by the Python engine!
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_plugin_python_get_viewport(PyObject* self, PyObject* handle);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_file_get_datadir_wrapper(PyObject* self, PyObject* plugin);
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_file_new_wrapper(PyObject* self, PyObject* plugin);
/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
+ * Util function to check if a specific member is define in a Python object.
*/
-static PyObject* remmina_pref_set_value_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_pref_get_value_wrapper(PyObject* self, PyObject* plugin);
+gboolean remmina_plugin_python_check_mandatory_member(PyObject* instance, const gchar* member);
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
+static PyObject* remmina_plugin_python_debug_wrapper(PyObject* self, PyObject* msg);
+static PyObject* remmina_register_plugin_wrapper(PyObject* self, PyObject* plugin);
+static PyObject* remmina_file_get_datadir_wrapper(PyObject* self, PyObject* plugin);
+static PyObject* remmina_file_new_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject* remmina_pref_set_value_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject* remmina_pref_get_value_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
static PyObject* remmina_pref_get_scale_quality_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
static PyObject* remmina_pref_get_sshtunnel_port_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
static PyObject* remmina_pref_get_ssh_loglevel_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
static PyObject* remmina_pref_get_ssh_parseconfig_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_pref_keymap_get_keyval_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_log_print_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_log_printf_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_widget_pool_register_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* rcw_open_from_file_full_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_public_get_server_port_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
+static PyObject* remmina_pref_keymap_get_keyval_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject* remmina_plugin_python_log_print_wrapper(PyObject* self, PyObject* arg);
+static PyObject* remmina_widget_pool_register_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject* rcw_open_from_file_full_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject* remmina_public_get_server_port_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
static PyObject* remmina_masterthread_exec_is_main_thread_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
static PyObject* remmina_gtksocket_available_wrapper(PyObject* self, PyObject* plugin);
+static PyObject*
+remmina_protocol_widget_get_profile_remote_height_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject*
+remmina_protocol_widget_get_profile_remote_width_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject* remmina_plugin_python_show_dialog_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
+static PyObject* remmina_plugin_python_get_mainwindow_wrapper(PyObject* self, PyObject* args);
+static PyObject* remmina_protocol_plugin_signal_connection_opened_wrapper(PyObject* self, PyObject* args);
+static PyObject* remmina_protocol_plugin_signal_connection_closed_wrapper(PyObject* self, PyObject* args);
+static PyObject* remmina_protocol_plugin_init_auth_wrapper(PyObject* self, PyObject* args, PyObject* kwargs);
/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_protocol_widget_get_profile_remote_heigh_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief
- *
- * @details
- *
- * @param self Is always NULL since it is a static function of the 'remmina' module
- * @param plugin The handle to retrieve the RemminaConnectionWidget
- */
-static PyObject* remmina_protocol_widget_get_profile_remote_width_wrapper(PyObject* self, PyObject* plugin);
-
-/**
- * @brief The functions of the remmina module.
- *
- * @details If any function has to be added this is the place to do it. This list is referenced
- * by the PyModuleDef (describing the module) instance below.
+ * Declares functions for the Remmina module. These functions can be called from Python and are wired to one of the
+ * functions here in this file.
*/
static PyMethodDef remmina_python_module_type_methods[] = {
- {"register_plugin", (PyCFunction)remmina_register_plugin_wrapper, METH_O, NULL },
- {"log_print", (PyCFunction)remmina_plugin_python_log_printf_wrapper, METH_VARARGS, NULL },
- {"get_datadir", (PyCFunction)remmina_file_get_datadir_wrapper},
- {"file_new", (PyCFunction)remmina_file_new_wrapper},
-
- {"pref_set_value", (PyCFunction)remmina_pref_set_value_wrapper},
- {"pref_get_value", (PyCFunction)remmina_pref_get_value_wrapper},
- {"pref_get_scale_quality", (PyCFunction)remmina_pref_get_scale_quality_wrapper},
- {"pref_get_sshtunnel_port", (PyCFunction)remmina_pref_get_sshtunnel_port_wrapper},
- {"pref_get_ssh_loglevel", (PyCFunction)remmina_pref_get_ssh_loglevel_wrapper},
- {"pref_get_ssh_parseconfig", (PyCFunction)remmina_pref_get_ssh_parseconfig_wrapper},
- {"pref_keymap_get_keyval", (PyCFunction)remmina_pref_keymap_get_keyval_wrapper},
-
- {"log_print", (PyCFunction)remmina_log_print_wrapper},
- {"log_printf", (PyCFunction)remmina_log_printf_wrapper},
-
- {"widget_pool_register", (PyCFunction)remmina_widget_pool_register_wrapper},
-
- {"rcw_open_from_file_full", (PyCFunction)rcw_open_from_file_full_wrapper },
- {"public_get_server_port", (PyCFunction)remmina_public_get_server_port_wrapper},
- {"masterthread_exec_is_main_thread", (PyCFunction)remmina_masterthread_exec_is_main_thread_wrapper},
- {"gtksocket_available", (PyCFunction)remmina_gtksocket_available_wrapper},
- {"protocol_widget_get_profile_remote_width", (PyCFunction)remmina_protocol_widget_get_profile_remote_width_wrapper},
- {"protocol_widget_get_profile_remote_heigh", (PyCFunction)remmina_protocol_widget_get_profile_remote_heigh_wrapper},
- {NULL} /* Sentinel */
+ /**
+ * The first function that need to be called from the plugin code, since it registers the Python class acting as
+ * a Remmina plugin. Without this call the plugin will not be recognized.
+ */
+ { "register_plugin", remmina_register_plugin_wrapper, METH_O, NULL },
+
+ /**
+ * Prints a string into the Remmina log infrastructure.
+ */
+ { "log_print", remmina_plugin_python_log_print_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Prints a debug message if enabled.
+ */
+ { "debug", remmina_plugin_python_debug_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Shows a GTK+ dialog.
+ */
+ { "show_dialog", (PyCFunction)remmina_plugin_python_show_dialog_wrapper, METH_VARARGS | METH_KEYWORDS,
+ NULL },
+
+ /**
+ * Returns the GTK+ object of the main window of Remmina.
+ */
+ { "get_main_window", remmina_plugin_python_get_mainwindow_wrapper, METH_NOARGS, NULL },
+
+ /**
+ * Calls remmina_file_get_datadir and returns its result.
+ */
+ { "get_datadir", remmina_file_get_datadir_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Calls remmina_file_new and returns its result.
+ */
+ { "file_new", (PyCFunction)remmina_file_new_wrapper, METH_VARARGS | METH_KEYWORDS, NULL },
+
+ /**
+ * Calls remmina_pref_set_value and returns its result.
+ */
+ { "pref_set_value", (PyCFunction)remmina_pref_set_value_wrapper, METH_VARARGS | METH_KEYWORDS, NULL },
+
+ /**
+ * Calls remmina_pref_get_value and returns its result.
+ */
+ { "pref_get_value", (PyCFunction)remmina_pref_get_value_wrapper, METH_VARARGS | METH_KEYWORDS, NULL },
+
+ /**
+ * Calls remmina_pref_get_scale_quality and returns its result.
+ */
+ { "pref_get_scale_quality", remmina_pref_get_scale_quality_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Calls remmina_pref_get_sshtunnel_port and returns its result.
+ */
+ { "pref_get_sshtunnel_port", remmina_pref_get_sshtunnel_port_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Calls remmina_pref_get_ssh_loglevel and returns its result.
+ */
+ { "pref_get_ssh_loglevel", remmina_pref_get_ssh_loglevel_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Calls remmina_pref_get_ssh_parseconfig and returns its result.
+ */
+ { "pref_get_ssh_parseconfig", remmina_pref_get_ssh_parseconfig_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Calls remmina_pref_keymap_get_keyval and returns its result.
+ */
+ { "pref_keymap_get_keyval", (PyCFunction)remmina_pref_keymap_get_keyval_wrapper, METH_VARARGS | METH_KEYWORDS,
+ NULL },
+
+ /**
+ * Calls remmina_widget_pool_register and returns its result.
+ */
+ { "widget_pool_register", (PyCFunction)remmina_widget_pool_register_wrapper, METH_VARARGS | METH_KEYWORDS, NULL },
+
+ /**
+ * Calls rcw_open_from_file_full and returns its result.
+ */
+ { "rcw_open_from_file_full", (PyCFunction)rcw_open_from_file_full_wrapper, METH_VARARGS | METH_KEYWORDS, NULL },
+
+ /**
+ * Calls remmina_public_get_server_port and returns its result.
+ */
+ { "public_get_server_port", (PyCFunction)remmina_public_get_server_port_wrapper, METH_VARARGS | METH_KEYWORDS,
+ NULL },
+
+ /**
+ * Calls remmina_masterthread_exec_is_main_thread and returns its result.
+ */
+ { "masterthread_exec_is_main_thread", remmina_masterthread_exec_is_main_thread_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Calls remmina_gtksocket_available and returns its result.
+ */
+ { "gtksocket_available", remmina_gtksocket_available_wrapper, METH_VARARGS, NULL },
+
+ /**
+ * Calls remmina_protocol_widget_get_profile_remote_width and returns its result.
+ */
+ { "protocol_widget_get_profile_remote_width",
+ (PyCFunction)remmina_protocol_widget_get_profile_remote_width_wrapper, METH_VARARGS | METH_KEYWORDS, NULL },
+
+ /**
+ * Calls remmina_protocol_widget_get_profile_remote_height and returns its result.
+ */
+ { "protocol_widget_get_profile_remote_height",
+ (PyCFunction)remmina_protocol_widget_get_profile_remote_height_wrapper, METH_VARARGS | METH_KEYWORDS, NULL },
+ { "protocol_plugin_signal_connection_opened", (PyCFunction)remmina_protocol_plugin_signal_connection_opened_wrapper,
+ METH_VARARGS, NULL },
+
+ { "protocol_plugin_signal_connection_closed", (PyCFunction)remmina_protocol_plugin_signal_connection_closed_wrapper,
+ METH_VARARGS, NULL },
+
+ { "protocol_plugin_init_auth", (PyCFunction)remmina_protocol_plugin_init_auth_wrapper, METH_VARARGS | METH_KEYWORDS,
+ NULL },
+
+ /* Sentinel */
+ { NULL }
};
-typedef struct {
- PyObject_HEAD
+/**
+ * Adapter struct to handle Remmina protocol settings.
+ */
+typedef struct
+{
+ PyObject_HEAD
RemminaProtocolSettingType settingType;
gchar* name;
gchar* label;
@@ -311,70 +244,93 @@ typedef struct {
PyObject* opt2;
} PyRemminaProtocolSetting;
-
/**
- * @brief The definition of the Python module 'remmina'.
+ * The definition of the Python module 'remmina'.
*/
static PyModuleDef remmina_python_module_type = {
- PyModuleDef_HEAD_INIT,
- .m_name = "remmina",
- .m_doc = "Remmina API.",
- .m_size = -1,
- .m_methods = remmina_python_module_type_methods
+ PyModuleDef_HEAD_INIT,
+ .m_name = "remmina",
+ .m_doc = "Remmina API.",
+ .m_size = -1,
+ .m_methods = remmina_python_module_type_methods
};
// -- Python Object -> Setting
-static PyObject* python_protocol_setting_new(PyTypeObject * type, PyObject* args, PyObject* kwds)
+/**
+ * Initializes the memory and the fields of the remmina.Setting Python type.
+ * @details This function is callback for the Python engine.
+ */
+static PyObject* python_protocol_setting_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
- PyRemminaProtocolSetting *self;
- self = (PyRemminaProtocolSetting*)type->tp_alloc(type, 0);
- if (!self)
- return NULL;
-
- self->name = "";
- self->label = "";
- self->compact = FALSE;
- self->opt1 = NULL;
- self->opt2 = NULL;
- self->settingType = 0;
+ TRACE_CALL(__func__);
- return self;
-}
+ PyRemminaProtocolSetting* self = (PyRemminaProtocolSetting*)type->tp_alloc(type, 0);
-static int python_protocol_setting_init(PyRemminaProtocolSetting *self, PyObject *args, PyObject *kwds)
-{
- static char *kwlist[] = { "type", "name", "label", "compact", "opt1", "opt2", NULL };
- PyObject* name, *label;
+ if (!self)
+ {
+ return NULL;
+ }
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lOOpOO", kwlist
- ,&self->settingType
- ,&name
- ,&label
- ,&self->compact
- ,&self->opt1
- ,&self->opt2))
- return -1;
+ self->name = "";
+ self->label = "";
+ self->compact = FALSE;
+ self->opt1 = NULL;
+ self->opt2 = NULL;
+ self->settingType = 0;
- Py_ssize_t len = PyUnicode_GetLength(label);
- if (len == 0) {
- self->label = "";
- } else {
- self->label = g_malloc(sizeof(char) * (len + 1));
- self->label[len] = 0;
- memcpy(self->label, PyUnicode_AsUTF8(label), len);
- }
+ return (PyObject*)self;
+}
- len = PyUnicode_GetLength(name);
- if (len == 0) {
- self->name = "";
- } else {
- self->name = g_malloc(sizeof(char) * (len + 1));
- self->name[len] = 0;
- memcpy(self->name, PyUnicode_AsUTF8(name), len);
- }
+/**
+ * Constructor of the remmina.Setting Python type.
+ * @details This function is callback for the Python engine.
+ */
+static int python_protocol_setting_init(PyRemminaProtocolSetting* self, PyObject* args, PyObject* kwargs)
+{
+ TRACE_CALL(__func__);
- return 0;
+ static gchar* kwlist[] = { "type", "name", "label", "compact", "opt1", "opt2", NULL };
+ PyObject* name;
+ PyObject* label;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|lOOpOO", kwlist,
+ &self->settingType, &name, &label, &self->compact, &self->opt1, &self->opt2))
+ {
+ return -1;
+ }
+
+ Py_ssize_t len = PyUnicode_GetLength(label);
+ if (len == 0)
+ {
+ self->label = "";
+ }
+ else
+ {
+ self->label = remmina_plugin_python_copy_string_from_python(label, len);
+ if (!self->label)
+ {
+ g_printerr("Unable to extract label during initialization of Python settings module!\n");
+ remmina_plugin_python_check_error();
+ }
+ }
+
+ len = PyUnicode_GetLength(name);
+ if (len == 0)
+ {
+ self->name = "";
+ }
+ else
+ {
+ self->name = remmina_plugin_python_copy_string_from_python(label, len);
+ if (!self->name)
+ {
+ g_printerr("Unable to extract name during initialization of Python settings module!\n");
+ remmina_plugin_python_check_error();
+ }
+ }
+
+ return 0;
}
static PyMemberDef python_protocol_setting_type_members[] = {
@@ -384,108 +340,335 @@ static PyMemberDef python_protocol_setting_type_members[] = {
{ "compact", offsetof(PyRemminaProtocolSetting, compact), T_BOOL, 0, NULL },
{ "opt1", offsetof(PyRemminaProtocolSetting, opt1), T_OBJECT, 0, NULL },
{ "opt2", offsetof(PyRemminaProtocolSetting, opt2), T_OBJECT, 0, NULL },
- {NULL}
+ { NULL }
};
static PyTypeObject python_protocol_setting_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "remmina.Setting",
- .tp_doc = "Remmina Setting information",
- .tp_basicsize = sizeof(PyRemminaProtocolSetting),
- .tp_itemsize = 0,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_new = python_protocol_setting_new,
- .tp_init = python_protocol_setting_init,
- .tp_members = python_protocol_setting_type_members
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "remmina.Setting",
+ .tp_doc = "Remmina Setting information",
+ .tp_basicsize = sizeof(PyRemminaProtocolSetting),
+ .tp_itemsize = 0,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = python_protocol_setting_new,
+ .tp_init = (initproc)python_protocol_setting_init,
+ .tp_members = python_protocol_setting_type_members
};
// -- Python Type -> Feature
-typedef struct {
- PyObject_HEAD
- RemminaProtocolFeatureType type;
- gint id;
- PyObject* opt1;
- PyObject* opt2;
- PyObject* opt3;
-} PyRemminaProtocolFeature;
static PyMemberDef python_protocol_feature_members[] = {
- { "type", offsetof(PyRemminaProtocolFeature, type), T_INT, 0, NULL },
- { "id", offsetof(PyRemminaProtocolFeature, id), T_STRING, 0, NULL },
- { "opt1", offsetof(PyRemminaProtocolFeature, opt1), T_OBJECT, 0, NULL },
- { "opt2", offsetof(PyRemminaProtocolFeature, opt2), T_OBJECT, 0, NULL },
- { "opt3", offsetof(PyRemminaProtocolFeature, opt3), T_OBJECT, 0, NULL },
- {NULL}
+ { "type", T_INT, offsetof(PyRemminaProtocolFeature, type), 0, NULL },
+ { "id", T_INT, offsetof(PyRemminaProtocolFeature, id), 0, NULL },
+ { "opt1", T_OBJECT, offsetof(PyRemminaProtocolFeature, opt1), 0, NULL },
+ { "opt2", T_OBJECT, offsetof(PyRemminaProtocolFeature, opt2), 0, NULL },
+ { "opt3", T_OBJECT, offsetof(PyRemminaProtocolFeature, opt3), 0, NULL },
+ { NULL }
};
-static PyObject* python_protocol_feature_new(PyTypeObject * type, PyObject* kws, PyObject* args)
+PyObject* python_protocol_feature_new(PyTypeObject* type, PyObject* kws, PyObject* args)
{
- PyRemminaProtocolFeature *self;
- self = (PyRemminaProtocolFeature*)type->tp_alloc(type, 0);
- if (!self)
- return NULL;
-
- self->id = 0;
- self->type = 0;
- self->opt1 = NULL;
- self->opt2 = NULL;
- self->opt3 = NULL;
+ TRACE_CALL(__func__);
- return (PyObject*)self;
+ PyRemminaProtocolFeature* self;
+ self = (PyRemminaProtocolFeature*)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ self->id = 0;
+ self->type = 0;
+ self->opt1 = (PyGeneric*)Py_None;
+ self->opt1->raw = NULL;
+ self->opt1->type_hint = REMMINA_TYPEHINT_UNDEFINED;
+ self->opt2 = (PyGeneric*)Py_None;
+ self->opt2->raw = NULL;
+ self->opt2->type_hint = REMMINA_TYPEHINT_UNDEFINED;
+ self->opt3 = (PyGeneric*)Py_None;
+ self->opt3->raw = NULL;
+ self->opt3->type_hint = REMMINA_TYPEHINT_UNDEFINED;
+
+ return (PyObject*)self;
}
-static int python_protocol_feature_init(PyRemminaProtocolFeature *self, PyObject *args, PyObject *kwds)
+static int python_protocol_feature_init(PyRemminaProtocolFeature* self, PyObject* args, PyObject* kwargs)
{
- static char *kwlist[] = { "type", "id", "opt1", "opt2", "opt3", NULL };
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "type", "id", "opt1", "opt2", "opt3", NULL };
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|llOOO", kwlist
- ,&self->type
- ,&self->id
- ,&self->opt1
- ,&self->opt2
- ,&self->opt3))
- return -1;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|llOOO", kwlist, &self->type, &self->id, &self->opt1, &self
+ ->opt2, &self->opt3))
+ return -1;
- return 0;
+ return 0;
}
static PyTypeObject python_protocol_feature_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "remmina.Feature",
- .tp_doc = "Remmina Setting information",
- .tp_basicsize = sizeof(PyRemminaProtocolFeature),
- .tp_itemsize = 0,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_new = python_protocol_feature_new,
- .tp_init = python_protocol_feature_init,
- .tp_members = python_protocol_feature_members
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "remmina.ProtocolFeature",
+ .tp_doc = "Remmina Setting information",
+ .tp_basicsize = sizeof(PyRemminaProtocolFeature),
+ .tp_itemsize = 0,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = python_protocol_feature_new,
+ .tp_init = (initproc)python_protocol_feature_init,
+ .tp_members = python_protocol_feature_members
+};
+
+PyRemminaProtocolFeature* remmina_plugin_python_protocol_feature_new(void)
+{
+ PyRemminaProtocolFeature* feature = (PyRemminaProtocolFeature*)PyObject_New(PyRemminaProtocolFeature, &python_protocol_feature_type);
+ feature->id = 0;
+ feature->opt1 = remmina_plugin_python_generic_new();
+ feature->opt1->raw = NULL;
+ feature->opt1->type_hint = REMMINA_TYPEHINT_UNDEFINED;
+ feature->opt2 = remmina_plugin_python_generic_new();
+ feature->opt2->raw = NULL;
+ feature->opt2->type_hint = REMMINA_TYPEHINT_UNDEFINED;
+ feature->opt3 = remmina_plugin_python_generic_new();
+ feature->opt3->raw = NULL;
+ feature->opt3->type_hint = REMMINA_TYPEHINT_UNDEFINED;
+ feature->type = 0;
+ Py_IncRef((PyObject*)feature);
+ return feature;
+}
+
+
+// -- Python Type -> Screenshot Data
+
+static PyMemberDef python_screenshot_data_members[] = {
+ { "buffer", T_OBJECT, offsetof(PyRemminaPluginScreenshotData, buffer), 0, NULL },
+ { "width", T_INT, offsetof(PyRemminaPluginScreenshotData, width), 0, NULL },
+ { "height", T_INT, offsetof(PyRemminaPluginScreenshotData, height), 0, NULL },
+ { "bitsPerPixel", T_INT, offsetof(PyRemminaPluginScreenshotData, bitsPerPixel), 0, NULL },
+ { "bytesPerPixel", T_INT, offsetof(PyRemminaPluginScreenshotData, bytesPerPixel), 0, NULL },
+ { NULL }
+};
+
+PyObject* python_screenshot_data_new(PyTypeObject* type, PyObject* kws, PyObject* args)
+{
+ TRACE_CALL(__func__);
+
+ PyRemminaPluginScreenshotData* self;
+ self = (PyRemminaPluginScreenshotData*)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ self->buffer = (PyByteArrayObject*)PyObject_New(PyByteArrayObject, &PyByteArray_Type);
+ self->height = 0;
+ self->width = 0;
+ self->bitsPerPixel = 0;
+ self->bytesPerPixel = 0;
+
+ return (PyObject*)self;
+}
+
+static int python_screenshot_data_init(PyRemminaPluginScreenshotData* self, PyObject* args, PyObject* kwargs)
+{
+ TRACE_CALL(__func__);
+
+ g_printerr("Not to be initialized within Python!");
+ return -1;
+}
+
+static PyTypeObject python_screenshot_data_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "remmina.RemminaScreenshotData",
+ .tp_doc = "Remmina Screenshot Data",
+ .tp_basicsize = sizeof(PyRemminaPluginScreenshotData),
+ .tp_itemsize = 0,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = python_screenshot_data_new,
+ .tp_init = (initproc)python_screenshot_data_init,
+ .tp_members = python_screenshot_data_members
};
+PyRemminaPluginScreenshotData* remmina_plugin_python_screenshot_data_new(void)
+{
+ PyRemminaPluginScreenshotData* data = (PyRemminaPluginScreenshotData*)PyObject_New(PyRemminaPluginScreenshotData, &python_screenshot_data_type);
+ data->buffer = PyObject_New(PyByteArrayObject, &PyByteArray_Type);
+ Py_IncRef((PyObject*)data->buffer);
+ data->height = 0;
+ data->width = 0;
+ data->bitsPerPixel = 0;
+ data->bytesPerPixel = 0;
+ return data;
+}
+
+static PyObject* remmina_plugin_python_generic_to_int(PyGeneric* self, PyObject* args);
+static PyObject* remmina_plugin_python_generic_to_bool(PyGeneric* self, PyObject* args);
+static PyObject* remmina_plugin_python_generic_to_string(PyGeneric* self, PyObject* args);
+
+static void remmina_plugin_python_generic_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyMethodDef remmina_plugin_python_generic_methods[] = {
+ { "to_int", (PyCFunction)remmina_plugin_python_generic_to_int, METH_NOARGS, "" },
+ { "to_bool", (PyCFunction)remmina_plugin_python_generic_to_bool, METH_NOARGS, "" },
+ { "to_string", (PyCFunction)remmina_plugin_python_generic_to_string, METH_NOARGS, "" },
+ { NULL }
+};
+
+static PyMemberDef remmina_plugin_python_generic_members[] = {
+ { "raw", T_OBJECT, offsetof(PyGeneric, raw), 0, "" },
+ { NULL }
+};
+
+PyObject* remmina_plugin_python_generic_type_new(PyTypeObject* type, PyObject* kws, PyObject* args)
+{
+ TRACE_CALL(__func__);
+
+ PyGeneric* self;
+ self = (PyGeneric*)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ self->raw = Py_None;
+
+ return (PyObject*)self;
+}
+
+static int remmina_plugin_python_generic_init(PyGeneric* self, PyObject* args, PyObject* kwargs)
+{
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "raw", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &self->raw))
+ return -1;
+
+ return 0;
+}
+
+static PyTypeObject python_generic_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "remmina.Generic",
+ .tp_doc = "",
+ .tp_basicsize = sizeof(PyGeneric),
+ .tp_itemsize = 0,
+ .tp_dealloc = remmina_plugin_python_generic_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = remmina_plugin_python_generic_type_new,
+ .tp_init = (initproc)remmina_plugin_python_generic_init,
+ .tp_members = remmina_plugin_python_generic_members,
+ .tp_methods = remmina_plugin_python_generic_methods,
+};
+
+PyGeneric* remmina_plugin_python_generic_new(void)
+{
+ PyGeneric* generic = (PyGeneric*)PyObject_New(PyGeneric, &python_generic_type);
+ generic->raw = PyLong_FromLongLong(0LL);
+ Py_IncRef((PyObject*)generic);
+ return generic;
+}
+
+static PyObject* remmina_plugin_python_generic_to_int(PyGeneric* self, PyObject* args)
+{
+ SELF_CHECK();
+
+ if (self->raw == NULL)
+ {
+ return Py_None;
+ }
+ else if (self->type_hint == REMMINA_TYPEHINT_SIGNED)
+ {
+ return PyLong_FromLongLong((long long)self->raw);
+ }
+ else if (self->type_hint == REMMINA_TYPEHINT_UNSIGNED)
+ {
+ return PyLong_FromUnsignedLongLong((unsigned long long)self->raw);
+ }
+
+ return Py_None;
+}
+static PyObject* remmina_plugin_python_generic_to_bool(PyGeneric* self, PyObject* args)
+{
+ SELF_CHECK();
+
+ if (self->raw == NULL)
+ {
+ return Py_None;
+ }
+ else if (self->type_hint == REMMINA_TYPEHINT_BOOLEAN)
+ {
+ return PyBool_FromLong((long)self->raw);
+ }
+
+ return Py_None;
+}
+static PyObject* remmina_plugin_python_generic_to_string(PyGeneric* self, PyObject* args)
+{
+ SELF_CHECK();
+
+ if (self->raw == NULL)
+ {
+ return Py_None;
+ }
+ else if (self->type_hint == REMMINA_TYPEHINT_STRING)
+ {
+ return PyUnicode_FromString((const char*)self->raw);
+ }
+
+ return Py_None;
+}
/**
- * @brief Is called from the Python engine when it initializes the 'remmina' module.
+ * Is called from the Python engine when it initializes the 'remmina' module.
* @details This function is only called by the Python engine!
*/
-static PyMODINIT_FUNC remmina_plugin_python_module_initialize(void)
-{
- TRACE_CALL(__func__);
- if (PyType_Ready(&python_protocol_setting_type) < 0) {
- g_printerr("Error initializing remmina.Setting!\n");
- PyErr_Print();
- return NULL;
- }
- if (PyType_Ready(&python_protocol_feature_type) < 0) {
- g_printerr("Error initializing remmina.Feature!\n");
- PyErr_Print();
- return NULL;
- }
-
- PyObject* module = PyModule_Create(&remmina_python_module_type);
- if (!module) {
- g_printerr("Error creating module 'remmina'!\n");
- PyErr_Print();
- return NULL;
- }
+PyMODINIT_FUNC remmina_plugin_python_module_initialize(void)
+{
+ TRACE_CALL(__func__);
+
+ if (PyType_Ready(&python_screenshot_data_type) < 0)
+ {
+ PyErr_Print();
+ return NULL;
+ }
+
+ if (PyType_Ready(&python_generic_type) < 0)
+ {
+ PyErr_Print();
+ return NULL;
+ }
+
+ if (PyType_Ready(&python_protocol_setting_type) < 0)
+ {
+ PyErr_Print();
+ return NULL;
+ }
+
+ if (PyType_Ready(&python_protocol_feature_type) < 0)
+ {
+ PyErr_Print();
+ return NULL;
+ }
+
+ remmina_plugin_python_protocol_widget_type_ready();
+ remmina_plugin_python_remmina_init_types();
+
+ PyObject* module = PyModule_Create(&remmina_python_module_type);
+ if (!module)
+ {
+ PyErr_Print();
+ return NULL;
+ }
+
+ PyModule_AddIntConstant(module, "BUTTONS_CLOSE", (long)GTK_BUTTONS_CLOSE);
+ PyModule_AddIntConstant(module, "BUTTONS_NONE", (long)GTK_BUTTONS_NONE);
+ PyModule_AddIntConstant(module, "BUTTONS_OK", (long)GTK_BUTTONS_OK);
+ PyModule_AddIntConstant(module, "BUTTONS_CLOSE", (long)GTK_BUTTONS_CLOSE);
+ PyModule_AddIntConstant(module, "BUTTONS_CANCEL", (long)GTK_BUTTONS_CANCEL);
+ PyModule_AddIntConstant(module, "BUTTONS_YES_NO", (long)GTK_BUTTONS_YES_NO);
+ PyModule_AddIntConstant(module, "BUTTONS_OK_CANCEL", (long)GTK_BUTTONS_OK_CANCEL);
+
+ PyModule_AddIntConstant(module, "MESSAGE_INFO", (long)GTK_MESSAGE_INFO);
+ PyModule_AddIntConstant(module, "MESSAGE_WARNING", (long)GTK_MESSAGE_WARNING);
+ PyModule_AddIntConstant(module, "MESSAGE_QUESTION", (long)GTK_MESSAGE_QUESTION);
+ PyModule_AddIntConstant(module, "MESSAGE_ERROR", (long)GTK_MESSAGE_ERROR);
+ PyModule_AddIntConstant(module, "MESSAGE_OTHER", (long)GTK_MESSAGE_OTHER);
PyModule_AddIntConstant(module, "PROTOCOL_SETTING_TYPE_SERVER", (long)REMMINA_PROTOCOL_SETTING_TYPE_SERVER);
PyModule_AddIntConstant(module, "PROTOCOL_SETTING_TYPE_PASSWORD", (long)REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD);
@@ -497,6 +680,7 @@ static PyMODINIT_FUNC remmina_plugin_python_module_initialize(void)
PyModule_AddIntConstant(module, "PROTOCOL_SETTING_TYPE_CHECK", (long)REMMINA_PROTOCOL_SETTING_TYPE_CHECK);
PyModule_AddIntConstant(module, "PROTOCOL_SETTING_TYPE_FILE", (long)REMMINA_PROTOCOL_SETTING_TYPE_FILE);
PyModule_AddIntConstant(module, "PROTOCOL_SETTING_TYPE_FOLDER", (long)REMMINA_PROTOCOL_SETTING_TYPE_FOLDER);
+ PyModule_AddIntConstant(module, "PROTOCOL_FEATURE_TYPE_MULTIMON", (long)REMMINA_PROTOCOL_FEATURE_TYPE_MULTIMON);
PyModule_AddIntConstant(module, "PROTOCOL_FEATURE_TYPE_PREF", (long)REMMINA_PROTOCOL_FEATURE_TYPE_PREF);
PyModule_AddIntConstant(module, "PROTOCOL_FEATURE_TYPE_TOOL", (long)REMMINA_PROTOCOL_FEATURE_TYPE_TOOL);
@@ -511,311 +695,531 @@ static PyMODINIT_FUNC remmina_plugin_python_module_initialize(void)
PyModule_AddIntConstant(module, "PROTOCOL_SSH_SETTING_REVERSE_TUNNEL", (long)REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL);
PyModule_AddIntConstant(module, "PROTOCOL_SSH_SETTING_SFTP", (long)REMMINA_PROTOCOL_SSH_SETTING_SFTP);
- PyModule_AddIntConstant(module, "PROTOCOL_FEATURE_PREF_RADIO", (long)REMMINA_PROTOCOL_FEATURE_PREF_RADIO);
- PyModule_AddIntConstant(module, "PROTOCOL_FEATURE_PREF_CHECK", (long)REMMINA_PROTOCOL_FEATURE_PREF_CHECK);
-
- if (PyModule_AddObject(module, "Setting", (PyObject*)&python_protocol_setting_type) < 0) {
- Py_DECREF(&python_protocol_setting_type);
- Py_DECREF(module);
- PyErr_Print();
- return NULL;
- }
-
- Py_INCREF(&python_protocol_feature_type);
- if (PyModule_AddObject(module, "Feature", (PyObject*)&python_protocol_feature_type) < 0) {
- Py_DECREF(&python_protocol_setting_type);
- Py_DECREF(&python_protocol_feature_type);
- Py_DECREF(module);
- PyErr_Print();
- return NULL;
- }
-
- return module;
+ PyModule_AddIntConstant(module, "PROTOCOL_FEATURE_PREF_RADIO", (long)REMMINA_PROTOCOL_FEATURE_PREF_RADIO);
+ PyModule_AddIntConstant(module, "PROTOCOL_FEATURE_PREF_CHECK", (long)REMMINA_PROTOCOL_FEATURE_PREF_CHECK);
+
+ PyModule_AddIntConstant(module, "MESSAGE_PANEL_FLAG_USERNAME", REMMINA_MESSAGE_PANEL_FLAG_USERNAME);
+ PyModule_AddIntConstant(module, "MESSAGE_PANEL_FLAG_USERNAME_READONLY", REMMINA_MESSAGE_PANEL_FLAG_USERNAME_READONLY);
+ PyModule_AddIntConstant(module, "MESSAGE_PANEL_FLAG_DOMAIN", REMMINA_MESSAGE_PANEL_FLAG_DOMAIN);
+ PyModule_AddIntConstant(module, "MESSAGE_PANEL_FLAG_SAVEPASSWORD", REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD);
+
+ if (PyModule_AddObject(module, "Setting", (PyObject*)&python_protocol_setting_type) < 0)
+ {
+ Py_DECREF(&python_protocol_setting_type);
+ Py_DECREF(module);
+ PyErr_Print();
+ return NULL;
+ }
+
+ Py_INCREF(&python_protocol_feature_type);
+ if (PyModule_AddObject(module, "Feature", (PyObject*)&python_protocol_feature_type) < 0)
+ {
+ Py_DECREF(&python_protocol_setting_type);
+ Py_DECREF(&python_protocol_feature_type);
+ Py_DECREF(module);
+ PyErr_Print();
+ return NULL;
+ }
+
+ return module;
}
/**
- * @brief Initializes all globals and registers the 'remmina' module in the Python engine.
+ * Initializes all globals and registers the 'remmina' module in the Python engine.
* @details This
*/
void remmina_plugin_python_module_init(void)
{
TRACE_CALL(__func__);
- remmina_plugin_registry = g_ptr_array_new();
- if (PyImport_AppendInittab("remmina", remmina_plugin_python_module_initialize)) {
- g_print("Error initializing remmina module for python!\n");
- PyErr_Print();
- }
-}
-gboolean remmina_plugin_python_check_mandatory_member(PyObject* instance, const gchar* member)
-{
- if (PyObject_HasAttrString(instance, member))
- return TRUE;
-
- g_printerr("Missing mandatory member in Python plugin instance: %s\n", member);
- return FALSE;
+ if (PyImport_AppendInittab("remmina", remmina_plugin_python_module_initialize))
+ {
+ PyErr_Print();
+ exit(1);
+ }
+
+ remmina_plugin_python_entry_init();
+ remmina_plugin_python_protocol_init();
+ remmina_plugin_python_tool_init();
+ remmina_plugin_python_pref_init();
+ remmina_plugin_python_secret_init();
+ remmina_plugin_python_file_init();
}
-static PyObject* remmina_plugin_python_log_printf_wrapper(PyObject* self, PyObject* msg)
+gboolean remmina_plugin_python_check_mandatory_member(PyObject* instance, const gchar* member)
{
TRACE_CALL(__func__);
- char* fmt = NULL;
- if (PyArg_ParseTuple(msg, "s", &fmt)) {
- remmina_log_printf(fmt);
- } else {
- g_print("Failed to load.\n");
- PyErr_Print();
- return Py_None;
- }
+ if (PyObject_HasAttrString(instance, member))
+ {
+ return TRUE;
+ }
- return Py_None;
+ g_printerr("Missing mandatory member '%s' in Python plugin instance!\n", member);
+ return FALSE;
}
static PyObject* remmina_register_plugin_wrapper(PyObject* self, PyObject* plugin_instance)
{
TRACE_CALL(__func__);
- if (plugin_instance) {
- if (!remmina_plugin_python_check_mandatory_member(plugin_instance, "name")
- || !remmina_plugin_python_check_mandatory_member(plugin_instance, "version")) {
- return NULL;
- }
-
- /* Protocol plugin definition and features */
- RemminaPlugin* remmina_plugin = NULL;
- gboolean is_protocol_plugin = FALSE;
-
- const gchar* pluginType = PyUnicode_AsUTF8(PyObject_GetAttrString(plugin_instance, "type"));
-
- if (g_str_equal(pluginType, "protocol")) {
- remmina_plugin = remmina_plugin_python_create_protocol_plugin(plugin_instance);
- is_protocol_plugin = TRUE;
- } else if (g_str_equal(pluginType, "entry")) {
- remmina_plugin = remmina_plugin_python_create_entry_plugin(plugin_instance);
- } else if (g_str_equal(pluginType, "file")) {
- remmina_plugin = remmina_plugin_python_create_file_plugin(plugin_instance);
- } else if (g_str_equal(pluginType, "tool")) {
- remmina_plugin = remmina_plugin_python_create_tool_plugin(plugin_instance);
- } else if (g_str_equal(pluginType, "pref")) {
- remmina_plugin = remmina_plugin_python_create_pref_plugin(plugin_instance);
- } else if (g_str_equal(pluginType, "secret")) {
- remmina_plugin = remmina_plugin_python_create_secret_plugin(plugin_instance);
- } else {
- g_printerr("Unknown plugin type: %s\n", pluginType);
- }
-
- if (remmina_plugin) {
- g_ptr_array_add(remmina_plugin_registry, plugin_instance);
- remmina_plugin_manager_service.register_plugin((RemminaPlugin *)remmina_plugin);
-
- PyPlugin* plugin = (PyPlugin*)malloc(sizeof(PyPlugin));
- Py_INCREF(plugin);
- plugin->protocol_plugin = remmina_plugin;
- plugin->generic_plugin = remmina_plugin;
- plugin->entry_plugin = NULL;
- plugin->file_plugin = NULL;
- plugin->pref_plugin = NULL;
- plugin->secret_plugin = NULL;
- plugin->tool_plugin = NULL;
- plugin->pythonInstance = plugin_instance;
- if (is_protocol_plugin) {
- plugin->gp = malloc(sizeof(PyRemminaProtocolWidget));
- plugin->gp->gp = NULL;
- }
- g_ptr_array_add(remmina_plugin_registry, plugin);
- }
- }
-
- return Py_None;
+ if (plugin_instance)
+ {
+ if (!remmina_plugin_python_check_mandatory_member(plugin_instance, "name")
+ || !remmina_plugin_python_check_mandatory_member(plugin_instance, "version"))
+ {
+ return NULL;
+ }
+
+ /* Protocol plugin definition and features */
+ const gchar* pluginType = PyUnicode_AsUTF8(PyObject_GetAttrString(plugin_instance, "type"));
+
+ RemminaPlugin* remmina_plugin = NULL;
+
+ PyPlugin* plugin = (PyPlugin*)remmina_plugin_python_malloc(sizeof(PyPlugin));
+ plugin->instance = plugin_instance;
+ Py_INCREF(plugin_instance);
+ plugin->protocol_plugin = NULL;
+ plugin->entry_plugin = NULL;
+ plugin->file_plugin = NULL;
+ plugin->pref_plugin = NULL;
+ plugin->secret_plugin = NULL;
+ plugin->tool_plugin = NULL;
+ g_print("New Python plugin registered: %ld\n", PyObject_Hash(plugin_instance));
+
+ if (g_str_equal(pluginType, "protocol"))
+ {
+ remmina_plugin = remmina_plugin_python_create_protocol_plugin(plugin);
+ }
+ else if (g_str_equal(pluginType, "entry"))
+ {
+ remmina_plugin = remmina_plugin_python_create_entry_plugin(plugin);
+ }
+ else if (g_str_equal(pluginType, "file"))
+ {
+ remmina_plugin = remmina_plugin_python_create_file_plugin(plugin);
+ }
+ else if (g_str_equal(pluginType, "tool"))
+ {
+ remmina_plugin = remmina_plugin_python_create_tool_plugin(plugin);
+ }
+ else if (g_str_equal(pluginType, "pref"))
+ {
+ remmina_plugin = remmina_plugin_python_create_pref_plugin(plugin);
+ }
+ else if (g_str_equal(pluginType, "secret"))
+ {
+ remmina_plugin = remmina_plugin_python_create_secret_plugin(plugin);
+ }
+ else
+ {
+ g_printerr("Unknown plugin type: %s\n", pluginType);
+ }
+
+ if (remmina_plugin)
+ {
+ if (remmina_plugin->type == REMMINA_PLUGIN_TYPE_PROTOCOL)
+ {
+ plugin->gp = remmina_plugin_python_protocol_widget_create();
+ }
+
+ remmina_plugin_manager_service.register_plugin((RemminaPlugin*)remmina_plugin);
+ }
+ }
+
+ return Py_None;
}
static PyObject* remmina_file_get_datadir_wrapper(PyObject* self, PyObject* plugin)
{
- return Py_None;
+ TRACE_CALL(__func__);
+
+ PyObject* result = Py_None;
+ const gchar* datadir = remmina_file_get_datadir();
+
+ if (datadir)
+ {
+ result = PyUnicode_FromFormat("%s", datadir);
+ }
+
+ remmina_plugin_python_check_error();
+ return result;
}
-static PyObject* remmina_file_new_wrapper(PyObject* self, PyObject* plugin)
+static PyObject* remmina_file_new_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
{
- return Py_None;
+ TRACE_CALL(__func__);
+
+ RemminaFile* file = remmina_file_new();
+ if (file)
+ {
+ return (PyObject*)remmina_plugin_python_remmina_file_to_python(file);
+ }
+
+ remmina_plugin_python_check_error();
+ return Py_None;
}
-static PyObject* remmina_pref_set_value_wrapper(PyObject* self, PyObject* plugin)
+static PyObject* remmina_pref_set_value_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
{
- return Py_None;
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "key", "value", NULL };
+ gchar* key, * value;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
+ {
+ return Py_None;
+ }
+
+ if (key)
+ {
+ remmina_pref_set_value(key, value);
+ }
+
+ remmina_plugin_python_check_error();
+ return Py_None;
}
-static PyObject* remmina_pref_get_value_wrapper(PyObject* self, PyObject* plugin)
+static PyObject* remmina_pref_get_value_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ static char* kwlist[] = { "key", NULL };
+ gchar* key;
+ PyObject* result = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &key))
+ {
+ return Py_None;
+ }
+
+ if (key)
+ {
+ const gchar* value = remmina_pref_get_value(key);
+ if (value)
+ {
+ result = PyUnicode_FromFormat("%s", result);
+ }
+ }
+
+ remmina_plugin_python_check_error();
+ return result;
+}
static PyObject* remmina_pref_get_scale_quality_wrapper(PyObject* self, PyObject* plugin)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ PyObject* result = PyLong_FromLong(remmina_pref_get_scale_quality());
+ remmina_plugin_python_check_error();
+ return result;
+}
static PyObject* remmina_pref_get_sshtunnel_port_wrapper(PyObject* self, PyObject* plugin)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ PyObject* result = PyLong_FromLong(remmina_pref_get_sshtunnel_port());
+ remmina_plugin_python_check_error();
+ return result;
+}
static PyObject* remmina_pref_get_ssh_loglevel_wrapper(PyObject* self, PyObject* plugin)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ PyObject* result = PyLong_FromLong(remmina_pref_get_ssh_loglevel());
+ remmina_plugin_python_check_error();
+ return result;
+}
static PyObject* remmina_pref_get_ssh_parseconfig_wrapper(PyObject* self, PyObject* plugin)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ PyObject* result = PyLong_FromLong(remmina_pref_get_ssh_parseconfig());
+ remmina_plugin_python_check_error();
+ return result;
+}
-static PyObject* remmina_pref_keymap_get_keyval_wrapper(PyObject* self, PyObject* plugin)
+static PyObject* remmina_pref_keymap_get_keyval_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ static char* kwlist[] = { "keymap", "keyval", NULL };
+ gchar* keymap;
+ guint keyval;
+ PyObject* result = Py_None;
-static PyObject* remmina_log_print_wrapper(PyObject* self, PyObject* plugin)
-{
- return Py_None;
-}
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sl", kwlist, &keymap, &keyval))
+ {
+ return PyLong_FromLong(-1);
+ }
+ if (keymap)
+ {
+ const guint value = remmina_pref_keymap_get_keyval(keymap, keyval);
+ result = PyLong_FromUnsignedLong(value);
+ }
-static PyObject* remmina_log_printf_wrapper(PyObject* self, PyObject* plugin)
-{
- return Py_None;
+ remmina_plugin_python_check_error();
+ return result;
}
-
-static PyObject* remmina_widget_pool_register_wrapper(PyObject* self, PyObject* plugin)
+static PyObject* remmina_plugin_python_log_print_wrapper(PyObject* self, PyObject* args)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ gchar* text;
+ if (!PyArg_ParseTuple(args, "s", &text) || !text)
+ {
+ return Py_None;
+ }
+
+ remmina_log_print(text);
+ return Py_None;
+}
-static PyObject* rcw_open_from_file_full_wrapper(PyObject* self, PyObject* plugin)
+static PyObject* remmina_plugin_python_debug_wrapper(PyObject* self, PyObject* args)
{
- return Py_None;
+ TRACE_CALL(__func__);
+
+ gchar* text;
+ if (!PyArg_ParseTuple(args, "s", &text) || !text)
+ {
+ return Py_None;
+ }
+
+ remmina_plugin_manager_service._remmina_debug("python", "%s", text);
+ return Py_None;
}
+static PyObject* remmina_widget_pool_register_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "widget", NULL };
+ PyObject* widget;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &widget) && widget)
+ {
+ remmina_widget_pool_register(get_pywidget(widget));
+ }
+
+ return Py_None;
+}
-static PyObject* remmina_public_get_server_port_wrapper(PyObject* self, PyObject* plugin)
+static PyObject* rcw_open_from_file_full_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
{
- return Py_None;
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "remminafile", "data", "handler", NULL };
+ PyObject* pyremminafile;
+ PyObject* data;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwargs, "OOO", kwlist, &pyremminafile, &data) && pyremminafile && data)
+ {
+ rcw_open_from_file_full((RemminaFile*)pyremminafile, NULL, (void*)data, NULL);
+ }
+
+ return Py_None;
}
+static PyObject* remmina_public_get_server_port_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "server", "defaultport", "host", "port", NULL };
+ gchar* server;
+ gint defaultport;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwargs, "slsl", kwlist, &server, &defaultport) && server)
+ {
+ gchar* host;
+ gint port;
+ remmina_public_get_server_port(server, defaultport, &host, &port);
+
+ PyObject* result = PyList_New(2);
+ PyList_Append(result, PyUnicode_FromString(host));
+ PyList_Append(result, PyLong_FromLong(port));
+ return PyList_AsTuple(result);
+ }
+
+ return Py_None;
+}
static PyObject* remmina_masterthread_exec_is_main_thread_wrapper(PyObject* self, PyObject* plugin)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ return PyBool_FromLong(remmina_masterthread_exec_is_main_thread());
+}
static PyObject* remmina_gtksocket_available_wrapper(PyObject* self, PyObject* plugin)
{
- return Py_None;
-}
+ TRACE_CALL(__func__);
+ return PyBool_FromLong(remmina_gtksocket_available());
+}
-static PyObject* remmina_protocol_widget_get_profile_remote_heigh_wrapper(PyObject* self, PyObject* plugin)
+static PyObject*
+remmina_protocol_widget_get_profile_remote_height_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
{
- return Py_None;
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "widget", NULL };
+ PyPlugin* plugin;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &plugin) && plugin && plugin->gp)
+ {
+ remmina_protocol_widget_get_profile_remote_height(plugin->gp->gp);
+ }
+
+ return Py_None;
}
+static PyObject*
+remmina_protocol_widget_get_profile_remote_width_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "widget", NULL };
+ PyPlugin* plugin;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &plugin) && plugin && plugin->gp)
+ {
+ remmina_protocol_widget_get_profile_remote_width(plugin->gp->gp);
+ }
+
+ return Py_None;
+}
-static PyObject* remmina_protocol_widget_get_profile_remote_width_wrapper(PyObject* self, PyObject* plugin)
+void remmina_plugin_python_to_protocol_setting(RemminaProtocolSetting* dest, PyObject* setting)
{
- return Py_None;
+ TRACE_CALL(__func__);
+
+ PyRemminaProtocolSetting* src = (PyRemminaProtocolSetting*)setting;
+ Py_INCREF(setting);
+ dest->name = src->name;
+ dest->label = src->label;
+ dest->compact = src->compact;
+ dest->type = src->settingType;
+ dest->validator = NULL;
+ dest->validator_data = NULL;
+ remmina_plugin_python_to_generic(src->opt1, &dest->opt1);
+ remmina_plugin_python_to_generic(src->opt2, &dest->opt2);
}
-static gboolean remmina_plugin_equal(gconstpointer lhs, gconstpointer rhs)
+void remmina_plugin_python_to_protocol_feature(RemminaProtocolFeature* dest, PyObject* feature)
{
- if (lhs && ((PyPlugin*)lhs)->generic_plugin && rhs)
- return g_str_equal(((PyPlugin*)lhs)->generic_plugin->name, ((gchar*)rhs));
- else
- return lhs == rhs;
+ TRACE_CALL(__func__);
+
+ PyRemminaProtocolFeature* src = (PyRemminaProtocolFeature*)feature;
+ Py_INCREF(feature);
+ dest->id = src->id;
+ dest->type = src->type;
+ dest->opt1 = src->opt1->raw;
+ dest->opt1_type_hint = src->opt1->type_hint;
+ dest->opt2 = src->opt2->raw;
+ dest->opt2_type_hint = src->opt2->type_hint;
+ dest->opt3 = src->opt3->raw;
+ dest->opt3_type_hint = src->opt3->type_hint;
}
-PyPlugin* remmina_plugin_python_module_get_plugin(RemminaProtocolWidget* gp)
+PyObject* remmina_plugin_python_show_dialog_wrapper(PyObject* self, PyObject* args, PyObject* kwargs)
{
- static PyPlugin* cached_plugin = NULL;
- static RemminaProtocolWidget* cached_widget = NULL;
- if (cached_widget && gp == cached_widget) {
- return cached_plugin;
- }
- guint index = 0;
- for (int i = 0; i < remmina_plugin_registry->len; ++i) {
- PyPlugin* plugin = (PyPlugin*)g_ptr_array_index(remmina_plugin_registry, i);
- if (g_str_equal(gp->plugin->name, plugin->generic_plugin->name))
- return cached_plugin = plugin;
- }
- g_printerr("[%s:%s]: No plugin named %s!\n", __FILE__, __LINE__, gp->plugin->name);
- return NULL;
+ TRACE_CALL(__func__);
+
+ static char* kwlist[] = { "type", "buttons", "message", NULL };
+ GtkMessageType msgType;
+ GtkButtonsType btnType;
+ gchar* message;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lls", kwlist, &msgType, &btnType, &message))
+ {
+ return PyLong_FromLong(-1);
+ }
+
+ remmina_main_show_dialog(msgType, btnType, message);
+
+ return Py_None;
}
-static void GetGeneric(PyObject* field, gpointer* target)
+PyObject* remmina_plugin_python_get_mainwindow_wrapper(PyObject* self, PyObject* args)
{
- if (!field || field == Py_None) {
- *target = NULL;
- return;
- }
- Py_INCREF(field);
- if (PyUnicode_Check(field)) {
- Py_ssize_t len = PyUnicode_GetLength(field);
+ TRACE_CALL(__func__);
+
+ GtkWindow* result = remmina_main_get_window();
- if (len == 0) {
- *target = "";
- } else {
- gchar* tmp = g_malloc(sizeof(char) * (len + 1));
- *(tmp + len) = 0;
- memcpy(tmp, PyUnicode_AsUTF8(field), len);
- *target = tmp;
- }
+ if (!result)
+ {
+ return Py_None;
+ }
- } else if (PyLong_Check(field)) {
- *target = malloc(sizeof(long));
- *target = PyLong_AsLong(field);
- } else if (PyTuple_Check(field)) {
- Py_ssize_t len = PyTuple_Size(field);
- if (len) {
- gpointer* dest = (gpointer*)malloc(sizeof(gpointer) * (len + 1));
- memset(dest, 0, sizeof(gpointer) * (len + 1));
+ return (PyObject*)new_pywidget((GObject*)result);
+}
- for (Py_ssize_t i = 0; i < len; ++i) {
- PyObject* item = PyTuple_GetItem(field, i);
- GetGeneric(item, dest + i);
- }
+static PyObject* remmina_protocol_plugin_signal_connection_closed_wrapper(PyObject* self, PyObject* args)
+{
+ TRACE_CALL(__func__);
- *target = dest;
- }
- }
- Py_DECREF(field);
+ PyObject* pygp = NULL;
+ if (!PyArg_ParseTuple(args, "O", &pygp) || !pygp)
+ {
+ g_printerr("Please provide the Remmina protocol widget instance!");
+ return Py_None;
+ }
+
+ remmina_plugin_manager_service.protocol_plugin_signal_connection_closed(((PyRemminaProtocolWidget*)pygp)->gp);
+ return Py_None;
}
-void ToRemminaProtocolSetting(RemminaProtocolSetting* dest, PyObject* setting)
+static PyObject* remmina_protocol_plugin_init_auth_wrapper(PyObject* module, PyObject* args, PyObject* kwds)
{
- PyRemminaProtocolSetting* src = (PyRemminaProtocolSetting*)setting;
- Py_INCREF(setting);
- dest->name = src->name;
- dest->label = src->label;
- dest->compact = src->compact;
- dest->type = src->settingType;
- GetGeneric(src->opt1, &dest->opt1);
- GetGeneric(src->opt2, &dest->opt2);
+ TRACE_CALL(__func__);
+
+ static gchar* keyword_list[] = { "widget", "flags", "title", "default_username", "default_password",
+ "default_domain", "password_prompt" };
+
+ PyRemminaProtocolWidget* self;
+ gint pflags = 0;
+ gchar* title, * default_username, * default_password, * default_domain, * password_prompt;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "Oisssss", keyword_list, &self, &pflags, &title, &default_username,
+ &default_password, &default_domain, &password_prompt))
+ {
+ if (pflags != 0 && !(pflags & REMMINA_MESSAGE_PANEL_FLAG_USERNAME)
+ && !(pflags & REMMINA_MESSAGE_PANEL_FLAG_USERNAME_READONLY)
+ && !(pflags & REMMINA_MESSAGE_PANEL_FLAG_DOMAIN)
+ && !(pflags & REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD))
+ {
+ g_printerr("panel_auth(pflags, title, default_username, default_password, default_domain, password_prompt): "
+ "%d is not a known value for RemminaMessagePanelFlags!\n", pflags);
+ }
+ else
+ {
+ return Py_BuildValue("i", remmina_protocol_widget_panel_auth(self
+ ->gp, pflags, title, default_username, default_password, default_domain, password_prompt));
+ }
+ }
+ else
+ {
+ g_printerr("panel_auth(pflags, title, default_username, default_password, default_domain, password_prompt): Error parsing arguments!\n");
+ PyErr_Print();
+ }
+ return Py_None;
}
-void ToRemminaProtocolFeature(RemminaProtocolFeature* dest, PyObject* feature) {
- PyRemminaProtocolFeature* src = (PyRemminaProtocolFeature*)feature;
- Py_INCREF(feature);
- dest->id = src->id;
- dest->type = src->type;
- GetGeneric(src->opt1, &dest->opt1);
- GetGeneric(src->opt2, &dest->opt2);
- GetGeneric(src->opt3, &dest->opt3);
+static PyObject* remmina_protocol_plugin_signal_connection_opened_wrapper(PyObject* self, PyObject* args)
+{
+ PyObject* pygp = NULL;
+ if (!PyArg_ParseTuple(args, "O", &pygp) || !pygp)
+ {
+ g_printerr("Please provide the Remmina protocol widget instance!");
+ return Py_None;
+ }
+
+ remmina_plugin_manager_service.protocol_plugin_signal_connection_opened(((PyRemminaProtocolWidget*)pygp)->gp);
+ return Py_None;
}
diff --git a/src/remmina_plugin_python_remmina.h b/src/remmina_plugin_python_remmina.h
index d3bfbbd7b..25869d58a 100644
--- a/src/remmina_plugin_python_remmina.h
+++ b/src/remmina_plugin_python_remmina.h
@@ -29,55 +29,64 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
- *
*/
-#pragma once
-
/**
- * @brief The Python abstraction of the protocol widget struct.
+ * @file remmina_plugin_python_remmina.h
*
- * @details This struct is responsible to provide the same accessibility to the protocol widget for Python as for
- * native plugins.
- */
-typedef struct {
- PyObject_HEAD
- RemminaProtocolWidget* gp;
-} PyRemminaProtocolWidget;
-
-/**
- * @brief Maps an instance of a Python plugin to a Remmina one.
+ * @brief Contains the implementation of the Python module 'remmina', provided to interface with the application from
+ * the Python plugin source.
*
- * @details This is used to map a Python plugin instance to the Remmina plugin one. Also instance specific data as the
- * protocol widget are stored in this struct.
+ * @detail In contrast to the wrapper functions that exist in the plugin specialisation files (e.g.
+ * remmina_plugin_python_protocol.c or remmina_plugin_python_entry.c), this file contains the API for the
+ * communication in the direction, from Python to Remmina. This means, if in the Python plugin a function
+ * is called, that is defined in Remmina, C code, at least in this file, is executed.
*/
-typedef struct {
- PyObject *pythonInstance;
- RemminaProtocolPlugin *protocol_plugin;
- RemminaFilePlugin* file_plugin;
- RemminaSecretPlugin* secret_plugin;
- RemminaToolPlugin* tool_plugin;
- RemminaEntryPlugin* entry_plugin;
- RemminaPrefPlugin* pref_plugin;
- RemminaPlugin* generic_plugin;
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- PyRemminaProtocolWidget *gp;
-} PyPlugin;
+#include "remmina_plugin_python_protocol_widget.h"
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+G_BEGIN_DECLS
/**
- * @brief Initializes the 'remmina' module in the Python engine.
+ * Initializes the 'remmina' module in the Python engine.
*/
void remmina_plugin_python_module_init(void);
/**
- * @brief Returns a pointer to the Python instance, mapped to the RemminaProtocolWidget or null if not found.
+ * @brief Returns a pointer to the Python instance, mapped to the RemminaProtocolWidget or null if not found.
+ *
+ * @param gp The widget that is owned by the plugin that should be found.
*
- * @details Remmina expects this callback function to be part of one plugin, which is the reason no instance information is explicitly passed. To bridge
- * that, this function can be used to retrieve the very plugin instance owning the given RemminaProtocolWidget.
+ * @details Remmina expects this callback function to be part of one plugin, which is the reason no instance information
+ * is explicitly passed. To bridge that, this function can be used to retrieve the very plugin instance owning
+ * the given RemminaProtocolWidget.
*/
PyPlugin* remmina_plugin_python_module_get_plugin(RemminaProtocolWidget* gp);
-void ToRemminaProtocolSetting(RemminaProtocolSetting* dest, PyObject* setting);
+/**
+ * @brief Converts the PyObject to RemminaProtocolSetting.
+ *
+ * @param dest A target for the converted value.
+ * @param setting The source value to convert.
+ */
+void remmina_plugin_python_to_protocol_setting(RemminaProtocolSetting* dest, PyObject* setting);
+
+/**
+ * @brief Converts the PyObject to RemminaProtocolFeature.
+ *
+ * @param dest A target for the converted value.
+ * @param setting The source value to convert.
+ */
+void remmina_plugin_python_to_protocol_feature(RemminaProtocolFeature* dest, PyObject* feature);
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_remmina_file.c b/src/remmina_plugin_python_remmina_file.c
index 6de367818..1c417129a 100644
--- a/src/remmina_plugin_python_remmina_file.c
+++ b/src/remmina_plugin_python_remmina_file.c
@@ -29,132 +29,177 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
- *
*/
-#include <glib.h>
-#include <gtk/gtk.h>
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N C L U D E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "remmina/remmina_trace_calls.h"
#include "remmina_file.h"
+#include "remmina_plugin_python_common.h"
#include "remmina_plugin_python_remmina_file.h"
-typedef struct {
- PyObject_HEAD
- RemminaFile* file;
-} PyRemminaFile;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// D E C L A R A T I O N S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static PyObject* file_get_path(PyRemminaFile* self, PyObject* args);
static PyObject* file_set_setting(PyRemminaFile* self, PyObject* args, PyObject* kwds);
static PyObject* file_get_setting(PyRemminaFile* self, PyObject* args, PyObject* kwds);
static PyObject* file_get_secret(PyRemminaFile* self, PyObject* setting);
static PyObject* file_unsave_passwords(PyRemminaFile* self, PyObject* args);
-static void file_dealloc(PyObject* self) { PyObject_Del(self); }
+
+static void file_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
static PyMethodDef python_remmina_file_type_methods[] = {
- { "get_path", (PyCFunction)file_get_path, METH_NOARGS, "" },
- { "set_setting", (PyCFunctionWithKeywords)file_set_setting, METH_VARARGS | METH_KEYWORDS, "Set file setting" },
- { "get_setting", (PyCFunctionWithKeywords)file_set_setting, METH_VARARGS | METH_KEYWORDS, "Get file setting" },
- { "get_secret", (PyCFunction)file_get_secret, METH_VARARGS, "Get secret file setting" },
- { "unsave_passwords", (PyCFunction)file_unsave_passwords },
- {NULL}
+ { "get_path", (PyCFunction)file_get_path, METH_NOARGS, "" },
+ { "set_setting", (PyCFunction)file_set_setting, METH_VARARGS | METH_KEYWORDS, "Set file setting" },
+ { "get_setting", (PyCFunction)file_get_setting, METH_VARARGS | METH_KEYWORDS, "Get file setting" },
+ { "get_secret", (PyCFunction)file_get_secret, METH_VARARGS, "Get secret file setting" },
+ { "unsave_passwords", (PyCFunction)file_unsave_passwords, METH_NOARGS, "" },
+ { NULL }
};
/**
* @brief The definition of the Python module 'remmina'.
*/
static PyTypeObject python_remmina_file_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "remmina.RemminaFileType",
- .tp_doc = "",
- .tp_basicsize = sizeof(PyRemminaFile),
- .tp_itemsize = 0,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_methods = python_remmina_file_type_methods,
- .tp_dealloc = file_dealloc
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "remmina.RemminaFile",
+ .tp_doc = "",
+ .tp_basicsize = sizeof(PyRemminaFile),
+ .tp_itemsize = 0,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = python_remmina_file_type_methods,
+ .tp_dealloc = file_dealloc
};
-PyObject* remmina_plugin_python_remmina_file_to_python(RemminaFile* file)
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void remmina_plugin_python_remmina_init_types(void)
+{
+ PyType_Ready(&python_remmina_file_type);
+}
+
+PyRemminaFile* remmina_plugin_python_remmina_file_to_python(RemminaFile* file)
{
TRACE_CALL(__func__);
- PyRemminaFile* result = PyObject_New(PyRemminaFile, &python_remmina_file_type);
- result->file = file;
- Py_INCREF(result);
- return result;
+
+ PyRemminaFile* result = PyObject_New(PyRemminaFile, &python_remmina_file_type);
+ result->file = file;
+ Py_INCREF(result);
+ return result;
}
static PyObject* file_get_path(PyRemminaFile* self, PyObject* args)
{
- return Py_BuildValue("s", remmina_file_get_filename(self->file));
+ TRACE_CALL(__func__);
+
+ return Py_BuildValue("s", remmina_file_get_filename(self->file));
}
static PyObject* file_set_setting(PyRemminaFile* self, PyObject* args, PyObject* kwds)
{
- static const gchar* keyword_list[] = { "key", "value" };
- gchar* key;
- PyObject* value;
-
- if (PyArg_ParseTupleAndKeywords(args, kwds, "sO", keyword_list, &key, &value)) {
- if (PyUnicode_Check(value)) {
- remmina_file_set_string(self->file, key, PyUnicode_AsUTF8(value));
- } else if (PyLong_Check(value)) {
- remmina_file_set_int(self->file, key, PyUnicode_AsUTF8(value));
- } else {
- g_printerr("%s: Not a string or int value\n", PyUnicode_AsUTF8(PyObject_Str(value)));
- }
- return Py_None;
- } else {
- g_printerr("file.set_setting(key, value): Error parsing arguments!\n");
- PyErr_Print();
- return NULL;
- }
+ TRACE_CALL(__func__);
+
+ static char* keyword_list[] = { "key", "value", NULL };
+ gchar* key;
+ PyObject* value;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "s|O", keyword_list, &key, &value))
+ {
+ if (PyUnicode_Check(value))
+ {
+ remmina_file_set_string(self->file, key, PyUnicode_AsUTF8(value));
+ }
+ else if (PyLong_Check(value))
+ {
+ remmina_file_set_int(self->file, key, PyLong_AsLong(value));
+ }
+ else
+ {
+ g_printerr("%s: Not a string or int value\n", PyUnicode_AsUTF8(PyObject_Str(value)));
+ }
+ return Py_None;
+ }
+ else
+ {
+ g_printerr("file.set_setting(key, value): Error parsing arguments!\n");
+ PyErr_Print();
+ return NULL;
+ }
}
static PyObject* file_get_setting(PyRemminaFile* self, PyObject* args, PyObject* kwds)
{
- static const gchar* keyword_list[] = { "key", "default" };
- gchar* key;
- PyObject* def;
-
- if (PyArg_ParseTupleAndKeywords(args, kwds, "sO", keyword_list, &key, &def)) {
- if (PyUnicode_Check(def)) {
- return Py_BuildValue("s", remmina_file_get_string(self->file, key));
- } else if (PyLong_Check(def)) {
- return Py_BuildValue("i", remmina_file_get_int(self->file, key, (gint)PyLong_AsLong(def)));
- } else {
- g_printerr("%s: Not a string or int value\n", PyUnicode_AsUTF8(PyObject_Str(def)));
- }
- return def;
- } else {
- g_printerr("file.get_setting(key, default): Error parsing arguments!\n");
- PyErr_Print();
- return NULL;
- }
+ TRACE_CALL(__func__);
+
+ static gchar* keyword_list[] = { "key", "default" };
+ gchar* key;
+ PyObject* def;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "sO", keyword_list, &key, &def))
+ {
+ if (PyUnicode_Check(def))
+ {
+ return Py_BuildValue("s", remmina_file_get_string(self->file, key));
+ }
+ else if (PyBool_Check(def))
+ {
+ return remmina_file_get_int(self->file, key, (gint)PyLong_AsLong(def)) ? Py_True : Py_False;
+ }
+ else if (PyLong_Check(def))
+ {
+ return Py_BuildValue("i", remmina_file_get_int(self->file, key, (gint)PyLong_AsLong(def)));
+ }
+ else
+ {
+ g_printerr("%s: Not a string or int value\n", PyUnicode_AsUTF8(PyObject_Str(def)));
+ }
+ return def;
+ }
+ else
+ {
+ g_printerr("file.get_setting(key, default): Error parsing arguments!\n");
+ PyErr_Print();
+ return Py_None;
+ }
}
static PyObject* file_get_secret(PyRemminaFile* self, PyObject* key)
{
- static const gchar* keyword_list[] = { "key", "default" };
-
- if (key && PyUnicode_Check(key)) {
- return Py_BuildValue("s", remmina_file_get_secret(self->file, PyUnicode_AsUTF8(key)));
- } else {
- g_printerr("file.get_secret(key): Error parsing arguments!\n");
- PyErr_Print();
- return NULL;
- }
+ TRACE_CALL(__func__);
+
+ if (key && PyUnicode_Check(key))
+ {
+ return Py_BuildValue("s", remmina_file_get_secret(self->file, PyUnicode_AsUTF8(key)));
+ }
+ else
+ {
+ g_printerr("file.get_secret(key): Error parsing arguments!\n");
+ PyErr_Print();
+ return NULL;
+ }
}
static PyObject* file_unsave_passwords(PyRemminaFile* self, PyObject* args)
{
- if (self) {
- remmina_file_unsave_passwords(self->file);
- return Py_None;
- } else {
- g_printerr("unsave_passwords(): self is null!\n");
- return NULL;
- }
+ TRACE_CALL(__func__);
+
+ if (self)
+ {
+ remmina_file_unsave_passwords(self->file);
+ return Py_None;
+ }
+ else
+ {
+ g_printerr("unsave_passwords(): self is null!\n");
+ return NULL;
+ }
}
diff --git a/src/remmina_plugin_python_remmina_file.h b/src/remmina_plugin_python_remmina_file.h
index 7290364ec..a2e7a53d3 100644
--- a/src/remmina_plugin_python_remmina_file.h
+++ b/src/remmina_plugin_python_remmina_file.h
@@ -29,9 +29,33 @@
* do not wish to do so, delete this exception statement from your
* version. * If you delete this exception statement from all source
* files in the program, then also delete it here.
+ */
+
+
+/**
+ * @file remmina_plugin_python_protocol.h
*
+ * @brief Contains the implementation of the Python type remmina.RemminaFile.
*/
#pragma once
-PyObject* remmina_plugin_python_remmina_file_to_python(RemminaFile* file);
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Wrapper for a Python object that contains a pointer to an instance of RemminaFile.
+ */
+typedef struct
+{
+ PyObject_HEAD
+ RemminaFile* file;
+} PyRemminaFile;
+
+void remmina_plugin_python_remmina_init_types(void);
+
+/**
+ * Converts the instance of RemminaFile to a Python object that can be passed to the Python engine.
+ */
+PyRemminaFile* remmina_plugin_python_remmina_file_to_python(RemminaFile* file);
diff --git a/src/remmina_plugin_python_secret.c b/src/remmina_plugin_python_secret.c
new file mode 100644
index 000000000..be659fdbb
--- /dev/null
+++ b/src/remmina_plugin_python_secret.c
@@ -0,0 +1,138 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N L U C E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina_plugin_python_common.h"
+#include "remmina_plugin_python_secret.h"
+#include "remmina_plugin_python_remmina_file.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void remmina_plugin_python_secret_init(void)
+{
+ TRACE_CALL(__func__);
+}
+
+gboolean remmina_plugin_python_secret_init_wrapper(RemminaSecretPlugin* instance)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ PyObject* result = CallPythonMethod(plugin->instance, "init", NULL);
+ return result == Py_None || result != Py_False;
+}
+
+gboolean remmina_plugin_python_secret_is_service_available_wrapper(RemminaSecretPlugin* instance)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ PyObject* result = CallPythonMethod(plugin->instance, "is_service_available", NULL);
+ return result == Py_None || result != Py_False;
+}
+
+void
+remmina_plugin_python_secret_store_password_wrapper(RemminaSecretPlugin* instance, RemminaFile* file, const gchar* key, const gchar* password)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ CallPythonMethod(plugin
+ ->instance, "store_password", "Oss", (PyObject*)remmina_plugin_python_remmina_file_to_python(file), key, password);
+}
+
+gchar*
+remmina_plugin_python_secret_get_password_wrapper(RemminaSecretPlugin* instance, RemminaFile* file, const gchar* key)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ PyObject* result = CallPythonMethod(plugin
+ ->instance, "get_password", "Os", (PyObject*)remmina_plugin_python_remmina_file_to_python(file), key);
+ Py_ssize_t len = PyUnicode_GetLength(result);
+ if (len == 0)
+ {
+ return NULL;
+ }
+
+ return remmina_plugin_python_copy_string_from_python(result, len);
+}
+
+void
+remmina_plugin_python_secret_delete_password_wrapper(RemminaSecretPlugin* instance, RemminaFile* file, const gchar* key)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ CallPythonMethod(plugin
+ ->instance, "delete_password", "Os", (PyObject*)remmina_plugin_python_remmina_file_to_python(file), key);
+}
+
+RemminaPlugin* remmina_plugin_python_create_secret_plugin(PyPlugin* plugin)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* instance = plugin->instance;
+
+ if (!remmina_plugin_python_check_attribute(instance, ATTR_NAME))
+ {
+ return NULL;
+ }
+
+ RemminaSecretPlugin* remmina_plugin = (RemminaSecretPlugin*)remmina_plugin_python_malloc(sizeof(RemminaSecretPlugin));
+
+ remmina_plugin->type = REMMINA_PLUGIN_TYPE_SECRET;
+ remmina_plugin->domain = GETTEXT_PACKAGE;
+ remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_NAME));
+ remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_VERSION));
+ remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_DESCRIPTION));
+ remmina_plugin->init_order = PyLong_AsLong(PyObject_GetAttrString(instance, ATTR_INIT_ORDER));
+
+ remmina_plugin->init = remmina_plugin_python_secret_init_wrapper;
+ remmina_plugin->is_service_available = remmina_plugin_python_secret_is_service_available_wrapper;
+ remmina_plugin->store_password = remmina_plugin_python_secret_store_password_wrapper;
+ remmina_plugin->get_password = remmina_plugin_python_secret_get_password_wrapper;
+ remmina_plugin->delete_password = remmina_plugin_python_secret_delete_password_wrapper;
+
+ plugin->secret_plugin = remmina_plugin;
+ plugin->generic_plugin = (RemminaPlugin*)remmina_plugin;
+
+ remmina_plugin_python_add_plugin(plugin);
+
+ return (RemminaPlugin*)remmina_plugin;
+} \ No newline at end of file
diff --git a/src/remmina_plugin_python_secret.h b/src/remmina_plugin_python_secret.h
new file mode 100644
index 000000000..c627d2a49
--- /dev/null
+++ b/src/remmina_plugin_python_secret.h
@@ -0,0 +1,67 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_entry.h
+ *
+ * @brief Contains the specialisation of RemminaPluginEntry plugins in Python.
+ */
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N L U C E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina/plugin.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+/**
+ * Initializes the Python plugin specialisation for secret plugins.
+ */
+void remmina_plugin_python_secret_init(void);
+
+/**
+ * @brief Creates a new instance of the RemminaPluginSecret, initializes its members and references the wrapper
+ * functions.
+ * @param instance The instance of the Python plugin.
+ * @return Returns a new instance of the RemminaPlugin (must be freed!).
+ */
+RemminaPlugin* remmina_plugin_python_create_secret_plugin(PyPlugin* instance);
+
+G_END_DECLS
diff --git a/src/remmina_plugin_python_tool.c b/src/remmina_plugin_python_tool.c
new file mode 100644
index 000000000..a951291f2
--- /dev/null
+++ b/src/remmina_plugin_python_tool.c
@@ -0,0 +1,84 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N L U C E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina_plugin_python_common.h"
+#include "remmina_plugin_python_tool.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void remmina_plugin_python_tool_init(void)
+{
+ TRACE_CALL(__func__);
+}
+
+void remmina_plugin_python_tool_exec_func_wrapper(GtkMenuItem* self, RemminaToolPlugin* instance)
+{
+ TRACE_CALL(__func__);
+
+ PyPlugin* plugin = remmina_plugin_python_get_plugin(instance->name);
+ CallPythonMethod(plugin->instance, "exec_func", NULL);
+}
+
+RemminaPlugin* remmina_plugin_python_create_tool_plugin(PyPlugin* plugin)
+{
+ TRACE_CALL(__func__);
+
+ PyObject* instance = plugin->instance;
+
+ if (!remmina_plugin_python_check_attribute(instance, ATTR_NAME))
+ {
+ return NULL;
+ }
+
+ RemminaToolPlugin* remmina_plugin = (RemminaToolPlugin*)remmina_plugin_python_malloc(sizeof(RemminaToolPlugin));
+
+ remmina_plugin->type = REMMINA_PLUGIN_TYPE_TOOL;
+ remmina_plugin->domain = GETTEXT_PACKAGE;
+ remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_NAME));
+ remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_VERSION));
+ remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(instance, ATTR_DESCRIPTION));
+ remmina_plugin->exec_func = remmina_plugin_python_tool_exec_func_wrapper;
+
+ plugin->tool_plugin = remmina_plugin;
+ plugin->generic_plugin = (RemminaPlugin*)remmina_plugin;
+
+ remmina_plugin_python_add_plugin(plugin);
+
+ return (RemminaPlugin*)remmina_plugin;
+}
diff --git a/src/remmina_plugin_python_tool.h b/src/remmina_plugin_python_tool.h
new file mode 100644
index 000000000..08852bdc4
--- /dev/null
+++ b/src/remmina_plugin_python_tool.h
@@ -0,0 +1,68 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+
+/**
+ * @file remmina_plugin_python_entry.h
+ *
+ * @brief Contains the specialisation of RemminaPluginEntry plugins in Python.
+ */
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// I N L U C E S
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "remmina/plugin.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// A P I
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+G_BEGIN_DECLS
+
+/**
+ * Initializes the Python plugin specialisation for tool plugins.
+ */
+void remmina_plugin_python_tool_init(void);
+
+/**
+ * @brief Creates a new instance of the RemminaPluginTool, initializes its members and references the wrapper
+ * functions.
+ * @param instance The instance of the Python plugin.
+ * @return Returns a new instance of the RemminaPlugin (must be freed!).
+ */
+RemminaPlugin* remmina_plugin_python_create_tool_plugin(PyPlugin* instance);
+
+G_END_DECLS
+
diff --git a/src/remmina_pref_dialog.c b/src/remmina_pref_dialog.c
index b49eeccdd..eadbedeb5 100644
--- a/src/remmina_pref_dialog.c
+++ b/src/remmina_pref_dialog.c
@@ -408,7 +408,7 @@ static gboolean remmina_pref_dialog_add_pref_plugin(gchar *name, RemminaPlugin *
gtk_widget_show(vbox);
gtk_notebook_append_page(GTK_NOTEBOOK(remmina_pref_dialog->notebook_preferences), vbox, widget);
- widget = pref_plugin->get_pref_body();
+ widget = pref_plugin->get_pref_body(pref_plugin);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
return FALSE;
diff --git a/src/remmina_protocol_widget.c b/src/remmina_protocol_widget.c
index 070b05c0e..9703d4cc5 100644
--- a/src/remmina_protocol_widget.c
+++ b/src/remmina_protocol_widget.c
@@ -282,15 +282,21 @@ void remmina_protocol_widget_open_connection_real(gpointer data)
feature->type = REMMINA_PROTOCOL_FEATURE_TYPE_TOOL;
feature->id = REMMINA_PROTOCOL_FEATURE_TOOL_SSH;
feature->opt1 = _("Connect via SSH from a new terminal");
+ feature->opt1_type_hint = REMMINA_TYPEHINT_STRING;
feature->opt2 = "utilities-terminal";
+ feature->opt2_type_hint = REMMINA_TYPEHINT_STRING;
feature->opt3 = NULL;
+ feature->opt3_type_hint = REMMINA_TYPEHINT_UNDEFINED;
feature++;
feature->type = REMMINA_PROTOCOL_FEATURE_TYPE_TOOL;
feature->id = REMMINA_PROTOCOL_FEATURE_TOOL_SFTP;
feature->opt1 = _("Open SFTP transfer…");
+ feature->opt1_type_hint = REMMINA_TYPEHINT_STRING;
feature->opt2 = "folder-remote";
+ feature->opt2_type_hint = REMMINA_TYPEHINT_STRING;
feature->opt3 = NULL;
+ feature->opt3_type_hint = REMMINA_TYPEHINT_UNDEFINED;
feature++;
}
feature->type = REMMINA_PROTOCOL_FEATURE_TYPE_END;