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.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2014-2022 Antenore Gatta, Giovanni Panozzo
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 
38 #include "config.h"
41 #include "remmina_plugin_python.h"
44 
46 // D E C L A R A T I O N S
48 
53 static const char
54  * python_init_commands[] = { "import sys", "sys.path.append('" REMMINA_RUNTIME_PLUGINDIR "')", NULL // Sentinel
55 };
56 
58 // U T I L S
60 
69 static int basename_no_ext(const char* in, char** out)
70 {
71  TRACE_CALL(__func__);
72 
73  assert(in);
74  assert(out);
75 
76  const char* base = strrchr(in, '/');
77  if (base)
78  {
79  base++;
80  }
81 
82  const char* base_end = strrchr(base, '.');
83  if (!base_end)
84  {
85  base_end = base + strlen(base);
86  }
87 
88  const int length = base_end - base;
89  const int memsize = sizeof(char*) * ((length) + 1);
90 
91  *out = (char*)remmina_plugin_python_malloc(memsize);
92 
93  memset(*out, 0, memsize);
94  strncpy(*out, base, length);
95  (*out)[length] = '\0';
96 
97  return length;
98 }
99 
101 // A P I
103 
105 {
106  TRACE_CALL(__func__);
107 
109  Py_Initialize();
110 
111  for (const char** ptr = python_init_commands; *ptr; ++ptr)
112  {
113  PyRun_SimpleString(*ptr);
114  }
115 
117 }
118 
119 gboolean remmina_plugin_python_load(RemminaPluginService* service, const char* name)
120 {
121  TRACE_CALL(__func__);
122 
123  assert(service);
124  assert(name);
125 
127 
128  char* filename = NULL;
129  if (basename_no_ext(name, &filename) == 0)
130  {
131  g_printerr("[%s:%d]: Can not extract filename from '%s'!\n", __FILE__, __LINE__, name);
132  return FALSE;
133  }
134 
135  PyObject* plugin_name = PyUnicode_DecodeFSDefault(filename);
136 
137  if (!plugin_name)
138  {
139  free(filename);
140  g_printerr("[%s:%d]: Error converting plugin filename to PyUnicode!\n", __FILE__, __LINE__);
141  return FALSE;
142  }
143 
144  wchar_t* program_name = NULL;
145  Py_ssize_t len = PyUnicode_AsWideChar(plugin_name, program_name, 0);
146  if (len <= 0)
147  {
148  free(filename);
149  g_printerr("[%s:%d]: Failed allocating %lu bytes!\n", __FILE__, __LINE__, (sizeof(wchar_t) * len));
150  return FALSE;
151  }
152 
153  program_name = (wchar_t*)remmina_plugin_python_malloc(sizeof(wchar_t) * len);
154  if (!program_name)
155  {
156  free(filename);
157  g_printerr("[%s:%d]: Failed allocating %lu bytes!\n", __FILE__, __LINE__, (sizeof(wchar_t) * len));
158  return FALSE;
159  }
160 
161  PyUnicode_AsWideChar(plugin_name, program_name, len);
162 
163  PySys_SetArgv(1, &program_name);
164 
165  if (PyImport_Import(plugin_name))
166  {
167  free(filename);
168  return TRUE;
169  }
170 
171  g_print("[%s:%d]: Failed to load python plugin file: '%s'\n", __FILE__, __LINE__, name);
172  PyErr_Print();
173  free(filename);
174 
175  return FALSE;
176 }
void remmina_plugin_python_module_init(void)
Initializes all globals and registers the &#39;remmina&#39; module in the Python engine.
Contains the implementation of the Python module &#39;remmina&#39;, provided to interface with the applicatio...
void remmina_plugin_python_init(void)
Initializes the Python plugin loaders.
Declares the interface between the Python plugin implementation and Remmina covering the initializati...
void remmina_plugin_python_set_service(RemminaPluginService *service)
Sets the pointer to the plugin service of Remmina.
void remmina_plugin_python_protocol_widget_init(void)
Initializes the widget backend of the protocol plugin implementation.
Contains functions and constants that are commonly used throughout the Python plugin implementation...
void * remmina_plugin_python_malloc(int bytes)
Allocates memory and checks for errors before returning.
gboolean remmina_plugin_python_load(RemminaPluginService *service, const char *name)
Contains the implementation of the widget handling used from the protocol plugin. ...
static int basename_no_ext(const char *in, char **out)
Extracts the filename without extension from a path.
static const char * python_init_commands[]
An null terminated array of commands that are executed after the initialization of the Python engine...