1 /*
2 ** Copyright 1998 - 2011 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5 
6 /*
7 */
8 #include	"config.h"
9 #include	<stdio.h>
10 #include	<string.h>
11 #include	<time.h>
12 
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 
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 
parsedig(const char ** p)30 static unsigned parsedig(const char **p)
31 {
32 	unsigned i=0;
33 
34 	while (my_isdigit(**p))
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 
82 #define lc(x) ((x) >= 'A' && (x) <= 'Z' ? (x) + ('a'-'A'):(x))
83 
parsekey(const char ** mon,const char * const * ary)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++)
91 			if (lc(ary[m][j]) != lc((*mon)[j]))
92 				break;
93 		if (!ary[m][j])
94 		{
95 			*mon += j;
96 			return (m+1);
97 		}
98 	}
99 	return (0);
100 }
101 
parsetime(const char ** t)102 static int parsetime(const char **t)
103 {
104 	unsigned h,m,s=0;
105 
106 	if (!my_isdigit(**t))	return (-1);
107 
108 	h=parsedig(t);
109 	if (h > 23)		return (-1);
110 	if (**t != ':')		return (-1);
111 	++*t;
112 	if (!my_isdigit(**t))	return (-1);
113 	m=parsedig(t);
114 	if (**t == ':')
115 	{
116 		++*t;
117 
118 		if (!my_isdigit(**t))	return (-1);
119 		s=parsedig(t);
120 	}
121 	if (m > 59 || s > 59)	return (-1);
122 	return (h * 60 * 60 + m * 60 + s);
123 }
124 
rfc822_parsedate_chk(const char * rfcdt,time_t * tret)125 int rfc822_parsedate_chk(const char *rfcdt, time_t *tret)
126 {
127 	unsigned day=0, mon=0, year;
128 	int secs;
129 	int offset;
130 	time_t t;
131 	unsigned y;
132 
133 	*tret=0;
134 
135 	/* Ignore day of the week.  Tolerate "Tue, 25 Feb 1997 ... "
136 	** without the comma.  Tolerate "Feb 25 1997 ...".
137 	*/
138 
139 	while (!day || !mon)
140 	{
141 		if (!*rfcdt)	return (-1);
142 		if (my_isalpha(*rfcdt))
143 		{
144 			if (mon)	return (-1);
145 			mon=parsekey(&rfcdt, mnames);
146 			if (!mon)
147 				while (*rfcdt && my_isalpha(*rfcdt))
148 					++rfcdt;
149 			continue;
150 		}
151 
152 		if (my_isdigit(*rfcdt))
153 		{
154 			if (day)	return (-1);
155 			day=parsedig(&rfcdt);
156 			if (!day)	return (-1);
157 			continue;
158 		}
159 		++rfcdt;
160 	}
161 
162 	while (*rfcdt && my_isspace(*rfcdt))
163 		++rfcdt;
164 	if (!my_isdigit(*rfcdt))	return (-1);
165 	year=parsedig(&rfcdt);
166 	if (year < 70)	year += 2000;
167 	if (year < 100)	year += 1900;
168 
169 	while (*rfcdt && my_isspace(*rfcdt))
170 		++rfcdt;
171 
172 	if (day == 0 || mon == 0 || mon > 12 || day > mdays(mon,year))
173 		return (-1);
174 
175 	secs=parsetime(&rfcdt);
176 	if (secs < 0)	return (-1);
177 
178 	offset=0;
179 
180 	/* RFC822 sez no parenthesis, but I've seen (EST) */
181 
182 	while ( *rfcdt )
183 	{
184 		if (my_isalnum(*rfcdt) || *rfcdt == '+' || *rfcdt == '-')
185 			break;
186 		++rfcdt;
187 	}
188 
189 	if (my_isalpha((int)(unsigned char)*rfcdt))
190 	{
191 	int	n=parsekey(&rfcdt, zonenames);
192 
193 		if (n > 0)	offset= zoneoffset[n-1];
194 	}
195 	else
196 	{
197 	int	sign=1;
198 	unsigned n;
199 
200 		switch (*rfcdt)	{
201 		case '-':
202 			sign= -1;
203 		case '+':
204 			++rfcdt;
205 		}
206 
207 		if (my_isdigit(*rfcdt))
208 		{
209 			n=parsedig(&rfcdt);
210 			if (n > 2359 || (n % 100) > 59)	n=0;
211 			offset = sign * ( (n % 100) * 60 + n / 100 * 60 * 60);
212 		}
213 	}
214 
215 	if (year < 1970)	return (-1);
216 	if (year > 9999)	return (-1);
217 
218 	t=0;
219 	for (y=1970; y<year; y++)
220 	{
221 		if ( leap(y) )
222 		{
223 			if (year-y >= 4)
224 			{
225 				y += 3;
226 				t += ( 365*3+366 ) * 24 * 60 * 60;
227 				continue;
228 			}
229 			t += 24 * 60 * 60;
230 		}
231 		t += 365 * 24 * 60 * 60;
232 	}
233 
234 	for (y=1; y < mon; y++)
235 		t += mdays(y, year) * 24 * 60 * 60;
236 
237 	*tret = ( t + (day-1) * 24 * 60 * 60 + secs - offset );
238 	return 0;
239 }
240 
rfc822_mkdt(time_t t)241 const char *rfc822_mkdt(time_t t)
242 {
243 static char buf[80];
244 struct	tm *tmptr=gmtime(&t);
245 
246 	buf[0]=0;
247 	if (tmptr)
248 	{
249 		sprintf(buf, "%s, %02d %s %04d %02d:%02d:%02d GMT",
250 			weekdays[tmptr->tm_wday],
251 			tmptr->tm_mday,
252 			mnames[tmptr->tm_mon],
253 			tmptr->tm_year + 1900,
254 			tmptr->tm_hour,
255 			tmptr->tm_min,
256 			tmptr->tm_sec);
257 	}
258 	return (buf);
259 }
260