1 /* $LynxId: LYmktime.c,v 1.14 2013/05/03 20:14:06 tom Exp $ */
2
3 #include <LYStrings.h>
4 #include <LYUtils.h>
5
6 #include <parsdate.h>
7
8 #ifdef TEST_DRIVER
LYstrncpy(char * dst,const char * src,int n)9 char *LYstrncpy(char *dst,
10 const char *src,
11 int n)
12 {
13 char *val;
14 int len;
15
16 if (src == 0)
17 src = "";
18 len = strlen(src);
19
20 if (n < 0)
21 n = 0;
22
23 val = StrNCpy(dst, src, n);
24 if (len < n)
25 *(dst + len) = '\0';
26 else
27 *(dst + n) = '\0';
28 return val;
29 }
30 #define strcasecomp strcasecmp
31 BOOLEAN WWW_TraceFlag = FALSE;
TraceFP(void)32 FILE *TraceFP(void)
33 {
34 return stderr;
35 }
36 #define USE_PARSDATE 0
37 #else
38 #define USE_PARSDATE 1
39 #endif
40
41 /*
42 * This function takes a string in the format
43 * "Mon, 01-Jan-96 13:45:35 GMT" or
44 * "Mon, 1 Jan 1996 13:45:35 GMT" or
45 * "dd-mm-yyyy"
46 * as an argument, and returns its conversion to clock format (seconds since
47 * 00:00:00 Jan 1 1970), or 0 if the string doesn't match the expected pattern.
48 * It also returns 0 if the time is in the past and the "absolute" argument is
49 * FALSE. It is intended for handling 'expires' strings in Version 0 cookies
50 * homologously to 'max-age' strings in Version 1 cookies, for which 0 is the
51 * minimum, and greater values are handled as '[max-age seconds] + time(NULL)'.
52 * If "absolute" if TRUE, we return the clock format value itself, but if
53 * anything goes wrong when parsing the expected patterns, we still return 0.
54 * - FM
55 */
LYmktime(char * string,int absolute)56 time_t LYmktime(char *string,
57 int absolute)
58 {
59 #if USE_PARSDATE
60 time_t result = 0;
61
62 if (non_empty(string)) {
63 CTRACE((tfp, "LYmktime: Parsing '%s'\n", string));
64 result = parsedate(string, 0);
65
66 if (!absolute) {
67 if ((long) (time((time_t *) 0) - result) >= 0)
68 result = 0;
69 }
70 if (result != 0) {
71 CTRACE((tfp, "LYmktime: clock=%" PRI_time_t ", ctime=%s",
72 CAST_time_t (result),
73 ctime(&result)));
74 }
75 }
76 return result;
77 #else
78 char *s;
79 time_t now, clock2;
80 int day, month, year, hour, minutes, seconds;
81 char *start;
82 char temp[8];
83
84 /*
85 * Make sure we have a string to parse. - FM
86 */
87 if (!non_empty(string))
88 return (0);
89 s = string;
90 CTRACE((tfp, "LYmktime: Parsing '%s'\n", s));
91
92 /*
93 * Skip any lead alphabetic "Day, " field and seek a numeric day field. -
94 * FM
95 */
96 while (*s != '\0' && !isdigit(UCH(*s)))
97 s++;
98 if (*s == '\0')
99 return (0);
100
101 /*
102 * Get the numeric day and convert to an integer. - FM
103 */
104 start = s;
105 while (*s != '\0' && isdigit(UCH(*s)))
106 s++;
107 if (*s == '\0' || (s - start) > 2)
108 return (0);
109 LYStrNCpy(temp, start, (s - start));
110 day = atoi(temp);
111 if (day < 1 || day > 31)
112 return (0);
113
114 /*
115 * Get the month string and convert to an integer. - FM
116 */
117 while (*s != '\0' && !isalnum(UCH(*s)))
118 s++;
119 if (*s == '\0')
120 return (0);
121 start = s;
122 while (*s != '\0' && isalnum(UCH(*s)))
123 s++;
124 if ((*s == '\0') ||
125 (s - start) < (isdigit(UCH(*(s - 1))) ? 2 : 3) ||
126 (s - start) > (isdigit(UCH(*(s - 1))) ? 2 : 9))
127 return (0);
128 LYStrNCpy(temp, start, (isdigit(UCH(*(s - 1))) ? 2 : 3));
129 switch (TOUPPER(temp[0])) {
130 case '0':
131 case '1':
132 month = atoi(temp);
133 if (month < 1 || month > 12) {
134 return (0);
135 }
136 break;
137 case 'A':
138 if (!strcasecomp(temp, "Apr")) {
139 month = 4;
140 } else if (!strcasecomp(temp, "Aug")) {
141 month = 8;
142 } else {
143 return (0);
144 }
145 break;
146 case 'D':
147 if (!strcasecomp(temp, "Dec")) {
148 month = 12;
149 } else {
150 return (0);
151 }
152 break;
153 case 'F':
154 if (!strcasecomp(temp, "Feb")) {
155 month = 2;
156 } else {
157 return (0);
158 }
159 break;
160 case 'J':
161 if (!strcasecomp(temp, "Jan")) {
162 month = 1;
163 } else if (!strcasecomp(temp, "Jun")) {
164 month = 6;
165 } else if (!strcasecomp(temp, "Jul")) {
166 month = 7;
167 } else {
168 return (0);
169 }
170 break;
171 case 'M':
172 if (!strcasecomp(temp, "Mar")) {
173 month = 3;
174 } else if (!strcasecomp(temp, "May")) {
175 month = 5;
176 } else {
177 return (0);
178 }
179 break;
180 case 'N':
181 if (!strcasecomp(temp, "Nov")) {
182 month = 11;
183 } else {
184 return (0);
185 }
186 break;
187 case 'O':
188 if (!strcasecomp(temp, "Oct")) {
189 month = 10;
190 } else {
191 return (0);
192 }
193 break;
194 case 'S':
195 if (!strcasecomp(temp, "Sep")) {
196 month = 9;
197 } else {
198 return (0);
199 }
200 break;
201 default:
202 return (0);
203 }
204
205 /*
206 * Get the numeric year string and convert to an integer. - FM
207 */
208 while (*s != '\0' && !isdigit(UCH(*s)))
209 s++;
210 if (*s == '\0')
211 return (0);
212 start = s;
213 while (*s != '\0' && isdigit(UCH(*s)))
214 s++;
215 if ((s - start) == 4) {
216 LYStrNCpy(temp, start, 4);
217 } else if ((s - start) == 2) {
218 now = time(NULL);
219 /*
220 * Assume that received 2-digit dates >= 70 are 19xx; others
221 * are 20xx. Only matters when dealing with broken software
222 * (HTTP server or web page) which is not Y2K compliant. The
223 * line is drawn on a best-guess basis; it is impossible for
224 * this to be completely accurate because it depends on what
225 * the broken sender software intends. (This totally breaks
226 * in 2100 -- setting up the next crisis...) - BL
227 */
228 if (atoi(start) >= 70)
229 LYStrNCpy(temp, "19", 2);
230 else
231 LYStrNCpy(temp, "20", 2);
232 strncat(temp, start, 2);
233 temp[4] = '\0';
234 } else {
235 return (0);
236 }
237 year = atoi(temp);
238
239 /*
240 * Get the numeric hour string and convert to an integer. - FM
241 */
242 while (*s != '\0' && !isdigit(UCH(*s)))
243 s++;
244 if (*s == '\0') {
245 hour = 0;
246 minutes = 0;
247 seconds = 0;
248 } else {
249 start = s;
250 while (*s != '\0' && isdigit(UCH(*s)))
251 s++;
252 if (*s != ':' || (s - start) > 2)
253 return (0);
254 LYStrNCpy(temp, start, (s - start));
255 hour = atoi(temp);
256
257 /*
258 * Get the numeric minutes string and convert to an integer. - FM
259 */
260 while (*s != '\0' && !isdigit(UCH(*s)))
261 s++;
262 if (*s == '\0')
263 return (0);
264 start = s;
265 while (*s != '\0' && isdigit(UCH(*s)))
266 s++;
267 if (*s != ':' || (s - start) > 2)
268 return (0);
269 LYStrNCpy(temp, start, (s - start));
270 minutes = atoi(temp);
271
272 /*
273 * Get the numeric seconds string and convert to an integer. - FM
274 */
275 while (*s != '\0' && !isdigit(UCH(*s)))
276 s++;
277 if (*s == '\0')
278 return (0);
279 start = s;
280 while (*s != '\0' && isdigit(UCH(*s)))
281 s++;
282 if (*s == '\0' || (s - start) > 2)
283 return (0);
284 LYStrNCpy(temp, start, (s - start));
285 seconds = atoi(temp);
286 }
287
288 /*
289 * Convert to clock format (seconds since 00:00:00 Jan 1 1970), but then
290 * zero it if it's in the past and "absolute" is not TRUE. - FM
291 */
292 month -= 3;
293 if (month < 0) {
294 month += 12;
295 year--;
296 }
297 day += (year - 1968) * 1461 / 4;
298 day += ((((month * 153) + 2) / 5) - 672);
299 clock2 = (time_t) ((day * 60 * 60 * 24) +
300 (hour * 60 * 60) +
301 (minutes * 60) +
302 seconds);
303 if (absolute == FALSE && (long) (time((time_t *) 0) - clock2) >= 0)
304 clock2 = (time_t) 0;
305 if (clock2 > 0)
306 CTRACE((tfp, "LYmktime: clock=%" PRI_time_t ", ctime=%s",
307 CAST_time_t (clock2),
308 ctime(&clock2)));
309
310 return (clock2);
311 #endif
312 }
313
314 #ifdef TEST_DRIVER
test_mktime(char * source)315 static void test_mktime(char *source)
316 {
317 time_t before = LYmktime(source, TRUE);
318 time_t after = parsedate(source, 0);
319
320 printf("TEST %s\n", source);
321 printf("\t%" PRI_time_t " %s", CAST_time_t (before), ctime(&before));
322 printf("\t%" PRI_time_t " %s", CAST_time_t (after), ctime(&after));
323
324 if (before != after)
325 printf("\t****\n");
326 }
327
main(void)328 int main(void)
329 {
330 test_mktime("Mon, 01-Jan-96 13:45:35 GMT");
331 test_mktime("Mon, 1 Jan 1996 13:45:35 GMT");
332 test_mktime("31-12-1999");
333 test_mktime("Wed May 14 22:00:00 2008");
334 test_mktime("Sun, 29-Jun-2008 23:19:30 GMT");
335 test_mktime("Sun Jul 06 07:00:00 2008 GMT");
336 return 0;
337 }
338 #endif
339