1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 
11 
12 static ngx_uint_t  mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
13 
14 time_t
ngx_parse_http_time(u_char * value,size_t len)15 ngx_parse_http_time(u_char *value, size_t len)
16 {
17     u_char      *p, *end;
18     ngx_int_t    month;
19     ngx_uint_t   day, year, hour, min, sec;
20     uint64_t     time;
21     enum {
22         no = 0,
23         rfc822,   /* Tue, 10 Nov 2002 23:50:13   */
24         rfc850,   /* Tuesday, 10-Dec-02 23:50:13 */
25         isoc      /* Tue Dec 10 23:50:13 2002    */
26     } fmt;
27 
28     fmt = 0;
29     end = value + len;
30 
31 #if (NGX_SUPPRESS_WARN)
32     day = 32;
33     year = 2038;
34 #endif
35 
36     for (p = value; p < end; p++) {
37         if (*p == ',') {
38             break;
39         }
40 
41         if (*p == ' ') {
42             fmt = isoc;
43             break;
44         }
45     }
46 
47     for (p++; p < end; p++) {
48         if (*p != ' ') {
49             break;
50         }
51     }
52 
53     if (end - p < 18) {
54         return NGX_ERROR;
55     }
56 
57     if (fmt != isoc) {
58         if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
59             return NGX_ERROR;
60         }
61 
62         day = (*p - '0') * 10 + (*(p + 1) - '0');
63         p += 2;
64 
65         if (*p == ' ') {
66             if (end - p < 18) {
67                 return NGX_ERROR;
68             }
69             fmt = rfc822;
70 
71         } else if (*p == '-') {
72             fmt = rfc850;
73 
74         } else {
75             return NGX_ERROR;
76         }
77 
78         p++;
79     }
80 
81     switch (*p) {
82 
83     case 'J':
84         month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
85         break;
86 
87     case 'F':
88         month = 1;
89         break;
90 
91     case 'M':
92         month = *(p + 2) == 'r' ? 2 : 4;
93         break;
94 
95     case 'A':
96         month = *(p + 1) == 'p' ? 3 : 7;
97         break;
98 
99     case 'S':
100         month = 8;
101         break;
102 
103     case 'O':
104         month = 9;
105         break;
106 
107     case 'N':
108         month = 10;
109         break;
110 
111     case 'D':
112         month = 11;
113         break;
114 
115     default:
116         return NGX_ERROR;
117     }
118 
119     p += 3;
120 
121     if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
122         return NGX_ERROR;
123     }
124 
125     p++;
126 
127     if (fmt == rfc822) {
128         if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
129             || *(p + 2) < '0' || *(p + 2) > '9'
130             || *(p + 3) < '0' || *(p + 3) > '9')
131         {
132             return NGX_ERROR;
133         }
134 
135         year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
136                + (*(p + 2) - '0') * 10 + (*(p + 3) - '0');
137         p += 4;
138 
139     } else if (fmt == rfc850) {
140         if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
141             return NGX_ERROR;
142         }
143 
144         year = (*p - '0') * 10 + (*(p + 1) - '0');
145         year += (year < 70) ? 2000 : 1900;
146         p += 2;
147     }
148 
149     if (fmt == isoc) {
150         if (*p == ' ') {
151             p++;
152         }
153 
154         if (*p < '0' || *p > '9') {
155             return NGX_ERROR;
156         }
157 
158         day = *p++ - '0';
159 
160         if (*p != ' ') {
161             if (*p < '0' || *p > '9') {
162                 return NGX_ERROR;
163             }
164 
165             day = day * 10 + (*p++ - '0');
166         }
167 
168         if (end - p < 14) {
169             return NGX_ERROR;
170         }
171     }
172 
173     if (*p++ != ' ') {
174         return NGX_ERROR;
175     }
176 
177     if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
178         return NGX_ERROR;
179     }
180 
181     hour = (*p - '0') * 10 + (*(p + 1) - '0');
182     p += 2;
183 
184     if (*p++ != ':') {
185         return NGX_ERROR;
186     }
187 
188     if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
189         return NGX_ERROR;
190     }
191 
192     min = (*p - '0') * 10 + (*(p + 1) - '0');
193     p += 2;
194 
195     if (*p++ != ':') {
196         return NGX_ERROR;
197     }
198 
199     if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
200         return NGX_ERROR;
201     }
202 
203     sec = (*p - '0') * 10 + (*(p + 1) - '0');
204 
205     if (fmt == isoc) {
206         p += 2;
207 
208         if (*p++ != ' ') {
209             return NGX_ERROR;
210         }
211 
212         if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
213             || *(p + 2) < '0' || *(p + 2) > '9'
214             || *(p + 3) < '0' || *(p + 3) > '9')
215         {
216             return NGX_ERROR;
217         }
218 
219         year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
220                + (*(p + 2) - '0') * 10 + (*(p + 3) - '0');
221     }
222 
223     if (hour > 23 || min > 59 || sec > 59) {
224         return NGX_ERROR;
225     }
226 
227     if (day == 29 && month == 1) {
228         if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
229             return NGX_ERROR;
230         }
231 
232     } else if (day > mday[month]) {
233         return NGX_ERROR;
234     }
235 
236     /*
237      * shift new year to March 1 and start months from 1 (not 0),
238      * it is needed for Gauss' formula
239      */
240 
241     if (--month <= 0) {
242         month += 12;
243         year -= 1;
244     }
245 
246     /* Gauss' formula for Gregorian days since March 1, 1 BC */
247 
248     time = (uint64_t) (
249             /* days in years including leap years since March 1, 1 BC */
250 
251             365 * year + year / 4 - year / 100 + year / 400
252 
253             /* days before the month */
254 
255             + 367 * month / 12 - 30
256 
257             /* days before the day */
258 
259             + day - 1
260 
261             /*
262              * 719527 days were between March 1, 1 BC and March 1, 1970,
263              * 31 and 28 days were in January and February 1970
264              */
265 
266             - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
267 
268 #if (NGX_TIME_T_SIZE <= 4)
269 
270     if (time > 0x7fffffff) {
271         return NGX_ERROR;
272     }
273 
274 #endif
275 
276     return (time_t) time;
277 }
278