diff --git a/app/Makefile.am b/app/Makefile.am index 45d948d06bf1b606254b71b9ef65d53b228ed06f..e4c94d3b69abb43686ba70e01b29de54862c186f 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -194,6 +194,7 @@ gimpconsoleldadd = \ $(Z_LIBS) \ $(JSON_C_LIBS) \ $(LIBMYPAINT_LIBS) \ + $(LIBCURL_LIBS) \ $(LIBBACKTRACE_LIBS) \ $(LIBUNWIND_LIBS) \ $(INTLLIBS) \ diff --git a/app/gimp-update.c b/app/gimp-update.c index 31eed04ed741d6a4d27fe9fdb1eb13f3d3445190..bebb228144e7f531487b0b3e3858067efa60029c 100644 --- a/app/gimp-update.c +++ b/app/gimp-update.c @@ -24,6 +24,10 @@ #include #include +#ifdef PLATFORM_OSX +#include +#endif /* PLATFORM_OSX */ + #ifndef GIMP_CONSOLE_COMPILATION #include #endif @@ -51,12 +55,25 @@ static gboolean gimp_version_break (const gchar *v, gint *major, gint *minor, gint *micro); +static void gimp_check_updates_process (gchar *source, + gchar *file_contents, + gsize file_length, + GimpCoreConfig *config); +#ifndef PLATFORM_OSX static void gimp_check_updates_callback (GObject *source, GAsyncResult *result, gpointer user_data); +#endif /* PLATFORM_OSX */ static void gimp_update_about_dialog (GimpCoreConfig *config, const GParamSpec *pspec, gpointer user_data); +#ifdef PLATFORM_OSX +static gsize gimp_curl_write_callback (void *contents, + size_t size, + size_t nmemb, + void *userp); +#endif /* PLATFORM_OSX */ +static gchar * gimp_get_version_url (void); /* Private Functions */ @@ -202,163 +219,174 @@ gimp_version_break (const gchar *v, } static void -gimp_check_updates_callback (GObject *source, - GAsyncResult *result, - gpointer user_data) +gimp_check_updates_process (gchar *source, + gchar *file_contents, + gsize file_length, + GimpCoreConfig *config) { - GimpCoreConfig *config = user_data; - char *file_contents = NULL; - gsize file_length = 0; - GError *error = NULL; + const gchar *platform; + const gchar *last_version = NULL; + const gchar *release_date = NULL; + const gchar *build_comment = NULL; + GError *error = NULL; + gint64 release_timestamp = 0; + gint build_revision = 0; + JsonParser *parser; + JsonPath *path; + JsonNode *result; + JsonArray *versions; + gint i; + + /* For Windows and macOS, let's look if installers are available. + * For other platforms, let's just look for source release. + */ + if (g_strcmp0 (GIMP_BUILD_PLATFORM_FAMILY, "windows") == 0 || + g_strcmp0 (GIMP_BUILD_PLATFORM_FAMILY, "macos") == 0) + platform = GIMP_BUILD_PLATFORM_FAMILY; + else + platform = "source"; - if (g_file_load_contents_finish (G_FILE (source), result, - &file_contents, &file_length, - NULL, &error)) + parser = json_parser_new (); + if (! json_parser_load_from_data (parser, file_contents, file_length, &error)) { - const gchar *platform; - const gchar *last_version = NULL; - const gchar *release_date = NULL; - const gchar *build_comment = NULL; - gint64 release_timestamp = 0; - gint build_revision = 0; - JsonParser *parser; - JsonPath *path; - JsonNode *result; - JsonArray *versions; - gint i; - - /* For Windows and macOS, let's look if installers are available. - * For other platforms, let's just look for source release. - */ - if (g_strcmp0 (GIMP_BUILD_PLATFORM_FAMILY, "windows") == 0 || - g_strcmp0 (GIMP_BUILD_PLATFORM_FAMILY, "macos") == 0) - platform = GIMP_BUILD_PLATFORM_FAMILY; - else - platform = "source"; - - parser = json_parser_new (); - if (! json_parser_load_from_data (parser, file_contents, file_length, &error)) - { - g_printerr ("%s: parsing of %s failed: %s\n", G_STRFUNC, - g_file_get_uri (G_FILE (source)), error->message); - g_free (file_contents); - g_clear_object (&parser); - g_clear_error (&error); + g_printerr ("%s: parsing of %s failed: %s\n", G_STRFUNC, + source, error->message); + g_free (file_contents); + g_clear_object (&parser); + g_clear_error (&error); - return; - } + return; + } - path = json_path_new (); - /* Ideally we could just use Json path filters like this to - * retrieve only released binaries for a given platform: - * g_strdup_printf ("$['STABLE'][?(@.%s)]['version']", platform); - * json_array_get_string_element (result, 0); - * And that would be it! We'd have our last release for given - * platform. - * Unfortunately json-glib does not support filter syntax, so we - * end up looping through releases. - */ - if (! json_path_compile (path, "$['STABLE'][*]", &error)) - { + path = json_path_new (); + /* Ideally we could just use Json path filters like this to + * retrieve only released binaries for a given platform: + * g_strdup_printf ("$['STABLE'][?(@.%s)]['version']", platform); + * json_array_get_string_element (result, 0); + * And that would be it! We'd have our last release for given + * platform. + * Unfortunately json-glib does not support filter syntax, so we + * end up looping through releases. + */ + if (! json_path_compile (path, "$['STABLE'][*]", &error)) + { #ifdef GIMP_UNSTABLE - g_printerr("Path compilation failed: %s\n", error->message); + g_printerr("Path compilation failed: %s\n", error->message); #endif - g_free (file_contents); - g_clear_object (&parser); - g_clear_error (&error); + g_free (file_contents); + g_clear_object (&parser); + g_clear_error (&error); - return; - } - result = json_path_match (path, json_parser_get_root (parser)); - g_return_if_fail (JSON_NODE_HOLDS_ARRAY (result)); + return; + } + result = json_path_match (path, json_parser_get_root (parser)); + g_return_if_fail (JSON_NODE_HOLDS_ARRAY (result)); - versions = json_node_get_array (result); - for (i = 0; i < (gint) json_array_get_length (versions); i++) + versions = json_node_get_array (result); + for (i = 0; i < (gint) json_array_get_length (versions); i++) + { + JsonObject *version; + + /* Note that we don't actually look for the highest version, + * but for the highest version for which a build for your + * platform (and optional build-id) is available. + * + * So we loop through the version list then the build array + * and break at first compatible release, since JSON arrays + * are ordered. + */ + version = json_array_get_object_element (versions, i); + if (json_object_has_member (version, platform)) { - JsonObject *version; - - /* Note that we don't actually look for the highest version, - * but for the highest version for which a build for your - * platform (and optional build-id) is available. - * - * So we loop through the version list then the build array - * and break at first compatible release, since JSON arrays - * are ordered. - */ - version = json_array_get_object_element (versions, i); - if (json_object_has_member (version, platform)) - { - JsonArray *builds; - gint j; + JsonArray *builds; + gint j; - builds = json_object_get_array_member (version, platform); + builds = json_object_get_array_member (version, platform); - for (j = 0; j < (gint) json_array_get_length (builds); j++) - { - const gchar *build_id = NULL; - JsonObject *build; - - build = json_array_get_object_element (builds, j); - if (json_object_has_member (build, "build-id")) - build_id = json_object_get_string_member (build, "build-id"); - if (g_strcmp0 (build_id, GIMP_BUILD_ID) == 0) - { - /* Release date is the build date if any set, - * otherwise the main version release date. - */ - if (json_object_has_member (build, "date")) - release_date = json_object_get_string_member (build, "date"); - else - release_date = json_object_get_string_member (version, "date"); - - /* These are optional data. */ - if (json_object_has_member (build, "revision")) - build_revision = json_object_get_int_member (build, "revision"); - if (json_object_has_member (build, "comment")) - build_comment = json_object_get_string_member (build, "comment"); - break; - } - } + for (j = 0; j < (gint) json_array_get_length (builds); j++) + { + const gchar *build_id = NULL; + JsonObject *build; - if (release_date) + build = json_array_get_object_element (builds, j); + if (json_object_has_member (build, "build-id")) + build_id = json_object_get_string_member (build, "build-id"); + if (g_strcmp0 (build_id, GIMP_BUILD_ID) == 0) { - last_version = json_object_get_string_member (version, "version"); + /* Release date is the build date if any set, + * otherwise the main version release date. + */ + if (json_object_has_member (build, "date")) + release_date = json_object_get_string_member (build, "date"); + else + release_date = json_object_get_string_member (version, "date"); + + /* These are optional data. */ + if (json_object_has_member (build, "revision")) + build_revision = json_object_get_int_member (build, "revision"); + if (json_object_has_member (build, "comment")) + build_comment = json_object_get_string_member (build, "comment"); break; } } + + if (release_date) + { + last_version = json_object_get_string_member (version, "version"); + break; + } } + } - if (last_version && release_date) - { - GDateTime *datetime; - gchar *str; + if (last_version && release_date) + { + GDateTime *datetime; + gchar *str; - str = g_strdup_printf ("%s 00:00:00Z", release_date); - datetime = g_date_time_new_from_iso8601 (str, NULL); - g_free (str); + str = g_strdup_printf ("%s 00:00:00Z", release_date); + datetime = g_date_time_new_from_iso8601 (str, NULL); + g_free (str); - if (datetime) - { - release_timestamp = g_date_time_to_unix (datetime); - g_date_time_unref (datetime); - } - else - { - /* JSON file data bug. */ - g_printerr ("%s: release date for version %s not properly formatted: %s\n", - G_STRFUNC, last_version, release_date); - - last_version = NULL; - release_date = NULL; - build_revision = 0; - build_comment = NULL; - } + if (datetime) + { + release_timestamp = g_date_time_to_unix (datetime); + g_date_time_unref (datetime); + } + else + { + /* JSON file data bug. */ + g_printerr ("%s: release date for version %s not properly formatted: %s\n", + G_STRFUNC, last_version, release_date); + + last_version = NULL; + release_date = NULL; + build_revision = 0; + build_comment = NULL; } - gimp_update_known (config, last_version, release_timestamp, build_revision, build_comment); + } + gimp_update_known (config, last_version, release_timestamp, build_revision, build_comment); - g_object_unref (path); - g_object_unref (parser); - g_free (file_contents); + g_object_unref (path); + g_object_unref (parser); + g_free (file_contents); +} + +#ifndef PLATFORM_OSX +static void +gimp_check_updates_callback (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GimpCoreConfig *config = user_data; + char *file_contents = NULL; + gsize file_length = 0; + GError *error = NULL; + + if (g_file_load_contents_finish (G_FILE (source), result, + &file_contents, &file_length, + NULL, &error)) + { + gimp_check_updates_process (g_file_get_uri (G_FILE (source)), file_contents, file_length, config); } else { @@ -367,6 +395,7 @@ gimp_check_updates_callback (GObject *source, g_clear_error (&error); } } +#endif /* PLATFORM_OSX */ static void gimp_update_about_dialog (GimpCoreConfig *config, @@ -389,6 +418,46 @@ gimp_update_about_dialog (GimpCoreConfig *config, } } +#ifdef PLATFORM_OSX +struct MemoryStruct { + gchar *memory; + gsize size; +}; + +static gsize +gimp_curl_write_callback(void *contents, size_t size, size_t nmemb, void *userp) +{ + gsize realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + gchar *ptr = g_realloc(mem->memory, mem->size + realsize + 1); + if(!ptr) { + g_printerr ("%s: not enough memory (realloc returned NULL)\n", G_STRFUNC); + return 0; + } + + mem->memory = ptr; + memcpy (&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} +#endif /* PLATFORM_OSX */ + +static gchar * +gimp_get_version_url () +{ +#ifdef GIMP_UNSTABLE + if (g_getenv ("GIMP_DEV_VERSIONS_JSON")) + return g_getenv ("GIMP_DEV_VERSIONS_JSON"); + else + return "https://testing.gimp.org/gimp_versions.json"; +#else + return "https://www.gimp.org/gimp_versions.json"; +#endif +} + /* Public Functions */ /* @@ -451,18 +520,43 @@ gimp_update_auto_check (GimpCoreConfig *config) void gimp_update_check (GimpCoreConfig *config) { - GFile *gimp_versions; +#ifdef PLATFORM_OSX + CURL *curl_handle; + CURLcode res; + struct MemoryStruct chunk; + gchar *gimp_versions; -#ifdef GIMP_UNSTABLE - if (g_getenv ("GIMP_DEV_VERSIONS_JSON")) - gimp_versions = g_file_new_for_path (g_getenv ("GIMP_DEV_VERSIONS_JSON")); + gimp_versions = gimp_get_version_url (); + + chunk.memory = g_malloc (1); /* will be g_realloced */ + chunk.size = 0; + + curl_global_init (CURL_GLOBAL_ALL); + curl_handle = curl_easy_init (); + + curl_easy_setopt (curl_handle, CURLOPT_URL, gimp_versions); + curl_easy_setopt (curl_handle, CURLOPT_WRITEFUNCTION, gimp_curl_write_callback); + curl_easy_setopt (curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + curl_easy_setopt (curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + res = curl_easy_perform (curl_handle); + + if (res == CURLE_OK) + gimp_check_updates_process (gimp_versions, chunk.memory, chunk.size, config); else - gimp_versions = g_file_new_for_uri ("https://testing.gimp.org/gimp_versions.json"); + g_printerr ("%s: curl_easy_perform() failed: %s\n", G_STRFUNC, + curl_easy_strerror (res)); + + curl_easy_cleanup (curl_handle); + curl_global_cleanup (); #else - gimp_versions = g_file_new_for_uri ("https://www.gimp.org/gimp_versions.json"); -#endif + GFile *gimp_versions; + + gimp_versions = g_file_new_for_path (gimp_get_version_url ()); + g_file_load_contents_async (gimp_versions, NULL, gimp_check_updates_callback, config); g_object_unref (gimp_versions); +#endif /* PLATFORM_OSX */ } /* diff --git a/configure.ac b/configure.ac index 01c312526ba3e398e4cbb0d93a8b1b887b55d85a..02c99e9aa00bc5325914ae3bb24570b649a03477 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,7 @@ m4_define([libheif_required_version], [1.3.2]) m4_define([libjxl_required_version], [0.6.1]) m4_define([liblzma_required_version], [5.0.0]) m4_define([libmypaint_required_version], [1.3.0]) +m4_define([libcurl_required_version], [7.25.0]) m4_define([libpng_required_version], [1.6.25]) m4_define([libunwind_required_version], [1.1.0]) m4_define([openexr_required_version], [1.6.1]) @@ -159,6 +160,7 @@ LCMS_REQUIRED_VERSION=lcms_required_version LIBPNG_REQUIRED_VERSION=libpng_required_version LIBLZMA_REQUIRED_VERSION=liblzma_required_version LIBMYPAINT_REQUIRED_VERSION=libmypaint_required_version +LIBCURL_REQUIRED_VERSION=libcurl_required_version PANGOCAIRO_REQUIRED_VERSION=pangocairo_required_version BABL_REQUIRED_VERSION=babl_required_version FONTCONFIG_REQUIRED_VERSION=fontconfig_required_version @@ -192,6 +194,7 @@ AC_SUBST(LCMS_REQUIRED_VERSION) AC_SUBST(LIBPNG_REQUIRED_VERSION) AC_SUBST(LIBLZMA_REQUIRED_VERSION) AC_SUBST(LIBMYPAINT_REQUIRED_VERSION) +AC_SUBST(LIBCURL_REQUIRED_VERSION) AC_SUBST(PANGOCAIRO_REQUIRED_VERSION) AC_SUBST(BABL_REQUIRED_VERSION) AC_SUBST(FONTCONFIG_REQUIRED_VERSION) @@ -1993,6 +1996,20 @@ fi AC_SUBST(mypaint_brushes_dir) +###################### +# Check for libcurl +###################### + +if test "x$platform_osx" = "xyes"; then + PKG_CHECK_MODULES(LIBCURL, libcurl >= 7.0.0,, + [ + PKG_CHECK_MODULES(LIBCURL, libcurl >= libcurl_required_version,, + [add_deps_error([libcurl >= libcurl_required_version])]) + warning_libcurl=" + WARNING: libcurl lower than version 7.25.0 is not advised." + ]) +fi + ################## # Check for webkit ##################