Remmina - The GTK+ Remote Desktop Client  1.2.0
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-2018 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 <string.h>
126 #include <sys/utsname.h>
127 #include <unistd.h>
128 #include <glib.h>
129 #include <glib/gi18n.h>
130 #include <glib/gstdio.h>
131 #include <gtk/gtk.h>
132 
133 #include "remmina_file.h"
134 #include "remmina_file_manager.h"
135 #include "remmina_icon.h"
136 #include "remmina_log.h"
137 #include "remmina_pref.h"
138 #include "remmina_sysinfo.h"
139 #include "remmina_utils.h"
141 
142 #ifdef GDK_WINDOWING_WAYLAND
143  #include <gdk/gdkwayland.h>
144 #endif
145 #ifdef GDK_WINDOWING_X11
146  #include <gdk/gdkx.h>
147 #endif
148 #include "remmina_stats.h"
149 
150 struct utsname u;
151 
152 struct ProfilesData {
153  GHashTable *proto_count;
154  GHashTable *proto_date;
155  const gchar *protocol;
156  const gchar *pdatestr;
157  gint pcount;
158  gchar datestr;
159 };
160 
162 {
163  TRACE_CALL(__func__);
164  GRand *rand;
165  GTimeVal t;
166  gchar *result;
167  int i;
168  static char alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
169 
170  result = g_malloc0(15);
171 
172  g_get_current_time(&t);
173  rand = g_rand_new_with_seed((guint32)t.tv_sec ^ (guint32)t.tv_usec);
174 
175  for (i = 0; i < 7; i++) {
176  result[i] = alpha[g_rand_int_range(rand, 0, sizeof(alpha) - 1)];
177  }
178 
179  g_rand_set_seed(rand, (guint32)t.tv_usec);
180  for (i = 0; i < 7; i++) {
181  result[i + 7] = alpha[g_rand_int_range(rand, 0, sizeof(alpha) - 1)];
182  }
183  g_rand_free(rand);
184 
185  return result;
186 }
187 
189 {
190  TRACE_CALL(__func__);
191  JsonNode *r;
192  GChecksum *chs;
193  const gchar *uname, *hname;
194  const gchar *uid_suffix;
195  gchar *uid_prefix;
196  gchar *uid;
197 
203  /* Generate a new UUID_PREFIX for this installation */
209  }
210 
211  uname = g_get_user_name();
212  hname = g_get_host_name();
213  chs = g_checksum_new(G_CHECKSUM_SHA256);
214  g_checksum_update(chs, (const guchar*)uname, strlen(uname));
215  g_checksum_update(chs, (const guchar*)hname, strlen(hname));
216  uid_suffix = g_checksum_get_string(chs);
217 
218  uid = g_strdup_printf("%s-%.10s", remmina_pref.periodic_usage_stats_uuid_prefix, uid_suffix);
219  g_checksum_free(chs);
220 
221  r = json_node_alloc();
222  json_node_init_string(r, uid);
223 
224  g_free(uid);
225 
226  return r;
227 
228 }
229 
231 {
232  TRACE_CALL(__func__);
233  JsonBuilder *b;
234  JsonNode *r;
235 
236  gchar *kernel_name;
237  gchar *kernel_release;
238  gchar *kernel_arch;
239  gchar *id;
240  gchar *description;
241  GHashTable *etc_release;
242  gchar *release;
243  gchar *codename;
244  GHashTableIter iter;
245  gchar *key, *value;
246 
250  b = json_builder_new();
251  json_builder_begin_object(b);
252 
253  json_builder_set_member_name(b, "kernel_name");
254  kernel_name = g_strdup_printf("%s", remmina_utils_get_kernel_name());
255  if (!kernel_name || kernel_name[0] == '\0') {
256  json_builder_add_null_value(b);
257  }else {
258  json_builder_add_string_value(b, kernel_name);
259  }
260  g_free(kernel_name);
261 
262  json_builder_set_member_name(b, "kernel_release");
263  kernel_release = g_strdup_printf("%s", remmina_utils_get_kernel_release());
264  if (!kernel_release || kernel_release[0] == '\0') {
265  json_builder_add_null_value(b);
266  }else {
267  json_builder_add_string_value(b, kernel_release);
268  }
269  g_free(kernel_release);
270 
271  json_builder_set_member_name(b, "kernel_arch");
272  kernel_arch = g_strdup_printf("%s", remmina_utils_get_kernel_arch());
273  if (!kernel_arch || kernel_arch[0] == '\0') {
274  json_builder_add_null_value(b);
275  }else {
276  json_builder_add_string_value(b, kernel_arch);
277  }
278  g_free(kernel_arch);
279 
280  json_builder_set_member_name(b, "lsb_distributor");
282  if (!id || id[0] == '\0') {
283  json_builder_add_null_value(b);
284  }else {
285  json_builder_add_string_value(b, id);
286  }
287  g_free(id);
288 
289  json_builder_set_member_name(b, "lsb_distro_description");
290  description = remmina_utils_get_lsb_description();
291  if (!description || description[0] == '\0') {
292  json_builder_add_null_value(b);
293  }else {
294  json_builder_add_string_value(b, description);
295  }
296  g_free(description);
297 
298  json_builder_set_member_name(b, "lsb_distro_release");
299  release = remmina_utils_get_lsb_release();
300  if (!release || release[0] == '\0') {
301  json_builder_add_null_value(b);
302  }else {
303  json_builder_add_string_value(b, release);
304  }
305  g_free(release);
306 
307  json_builder_set_member_name(b, "lsb_distro_codename");
308  codename = remmina_utils_get_lsb_codename();
309  if (!codename || codename[0] == '\0') {
310  json_builder_add_null_value(b);
311  }else {
312  json_builder_add_string_value(b, codename);
313  }
314  g_free(codename);
315 
316  etc_release = remmina_utils_get_etc_release();
317  json_builder_set_member_name(b, "etc_release");
318  if (etc_release) {
319  json_builder_begin_object(b);
320  g_hash_table_iter_init (&iter, etc_release);
321  while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&value)) {
322  json_builder_set_member_name(b, key);
323  json_builder_add_string_value(b, value);
324  }
325  json_builder_end_object(b);
326  g_hash_table_remove_all(etc_release);
327  g_hash_table_unref(etc_release);
328  }else {
329  json_builder_add_null_value(b);
330  }
331 
336  json_builder_end_object(b);
337  r = json_builder_get_root(b);
338  g_object_unref(b);
339  return r;
340 }
341 
343 {
344  TRACE_CALL(__func__);
345  JsonBuilder *b;
346  JsonNode *r;
347  gchar *flatpak_info;
348 
352  b = json_builder_new();
353  json_builder_begin_object(b);
354  json_builder_set_member_name(b, "version");
355  json_builder_add_string_value(b, VERSION);
356  json_builder_set_member_name(b, "git_revision");
357  json_builder_add_string_value(b, REMMINA_GIT_REVISION);
358  json_builder_set_member_name(b, "snap_build");
359 #ifdef SNAP_BUILD
360  json_builder_add_int_value(b, 1);
361 #else
362  json_builder_add_int_value(b, 0);
363 #endif
364 
368  json_builder_set_member_name(b, "flatpak_build");
369  /* Flatpak sandbox should contain the file ${XDG_RUNTIME_DIR}/flatpak-info */
370  flatpak_info = g_build_filename(g_get_user_runtime_dir(), "flatpak-info", NULL);
371  if (g_file_test(flatpak_info, G_FILE_TEST_EXISTS)) {
372  json_builder_add_int_value(b, 1);
373  } else {
374  json_builder_add_int_value(b, 0);
375  }
376  g_free(flatpak_info);
377 
378  json_builder_end_object(b);
379  r = json_builder_get_root(b);
380  g_object_unref(b);
381  return r;
382 }
383 
385 {
386  TRACE_CALL(__func__);
387  JsonBuilder *b;
388  JsonNode *r;
389 
394  b = json_builder_new();
395  json_builder_begin_object(b);
396  json_builder_set_member_name(b, "major");
397  json_builder_add_int_value(b, gtk_get_major_version());
398  json_builder_set_member_name(b, "minor");
399  json_builder_add_int_value(b, gtk_get_minor_version());
400  json_builder_set_member_name(b, "micro");
401  json_builder_add_int_value(b, gtk_get_micro_version());
402  json_builder_end_object(b);
403  r = json_builder_get_root(b);
404  g_object_unref(b);
405  return r;
406 
407 }
408 
410 {
411  TRACE_CALL(__func__);
412  JsonNode *r;
413  GdkDisplay *disp;
414  gchar *bkend;
415 
420  disp = gdk_display_get_default();
421 
422 #ifdef GDK_WINDOWING_WAYLAND
423  if (GDK_IS_WAYLAND_DISPLAY(disp)) {
424  bkend = "Wayland";
425  }else
426 #endif
427 #ifdef GDK_WINDOWING_X11
428  if (GDK_IS_X11_DISPLAY(disp)) {
429  bkend = "X11";
430  } else
431 #endif
432  bkend = "n/a";
433 
434  r = json_node_alloc();
435  json_node_init_string(r, bkend);
436 
437  return r;
438 
439 }
440 
442 {
443  TRACE_CALL(__func__);
444  JsonBuilder *b;
445  JsonNode *r;
446  gchar *wmver;
447  gchar *wmname;
448 
449  b = json_builder_new();
450  json_builder_begin_object(b);
451 
452  json_builder_set_member_name(b, "window_manager");
453 
456  if (!wmver || wmver[0] == '\0') {
457  remmina_log_print("Gnome Shell not found\n");
458  }else {
459  remmina_log_printf("Gnome Shell version: %s\n", wmver);
460  json_builder_add_string_value(b, "Gnome Shell");
461  json_builder_set_member_name(b, "gnome_shell_ver");
462  json_builder_add_string_value(b, wmver);
463  goto end;
464  }
465  g_free(wmver);
466 
467  wmname = remmina_sysinfo_get_wm_name();
468  if (!wmname || wmname[0] == '\0') {
470  remmina_log_print("Cannot determine the Window Manger name\n");
471  json_builder_add_string_value(b, "n/a");
472  }else {
473  remmina_log_printf("Window Manger names %s\n", wmname);
474  json_builder_add_string_value(b, wmname);
475  }
476  g_free(wmname);
477 
478  end:
479  json_builder_end_object(b);
480  r = json_builder_get_root(b);
481  g_object_unref(b);
482  return r;
483 }
484 
486 {
487  TRACE_CALL(__func__);
488  JsonBuilder *b;
489  JsonNode *r;
490  gboolean sni;
492  b = json_builder_new();
493  json_builder_begin_object(b);
494 
495  json_builder_set_member_name(b, "appindicator_supported");
497  if (sni) {
499  json_builder_add_int_value(b, 1);
500  json_builder_set_member_name(b, "appindicator_compiled");
501 #ifdef HAVE_LIBAPPINDICATOR
502 
503  json_builder_add_int_value(b, 1);
504 #else
505 
506  json_builder_add_int_value(b, 0);
507 #endif
508  } else {
510  json_builder_add_int_value(b, 0);
511  json_builder_set_member_name(b, "icon_is_active");
514  json_builder_add_int_value(b, 1);
515  json_builder_set_member_name(b, "appindicator_type");
516 #ifdef HAVE_LIBAPPINDICATOR
517 
518  json_builder_add_string_value(b, "AppIndicator on GtkStatusIcon/xembed");
519 #else
520 
521  json_builder_add_string_value(b, "Remmina icon on GtkStatusIcon/xembed");
522 #endif
523  }else {
525  json_builder_add_int_value(b, 0);
526  }
527  }
528  json_builder_end_object(b);
529  r = json_builder_get_root(b);
530  g_object_unref(b);
531  return r;
532 }
533 
540 static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_data)
541 {
542  TRACE_CALL(__func__);
543 
544  gint count = 0;
545  gpointer pcount, kpo;
546  gpointer pdate, kdo;
547  gchar *sday, *smonth, *syear;
548  gchar *dday, *dmonth, *dyear;
549 
550  GDateTime *ds;
551  GDateTime *dd;
553  struct ProfilesData* pdata;
554  pdata = (struct ProfilesData*)user_data;
555 
556  pdata->protocol = remmina_file_get_string(remminafile, "protocol");
557  pdata->pdatestr = remmina_file_get_string(remminafile, "last_success");
558 
559  ds = dd = NULL;
560  if (pdata->pdatestr && pdata->pdatestr[0] != '\0' && strlen(pdata->pdatestr) >= 6) {
561  dyear = g_strdup_printf("%.4s", pdata->pdatestr);
562  dmonth = g_strdup_printf("%.2s", pdata->pdatestr + 4);
563  dday = g_strdup_printf("%.2s", pdata->pdatestr + 6);
564  dd = g_date_time_new_local(g_ascii_strtoll(dyear, NULL, 0),
565  g_ascii_strtoll(dmonth, NULL, 0),
566  g_ascii_strtoll(dday, NULL, 0), 0, 0, 0.0);
567  g_free(dyear);
568  g_free(dmonth);
569  g_free(dday);
570  }
571 
572 
573  if (pdata->protocol && pdata->protocol[0] != '\0') {
574  if (g_hash_table_lookup_extended(pdata->proto_count, pdata->protocol, &kpo, &pcount)) {
575  count = GPOINTER_TO_INT(pcount) + 1;
576  }else {
577  count = 1;
578  g_hash_table_insert(pdata->proto_count, g_strdup(pdata->protocol), GINT_TO_POINTER(count));
579  }
580  g_hash_table_replace(pdata->proto_count, g_strdup(pdata->protocol), GINT_TO_POINTER(count));
581  pdate = NULL;
582  if (g_hash_table_lookup_extended(pdata->proto_date, pdata->protocol, &kdo, &pdate)) {
583 
584  ds = NULL;
585  if (pdate && strlen(pdate) >= 6) {
586  syear = g_strdup_printf("%.4s", (char*)pdate);
587  smonth = g_strdup_printf("%.2s", (char*)pdate + 4);
588  sday = g_strdup_printf("%.2s", (char*)pdate + 6);
589  ds = g_date_time_new_local(g_ascii_strtoll(syear, NULL, 0),
590  g_ascii_strtoll(smonth, NULL, 0),
591  g_ascii_strtoll(sday, NULL, 0), 0, 0, 0.0);
592  g_free(syear);
593  g_free(smonth);
594  g_free(sday);
595  }
596 
598  if (ds && dd) {
599  gint res = g_date_time_compare( ds, dd );
601  if (res < 0 ) {
602  //remmina_log_printf("Date %s is newer than the one inside pdata->protocol for protocol %s\n", g_strdup(pdata->pdatestr), g_strdup(pdata->protocol));
603  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
604  }
605  }
607  if (!ds && dd) {
608  //remmina_log_printf("Date %s inserted in pdata->protocol for protocol %s\n", g_strdup(pdata->pdatestr), g_strdup(pdata->protocol));
609  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
610  }
612  if ((!ds && !dd) && pdata->pdatestr) {
613  //remmina_log_printf("Date NULL inserted in pdata->protocol for protocol %s\n", g_strdup(pdata->protocol));
614  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), NULL);
615  }
616  }else {
619  if (pdata->pdatestr) {
620  //remmina_log_printf("Date %s inserted in pdata->protocol for protocol %s\n", g_strdup(pdata->pdatestr), g_strdup(pdata->protocol));
621  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
622  }else {
624  //remmina_log_printf("We set %s protocol date to NULL\n", g_strdup(pdata->protocol));
625  g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), NULL);
626  }
627  }
628  }
629  if (dd)
630  g_date_time_unref(dd);
631  if (ds)
632  g_date_time_unref(ds);
633 }
634 
658 {
659  TRACE_CALL(__func__);
660 
661  JsonBuilder *b;
662  JsonNode *r;
663  gchar *s;
664 
665  gint profiles_count;
666  GHashTableIter pcountiter, pdateiter;
667  gpointer pcountkey, pcountvalue;
668  gpointer pdatekey, pdatevalue;
669 
670  struct ProfilesData *pdata;
671  pdata = g_malloc0(sizeof(struct ProfilesData));
672 
673  b = json_builder_new();
674  json_builder_begin_object(b);
675 
676  json_builder_set_member_name(b, "profile_count");
677 
681  pdata->proto_date = g_hash_table_new_full(g_str_hash, g_str_equal,
682  (GDestroyNotify)g_free, (GDestroyNotify)g_free);
683  pdata->proto_count = g_hash_table_new_full(g_str_hash, g_str_equal,
684  (GDestroyNotify)g_free, NULL);
685 
686  profiles_count = remmina_file_manager_iterate(
688  (gpointer)pdata);
689 
690  json_builder_add_int_value(b, profiles_count);
691 
692  g_hash_table_iter_init(&pcountiter, pdata->proto_count);
693  while (g_hash_table_iter_next(&pcountiter, &pcountkey, &pcountvalue)) {
694  json_builder_set_member_name(b, (gchar*)pcountkey);
695  json_builder_add_int_value(b, GPOINTER_TO_INT(pcountvalue));
696  }
697 
698  g_hash_table_iter_init(&pdateiter, pdata->proto_date);
699  while (g_hash_table_iter_next(&pdateiter, &pdatekey, &pdatevalue)) {
700  s = g_strdup_printf("DATE_%s", (gchar*)pdatekey);
701  json_builder_set_member_name(b, s);
702  g_free(s);
703  json_builder_add_string_value(b, (gchar*)pdatevalue);
704  }
705 
706  json_builder_end_object(b);
707  r = json_builder_get_root(b);
708  g_object_unref(b);
709 
710  g_hash_table_remove_all(pdata->proto_date);
711  g_hash_table_unref(pdata->proto_date);
712  g_hash_table_remove_all(pdata->proto_count);
713  g_hash_table_unref(pdata->proto_count);
714 
715  g_free(pdata);
716 
717  return r;
718 }
719 
728 {
729  TRACE_CALL(__func__);
730  JsonBuilder *b;
731  JsonNode *n;
732 
733  b = json_builder_new();
734  json_builder_begin_object(b);
735 
736  n = remmina_stats_get_uid();
737  json_builder_set_member_name(b, "UID");
738  json_builder_add_value(b, n);
739 
741  json_builder_set_member_name(b, "REMMINAVERSION");
742  json_builder_add_value(b, n);
743 
744  if (uname(&u) == -1)
745  g_print("uname:");
746 
748  json_builder_set_member_name(b, "SYSTEM");
749  json_builder_add_value(b, n);
750 
752  json_builder_set_member_name(b, "GTKVERSION");
753  json_builder_add_value(b, n);
754 
756  json_builder_set_member_name(b, "GTKBACKEND");
757  json_builder_add_value(b, n);
758 
760  json_builder_set_member_name(b, "WINDOWMANAGER");
761  json_builder_add_value(b, n);
762 
764  json_builder_set_member_name(b, "APPINDICATOR");
765  json_builder_add_value(b, n);
766 
768  json_builder_set_member_name(b, "PROFILES");
769  json_builder_add_value(b, n);
770 
771  json_builder_end_object(b);
772  n = json_builder_get_root(b);
773  g_object_unref(b);
774 
775  return n;
776 
777 }
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:298
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
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:197
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()
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:651
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
RemminaPref remmina_pref
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()