diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h index 7a63defb..617e5dc8 100644 --- a/common/flatpak-utils-private.h +++ b/common/flatpak-utils-private.h @@ -241,6 +241,14 @@ gboolean flatpak_remove_dangling_symlinks (GFile *dir, GCancellable *cancellable, GError **error); +gboolean flatpak_utils_ascii_string_to_unsigned (const gchar *str, + guint base, + guint64 min, + guint64 max, + guint64 *out_num, + GError **error); + + #if !GLIB_CHECK_VERSION (2, 40, 0) static inline gboolean g_key_file_save_to_file (GKeyFile *key_file, diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c index 9c31853d..521c325e 100644 --- a/common/flatpak-utils.c +++ b/common/flatpak-utils.c @@ -5964,3 +5964,88 @@ flatpak_check_required_version (const char *ref, return TRUE; } + +static gboolean +str_has_sign (const gchar *str) +{ + return str[0] == '-' || str[0] == '+'; +} + +static gboolean +str_has_hex_prefix (const gchar *str) +{ + return str[0] == '0' && g_ascii_tolower (str[1]) == 'x'; +} + +/* Copied from glib-2.54.0 to avoid the Glib's version bump. + * Function name in glib: g_ascii_string_to_unsigned + * If this is being dropped(migration to g_ascii_string_to_unsigned) + * make sure to remove str_has_hex_prefix and str_has_sign helpers too. + */ +gboolean +flatpak_utils_ascii_string_to_unsigned (const gchar *str, + guint base, + guint64 min, + guint64 max, + guint64 *out_num, + GError **error) +{ + guint64 number; + const gchar *end_ptr = NULL; + gint saved_errno = 0; + + g_return_val_if_fail (str != NULL, FALSE); + g_return_val_if_fail (base >= 2 && base <= 36, FALSE); + g_return_val_if_fail (min <= max, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (str[0] == '\0') + { + g_set_error_literal (error, + G_NUMBER_PARSER_ERROR, G_NUMBER_PARSER_ERROR_INVALID, + _("Empty string is not a number")); + return FALSE; + } + + errno = 0; + number = g_ascii_strtoull (str, (gchar **)&end_ptr, base); + saved_errno = errno; + + if (/* We do not allow leading whitespace, but g_ascii_strtoull + * accepts it and just skips it, so we need to check for it + * ourselves. + */ + g_ascii_isspace (str[0]) || + /* Unsigned number should have no sign. + */ + str_has_sign (str) || + /* We don't support hexadecimal numbers prefixed with 0x or + * 0X. + */ + (base == 16 && str_has_hex_prefix (str)) || + (saved_errno != 0 && saved_errno != ERANGE) || + end_ptr == NULL || + *end_ptr != '\0') + { + g_set_error (error, + G_NUMBER_PARSER_ERROR, G_NUMBER_PARSER_ERROR_INVALID, + _("ā€œ%sā€ is not an unsigned number"), str); + return FALSE; + } + if (saved_errno == ERANGE || number < min || number > max) + { + gchar *min_str = g_strdup_printf ("%" G_GUINT64_FORMAT, min); + gchar *max_str = g_strdup_printf ("%" G_GUINT64_FORMAT, max); + + g_set_error (error, + G_NUMBER_PARSER_ERROR, G_NUMBER_PARSER_ERROR_OUT_OF_BOUNDS, + _("Number ā€œ%sā€ is out of bounds [%s, %s]"), + str, min_str, max_str); + g_free (min_str); + g_free (max_str); + return FALSE; + } + if (out_num != NULL) + *out_num = number; + return TRUE; +}