[gimp/gimp-2-10] app: check last GIMP release from gimp.org/gimp_versions.json.



commit ef4b353b49ba4d767953b82c152856fe1b3ce0a3
Author: Jehan <jehan girinstud io>
Date:   Sun Dec 8 19:08:49 2019 +0100

    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).
    
    (cherry picked from commit 506a0476f4b6615a6a578e8926a856a5db435de0)

 app/Makefile.am                  |   2 +
 app/app.c                        |  11 +++
 app/config/gimpcoreconfig.c      |  44 +++++++++
 app/config/gimpcoreconfig.h      |   4 +
 app/config/gimprc-blurbs.h       |   9 ++
 app/dialogs/preferences-dialog.c |   8 ++
 app/gimp-update.c                | 189 +++++++++++++++++++++++++++++++++++++++
 app/gimp-update.h                |  28 ++++++
 configure.ac                     |   8 ++
 9 files changed, 303 insertions(+)
---
diff --git a/app/Makefile.am b/app/Makefile.am
index 542a80fcd2..45d948d06b 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -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
 
diff --git a/app/app.c b/app/app.c
index 81e6bdc503..94be6e7c3e 100644
--- a/app/app.c
+++ b/app/app.c
@@ -73,6 +73,7 @@
 #include "gimp-debug.h"
 
 #include "gimp-intl.h"
+#include "gimp-update.h"
 
 
 /*  local prototypes  */
@@ -191,6 +192,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))
@@ -281,6 +283,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
@@ -437,6 +445,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);
diff --git a/app/config/gimpcoreconfig.c b/app/config/gimpcoreconfig.c
index c59b778cd1..276197cab8 100644
--- a/app/config/gimpcoreconfig.c
+++ b/app/config/gimpcoreconfig.c
@@ -127,6 +127,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,
@@ -675,6 +678,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",
@@ -841,6 +865,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);
@@ -1044,6 +1070,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;
@@ -1253,6 +1288,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;
diff --git a/app/config/gimpcoreconfig.h b/app/config/gimpcoreconfig.h
index 747616f62f..4f22f79b2a 100644
--- a/app/config/gimpcoreconfig.h
+++ b/app/config/gimpcoreconfig.h
@@ -102,6 +102,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
diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h
index 4660857e39..4d95f0dabb 100644
--- a/app/config/gimprc-blurbs.h
+++ b/app/config/gimprc-blurbs.h
@@ -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.")
 
diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c
index 4a83f0ac6e..5dc0704b3c 100644
--- a/app/dialogs/preferences-dialog.c
+++ b/app/dialogs/preferences-dialog.c
@@ -1215,6 +1215,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);
 
diff --git a/app/gimp-update.c b/app/gimp-update.c
new file mode 100644
index 0000000000..4789a5a302
--- /dev/null
+++ b/app/gimp-update.c
@@ -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;
+}
diff --git a/app/gimp-update.h b/app/gimp-update.h
new file mode 100644
index 0000000000..43e841102d
--- /dev/null
+++ b/app/gimp-update.h
@@ -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__ */
diff --git a/configure.ac b/configure.ac
index 1e77af5548..5e7eb6c7c6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,6 +62,7 @@ m4_define([gtk_required_version], [2.24.32])
 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([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])
@@ -2279,6 +2280,13 @@ AC_ARG_WITH(pdbgen,
 AM_CONDITIONAL(WITH_PDBGEN, test "x$with_pdbgen" = xyes)
 
 
+#####################
+# 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
 ##################


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]