Imported Upstream version 0.63.0
[hcoop/debian/courier-authlib.git] / rfc822 / rfc822_parsedt.c
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 }