1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
5
#include "utc-mktime.h"
11
Official specification is still RFC261, Section 3.3, but we anticipate
12
HTTPbis and use the draft Part 2, Section 5.1 as reference for our
15
http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-20#section-5.1
17
The defined syntax is as follows:
19
HTTP-date = rfc1123-date / obs-date
23
rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
24
; fixed length subset of the format defined in
25
; Section 5.2.14 of [RFC1123]
26
day-name = %x4D.6F.6E ; "Mon", case-sensitive
27
/ %x54.75.65 ; "Tue", case-sensitive
28
/ %x57.65.64 ; "Wed", case-sensitive
29
/ %x54.68.75 ; "Thu", case-sensitive
30
/ %x46.72.69 ; "Fri", case-sensitive
31
/ %x53.61.74 ; "Sat", case-sensitive
32
/ %x53.75.6E ; "Sun", case-sensitive
33
date1 = day SP month SP year
36
month = %x4A.61.6E ; "Jan", case-sensitive
37
/ %x46.65.62 ; "Feb", case-sensitive
38
/ %x4D.61.72 ; "Mar", case-sensitive
39
/ %x41.70.72 ; "Apr", case-sensitive
40
/ %x4D.61.79 ; "May", case-sensitive
41
/ %x4A.75.6E ; "Jun", case-sensitive
42
/ %x4A.75.6C ; "Jul", case-sensitive
43
/ %x41.75.67 ; "Aug", case-sensitive
44
/ %x53.65.70 ; "Sep", case-sensitive
45
/ %x4F.63.74 ; "Oct", case-sensitive
46
/ %x4E.6F.76 ; "Nov", case-sensitive
47
/ %x44.65.63 ; "Dec", case-sensitive
49
GMT = %x47.4D.54 ; "GMT", case-sensitive
50
time-of-day = hour ":" minute ":" second
56
The semantics of day-name, day, month, year, and time-of-day are the
57
same as those defined for the RFC 5322 constructs with the
58
corresponding name ([RFC5322], Section 3.3).
62
obs-date = rfc850-date / asctime-date
64
rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT
65
date2 = day "-" month "-" 2DIGIT
66
; day-month-year (e.g., 02-Jun-82)
67
day-name-l = %x4D.6F.6E.64.61.79 ; "Monday", case-sensitive
68
/ %x54.75.65.73.64.61.79 ; "Tuesday", case-sensitive
69
/ %x57.65.64.6E.65.73.64.61.79 ; "Wednesday", case-sensitive
70
/ %x54.68.75.72.73.64.61.79 ; "Thursday", case-sensitive
71
/ %x46.72.69.64.61.79 ; "Friday", case-sensitive
72
/ %x53.61.74.75.72.64.61.79 ; "Saturday", case-sensitive
73
/ %x53.75.6E.64.61.79 ; "Sunday", case-sensitive
75
asctime-date = day-name SP date3 SP time-of-day SP year
76
date3 = month SP ( 2DIGIT / ( SP 1DIGIT ))
77
; month day (e.g., Jun 2)
81
static const char *month_names[] = {
82
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
83
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
86
static const char *weekday_names[] = {
87
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
90
static const char *weekday_names_long[] = {
91
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
94
struct http_date_parser {
95
const unsigned char *cur, *end;
102
http_date_parse_sp(struct http_date_parser *parser)
104
if (parser->cur >= parser->end)
106
if (parser->cur[0] != ' ')
113
http_date_parse_number(struct http_date_parser *parser,
114
int digits, int *number_r)
118
if (parser->cur >= parser->end || !i_isdigit(parser->cur[0]))
121
*number_r = parser->cur[0] - '0';
124
for (i=0; i < digits-1; i++) {
125
if (parser->cur >= parser->end || !i_isdigit(parser->cur[0]))
127
*number_r = ((*number_r) * 10) + parser->cur[0] - '0';
134
http_date_parse_word(struct http_date_parser *parser,
135
int maxchars, string_t **word_r)
140
if (parser->cur >= parser->end || !i_isalpha(parser->cur[0]))
143
word = t_str_new(maxchars);
144
str_append_c(word, parser->cur[0]);
147
for (i=0; i < maxchars-1; i++) {
148
if (parser->cur >= parser->end || !i_isalpha(parser->cur[0]))
150
str_append_c(word, parser->cur[0]);
154
if (i_isalpha(parser->cur[0]))
161
http_date_parse_year(struct http_date_parser *parser)
164
if (http_date_parse_number(parser, 4, &parser->tm.tm_year) <= 0)
166
if (parser->tm.tm_year < 1900)
168
parser->tm.tm_year -= 1900;
173
http_date_parse_month(struct http_date_parser *parser)
178
if (http_date_parse_word(parser, 3, &month) <= 0 || str_len(month) != 3)
181
for (i = 0; i < 12; i++) {
182
if (strcmp(month_names[i], str_c(month)) == 0) {
189
parser->tm.tm_mon = i;
194
http_date_parse_day(struct http_date_parser *parser)
197
if (http_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0)
203
http_date_parse_time_of_day(struct http_date_parser *parser)
205
/* time-of-day = hour ":" minute ":" second
206
; 00:00:00 - 23:59:59
213
if (http_date_parse_number(parser, 2, &parser->tm.tm_hour) <= 0)
217
if (parser->cur >= parser->end || parser->cur[0] != ':')
221
/* minute = 2DIGIT */
222
if (http_date_parse_number(parser, 2, &parser->tm.tm_min) <= 0)
226
if (parser->cur >= parser->end || parser->cur[0] != ':')
230
/* second = 2DIGIT */
231
if (http_date_parse_number(parser, 2, &parser->tm.tm_sec) <= 0)
237
http_date_parse_time_gmt(struct http_date_parser *parser)
241
/* Remaining: {...} SP time-of-day SP GMT
245
if (http_date_parse_sp(parser) <= 0)
247
if (http_date_parse_time_of_day(parser) <= 0)
251
if (http_date_parse_sp(parser) <= 0)
253
if (http_date_parse_word(parser, 3, &gmt) <= 0 ||
254
strcmp("GMT", str_c(gmt)) != 0)
260
http_date_parse_format_rfc1123(struct http_date_parser *parser)
263
rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
264
; fixed length subset of the format defined in
265
; Section 5.2.14 of [RFC1123]
266
date1 = day SP month SP year
269
Remaining: {...} SP day SP month SP year SP time-of-day SP GMT
274
if (http_date_parse_sp(parser) <= 0)
276
if (http_date_parse_day(parser) <= 0)
280
if (http_date_parse_sp(parser) <= 0)
282
if (http_date_parse_month(parser) <= 0)
286
if (http_date_parse_sp(parser) <= 0)
288
if (http_date_parse_year(parser) <= 0)
291
/* SP time-of-day SP GMT */
292
return http_date_parse_time_gmt(parser);
296
http_date_parse_format_rfc850(struct http_date_parser *parser)
299
rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT
300
date2 = day "-" month "-" 2DIGIT
301
; day-month-year (e.g., 02-Jun-82)
303
Remaining: "," SP day "-" month "-" 2DIGIT SP time-of-day SP GMT
307
if (parser->cur >= parser->end || parser->cur[0] != ',')
310
if (http_date_parse_sp(parser) <= 0)
314
if (http_date_parse_day(parser) <= 0)
318
if (parser->cur >= parser->end || parser->cur[0] != '-')
323
if (http_date_parse_month(parser) <= 0)
327
if (parser->cur >= parser->end || parser->cur[0] != '-')
332
if (http_date_parse_number(parser, 2, &parser->tm.tm_year) <= 0)
334
if (parser->tm.tm_year < 70)
335
parser->tm.tm_year += 100;
337
/* SP time-of-day SP GMT */
338
return http_date_parse_time_gmt(parser);
342
http_date_parse_format_asctime(struct http_date_parser *parser)
347
asctime-date = day-name SP date3 SP time-of-day SP year
348
date3 = month SP ( 2DIGIT / ( SP 1DIGIT ))
349
; month day (e.g., Jun 2)
351
Remaining: {...} month SP ( 2DIGIT / ( SP 1DIGIT )) SP time-of-day SP year
355
if (http_date_parse_month(parser) <= 0)
359
if (http_date_parse_sp(parser) <= 0)
362
/* SP 1DIGIT / 2DIGIT */
363
if ((ret=http_date_parse_sp(parser)) < 0)
366
if (http_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0)
369
if (http_date_parse_number(parser, 1, &parser->tm.tm_mday) <= 0)
374
if (http_date_parse_sp(parser) <= 0)
376
if (http_date_parse_time_of_day(parser) <= 0)
380
if (http_date_parse_sp(parser) <= 0)
383
return http_date_parse_year(parser);
387
http_date_parse_format_any(struct http_date_parser *parser)
393
HTTP-date = rfc1123-date / obs-date
394
rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
395
; fixed length subset of the format defined in
396
; Section 5.2.14 of [RFC1123]
397
obs-date = rfc850-date / asctime-date
398
rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT
399
asctime-date = day-name SP date3 SP time-of-day SP year
402
if (http_date_parse_word(parser, 9, &dayname) <= 0)
405
if (str_len(dayname) > 3) {
407
for (i = 0; i < 7; i++) {
408
if (strcmp(weekday_names_long[i], str_c(dayname)) == 0) {
414
return http_date_parse_format_rfc850(parser);
417
/* rfc1123-date / asctime-date */
418
for (i = 0; i < 7; i++) {
419
if (strcmp(weekday_names[i], str_c(dayname)) == 0) {
424
if (i >= 7 || parser->cur >= parser->end)
427
if (parser->cur[0] == ' ') {
430
return http_date_parse_format_asctime(parser);
433
if (parser->cur[0] != ',')
438
return http_date_parse_format_rfc1123(parser);
441
bool http_date_parse(const unsigned char *data, size_t size,
444
struct http_date_parser parser;
447
memset(&parser, 0, sizeof(parser));
449
parser.end = data + size;
451
if (http_date_parse_format_any(&parser) <= 0)
454
if (parser.cur != parser.end)
457
parser.tm.tm_isdst = -1;
458
timestamp = utc_mktime(&parser.tm);
459
if (timestamp == (time_t)-1)
462
*timestamp_r = timestamp;
466
bool http_date_parse_tm(const unsigned char *data, size_t size,
472
if (!http_date_parse(data, size, ×tamp))
475
tm = gmtime(×tamp);
480
const char *http_date_create_tm(struct tm *tm)
482
return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d GMT",
483
weekday_names[tm->tm_wday],
485
month_names[tm->tm_mon],
487
tm->tm_hour, tm->tm_min, tm->tm_sec);
490
const char *http_date_create(time_t timestamp)
493
tm = gmtime(×tamp);
495
return http_date_create_tm(tm);