--- a/libpurple/tests/test_util.c Thu Sep 16 21:11:40 2021 -0500
+++ b/libpurple/tests/test_util.c Thu Sep 16 21:11:59 2021 -0500
@@ -162,13 +162,21 @@
fail_unless(377182200 == purple_str_to_time("19811214T12:50:00", TRUE, NULL, NULL, NULL));
+ fail_unless(377203800 == purple_str_to_time("19811214T12:50:00-06", FALSE, NULL, NULL, NULL)); fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21", TRUE, NULL, NULL, NULL));
fail_unless(1282941722 == purple_str_to_time("2010-08-27.204202", TRUE, NULL, NULL, NULL));
+ fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21.3234", TRUE, NULL, NULL, NULL)); + fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21Z", TRUE, NULL, NULL, NULL)); + fail_unless(1631512800 == purple_str_to_time("09-13-2021", TRUE, NULL, NULL, NULL)); timestamp = purple_str_to_time("2010-08-27.134202-0700PDT", FALSE, &tm, &tz_off, &rest);
fail_unless(1282941722 == timestamp);
fail_unless((-7 * 60 * 60) == tz_off);
assert_string_equal("PDT", rest);
+ timestamp = purple_str_to_time("09/13/202115:34:34", TRUE, NULL, NULL, &rest); + fail_unless(1631512800 == timestamp); + assert_string_equal("15:34:34", rest); @@ -253,6 +261,8 @@
Suite *s = suite_create("Utility Functions");
TCase *tc = tcase_create("Base16");
@@ -306,5 +316,7 @@
tcase_add_test(tc, test_uri_escape_for_open);
--- a/libpurple/util.c Thu Sep 16 21:11:40 2021 -0500
+++ b/libpurple/util.c Thu Sep 16 21:11:59 2021 -0500
@@ -84,6 +84,40 @@
static char *custom_user_dir = NULL;
static char *user_dir = NULL;
+static GRegex *str_to_time_regex = NULL; +static const gchar *str_to_time_pattern = + "(?P<year>\\d{4})? # look for a leading year\n" + "(?:[-\\/]?) # an optional separator of - or /\n" + "(?P<month>\\d{2}) # the two digit month\n" + "(?:[-\\/]?) # another optional separator of - or /\n" + "(?P<day>\\d{2}) # the two digit day\n" + "# we now have an optional trailing year or seconds\n" + " (?:[-\\/]?(?P<trailingyear>\\d{4})) # the trailing year may have a - or / separator\n" + " [T.] # T signifies that this is an iso8601 time\n" + " (?P<hours>\\d{2}) # two digit hour\n" + " :? # optional : separator\n" + " (?P<minutes>\\d{2}) # two digit minutes\n" + " :? # optional : separator\n" + " (?P<seconds>\\d{2}) # two digit seconds\n" + " (?:\\.(?P<microseconds>\\d+))? # optional microseconds\n" + " (?P<tzsign>[+-]) # required tz sign\n" + " (?P<tzhour>\\d{2}) # required tz hour\n" + " :? # optional : separator\n" + " (?<tzminute>\\d{2}))? # optional tz minutes\n" + " (?P<utc>Z) # utc time\n" + "\\s* # remove trailing whitespace\n"; purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data,
@@ -109,8 +143,19 @@
- /* This does nothing right now. It exists for symmetry with
- * purple_util_uninit() and forwards compatibility. */
+ str_to_time_regex = g_regex_new(str_to_time_pattern, + G_REGEX_EXTENDED | G_REGEX_RAW, 0, + purple_debug_fatal("util", + "Failed to compile the regex for purple_str_to_time" + error->message ? error->message : "unknown"); @@ -123,6 +168,9 @@
+ g_regex_unref(str_to_time_regex); + str_to_time_regex = NULL; /**************************************************************************
@@ -705,169 +753,167 @@
purple_str_to_time(const char *timestamp, gboolean utc,
struct tm *tm, long *tz_off, const char **rest)
+ GMatchInfo *info = NULL; + gboolean mktime_with_utc = FALSE, matched = FALSE;
long tzoff = PURPLE_NO_TZ_OFF;
- gboolean mktime_with_utc = FALSE;
g_return_val_if_fail(timestamp != NULL, 0);
memset(&t, 0, sizeof(struct tm));
- /* Strip leading whitespace */
- while (g_ascii_isspace(*str))
- if (rest != NULL && *str != '\0')
- if (!g_ascii_isdigit(*str) && *str != '-' && *str != '+') {
- if (rest != NULL && *str != '\0')
- if (sscanf(str, "%04d", &year) && year >= 1900) {
- if (*str == '-' || *str == '/')
- t.tm_year = year - 1900;
- if (!sscanf(str, "%02d", &t.tm_mon)) {
- if (rest != NULL && *str != '\0')
- if (*str == '-' || *str == '/')
- if (!sscanf(str, "%02d", &t.tm_mday)) {
- if (rest != NULL && *str != '\0')
+ matched = g_regex_match(str_to_time_regex, timestamp, 0, &info); + g_match_info_free(info);
- /* Grab the year off the end if there's still stuff */
- if (*str == '/' || *str == '-') {
- /* But make sure we don't read the year twice */
- if (rest != NULL && *str != '\0')
+ /* Set rest to the end of the match in timestamp. */ + if(g_match_info_fetch_pos(info, 0, NULL, &end)) { + *rest = timestamp + end;
- if (!sscanf(str, "%04d", &t.tm_year)) {
- if (rest != NULL && *str != '\0')
+ /* check the year or the trailingyear if year is not set */ + match = g_match_info_fetch_named(info, "year"); + if(match != NULL && *match != '\0') { + gint year = atoi(match); + t.tm_year = year - 1900; + match = g_match_info_fetch_named(info, "trailingyear"); + if(match != NULL && *match != '\0') { + t.tm_year = atoi(match) - 1900;
- } else if (*str == 'T' || *str == '.') {
- /* Continue grabbing the hours/minutes/seconds */
- if ((sscanf(str, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 &&
- (sscanf(str, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 &&
- gint sign, tzhrs, tzmins;
- /* Cut off those pesky micro-seconds */
- } while (*str >= '0' && *str <= '9');
+ match = g_match_info_fetch_named(info, "month"); + if(match != NULL && *match != '\0') { + t.tm_mon = atoi(match) - 1; + match = g_match_info_fetch_named(info, "day"); + if(match != NULL && *match != '\0') { + t.tm_mday = atoi(match); + match = g_match_info_fetch_named(info, "hours"); + if(match != NULL && *match != '\0') { + t.tm_hour = atoi(match); + match = g_match_info_fetch_named(info, "minutes"); + if(match != NULL && *match != '\0') { + t.tm_min = atoi(match); + match = g_match_info_fetch_named(info, "seconds"); + if(match != NULL && *match != '\0') { + t.tm_sec = atoi(match); + /* check if this is utc time */ + match = g_match_info_fetch_named(info, "utc"); + if(match != NULL && *match != '\0') { + /* free match if it was just an empty string */ + /* if match is null, check if we have tzsign which is required */ + match = g_match_info_fetch_named(info, "tzsign"); + gint tzsign = -1, tzhour = 0, tzminute = 0; + /* Figure out if we're ahead or behind of utc, assuming ahead by
- sign = (*str == '+') ? 1 : -1;
- /* Process the timezone */
- if (*str == '+' || *str == '-') {
- if (((sscanf(str, "%02d:%02d", &tzhrs, &tzmins) == 2 && (str += 5)) ||
- (sscanf(str, "%02d%02d", &tzhrs, &tzmins) == 2 && (str += 4))))
- mktime_with_utc = TRUE;
- tzoff = tzhrs * 60 * 60 + tzmins * 60;
- } else if (*str == 'Z') {
+ match = g_match_info_fetch_named(info, "tzhour"); + if(match != NULL && *match != '\0') { + /* get the tz minute */ + match = g_match_info_fetch_named(info, "tzminute"); + if(match != NULL && *match != '\0') { + tzminute = atoi(match); + /* we need at least either an hour or minute to offset the timezone */ + if(tzhour > 0 || tzminute > 0) {
- /* No timezone specified. */
- mktime_with_utc = TRUE;
+ tzoff = tzsign * ((tzhour * 3600) + (tzminute * 60)); - if (rest != NULL && *str != '\0') {
- /* Strip trailing whitespace */
- while (g_ascii_isspace(*str))
+ /* If we have a time, figure out if we need to adjust our tz offset. */ + if(t.tm_hour > 0 || t.tm_min > 0 || t.tm_sec > 0) { + mktime_with_utc = TRUE;
- if (tzoff != PURPLE_NO_TZ_OFF)
+ if(tzoff != PURPLE_NO_TZ_OFF) {
+ g_match_info_free(info);