1 /* Parser of HTTP date */
10 #ifdef TIME_WITH_SYS_TIME
11 #ifdef HAVE_SYS_TIME_H
18 #if defined(TM_IN_SYS_TIME) && defined(HAVE_SYS_TIME_H)
20 #elif defined(HAVE_TIME_H)
27 #include "protocol/date.h"
28 #include "util/conv.h"
29 #include "util/time.h"
32 * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
33 * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
34 * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
38 parse_year(const unsigned char **date_p
, unsigned char *end
)
40 const unsigned char *date
= *date_p
;
43 if ((end
&& date
+ 1 >= end
)
48 year
= (date
[0] - '0') * 10 + date
[1] - '0';
50 if ((!end
|| date
+ 3 < end
)
52 && isdigit(date
[3])) {
53 /* Four digits date */
54 year
= year
* 10 + date
[2] - '0';
55 year
= year
* 10 + date
[3] - '0' - 1900;
58 } else if (year
< 70) {
59 /* Assuming the epoch starting at 1.1.1970 so it's already next
70 parse_month(const unsigned char **buf
, unsigned char *end
)
72 const unsigned char *month
= *buf
;
75 /* Check that the buffer has atleast 3 chars. */
76 if ((end
&& month
+ 2 > end
)
77 || !month
[0] || !month
[1] || !month
[2])
80 monthnum
= month2num(month
);
89 /* Return day number. */
91 parse_day(const unsigned char **date_p
, unsigned char *end
)
93 const unsigned char *date
= *date_p
;
96 if ((end
&& date
>= end
) || !isdigit(*date
))
100 if ((!end
|| date
< end
) && isdigit(*date
)) {
101 day
= day
* 10 + *date
++ - '0';
109 parse_time(const unsigned char **time
, struct tm
*tm
, unsigned char *end
)
111 unsigned char h1
, h2
, m1
, m2
;
112 const unsigned char *date
= *time
;
114 #define check_time(tm) \
115 ((tm)->tm_hour <= 23 && (tm)->tm_min <= 59 && (tm)->tm_sec <= 59)
118 if (end
&& date
+ 5 > end
)
121 h1
= *date
++; if (!isdigit(h1
)) return 0;
122 h2
= *date
++; if (!isdigit(h2
)) return 0;
124 if (*date
++ != ':') return 0;
126 m1
= *date
++; if (!isdigit(m1
)) return 0;
127 m2
= *date
++; if (!isdigit(m2
)) return 0;
129 tm
->tm_hour
= (h1
- '0') * 10 + h2
- '0';
130 tm
->tm_min
= (m1
- '0') * 10 + m2
- '0';
133 /* Eat :SS or [PA]M or nothing */
134 if (end
&& date
+ 2 >= end
) {
136 return check_time(tm
);
140 unsigned char s1
, s2
;
144 if (end
&& date
+ 2 >= end
)
147 s1
= *date
++; if (!isdigit(s1
)) return 0;
148 s2
= *date
++; if (!isdigit(s2
)) return 0;
150 tm
->tm_sec
= (s1
- '0') * 10 + s2
- '0';
152 } else if (*date
== 'A' || *date
== 'P') {
153 /* Adjust hour from AM/PM. The sequence goes 11:00AM, 12:00PM,
154 * 01:00PM ... 11:00PM, 12:00AM, 01:00AM. --wget */
155 if (tm
->tm_hour
== 12)
167 return check_time(tm
);
172 my_timegm(struct tm
*tm
)
176 /* Okay, the next part of the code is somehow problematic. Now, we use
177 * own code for calculating the number of seconds from 1.1.1970,
178 * brought here by SC from w3m. I don't like it a lot, but it's 100%
179 * portable, it's faster and it's shorter. --pasky */
184 /* Since mktime thinks we have localtime, we need a wrapper
186 /* FIXME: It was reported that it doesn't work somewhere :/. */
188 unsigned char *tz
= getenv("TZ");
191 /* Temporary disable timezone in-place. */
192 unsigned char tmp
= *tz
;
203 /* Already GMT, cool! */
209 /* Following code was borrowed from w3m, and its developers probably
210 * borrowed it from somewhere else as well, altough they didn't bother
211 * to mention that. */ /* Actually, same code appears to be in lynx as
212 * well.. oh well. :) */
213 /* See README.timegm for explanation about how this works. */
215 if (tm
->tm_mon
< 0) {
219 tm
->tm_mon
*= 153; tm
->tm_mon
+= 2;
222 tm
->tm_mday
+= tm
->tm_year
* 1461 / 4;
223 tm
->tm_mday
+= ((tm
->tm_mon
/ 5) - 672);
225 t
= ((tm
->tm_mday
* 60 * 60 * 24) +
226 (tm
->tm_hour
* 60 * 60) +
231 if (t
== (time_t) -1) return 0;
238 parse_date(unsigned char **date_pos
, unsigned char *end
,
239 int update_pos
, int skip_week_day
)
241 #define skip_time_sep(date, end) \
243 const unsigned char *start = (date); \
244 while ((!(end) || (date) < (end)) \
245 && (*(date) == ' ' || *(date) == '-')) \
247 if (date == start) return 0; \
251 const unsigned char *date
= (const unsigned char *) *date_pos
;
256 /* Skip day-of-week */
257 for (; (!end
|| date
< end
) && *date
!= ' '; date
++)
258 if (!*date
) return 0;
260 /* As pasky said who cares if we allow '-'s here? */
261 skip_time_sep(date
, end
);
264 if (isdigit(*date
)) {
265 /* RFC 1036 / RFC 1123 */
270 tm
.tm_mday
= parse_day(&date
, end
);
271 if (tm
.tm_mday
> 31) return 0;
273 skip_time_sep(date
, end
);
277 tm
.tm_mon
= parse_month(&date
, end
);
278 if (tm
.tm_mon
< 0) return 0;
280 skip_time_sep(date
, end
);
284 tm
.tm_year
= parse_year(&date
, end
);
285 if (tm
.tm_year
< 0) return 0;
287 skip_time_sep(date
, end
);
291 if (!parse_time(&date
, &tm
, end
)) return 0;
294 /* ANSI C's asctime() format */
298 tm
.tm_mon
= parse_month(&date
, end
);
299 if (tm
.tm_mon
< 0) return 0;
301 /* I know, we shouldn't allow '-', but who cares ;). --pasky */
302 skip_time_sep(date
, end
);
306 tm
.tm_mday
= parse_day(&date
, end
);
307 if (tm
.tm_mday
> 31) return 0;
309 skip_time_sep(date
, end
);
313 if (!parse_time(&date
, &tm
, end
)) return 0;
315 skip_time_sep(date
, end
);
319 tm
.tm_year
= parse_year(&date
, end
);
320 if (tm
.tm_year
< 0) return 0;
325 *date_pos
= (unsigned char *) date
;
327 return (time_t) my_timegm(&tm
);