- fix bitmask checking
[m6w6/ext-http] / http_date_api.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21 #include "php.h"
22
23 #include "php_http.h"
24 #include "php_http_std_defs.h"
25
26 #include <ctype.h>
27
28 static int check_day(char *day, size_t len);
29 static int check_month(char *month);
30 static int check_tzone(char *tzone);
31
32 /* {{{ day/month names */
33 static const char *days[] = {
34 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
35 };
36 static const char *wkdays[] = {
37 "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
38 };
39 static const char *weekdays[] = {
40 "Monday", "Tuesday", "Wednesday",
41 "Thursday", "Friday", "Saturday", "Sunday"
42 };
43 static const char *months[] = {
44 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
45 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
46 };
47 enum assume_next {
48 DATE_MDAY,
49 DATE_YEAR,
50 DATE_TIME
51 };
52 #define DS -60
53 static const struct time_zone {
54 const char *name;
55 const int offset;
56 } time_zones[] = {
57 {"GMT", 0}, /* Greenwich Mean */
58 {"UTC", 0}, /* Universal (Coordinated) */
59 {"WET", 0}, /* Western European */
60 {"BST", 0 DS}, /* British Summer */
61 {"WAT", 60}, /* West Africa */
62 {"AST", 240}, /* Atlantic Standard */
63 {"ADT", 240 DS},/* Atlantic Daylight */
64 {"EST", 300}, /* Eastern Standard */
65 {"EDT", 300 DS},/* Eastern Daylight */
66 {"CST", 360}, /* Central Standard */
67 {"CDT", 360 DS},/* Central Daylight */
68 {"MST", 420}, /* Mountain Standard */
69 {"MDT", 420 DS},/* Mountain Daylight */
70 {"PST", 480}, /* Pacific Standard */
71 {"PDT", 480 DS},/* Pacific Daylight */
72 {"YST", 540}, /* Yukon Standard */
73 {"YDT", 540 DS},/* Yukon Daylight */
74 {"HST", 600}, /* Hawaii Standard */
75 {"HDT", 600 DS},/* Hawaii Daylight */
76 {"CAT", 600}, /* Central Alaska */
77 {"AHST", 600}, /* Alaska-Hawaii Standard */
78 {"NT", 660}, /* Nome */
79 {"IDLW", 720}, /* International Date Line West */
80 {"CET", -60}, /* Central European */
81 {"MET", -60}, /* Middle European */
82 {"MEWT", -60}, /* Middle European Winter */
83 {"MEST", -60 DS},/* Middle European Summer */
84 {"CEST", -60 DS},/* Central European Summer */
85 {"MESZ", -60 DS},/* Middle European Summer */
86 {"FWT", -60}, /* French Winter */
87 {"FST", -60 DS},/* French Summer */
88 {"EET", -120}, /* Eastern Europe, USSR Zone 1 */
89 {"WAST", -420}, /* West Australian Standard */
90 {"WADT", -420 DS},/* West Australian Daylight */
91 {"CCT", -480}, /* China Coast, USSR Zone 7 */
92 {"JST", -540}, /* Japan Standard, USSR Zone 8 */
93 {"EAST", -600}, /* Eastern Australian Standard */
94 {"EADT", -600 DS},/* Eastern Australian Daylight */
95 {"GST", -600}, /* Guam Standard, USSR Zone 9 */
96 {"NZT", -720}, /* New Zealand */
97 {"NZST", -720}, /* New Zealand Standard */
98 {"NZDT", -720 DS},/* New Zealand Daylight */
99 {"IDLE", -720}, /* International Date Line East */
100 };
101 /* }}} */
102
103 /* {{{ Day/Month/TZ checks for http_parse_date()
104 Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. */
105 static int check_day(char *day, size_t len)
106 {
107 int i;
108 const char * const *check = (len > 3) ? &weekdays[0] : &wkdays[0];
109 for (i = 0; i < 7; i++) {
110 if (!strcmp(day, check[0])) {
111 return i;
112 }
113 check++;
114 }
115 return -1;
116 }
117
118 static int check_month(char *month)
119 {
120 int i;
121 const char * const *check = &months[0];
122 for (i = 0; i < 12; i++) {
123 if (!strcmp(month, check[0])) {
124 return i;
125 }
126 check++;
127 }
128 return -1;
129 }
130
131 /* return the time zone offset between GMT and the input one, in number
132 of seconds or -1 if the timezone wasn't found/legal */
133
134 static int check_tzone(char *tzone)
135 {
136 unsigned i;
137 const struct time_zone *check = time_zones;
138 for (i = 0; i < sizeof(time_zones) / sizeof(time_zones[0]); i++) {
139 if (!strcmp(tzone, check->name)) {
140 return check->offset * 60;
141 }
142 check++;
143 }
144 return -1;
145 }
146 /* }}} */
147
148 /* {{{ char *http_date(time_t) */
149 PHP_HTTP_API char *_http_date(time_t t TSRMLS_DC)
150 {
151 struct tm *gmtime, tmbuf;
152
153 if (gmtime = php_gmtime_r(&t, &tmbuf)) {
154 char *date = ecalloc(1, 31);
155 snprintf(date, 30,
156 "%s, %02d %s %04d %02d:%02d:%02d GMT",
157 days[gmtime->tm_wday], gmtime->tm_mday,
158 months[gmtime->tm_mon], gmtime->tm_year + 1900,
159 gmtime->tm_hour, gmtime->tm_min, gmtime->tm_sec
160 );
161 return date;
162 }
163
164 return NULL;
165 }
166 /* }}} */
167
168 /* {{{ time_t http_parse_date(char *)
169 Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. */
170 PHP_HTTP_API time_t _http_parse_date(const char *date)
171 {
172 time_t t = 0;
173 int tz_offset = -1, year = -1, month = -1, monthday = -1, weekday = -1,
174 hours = -1, minutes = -1, seconds = -1;
175 struct tm tm;
176 enum assume_next dignext = DATE_MDAY;
177 const char *indate = date;
178
179 int part = 0; /* max 6 parts */
180
181 while (*date && (part < 6)) {
182 int found = 0;
183
184 while (*date && !isalnum(*date)) {
185 date++;
186 }
187
188 if (isalpha(*date)) {
189 /* a name coming up */
190 char buf[32] = "";
191 size_t len;
192 sscanf(date, "%31[A-Za-z]", buf);
193 len = strlen(buf);
194
195 if (weekday == -1) {
196 weekday = check_day(buf, len);
197 if (weekday != -1) {
198 found = 1;
199 }
200 }
201
202 if (!found && (month == -1)) {
203 month = check_month(buf);
204 if (month != -1) {
205 found = 1;
206 }
207 }
208
209 if (!found && (tz_offset == -1)) {
210 /* this just must be a time zone string */
211 tz_offset = check_tzone(buf);
212 if (tz_offset != -1) {
213 found = 1;
214 }
215 }
216
217 if (!found) {
218 return -1; /* bad string */
219 }
220 date += len;
221 }
222 else if (isdigit(*date)) {
223 /* a digit */
224 int val;
225 char *end;
226 if ((seconds == -1) &&
227 (3 == sscanf(date, "%02d:%02d:%02d", &hours, &minutes, &seconds))) {
228 /* time stamp! */
229 date += 8;
230 found = 1;
231 }
232 else {
233 val = (int) strtol(date, &end, 10);
234
235 if ((tz_offset == -1) && ((end - date) == 4) && (val < 1300) &&
236 (indate < date) && ((date[-1] == '+' || date[-1] == '-'))) {
237 /* four digits and a value less than 1300 and it is preceeded with
238 a plus or minus. This is a time zone indication. */
239 found = 1;
240 tz_offset = (val / 100 * 60 + val % 100) * 60;
241
242 /* the + and - prefix indicates the local time compared to GMT,
243 this we need ther reversed math to get what we want */
244 tz_offset = date[-1] == '+' ? -tz_offset : tz_offset;
245 }
246
247 if (((end - date) == 8) && (year == -1) && (month == -1) && (monthday == -1)) {
248 /* 8 digits, no year, month or day yet. This is YYYYMMDD */
249 found = 1;
250 year = val / 10000;
251 month = (val % 10000) / 100 - 1; /* month is 0 - 11 */
252 monthday = val % 100;
253 }
254
255 if (!found && (dignext == DATE_MDAY) && (monthday == -1)) {
256 if ((val > 0) && (val < 32)) {
257 monthday = val;
258 found = 1;
259 }
260 dignext = DATE_YEAR;
261 }
262
263 if (!found && (dignext == DATE_YEAR) && (year == -1)) {
264 year = val;
265 found = 1;
266 if (year < 1900) {
267 year += year > 70 ? 1900 : 2000;
268 }
269 if(monthday == -1) {
270 dignext = DATE_MDAY;
271 }
272 }
273
274 if (!found) {
275 return -1;
276 }
277
278 date = end;
279 }
280 }
281
282 part++;
283 }
284
285 if (-1 == seconds) {
286 seconds = minutes = hours = 0; /* no time, make it zero */
287 }
288
289 if ((-1 == monthday) || (-1 == month) || (-1 == year)) {
290 /* lacks vital info, fail */
291 return -1;
292 }
293
294 if (sizeof(time_t) < 5) {
295 /* 32 bit time_t can only hold dates to the beginning of 2038 */
296 if (year > 2037) {
297 return 0x7fffffff;
298 }
299 }
300
301 tm.tm_sec = seconds;
302 tm.tm_min = minutes;
303 tm.tm_hour = hours;
304 tm.tm_mday = monthday;
305 tm.tm_mon = month;
306 tm.tm_year = year - 1900;
307 tm.tm_wday = 0;
308 tm.tm_yday = 0;
309 tm.tm_isdst = 0;
310
311 t = mktime(&tm);
312
313 /* time zone adjust */
314 if (t != -1) {
315 struct tm *gmt, keeptime2;
316 long delta;
317 time_t t2;
318
319 if(!(gmt = php_gmtime_r(&t, &keeptime2))) {
320 return -1; /* illegal date/time */
321 }
322
323 t2 = mktime(gmt);
324
325 /* Add the time zone diff (between the given timezone and GMT) and the
326 diff between the local time zone and GMT. */
327 delta = (tz_offset != -1 ? tz_offset : 0) + (t - t2);
328
329 if((delta > 0) && (t + delta < t)) {
330 return -1; /* time_t overflow */
331 }
332
333 t += delta;
334 }
335
336 return t;
337 }
338 /* }}} */
339
340
341 /*
342 * Local variables:
343 * tab-width: 4
344 * c-basic-offset: 4
345 * End:
346 * vim600: sw=4 ts=4 fdm=marker
347 * vim<600: sw=4 ts=4
348 */
349