1 /*
2  * This file is part of Siril, an astronomy image processor.
3  * Copyright (C) 2005-2011 Francois Meyer (dulle at free.fr)
4  * Copyright (C) 2012-2021 team free-astro (see more in AUTHORS file)
5  * Reference site is https://free-astro.org/index.php/Siril
6  *
7  * Siril is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Siril is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Siril. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <json-glib/json-glib.h>
25 
26 #ifdef HAVE_LIBCURL
27 #include <curl/curl.h>
28 #endif
29 
30 #include <string.h>
31 
32 #include "core/siril.h"
33 #include "core/proto.h"
34 #include "core/processing.h"
35 #include "gui/utils.h"
36 #include "gui/message_dialog.h"
37 #include "gui/progress_and_log.h"
38 #include "core/siril_update.h"
39 
40 
41 #define DOMAIN "https://siril.org/"
42 #define SIRIL_VERSIONS DOMAIN"siril_versions.json"
43 #define SIRIL_DOWNLOAD DOMAIN"download"
44 #define GITLAB_URL "https://gitlab.com/free-astro/siril/raw"
45 
46 static gchar *get_changelog(gint x, gint y, gint z, gint p);
47 
48 
49 // taken from gimp
siril_update_get_highest(JsonParser * parser,gchar ** highest_version,gint64 * release_timestamp,gint * build_revision,gchar ** build_comment)50 static gboolean siril_update_get_highest(JsonParser *parser,
51 		gchar **highest_version, gint64 *release_timestamp,
52 		gint *build_revision, gchar **build_comment) {
53 	JsonPath *path;
54 	JsonNode *result;
55 	JsonArray *versions;
56 	const gchar *platform;
57 	const gchar *path_str;
58 	const gchar *release_date = NULL;
59 	GError *error = NULL;
60 	gint i;
61 
62 	g_return_val_if_fail(highest_version != NULL, FALSE);
63 	g_return_val_if_fail(release_timestamp != NULL, FALSE);
64 	g_return_val_if_fail(build_revision != NULL, FALSE);
65 	g_return_val_if_fail(build_comment != NULL, FALSE);
66 
67 	*highest_version = NULL;
68 	*release_timestamp = 0;
69 	*build_revision = 0;
70 	*build_comment = NULL;
71 
72 	path_str = "$['RELEASE'][*]";
73 
74 	/* For Windows and macOS, let's look if installers are available.
75 	 * For other platforms, let's just look for source release.
76 	 */
77 	if (g_strcmp0(SIRIL_BUILD_PLATFORM_FAMILY, "windows") == 0
78 			|| g_strcmp0(SIRIL_BUILD_PLATFORM_FAMILY, "macos") == 0)
79 		platform = SIRIL_BUILD_PLATFORM_FAMILY;
80 	else
81 		platform = "source";
82 
83 	path = json_path_new();
84 	/* Ideally we could just use Json path filters like this to
85 	 * retrieve only released binaries for a given platform:
86 	 * g_strdup_printf ("$['STABLE'][?(@.%s)]['version']", platform);
87 	 * json_array_get_string_element (result, 0);
88 	 * And that would be it! We'd have our last release for given
89 	 * platform.
90 	 * Unfortunately json-glib does not support filter syntax, so we
91 	 * end up looping through releases.
92 	 */
93 	if (!json_path_compile(path, path_str, &error)) {
94 		g_warning("%s: path compilation failed: %s\n", G_STRFUNC,
95 				error->message);
96 		g_clear_error(&error);
97 		g_object_unref(path);
98 
99 		return FALSE;
100 	}
101 	result = json_path_match(path, json_parser_get_root(parser));
102 	if (!JSON_NODE_HOLDS_ARRAY(result)) {
103 		g_printerr("%s: match for \"%s\" is not a JSON array.\n",
104 		G_STRFUNC, path_str);
105 		g_object_unref(path);
106 
107 		return FALSE;
108 	}
109 
110 	versions = json_node_get_array(result);
111 	for (i = 0; i < (gint) json_array_get_length(versions); i++) {
112 		JsonObject *version;
113 
114 		/* Note that we don't actually look for the highest version,
115 		 * but for the highest version for which a build for your
116 		 * platform (and optional build-id) is available.
117 		 *
118 		 * So we loop through the version list then the build array
119 		 * and break at first compatible release, since JSON arrays
120 		 * are ordered.
121 		 */
122 		version = json_array_get_object_element(versions, i);
123 		if (json_object_has_member(version, platform)) {
124 			JsonArray *builds;
125 			gint j;
126 
127 			builds = json_object_get_array_member(version, platform);
128 
129 			for (j = 0; j < (gint) json_array_get_length(builds); j++) {
130 				const gchar *build_id = NULL;
131 				JsonObject *build;
132 
133 				build = json_array_get_object_element(builds, j);
134 				if (json_object_has_member(build, "build-id"))
135 					build_id = json_object_get_string_member (build, "build-id");
136 				if (g_strcmp0(build_id, "org.free_astro.siril") == 0
137 						|| g_strcmp0(platform, "source") == 0) {
138 					/* Release date is the build date if any set,
139 					 * otherwise the main version release date.
140 					 */
141 					if (json_object_has_member(build, "date"))
142 						release_date = json_object_get_string_member(build, "date");
143 					else
144 						release_date = json_object_get_string_member(version, "date");
145 
146 					/* These are optional data. */
147 					if (json_object_has_member(build, "revision"))
148 						*build_revision = json_object_get_int_member(build, "revision");
149 					if (json_object_has_member(build, "comment"))
150 						*build_comment = g_strdup(json_object_get_string_member(build, "comment"));
151 					break;
152 				}
153 			}
154 
155 			if (release_date) {
156 				*highest_version = g_strdup(json_object_get_string_member(version, "version"));
157 				break;
158 			}
159 		}
160 	}
161 
162 	if (*highest_version && *release_date) {
163 		GDateTime *datetime;
164 		gchar *str;
165 
166 		str = g_strdup_printf("%s 00:00:00Z", release_date);
167 		datetime = g_date_time_new_from_iso8601(str, NULL);
168 		g_free(str);
169 
170 		if (datetime) {
171 			*release_timestamp = g_date_time_to_unix(datetime);
172 			g_date_time_unref(datetime);
173 		} else {
174 			/* JSON file data bug. */
175 			g_printerr("%s: release date for version %s not properly formatted: %s\n",
176 					G_STRFUNC, *highest_version, release_date);
177 
178 			g_clear_pointer(highest_version, g_free);
179 			g_clear_pointer(build_comment, g_free);
180 			*build_revision = 0;
181 		}
182 	}
183 
184 	json_node_unref(result);
185 	g_object_unref(path);
186 
187 	return (*highest_version != NULL);
188 }
189 
190 /**
191  * Check if the version is a patched version.
192  * patched version are named like that x.y.z.patch where patch only contains digits.
193  * if patch contains alpha char it is because that's an alpha or beta version. Not a patched one.
194  * @param version version to be tested
195  * @return 0 if the version is not patched. The version of the patch is returned otherwise.
196  */
check_for_patch(gchar * version)197 static guint check_for_patch(gchar *version) {
198 	guint i = 0;
199 
200 	while (version[i]) {
201 		if (g_ascii_isalpha(version[i])) return 0;
202 		i++;
203 	}
204 	return (g_ascii_strtoull(version, NULL, 10));
205 }
206 
get_current_version_number()207 static version_number get_current_version_number() {
208 	gchar **fullVersionNumber;
209 	version_number version;
210 
211 	fullVersionNumber = g_strsplit_set(PACKAGE_VERSION, ".-", -1);
212 	version.major_version = g_ascii_strtoull(fullVersionNumber[0], NULL, 10);
213 	version.minor_version = g_ascii_strtoull(fullVersionNumber[1], NULL, 10);
214 	version.micro_version = g_ascii_strtoull(fullVersionNumber[2], NULL, 10);
215 	version.patched_version = (fullVersionNumber[3] == NULL) ? 0 : check_for_patch(fullVersionNumber[3]);
216 
217 	g_strfreev(fullVersionNumber);
218 
219 	return version;
220 }
221 
get_last_version_number(gchar * version_str)222 static version_number get_last_version_number(gchar *version_str) {
223 	gchar **v;
224 	version_number version = { 0 };
225 
226 	v = g_strsplit_set(version_str, ".-", -1);
227 
228 	if (v[0])
229 		version.major_version = g_ascii_strtoull(v[0], NULL, 10);
230 	if (v[0] && v[1])
231 		version.minor_version = g_ascii_strtoull(v[1], NULL, 10);
232 	if (v[0] && v[1] && v[2])
233 		version.micro_version = g_ascii_strtoull(v[2], NULL, 10);
234 	if (v[0] && v[1] && v[2] && v[3])
235 		version.patched_version = g_ascii_strtoull(v[3], NULL, 10);
236 
237 	g_strfreev(v);
238 	return version;
239 }
240 
241 /**
242  * This function compare x1.y1.z1.patch1 vs x2.y2.z2.patch2
243  * @param v1 First version number to be tested
244  * @param v2 Second version number to be tested
245  * @return -1 if v1 < v2, 1 if v1 > v2 and 0 if v1 is equal to v2
246  */
compare_version(version_number v1,version_number v2)247 static int compare_version(version_number v1, version_number v2) {
248 	if (v1.major_version < v2.major_version)
249 		return -1;
250 	else if (v1.major_version > v2.major_version)
251 		return 1;
252 	else {
253 		if (v1.minor_version < v2.minor_version)
254 			return -1;
255 		else if (v1.minor_version > v2.minor_version)
256 			return 1;
257 		else {
258 			if (v1.micro_version < v2.micro_version)
259 				return -1;
260 			else if (v1.micro_version > v2.micro_version)
261 				return 1;
262 			else {
263 				if (v1.patched_version < v2.patched_version)
264 					return -1;
265 				else if (v1.patched_version > v2.patched_version)
266 					return 1;
267 			}
268 		}
269 	}
270 	return 0;
271 }
272 
parse_changelog(gchar * changelog)273 static gchar *parse_changelog(gchar *changelog) {
274 	gchar **token;
275 	GString *strResult;
276 	guint nargs, i;
277 
278 	token = g_strsplit(changelog, "\n", -1);
279 	nargs = g_strv_length(token);
280 
281 	strResult = g_string_new(token[0]);
282 	strResult = g_string_append(strResult, "\n\n");
283 	/* we start at line 3 */
284 	i = 3;
285 	while (i < nargs && token[i][0] != '\0') {
286 		strResult = g_string_append(strResult, token[i]);
287 		strResult = g_string_append(strResult, "\n");
288 		i++;
289 	}
290 	g_strfreev(token);
291 	return g_string_free(strResult, FALSE);
292 }
293 
check_version(gchar * version,gboolean * verbose,gchar ** data)294 static gchar *check_version(gchar *version, gboolean *verbose, gchar **data) {
295 	gchar *changelog = NULL;
296 	gchar *msg = NULL;
297 
298 	version_number last_version_available = get_last_version_number(version);
299 	version_number current_version = get_current_version_number();
300 	guint x = last_version_available.major_version;
301 	guint y = last_version_available.minor_version;
302 	guint z = last_version_available.micro_version;
303 	guint patch = last_version_available.patched_version;
304 	if (x == 0 && y == 0 && z == 0) {
305 		if (*verbose)
306 			msg = siril_log_message(_("No update check: cannot fetch version file\n"));
307 	} else {
308 		if (compare_version(current_version, last_version_available) < 0) {
309 			msg = siril_log_message(_("New version is available. You can download it at "
310 							"<a href=\"%s\">%s</a>\n"),
311 					SIRIL_DOWNLOAD, SIRIL_DOWNLOAD);
312 			changelog = get_changelog(x, y, z, patch);
313 			if (changelog) {
314 				*data = parse_changelog(changelog);
315 				/* force the verbose variable */
316 				*verbose = TRUE;
317 			}
318 		} else if (compare_version(current_version, last_version_available)	> 0) {
319 			if (*verbose)
320 				msg = siril_log_message(_("No update check: this is a development version\n"));
321 		} else {
322 			if (*verbose)
323 				msg = siril_log_message(_("Siril is up to date\n"));
324 		}
325 		g_free(changelog);
326 	}
327 	return msg;
328 }
329 
330 // TODO: For now, to fix this bug https://gitlab.com/free-astro/siril/-/issues/604() we need to use GIO for Windows
331 #if defined HAVE_LIBCURL && !defined _WIN32
332 
333 struct _update_data {
334 	gchar *url;
335 	long code;
336 	gchar *content;
337 	gboolean verbose;
338 };
339 
340 
341 static const int DEFAULT_FETCH_RETRIES = 5;
342 static CURL *curl;
343 
344 struct ucontent {
345 	gchar *data;
346 	size_t len;
347 };
348 
cbk_curl(void * buffer,size_t size,size_t nmemb,void * userp)349 static size_t cbk_curl(void *buffer, size_t size, size_t nmemb, void *userp) {
350 	size_t realsize = size * nmemb;
351 	struct ucontent *mem = (struct ucontent *) userp;
352 
353 	mem->data = g_try_realloc(mem->data, mem->len + realsize + 1);
354 
355 	memcpy(&(mem->data[mem->len]), buffer, realsize);
356 	mem->len += realsize;
357 	mem->data[mem->len] = 0;
358 
359 	return realsize;
360 }
361 
init()362 static void init() {
363 	if (!curl) {
364 		g_fprintf(stdout, "initializing CURL\n");
365 		curl_global_init(CURL_GLOBAL_ALL);
366 		curl = curl_easy_init();
367 	}
368 
369 	if (!curl)
370 		exit(EXIT_FAILURE);
371 }
372 
http_cleanup()373 static void http_cleanup() {
374 	curl_easy_cleanup(curl);
375 	curl_global_cleanup();
376 	curl = NULL;
377 }
378 
get_changelog(gint x,gint y,gint z,gint p)379 static gchar *get_changelog(gint x, gint y, gint z, gint p) {
380 	struct ucontent *changelog;
381 	gchar *result = NULL;
382 	gchar str[20];
383 	gchar *changelog_url;
384 	long code;
385 
386 	GString *url = g_string_new(GITLAB_URL);
387 
388 	changelog = g_try_malloc(sizeof(struct ucontent));
389 	if (changelog == NULL) {
390 		PRINT_ALLOC_ERR;
391 		return NULL;
392 	}
393 
394 	changelog->data = g_malloc(1);
395 	changelog->data[0] = '\0';
396 	changelog->len = 0;
397 
398 	if (p != 0) {
399 		g_snprintf(str, sizeof(str), "/%d.%d.%d.%d/", x, y, z, p);
400 	} else {
401 		g_snprintf(str, sizeof(str), "/%d.%d.%d/", x, y, z);
402 	}
403 	url = g_string_append(url, str);
404 	url = g_string_append(url, "ChangeLog");
405 
406 	changelog_url = g_string_free(url, FALSE);
407 	curl_easy_setopt(curl, CURLOPT_URL, changelog_url);
408 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
409 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, changelog);
410 	if (curl_easy_perform(curl) == CURLE_OK) {
411 		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
412 		if (code == 200) {
413 			result = g_strdup(changelog->data);
414 		}
415 	}
416 	if (!result)
417 		g_free(changelog->data);
418 	g_free(changelog);
419 	g_free(changelog_url);
420 
421 	return result;
422 }
423 
check_update_version(struct _update_data * args)424 static gchar *check_update_version(struct _update_data *args) {
425 	JsonParser *parser;
426 	gchar *last_version = NULL;
427 	gchar *build_comment = NULL;
428 	gint64 release_timestamp = 0;
429 	gint build_revision = 0;
430 	GError *error = NULL;
431 	gchar *msg = NULL;
432 	gchar *data = NULL;
433 	GtkMessageType message_type = GTK_MESSAGE_ERROR;
434 
435 	parser = json_parser_new();
436 	if (!json_parser_load_from_data(parser, args->content, -1, &error)) {
437 		g_printerr("%s: parsing of %s failed: %s\n", G_STRFUNC,
438 				args->url, error->message);
439 		g_clear_object(&parser);
440 		g_clear_error(&error);
441 
442 		return NULL;
443 	}
444 
445 	siril_update_get_highest(parser, &last_version, &release_timestamp,	&build_revision, &build_comment);
446 
447 	if (last_version) {
448 		g_fprintf(stdout, "Last available version: %s\n", last_version);
449 
450 		msg = check_version(last_version, &(args->verbose), &data);
451 		message_type = GTK_MESSAGE_INFO;
452 	} else {
453 		msg = siril_log_message(_("Cannot fetch version file\n"));
454 	}
455 
456 	if (args->verbose) {
457 		set_cursor_waiting(FALSE);
458 		if (msg) {
459 			siril_data_dialog(message_type, _("Software Update"), msg, data);
460 		}
461 	}
462 
463 	g_clear_pointer(&last_version, g_free);
464 	g_clear_pointer(&build_comment, g_free);
465 	g_object_unref(parser);
466 
467 	return msg;
468 }
469 
end_update_idle(gpointer p)470 static gboolean end_update_idle(gpointer p) {
471 	char *msg = NULL;
472 	struct _update_data *args = (struct _update_data *) p;
473 
474 	if (args->content == NULL) {
475 		switch(args->code) {
476 		case 0:
477 			msg = siril_log_message(_("Unable to check updates! "
478 					"Please Check your network connection\n"));
479 			break;
480 		default:
481 			msg = siril_log_message(_("Unable to check updates! Error: %ld\n"),
482 					args->code);
483 		}
484 	} else {
485 		msg = check_update_version(args);
486 	}
487 
488 	/* free data */
489 	g_free(args->content);
490 	free(args);
491 	http_cleanup();
492 	set_progress_bar_data(PROGRESS_TEXT_RESET, PROGRESS_RESET);
493 	stop_processing_thread();
494 	return FALSE;
495 }
496 
fetch_url(gpointer p)497 static gpointer fetch_url(gpointer p) {
498 	struct ucontent *content;
499 	gchar *result;
500 	long code = -1L;
501 	int retries;
502 	unsigned int s;
503 	struct _update_data *args = (struct _update_data *) p;
504 
505 	content = g_try_malloc(sizeof(struct ucontent));
506 	if (content == NULL) {
507 		PRINT_ALLOC_ERR;
508 		return NULL;
509 	}
510 
511 	g_fprintf(stdout, "fetch_url(): %s\n", args->url);
512 
513 	init();
514 	set_progress_bar_data(NULL, 0.1);
515 
516 	result = NULL;
517 
518 	retries = DEFAULT_FETCH_RETRIES;
519 
520 	retrieve: content->data = g_malloc(1);
521 	content->data[0] = '\0';
522 	content->len = 0;
523 
524 	curl_easy_setopt(curl, CURLOPT_URL, args->url);
525 	curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
526 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cbk_curl);
527 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, content);
528 	curl_easy_setopt(curl, CURLOPT_USERAGENT, "siril/0.0");
529 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
530 
531 	CURLcode retval = curl_easy_perform(curl);
532 	if (retval == CURLE_OK) {
533 		if (retries == DEFAULT_FETCH_RETRIES) set_progress_bar_data(NULL, 0.4);
534 		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
535 		if (retries == DEFAULT_FETCH_RETRIES) set_progress_bar_data(NULL, 0.6);
536 
537 		switch (code) {
538 		case 200:
539 			result = content->data;
540 			break;
541 		case 500:
542 		case 502:
543 		case 503:
544 		case 504:
545 			g_fprintf(stderr, "Fetch failed with code %ld for URL %s\n", code,
546 					args->url);
547 
548 			if (retries && get_thread_run()) {
549 				double progress = (DEFAULT_FETCH_RETRIES - retries) / (double) DEFAULT_FETCH_RETRIES;
550 				progress *= 0.4;
551 				progress += 0.6;
552 				s = 2 * (DEFAULT_FETCH_RETRIES - retries) + 2;
553 				char *msg = siril_log_message(_("Error: %ld. Wait %us before retry\n"), code, s);
554 				msg[strlen(msg) - 1] = 0; /* we remove '\n' at the end */
555 				set_progress_bar_data(msg, progress);
556 				g_usleep(s * 1E6);
557 
558 				g_free(content->data);
559 				retries--;
560 				goto retrieve;
561 			}
562 
563 			break;
564 		default:
565 			g_fprintf(stderr, "Fetch failed with code %ld for URL %s\n", code,
566 					args->url);
567 		}
568 		g_fprintf(stderr, "Fetch succeeded with code %ld for URL %s\n", code,
569 				args->url);
570 		args->code = code;
571 	} else {
572 		siril_log_color_message(_("Cannot retrieve information from the update URL. Error: [%ld]\n"), "red", retval);
573 	}
574 	set_progress_bar_data(NULL, PROGRESS_DONE);
575 
576 	if (!result)
577 		g_free(content->data);
578 	g_free(content);
579 
580 	args->content = result;
581 
582 	gdk_threads_add_idle(end_update_idle, args);
583 	return NULL;
584 }
585 
siril_check_updates(gboolean verbose)586 void siril_check_updates(gboolean verbose) {
587 	struct _update_data *args;
588 
589 	args = malloc(sizeof(struct _update_data));
590 	args->url = SIRIL_VERSIONS;
591 	args->code = 0L;
592 	args->content = NULL;
593 	args->verbose = verbose;
594 
595 	set_progress_bar_data(_("Looking for updates..."), PROGRESS_NONE);
596 	if (args->verbose)
597 		set_cursor_waiting(TRUE);
598 	start_in_new_thread(fetch_url, args);
599 }
600 
601 #else
602 
get_changelog(gint x,gint y,gint z,gint p)603 static gchar *get_changelog(gint x, gint y, gint z, gint p) {
604 	GError *error = NULL;
605 	gchar *result = NULL;
606 	gchar *str;
607 
608 	if (p != 0) {
609 		str = g_strdup_printf("/%d.%d.%d.%d/", x, y, z, p);
610 	} else {
611 		str = g_strdup_printf("/%d.%d.%d/", x, y, z);
612 	}
613 	GString *url = g_string_new(GITLAB_URL);
614 	url = g_string_append(url, str);
615 	url = g_string_append(url, "ChangeLog");
616 
617 	gchar *changelog_url = g_string_free(url, FALSE);
618 	GFile *file = g_file_new_for_uri(changelog_url);
619 
620 	if (!g_file_load_contents(file, NULL, &result, NULL, NULL, &error)) {
621 		siril_log_message(_("Error loading url: %s: %s\n"), changelog_url, error->message);
622 		g_clear_error(&error);
623 	}
624 
625 	g_free(changelog_url);
626 	g_free(str);
627 	g_object_unref(file);
628 
629 	return result;
630 }
631 
siril_check_updates_callback(GObject * source,GAsyncResult * result,gpointer user_data)632 static void siril_check_updates_callback(GObject *source, GAsyncResult *result,
633 		gpointer user_data) {
634 	gboolean verbose = GPOINTER_TO_INT(user_data);
635 	char *file_contents = NULL;
636 	gsize file_length = 0;
637 	GError *error = NULL;
638 	gchar *msg = NULL;
639 	gchar *data = NULL;
640 	GtkMessageType message_type = GTK_MESSAGE_ERROR;
641 
642 	if (g_file_load_contents_finish(G_FILE(source), result, &file_contents,
643 			&file_length,
644 			NULL, &error)) {
645 		JsonParser *parser;
646 		gchar *last_version = NULL;
647 		gchar *build_comment = NULL;
648 		gint64 release_timestamp = 0;
649 		gint build_revision = 0;
650 
651 		parser = json_parser_new();
652 		if (!json_parser_load_from_data(parser, file_contents, file_length,
653 				&error)) {
654 			g_printerr("%s: parsing of %s failed: %s\n", G_STRFUNC,
655 					g_file_get_uri(G_FILE(source)), error->message);
656 			g_free(file_contents);
657 			g_clear_object(&parser);
658 			g_clear_error(&error);
659 
660 			return;
661 		}
662 
663 		siril_update_get_highest(parser, &last_version, &release_timestamp,	&build_revision, &build_comment);
664 
665 		if (last_version) {
666 			g_fprintf(stdout, "Last available version: %s\n", last_version);
667 
668 			msg = check_version(last_version, &verbose, &data);
669 			message_type = GTK_MESSAGE_INFO;
670 		} else {
671 			msg = siril_log_message(_("Cannot fetch version file\n"));
672 		}
673 
674 		g_clear_pointer(&last_version, g_free);
675 		g_clear_pointer(&build_comment, g_free);
676 		g_object_unref(parser);
677 		g_free(file_contents);
678 	} else {
679 		g_printerr("%s: loading of %s failed: %s\n", G_STRFUNC,
680 				g_file_get_uri(G_FILE(source)), error->message);
681 		g_clear_error(&error);
682 		msg = siril_log_message(_("Cannot fetch version file\n"));
683 	}
684 	if (verbose) {
685 		set_cursor_waiting(FALSE);
686 		if (msg) {
687 			siril_data_dialog(message_type, _("Software Update"), msg, data);
688 		}
689 	}
690 	/* free data */
691 	g_free(data);
692 }
693 
siril_check_updates(gboolean verbose)694 void siril_check_updates(gboolean verbose) {
695 	GFile *siril_versions;
696 
697 	siril_versions = g_file_new_for_uri(SIRIL_VERSIONS);
698 
699 	g_file_load_contents_async(siril_versions, NULL, siril_check_updates_callback, GINT_TO_POINTER(verbose));
700 	g_object_unref(siril_versions);
701 }
702 #endif
703