Remmina - The GTK+ Remote Desktop Client  v1.4.33
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_utils.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2016-2023 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  */
34 
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/utsname.h>
42 #include <locale.h>
43 
44 #include <glib/gi18n.h>
45 #include <glib/gstdio.h>
46 #include <gio/gio.h>
47 #include "remmina_sodium.h"
49 
51 #define EMPTY(ptr) \
52  (!(ptr) || !*(ptr))
53 
54 struct utsname u;
55 
56 /* Copyright (C) 1998 VMware, Inc. All rights reserved.
57  * Some of the code in this file is taken from the VMware open client.
58  */
59 typedef struct lsb_distro_info {
60  gchar * name;
61  gchar * scanstring;
63 
64 /*
65  * static LSBDistroInfo lsbFields[] = {
66  * { "DISTRIB_ID=", "DISTRIB_ID=%s" },
67  * { "DISTRIB_RELEASE=", "DISTRIB_RELEASE=%s" },
68  * { "DISTRIB_CODENAME=", "DISTRIB_CODENAME=%s" },
69  * { "DISTRIB_DESCRIPTION=", "DISTRIB_DESCRIPTION=%s" },
70  * { NULL, NULL },
71  * };
72  */
73 
74 typedef struct distro_info {
75  gchar * name;
76  gchar * filename;
77 } DistroInfo;
78 
79 static DistroInfo distroArray[] = {
80  { "RedHat", "/etc/redhat-release" },
81  { "RedHat", "/etc/redhat_version" },
82  { "Sun", "/etc/sun-release" },
83  { "SuSE", "/etc/SuSE-release" },
84  { "SuSE", "/etc/novell-release" },
85  { "SuSE", "/etc/sles-release" },
86  { "SuSE", "/etc/os-release" },
87  { "Debian", "/etc/debian_version" },
88  { "Debian", "/etc/debian_release" },
89  { "Ubuntu", "/etc/lsb-release" },
90  { "Mandrake", "/etc/mandrake-release" },
91  { "Mandriva", "/etc/mandriva-release" },
92  { "Mandrake", "/etc/mandrakelinux-release" },
93  { "TurboLinux", "/etc/turbolinux-release" },
94  { "Fedora Core", "/etc/fedora-release" },
95  { "Gentoo", "/etc/gentoo-release" },
96  { "Novell", "/etc/nld-release" },
97  { "Annvix", "/etc/annvix-release" },
98  { "Arch", "/etc/arch-release" },
99  { "Arklinux", "/etc/arklinux-release" },
100  { "Aurox", "/etc/aurox-release" },
101  { "BlackCat", "/etc/blackcat-release" },
102  { "Cobalt", "/etc/cobalt-release" },
103  { "Conectiva", "/etc/conectiva-release" },
104  { "Immunix", "/etc/immunix-release" },
105  { "Knoppix", "/etc/knoppix_version" },
106  { "Linux-From-Scratch", "/etc/lfs-release" },
107  { "Linux-PPC", "/etc/linuxppc-release" },
108  { "MkLinux", "/etc/mklinux-release" },
109  { "PLD", "/etc/pld-release" },
110  { "Slackware", "/etc/slackware-version" },
111  { "Slackware", "/etc/slackware-release" },
112  { "SMEServer", "/etc/e-smith-release" },
113  { "Solaris", "/etc/release" },
114  { "Solus", "/etc/solus-release" },
115  { "Tiny Sofa", "/etc/tinysofa-release" },
116  { "UltraPenguin", "/etc/ultrapenguin-release" },
117  { "UnitedLinux", "/etc/UnitedLinux-release" },
118  { "VALinux", "/etc/va-release" },
119  { "Yellow Dog", "/etc/yellowdog-release" },
120  { NULL, NULL },
121 };
122 
123 gint remmina_utils_strpos(const gchar *haystack, const gchar *needle)
124 {
125  TRACE_CALL(__func__);
126  const gchar *sub;
127 
128  if (!*needle)
129  return -1;
130 
131  sub = strstr(haystack, needle);
132  if (!sub)
133  return -1;
134 
135  return sub - haystack;
136 }
137 
138 /* end can be -1 for haystack->len.
139  * returns: position of found text or -1.
140  * (C) Taken from geany */
141 gint remmina_utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
142 {
143  TRACE_CALL(__func__);
144  gint pos;
145 
146  g_return_val_if_fail(haystack != NULL, -1);
147  if (haystack->len == 0)
148  return -1;
149 
150  g_return_val_if_fail(start >= 0, -1);
151  if (start >= (gint)haystack->len)
152  return -1;
153 
154  g_return_val_if_fail(!EMPTY(needle), -1);
155 
156  if (end < 0)
157  end = haystack->len;
158 
159  pos = remmina_utils_strpos(haystack->str + start, needle);
160  if (pos == -1)
161  return -1;
162 
163  pos += start;
164  if (pos >= end)
165  return -1;
166  return pos;
167 }
168 
169 /* Replaces @len characters from offset @a pos.
170  * len can be -1 to replace the remainder of @a str.
171  * returns: pos + strlen(replace).
172  * (C) Taken from geany */
173 gint remmina_utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
174 {
175  TRACE_CALL(__func__);
176  g_string_erase(str, pos, len);
177  if (replace) {
178  g_string_insert(str, pos, replace);
179  pos += strlen(replace);
180  }
181  return pos;
182 }
183 
193 guint remmina_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
194 {
195  TRACE_CALL(__func__);
196  guint count = 0;
197  gint pos = 0;
198  gsize needle_length = strlen(needle);
199 
200  while (1) {
201  pos = remmina_utils_string_find(haystack, pos, -1, needle);
202 
203  if (pos == -1)
204  break;
205 
206  pos = remmina_utils_string_replace(haystack, pos, needle_length, replace);
207  count++;
208  }
209  return count;
210 }
211 
220 gchar *remmina_utils_string_strip(const gchar *s)
221 {
222  gchar *p = g_malloc(strlen(s) + 1);
223 
224  if (p) {
225  gchar *p2 = p;
226  while (*s != '\0') {
227  if (*s != '\t' && *s != '\n' && *s != '\"')
228  *p2++ = *s++;
229  else
230  ++s;
231  }
232  *p2 = '\0';
233  }
234  return p;
235 }
236 
251 static gchar *remmina_utils_read_distrofile(gchar *filename)
252 {
253  TRACE_CALL(__func__);
254  gsize file_sz;
255  struct stat st;
256  gchar *distro_desc = NULL;
257  GError *err = NULL;
258 
259  if (g_stat(filename, &st) == -1) {
260  g_debug("%s: could not stat the file %s\n", __func__, filename);
261  return NULL;
262  }
263 
264  g_debug("%s: File %s is %lu bytes long\n", __func__, filename, st.st_size);
265  if (st.st_size > 131072)
266  return NULL;
267 
268  if (!g_file_get_contents(filename, &distro_desc, &file_sz, &err)) {
269  g_debug("%s: could not get the file content%s: %s\n", __func__, filename, err->message);
270  g_error_free(err);
271  return NULL;
272  }
273 
274  if (file_sz == 0) {
275  g_debug("%s: Cannot work with empty file.\n", __FUNCTION__);
276  return NULL;
277  }
278 
279  g_debug("%s: Distro description %s\n", __func__, distro_desc);
280  return distro_desc;
281 }
282 
288 {
289  gchar *lang = setlocale(LC_ALL, NULL);
290  gchar *ptr;
291 
292  if (!lang || lang[0] == '\0') {
293  lang = "en_US\0";
294  } else {
295  ptr = strchr(lang, '.');
296  if (ptr != NULL)
297  *ptr = '\0';
298  }
299 
300  return lang;
301 }
307 {
308  TRACE_CALL(__func__);
309  return u.sysname;
310 }
311 
317 {
318  TRACE_CALL(__func__);
319  return u.release;
320 }
321 
327 {
328  TRACE_CALL(__func__);
329  return u.machine;
330 }
331 
337 {
338  TRACE_CALL(__func__);
339  gchar *lsb_id = NULL;
340  if (g_spawn_command_line_sync("/usr/bin/lsb_release -si", &lsb_id, NULL, NULL, NULL))
341  return lsb_id;
342  return NULL;
343 }
344 
350 {
351  TRACE_CALL(__func__);
352  gchar *lsb_description = NULL;
353  GError *err = NULL;
354 
355  if (g_spawn_command_line_sync("/usr/bin/lsb_release -sd", &lsb_description, NULL, NULL, &err)) {
356  return lsb_description;
357  } else {
358  g_debug("%s: could not execute lsb_release %s\n", __func__, err->message);
359  g_error_free(err);
360  }
361  g_debug("%s: lsb_release %s\n", __func__, lsb_description);
362  return NULL;
363 }
364 
370 {
371  TRACE_CALL(__func__);
372  gchar *lsb_release = NULL;
373  if (g_spawn_command_line_sync("/usr/bin/lsb_release -sr", &lsb_release, NULL, NULL, NULL))
374  return lsb_release;
375  return NULL;
376 }
377 
383 {
384  TRACE_CALL(__func__);
385  gchar *lsb_codename = NULL;
386  if (g_spawn_command_line_sync("/usr/bin/lsb_release -sc", &lsb_codename, NULL, NULL, NULL))
387  return lsb_codename;
388  return NULL;
389 }
390 
397 {
398  TRACE_CALL(__func__);
399  gchar *etc_release = NULL;
400  gint i;
401  GHashTable *r;
402 
403  r = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
404 
405  for (i = 0; distroArray[i].filename != NULL; i++) {
406  g_debug("%s: File %s\n", __func__, distroArray[i].filename);
407  etc_release = remmina_utils_read_distrofile(distroArray[i].filename);
408  if (etc_release) {
409  if (etc_release[0] != '\0') {
410  g_debug("%s: Distro description %s\n", __func__, etc_release);
411  g_hash_table_insert(r, distroArray[i].filename, etc_release);
412  } else {
413  g_free(etc_release);
414  }
415  }
416  }
417  return r;
418 }
419 
425 {
426  TRACE_CALL(__func__);
427  gchar *kernel_string;
428 
429  if (uname(&u) == -1)
430  g_print("uname:");
431 
432  kernel_string = g_strdup_printf("%s;%s;%s\n",
436  if (!kernel_string || kernel_string[0] == '\0') {
437  if (kernel_string)
438  g_free(kernel_string);
439  kernel_string = g_strdup_printf("%s;%s;%s\n",
440  "UNKNOWN",
441  "UNKNOWN",
442  "UNKNOWN");
443  }
444  return kernel_string;
445 }
446 
460 gchar *remmina_sha1_file(const gchar *filename)
461 {
462  FILE *file;
463 
464 #define BLOCK_SIZE 4096
465  unsigned char block[BLOCK_SIZE];
466  size_t bytes_read;
467  GChecksum *sha1;
468  char *digest = NULL;
469 
470  file = fopen(filename, "r");
471  if (file == NULL)
472  return NULL;
473 
474  sha1 = g_checksum_new(G_CHECKSUM_SHA1);
475  if (sha1 == NULL)
476  goto DONE;
477 
478  while (1) {
479  bytes_read = fread(block, 1, 4096, file);
480  if (bytes_read == 0) {
481  if (feof(file))
482  break;
483  else if (ferror(file))
484  goto DONE;
485  } else {
486  g_checksum_update(sha1, block, bytes_read);
487  }
488  }
489 
490  digest = g_strdup(g_checksum_get_string(sha1));
491 
492 DONE:
493  if (sha1)
494  g_checksum_free(sha1);
495  if (file)
496  fclose(file);
497 
498  return digest;
499 }
struct distro_info DistroInfo
struct utsname u
Definition: remmina_utils.c:54
gint remmina_utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
gint remmina_utils_strpos(const gchar *haystack, const gchar *needle)
gchar * remmina_utils_get_lsb_codename()
Print the Distribution codename as specified by the lsb_release command.
gchar * name
Definition: remmina_utils.c:75
gchar * remmina_utils_get_lsb_id()
Print the Distributor as specified by the lsb_release command.
const gchar * remmina_utils_get_kernel_name()
Return the OS name as in "uname -s".
const gchar * remmina_utils_get_kernel_release()
Return the OS version as in "uname -r".
gchar * remmina_utils_string_strip(const gchar *s)
Strip , and " from a given string.
static gchar * remmina_utils_read_distrofile(gchar *filename)
OS related functions.
gchar * remmina_utils_get_lsb_release()
Print the Distribution release name as specified by the lsb_release command.
gchar * remmina_utils_get_lang()
Return the current language defined in the LC_ALL.
const gchar * remmina_utils_get_os_info()
A sample function to show how use the other fOS related functions.
struct lsb_distro_info LSBDistroInfo
gchar * remmina_utils_get_lsb_description()
Print the Distribution description as specified by the lsb_release command.
gchar * remmina_sha1_file(const gchar *filename)
Create a hexadecimal string version of the SHA-1 digest of the contents of the named file...
gchar * scanstring
Definition: remmina_utils.c:61
guint remmina_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
static DistroInfo distroArray[]
Definition: remmina_utils.c:79
gchar * filename
Definition: remmina_utils.c:76
GHashTable * remmina_utils_get_etc_release()
Print the distribution description if found.
gint remmina_utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
const gchar * remmina_utils_get_kernel_arch()
Return the machine hardware name as in "uname -m".