39 #include <gio/gdesktopappinfo.h> 41 #include <glib/gi18n.h> 42 #include <glib/gstdio.h> 43 #include <libsoup/soup.h> 47 #include <sys/types.h> 61 #define ARR_SIZE(arr) (sizeof((arr)) / sizeof((arr[0]))) 63 #define READ_BUFFER_LEN 1024 65 #define RMNEWS_CHECK_1ST_MS 3000 66 #define RMNEWS_CHECK_INTERVAL_MS 12000 68 #define RMNEWS_INTERVAL_SEC 604800 70 #define REMMINA_URL "https://remmina.org/" 71 #define RMNEWS_OUTPUT "/var/tmp/latest_news.md" 74 #define GET_OBJ(object_name) gtk_builder_get_object(rmnews_news_dialog->builder, object_name) 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 87 "x-scheme-handler/rdp",
88 "x-scheme-handler/spice",
89 "x-scheme-handler/vnc",
90 "x-scheme-handler/remmina",
91 "application/x-remmina",
106 #if SOUP_CHECK_VERSION (2, 99, 2) 109 GError *error = NULL;
110 g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
114 g_printerr (
"Failed to download: %s\n", error->message);
115 g_error_free (error);
124 TRACE_CALL(__func__);
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);
144 desktop_info = g_desktop_app_info_new(
id);
148 info = G_APP_INFO(desktop_info);
152 REMMINA_DEBUG(
"Failed to set '%s' as the default application for secondary content type '%s': %s",
155 REMMINA_DEBUG(
"Set '%s' as the default application for '%s'",
156 g_app_info_get_name(info),
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);
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;
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;
200 TRACE_CALL(__func__);
205 rmnews_news_dialog->
retval = 1;
208 rmnews_news_dialog->
dialog = GTK_DIALOG(gtk_builder_get_object(rmnews_news_dialog->
builder,
"RemminaNewsDialog"));
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"));
219 gtk_widget_set_sensitive(GTK_WIDGET(rmnews_news_dialog->
rmnews_news_switch), RMNEWS_ENABLE_NEWS);
228 gtk_label_set_markup(rmnews_news_dialog->
rmnews_label, contents);
235 g_signal_connect(rmnews_news_dialog->dialog,
"close",
237 g_signal_connect(rmnews_news_dialog->dialog,
"delete-event",
241 gtk_builder_connect_signals(rmnews_news_dialog->builder, NULL);
244 gtk_widget_show_all(GTK_WIDGET(rmnews_news_dialog->dialog));
245 gtk_window_present(GTK_WINDOW(rmnews_news_dialog->dialog));
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);
251 #if SOUP_CHECK_VERSION (2, 99, 2) 254 TRACE_CALL(__func__);
258 gchar *filesha = NULL;
259 gchar *filesha_after = NULL;
261 GError *error = NULL;
262 GInputStream *in = soup_session_send_finish (SOUP_SESSION (source), result, &error);
265 REMMINA_DEBUG (
"Failed to send request: %s", error->message);
266 g_error_free (error);
270 GDateTime *gdt = g_date_time_new_now_utc();
271 gint64 unixts = g_date_time_to_unix(gdt);
272 g_date_time_unref(gdt);
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";
281 GOutputStream *out = G_OUTPUT_STREAM (g_file_replace (output_file, NULL, NULL,
282 G_FILE_CREATE_REPLACE_DESTINATION, NULL, &error));
284 REMMINA_DEBUG(
"Failed to create \"%s\": %s",
output_file_path, error->message);
287 REMMINA_DEBUG (
"Saving preferences");
289 g_free(filesha); filesha = NULL;
290 g_error_free (error);
292 g_object_unref (output_file);
293 g_object_unref (out);
304 g_output_stream_splice (G_OUTPUT_STREAM (out), in,
305 G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
310 REMMINA_DEBUG (
"Failed to download: %s", error->message);
313 REMMINA_DEBUG (
"Saving preferences");
315 g_free(filesha); filesha = NULL;
316 g_error_free (error);
318 g_object_unref (output_file);
319 g_object_unref (out);
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");
331 REMMINA_DEBUG (
"Saving preferences");
341 g_free(filesha); filesha = NULL;
342 g_object_unref (out);
344 REMMINA_DEBUG(
"Cannot open output file for writing, because output_file_path is NULL");
347 REMMINA_DEBUG (
"Saving preferences");
359 TRACE_CALL(__func__);
362 g_autoptr(SoupBuffer) sb;
363 FILE *output_file = NULL;
364 gchar *filesha = NULL;
365 gchar *filesha_after = NULL;
370 status = soup_message_get_status(msg);
371 REMMINA_DEBUG(
"Status code %d", status);
373 name = soup_message_get_uri(msg)->path;
375 gdt = g_date_time_new_now_utc();
376 unixts = g_date_time_to_unix(gdt);
377 g_date_time_unref(gdt);
379 if (SOUP_STATUS_IS_REDIRECTION(status)) {
380 header = soup_message_headers_get_one(soup_message_get_response_headers(msg),
382 REMMINA_DEBUG(
"Redirection detected");
387 REMMINA_DEBUG(
" -> %s\n", header);
389 uri = soup_uri_new_with_base(soup_message_get_uri(msg), header);
390 uri_string = soup_uri_to_string(uri, FALSE);
397 REMMINA_DEBUG (
"Saving preferences");
402 if (!SOUP_STATUS_IS_SUCCESSFUL(status)) {
403 REMMINA_DEBUG (
"Could not access %s: %s", name , soup_status_get_phrase(status));
405 REMMINA_DEBUG (
"Saving preferences");
409 REMMINA_DEBUG(
"Status 200");
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";
421 REMMINA_DEBUG (
"Saving preferences");
423 g_free(filesha); filesha = NULL;
427 REMMINA_DEBUG(
"Cannot open output file for writing, because output_file_path is NULL");
430 REMMINA_DEBUG (
"Saving preferences");
434 sb = soup_message_body_flatten(msg->response_body);
436 fwrite(sb->data, 1, sb->length, output_file);
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");
447 REMMINA_DEBUG (
"Saving preferences");
457 g_free(filesha); filesha = NULL;
473 TRACE_CALL(__func__);
475 const gchar *uname, *hname;
476 const gchar *uid_suffix;
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);
499 g_checksum_free(chs);
506 TRACE_CALL(__func__);
510 msg = soup_message_new(
"GET", url);
511 soup_message_set_flags(msg, SOUP_MESSAGE_NO_REDIRECT);
513 REMMINA_DEBUG(
"Fetching %s", url);
515 #if SOUP_CHECK_VERSION (2, 99, 2) 517 soup_session_send_async (
session, msg, G_PRIORITY_DEFAULT,
529 TRACE_CALL(__func__);
531 SoupLogger *logger = NULL;
534 gchar mage[20], gcount[20];
537 gchar *
cachedir = g_build_path(
"/", g_get_user_cache_dir(), REMMINA_APP_ID, NULL);
538 gint d = g_mkdir_with_parents(cachedir, 0750);
552 REMMINA_DEBUG (
"Preferences NOT saved");
559 REMMINA_DEBUG(
"Cannot store the remmina news file");
571 REMMINA_DEBUG(
"Gathering news");
573 session = soup_session_new_with_options (
"user-agent",
"get ",
574 "accept-language-auto", TRUE,
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);
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,
588 logger = soup_logger_new(SOUP_LOGGER_LOG_NONE, -1);
591 soup_session_add_feature(
session, SOUP_SESSION_FEATURE(logger));
592 g_object_unref(logger);
595 REMMINA_DEBUG(
"Language %s", lang);
599 if (stat(
"/etc/machine-id", &sb) == 0)
600 sprintf(mage,
"%ld", (
long)(time(NULL) - sb.st_mtim.tv_sec));
607 "news/remmina_news.php?lang=",
626 TRACE_CALL(__func__);
631 gdt = g_date_time_new_now_utc();
632 unixts = g_date_time_to_unix(gdt);
633 g_date_time_unref(gdt);
639 gint randidx = randombytes_uniform(8);
640 REMMINA_DEBUG(
"Setting a random periodic_rmnews_last_get to %d - %d", unixts,
eweekdays[randidx]);
651 if (unixts > next || (unixts < remmina_pref.periodic_rmnews_last_get && unixts > 1514764800)) {
661 return G_SOURCE_CONTINUE;
666 TRACE_CALL(__func__);
670 RMNEWS_CHECK_INTERVAL_MS);
void rmnews_news_switch_state_set_cb()
GtkWindow * remmina_main_get_window()
void rmnews_show_news(GtkWindow *parent)
static void rmnews_close_clicked(GtkButton *btn, gpointer user_data)
GtkLabel * rmnews_defaultcl_label
GtkButton * rmnews_button_close
static const gchar * supported_mime_types[]
gboolean periodic_news_permitted
static gboolean rmnews_dialog_deleted(GtkButton *btn, gpointer user_data)
static void rmnews_get_url_cb(GObject *source, GAsyncResult *result, gpointer user_data)
void * remmina_scheduler_setup(GSourceFunc cb, gpointer cb_data, guint first_interval, guint interval)
GtkTextView * rmnews_text_view
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
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)
gchar * periodic_rmnews_uuid_prefix
gchar * remmina_sha1_file(const gchar *filename)
Create a hexadecimal string version of the SHA-1 digest of the contents of the named file...
GtkSwitch * rmnews_news_switch
static const gchar * output_file_path
static RemminaNewsDialog * rmnews_news_dialog
glong periodic_rmnews_get_count
GtkBuilder * remmina_public_gtk_builder_new_from_resource(gchar *resource)
static gboolean rmnews_periodic_check(gpointer user_data)
gboolean remmina_pref_save(void)
static gchar * rmnews_get_file_contents(gchar *path)
void rmnews_get_url(const char *url)
gchar * rmnews_get_uid()
Try to get a unique system+user ID to identify this remmina user and avoid some duplicated task...
void rmnews_defaultcl_on_click()
gboolean remmina_pref_is_rw(void)
glong periodic_rmnews_last_get
GtkButton * rmnews_defaultcl_button