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