diff options
author | Antenore Gatta <antenore@simbiosi.org> | 2019-05-14 00:16:12 +0300 |
---|---|---|
committer | Antenore Gatta <antenore@simbiosi.org> | 2019-05-14 00:16:12 +0300 |
commit | 9e1e7e6f314df21e155652eec145659b20c51139 (patch) | |
tree | 18d49cbb585033ebb6d9d0b4f8c884d95d2b002a /src | |
parent | 35e0ae83657ce05ff5d0c27a209ac25d74f226ac (diff) |
Remmina news dialog
- A new scheduler that is used by the stat sender and by the remmina news
- A dialog that show the latest news per each Remmina version, starting from this commit
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 278 | ||||
-rw-r--r-- | src/remmina.c | 2 | ||||
-rw-r--r-- | src/remmina_pref.c | 6 | ||||
-rw-r--r-- | src/remmina_pref.h | 2 | ||||
-rw-r--r-- | src/remmina_scheduler.c | 90 | ||||
-rw-r--r-- | src/remmina_scheduler.h | 49 | ||||
-rw-r--r-- | src/remmina_stats_sender.c | 26 | ||||
-rw-r--r-- | src/remmina_unlock.c | 3 | ||||
-rw-r--r-- | src/remmina_utils.c | 52 | ||||
-rw-r--r-- | src/remmina_utils.h | 1 | ||||
-rw-r--r-- | src/rmnews.c | 321 | ||||
-rw-r--r-- | src/rmnews.h | 51 |
12 files changed, 722 insertions, 159 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcdbf2465..e6b9458f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,99 +36,103 @@ cmake_minimum_required(VERSION 2.8) list(APPEND REMMINA_SRCS - "remmina_about.c" - "remmina_about.h" - "remmina_applet_menu.c" - "remmina_applet_menu.h" - "remmina_applet_menu_item.c" - "remmina_applet_menu_item.h" - "remmina_avahi.c" - "remmina_avahi.h" - "remmina.c" - "remmina.h" - "remmina_chat_window.c" - "remmina_chat_window.h" - "remmina_crypt.c" - "remmina_crypt.h" - "remmina_exec.c" - "remmina_exec.h" - "remmina_file.c" - "remmina_file_editor.c" - "remmina_file_editor.h" - "remmina_file.h" - "remmina_file_manager.c" - "remmina_file_manager.h" - "remmina_ftp_client.c" - "remmina_ftp_client.h" - "remmina_icon.c" - "remmina_icon.h" - "remmina_key_chooser.c" - "remmina_key_chooser.h" - "remmina_log.c" - "remmina_log.h" - "remmina_main.c" - "remmina_main.h" - "remmina_marshals.c" - "remmina_marshals.h" - "remmina_marshals.list" - "remmina_masterthread_exec.c" - "remmina_masterthread_exec.h" - "remmina_message_panel.c" - "remmina_message_panel.h" - "remmina_plugin_manager.c" - "remmina_plugin_manager.h" - "remmina_ext_exec.c" - "remmina_ext_exec.h" - "remmina_pref.c" - "remmina_pref_dialog.c" - "remmina_pref_dialog.h" - "remmina_pref.h" - "remmina_protocol_widget.c" - "remmina_protocol_widget.h" - "remmina_public.c" - "remmina_public.h" - "remmina_scrolled_viewport.c" - "remmina_scrolled_viewport.h" - "remmina_sftp_client.c" - "remmina_sftp_client.h" - "remmina_sftp_plugin.c" - "remmina_sftp_plugin.h" - "remmina_sodium.c" - "remmina_sodium.h" - "remmina_ssh.c" - "remmina_ssh.h" - "remmina_ssh_plugin.c" - "remmina_ssh_plugin.h" - "remmina_string_array.c" - "remmina_string_array.h" - "remmina_string_list.c" - "remmina_string_list.h" - "remmina_unlock.c" - "remmina_unlock.h" - "remmina_utils.c" - "remmina_utils.h" - "remmina_widget_pool.c" - "remmina_widget_pool.h" - "remmina_external_tools.c" - "remmina_external_tools.h" - "remmina_sysinfo.h" - "remmina_sysinfo.c" - "rcw.c" - "rcw.h" - "remmina_mpchange.c" - "remmina_mpchange.h" - "remmina_stats.c" - "remmina_stats.h" - "remmina_stats_sender.c" - "remmina_stats_sender.h" - ) + "remmina_about.c" + "remmina_about.h" + "remmina_applet_menu.c" + "remmina_applet_menu.h" + "remmina_applet_menu_item.c" + "remmina_applet_menu_item.h" + "remmina_avahi.c" + "remmina_avahi.h" + "remmina.c" + "remmina.h" + "remmina_chat_window.c" + "remmina_chat_window.h" + "remmina_crypt.c" + "remmina_crypt.h" + "remmina_exec.c" + "remmina_exec.h" + "remmina_file.c" + "remmina_file_editor.c" + "remmina_file_editor.h" + "remmina_file.h" + "remmina_file_manager.c" + "remmina_file_manager.h" + "remmina_ftp_client.c" + "remmina_ftp_client.h" + "remmina_icon.c" + "remmina_icon.h" + "remmina_key_chooser.c" + "remmina_key_chooser.h" + "remmina_log.c" + "remmina_log.h" + "remmina_main.c" + "remmina_main.h" + "remmina_marshals.c" + "remmina_marshals.h" + "remmina_marshals.list" + "remmina_masterthread_exec.c" + "remmina_masterthread_exec.h" + "remmina_message_panel.c" + "remmina_message_panel.h" + "remmina_plugin_manager.c" + "remmina_plugin_manager.h" + "remmina_ext_exec.c" + "remmina_ext_exec.h" + "remmina_pref.c" + "remmina_pref_dialog.c" + "remmina_pref_dialog.h" + "remmina_pref.h" + "remmina_protocol_widget.c" + "remmina_protocol_widget.h" + "remmina_public.c" + "remmina_public.h" + "remmina_scrolled_viewport.c" + "remmina_scrolled_viewport.h" + "remmina_sftp_client.c" + "remmina_sftp_client.h" + "remmina_sftp_plugin.c" + "remmina_sftp_plugin.h" + "remmina_sodium.c" + "remmina_sodium.h" + "remmina_ssh.c" + "remmina_ssh.h" + "remmina_ssh_plugin.c" + "remmina_ssh_plugin.h" + "remmina_string_array.c" + "remmina_string_array.h" + "remmina_string_list.c" + "remmina_string_list.h" + "remmina_unlock.c" + "remmina_unlock.h" + "remmina_utils.c" + "remmina_utils.h" + "remmina_widget_pool.c" + "remmina_widget_pool.h" + "remmina_external_tools.c" + "remmina_external_tools.h" + "remmina_sysinfo.h" + "remmina_sysinfo.c" + "rcw.c" + "rcw.h" + "rmnews.c" + "rmnews.h" + "remmina_mpchange.c" + "remmina_mpchange.h" + "remmina_scheduler.c" + "remmina_scheduler.h" + "remmina_stats.c" + "remmina_stats.h" + "remmina_stats_sender.c" + "remmina_stats_sender.h" + ) add_executable(remmina ${REMMINA_SRCS}) include_directories(${GTK_INCLUDE_DIRS}) target_link_libraries(remmina ${GTK_LIBRARIES}) if(WITH_MANPAGES) - install(FILES remmina.1 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man1) + install(FILES remmina.1 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man1) endif() find_package(X11) @@ -139,87 +143,87 @@ target_link_libraries(remmina ${CMAKE_THREAD_LIBS_INIT}) find_package(Intl) if(INTL_FOUND AND "${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD|OpenBSD|NetBSD") - message(STATUS "${CMAKE_SYSTEM_NAME} detected, building with Intl") - include_directories(${Intl_INCLUDE_DIRS}) - target_link_libraries(remmina ${Intl_LIBRARIES}) + message(STATUS "${CMAKE_SYSTEM_NAME} detected, building with Intl") + include_directories(${Intl_INCLUDE_DIRS}) + target_link_libraries(remmina ${Intl_LIBRARIES}) endif() find_suggested_package(LIBSSH) if(LIBSSH_FOUND) - add_definitions(-DHAVE_LIBSSH) - include_directories(${LIBSSH_INCLUDE_DIRS}) - target_link_libraries(remmina ${LIBSSH_LIBRARIES}) + add_definitions(-DHAVE_LIBSSH) + include_directories(${LIBSSH_INCLUDE_DIRS}) + target_link_libraries(remmina ${LIBSSH_LIBRARIES}) endif() if(GCRYPT_FOUND) - include_directories(${GCRYPT_INCLUDE_DIRS}) - target_link_libraries(remmina ${GCRYPT_LIBRARIES}) + include_directories(${GCRYPT_INCLUDE_DIRS}) + target_link_libraries(remmina ${GCRYPT_LIBRARIES}) endif() if(AVAHI_FOUND) - include_directories(${AVAHI_INCLUDE_DIRS}) - target_link_libraries(remmina ${AVAHI_LIBRARIES}) + include_directories(${AVAHI_INCLUDE_DIRS}) + target_link_libraries(remmina ${AVAHI_LIBRARIES}) endif() if(OPENSSL_FOUND) - include_directories(${OPENSSL_INCLUDE_DIRS}) - target_link_libraries(remmina ${OPENSSL_LIBRARIES}) + include_directories(${OPENSSL_INCLUDE_DIRS}) + target_link_libraries(remmina ${OPENSSL_LIBRARIES}) endif() option(WITH_VTE "Build with support for VTE" ON) if(GTK3_FOUND AND WITH_VTE) - set(_VTE_VERSION_NUMS 2.91 2.90) - foreach(__VTE_VERSION ${_VTE_VERSION_NUMS}) - set(_VTE_VERSION_NUM ${__VTE_VERSION}) - find_package(VTE) - if(VTE_FOUND) - break() - endif() - message(STATUS "VTE ${__VTE_VERSION} not found") - endforeach() + set(_VTE_VERSION_NUMS 2.91 2.90) + foreach(__VTE_VERSION ${_VTE_VERSION_NUMS}) + set(_VTE_VERSION_NUM ${__VTE_VERSION}) + find_package(VTE) + if(VTE_FOUND) + break() + endif() + message(STATUS "VTE ${__VTE_VERSION} not found") + endforeach() elseif(WITH_VTE) - set(_VTE_VERSION_NUM) - find_package(VTE) + set(_VTE_VERSION_NUM) + find_package(VTE) endif() if(VTE_FOUND) - add_definitions(-DHAVE_LIBVTE) - include_directories(${VTE_INCLUDE_DIRS}) - target_link_libraries(remmina ${VTE_LIBRARIES}) + add_definitions(-DHAVE_LIBVTE) + include_directories(${VTE_INCLUDE_DIRS}) + target_link_libraries(remmina ${VTE_LIBRARIES}) endif() if(sodium_FOUND) - include_directories(${sodium_INCLUDE_DIR}) - target_link_libraries(remmina sodium) + include_directories(${sodium_INCLUDE_DIR}) + target_link_libraries(remmina sodium) endif() if(GTK3_FOUND) - find_suggested_package(APPINDICATOR) - if(APPINDICATOR_FOUND) - add_definitions(-DHAVE_LIBAPPINDICATOR) - include_directories(${APPINDICATOR_INCLUDE_DIRS}) - target_link_libraries(remmina ${APPINDICATOR_LIBRARIES}) - endif() - find_required_package(JSONGLIB) - if(JSONGLIB_FOUND) - include_directories(${JSONGLIB_INCLUDE_DIRS}) - target_link_libraries(remmina ${JSONGLIB_LIBRARIES}) - else() - message(FATAL_ERROR "json-glib library not found") - endif() - find_required_package(LIBSOUP24) - if(LIBSOUP24_FOUND) - include_directories(${LIBSOUP24_INCLUDE_DIRS}) - target_link_libraries(remmina ${LIBSOUP24_LIBRARIES}) - else() - message(FATAL_ERROR "libsoup 2.4 library not found") - endif() + find_suggested_package(APPINDICATOR) + if(APPINDICATOR_FOUND) + add_definitions(-DHAVE_LIBAPPINDICATOR) + include_directories(${APPINDICATOR_INCLUDE_DIRS}) + target_link_libraries(remmina ${APPINDICATOR_LIBRARIES}) + endif() + find_required_package(JSONGLIB) + if(JSONGLIB_FOUND) + include_directories(${JSONGLIB_INCLUDE_DIRS}) + target_link_libraries(remmina ${JSONGLIB_LIBRARIES}) + else() + message(FATAL_ERROR "json-glib library not found") + endif() + find_required_package(LIBSOUP24) + if(LIBSOUP24_FOUND) + include_directories(${LIBSOUP24_INCLUDE_DIRS}) + target_link_libraries(remmina ${LIBSOUP24_LIBRARIES}) + else() + message(FATAL_ERROR "libsoup 2.4 library not found") + endif() endif() add_subdirectory(external_tools) install(TARGETS remmina DESTINATION ${CMAKE_INSTALL_BINDIR}) install(DIRECTORY include/remmina/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/remmina - FILES_MATCHING PATTERN "*.h") + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/remmina + FILES_MATCHING PATTERN "*.h") diff --git a/src/remmina.c b/src/remmina.c index fa085b907..abf80ddad 100644 --- a/src/remmina.c +++ b/src/remmina.c @@ -55,6 +55,7 @@ #include "remmina_ssh_plugin.h" #include "remmina_widget_pool.h" #include "remmina/remmina_trace_calls.h" +#include "rmnews.h" #include "remmina_stats_sender.h" @@ -240,6 +241,7 @@ static void remmina_on_startup(GApplication *app) g_application_hold(app); remmina_stats_sender_schedule(); + rmnews_schedule(); /* Check for secret plugin and service initialization and show some warnings on the console if * there is something missing */ diff --git a/src/remmina_pref.c b/src/remmina_pref.c index 673c9632a..b248bcd80 100644 --- a/src/remmina_pref.c +++ b/src/remmina_pref.c @@ -663,6 +663,11 @@ void remmina_pref_init(void) remmina_pref.periodic_usage_stats_uuid_prefix = NULL; + if (g_key_file_has_key(gkeyfile, "remmina_news", "periodic_rmnews_last_get", NULL)) + remmina_pref.periodic_rmnews_last_get = g_key_file_get_int64(gkeyfile, "remmina_news", "periodic_rmnews_last_get", NULL); + else + remmina_pref.periodic_rmnews_last_get = 0; + g_key_file_free(gkeyfile); if (remmina_pref.secret == NULL) @@ -774,6 +779,7 @@ gboolean remmina_pref_save(void) g_key_file_set_boolean(gkeyfile, "usage_stats", "periodic_usage_stats_permission_asked", remmina_pref.periodic_usage_stats_permission_asked); g_key_file_set_boolean(gkeyfile, "usage_stats", "periodic_usage_stats_permitted", remmina_pref.periodic_usage_stats_permitted); g_key_file_set_int64(gkeyfile, "usage_stats", "periodic_usage_stats_last_sent", remmina_pref.periodic_usage_stats_last_sent); + g_key_file_set_int64(gkeyfile, "remmina_news", "periodic_rmnews_last_get", remmina_pref.periodic_rmnews_last_get); g_key_file_set_string(gkeyfile, "usage_stats", "periodic_usage_stats_uuid_prefix", remmina_pref.periodic_usage_stats_uuid_prefix ? remmina_pref.periodic_usage_stats_uuid_prefix : ""); diff --git a/src/remmina_pref.h b/src/remmina_pref.h index 4ff2d27a0..609048c10 100644 --- a/src/remmina_pref.h +++ b/src/remmina_pref.h @@ -202,6 +202,8 @@ typedef struct _RemminaPref { gchar *periodic_usage_stats_uuid_prefix; gchar *last_success; + /* Remmina news */ + glong periodic_rmnews_last_get; } RemminaPref; diff --git a/src/remmina_scheduler.c b/src/remmina_scheduler.c new file mode 100644 index 000000000..9d36595dd --- /dev/null +++ b/src/remmina_scheduler.c @@ -0,0 +1,90 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2016-2019 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +#include "config.h" +#include <stdlib.h> +#include <gio/gio.h> +#include <glib/gi18n.h> +#include <string.h> +#include "remmina_scheduler.h" +#include "remmina/remmina_trace_calls.h" + +static gboolean remmina_scheduler_periodic_check(gpointer user_data) +{ + TRACE_CALL(__func__); + rsSchedData *rssd = (rsSchedData *)user_data; + + rssd->count++; + if (rssd->cb_func_ptr(rssd->cb_func_data) == G_SOURCE_REMOVE) { + g_free(rssd); + return G_SOURCE_REMOVE; + } + if (rssd->count <= 1) { + rssd->source = g_timeout_add_full(G_PRIORITY_LOW, + rssd->interval, + remmina_scheduler_periodic_check, + rssd, + NULL); + return G_SOURCE_REMOVE; + } + return G_SOURCE_CONTINUE; +} + +void *remmina_scheduler_setup(GSourceFunc cb, + gpointer cb_data, + guint first_interval, + guint interval) +{ + TRACE_CALL(__func__); + rsSchedData *rssd; + rssd = g_malloc(sizeof(rsSchedData)); + rssd->cb_func_ptr = cb; + rssd->cb_func_data = cb_data; + rssd->interval = interval; + rssd->count = 0; + rssd->source = g_timeout_add_full(G_PRIORITY_LOW, + first_interval, + remmina_scheduler_periodic_check, + rssd, + NULL); + return (void *)rssd; +} + +void remmina_schedluer_remove(void *s) +{ + TRACE_CALL(__func__); + rsSchedData *rssd = (rsSchedData *)s; + g_source_remove(rssd->source); + g_free(rssd); +} diff --git a/src/remmina_scheduler.h b/src/remmina_scheduler.h new file mode 100644 index 000000000..5668fd43f --- /dev/null +++ b/src/remmina_scheduler.h @@ -0,0 +1,49 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2016-2019 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +#pragma once + +typedef struct { + GSourceFunc cb_func_ptr; + gpointer cb_func_data; + guint interval; + guint source; + guint count; +} rsSchedData; + +G_BEGIN_DECLS + +void *remmina_scheduler_setup(GSourceFunc cb, gpointer cb_data, guint first_interval, guint interval); + +G_END_DECLS diff --git a/src/remmina_stats_sender.c b/src/remmina_stats_sender.c index 4c33f6326..5d42f61e6 100644 --- a/src/remmina_stats_sender.c +++ b/src/remmina_stats_sender.c @@ -39,6 +39,7 @@ #include <openssl/rsa.h> #include <openssl/pem.h> #include <openssl/err.h> +#include "remmina_scheduler.h" #include "remmina/remmina_trace_calls.h" #include "remmina_log.h" #include "remmina_stats.h" @@ -57,8 +58,8 @@ #define PERIODIC_UPLOAD_URL "https://www.remmina.org/stats/upload_stats.php" -static gint periodic_check_source; -static gint periodic_check_counter; +//static gint periodic_check_source; +//static gint periodic_check_counter; static char *remmina_RSA_PubKey_v1 = "-----BEGIN PUBLIC KEY-----\n" @@ -315,27 +316,14 @@ static gboolean remmina_stats_sender_periodic_check(gpointer user_data) remmina_stats_sender_send(FALSE); } - periodic_check_counter++; - if (periodic_check_counter <= 1) { - /* Reschedule periodic check less frequently after 1st tick. - * Note that PERIODIC_CHECK_INTERVAL_MS becomes also a retry interval in case of - * upload failure */ - periodic_check_source = g_timeout_add_full(G_PRIORITY_LOW, PERIODIC_CHECK_INTERVAL_MS, remmina_stats_sender_periodic_check, NULL, NULL); - return G_SOURCE_REMOVE; - } return G_SOURCE_CONTINUE; } void remmina_stats_sender_schedule() { TRACE_CALL(__func__); - /* If permitted, schedule the 1st statistics periodic check */ - if (remmina_stat_sender_can_send()) { - periodic_check_counter = 0; - periodic_check_source = g_timeout_add_full(G_PRIORITY_LOW, PERIODIC_CHECK_1ST_MS, remmina_stats_sender_periodic_check, NULL, NULL); - } else - periodic_check_source = 0; + remmina_scheduler_setup(remmina_stats_sender_periodic_check, + NULL, + PERIODIC_CHECK_1ST_MS, + PERIODIC_CHECK_INTERVAL_MS); } - - - diff --git a/src/remmina_unlock.c b/src/remmina_unlock.c index 8e468bb7c..2050193c2 100644 --- a/src/remmina_unlock.c +++ b/src/remmina_unlock.c @@ -118,17 +118,14 @@ gint remmina_unlock_new(GtkWindow *parent) unlock_timeout = remmina_pref.unlock_timeout; - g_print ("%f unlock_timeout\n", unlock_timeout); remmina_unlock_dialog = g_new0(RemminaUnlockDialog, 1); remmina_unlock_dialog->retval = 1; - //gtk_widget_destroy(GTK_WIDGET(remmina_unlock_dialog->dialog)); if (timer == NULL) remmina_unlock_timer_init(); if (timer != NULL) elapsed = g_timer_elapsed(timer, NULL); - g_print ("elapsed %f\n", elapsed); if (((int)unlock_timeout - elapsed) < 0) lock = TRUE; if (((int)unlock_timeout - elapsed) >= 0) lock = FALSE; /* timer & timout = 0 */ diff --git a/src/remmina_utils.c b/src/remmina_utils.c index 90f1d8f57..adc34b5ba 100644 --- a/src/remmina_utils.c +++ b/src/remmina_utils.c @@ -427,3 +427,55 @@ const gchar* remmina_utils_get_os_info() return kernel_string; } +/** + * Create a hexadecimal string version of the SHA-1 digest of the + * contents of the named file. + * + * @return a newly allocated string which the caller + * should free() when finished. + * + * If any error occurs while reading the file, (permission denied, + * file not found, etc.), this function returns NULL. + * + * Taken from https://github.com/ttuegel/notmuch do PR in case of substantial modifications. + * + */ +gchar* remmina_sha1_file (const gchar *filename) +{ + FILE *file; +#define BLOCK_SIZE 4096 + unsigned char block[BLOCK_SIZE]; + size_t bytes_read; + GChecksum *sha1; + char *digest = NULL; + + file = fopen (filename, "r"); + if (file == NULL) + return NULL; + + sha1 = g_checksum_new (G_CHECKSUM_SHA1); + if (sha1 == NULL) + goto DONE; + + while (1) { + bytes_read = fread (block, 1, 4096, file); + if (bytes_read == 0) { + if (feof (file)) + break; + else if (ferror (file)) + goto DONE; + } else { + g_checksum_update (sha1, block, bytes_read); + } + } + + digest = g_strdup (g_checksum_get_string (sha1)); + + DONE: + if (sha1) + g_checksum_free (sha1); + if (file) + fclose (file); + + return digest; +} diff --git a/src/remmina_utils.h b/src/remmina_utils.h index f1f96fe1c..156dce934 100644 --- a/src/remmina_utils.h +++ b/src/remmina_utils.h @@ -54,3 +54,4 @@ gchar* remmina_utils_get_lsb_release(); gchar* remmina_utils_get_lsb_codename(); GHashTable* remmina_utils_get_etc_release(); const gchar* remmina_utils_get_os_info(); +gchar* remmina_sha1_file (const gchar *filename); diff --git a/src/rmnews.c b/src/rmnews.c new file mode 100644 index 000000000..6f35e1dc1 --- /dev/null +++ b/src/rmnews.c @@ -0,0 +1,321 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2016-2019 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +#include "config.h" +#include "remmina/remmina_trace_calls.h" + +#include <stdlib.h> +#include <stdio.h> +#include <gtk/gtk.h> +#include <gio/gio.h> +#include <glib/gi18n.h> +#include <libsoup/soup.h> + +#include "remmina_main.h" +#include "remmina_pref.h" +#include "remmina_public.h" +#include "remmina_utils.h" +#include "remmina_scheduler.h" +#include "rmnews.h" + +/* Neas file buffer */ +#define READ_BUFFER_LEN 1024 +/* Timers */ +#define RMNEWS_CHECK_1ST_MS 3000 +#define RMNEWS_CHECK_INTERVAL_MS 12000 +/* How many seconds before to get news */ +#define RMNEWS_INTERVAL_SEC 604800 +/* TODO: move in config.h */ +#define REMMINA_URL "https://remmina.org/" +#define RMNEWS_OUTPUT "/var/tmp/latest_news.md" + +static RemminaNewsDialog *rmnews_news_dialog; +#define GET_OBJ(object_name) gtk_builder_get_object(rmnews_news_dialog->builder, object_name) + +static SoupSession *session; +static const gchar *rmnews_url = NULL; +static const gchar *output_file_path = NULL; + +static gchar *rmnews_get_file_contents(gchar *path) +{ + gsize size; + gchar *content; + + if (g_file_get_contents(path, &content, &size, NULL)) { + if (!g_utf8_validate(content, size, NULL)) { + g_error("%s content is not UTF-8", path); + g_free(content); + } + } + //return g_markup_escape_text(content, strlen(content)); + return content; +} + +static void rmnews_close_clicked(GtkButton *btn, gpointer user_data) +{ + TRACE_CALL(__func__); + gtk_widget_destroy(GTK_WIDGET(rmnews_news_dialog->dialog)); + rmnews_news_dialog->dialog = NULL; +} +static gint rmnews_show_news(GtkWindow *parent) +{ + TRACE_CALL(__func__); + + rmnews_news_dialog = g_new0(RemminaNewsDialog, 1); + rmnews_news_dialog->retval = 1; + + rmnews_news_dialog->builder = remmina_public_gtk_builder_new_from_file("remmina_news.glade"); + rmnews_news_dialog->dialog = GTK_DIALOG(gtk_builder_get_object(rmnews_news_dialog->builder, "RemminaNewsDialog")); + if (parent) + gtk_window_set_transient_for(GTK_WINDOW(rmnews_news_dialog->dialog), parent); + + rmnews_news_dialog->rmnews_text_view = GTK_TEXT_VIEW(GET_OBJ("rmnews_text_view")); + rmnews_news_dialog->rmnews_label = GTK_LABEL(GET_OBJ("rmnews_label")); + rmnews_news_dialog->rmnews_button_close = GTK_BUTTON(GET_OBJ("rmnews_button_close")); + gtk_widget_set_can_default(GTK_WIDGET(rmnews_news_dialog->rmnews_button_close), TRUE); + gtk_widget_grab_default(GTK_WIDGET(rmnews_news_dialog->rmnews_button_close)); + + gchar *contents = rmnews_get_file_contents(g_strdup(output_file_path)); + if (contents) { + gtk_label_set_markup(rmnews_news_dialog->rmnews_label, contents); + g_free(contents); + } + + g_signal_connect(rmnews_news_dialog->rmnews_button_close, "clicked", + G_CALLBACK(rmnews_close_clicked), (gpointer)rmnews_news_dialog); + g_signal_connect(rmnews_news_dialog->dialog, "close", + G_CALLBACK(rmnews_close_clicked), (gpointer)rmnews_news_dialog); + + /* Connect signals */ + gtk_builder_connect_signals(rmnews_news_dialog->builder, NULL); + gtk_dialog_run(rmnews_news_dialog->dialog); + return rmnews_news_dialog->retval; +} + +static void rmnews_get_url_cb(SoupSession *session, SoupMessage *msg, gpointer data) +{ + TRACE_CALL(__func__); + const char *name; + const char *header; + FILE *output_file = NULL; + gchar *filesha = NULL; + gchar *filesha_after = NULL; + GTimeVal t; + + g_info("Status code %d", msg->status_code); + + name = soup_message_get_uri(msg)->path; + + if (SOUP_STATUS_IS_CLIENT_ERROR(msg->status_code)) { + g_info("Status 404 - Release file not available"); + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + remmina_pref_save(); + return; + } + + if (SOUP_STATUS_IS_SERVER_ERROR(msg->status_code)) { + g_info("Server not available"); + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + remmina_pref_save(); + return; + } + + if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) { + g_info("Transport Error"); + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + remmina_pref_save(); + return; + } + + if (msg->status_code == SOUP_STATUS_SSL_FAILED) { + GTlsCertificateFlags flags; + + if (soup_message_get_https_status(msg, NULL, &flags)) + g_warning("%s: %d %s (0x%x)\n", name, msg->status_code, msg->reason_phrase, flags); + else + g_warning("%s: %d %s (no handshake status)\n", name, msg->status_code, msg->reason_phrase); + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + remmina_pref_save(); + return; + } else if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) { + g_warning("%s: %d %s\n", name, msg->status_code, msg->reason_phrase); + } + + if (SOUP_STATUS_IS_REDIRECTION(msg->status_code)) { + header = soup_message_headers_get_one(msg->response_headers, + "Location"); + g_warning("Redirection detected"); + if (header) { + SoupURI *uri; + char *uri_string; + + g_info(" -> %s\n", header); + + uri = soup_uri_new_with_base(soup_message_get_uri(msg), header); + uri_string = soup_uri_to_string(uri, FALSE); + rmnews_get_url(uri_string); + g_free(uri_string); + soup_uri_free(uri); + } + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + remmina_pref_save(); + return; + } else if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + g_info("Status 200"); + if (output_file_path) { + g_info("Calculating the SHA1 of the local file"); + filesha = remmina_sha1_file(output_file_path); + g_info("SHA1 is %s", filesha); + if (filesha == NULL || filesha[0] == 0) + filesha = "0\0"; + g_info("Opening %s output file for writing", output_file_path); + output_file = fopen(output_file_path, "w"); + if (!output_file) { + g_printerr("Error trying to create file %s.\n", output_file_path); + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + remmina_pref_save(); + return; + } + } else { + g_error("Cannot open output file for writing, because output_file_path is NULL"); + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + remmina_pref_save(); + return; + } + + + if (output_file) { + fwrite(msg->response_body->data, + 1, + msg->response_body->length, + output_file); + + if (output_file_path) + fclose(output_file); + if (output_file_path) + filesha_after = remmina_sha1_file(output_file_path); + g_info("SHA1 after download is %s", filesha_after); + if (g_strcmp0(filesha, filesha_after) != 0) { + g_info("SHA1 differs, we show the news and reset the counter"); + remmina_pref.periodic_rmnews_last_get = 0; + rmnews_show_news(remmina_main_get_window()); + } else { + g_get_current_time(&t); + remmina_pref.periodic_rmnews_last_get = t.tv_sec; + } + remmina_pref_save(); + } + } + g_object_unref(msg); +} + +void rmnews_get_url(const char *url) +{ + TRACE_CALL(__func__); + + SoupMessage *msg; + + msg = soup_message_new("GET", url); + soup_message_set_flags(msg, SOUP_MESSAGE_NO_REDIRECT); + + g_info("Fetching %s", url); + + g_object_ref(msg); + soup_session_queue_message(session, msg, rmnews_get_url_cb, NULL); +} + +void rmnews_get_news() +{ + TRACE_CALL(__func__); + + SoupLogger *logger = NULL; + + gchar *cachedir = g_build_path("/", g_get_user_cache_dir(), REMMINA_APP_ID, NULL); + g_mkdir_with_parents(cachedir, 0750); + output_file_path = g_build_path("/", cachedir, "latest_news.md", NULL); + + if (output_file_path) { + g_info("Output file set to %s", output_file_path); + } else { + output_file_path = RMNEWS_OUTPUT; + g_warning("Output file set to %s", output_file_path); + } + + g_info("Gathering news"); + session = g_object_new(SOUP_TYPE_SESSION, + SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER, + SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR, + SOUP_SESSION_USER_AGENT, "get ", + SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE, + NULL); + logger = soup_logger_new(SOUP_LOGGER_LOG_BODY, -1); + soup_session_add_feature(session, SOUP_SESSION_FEATURE(logger)); + g_object_unref(logger); + + rmnews_get_url(g_strconcat(REMMINA_URL, + "remmina_news.", + VERSION, + ".md", + NULL)); + + g_object_unref(session); +} + +static gboolean rmnews_periodic_check(gpointer user_data) +{ + TRACE_CALL(__func__); + GTimeVal t; + glong next; + + next = remmina_pref.periodic_rmnews_last_get + RMNEWS_INTERVAL_SEC; + g_get_current_time(&t); + if (t.tv_sec > next || (t.tv_sec < remmina_pref.periodic_rmnews_last_get && t.tv_sec > 1514764800)) + rmnews_get_news(); + return G_SOURCE_CONTINUE; +} +void rmnews_schedule() +{ + TRACE_CALL(__func__); + remmina_scheduler_setup(rmnews_periodic_check, + NULL, + RMNEWS_CHECK_1ST_MS, + RMNEWS_CHECK_INTERVAL_MS); +} diff --git a/src/rmnews.h b/src/rmnews.h new file mode 100644 index 000000000..e76a87b76 --- /dev/null +++ b/src/rmnews.h @@ -0,0 +1,51 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2016-2019 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +typedef struct _RemminaNewsDialog { + GtkBuilder * builder; + GtkDialog * dialog; + + GtkTextView * rmnews_text_view; + GtkLabel * rmnews_label; + GtkButton * rmnews_button_close; + + gint retval; +} RemminaNewsDialog; + +#pragma once + +G_BEGIN_DECLS +void rmnews_schedule(); +void rmnews_get_url(const char *url); +G_END_DECLS |