app: check last GIMP release from gimp.org/gimp_versions.json.

GIMP will now process the remote gimp_versions json file to look if one
is using the last version of GIMP. This initial code doesn't act up yet
on this information. This will come in further commits.

Here are the characteristics:
- Since this requires internet access, a new checkbox is available in
  the Preferences dialog, allowing to disable version checks. Note that
  it is enabled by default as it is an important security feature, but
  it has to be deactivatable.
- The remote access is done as an async operation because we don't want
  it to block the startup in any way (for whatever reason). Also it
  doesn't output errors if it fails to not be a bother (you don't
  technically need internet access for an image program).
- We don't check at every startup. At each successful check, we save a
  timestamp to prevent too frequent useless checks (I set it the timer
  to a week or more for now).
This commit is contained in:
Jehan 2019-12-08 19:08:49 +01:00
parent bed5fe37d8
commit 506a0476f4
9 changed files with 303 additions and 0 deletions

View File

@ -73,6 +73,8 @@ libapp_sources = \
gimp-log.c \
gimp-log.h \
gimp-priorities.h \
gimp-update.c \
gimp-update.h \
gimp-version.c \
gimp-version.h

View File

@ -73,6 +73,7 @@
#include "gimp-debug.h"
#include "gimp-intl.h"
#include "gimp-update.h"
/* local prototypes */
@ -196,6 +197,7 @@ app_run (const gchar *full_prog_name,
GimpLangRc *temprc;
gchar *language = NULL;
GError *font_error = NULL;
gboolean save_gimprc_at_exit = FALSE;
if (filenames && filenames[0] && ! filenames[1] &&
g_file_test (filenames[0], G_FILE_TEST_IS_DIR))
@ -286,6 +288,12 @@ app_run (const gchar *full_prog_name,
gimp_load_config (gimp, alternate_system_gimprc, alternate_gimprc);
/* We usually only save gimprc when Preferences are edited.
* Thus we have to add a special flag when we make an update
* check so that the timestamp is saved.
*/
save_gimprc_at_exit = gimp_update_check (gimp->config);
/* Initialize the error handling after creating/migrating the config
* directory because it will create some folders for backup and crash
* logs in advance. Therefore running this before
@ -436,6 +444,9 @@ app_run (const gchar *full_prog_name,
if (gimp->be_verbose)
g_print ("EXIT: %s\n", G_STRFUNC);
if (save_gimprc_at_exit)
gimp_rc_save (GIMP_RC (gimp->config));
g_main_loop_unref (loop);
gimp_gegl_exit (gimp);

View File

@ -128,6 +128,9 @@ enum
PROP_EXPORT_METADATA_XMP,
PROP_EXPORT_METADATA_IPTC,
PROP_DEBUG_POLICY,
PROP_CHECK_UPDATES,
PROP_CHECK_UPDATE_TIMESTAMP,
PROP_LAST_KNOWN_RELEASE,
/* ignored, only for backward compatibility: */
PROP_INSTALL_COLORMAP,
@ -676,6 +679,27 @@ gimp_core_config_class_init (GimpCoreConfigClass *klass)
GIMP_PARAM_STATIC_STRINGS |
GIMP_CONFIG_PARAM_AGGREGATE);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CHECK_UPDATES,
"check-updates",
"Check for updates",
CHECK_UPDATES_BLURB,
TRUE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_INT64 (object_class, PROP_CHECK_UPDATE_TIMESTAMP,
"check-update-timestamp",
"timestamp of the last update check",
CHECK_UPDATE_TIMESTAMP_BLURB,
0, G_MAXINT64, 0,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_STRING (object_class, PROP_LAST_KNOWN_RELEASE,
"last-known-release",
"last known release of GIMP",
LAST_KNOWN_RELEASE_BLURB,
NULL,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SAVE_DOCUMENT_HISTORY,
"save-document-history",
"Save document history",
@ -849,6 +873,8 @@ gimp_core_config_finalize (GObject *object)
g_free (core_config->plug_in_rc_path);
g_free (core_config->import_raw_plug_in);
g_clear_pointer (&core_config->last_known_release, g_free);
g_clear_object (&core_config->default_image);
g_clear_object (&core_config->default_grid);
g_clear_object (&core_config->color_management);
@ -1052,6 +1078,15 @@ gimp_core_config_set_property (GObject *object,
gimp_config_sync (g_value_get_object (value),
G_OBJECT (core_config->color_management), 0);
break;
case PROP_CHECK_UPDATES:
core_config->check_updates = g_value_get_boolean (value);
break;
case PROP_CHECK_UPDATE_TIMESTAMP:
core_config->check_update_timestamp = g_value_get_int64 (value);
break;
case PROP_LAST_KNOWN_RELEASE:
core_config->last_known_release = g_value_dup_string (value);
break;
case PROP_SAVE_DOCUMENT_HISTORY:
core_config->save_document_history = g_value_get_boolean (value);
break;
@ -1264,6 +1299,15 @@ gimp_core_config_get_property (GObject *object,
case PROP_COLOR_MANAGEMENT:
g_value_set_object (value, core_config->color_management);
break;
case PROP_CHECK_UPDATES:
g_value_set_boolean (value, core_config->check_updates);
break;
case PROP_CHECK_UPDATE_TIMESTAMP:
g_value_set_int64 (value, core_config->check_update_timestamp);
break;
case PROP_LAST_KNOWN_RELEASE:
g_value_set_string (value, core_config->last_known_release);
break;
case PROP_SAVE_DOCUMENT_HISTORY:
g_value_set_boolean (value, core_config->save_document_history);
break;

View File

@ -103,6 +103,10 @@ struct _GimpCoreConfig
gboolean export_metadata_xmp;
gboolean export_metadata_iptc;
GimpDebugPolicy debug_policy;
gboolean check_updates;
gint64 check_update_timestamp;
gchar *last_known_release;
};
struct _GimpCoreConfigClass

View File

@ -40,6 +40,12 @@ _("Specifies whether to keep the canvas padding when \"View -> Show All\" " \
#define CANVAS_PADDING_MODE_BLURB \
_("Specifies how the area around the image should be drawn.")
#define CHECK_UPDATES_BLURB \
_("Check for availability of GIMP updates through background internet queries.")
#define CHECK_UPDATE_TIMESTAMP_BLURB \
_("Timestamp of the last update check.")
#define COLOR_MANAGEMENT_BLURB \
"Defines the color management behavior."
@ -255,6 +261,9 @@ _("Sets the level of interpolation used for scaling and other " \
#define LANGUAGE_BLURB \
_("Specifies the language to use for the user interface.")
#define LAST_KNOWN_RELEASE_BLURB \
_("The last known release version of GIMP as queried from official website.")
#define LAST_OPENED_SIZE_BLURB \
_("How many recently opened image filenames to keep on the File menu.")

View File

@ -1209,6 +1209,14 @@ prefs_dialog_new (Gimp *gimp,
"(please report)."));
gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
/* Internet access */
vbox2 = prefs_frame_new (_("Network access"), GTK_CONTAINER (vbox),
FALSE);
prefs_check_button_add (object, "check-updates",
_("Check for updates (requires internet)"),
GTK_BOX (vbox2));
/* Image Thumbnails */
vbox2 = prefs_frame_new (_("Image Thumbnails"), GTK_CONTAINER (vbox), FALSE);

189
app/gimp-update.c Normal file
View File

@ -0,0 +1,189 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimp-update.c
* Copyright (C) 2019 Jehan
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib.h>
#include <json-glib/json-glib.h>
#include <stdio.h>
#include "libgimpbase/gimpbase.h"
#include "core/core-types.h"
#include "config/gimpcoreconfig.h"
#include "gimp-intl.h"
#include "gimp-update.h"
static gboolean
gimp_version_break (const gchar *v,
gint *major,
gint *minor,
gint *micro)
{
gchar **versions;
*major = 0;
*minor = 0;
*micro = 0;
if (v == NULL)
return FALSE;
versions = g_strsplit_set (v, ".", 3);
if (versions[0] != NULL)
{
*major = g_ascii_strtoll (versions[0], NULL, 10);
if (versions[1] != NULL)
{
*minor = g_ascii_strtoll (versions[1], NULL, 10);
if (versions[2] != NULL)
{
*micro = g_ascii_strtoll (versions[2], NULL, 10);
return TRUE;
}
}
}
g_strfreev (versions);
return (*major > 0);
}
static const gchar *
gimp_version_max (const gchar *v1,
const gchar *v2)
{
gint major1;
gint minor1;
gint micro1;
gint major2;
gint minor2;
gint micro2;
if (v1 == NULL)
return v2;
else if (v2 == NULL)
return v1;
else if (gimp_version_break (v1, &major1, &minor1, &micro1) &&
gimp_version_break (v2, &major2, &minor2, &micro2))
{
if (major1 > major2 ||
(major1 == major2 && minor1 > minor2) ||
(major1 == major2 && minor1 == minor2 && micro1 > micro2))
return v1;
else
return v2;
}
return NULL;
}
static void
gimp_check_updates_callback (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GFileInputStream *stream;
GimpCoreConfig *config = user_data;
GError *error = NULL;
stream = g_file_read_finish (G_FILE (source), result, &error);
if (stream)
{
JsonParser *parser = NULL;
JsonPath *path;
JsonNode *result;
JsonObject *versions;
GList *members;
GList *iter;
const gchar *last_version = NULL;
gint major;
gint minor;
gint micro;
parser = json_parser_new ();
if (! json_parser_load_from_stream (parser, G_INPUT_STREAM (stream), NULL, &error))
{
g_clear_object (&stream);
g_clear_object (&parser);
return;
}
path = json_path_new ();
json_path_compile (path, "$['STABLE']", &error);
result = json_path_match (path, json_parser_get_root (parser));
versions = json_array_get_object_element (json_node_get_array (result), 0);
members = json_object_get_members (versions);
for (iter = members; iter; iter = iter->next)
last_version = gimp_version_max (last_version, iter->data);
/* If version is not properly parsed, something is wrong with
* upstream version number or parsing. This should not happen.
*/
if (gimp_version_break (last_version, &major, &minor, &micro))
{
g_object_set (config,
"check-update-timestamp", g_get_real_time(),
"last-known-release",
(major > GIMP_MAJOR_VERSION ||
(major == GIMP_MAJOR_VERSION && minor > GIMP_MINOR_VERSION) ||
(major == GIMP_MAJOR_VERSION && minor == GIMP_MINOR_VERSION && micro > GIMP_MICRO_VERSION)) ?
last_version : NULL,
NULL);
}
g_list_free (members);
json_node_unref (result);
g_object_unref (path);
g_object_unref (parser);
g_object_unref (stream);
}
}
gboolean
gimp_update_check (GimpCoreConfig *config)
{
GFile *gimp_versions;
gint64 prev_update_timestamp;
gint64 current_timestamp;
if (! config->check_updates)
return FALSE;
g_object_get (config,
"check-update-timestamp", &prev_update_timestamp,
NULL);
current_timestamp = g_get_real_time();
/* Do not check more than once a week. */
if (current_timestamp < prev_update_timestamp + G_USEC_PER_SEC * 3600L * 24L * 7L)
return FALSE;
gimp_versions = g_file_new_for_uri ("https://testing.gimp.org/gimp_versions.json");
g_file_read_async (gimp_versions, 0, NULL, gimp_check_updates_callback, config);
g_object_unref (gimp_versions);
return TRUE;
}

28
app/gimp-update.h Normal file
View File

@ -0,0 +1,28 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimp-update.h
* Copyright (C) 2019 Jehan
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __APP_GIMP_UPDATE_H__
#define __APP_GIMP_UPDATE_H__
gboolean gimp_update_check (GimpCoreConfig *config);
#endif /* __APP_GIMP_UPDATE_H__ */

View File

@ -67,6 +67,7 @@ m4_define([gtkdoc_required_version], [1.0])
m4_define([harfbuzz_required_version], [0.9.19])
m4_define([intltool_required_version], [0.40.1])
m4_define([introspection_required_version], [1.32.0])
m4_define([json_glib_required_version], [1.2.6])
m4_define([lcms_required_version], [2.8])
m4_define([libgudev_required_version], [167])
m4_define([libheif_required_version], [1.3.2])
@ -2280,6 +2281,13 @@ AM_CONDITIONAL(WITH_PDBGEN, test "x$with_pdbgen" = xyes)
GOBJECT_INTROSPECTION_REQUIRE(introspection_required_version)
#####################
# Check for json-glib
#####################
PKG_CHECK_MODULES(JSON_GLIB, json-glib-1.0 >= json_glib_required_version,,
[add_deps_error([json-glib-1.0 >= json_glib_required_version])])
#######################################
# Check for python runtime dependencies
#######################################