Remmina - The GTK+ Remote Desktop Client  v1.4.34
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_public.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include "config.h"
38 #include <gtk/gtk.h>
39 #include <glib/gi18n.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #ifdef HAVE_NETDB_H
45 #include <netdb.h>
46 #endif
47 #ifdef HAVE_SYS_SOCKET_H
48 #include <sys/socket.h>
49 #endif
50 #ifdef HAVE_SYS_UN_H
51 #include <sys/un.h>
52 #endif
53 #ifdef GDK_WINDOWING_X11
54 #include <gdk/gdkx.h>
55 #include <X11/Xlib.h>
56 #include <X11/Xutil.h>
57 #include <X11/Xatom.h>
58 #elif defined(GDK_WINDOWING_WAYLAND)
59 #include <gdk/gdkwayland.h>
60 #endif
61 #include "remmina_public.h"
63 
64 GtkWidget*
65 remmina_public_create_combo_entry(const gchar *text, const gchar *def, gboolean descending)
66 {
67  TRACE_CALL(__func__);
68  GtkWidget *combo;
69  gboolean found;
70  gchar *buf, *ptr1, *ptr2;
71  gint i;
72 
73  //g_debug("text: %s\n", text);
74  //g_debug("def: %s\n", def);
75 
76  combo = gtk_combo_box_text_new_with_entry();
77  found = FALSE;
78 
79  if (text && text[0] != '\0') {
80  buf = g_strdup(text);
81  ptr1 = buf;
82  i = 0;
83  while (ptr1 && *ptr1 != '\0') {
84  ptr2 = strchr(ptr1, CHAR_DELIMITOR);
85  if (ptr2)
86  *ptr2++ = '\0';
87 
88  if (descending) {
89  gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(combo), ptr1);
90  if (!found && g_strcmp0(ptr1, def) == 0) {
91  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
92  found = TRUE;
93  }
94  }else {
95  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ptr1);
96  if (!found && g_strcmp0(ptr1, def) == 0) {
97  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
98  found = TRUE;
99  }
100  }
101 
102  ptr1 = ptr2;
103  i++;
104  }
105 
106  g_free(buf);
107  }
108 
109  if (!found && def && def[0] != '\0') {
110  gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo))), def);
111  }
112 
113  return combo;
114 }
115 
116 GtkWidget*
117 remmina_public_create_combo_text_d(const gchar *text, const gchar *def, const gchar *empty_choice)
118 {
119  TRACE_CALL(__func__);
120  GtkWidget *combo;
121  GtkListStore *store;
122  GtkCellRenderer *text_renderer;
123 
124  store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
125  combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
126 
127  text_renderer = gtk_cell_renderer_text_new();
128  gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(combo), text_renderer, TRUE);
129  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), text_renderer, "text", 1);
130 
131  remmina_public_load_combo_text_d(combo, text, def, empty_choice);
132 
133  return combo;
134 }
135 
136 void remmina_public_load_combo_text_d(GtkWidget *combo, const gchar *text, const gchar *def, const gchar *empty_choice)
137 {
138  TRACE_CALL(__func__);
139  GtkListStore *store;
140  GtkTreeIter iter;
141  gint i;
142  gchar *buf, *ptr1, *ptr2;
143 
144  store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo)));
145  gtk_list_store_clear(store);
146 
147  i = 0;
148 
149  if (empty_choice) {
150  gtk_list_store_append(store, &iter);
151  gtk_list_store_set(store, &iter, 0, "", 1, empty_choice, -1);
152  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
153  i++;
154  }
155 
156  if (text == NULL || text[0] == '\0')
157  return;
158 
159  buf = g_strdup(text);
160  ptr1 = buf;
161  while (ptr1 && *ptr1 != '\0') {
162  ptr2 = strchr(ptr1, CHAR_DELIMITOR);
163  if (ptr2)
164  *ptr2++ = '\0';
165 
166  gtk_list_store_append(store, &iter);
167  gtk_list_store_set(store, &iter, 0, ptr1, 1, ptr1, -1);
168 
169  if (i == 0 || g_strcmp0(ptr1, def) == 0) {
170  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
171  }
172 
173  i++;
174  ptr1 = ptr2;
175  }
176 
177  g_free(buf);
178 }
179 
180 GtkWidget*
181 remmina_public_create_combo(gboolean use_icon)
182 {
183  TRACE_CALL(__func__);
184  GtkWidget *combo;
185  GtkListStore *store;
186  GtkCellRenderer *renderer;
187 
188  if (use_icon) {
189  store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
190  }else {
191  store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
192  }
193  combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
194  gtk_widget_set_hexpand(combo, TRUE);
195 
196  if (use_icon) {
197  renderer = gtk_cell_renderer_pixbuf_new();
198  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, FALSE);
199  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), renderer, "icon-name", 2);
200  }
201  renderer = gtk_cell_renderer_text_new();
202  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
203  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), renderer, "text", 1);
204  if (use_icon)
205  g_object_set(G_OBJECT(renderer), "xpad", 5, NULL);
206 
207  return combo;
208 }
209 
210 GtkWidget*
211 remmina_public_create_combo_map(const gpointer *key_value_list, const gchar *def, gboolean use_icon, const gchar *domain)
212 {
213  TRACE_CALL(__func__);
214  gint i;
215  GtkWidget *combo;
216  GtkListStore *store;
217  GtkTreeIter iter;
218 
219  combo = remmina_public_create_combo(use_icon);
220  store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo)));
221 
222  for (i = 0; key_value_list[i]; i += (use_icon ? 3 : 2)) {
223  gtk_list_store_append(store, &iter);
224  gtk_list_store_set(
225  store,
226  &iter,
227  0,
228  key_value_list[i],
229  1,
230  key_value_list[i + 1] && ((char*)key_value_list[i + 1])[0] ?
231  g_dgettext(domain, key_value_list[i + 1]) : "", -1);
232  if (use_icon) {
233  gtk_list_store_set(store, &iter, 2, key_value_list[i + 2], -1);
234  }
235  if (i == 0 || g_strcmp0(key_value_list[i], def) == 0) {
236  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i / (use_icon ? 3 : 2));
237  }
238  }
239  return combo;
240 }
241 
242 GtkWidget*
243 remmina_public_create_combo_mapint(const gpointer *key_value_list, gint def, gboolean use_icon, const gchar *domain)
244 {
245  TRACE_CALL(__func__);
246  gchar buf[20];
247  g_snprintf(buf, sizeof(buf), "%i", def);
248  return remmina_public_create_combo_map(key_value_list, buf, use_icon, domain);
249 }
250 
251 void remmina_public_create_group(GtkGrid *grid, const gchar *group, gint row, gint rows, gint cols)
252 {
253  TRACE_CALL(__func__);
254  GtkWidget *widget;
255  gchar *str;
256 
257  widget = gtk_label_new(NULL);
258  gtk_widget_show(widget);
259  gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
260  gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
261  str = g_markup_printf_escaped("<b>%s</b>", group);
262  gtk_label_set_markup(GTK_LABEL(widget), str);
263  g_free(str);
264  gtk_grid_attach(GTK_GRID(grid), widget, 0, row, 1, 2);
265 
266  widget = gtk_label_new(NULL);
267  gtk_widget_show(widget);
268  gtk_grid_attach(GTK_GRID(grid), widget, 0, row + 1, 1, 1);
269 }
270 
271 gchar*
273 {
274  TRACE_CALL(__func__);
275  GtkTreeModel *model;
276  GtkTreeIter iter;
277  gchar *s;
278 
279  if (GTK_IS_COMBO_BOX_TEXT(combo)) {
280  return gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
281  }
282 
283  if (!gtk_combo_box_get_active_iter(combo, &iter))
284  return NULL;
285 
286  model = gtk_combo_box_get_model(combo);
287  gtk_tree_model_get(model, &iter, 0, &s, -1);
288 
289  return s;
290 }
291 
292 #if !GTK_CHECK_VERSION(3, 22, 0)
293 void remmina_public_popup_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
294 {
295  TRACE_CALL(__func__);
296  GtkWidget *widget;
297  gint tx, ty;
298  GtkAllocation allocation;
299 
300  widget = GTK_WIDGET(user_data);
301  if (gtk_widget_get_window(widget) == NULL) {
302  *x = 0;
303  *y = 0;
304  *push_in = TRUE;
305  return;
306  }
307  gdk_window_get_origin(gtk_widget_get_window(widget), &tx, &ty);
308  gtk_widget_get_allocation(widget, &allocation);
309  /* I’m unsure why the author made the check about a GdkWindow inside the
310  * widget argument. This function generally is called passing by a ToolButton
311  * which hasn’t any GdkWindow, therefore the positioning is wrong
312  * I think the gtk_widget_get_has_window() check should be removed
313  *
314  * While leaving the previous check intact I’m checking also if the provided
315  * widget is a GtkToggleToolButton and position the menu accordingly. */
316  if (gtk_widget_get_has_window(widget) ||
317  g_strcmp0(gtk_widget_get_name(widget), "GtkToggleToolButton") == 0) {
318  tx += allocation.x;
319  ty += allocation.y;
320  }
321 
322  *x = tx;
323  *y = ty + allocation.height - 1;
324  *push_in = TRUE;
325 }
326 #endif
327 
328 gchar*
329 remmina_public_combine_path(const gchar *path1, const gchar *path2)
330 {
331  TRACE_CALL(__func__);
332  if (!path1 || path1[0] == '\0')
333  return g_strdup(path2);
334  if (path1[strlen(path1) - 1] == '/')
335  return g_strdup_printf("%s%s", path1, path2);
336  return g_strdup_printf("%s/%s", path1, path2);
337 }
338 
339 //static int remmina_public_open_unix_sock(const char *unixsock, GError **error)
340 gint remmina_public_open_unix_sock(const char *unixsock)
341 {
342  struct sockaddr_un addr;
343  int fd;
344 
345  if (strlen(unixsock) + 1 > sizeof(addr.sun_path)) {
346  //g_set_error(error, REMMINA_ERROR, REMMINA_ERROR_FAILED,
347  g_debug(_("Address is too long for UNIX socket_path: %s"), unixsock);
348  return -1;
349  }
350 
351  memset(&addr, 0, sizeof addr);
352  addr.sun_family = AF_UNIX;
353  strcpy(addr.sun_path, unixsock);
354 
355  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
356  //g_set_error(error, REMMINA_ERROR, REMMINA_ERROR_FAILED,
357  g_debug(_("Creating UNIX socket failed: %s"), g_strerror(errno));
358  return -1;
359  }
360 
361  if (connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0) {
362  //g_set_error(error, REMMINA_ERROR, REMMINA_ERROR_FAILED,
363  g_debug(_("Connecting to UNIX socket failed: %s"), g_strerror(errno));
364  close(fd);
365  return -1;
366  }
367 
368  return fd;
369 }
370 
371 void remmina_public_get_server_port_old(const gchar *server, gint defaultport, gchar **host, gint *port)
372 {
373  TRACE_CALL(__func__);
374  gchar *str, *ptr, *ptr2;
375 
376  str = g_strdup(server);
377 
378  if (str) {
379  /* [server]:port format */
380  ptr = strchr(str, '[');
381  if (ptr) {
382  ptr++;
383  ptr2 = strchr(ptr, ']');
384  if (ptr2) {
385  *ptr2++ = '\0';
386  if (*ptr2 == ':')
387  defaultport = atoi(ptr2 + 1);
388  }
389  if (host)
390  *host = g_strdup(ptr);
391  if (port)
392  *port = defaultport;
393  g_free(str);
394  g_debug ("(%s) - host: %s", __func__, *host);
395  g_debug ("(%s) - port: %d", __func__, *port);
396  return;
397  }
398 
399  /* server:port format, IPv6 cannot use this format */
400  ptr = strchr(str, ':');
401  if (ptr) {
402  ptr2 = strchr(ptr + 1, ':');
403  if (ptr2 == NULL) {
404  *ptr++ = '\0';
405  defaultport = atoi(ptr);
406  }
407  /* More than one ':' means this is IPv6 address. Treat it as a whole address */
408  }
409  }
410 
411  if (host)
412  *host = str;
413  else
414  g_free(str);
415  if (port)
416  *port = defaultport;
417 
418  g_debug ("(%s) - host: %s", __func__, *host);
419  g_debug ("(%s) - port: %d", __func__, *port);
420 }
421 
422 void remmina_public_get_server_port(const gchar *server, gint defaultport, gchar **host, gint *port)
423 {
424  TRACE_CALL(__func__);
425 
426  const gchar *nul_terminated_server = NULL;
427  if (server != NULL) {
428  if(strstr(g_strdup(server), "ID:") != NULL) {
429  g_debug ("(%s) - Using remmina_public_get_server_port_old to parse the repeater ID", __func__);
430  remmina_public_get_server_port_old (server, defaultport, host, port);
431  return;
432  }
433 
434  GNetworkAddress *address;
435  GError *err = NULL;
436 
437  nul_terminated_server = g_strdup (server);
438  g_debug ("(%s) - Parsing server: %s, default port: %d", __func__, server, defaultport);
439  address = (GNetworkAddress*)g_network_address_parse ((const gchar *)nul_terminated_server, defaultport, &err);
440 
441  if (address == NULL) {
442  g_debug ("(%s) - Error converting server string: %s, with error: %s", __func__, nul_terminated_server, err->message);
443  *host = NULL;
444  if (err)
445  g_error_free(err);
446  } else {
447 
448  *host = g_strdup(g_network_address_get_hostname (address));
449  *port = g_network_address_get_port (address);
450  }
451  } else
452  *host = NULL;
453 
454  if (port == 0)
455  *port = defaultport;
456 
457  g_debug ("(%s) - host: %s", __func__, *host);
458  g_debug ("(%s) - port: %d", __func__, *port);
459 
460  return;
461 }
462 
463 gboolean remmina_public_get_xauth_cookie(const gchar *display, gchar **msg)
464 {
465  TRACE_CALL(__func__);
466  gchar buf[200];
467  gchar *out = NULL;
468  gchar *ptr;
469  GError *error = NULL;
470  gboolean ret;
471 
472  if (!display)
473  display = gdk_display_get_name(gdk_display_get_default());
474 
475  g_snprintf(buf, sizeof(buf), "xauth list %s", display);
476  ret = g_spawn_command_line_sync(buf, &out, NULL, NULL, &error);
477  if (ret) {
478  if ((ptr = g_strrstr(out, "MIT-MAGIC-COOKIE-1")) == NULL) {
479  *msg = g_strdup_printf("xauth returns %s", out);
480  ret = FALSE;
481  }else {
482  ptr += 19;
483  while (*ptr == ' ')
484  ptr++;
485  *msg = g_strndup(ptr, 32);
486  }
487  g_free(out);
488  }else {
489  *msg = g_strdup(error->message);
490  }
491  return ret;
492 }
493 
494 gint remmina_public_open_xdisplay(const gchar *disp)
495 {
496  TRACE_CALL(__func__);
497  gchar *display;
498  gchar *ptr;
499  gint port;
500  struct sockaddr_un addr;
501  gint sock = -1;
502 
503  display = g_strdup(disp);
504  ptr = g_strrstr(display, ":");
505  if (ptr) {
506  *ptr++ = '\0';
507  /* Assume you are using a local display… might need to implement remote display in the future */
508  if (display[0] == '\0' || strcmp(display, "unix") == 0) {
509  port = atoi(ptr);
510  sock = socket(AF_UNIX, SOCK_STREAM, 0);
511  if (sock >= 0) {
512  memset(&addr, 0, sizeof(addr));
513  addr.sun_family = AF_UNIX;
514  snprintf(addr.sun_path, sizeof(addr.sun_path), X_UNIX_SOCKET, port);
515  if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
516  close(sock);
517  sock = -1;
518  }
519  }
520  }
521  }
522 
523  g_free(display);
524  return sock;
525 }
526 
527 /* Find hardware keycode for the requested keyval */
528 guint16 remmina_public_get_keycode_for_keyval(GdkKeymap *keymap, guint keyval)
529 {
530  TRACE_CALL(__func__);
531  GdkKeymapKey *keys = NULL;
532  gint length = 0;
533  guint16 keycode = 0;
534 
535  if (gdk_keymap_get_entries_for_keyval(keymap, keyval, &keys, &length)) {
536  keycode = keys[0].keycode;
537  g_free(keys);
538  }
539  return keycode;
540 }
541 
542 /* Check if the requested keycode is a key modifier */
543 gboolean remmina_public_get_modifier_for_keycode(GdkKeymap *keymap, guint16 keycode)
544 {
545  TRACE_CALL(__func__);
546  //g_return_val_if_fail(keycode > 0, FALSE);
547  if (keycode > 0) return FALSE;
548 #ifdef GDK_WINDOWING_X11
549  return gdk_x11_keymap_key_is_modifier(keymap, keycode);
550 #else
551  return FALSE;
552 #endif
553 }
554 
555 /* Load a GtkBuilder object from a filename */
556 GtkBuilder* remmina_public_gtk_builder_new_from_file(gchar *filename)
557 {
558  TRACE_CALL(__func__);
559  GError *err = NULL;
560  gchar *ui_path = g_strconcat(REMMINA_RUNTIME_UIDIR, G_DIR_SEPARATOR_S, filename, NULL);
561  GtkBuilder *builder = gtk_builder_new();
562  gtk_builder_add_from_file(builder, ui_path, &err);
563  if (err != NULL) {
564  g_print("Error adding build from file. Error: %s", err->message);
565  g_error_free(err);
566  }
567  g_free(ui_path);
568  return builder;
569 }
570 
571 /* Load a GtkBuilder object from a resource */
573 {
574  TRACE_CALL(__func__);
575  GError *err = NULL;
576  GtkBuilder *builder = gtk_builder_new();
577  gtk_builder_add_from_resource (builder, resource, &err);
578  //GtkBuilder *builder = gtk_builder_new_from_resource (resource);
579  if (err != NULL) {
580  g_print("Error adding build from resource. Error: %s", err->message);
581  g_error_free(err);
582  }
583  return builder;
584 }
585 
586 /* Change parent container for a widget
587  * If possible use this function instead of the deprecated gtk_widget_reparent */
588 void remmina_public_gtk_widget_reparent(GtkWidget *widget, GtkContainer *container)
589 {
590  TRACE_CALL(__func__);
591  g_object_ref(widget);
592  gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(widget)), widget);
593  gtk_container_add(container, widget);
594  g_object_unref(widget);
595 }
596 
597 /* Validate the inserted value for a new resolution */
598 gboolean remmina_public_resolution_validation_func(const gchar *new_str, gchar **error)
599 {
600  TRACE_CALL(__func__);
601  gint i;
602  gint width, height;
603  gboolean splitted;
604  gboolean result;
605 
606  width = 0;
607  height = 0;
608  splitted = FALSE;
609  result = TRUE;
610  for (i = 0; new_str[i] != '\0'; i++) {
611  if (new_str[i] == 'x') {
612  if (splitted) {
613  result = FALSE;
614  break;
615  }
616  splitted = TRUE;
617  continue;
618  }
619  if (new_str[i] < '0' || new_str[i] > '9') {
620  result = FALSE;
621  break;
622  }
623  if (splitted) {
624  height = 1;
625  }else {
626  width = 1;
627  }
628  }
629 
630  if (width == 0 || height == 0)
631  result = FALSE;
632 
633  if (!result)
634  *error = g_strdup(_("Please enter format 'widthxheight'."));
635  return result;
636 }
637 
638 /* Used to send desktop notifications */
639 void remmina_public_send_notification(const gchar *notification_id,
640  const gchar *notification_title, const gchar *notification_message)
641 {
642  TRACE_CALL(__func__);
643 
644  g_autoptr(GNotification) n = NULL;
645  gint priority = G_NOTIFICATION_PRIORITY_NORMAL;
646 
647  n = g_notification_new(notification_title);
648  g_notification_set_body(n, notification_message);
649  if (g_strcmp0 (notification_id, "remmina-security-trust-all-id") == 0) {
650  g_debug ("remmina_public_send_notification: We got a remmina-security-trust-all-id notification");
651  priority = G_NOTIFICATION_PRIORITY_HIGH;
655  g_notification_set_default_action_and_target (n, "app.preferences", "i", 5);
656  g_notification_add_button_with_target (n, _("Change security settings"), "app.preferences", "i", 5);
657  }
658 #if GLIB_CHECK_VERSION(2, 42, 0)
659  g_notification_set_priority(n, priority);
660 #endif
661  g_application_send_notification(g_application_get_default(), notification_id, n);
662 }
663 
664 /* Replaces all occurrences of search in a new copy of string by replacement. */
665 gchar* remmina_public_str_replace(const gchar *string, const gchar *search, const gchar *replacement)
666 {
667  TRACE_CALL(__func__);
668  gchar *str, **arr;
669 
670  g_return_val_if_fail(string != NULL, NULL);
671  g_return_val_if_fail(search != NULL, NULL);
672 
673  if (replacement == NULL)
674  replacement = "";
675 
676  arr = g_strsplit(string, search, -1);
677  if (arr != NULL && arr[0] != NULL)
678  str = g_strjoinv(replacement, arr);
679  else
680  str = g_strdup(string);
681 
682  g_strfreev(arr);
683  return str;
684 }
685 
686 /* Replaces all occurrences of search in a new copy of string by replacement
687  * and overwrites the original string */
688 gchar* remmina_public_str_replace_in_place(gchar *string, const gchar *search, const gchar *replacement)
689 {
690  TRACE_CALL(__func__);
691  gchar *new_string = remmina_public_str_replace(string, search, replacement);
692  string = g_strdup(new_string);
693  g_free(new_string);
694  return string;
695 }
696 
697 int remmina_public_split_resolution_string(const char *resolution_string, int *w, int *h)
698 {
699  int lw, lh;
700 
701  if (resolution_string == NULL || resolution_string[0] == 0)
702  return 0;
703  if (sscanf(resolution_string, "%dx%d", &lw, &lh) != 2)
704  return 0;
705  *w = lw;
706  *h = lh;
707  return 1;
708 }
709 
710 /* Return TRUE if current gtk version library in use is greater or equal than
711  * the required major.minor.micro */
712 gboolean remmina_gtk_check_version(guint major, guint minor, guint micro)
713 {
714  guint rtmajor, rtminor, rtmicro;
715  rtmajor = gtk_get_major_version();
716  if (rtmajor > major) {
717  return TRUE;
718  }else if (rtmajor == major) {
719  rtminor = gtk_get_minor_version();
720  if (rtminor > minor) {
721  return TRUE;
722  }else if (rtminor == minor) {
723  rtmicro = gtk_get_micro_version();
724  if (rtmicro >= micro) {
725  return TRUE;
726  }else {
727  return FALSE;
728  }
729  }else {
730  return FALSE;
731  }
732  }else {
733  return FALSE;
734  }
735 }
gboolean remmina_public_resolution_validation_func(const gchar *new_str, gchar **error)
gboolean remmina_public_get_xauth_cookie(const gchar *display, gchar **msg)
gboolean remmina_public_get_modifier_for_keycode(GdkKeymap *keymap, guint16 keycode)
GtkBuilder * remmina_public_gtk_builder_new_from_file(gchar *filename)
GtkWidget * remmina_public_create_combo_text_d(const gchar *text, const gchar *def, const gchar *empty_choice)
GtkBuilder * remmina_public_gtk_builder_new_from_resource(gchar *resource)
gint remmina_public_open_unix_sock(const char *unixsock)
Return a file descriptor handle for a unix socket.
gint remmina_public_open_xdisplay(const gchar *disp)
GtkWidget * remmina_public_create_combo_map(const gpointer *key_value_list, const gchar *def, gboolean use_icon, const gchar *domain)
gchar * remmina_public_combine_path(const gchar *path1, const gchar *path2)
void remmina_public_send_notification(const gchar *notification_id, const gchar *notification_title, const gchar *notification_message)
gchar * remmina_public_str_replace_in_place(gchar *string, const gchar *search, const gchar *replacement)
void remmina_public_popup_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
guint16 remmina_public_get_keycode_for_keyval(GdkKeymap *keymap, guint keyval)
GtkWidget * remmina_public_create_combo_entry(const gchar *text, const gchar *def, gboolean descending)
void remmina_public_load_combo_text_d(GtkWidget *combo, const gchar *text, const gchar *def, const gchar *empty_choice)
int remmina_public_split_resolution_string(const char *resolution_string, int *w, int *h)
GtkWidget * remmina_public_create_combo(gboolean use_icon)
void remmina_public_gtk_widget_reparent(GtkWidget *widget, GtkContainer *container)
GtkWidget * remmina_public_create_combo_mapint(const gpointer *key_value_list, gint def, gboolean use_icon, const gchar *domain)
gboolean remmina_gtk_check_version(guint major, guint minor, guint micro)
void remmina_public_get_server_port_old(const gchar *server, gint defaultport, gchar **host, gint *port)
void remmina_public_get_server_port(const gchar *server, gint defaultport, gchar **host, gint *port)
gchar * remmina_public_str_replace(const gchar *string, const gchar *search, const gchar *replacement)
gchar * remmina_public_combo_get_active_text(GtkComboBox *combo)
void remmina_public_create_group(GtkGrid *grid, const gchar *group, gint row, gint rows, gint cols)