/* * 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, see . * * Authors: * - Philip Withnall */ #include "config.h" #include #include #include #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: * , :: 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); }