Remmina - The GTK+ Remote Desktop Client  v1.3.1
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_stats.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2016-2019 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 
124 #include "config.h"
125 #include <locale.h>
126 #include <string.h>
127 #include <sys/utsname.h>
128 #include <unistd.h>
129 #include <glib.h>
130 #include <glib/gi18n.h>
131 #include <glib/gstdio.h>
132 #include <gtk/gtk.h>
133 
134 #include "remmina_file.h"
135 #include "remmina_file_manager.h"
136 #include "remmina_icon.h"
137 #include "remmina_log.h"
138 #include "remmina_pref.h"
139 #include "remmina_sysinfo.h"
140 #include "remmina_utils.h"
142 
143 #ifdef GDK_WINDOWING_WAYLAND
144  #include <gdk/gdkwayland.h>
145 #endif
146 #ifdef GDK_WINDOWING_X11
147  #include <gdk/gdkx.h>
148 #endif
149 #include "remmina_stats.h"
150 
151 struct utsname u;
152 
153 struct ProfilesData {
154  GHashTable *proto_count;
155  GHashTable *proto_date;
156  const gchar *protocol;
157  const gchar *pdatestr;
158  gint pcount;
159  gchar datestr;
160 };
161 
163 {
164  TRACE_CALL(__func__);
165  GRand *rand;
166  GTimeVal t;
167  gchar *result;
168  int i;
169  static char alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
170 
171  result = g_malloc0(15);
172 
173  g_get_current_time(&t);
174  rand = g_rand_new_with_seed((guint32)t.tv_sec ^ (guint32)t.tv_usec);
175 
176  for (i = 0; i < 7; i++) {
177  result[i] = alpha[g_rand_int_range(rand, 0, sizeof(alpha) - 1)];
178  }
179 
180  g_rand_set_seed(rand, (guint32)t.tv_usec);
181  for (i = 0; i < 7; i++) {
182  result[i + 7] = alpha[g_rand_int_range(rand, 0, sizeof(alpha) - 1)];
183  }
184  g_rand_free(rand);
185 
186  return result;
187 }
188 
190 {
191  TRACE_CALL(__func__);
192  JsonNode *r;
193  GChecksum *chs;
194  const gchar *uname, *hname;
195  const gchar *uid_suffix;
196  gchar *uid_prefix;
197  gchar *uid;
198 
204  /* Generate a new UUID_PREFIX for this installation */
210  }
211 
212  uname = g_get_user_name();
213  hname = g_get_host_name();
214  chs = g_checksum_new(G_CHECKSUM_SHA256);
215  g_checksum_update(chs, (const guchar*)uname, strlen(uname));
216  g_checksum_update(chs, (const guchar*)hname, strlen(hname));
217  uid_suffix = g_checksum_get_string(chs);
218 
219  uid = g_strdup_printf("%s-%.10s", remmina_pref.periodic_usage_stats_uuid_prefix, uid_suffix);
220  g_checksum_free(chs);
221 
222  r = json_node_alloc();
223  json_node_init_string(r, uid);
224 
225  g_free(uid);
226 
227  return r;
228 
229 }
230 
232 {
233  TRACE_CALL(__func__);
234  JsonBuilder *b;
235  JsonNode *r;
236 
237  gchar *kernel_name;
238  gchar *kernel_release;
239  gchar *kernel_arch;
240  gchar *id;
241  gchar *description;
242  GHashTable *etc_release;
243  gchar *release;
244  gchar *codename;
245  GHashTableIter iter;
246  gchar *key, *value;
247 
251  b = json_builder_new();
252  json_builder_begin_object(b);
253 
254  json_builder_set_member_name(b, "kernel_name");
255  kernel_name = g_strdup_printf("%s", remmina_utils_get_kernel_name());
256  if (!kernel_name || kernel_name[0] == '\0') {
257  json_builder_add_null_value(b);
258  }else {
259  json_builder_add_string_value(b, kernel_name);
260  }
261  g_free(kernel_name);
262 
263  json_builder_set_member_name(b, "kernel_release");
264  kernel_release = g_strdup_printf("%s", remmina_utils_get_kernel_release());
265  if (!kernel_release || kernel_release[0] == '\0') {
266  json_builder_add_null_value(b);
267  }else {
268  json_builder_add_string_value(b, kernel_release);
269  }
270  g_free(kernel_release);
271 
272  json_builder_set_member_name(b, "kernel_arch");
273  kernel_arch = g_strdup_printf("%s", remmina_utils_get_kernel_arch());
274  if (!kernel_arch || kernel_arch[0] == '\0') {
275  json_builder_add_null_value(b);
276  }else {
277  json_builder_add_string_value(b, kernel_arch);
278  }
279  g_free(kernel_arch);
280 
281  json_builder_set_member_name(b, "lsb_distributor");
283  if (!id || id[0] == '\0') {
284  json_builder_add_null_value(b);
285  }else {
286  json_builder_add_string_value(b, id);
287  }
288  g_free(id);
289 
290  json_builder_set_member_name(b, "lsb_distro_description");
291  description = remmina_utils_get_lsb_description();
292  if (!description || description[0] == '\0') {
293  json_builder_add_null_value(b);
294  }else {
295  json_builder_add_string_value(b, description);
296  }
297  g_free(description);
298 
299  json_builder_set_member_name(b, "lsb_distro_release");
300  release = remmina_utils_get_lsb_release();
301  if (!release || release[0] == '\0') {
302  json_builder_add_null_value(b);
303  }else {
304  json_builder_add_string_value(b, release);
305  }
306  g_free(release);
307 
308  json_builder_set_member_name(b, "lsb_distro_codename");
309  codename = remmina_utils_get_lsb_codename();
310  if (!codename || codename[0] == '\0') {
311  json_builder_add_null_value(b);
312  }else {
313  json_builder_add_string_value(b, codename);
314  }
315  g_free(codename);
316 
317  etc_release = remmina_utils_get_etc_release();
318  json_builder_set_member_name(b, "etc_release");
319  if (etc_release) {
320  json_builder_begin_object(b);
321  g_hash_table_iter_init (&iter, etc_release);
322  while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&value)) {
323  json_builder_set_member_name(b, key);
324  json_builder_add_string_value(b, value);
325  }
326  json_builder_end_object(b);
327  g_hash_table_remove_all(etc_release);
328  g_hash_table_unref(etc_release);
329  }else {
330  json_builder_add_null_value(b);
331  }
332 
337  json_builder_end_object(b);
338  r = json_builder_get_root(b);
339  g_object_unref(b);
340  return r;
341 }
342 
350 {
351  TRACE_CALL(__func__);
352  JsonBuilder *b;
353  JsonNode *r;
354 
355  gchar *language;
356 
357  language = setlocale (LC_ALL, NULL);
358 
359  b = json_builder_new();
360  json_builder_begin_object(b);
361  json_builder_set_member_name(b, "language");
362 
363  if (!language || language[0] == '\0') {
364  json_builder_add_null_value(b);
365 
366  }else {
367  json_builder_add_string_value(b, language);
368  }
369 
370  json_builder_end_object(b);
371  r = json_builder_get_root(b);
372  g_object_unref(b);
373  return r;
374 
375 }
376 
378 {
379  TRACE_CALL(__func__);
380  JsonBuilder *b;
381  JsonNode *r;
382  gchar *flatpak_info;
383 
387  b = json_builder_new();
388  json_builder_begin_object(b);
389  json_builder_set_member_name(b, "version");
390  json_builder_add_string_value(b, VERSION);
391  json_builder_set_member_name(b, "git_revision");
392  json_builder_add_string_value(b, REMMINA_GIT_REVISION);
393  json_builder_set_member_name(b, "snap_build");
394 #ifdef SNAP_BUILD
395  json_builder_add_int_value(b, 1);
396 #else
397  json_builder_add_int_value(b, 0);
398 #endif
399 
403  json_builder_set_member_name(b, "flatpak_build");
404  /* Flatpak sandbox should contain the file ${XDG_RUNTIME_DIR}/flatpak-info */
405  flatpak_info = g_build_filename(g_get_user_runtime_dir(), "flatpak-info", NULL);
406  if (g_file_test(flatpak_info, G_FILE_TEST_EXISTS)) {
407  json_builder_add_int_value(b, 1);
408  } else {
409  json_builder_add_int_value(b, 0);
410  }
411  g_free(flatpak_info);
412 
413  json_builder_end_object(b);
414  r = json_builder_get_root(b);
415  g_object_unref(b);
416  return r;
417 }
418 
420 {
421  TRACE_CALL(__func__);
422  JsonBuilder *b;
423  JsonNode *r;
424 
429  b = json_builder_new();
430  json_builder_begin_object(b);
431  json_builder_set_member_name(b, "major");
432  json_builder_add_int_value(b, gtk_get_major_version());
433  json_builder_set_member_name(b, "minor");
434  json_builder_add_int_value(b, gtk_get_minor_version());
435  json_builder_set_member_name(b, "micro");
436  json_builder_add_int_value(b, gtk_get_micro_version());
437  json_builder_end_object(b);
438  r = json_builder_get_root(b);
439  g_object_unref(b);
440  return r;
441 
442 }
443 
445 {
446  TRACE_CALL(__func__);
447  JsonNode *r;
448  GdkDisplay *disp;
449  gchar *bkend;
450 
455  disp = gdk_display_get_default();
456 
457 #ifdef GDK_WINDOWING_WAYLAND
458  if (GDK_IS_WAYLAND_DISPLAY(disp)) {
459  bkend = "Wayland";
460  }else
461 #endif
462 #ifdef GDK_WINDOWING_X11
463  if (GDK_IS_X11_DISPLAY(disp)) {
464  bkend = "X11";
465  } else
466 #endif
467  bkend = "n/a";
468 
469  r = json_node_alloc();
470  json_node_init_string(r, bkend);
471 
472  return r;
473 
474 }
475 
477 {
478  TRACE_CALL(__func__);
479  JsonBuilder *b;
480  JsonNode *r;
481  gchar *wmver;
482  gchar *wmname;
483 
484  b = json_builder_new();
485  json_builder_begin_object(b);
486 
487  json_builder_set_member_name(b, "window_manager");
488 
491  if (!wmver || wmver[0] == '\0') {
492  remmina_log_print("Gnome Shell not found\n");
493  }else {
494  remmina_log_printf("Gnome Shell version: %s\n", wmver);
495  json_builder_add_string_value(b, "Gnome Shell");
496  json_builder_set_member_name(b, "gnome_shell_ver");
497  json_builder_add_string_value(b, wmver);
498  goto end;
499  }
500  g_free(wmver);
501 
502  wmname = remmina_sysinfo_get_wm_name();
503  if (!wmname || wmname[0] == '\0') {
505  remmina_log_print("Cannot determine the Window Manger name\n");
506  json_builder_add_string_value(b, "n/a");
507  }else {
508  remmina_log_printf("Window Manger names %s\n", wmname);
509  json_builder_add_string_value(b, wmname);
510  }
511  g_free(wmname);
512 
513  end:
514  json_builder_end_object(b);
515  r = json_builder_get_root(b);
516  g_object_unref(b);
517  return r;
518 }
519 
521 {
522  TRACE_CALL(__func__);
523  JsonBuilder *b;
524  JsonNode *r;
525  gboolean sni;
527  b = json_builder_new();
528  json_builder_begin_object(b);
529 
530  json_builder_set_member_name(b, "appindicator_supported");
532  if (sni) {
534  json_builder_add_int_value(b, 1);
535  json_builder_set_member_name(b, "appindicator_compiled");
536 #ifdef HAVE_LIBAPPINDICATOR
537 
538  json_builder_add_int_value(b, 1);
539 #else
540 
541  json_builder_add_int_value(b, 0);
542 #endif
543  } else {
545  json_builder_add_int_value(b, 0);
546  json_builder_set_member_name(b, "icon_is_active");
549  json_builder_add_int_value(b, 1);
550  json_builder_set_member_name(b, "appindicator_type");
551 #ifdef HAVE_LIBAPPINDICATOR
552 
553  json_builder_add_string_value(b, "AppIndicator on GtkStatusIcon/xembed");
554 #else
555 
556  json_builder_add_string_value(b, "Remmina icon on GtkStatusIcon/xembed");
557 #endif
558  }else {
560  json_builder_add_int_value(b, 0);
561  }
562  }
563  json_builder_end_object(b);
564  r = json_builder_get_root(b);
565  g_object_unref(b);
566  return r;
567 }
568 
575 static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_data)
576 {
577  TRACE_CALL(__func__);
578 
579  gint count = 0;
580  gpointer pcount, kpo;
581  gpointer pdate, kdo;
582  gchar *sday, *smonth, *syear;
583  gchar *dday, *dmonth, *dyear;
584 
585  GDateTime *ds;
586  GDateTime *dd;
588  struct ProfilesData* pdata;
589  pdata = (struct ProfilesData*)user_data;
590 
591  pdata->protocol = remmina_file_get_string(remminafile, "protocol");
592  pdata->pdatestr = remmina_file_get_string(remminafile, "last_success");
593 
594  ds = dd = NULL;
595  if (pdata->pdatestr && pdata->pdatestr[0] != '\0' && strlen(pdata->pdatestr) >= 6) {
596  dyear = g_strdup_printf("%.4s", pdata->pdatestr);
597  dmonth = g_strdup_printf("%.2s", pdata->pdatestr + 4);
598  dday = g_strdup_printf("%.2s", pdata->pdatestr + 6);
599  dd = g_date_time_new_local(g_ascii_strtoll(dyear, NULL, 0),
600  g_ascii_strtoll(dmonth, NULL, 0),
601  g_ascii_strtoll(dday, NULL, 0), 0, 0, 0.0);
602  g_free(dyear);
603  g_free(dmonth);
604  g_free(dday);
605  }
606 
607 
608  if (pdata->protocol && pdata->protocol[0] != '\0') {
609  if (g_hash_table_lookup_extended(pdata->proto_count, pdata->protocol, &kpo, &pcount)) {
610  count = GPOINTER_TO_INT(pcount) + 1;
611  }else {
612  count = 1;
613  g_hash_table_insert(pdata->proto_count, g_strdup(pdata->protocol), GINT_TO_POINTER(count));
614  }
615  g_hash_table_replace(pdata->proto_count, g_strdup(pdata->protocol), GINT_TO_POINTER(count));
616  pdate = NULL;
617  if (g_hash_table_lookup_extended(pdata->proto_date, pdata->protocol, &kdo, &pdate)) {
618 
619  ds = NULL;
620  if (pdate && strlen(pdate) >= 6) {
621  syear = g_strdup_printf("%.4s", (char*)pdate);
622  smonth = g_strdup_printf("%.2s", (char*)pdate + 4);
623  sday = g_strdup_printf("%.2s", (char*)pdate + 6);
624  ds = g_date_time_new_local(g_ascii_strtoll(syear, NULL, 0),
625  g_ascii_strtoll(smonth, NULL, 0),
626  g_ascii_strtoll(sday, NULL, 0), 0, 0, 0.0);
627  g_free(syear);
628  g_free(smonth);
629  g_free(sday);
630  }
631 
633  if (ds && dd) {
634  gint res = g_date_time_compare( ds, dd );
636  if (res < 0 ) {
637  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
638  }
639  }
641  if (!ds && dd) {
642  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
643  }
645  if ((!ds && !dd) && pdata->pdatestr) {
646  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), NULL);
647  }
648  }else {
651  if (pdata->pdatestr) {
652  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
653  }else {
655  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), NULL);
656  }
657  }
658  }
659  if (dd)
660  g_date_time_unref(dd);
661  if (ds)
662  g_date_time_unref(ds);
663 }
664 
689 {
690  TRACE_CALL(__func__);
691 
692  JsonBuilder *b;
693  JsonNode *r;
694  gchar *s;
695 
696  gint profiles_count;
697  GHashTableIter pcountiter, pdateiter;
698  gpointer pcountkey, pcountvalue;
699  gpointer pdatekey, pdatevalue;
700 
701  struct ProfilesData *pdata;
702  pdata = g_malloc0(sizeof(struct ProfilesData));
703 
704  b = json_builder_new();
705  json_builder_begin_object(b);
706 
707  json_builder_set_member_name(b, "profile_count");
708 
712  pdata->proto_date = g_hash_table_new_full(g_str_hash, g_str_equal,
713  (GDestroyNotify)g_free, (GDestroyNotify)g_free);
714  pdata->proto_count = g_hash_table_new_full(g_str_hash, g_str_equal,
715  (GDestroyNotify)g_free, NULL);
716 
717  profiles_count = remmina_file_manager_iterate(
719  (gpointer)pdata);
720 
721  json_builder_add_int_value(b, profiles_count);
722 
723  g_hash_table_iter_init(&pcountiter, pdata->proto_count);
724  while (g_hash_table_iter_next(&pcountiter, &pcountkey, &pcountvalue)) {
725  json_builder_set_member_name(b, (gchar*)pcountkey);
726  json_builder_add_int_value(b, GPOINTER_TO_INT(pcountvalue));
727  }
728 
729  g_hash_table_iter_init(&pdateiter, pdata->proto_date);
730  while (g_hash_table_iter_next(&pdateiter, &pdatekey, &pdatevalue)) {
731  s = g_strdup_printf("DATE_%s", (gchar*)pdatekey);
732  json_builder_set_member_name(b, s);
733  g_free(s);
734  json_builder_add_string_value(b, (gchar*)pdatevalue);
735  }
736 
737  json_builder_end_object(b);
738  r = json_builder_get_root(b);
739  g_object_unref(b);
740 
741  g_hash_table_remove_all(pdata->proto_date);
742  g_hash_table_unref(pdata->proto_date);
743  g_hash_table_remove_all(pdata->proto_count);
744  g_hash_table_unref(pdata->proto_count);
745 
746  g_free(pdata);
747 
748  return r;
749 }
750 
759 {
760  TRACE_CALL(__func__);
761  JsonBuilder *b;
762  JsonNode *n;
763 
764  b = json_builder_new();
765  json_builder_begin_object(b);
766 
767  n = remmina_stats_get_uid();
768  json_builder_set_member_name(b, "UID");
769  json_builder_add_value(b, n);
770 
772  json_builder_set_member_name(b, "REMMINAVERSION");
773  json_builder_add_value(b, n);
774 
775  if (uname(&u) == -1)
776  g_print("uname:");
777 
779  json_builder_set_member_name(b, "SYSTEM");
780  json_builder_add_value(b, n);
781 
787  json_builder_set_member_name(b, "ENVIRONMENT");
788  json_builder_add_value(b, n);
789 
791  json_builder_set_member_name(b, "GTKVERSION");
792  json_builder_add_value(b, n);
793 
795  json_builder_set_member_name(b, "GTKBACKEND");
796  json_builder_add_value(b, n);
797 
799  json_builder_set_member_name(b, "WINDOWMANAGER");
800  json_builder_add_value(b, n);
801 
803  json_builder_set_member_name(b, "APPINDICATOR");
804  json_builder_add_value(b, n);
805 
807  json_builder_set_member_name(b, "PROFILES");
808  json_builder_add_value(b, n);
809 
810  json_builder_end_object(b);
811  n = json_builder_get_root(b);
812  g_object_unref(b);
813 
814  return n;
815 
816 }
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:350
JsonNode * remmina_stats_get_uid()
const gchar * protocol
static gchar * remmina_stats_gen_random_uuid_prefix()
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
JsonNode * remmina_stats_get_user_env()
Gets the following user environment:
gint pcount
Date in string format in the proto_date hash table.
GHashTable * proto_count
gchar * remmina_utils_get_lsb_codename()
Print the Distribution codename as specified by the lsb_release command.
gboolean remmina_icon_is_available(void)
Determine whenever the Remmina icon is available.
Definition: remmina_icon.c:376
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".
struct utsname u
JsonNode * remmina_stats_get_gtk_version()
JsonNode * remmina_stats_get_os_info()
General utility functions, non-GTK related.
gchar * remmina_utils_get_lsb_release()
Print the Distribution release name as specified by the lsb_release command.
gchar * periodic_usage_stats_uuid_prefix
Definition: remmina_pref.h:198
gchar * remmina_utils_get_lsb_description()
Print the Distribution description as specified by the lsb_release command.
gchar * remmina_sysinfo_get_gnome_shell_version()
Query DBUS to get gnome shell version.
JsonNode * remmina_stats_get_gtk_backend()
RemminaPref remmina_pref
Definition: rcw.c:71
JsonNode * remmina_stats_get_profiles()
Add a json member profile_count with a child for each protocol used by the user.
static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_data)
Given a remmina file, fills a structure containing profiles keys/value tuples.
JsonNode * remmina_stats_get_all()
Get all statistics in json format to send periodically to the PHP server.
gchar * remmina_sysinfo_get_wm_name()
Query environment variables to get the Window manager name.
const gchar * pdatestr
Key in the proto_count hash table.
gboolean remmina_pref_save(void)
Definition: remmina_pref.c:656
gboolean remmina_sysinfo_is_appindicator_available()
JsonNode * remmina_stats_get_indicator()
JsonNode * remmina_stats_get_wm_name()
void remmina_log_printf(const gchar *fmt,...)
Definition: remmina_log.c:192
GHashTable * remmina_utils_get_etc_release()
Print the distribution description if found.
void remmina_log_print(const gchar *text)
Definition: remmina_log.c:183
const gchar * remmina_utils_get_kernel_arch()
Return the machine hardware name as in "uname -m".
GHashTable * proto_date
gint remmina_file_manager_iterate(GFunc func, gpointer user_data)
JsonNode * remmina_stats_get_version()