2 +--------------------------------------------------------------------+
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
20 #include "php_http_api.h"
21 #include "php_http_date_api.h"
23 ZEND_EXTERN_MODULE_GLOBALS(http
);
25 static inline int check_day(const char *day
, size_t len
);
26 static inline int check_month(const char *month
);
27 static inline int check_tzone(const char *tzone
);
28 static inline time_t parse_date(const char *month
);
30 /* {{{ day/month names */
31 static const char *days
[] = {
32 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
34 static const char *wkdays
[] = {
35 "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
37 static const char *weekdays
[] = {
38 "Monday", "Tuesday", "Wednesday",
39 "Thursday", "Friday", "Saturday", "Sunday"
41 static const char *months
[] = {
42 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
43 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
51 static const struct time_zone
{
55 {"GMT", 0}, /* Greenwich Mean */
56 {"UTC", 0}, /* Universal (Coordinated) */
57 {"WET", 0}, /* Western European */
58 {"BST", 0 DS
}, /* British Summer */
59 {"WAT", 60}, /* West Africa */
60 {"AST", 240}, /* Atlantic Standard */
61 {"ADT", 240 DS
},/* Atlantic Daylight */
62 {"EST", 300}, /* Eastern Standard */
63 {"EDT", 300 DS
},/* Eastern Daylight */
64 {"CST", 360}, /* Central Standard */
65 {"CDT", 360 DS
},/* Central Daylight */
66 {"MST", 420}, /* Mountain Standard */
67 {"MDT", 420 DS
},/* Mountain Daylight */
68 {"PST", 480}, /* Pacific Standard */
69 {"PDT", 480 DS
},/* Pacific Daylight */
70 {"YST", 540}, /* Yukon Standard */
71 {"YDT", 540 DS
},/* Yukon Daylight */
72 {"HST", 600}, /* Hawaii Standard */
73 {"HDT", 600 DS
},/* Hawaii Daylight */
74 {"CAT", 600}, /* Central Alaska */
75 {"AHST", 600}, /* Alaska-Hawaii Standard */
76 {"NT", 660}, /* Nome */
77 {"IDLW", 720}, /* International Date Line West */
78 {"CET", -60}, /* Central European */
79 {"MET", -60}, /* Middle European */
80 {"MEWT", -60}, /* Middle European Winter */
81 {"MEST", -60 DS
},/* Middle European Summer */
82 {"CEST", -60 DS
},/* Central European Summer */
83 {"MESZ", -60 DS
},/* Middle European Summer */
84 {"FWT", -60}, /* French Winter */
85 {"FST", -60 DS
},/* French Summer */
86 {"EET", -120}, /* Eastern Europe, USSR Zone 1 */
87 {"WAST", -420}, /* West Australian Standard */
88 {"WADT", -420 DS
},/* West Australian Daylight */
89 {"CCT", -480}, /* China Coast, USSR Zone 7 */
90 {"JST", -540}, /* Japan Standard, USSR Zone 8 */
91 {"EAST", -600}, /* Eastern Australian Standard */
92 {"EADT", -600 DS
},/* Eastern Australian Daylight */
93 {"GST", -600}, /* Guam Standard, USSR Zone 9 */
94 {"NZT", -720}, /* New Zealand */
95 {"NZST", -720}, /* New Zealand Standard */
96 {"NZDT", -720 DS
},/* New Zealand Daylight */
97 {"IDLE", -720}, /* International Date Line East */
101 /* {{{ Day/Month/TZ checks for http_parse_date()
102 Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. */
103 static inline int check_day(const char *day
, size_t len
)
106 const char * const *check
= (len
> 3) ? &weekdays
[0] : &wkdays
[0];
107 for (i
= 0; i
< 7; i
++) {
108 if (!strcmp(day
, check
[0])) {
116 static inline int check_month(const char *month
)
119 const char * const *check
= &months
[0];
120 for (i
= 0; i
< 12; i
++) {
121 if (!strcmp(month
, check
[0])) {
129 /* return the time zone offset between GMT and the input one, in number
130 of seconds or -1 if the timezone wasn't found/legal */
132 static inline int check_tzone(const char *tzone
)
135 const struct time_zone
*check
= time_zones
;
136 for (i
= 0; i
< sizeof(time_zones
) / sizeof(time_zones
[0]); i
++) {
137 if (!strcmp(tzone
, check
->name
)) {
138 return check
->offset
* 60;
146 /* {{{ char *http_date(time_t) */
147 PHP_HTTP_API
char *_http_date(time_t t TSRMLS_DC
)
149 struct tm
*gmtime
, tmbuf
;
151 if ((gmtime
= php_gmtime_r(&t
, &tmbuf
))) {
152 char *date
= ecalloc(1, 31);
154 "%s, %02d %s %04d %02d:%02d:%02d GMT",
155 days
[gmtime
->tm_wday
], gmtime
->tm_mday
,
156 months
[gmtime
->tm_mon
], gmtime
->tm_year
+ 1900,
157 gmtime
->tm_hour
, gmtime
->tm_min
, gmtime
->tm_sec
166 /* {{{ time_t http_parse_date(char *) */
167 PHP_HTTP_API
time_t _http_parse_date(const char *date TSRMLS_DC
)
172 /* fix odd offsets with Win32 */
173 char tzput
[64] = "TZ=";
174 const char *tzget
= NULL
;
176 if ((tzget
= getenv("TZ"))) {
177 strlcat(tzput
, tzget
, 63);
182 t
= parse_date(date
);
189 http_error_ex(HE_NOTICE
, HTTP_E_RUNTIME
, "Could not parse date: %s", date
);
196 /* time_t parse_date(char *)
197 Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. */
198 static inline time_t parse_date(const char *date
)
201 int tz_offset
= -1, year
= -1, month
= -1, monthday
= -1, weekday
= -1,
202 hours
= -1, minutes
= -1, seconds
= -1;
204 enum assume_next dignext
= DATE_MDAY
;
205 const char *indate
= date
;
207 int part
= 0; /* max 6 parts */
209 while (*date
&& (part
< 6)) {
212 while (*date
&& !isalnum(*date
)) {
216 if (isalpha(*date
)) {
217 /* a name coming up */
220 sscanf(date
, "%31[A-Za-z]", buf
);
224 weekday
= check_day(buf
, len
);
230 if (!found
&& (month
== -1)) {
231 month
= check_month(buf
);
237 if (!found
&& (tz_offset
== -1)) {
238 /* this just must be a time zone string */
239 tz_offset
= check_tzone(buf
);
240 if (tz_offset
!= -1) {
246 return -1; /* bad string */
250 else if (isdigit(*date
)) {
254 if ((seconds
== -1) &&
255 (3 == sscanf(date
, "%02d:%02d:%02d", &hours
, &minutes
, &seconds
))) {
261 val
= (int) strtol(date
, &end
, 10);
263 if ((tz_offset
== -1) && ((end
- date
) == 4) && (val
< 1300) &&
264 (indate
< date
) && ((date
[-1] == '+' || date
[-1] == '-'))) {
265 /* four digits and a value less than 1300 and it is preceeded with
266 a plus or minus. This is a time zone indication. */
268 tz_offset
= (val
/ 100 * 60 + val
% 100) * 60;
270 /* the + and - prefix indicates the local time compared to GMT,
271 this we need ther reversed math to get what we want */
272 tz_offset
= date
[-1] == '+' ? -tz_offset
: tz_offset
;
275 if (((end
- date
) == 8) && (year
== -1) && (month
== -1) && (monthday
== -1)) {
276 /* 8 digits, no year, month or day yet. This is YYYYMMDD */
279 month
= (val
% 10000) / 100 - 1; /* month is 0 - 11 */
280 monthday
= val
% 100;
283 if (!found
&& (dignext
== DATE_MDAY
) && (monthday
== -1)) {
284 if ((val
> 0) && (val
< 32)) {
291 if (!found
&& (dignext
== DATE_YEAR
) && (year
== -1)) {
295 year
+= year
> 70 ? 1900 : 2000;
314 seconds
= minutes
= hours
= 0; /* no time, make it zero */
317 if ((-1 == monthday
) || (-1 == month
) || (-1 == year
)) {
318 /* lacks vital info, fail */
322 if (sizeof(time_t) < 5) {
323 /* 32 bit time_t can only hold dates to the beginning of 2038 */
332 tm
.tm_mday
= monthday
;
334 tm
.tm_year
= year
- 1900;
341 /* time zone adjust */
343 struct tm
*gmt
, keeptime2
;
347 if(!(gmt
= php_gmtime_r(&t
, &keeptime2
))) {
348 return -1; /* illegal date/time */
353 /* Add the time zone diff (between the given timezone and GMT) and the
354 diff between the local time zone and GMT. */
355 delta
= (tz_offset
!= -1 ? tz_offset
: 0) + (t
- t2
);
357 if((delta
> 0) && (t
+ delta
< t
)) {
358 return -1; /* time_t overflow */
374 * vim600: sw=4 ts=4 fdm=marker