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