src/libostree/ostree-cmdprivate.c \
src/libostree/ostree-core-private.h \
src/libostree/ostree-core.c \
+ src/libostree/ostree-date-utils.c \
+ src/libostree/ostree-date-utils-private.h \
src/libostree/ostree-dummy-enumtypes.c \
src/libostree/ostree-checksum-input-stream.c \
src/libostree/ostree-checksum-input-stream.h \
_installed_or_uninstalled_test_programs = tests/test-varint tests/test-ot-unix-utils tests/test-bsdiff tests/test-mutable-tree \
tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \
tests/test-checksum tests/test-lzma tests/test-rollsum \
- tests/test-basic-c tests/test-sysroot-c tests/test-pull-c tests/test-repo tests/test-include-ostree-h tests/test-kargs
+ tests/test-basic-c tests/test-sysroot-c tests/test-pull-c tests/test-repo tests/test-include-ostree-h tests/test-kargs \
+ tests/test-rfc2616-dates
if USE_GPGME
_installed_or_uninstalled_test_programs += \
tests_test_lzma_CFLAGS = $(TESTS_CFLAGS) $(OT_DEP_LZMA_CFLAGS)
tests_test_lzma_LDADD = $(TESTS_LDADD) $(OT_DEP_LZMA_LIBS)
+tests_test_rfc2616_dates_SOURCES = \
+ src/libostree/ostree-date-utils.c \
+ tests/test-rfc2616-dates.c
+tests_test_rfc2616_dates_CFLAGS = $(TESTS_CFLAGS)
+tests_test_rfc2616_dates_LDADD = $(TESTS_LDADD)
+
if USE_GPGME
tests_test_gpg_verify_result_SOURCES = \
src/libostree/ostree-gpg-verify-result-private.h \
--- /dev/null
+/*
+ * Copyright © 2020 Endless OS Foundation LLC
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * - Philip Withnall <pwithnall@endlessos.org>
+ */
+
+#pragma once
+
+#ifndef __GI_SCANNER__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+GDateTime *_ostree_parse_rfc2616_date_time (const char *buf,
+ size_t len);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2020 Endless OS Foundation LLC
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * - Philip Withnall <pwithnall@endlessos.org>
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+
+#include "ostree-date-utils-private.h"
+
+/* @buf must already be known to be long enough */
+static gboolean
+parse_uint (const char *buf,
+ guint n_digits,
+ guint min,
+ guint max,
+ guint *out)
+{
+ guint64 number;
+ const char *end_ptr = NULL;
+ gint saved_errno = 0;
+
+ g_return_val_if_fail (n_digits == 2 || n_digits == 4, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ errno = 0;
+ number = g_ascii_strtoull (buf, (gchar **)&end_ptr, 10);
+ saved_errno = errno;
+
+ if (!g_ascii_isdigit (buf[0]) ||
+ saved_errno != 0 ||
+ end_ptr == NULL ||
+ end_ptr != buf + n_digits ||
+ number < min ||
+ number > max)
+ return FALSE;
+
+ *out = number;
+ return TRUE;
+}
+
+/* Locale-independent parsing for RFC 2616 date/times.
+ *
+ * Reference: https://tools.ietf.org/html/rfc2616#section-3.3.1
+ *
+ * Syntax:
+ * <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
+ *
+ * Note that this only accepts the full-year and GMT formats specified by
+ * RFC 1123. It doesn’t accept RFC 850 or asctime formats.
+ *
+ * Example:
+ * Wed, 21 Oct 2015 07:28:00 GMT
+ */
+GDateTime *
+_ostree_parse_rfc2616_date_time (const char *buf,
+ size_t len)
+{
+ guint day_int, year_int, hour_int, minute_int, second_int;
+ const char *day_names[] =
+ {
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun",
+ };
+ size_t day_name_index;
+ const char *month_names[] =
+ {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+ };
+ size_t month_name_index;
+
+ if (len != 29)
+ return NULL;
+
+ const char *day_name = buf;
+ const char *day = buf + 5;
+ const char *month_name = day + 3;
+ const char *year = month_name + 4;
+ const char *hour = year + 5;
+ const char *minute = hour + 3;
+ const char *second = minute + 3;
+ const char *tz = second + 3;
+
+ for (day_name_index = 0; day_name_index < G_N_ELEMENTS (day_names); day_name_index++)
+ {
+ if (strncmp (day_names[day_name_index], day_name, 3) == 0)
+ break;
+ }
+ if (day_name_index >= G_N_ELEMENTS (day_names))
+ return NULL;
+ /* don’t validate whether the day_name matches the rest of the date */
+ if (*(day_name + 3) != ',' || *(day_name + 4) != ' ')
+ return NULL;
+ if (!parse_uint (day, 2, 1, 31, &day_int))
+ return NULL;
+ if (*(day + 2) != ' ')
+ return NULL;
+ for (month_name_index = 0; month_name_index < G_N_ELEMENTS (month_names); month_name_index++)
+ {
+ if (strncmp (month_names[month_name_index], month_name, 3) == 0)
+ break;
+ }
+ if (month_name_index >= G_N_ELEMENTS (month_names))
+ return NULL;
+ if (*(month_name + 3) != ' ')
+ return NULL;
+ if (!parse_uint (year, 4, 0, 9999, &year_int))
+ return NULL;
+ if (*(year + 4) != ' ')
+ return NULL;
+ if (!parse_uint (hour, 2, 0, 23, &hour_int))
+ return NULL;
+ if (*(hour + 2) != ':')
+ return NULL;
+ if (!parse_uint (minute, 2, 0, 59, &minute_int))
+ return NULL;
+ if (*(minute + 2) != ':')
+ return NULL;
+ if (!parse_uint (second, 2, 0, 60, &second_int)) /* allow leap seconds */
+ return NULL;
+ if (*(second + 2) != ' ')
+ return NULL;
+ if (strncmp (tz, "GMT", 3) != 0)
+ return NULL;
+
+ return g_date_time_new_utc (year_int, month_name_index + 1, day_int,
+ hour_int, minute_int, second_int);
+}
#define CURLPIPE_MULTIPLEX 0
#endif
+#include "ostree-date-utils-private.h"
#include "ostree-fetcher.h"
#include "ostree-fetcher-util.h"
#include "ostree-enumtypes.h"
return realsize;
}
-/* @buf must already be known to be long enough */
-static gboolean
-parse_uint (const char *buf,
- guint n_digits,
- guint min,
- guint max,
- guint *out)
-{
- guint64 number;
- const char *end_ptr = NULL;
- gint saved_errno = 0;
-
- g_return_val_if_fail (n_digits == 2 || n_digits == 4, FALSE);
- g_return_val_if_fail (out != NULL, FALSE);
-
- errno = 0;
- number = g_ascii_strtoull (buf, (gchar **)&end_ptr, 10);
- saved_errno = errno;
-
- if (!g_ascii_isdigit (buf[0]) ||
- saved_errno != 0 ||
- end_ptr == NULL ||
- end_ptr != buf + n_digits ||
- number < min ||
- number > max)
- return FALSE;
-
- *out = number;
- return TRUE;
-}
-
-/* Locale-independent parsing for RFC 2616 date/times.
- *
- * Reference: https://tools.ietf.org/html/rfc2616#section-3.3.1
- *
- * Syntax:
- * <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
- *
- * Note that this only accepts the full-year and GMT formats specified by
- * RFC 1123. It doesn’t accept RFC 850 or asctime formats.
- *
- * Example:
- * Wed, 21 Oct 2015 07:28:00 GMT
- */
-static GDateTime *
-parse_rfc2616_date_time (const char *buf,
- size_t len)
-{
- guint day_int, year_int, hour_int, minute_int, second_int;
- const char *day_names[] =
- {
- "Mon",
- "Tue",
- "Wed",
- "Thu",
- "Fri",
- "Sat",
- "Sun",
- };
- size_t day_name_index;
- const char *month_names[] =
- {
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- };
- size_t month_name_index;
-
- if (len != 29)
- return NULL;
-
- const char *day_name = buf;
- const char *day = buf + 5;
- const char *month_name = day + 3;
- const char *year = month_name + 4;
- const char *hour = year + 5;
- const char *minute = hour + 3;
- const char *second = minute + 3;
- const char *tz = second + 3;
-
- for (day_name_index = 0; day_name_index < G_N_ELEMENTS (day_names); day_name_index++)
- {
- if (strncmp (day_names[day_name_index], day_name, 3) == 0)
- break;
- }
- if (day_name_index >= G_N_ELEMENTS (day_names))
- return NULL;
- /* don’t validate whether the day_name matches the rest of the date */
- if (*(day_name + 3) != ',' || *(day_name + 4) != ' ')
- return NULL;
- if (!parse_uint (day, 2, 1, 31, &day_int))
- return NULL;
- if (*(day + 2) != ' ')
- return NULL;
- for (month_name_index = 0; month_name_index < G_N_ELEMENTS (month_names); month_name_index++)
- {
- if (strncmp (month_names[month_name_index], month_name, 3) == 0)
- break;
- }
- if (month_name_index >= G_N_ELEMENTS (month_names))
- return NULL;
- if (*(month_name + 3) != ' ')
- return NULL;
- if (!parse_uint (year, 4, 0, 9999, &year_int))
- return NULL;
- if (*(year + 4) != ' ')
- return NULL;
- if (!parse_uint (hour, 2, 0, 23, &hour_int))
- return NULL;
- if (*(hour + 2) != ':')
- return NULL;
- if (!parse_uint (minute, 2, 0, 59, &minute_int))
- return NULL;
- if (*(minute + 2) != ':')
- return NULL;
- if (!parse_uint (second, 2, 0, 60, &second_int)) /* allow leap seconds */
- return NULL;
- if (*(second + 2) != ' ')
- return NULL;
- if (strncmp (tz, "GMT", 3) != 0)
- return NULL;
-
- return g_date_time_new_utc (year_int, month_name_index + 1, day_int,
- hour_int, minute_int, second_int);
-}
-
/* CURLOPT_HEADERFUNCTION */
static size_t
response_header_cb (const char *buffer, size_t size, size_t n_items, void *user_data)
strncasecmp (buffer, last_modified_header, strlen (last_modified_header)) == 0)
{
g_autofree char *lm_buf = g_strstrip (g_strdup (buffer + strlen (last_modified_header)));
- g_autoptr(GDateTime) dt = parse_rfc2616_date_time (lm_buf, strlen (lm_buf));
+ g_autoptr(GDateTime) dt = _ostree_parse_rfc2616_date_time (lm_buf, strlen (lm_buf));
req->out_last_modified = (dt != NULL) ? g_date_time_to_unix (dt) : 0;
}
test-repo-finder-avahi
test-repo-finder-config
test-repo-finder-mount
+test-rfc2616-dates
test-rollsum-cli
test-kargs
--- /dev/null
+/*
+ * Copyright © 2020 Endless OS Foundation LLC
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * - Philip Withnall <pwithnall@endlessos.org>
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "ostree-date-utils-private.h"
+
+static void
+test_ostree_parse_rfc2616_date_time (void)
+{
+#if GLIB_CHECK_VERSION(2, 62, 0)
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ const struct
+ {
+ const char *rfc2616;
+ const char *expected_iso8601; /* (nullable) if parsing is expected to fail */
+ }
+ tests[] =
+ {
+ { "Wed, 21 Oct 2015 07:28:00 GMT", "2015-10-21T07:28:00Z" },
+ { "Wed, 21 Oct 2015 07:28:00", NULL }, /* too short */
+ { "Wed, 21 Oct 2015 07:28:00 CEST", NULL }, /* too long; not GMT */
+ { "Cat, 21 Oct 2015 07:28:00 GMT", NULL }, /* invalid day */
+ { "Wed 21 Oct 2015 07:28:00 GMT", NULL }, /* no comma */
+ { "Wed,21 Oct 2015 07:28:00 GMT ", NULL }, /* missing space */
+ { "Wed, xx Oct 2015 07:28:00 GMT", NULL }, /* no day-of-month */
+ { "Wed, 011Oct 2015 07:28:00 GMT", NULL }, /* overlong day-of-month */
+ { "Wed, 00 Oct 2015 07:28:00 GMT", NULL }, /* day-of-month underflow */
+ { "Wed, 32 Oct 2015 07:28:00 GMT", NULL }, /* day-of-month overflow */
+ { "Wed, 21,Oct 2015 07:28:00 GMT", NULL }, /* missing space */
+ { "Wed, 21 Cat 2015 07:28:00 GMT", NULL }, /* invalid month */
+ { "Wed, 21 Oct,2015 07:28:00 GMT", NULL }, /* missing space */
+ { "Wed, 21 Oct xxxx 07:28:00 GMT", NULL }, /* no year */
+ { "Wed, 21 Oct 0201507:28:00 GMT", NULL }, /* overlong year */
+ { "Wed, 21 Oct 0000 07:28:00 GMT", NULL }, /* year underflow */
+ { "Wed, 21 Oct 10000 07:28:00 GM", NULL }, /* year overflow */
+ { "Wed, 21 Oct 2015,07:28:00 GMT", NULL }, /* missing space */
+ { "Wed, 21 Oct 2015 07 28:00 GMT", NULL }, /* missing colon */
+ { "Wed, 21 Oct 2015 007:28:00 GM", NULL }, /* overlong hour */
+ { "Wed, 21 Oct 2015 xx:28:00 GMT", NULL }, /* missing hour */
+ { "Wed, 21 Oct 2015 -1:28:00 GMT", NULL }, /* hour underflow */
+ { "Wed, 21 Oct 2015 24:28:00 GMT", NULL }, /* hour overflow */
+ { "Wed, 21 Oct 2015 07:28 00 GMT", NULL }, /* missing colon */
+ { "Wed, 21 Oct 2015 07:028:00 GM", NULL }, /* overlong minute */
+ { "Wed, 21 Oct 2015 07:xx:00 GMT", NULL }, /* missing minute */
+ { "Wed, 21 Oct 2015 07:-1:00 GMT", NULL }, /* minute underflow */
+ { "Wed, 21 Oct 2015 07:60:00 GMT", NULL }, /* minute overflow */
+ { "Wed, 21 Oct 2015 07:28:00CEST", NULL }, /* missing space */
+ { "Wed, 21 Oct 2015 07:28:000 GM", NULL }, /* overlong second */
+ { "Wed, 21 Oct 2015 07:28:xx GMT", NULL }, /* missing second */
+ { "Wed, 21 Oct 2015 07:28:-1 GMT", NULL }, /* seconds underflow */
+ { "Wed, 21 Oct 2015 07:28:61 GMT", NULL }, /* seconds overflow */
+ { "Wed, 21 Oct 2015 07:28:00 UTC", NULL }, /* invalid timezone (only GMT is allowed) */
+ { "Thu, 01 Jan 1970 00:00:00 GMT", "1970-01-01T00:00:00Z" }, /* extreme but valid date */
+ { "Mon, 31 Dec 9999 23:59:59 GMT", "9999-12-31T23:59:59Z" }, /* extreme but valid date */
+ };
+
+ for (gsize i = 0; i < G_N_ELEMENTS (tests); i++)
+ {
+ g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].rfc2616);
+
+ /* Parse once with a trailing nul */
+ g_autoptr(GDateTime) dt1 = _ostree_parse_rfc2616_date_time (tests[i].rfc2616, strlen (tests[i].rfc2616));
+ if (tests[i].expected_iso8601 == NULL)
+ g_assert_null (dt1);
+ else
+ {
+ g_assert_nonnull (dt1);
+ g_autofree char *iso8601 = g_date_time_format_iso8601 (dt1);
+ g_assert_cmpstr (iso8601, ==, tests[i].expected_iso8601);
+ }
+
+ /* And parse again with no trailing nul */
+ g_autofree char *rfc2616_no_nul = g_malloc (strlen (tests[i].rfc2616));
+ memcpy (rfc2616_no_nul, tests[i].rfc2616, strlen (tests[i].rfc2616));
+ g_autoptr(GDateTime) dt2 = _ostree_parse_rfc2616_date_time (rfc2616_no_nul, strlen (tests[i].rfc2616));
+ if (tests[i].expected_iso8601 == NULL)
+ g_assert_null (dt2);
+ else
+ {
+ g_assert_nonnull (dt2);
+ g_autofree char *iso8601 = g_date_time_format_iso8601 (dt2);
+ g_assert_cmpstr (iso8601, ==, tests[i].expected_iso8601);
+ }
+ }
+G_GNUC_END_IGNORE_DEPRECATIONS
+#else
+ /* GLib 2.62 is needed for g_date_time_format_iso8601(). */
+ g_test_skip ("RFC 2616 date parsing test needs GLib ≥ 2.62.0");
+#endif
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/ostree_parse_rfc2616_date_time", test_ostree_parse_rfc2616_date_time);
+ return g_test_run ();
+}