Import Debian changes 0.66.4-9
[hcoop/debian/courier-authlib.git] / libs / rfc822 / rfc822_parsedt.c
CommitLineData
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
30static 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
42static const char * const weekdays[7]={
43 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
44 } ;
45
46static 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
55static 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
58static const char * const zonenames[] = {
59 "UT","GMT",
60 "EST","EDT",
61 "CST","CDT",
62 "MST","MDT",
63 "PST","PDT",
64 "Z",
d50284c4 65 "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M",
d9898ee8 66 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
67 NULL};
68
69#define ZH(n) ( (n) * 60 * 60 )
70
71static 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 84static unsigned parsekey(const char **mon, const char * const *ary)
85{
86unsigned 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
102static 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
d50284c4 125int rfc822_parsedate_chk(const char *rfcdt, time_t *tret)
d9898ee8 126{
d50284c4
CE
127 unsigned day=0, mon=0, year;
128 int secs;
129 int offset;
130 time_t t;
131 unsigned y;
132
133 *tret=0;
d9898ee8 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 {
d50284c4 141 if (!*rfcdt) return (-1);
b0322a85 142 if (my_isalpha(*rfcdt))
d9898ee8 143 {
d50284c4 144 if (mon) return (-1);
d9898ee8 145 mon=parsekey(&rfcdt, mnames);
146 if (!mon)
b0322a85 147 while (*rfcdt && my_isalpha(*rfcdt))
d9898ee8 148 ++rfcdt;
149 continue;
150 }
151
b0322a85 152 if (my_isdigit(*rfcdt))
d9898ee8 153 {
d50284c4 154 if (day) return (-1);
d9898ee8 155 day=parsedig(&rfcdt);
d50284c4 156 if (!day) return (-1);
d9898ee8 157 continue;
158 }
159 ++rfcdt;
160 }
161
b0322a85 162 while (*rfcdt && my_isspace(*rfcdt))
d9898ee8 163 ++rfcdt;
d50284c4 164 if (!my_isdigit(*rfcdt)) return (-1);
d9898ee8 165 year=parsedig(&rfcdt);
166 if (year < 70) year += 2000;
167 if (year < 100) year += 1900;
168
b0322a85 169 while (*rfcdt && my_isspace(*rfcdt))
d9898ee8 170 ++rfcdt;
171
172 if (day == 0 || mon == 0 || mon > 12 || day > mdays(mon,year))
d50284c4 173 return (-1);
d9898ee8 174
175 secs=parsetime(&rfcdt);
d50284c4 176 if (secs < 0) return (-1);
d9898ee8 177
178 offset=0;
179
180 /* RFC822 sez no parenthesis, but I've seen (EST) */
181
182 while ( *rfcdt )
183 {
b0322a85 184 if (my_isalnum(*rfcdt) || *rfcdt == '+' || *rfcdt == '-')
d9898ee8 185 break;
186 ++rfcdt;
187 }
188
b0322a85 189 if (my_isalpha((int)(unsigned char)*rfcdt))
d9898ee8 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
b0322a85 207 if (my_isdigit(*rfcdt))
d9898ee8 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
d50284c4
CE
215 if (year < 1970) return (-1);
216 if (year > 9999) return (-1);
d9898ee8 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
d50284c4
CE
237 *tret = ( t + (day-1) * 24 * 60 * 60 + secs - offset );
238 return 0;
d9898ee8 239}
240
241const char *rfc822_mkdt(time_t t)
242{
243static char buf[80];
244struct 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}