diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 17:06:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 17:06:32 +0000 |
commit | 2dad5357405ad33cfa792f04b3ab62a5d188841e (patch) | |
tree | b8f8893942060fe3cfb04ac374cda96fdfc8f453 /plugins/python_wrapper/python_wrapper_common.c | |
parent | Initial commit. (diff) | |
download | remmina-2dad5357405ad33cfa792f04b3ab62a5d188841e.tar.xz remmina-2dad5357405ad33cfa792f04b3ab62a5d188841e.zip |
Adding upstream version 1.4.34+dfsg.upstream/1.4.34+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/python_wrapper/python_wrapper_common.c')
-rw-r--r-- | plugins/python_wrapper/python_wrapper_common.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/plugins/python_wrapper/python_wrapper_common.c b/plugins/python_wrapper/python_wrapper_common.c new file mode 100644 index 0000000..e6a2d05 --- /dev/null +++ b/plugins/python_wrapper/python_wrapper_common.c @@ -0,0 +1,327 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2014-2023 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 "python_wrapper_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 python_wrapper_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* python_wrapper_last_result(void) +{ + TRACE_CALL(__func__); + + return __last_result; +} + +PyObject* python_wrapper_last_result_set(PyObject* last_result) +{ + TRACE_CALL(__func__); + + return __last_result = last_result; +} + +gboolean python_wrapper_check_error(void) +{ + TRACE_CALL(__func__); + + if (PyErr_Occurred()) + { + PyErr_Print(); + return TRUE; + } + + return FALSE; +} + +void python_wrapper_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(python_wrapper_last_result()))); +} + +long python_wrapper_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 python_wrapper_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* python_wrapper_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* python_wrapper_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*)python_wrapper_malloc(label_size); + result[len] = '\0'; + memcpy(result, py_str, len); + } + + return result; +} + +void python_wrapper_set_service(RemminaPluginService* service) +{ + remmina_plugin_service = service; +} + +RemminaPluginService* python_wrapper_get_service(void) +{ + return remmina_plugin_service; +} + +void python_wrapper_add_plugin(PyPlugin* plugin) +{ + TRACE_CALL(__func__); + + if (!plugin_map) + { + plugin_map = g_ptr_array_new(); + } + + PyPlugin* test = python_wrapper_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 python_wrapper_to_generic(PyObject* field, gpointer* target) +{ + TRACE_CALL(__func__); + + if (PyUnicode_Check(field)) + { + Py_ssize_t len = PyUnicode_GetLength(field); + + if (len > 0) + { + *target = python_wrapper_copy_string_from_python(field, len); + } + else + { + *target = ""; + } + + return REMMINA_TYPEHINT_STRING; + } + else if (PyBool_Check(field)) + { + *target = python_wrapper_malloc(sizeof(long)); + long* long_target = (long*)target; + *long_target = PyLong_AsLong(field); + return REMMINA_TYPEHINT_BOOLEAN; + } + else if (PyLong_Check(field)) + { + *target = python_wrapper_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*)python_wrapper_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); + python_wrapper_to_generic(item, dest + i); + } + + *target = dest; + } + return REMMINA_TYPEHINT_TUPLE; + } + + *target = NULL; + return REMMINA_TYPEHINT_UNDEFINED; +} + +PyPlugin* python_wrapper_get_plugin(const gchar* 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; +} + +PyPlugin* python_wrapper_get_plugin_by_protocol_widget(RemminaProtocolWidget* gp) +{ + TRACE_CALL(__func__); + + assert(plugin_map); + assert(gp); + + const gchar* name = python_wrapper_get_service()->protocol_widget_get_name(gp); + if (!name) { + return NULL; + } + + return python_wrapper_get_plugin(name); +} + +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); +} |