d9898ee8 |
1 | /* |
2 | ** Copyright 1998 - 1999 Double Precision, Inc. |
3 | ** See COPYING for distribution information. |
4 | */ |
5 | |
6 | /* |
7 | ** $Id: rfc822_parsedt.c,v 1.4 2002/05/21 16:02:19 mrsam Exp $ |
8 | */ |
9 | #include <stdio.h> |
10 | #include <string.h> |
11 | #include <ctype.h> |
12 | #include <time.h> |
13 | |
14 | /* |
15 | ** time_t rfc822_parsedate(const char *p) |
16 | ** |
17 | ** p - contents of the Date: header, attempt to parse it into a time_t. |
18 | ** |
19 | ** returns - time_t, or 0 if the date cannot be parsed |
20 | */ |
21 | |
22 | static unsigned parsedig(const char **p) |
23 | { |
24 | unsigned i=0; |
25 | |
26 | while (isdigit((int)(unsigned char)**p)) |
27 | { |
28 | i=i*10 + **p - '0'; |
29 | ++*p; |
30 | } |
31 | return (i); |
32 | } |
33 | |
34 | static const char * const weekdays[7]={ |
35 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" |
36 | } ; |
37 | |
38 | static const char * const mnames[13]={ |
39 | "Jan", "Feb", "Mar", "Apr", |
40 | "May", "Jun", "Jul", "Aug", |
41 | "Sep", "Oct", "Nov", "Dec", NULL}; |
42 | |
43 | #define leap(y) ( \ |
44 | ((y) % 400) == 0 || \ |
45 | (((y) % 4) == 0 && (y) % 100) ) |
46 | |
47 | static unsigned mlength[]={31,28,31,30,31,30,31,31,30,31,30,31}; |
48 | #define mdays(m,y) ( (m) != 2 ? mlength[(m)-1] : leap(y) ? 29:28) |
49 | |
50 | static const char * const zonenames[] = { |
51 | "UT","GMT", |
52 | "EST","EDT", |
53 | "CST","CDT", |
54 | "MST","MDT", |
55 | "PST","PDT", |
56 | "Z", |
57 | "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", |
58 | "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", |
59 | NULL}; |
60 | |
61 | #define ZH(n) ( (n) * 60 * 60 ) |
62 | |
63 | static int zoneoffset[] = { |
64 | 0, 0, |
65 | ZH(-5), ZH(-4), |
66 | ZH(-6), ZH(-5), |
67 | ZH(-7), ZH(-6), |
68 | ZH(-8), ZH(-7), |
69 | 0, |
70 | |
71 | ZH(-1), ZH(-2), ZH(-3), ZH(-4), ZH(-5), ZH(-6), ZH(-7), ZH(-8), ZH(-9), ZH(-10), ZH(-11), ZH(-12), |
72 | ZH(1), ZH(2), ZH(3), ZH(4), ZH(5), ZH(6), ZH(7), ZH(8), ZH(9), ZH(10), ZH(11), ZH(12) }; |
73 | |
74 | static unsigned parsekey(const char **mon, const char * const *ary) |
75 | { |
76 | unsigned m, j; |
77 | |
78 | for (m=0; ary[m]; m++) |
79 | { |
80 | for (j=0; ary[m][j]; j++) |
81 | if (tolower(ary[m][j]) != tolower((*mon)[j])) |
82 | break; |
83 | if (!ary[m][j]) |
84 | { |
85 | *mon += j; |
86 | return (m+1); |
87 | } |
88 | } |
89 | return (0); |
90 | } |
91 | |
92 | static int parsetime(const char **t) |
93 | { |
94 | unsigned h,m,s=0; |
95 | |
96 | if (!isdigit((int)(unsigned char)**t)) return (-1); |
97 | h=parsedig(t); |
98 | if (h > 23) return (-1); |
99 | if (**t != ':') return (-1); |
100 | ++*t; |
101 | if (!isdigit((int)(unsigned char)**t)) return (-1); |
102 | m=parsedig(t); |
103 | if (**t == ':') |
104 | { |
105 | ++*t; |
106 | if (!isdigit((int)(unsigned char)**t)) return (-1); |
107 | s=parsedig(t); |
108 | } |
109 | if (m > 59 || s > 59) return (-1); |
110 | return (h * 60 * 60 + m * 60 + s); |
111 | } |
112 | |
113 | time_t rfc822_parsedt(const char *rfcdt) |
114 | { |
115 | unsigned day=0, mon=0, year; |
116 | int secs; |
117 | int offset; |
118 | time_t t; |
119 | unsigned y; |
120 | |
121 | /* Ignore day of the week. Tolerate "Tue, 25 Feb 1997 ... " |
122 | ** without the comma. Tolerate "Feb 25 1997 ...". |
123 | */ |
124 | |
125 | while (!day || !mon) |
126 | { |
127 | if (!*rfcdt) return (0); |
128 | if (isalpha((int)(unsigned char)*rfcdt)) |
129 | { |
130 | if (mon) return (0); |
131 | mon=parsekey(&rfcdt, mnames); |
132 | if (!mon) |
133 | while (*rfcdt && isalpha((int)(unsigned char)*rfcdt)) |
134 | ++rfcdt; |
135 | continue; |
136 | } |
137 | |
138 | if (isdigit((int)(unsigned char)*rfcdt)) |
139 | { |
140 | if (day) return (0); |
141 | day=parsedig(&rfcdt); |
142 | if (!day) return (0); |
143 | continue; |
144 | } |
145 | ++rfcdt; |
146 | } |
147 | |
148 | while (*rfcdt && isspace((int)(unsigned char)*rfcdt)) |
149 | ++rfcdt; |
150 | if (!isdigit((int)(unsigned char)*rfcdt)) return (0); |
151 | year=parsedig(&rfcdt); |
152 | if (year < 70) year += 2000; |
153 | if (year < 100) year += 1900; |
154 | |
155 | while (*rfcdt && isspace((int)(unsigned char)*rfcdt)) |
156 | ++rfcdt; |
157 | |
158 | if (day == 0 || mon == 0 || mon > 12 || day > mdays(mon,year)) |
159 | return (0); |
160 | |
161 | secs=parsetime(&rfcdt); |
162 | if (secs < 0) return (0); |
163 | |
164 | offset=0; |
165 | |
166 | /* RFC822 sez no parenthesis, but I've seen (EST) */ |
167 | |
168 | while ( *rfcdt ) |
169 | { |
170 | if (isalnum((int)(unsigned char)*rfcdt) || *rfcdt == '+' || *rfcdt == '-') |
171 | break; |
172 | ++rfcdt; |
173 | } |
174 | |
175 | if (isalpha((int)(unsigned char)*rfcdt)) |
176 | { |
177 | int n=parsekey(&rfcdt, zonenames); |
178 | |
179 | if (n > 0) offset= zoneoffset[n-1]; |
180 | } |
181 | else |
182 | { |
183 | int sign=1; |
184 | unsigned n; |
185 | |
186 | switch (*rfcdt) { |
187 | case '-': |
188 | sign= -1; |
189 | case '+': |
190 | ++rfcdt; |
191 | } |
192 | |
193 | if (isdigit((int)(unsigned char)*rfcdt)) |
194 | { |
195 | n=parsedig(&rfcdt); |
196 | if (n > 2359 || (n % 100) > 59) n=0; |
197 | offset = sign * ( (n % 100) * 60 + n / 100 * 60 * 60); |
198 | } |
199 | } |
200 | |
201 | if (year < 1970) return (0); |
202 | if (year > 9999) return (0); |
203 | |
204 | t=0; |
205 | for (y=1970; y<year; y++) |
206 | { |
207 | if ( leap(y) ) |
208 | { |
209 | if (year-y >= 4) |
210 | { |
211 | y += 3; |
212 | t += ( 365*3+366 ) * 24 * 60 * 60; |
213 | continue; |
214 | } |
215 | t += 24 * 60 * 60; |
216 | } |
217 | t += 365 * 24 * 60 * 60; |
218 | } |
219 | |
220 | for (y=1; y < mon; y++) |
221 | t += mdays(y, year) * 24 * 60 * 60; |
222 | |
223 | return ( t + (day-1) * 24 * 60 * 60 + secs - offset ); |
224 | } |
225 | |
226 | const char *rfc822_mkdt(time_t t) |
227 | { |
228 | static char buf[80]; |
229 | struct tm *tmptr=gmtime(&t); |
230 | |
231 | buf[0]=0; |
232 | if (tmptr) |
233 | { |
234 | sprintf(buf, "%s, %02d %s %04d %02d:%02d:%02d GMT", |
235 | weekdays[tmptr->tm_wday], |
236 | tmptr->tm_mday, |
237 | mnames[tmptr->tm_mon], |
238 | tmptr->tm_year + 1900, |
239 | tmptr->tm_hour, |
240 | tmptr->tm_min, |
241 | tmptr->tm_sec); |
242 | } |
243 | return (buf); |
244 | } |