Remmina - The GTK+ Remote Desktop Client  v1.4.25
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_plugin_python_common.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2014-2021 Antenore Gatta, Giovanni Panozzo, Mathias Winterhalter (ToolsDevler)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give
21  * permission to link the code of portions of this program with the
22  * OpenSSL library under certain conditions as described in each
23  * individual source file, and distribute linked combinations
24  * including the two.
25  * You must obey the GNU General Public License in all respects
26  * for all of the code used other than OpenSSL. * If you modify
27  * file(s) with this exception, you may extend this exception to your
28  * version of the file(s), but you are not obligated to do so. * If you
29  * do not wish to do so, delete this exception statement from your
30  * version. * If you delete this exception statement from all source
31  * files in the program, then also delete it here.
32  */
33 
35 // I N C L U D E S
37 
39 
40 #include <assert.h>
41 #include <stdio.h>
42 
43 #pragma GCC diagnostic push
44 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
45 #include "pygobject.h"
46 #pragma GCC diagnostic pop
47 
49 // D E C L A R A T I O N S
51 
56 static PyObject* __last_result;
57 static GPtrArray* plugin_map = NULL;
58 
60 
61 const char* ATTR_NAME = "name";
62 const char* ATTR_ICON_NAME = "icon_name";
63 const char* ATTR_DESCRIPTION = "description";
64 const char* ATTR_VERSION = "version";
65 const char* ATTR_ICON_NAME_SSH = "icon_name_ssh";
66 const char* ATTR_FEATURES = "features";
67 const char* ATTR_BASIC_SETTINGS = "basic_settings";
68 const char* ATTR_ADVANCED_SETTINGS = "advanced_settings";
69 const char* ATTR_SSH_SETTING = "ssh_setting";
70 const char* ATTR_EXPORT_HINTS = "export_hints";
71 const char* ATTR_PREF_LABEL = "pref_label";
72 const char* ATTR_INIT_ORDER = "init_order";
73 
78 static const int REASONABLE_LIMIT_FOR_MALLOC = 1024 * 1024;
79 
81 // A P I
83 
85 {
86  TRACE_CALL(__func__);
87 
88  return __last_result;
89 }
90 
91 PyObject* remmina_plugin_python_last_result_set(PyObject* last_result)
92 {
93  TRACE_CALL(__func__);
94 
95  return __last_result = last_result;
96 }
97 
99 {
100  TRACE_CALL(__func__);
101 
102  if (PyErr_Occurred())
103  {
104  PyErr_Print();
105  return TRUE;
106  }
107 
108  return FALSE;
109 }
110 
111 void remmina_plugin_python_log_method_call(PyObject* instance, const char* method)
112 {
113  TRACE_CALL(__func__);
114 
115  assert(instance);
116  assert(method);
117  g_print("Python@%ld: %s.%s(...) -> %s\n",
118  PyObject_Hash(instance),
119  instance->ob_type->tp_name,
120  method,
121  PyUnicode_AsUTF8(PyObject_Str(remmina_plugin_python_last_result())));
122 }
123 
124 long remmina_plugin_python_get_attribute_long(PyObject* instance, const char* attr_name, long def)
125 {
126  TRACE_CALL(__func__);
127 
128  assert(instance);
129  assert(attr_name);
130  PyObject* attr = PyObject_GetAttrString(instance, attr_name);
131  if (attr && PyLong_Check(attr))
132  {
133  return PyLong_AsLong(attr);
134  }
135 
136  return def;
137 }
138 
139 gboolean remmina_plugin_python_check_attribute(PyObject* instance, const char* attr_name)
140 {
141  TRACE_CALL(__func__);
142 
143  assert(instance);
144  assert(attr_name);
145  if (PyObject_HasAttrString(instance, attr_name))
146  {
147  return TRUE;
148  }
149 
150  g_printerr("Python plugin instance is missing member: %s\n", attr_name);
151  return FALSE;
152 }
153 
155 {
156  TRACE_CALL(__func__);
157 
158  assert(bytes > 0);
159  assert(bytes <= REASONABLE_LIMIT_FOR_MALLOC);
160 
161  void* result = malloc(bytes);
162 
163  if (!result)
164  {
165  g_printerr("Unable to allocate %d bytes in memory!\n", bytes);
166  perror("malloc");
167  }
168 
169  return result;
170 }
171 
172 char* remmina_plugin_python_copy_string_from_python(PyObject* string, Py_ssize_t len)
173 {
174  TRACE_CALL(__func__);
175 
176  char* result = NULL;
177  if (len <= 0 || string == NULL)
178  {
179  return NULL;
180  }
181 
182  const char* py_str = PyUnicode_AsUTF8(string);
183  if (py_str)
184  {
185  const int label_size = sizeof(char) * (len + 1);
186  result = (char*)remmina_plugin_python_malloc(label_size);
187  result[len] = '\0';
188  memcpy(result, py_str, len);
189  }
190 
191  return result;
192 }
193 
195 {
196  remmina_plugin_service = service;
197 }
198 
200 {
201  return remmina_plugin_service;
202 }
203 
205 {
206  TRACE_CALL(__func__);
207 
208  if (!plugin_map)
209  {
210  plugin_map = g_ptr_array_new();
211  }
212 
214  if (test)
215  {
216  g_printerr("A plugin named '%s' has already been registered! Skipping...", plugin->generic_plugin->name);
217  }
218  else
219  {
220  g_ptr_array_add(plugin_map, plugin);
221  }
222 }
223 
224 RemminaTypeHint remmina_plugin_python_to_generic(PyObject* field, gpointer* target)
225 {
226  TRACE_CALL(__func__);
227 
228  if (PyUnicode_Check(field))
229  {
230  Py_ssize_t len = PyUnicode_GetLength(field);
231 
232  if (len > 0)
233  {
234  *target = remmina_plugin_python_copy_string_from_python(field, len);
235  }
236  else
237  {
238  *target = "";
239  }
240 
242  }
243  else if (PyBool_Check(field))
244  {
245  *target = remmina_plugin_python_malloc(sizeof(long));
246  long* long_target = (long*)target;
247  *long_target = PyLong_AsLong(field);
249  }
250  else if (PyLong_Check(field))
251  {
252  *target = remmina_plugin_python_malloc(sizeof(long));
253  long* long_target = (long*)target;
254  *long_target = PyLong_AsLong(field);
256  }
257  else if (PyTuple_Check(field))
258  {
259  Py_ssize_t len = PyTuple_Size(field);
260  if (len)
261  {
262  gpointer* dest = (gpointer*)remmina_plugin_python_malloc(sizeof(gpointer) * (len + 1));
263  memset(dest, 0, sizeof(gpointer) * (len + 1));
264 
265  for (Py_ssize_t i = 0; i < len; ++i)
266  {
267  PyObject* item = PyTuple_GetItem(field, i);
268  remmina_plugin_python_to_generic(item, dest + i);
269  }
270 
271  *target = dest;
272  }
273  return REMMINA_TYPEHINT_TUPLE;
274  }
275 
276  *target = NULL;
278 }
279 
281 {
282  TRACE_CALL(__func__);
283 
284  assert(plugin_map);
285  assert(name);
286 
287  for (gint i = 0; i < plugin_map->len; ++i)
288  {
289  PyPlugin* plugin = (PyPlugin*)g_ptr_array_index(plugin_map, i);
290  if (plugin->generic_plugin && plugin->generic_plugin->name && g_str_equal(name, plugin->generic_plugin->name))
291  {
292  return plugin;
293  }
294  }
295 
296  return NULL;
297 }
298 
300 {
301  pygobject_init(-1, -1, -1);
302 }
303 
304 GtkWidget* new_pywidget(GObject* obj)
305 {
306  return (GtkWidget*)pygobject_new(obj);
307 }
308 
309 GtkWidget* get_pywidget(PyObject* obj)
310 {
311  return (GtkWidget*)pygobject_get(obj);
312 }
RemminaTypeHint
Definition: types.h:60
const char * ATTR_INIT_ORDER
void init_pygobject()
Initializes the pygobject library.
RemminaTypeHint remmina_plugin_python_to_generic(PyObject *field, gpointer *target)
Extracts data from a PyObject instance to a generic pointer and returns a type hint if it could be de...
Maps an instance of a Python plugin to a Remmina one.
void remmina_plugin_python_log_method_call(PyObject *instance, const char *method)
Prints a log message to inform the user a python message has been called.
const char * ATTR_EXPORT_HINTS
char * remmina_plugin_python_copy_string_from_python(PyObject *string, Py_ssize_t len)
Copies a string from a Python object to a new point in memory.
long remmina_plugin_python_get_attribute_long(PyObject *instance, const char *attr_name, long def)
Gets the attribute as long value.
PyPlugin * remmina_plugin_python_get_plugin(const char *name)
Tries to find the Python plugin matching to the given instance of RemminaPlugin.
GtkWidget * get_pywidget(PyObject *obj)
Extracts a GtkWidget from a PyObject instance.
const char * ATTR_PREF_LABEL
static const int REASONABLE_LIMIT_FOR_MALLOC
To prevent some memory related attacks or accidental allocation of an excessive amount of byes...
gboolean remmina_plugin_python_check_error(void)
Checks if an error has occurred and prints it.
static GPtrArray * plugin_map
const char * ATTR_ADVANCED_SETTINGS
const char * ATTR_DESCRIPTION
const char * ATTR_ICON_NAME_SSH
RemminaPlugin * generic_plugin
static PyObject * __last_result
A cache to store the last result that has been returned by the Python code using CallPythonMethod (...
void remmina_plugin_python_add_plugin(PyPlugin *plugin)
Registers the given plugin if no other plugin with the same name has been already registered...
void remmina_plugin_python_set_service(RemminaPluginService *service)
Sets the pointer to the plugin service of Remmina.
const char * ATTR_FEATURES
gboolean remmina_plugin_python_check_attribute(PyObject *instance, const char *attr_name)
Checks if a given attribute exists.
RemminaPluginService * remmina_plugin_python_get_service(void)
const char * ATTR_BASIC_SETTINGS
const char * ATTR_NAME
Contains functions and constants that are commonly used throughout the Python plugin implementation...
static RemminaPluginService * remmina_plugin_service
void * remmina_plugin_python_malloc(int bytes)
Allocates memory and checks for errors before returning.
static PyObject * pygobject_init(int req_major, int req_minor, int req_micro)
pygobject_init: : minimum version major number, or -1 : minimum version minor number, or -1 : minimum version micro number, or -1
Definition: pygobject.h:323
const char * ATTR_ICON_NAME
GtkWidget * new_pywidget(GObject *obj)
Creates a new GtkWidget.
PyObject * remmina_plugin_python_last_result_set(PyObject *last_result)
Sets the result of the last python method call.
const gchar * name
Definition: plugin.h:56
const char * ATTR_SSH_SETTING
const char * ATTR_VERSION
PyObject * remmina_plugin_python_last_result(void)
Gets the result of the last python method call.