return str;
}
-static bool imap_mktime(struct tm *tm, time_t *time_r)
+static bool tm_is_too_large(const struct tm *tm, time_t *max_time_r)
+{
+ static time_t max_time = 0;
+ static struct tm max_tm = { 0, };
+
+ if (max_time == 0) {
+#if TIME_T_MAX_BITS == 32
+ max_time = 0xffffffffUL;
+#elif TIME_T_MAX_BITS == 64
+ max_time = 0xffffffffffffffffULL;
+#else
+ max_time = ((time_t)1 << TIME_T_MAX_BITS) - 1;
+#endif
+ max_tm = *gmtime(&max_time);
+ }
+ *max_time_r = max_time;
+
+ if (tm->tm_year != max_tm.tm_year)
+ return tm->tm_year > max_tm.tm_year;
+ if (tm->tm_mon != max_tm.tm_mon)
+ return tm->tm_mon > max_tm.tm_mon;
+ if (tm->tm_mday != max_tm.tm_mday)
+ return tm->tm_mday > max_tm.tm_mday;
+ if (tm->tm_hour != max_tm.tm_hour)
+ return tm->tm_hour > max_tm.tm_hour;
+ if (tm->tm_min != max_tm.tm_min)
+ return tm->tm_min > max_tm.tm_min;
+ if (tm->tm_sec != max_tm.tm_sec)
+ return tm->tm_sec > max_tm.tm_sec;
+ return FALSE;
+}
+
+static int imap_mktime(struct tm *tm, time_t *time_r)
{
*time_r = utc_mktime(tm);
if (*time_r != (time_t)-1)
- return TRUE;
+ return 1;
/* the date is outside valid range for time_t. it might still be
technically valid though, so try to handle this case.
#else
*time_r = 0;
#endif
- } else {
+ return 0;
+ } else if (tm_is_too_large(tm, time_r)) {
/* too high. return the highest allowed value.
we shouldn't get here with 64bit time_t,
but handle that anyway. */
-#if (TIME_T_MAX_BITS == 32 || TIME_T_MAX_BITS == 64)
- *time_r = (1UL << (TIME_T_MAX_BITS-1)) - 1;
-#else
- *time_r = (1UL << TIME_T_MAX_BITS) - 1;
-#endif
+ return 0;
+ } else {
+ return -1;
}
- return FALSE;
}
bool imap_parse_date(const char *str, time_t *timestamp_r)
return FALSE;
tm.tm_isdst = -1;
- (void)imap_mktime(&tm, timestamp_r);
+ if (imap_mktime(&tm, timestamp_r) < 0)
+ return FALSE;
return TRUE;
}
int *timezone_offset_r)
{
struct tm tm;
+ int ret;
str = imap_parse_date_internal(str, &tm);
if (str == NULL)
*timezone_offset_r = parse_timezone(str);
tm.tm_isdst = -1;
- if (imap_mktime(&tm, timestamp_r))
+ if ((ret = imap_mktime(&tm, timestamp_r)) > 0)
*timestamp_r -= *timezone_offset_r * 60;
- return TRUE;
+ return ret >= 0;
}
static void imap_to_date_tm(char buf[11], const struct tm *tm)
--- /dev/null
+/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "env-util.h"
+#include "imap-date.h"
+#include "test-common.h"
+
+#include <time.h>
+
+static void test_imap_date(void)
+{
+ const struct {
+ const char *str;
+ time_t timestamp;
+ } tests[] = {
+ { "01-Jan-1970", 0 },
+ { "19-Jan-2038", 2147472000 },
+#if TIME_T_MAX_BITS >= 32
+ { "07-Feb-2106", 4294944000 },
+#endif
+#if TIME_T_MAX_BITS >= 37
+ { "08-Apr-6325", 137438899200LL },
+#endif
+#if TIME_T_MAX_BITS >= 38
+ { "31-Dec-9999", 253402214400LL },
+#endif
+ /* conversions to maximum values */
+#if TIME_T_MAX_BITS == 31
+ { "20-Jan-2038", 2147483647 },
+ { "31-Dec-9999", 2147483647 },
+#elif TIME_T_MAX_BITS == 32
+ { "08-Feb-2106", 4294967295 },
+ { "31-Dec-9999", 4294967295 },
+#endif
+ };
+ const char *invalid_tests[] = {
+ "32-Jan-2023",
+ "29-Feb-2023",
+ "31-Apr-2023",
+ };
+ time_t ts;
+
+ test_begin("imap_parse_date()");
+ for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+ test_assert_idx(imap_parse_date(tests[i].str, &ts), i);
+ test_assert_cmp_idx(tests[i].timestamp, ==, ts, i);
+ }
+ for (unsigned int i = 0; i < N_ELEMENTS(invalid_tests); i++)
+ test_assert_idx(!imap_parse_date(invalid_tests[i], &ts), i);
+ test_end();
+}
+
+static void test_imap_datetime(void)
+{
+ const struct {
+ const char *str;
+ time_t timestamp;
+ int tz;
+ } tests[] = {
+ { "01-Jan-1970 00:00:00 +0000", 0, 0 },
+ { "19-Jan-2038 03:14:07 +0000", 2147483647, 0 },
+ { "19-Jan-2038 05:14:07 +0200", 2147483647, 2*60 },
+#if TIME_T_MAX_BITS >= 32
+ { "07-Feb-2106 06:28:15 +0000", 4294967295, 0 },
+#endif
+#if TIME_T_MAX_BITS >= 37
+ { "08-Apr-6325 15:04:31 +0000", 137438953471LL, 0 },
+#endif
+#if TIME_T_MAX_BITS >= 38
+ { "31-Dec-9999 23:59:59 +2359", 253402300799LL - 23*60*60 - 59*60, 23*60 + 59 },
+ { "31-Dec-9999 23:59:59 -2359", 253402300799LL + 23*60*60 + 59*60, -23*60 - 59 },
+#endif
+ /* conversions to maximum values */
+#if TIME_T_MAX_BITS == 31
+ { "19-Jan-2038 03:14:08 +0000", 2147483647, 0 },
+ { "31-Dec-9999 23:59:59 -2359", 2147483647, -23*60 - 59 },
+#elif TIME_T_MAX_BITS == 32
+ { "07-Feb-2106 06:28:16 +0000", 4294967295, 0 },
+ { "31-Dec-9999 23:59:59 -2359", 4294967295, -23*60 - 59 },
+#endif
+ };
+ const char *invalid_tests[] = {
+ "02-Jan-2023 24:00:00",
+ "02-Jan-2023 23:60:00",
+ "02-Jan-2023 23:00:60",
+ };
+ time_t ts;
+ int tz;
+
+ test_begin("imap_parse_date()");
+ for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+ test_assert_idx(imap_parse_datetime(tests[i].str, &ts, &tz), i);
+ test_assert_cmp_idx(tests[i].timestamp, ==, ts, i);
+ test_assert_idx(tests[i].tz == tz, i);
+ }
+ for (unsigned int i = 0; i < N_ELEMENTS(invalid_tests); i++)
+ test_assert_idx(!imap_parse_datetime(invalid_tests[i], &ts, &tz), i);
+ test_end();
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_imap_date,
+ test_imap_datetime,
+ NULL
+ };
+ env_put("TZ", "UTC");
+ tzset();
+ return test_run(test_functions);
+}