Remmina - The GTK+ Remote Desktop Client  v1.4.27
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.
rmnews.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2016-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  */
34 
35 #include "config.h"
37 
38 #include <fcntl.h>
39 #include <gio/gdesktopappinfo.h>
40 #include <gio/gio.h>
41 #include <glib/gi18n.h>
42 #include <glib/gstdio.h>
43 #include <libsoup/soup.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <time.h>
49 
50 #include "remmina.h"
51 #include "remmina_main.h"
52 #include "remmina_log.h"
53 #include "remmina_pref.h"
54 #include "remmina_public.h"
55 #include "remmina_sodium.h"
56 #include "remmina_utils.h"
57 #include "remmina_scheduler.h"
58 #include "remmina_sysinfo.h"
59 #include "rmnews.h"
60 
61 #define ARR_SIZE(arr) (sizeof((arr)) / sizeof((arr[0])))
62 /* Neas file buffer */
63 #define READ_BUFFER_LEN 1024
64 /* Timers */
65 #define RMNEWS_CHECK_1ST_MS 3000
66 #define RMNEWS_CHECK_INTERVAL_MS 12000
67 /* How many seconds before to get news */
68 #define RMNEWS_INTERVAL_SEC 604800
69 /* TODO: move in config.h */
70 #define REMMINA_URL "https://remmina.org/"
71 #define RMNEWS_OUTPUT "/var/tmp/latest_news.md"
72 
74 #define GET_OBJ(object_name) gtk_builder_get_object(rmnews_news_dialog->builder, object_name)
75 
76 static SoupSession *session;
77 
78 #if SOUP_MAJOR_VERSION < 3
79 #define soup_message_get_status(message) message->status_code
80 #define soup_message_get_response_headers(message) message->response_headers
81 #endif
82 
83 static const gchar *output_file_path = NULL;
84 
85 static
86 const gchar *supported_mime_types[] = {
87  "x-scheme-handler/rdp",
88  "x-scheme-handler/spice",
89  "x-scheme-handler/vnc",
90  "x-scheme-handler/remmina",
91  "application/x-remmina",
92  NULL
93 };
94 
95 gint eweekdays[7] = {
96  86400,
97  172800,
98  259200,
99  345600,
100  432000,
101  518400,
102  604800
103 };
104 
105 
106 #if SOUP_CHECK_VERSION (2, 99, 2)
107 static void rmnews_on_stream_splice (GObject *source, GAsyncResult *result, gpointer user_data)
108 {
109  GError *error = NULL;
110  g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
111  result,
112  &error);
113  if (error) {
114  g_printerr ("Failed to download: %s\n", error->message);
115  g_error_free (error);
116  return;
117  }
118 
119 }
120 #endif
121 
123 {
124  TRACE_CALL(__func__);
125  if (rmnews_news_dialog->rmnews_news_switch && \
126  gtk_switch_get_active(rmnews_news_dialog->rmnews_news_switch)) {
129  } else {
132  }
133 }
134 
136 {
137  TRACE_CALL(__func__);
138  g_autoptr(GError) error = NULL;
139  GDesktopAppInfo *desktop_info;
140  GAppInfo *info = NULL;
141  g_autofree gchar *id = g_strconcat(REMMINA_APP_ID, ".desktop", NULL);
142  int i;
143 
144  desktop_info = g_desktop_app_info_new(id);
145  if (!desktop_info)
146  return;
147 
148  info = G_APP_INFO(desktop_info);
149 
150  for (i = 0; supported_mime_types[i]; i++) {
151  if (!g_app_info_set_as_default_for_type(info, supported_mime_types[i], &error))
152  REMMINA_DEBUG("Failed to set '%s' as the default application for secondary content type '%s': %s",
153  g_app_info_get_name(info), supported_mime_types[i], error->message);
154  else
155  REMMINA_DEBUG("Set '%s' as the default application for '%s'",
156  g_app_info_get_name(info),
158  }
159 }
160 
161 static gchar *rmnews_get_file_contents(gchar *path)
162 {
163  gsize size;
164  gchar *content;
165 
166  if (g_file_get_contents(path, &content, &size, NULL)) {
167  if (!g_utf8_validate(content, size, NULL)) {
168  REMMINA_DEBUG("%s content is not UTF-8", path);
169  g_free(content);
170  content = NULL;
171  }
172  }
173  //return g_markup_escape_text(content, strlen(content));
174  return content;
175 }
176 
177 static void rmnews_close_clicked(GtkButton *btn, gpointer user_data)
178 {
179  TRACE_CALL(__func__);
180  if (rmnews_news_dialog->dialog)
181  gtk_widget_destroy(GTK_WIDGET(rmnews_news_dialog->dialog));
182  rmnews_news_dialog->dialog = NULL;
183  g_free(rmnews_news_dialog);
184  rmnews_news_dialog = NULL;
185 }
186 
187 static gboolean rmnews_dialog_deleted(GtkButton *btn, gpointer user_data)
188 {
189  TRACE_CALL(__func__);
190  gtk_widget_destroy(GTK_WIDGET(rmnews_news_dialog->dialog));
191  rmnews_news_dialog->dialog = NULL;
192  g_free(rmnews_news_dialog);
193  rmnews_news_dialog = NULL;
194 
195  return FALSE;
196 }
197 
198 void rmnews_show_news(GtkWindow *parent)
199 {
200  TRACE_CALL(__func__);
201 
202  if (disablenews) return;
203 
204  rmnews_news_dialog = g_new0(RemminaNewsDialog, 1);
205  rmnews_news_dialog->retval = 1;
206 
207  rmnews_news_dialog->builder = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_news.glade");
208  rmnews_news_dialog->dialog = GTK_DIALOG(gtk_builder_get_object(rmnews_news_dialog->builder, "RemminaNewsDialog"));
209 
210  rmnews_news_dialog->rmnews_text_view = GTK_TEXT_VIEW(GET_OBJ("rmnews_text_view"));
211  rmnews_news_dialog->rmnews_label = GTK_LABEL(GET_OBJ("rmnews_label"));
212  rmnews_news_dialog->rmnews_defaultcl_label = GTK_LABEL(GET_OBJ("rmnews_defaultcl_label"));
213  rmnews_news_dialog->rmnews_defaultcl_button = GTK_BUTTON(GET_OBJ("rmnews_defaultcl_switch"));
214  rmnews_news_dialog->rmnews_news_switch = GTK_SWITCH(GET_OBJ("rmnews_news_switch"));
216  gtk_switch_set_active(rmnews_news_dialog->rmnews_news_switch, TRUE);
217  else
218  gtk_switch_set_active(rmnews_news_dialog->rmnews_news_switch, FALSE);
219  gtk_widget_set_sensitive(GTK_WIDGET(rmnews_news_dialog->rmnews_news_switch), RMNEWS_ENABLE_NEWS);
220 
221  rmnews_news_dialog->rmnews_button_close = GTK_BUTTON(GET_OBJ("rmnews_button_close"));
222  gtk_widget_set_can_default(GTK_WIDGET(rmnews_news_dialog->rmnews_button_close), TRUE);
223  gtk_widget_grab_default(GTK_WIDGET(rmnews_news_dialog->rmnews_button_close));
224 
226  gchar *contents = rmnews_get_file_contents(g_strdup(output_file_path));
227  if (contents) {
228  gtk_label_set_markup(rmnews_news_dialog->rmnews_label, contents);
229  g_free(contents);
230  }
231  }
232 
233  g_signal_connect(rmnews_news_dialog->rmnews_button_close, "clicked",
234  G_CALLBACK(rmnews_close_clicked), (gpointer)rmnews_news_dialog);
235  g_signal_connect(rmnews_news_dialog->dialog, "close",
236  G_CALLBACK(rmnews_close_clicked), NULL);
237  g_signal_connect(rmnews_news_dialog->dialog, "delete-event",
238  G_CALLBACK(rmnews_dialog_deleted), NULL);
239 
240  /* Connect signals */
241  gtk_builder_connect_signals(rmnews_news_dialog->builder, NULL);
242 
243  /* Show the non-modal news dialog */
244  gtk_widget_show_all(GTK_WIDGET(rmnews_news_dialog->dialog));
245  gtk_window_present(GTK_WINDOW(rmnews_news_dialog->dialog));
246  if (parent)
247  gtk_window_set_transient_for(GTK_WINDOW(rmnews_news_dialog->dialog), parent);
248  gtk_window_set_modal(GTK_WINDOW(rmnews_news_dialog->dialog), TRUE);
249 }
250 
251 #if SOUP_CHECK_VERSION (2, 99, 2)
252 static void rmnews_get_url_cb (GObject *source, GAsyncResult *result, gpointer user_data)
253 {
254  TRACE_CALL(__func__);
255  const char *name;
256  const char *header;
257  GFile *output_file;
258  gchar *filesha = NULL;
259  gchar *filesha_after = NULL;
260 
261  GError *error = NULL;
262  GInputStream *in = soup_session_send_finish (SOUP_SESSION (source), result, &error);
263 
264  if (error) {
265  REMMINA_DEBUG ("Failed to send request: %s", error->message);
266  g_error_free (error);
267  return;
268  }
269 
270  GDateTime *gdt = g_date_time_new_now_utc();
271  gint64 unixts = g_date_time_to_unix(gdt);
272  g_date_time_unref(gdt);
273 
274  if (output_file_path) {
275  REMMINA_DEBUG("Calculating the SHA1 of the local file");
277  REMMINA_DEBUG("SHA1 is %s", filesha);
278  if (filesha == NULL || filesha[0] == 0) filesha = "0\0";
279  REMMINA_DEBUG("Opening %s output file for writing", output_file_path);
280  GFile *output_file = g_file_new_for_commandline_arg (output_file_path);
281  GOutputStream *out = G_OUTPUT_STREAM (g_file_replace (output_file, NULL, NULL,
282  G_FILE_CREATE_REPLACE_DESTINATION, NULL, &error));
283  if (error) {
284  REMMINA_DEBUG("Failed to create \"%s\": %s", output_file_path, error->message);
286  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
287  REMMINA_DEBUG ("Saving preferences");
289  g_free(filesha); filesha = NULL;
290  g_error_free (error);
291  g_object_unref (in);
292  g_object_unref (output_file);
293  g_object_unref (out);
294  return;
295  }
296 
297  /* Start downloading to the file */
298  // g_output_stream_splice_async (G_OUTPUT_STREAM (out), in,
299  // G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
300  // G_PRIORITY_DEFAULT,
301  // NULL,
302  // rmnews_on_stream_splice,
303  // NULL);
304  g_output_stream_splice (G_OUTPUT_STREAM (out), in,
305  G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
306  NULL,
307  &error);
308 
309  if (error) {
310  REMMINA_DEBUG ("Failed to download: %s", error->message);
312  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
313  REMMINA_DEBUG ("Saving preferences");
315  g_free(filesha); filesha = NULL;
316  g_error_free (error);
317  g_object_unref (in);
318  g_object_unref (output_file);
319  g_object_unref (out);
320  return;
321  }
322 
323 
324  filesha_after = remmina_sha1_file(output_file_path);
325 
326  REMMINA_DEBUG("SHA1 after download is %s", filesha_after);
327  if (g_strcmp0(filesha, filesha_after) != 0) {
328  REMMINA_DEBUG("SHA1 differs, we show the news and reset the counter");
330  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
331  REMMINA_DEBUG ("Saving preferences");
332  GtkWindow *parent = remmina_main_get_window();
333  if (!kioskmode && kioskmode == FALSE)
334  rmnews_show_news(parent);
335  } else {
337  }
338  /* Increase counter with number of successful GETs */
341  g_free(filesha); filesha = NULL;
342  g_object_unref (out);
343  } else {
344  REMMINA_DEBUG("Cannot open output file for writing, because output_file_path is NULL");
346  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
347  REMMINA_DEBUG ("Saving preferences");
349  return;
350  }
351 
352  g_object_unref (in);
353 
354 
355 }
356 #else
357 static void rmnews_get_url_cb(SoupSession *session, SoupMessage *msg, gpointer data)
358 {
359  TRACE_CALL(__func__);
360  const char *name;
361  const char *header;
362  g_autoptr(SoupBuffer) sb;
363  FILE *output_file = NULL;
364  gchar *filesha = NULL;
365  gchar *filesha_after = NULL;
366  GDateTime *gdt;
367  gint64 unixts;
368  gint status;
369 
370  status = soup_message_get_status(msg);
371  REMMINA_DEBUG("Status code %d", status);
372 
373  name = soup_message_get_uri(msg)->path;
374 
375  gdt = g_date_time_new_now_utc();
376  unixts = g_date_time_to_unix(gdt);
377  g_date_time_unref(gdt);
378 
379  if (SOUP_STATUS_IS_REDIRECTION(status)) {
380  header = soup_message_headers_get_one(soup_message_get_response_headers(msg),
381  "Location");
382  REMMINA_DEBUG("Redirection detected");
383  if (header) {
384  SoupURI *uri;
385  char *uri_string;
386 
387  REMMINA_DEBUG(" -> %s\n", header);
388 
389  uri = soup_uri_new_with_base(soup_message_get_uri(msg), header);
390  uri_string = soup_uri_to_string(uri, FALSE);
391  rmnews_get_url(uri_string);
392  g_free(uri_string);
393  soup_uri_free(uri);
394  }
396  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
397  REMMINA_DEBUG ("Saving preferences");
399  return;
400  }
401 
402  if (!SOUP_STATUS_IS_SUCCESSFUL(status)) {
403  REMMINA_DEBUG ("Could not access %s: %s", name , soup_status_get_phrase(status));
404  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
405  REMMINA_DEBUG ("Saving preferences");
407  return;
408  } else {
409  REMMINA_DEBUG("Status 200");
410  if (output_file_path) {
411  REMMINA_DEBUG("Calculating the SHA1 of the local file");
413  REMMINA_DEBUG("SHA1 is %s", filesha);
414  if (filesha == NULL || filesha[0] == 0) filesha = "0\0";
415  REMMINA_DEBUG("Opening %s output file for writing", output_file_path);
416  output_file = fopen(output_file_path, "w");
417  if (!output_file) {
418  REMMINA_DEBUG("Error trying to create file %s.", output_file_path);
420  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
421  REMMINA_DEBUG ("Saving preferences");
423  g_free(filesha); filesha = NULL;
424  return;
425  }
426  } else {
427  REMMINA_DEBUG("Cannot open output file for writing, because output_file_path is NULL");
429  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
430  REMMINA_DEBUG ("Saving preferences");
432  return;
433  }
434  sb = soup_message_body_flatten(msg->response_body);
435  if (output_file) {
436  fwrite(sb->data, 1, sb->length, output_file);
437 
438  if (output_file_path) {
439  fclose(output_file);
440  filesha_after = remmina_sha1_file(output_file_path);
441  }
442  REMMINA_DEBUG("SHA1 after download is %s", filesha_after);
443  if (g_strcmp0(filesha, filesha_after) != 0) {
444  REMMINA_DEBUG("SHA1 differs, we show the news and reset the counter");
446  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
447  REMMINA_DEBUG ("Saving preferences");
448  GtkWindow *parent = remmina_main_get_window();
449  if (!kioskmode && kioskmode == FALSE)
450  rmnews_show_news(parent);
451  } else {
453  }
454  /* Increase counter with number of successful GETs */
457  g_free(filesha); filesha = NULL;
458  }
459  }
460 
461  g_object_unref(msg);
462 }
463 #endif
464 
472 {
473  TRACE_CALL(__func__);
474  GChecksum *chs;
475  const gchar *uname, *hname;
476  const gchar *uid_suffix;
477  gchar *uid_prefix;
478  gchar *uid;
479 
480  /* This code is very similar to remmina_stats_get_uid() */
481 
483  /* Generate a new UUID_PREFIX for news on this installation */
484  uid_prefix = remmina_gen_random_uuid();
489  }
490 
491  uname = g_get_user_name();
492  hname = g_get_host_name();
493  chs = g_checksum_new(G_CHECKSUM_SHA256);
494  g_checksum_update(chs, (const guchar *)uname, strlen(uname));
495  g_checksum_update(chs, (const guchar *)hname, strlen(hname));
496  uid_suffix = g_checksum_get_string(chs);
497 
498  uid = g_strdup_printf("02-%s-%.10s", remmina_pref.periodic_rmnews_uuid_prefix, uid_suffix);
499  g_checksum_free(chs);
500 
501  return uid;
502 }
503 
504 void rmnews_get_url(const char *url)
505 {
506  TRACE_CALL(__func__);
507 
508  SoupMessage *msg;
509 
510  msg = soup_message_new("GET", url);
511  soup_message_set_flags(msg, SOUP_MESSAGE_NO_REDIRECT);
512 
513  REMMINA_DEBUG("Fetching %s", url);
514 
515 #if SOUP_CHECK_VERSION (2, 99, 2)
516  // Use soup_session_send_async or soup_session_send_and_read_async
517  soup_session_send_async ( session, msg, G_PRIORITY_DEFAULT,
518  NULL, // cancellable
519  rmnews_get_url_cb, // callback
520  NULL); // user_data
521 #else
522  g_object_ref(msg);
523  soup_session_queue_message(session, msg, rmnews_get_url_cb, NULL);
524 #endif
525 }
526 
528 {
529  TRACE_CALL(__func__);
530 
531  SoupLogger *logger = NULL;
532  int fd;
533  gchar *uid;
534  gchar mage[20], gcount[20];
535  struct stat sb;
536 
537  gchar *cachedir = g_build_path("/", g_get_user_cache_dir(), REMMINA_APP_ID, NULL);
538  gint d = g_mkdir_with_parents(cachedir, 0750);
539  if (d < 0)
540  output_file_path = RMNEWS_OUTPUT;
541  else
542  output_file_path = g_build_path("/", cachedir, "latest_news.md", NULL);
543 
544  REMMINA_DEBUG("Output file set to %s", output_file_path);
545 
548  g_file_set_contents(output_file_path, "", 0, NULL);
549  /* Just a symolic date */
550  remmina_pref.periodic_rmnews_last_get = 191469343000;
551  REMMINA_DEBUG ("periodic_rmnews_last_get set to %ld", remmina_pref.periodic_rmnews_last_get);
552  REMMINA_DEBUG ("Preferences NOT saved");
553  }
554 
555  fd = g_open(output_file_path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
556  REMMINA_DEBUG("Returned %d while creating %s", fd, output_file_path);
557  /* If we cannot create the remmina_news file, we avoid connections */
558  if (fd < 0) {
559  REMMINA_DEBUG("Cannot store the remmina news file");
560  return;
561  }
562  g_close(fd, NULL);
563 
564  REMMINA_DEBUG("Output file %s created successfully", output_file_path);
565 
566  if (output_file_path) {
567  } else {
568  REMMINA_DEBUG("Output file set to %s", output_file_path);
569  }
570 
571  REMMINA_DEBUG("Gathering news");
572  /* Build the session with all of the features we need */
573  session = soup_session_new_with_options ("user-agent", "get ",
574  "accept-language-auto", TRUE,
575  "timeout", 15,
576  NULL);
577 
578 #if SOUP_CHECK_VERSION (2, 99, 2)
579  soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
580  logger = soup_logger_new(SOUP_LOGGER_LOG_NONE);
581 #else
582  session = g_object_new(SOUP_TYPE_SESSION,
583  SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
584  SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
585  SOUP_SESSION_USER_AGENT, "get ",
586  SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
587  NULL);
588  logger = soup_logger_new(SOUP_LOGGER_LOG_NONE, -1);
589 #endif
590  /* TODO: Catch log level and set SOUP_LOGGER_LOG_MINIMAL or more */
591  soup_session_add_feature(session, SOUP_SESSION_FEATURE(logger));
592  g_object_unref(logger);
593 
594  gchar *lang = remmina_utils_get_lang();
595  REMMINA_DEBUG("Language %s", lang);
596 
597  uid = rmnews_get_uid();
598 
599  if (stat("/etc/machine-id", &sb) == 0)
600  sprintf(mage, "%ld", (long)(time(NULL) - sb.st_mtim.tv_sec));
601  else
602  strcpy(mage, "0");
603 
604  sprintf(gcount, "%ld", remmina_pref.periodic_rmnews_get_count);
605 
606  rmnews_get_url(g_strconcat(REMMINA_URL,
607  "news/remmina_news.php?lang=",
608  lang,
609  "&ver="
610  VERSION,
611  "&uid=",
612  uid,
613  "&sa=0",
614  "&mage=",
615  mage,
616  "&gcount=",
617  gcount,
618  NULL));
619 
620  g_free(uid);
621  g_object_unref(session);
622 }
623 
624 static gboolean rmnews_periodic_check(gpointer user_data)
625 {
626  TRACE_CALL(__func__);
627  GDateTime *gdt;
628  gint64 unixts;
629  glong next = 0;
630 
631  gdt = g_date_time_new_now_utc();
632  unixts = g_date_time_to_unix(gdt);
633  g_date_time_unref(gdt);
634 
635  /* if remmina_pref is not writable ... */
637  /* We randomly set periodic_rmnews_last_get to a day between today
638  * and 7 days ago */
639  gint randidx = randombytes_uniform(8);
640  REMMINA_DEBUG("Setting a random periodic_rmnews_last_get to %d - %d", unixts, eweekdays[randidx]);
641  remmina_pref.periodic_rmnews_last_get = unixts - eweekdays[randidx];
642  }
643  //REMMINA_DEBUG("periodic_rmnews_last_get is %ld", remmina_pref.periodic_rmnews_last_get);
644 
649  }
650  next = remmina_pref.periodic_rmnews_last_get + RMNEWS_INTERVAL_SEC;
651  if (unixts > next || (unixts < remmina_pref.periodic_rmnews_last_get && unixts > 1514764800)) {
652  //REMMINA_DEBUG("remmina_pref.periodic_news_permitted is %d", remmina_pref.periodic_news_permitted);
654  rmnews_get_news();
655  } else if (remmina_pref.periodic_rmnews_get_count == 0) {
659  }
660  }
661  return G_SOURCE_CONTINUE;
662 }
663 
665 {
666  TRACE_CALL(__func__);
668  NULL,
669  RMNEWS_CHECK_1ST_MS,
670  RMNEWS_CHECK_INTERVAL_MS);
671 }
void rmnews_news_switch_state_set_cb()
Definition: rmnews.c:122
GtkWindow * remmina_main_get_window()
void rmnews_show_news(GtkWindow *parent)
Definition: rmnews.c:198
static void rmnews_close_clicked(GtkButton *btn, gpointer user_data)
Definition: rmnews.c:177
GtkLabel * rmnews_defaultcl_label
Definition: rmnews.h:42
GtkButton * rmnews_button_close
Definition: rmnews.h:41
GtkDialog * dialog
Definition: rmnews.h:37
static const gchar * supported_mime_types[]
Definition: rmnews.c:86
gboolean periodic_news_permitted
Definition: remmina_pref.h:239
static gboolean rmnews_dialog_deleted(GtkButton *btn, gpointer user_data)
Definition: rmnews.c:187
static void rmnews_get_url_cb(GObject *source, GAsyncResult *result, gpointer user_data)
Definition: rmnews.c:252
void * remmina_scheduler_setup(GSourceFunc cb, gpointer cb_data, guint first_interval, guint interval)
GtkTextView * rmnews_text_view
Definition: rmnews.h:39
General utility functions, non-GTK related.
gchar * remmina_gen_random_uuid()
Generate a random sting of chars to be used as part of UID for news or stats.
static SoupSession * session
Definition: rmnews.c:76
gchar * remmina_utils_get_lang()
Return the current language defined in the LC_ALL.
static void rmnews_on_stream_splice(GObject *source, GAsyncResult *result, gpointer user_data)
Definition: rmnews.c:107
gchar * periodic_rmnews_uuid_prefix
Definition: remmina_pref.h:242
gchar * remmina_sha1_file(const gchar *filename)
Create a hexadecimal string version of the SHA-1 digest of the contents of the named file...
gint eweekdays[7]
Definition: rmnews.c:95
GtkSwitch * rmnews_news_switch
Definition: rmnews.h:44
static const gchar * output_file_path
Definition: rmnews.c:83
static RemminaNewsDialog * rmnews_news_dialog
Definition: rmnews.c:73
glong periodic_rmnews_get_count
Definition: remmina_pref.h:241
GtkBuilder * remmina_public_gtk_builder_new_from_resource(gchar *resource)
static gchar * cachedir
GtkLabel * rmnews_label
Definition: rmnews.h:40
static gboolean rmnews_periodic_check(gpointer user_data)
Definition: rmnews.c:624
void rmnews_schedule()
Definition: rmnews.c:664
gboolean disablenews
Definition: remmina.c:87
RemminaPref remmina_pref
Definition: rcw.c:79
GtkBuilder * builder
Definition: rmnews.h:36
gboolean remmina_pref_save(void)
Definition: remmina_pref.c:799
static gchar * rmnews_get_file_contents(gchar *path)
Definition: rmnews.c:161
void rmnews_get_url(const char *url)
Definition: rmnews.c:504
gchar * rmnews_get_uid()
Try to get a unique system+user ID to identify this remmina user and avoid some duplicated task...
Definition: rmnews.c:471
gboolean kioskmode
Definition: remmina.c:86
void rmnews_defaultcl_on_click()
Definition: rmnews.c:135
gboolean remmina_pref_is_rw(void)
Definition: remmina_pref.c:789
glong periodic_rmnews_last_get
Definition: remmina_pref.h:240
GtkButton * rmnews_defaultcl_button
Definition: rmnews.h:43
void rmnews_get_news()
Definition: rmnews.c:527