Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/lib-mail/rfc822-date.c @ 49:6be018ca51ef HEAD
Year was set wrong in date parser. We'll also now do full checking for date
string to make sure we don't overflow/underflow anything.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 27 Aug 2002 20:35:00 +0300 |
parents | 3b1985cbc908 |
children | 8abce0f7d065 |
rev | line source |
---|---|
0 | 1 /* Copyright (C) 2002 Timo Sirainen */ |
2 | |
3 #include "lib.h" | |
4 #include "gmtoff.h" | |
5 #include "rfc822-date.h" | |
6 #include "rfc822-tokenize.h" | |
7 | |
8 #include <ctype.h> | |
9 | |
10 static const char *month_names[] = { | |
11 "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
12 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | |
13 }; | |
14 | |
15 static const char *weekday_names[] = { | |
16 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
17 }; | |
18 | |
19 static int parse_timezone(const char *str, unsigned int len) | |
20 { | |
21 int offset; | |
22 char chr; | |
23 | |
24 if (len == 5 && (*str == '+' || *str == '-')) { | |
25 /* numeric offset */ | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
26 if (!i_isdigit(str[0]) || !i_isdigit(str[1]) || |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
27 !i_isdigit(str[1]) || !i_isdigit(str[2])) |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
28 return FALSE; |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
29 |
0 | 30 offset = (str[1]-'0') * 1000 + (str[2]-'0') * 100 + |
31 (str[3]-'0') * 10 + (str[4]-'0'); | |
32 return *str == '+' ? offset : -offset; | |
33 } | |
34 | |
35 if (len == 1) { | |
36 /* military zone - handle them the correct way, not as | |
37 RFC822 says. RFC2822 though suggests that they'd be | |
38 considered as unspecified.. */ | |
39 chr = i_toupper(*str); | |
40 if (chr < 'J') | |
41 return (*str-'A'+1) * 60; | |
42 if (chr == 'J') | |
43 return 0; | |
44 if (chr <= 'M') | |
45 return (*str-'A') * 60; | |
46 if (chr < 'Z') | |
47 return ('M'-*str) * 60; | |
48 return 0; | |
49 } | |
50 | |
51 if (len == 2 && i_toupper(str[0]) == 'U' && i_toupper(str[1]) == 'T') { | |
52 /* UT - Universal Time */ | |
53 return 0; | |
54 } | |
55 | |
56 if (len == 3) { | |
57 /* GMT | [ECMP][DS]T */ | |
58 if (str[2] != 'T') | |
59 return 0; | |
60 | |
61 switch (i_toupper(*str)) { | |
62 case 'E': | |
63 offset = -5 * 60; | |
64 break; | |
65 case 'C': | |
66 offset = -6 * 60; | |
67 break; | |
68 case 'M': | |
69 offset = -7 * 60; | |
70 break; | |
71 case 'P': | |
72 offset = -8 * 60; | |
73 break; | |
74 default: | |
75 /* GMT and others */ | |
76 return 0; | |
77 } | |
78 | |
79 if (i_toupper(str[1]) == 'D') | |
80 return offset + 60; | |
81 if (i_toupper(str[1] == 'S')) | |
82 return offset; | |
83 } | |
84 | |
85 return 0; | |
86 } | |
87 | |
88 static const Rfc822Token *next_token(const Rfc822Token **tokens) | |
89 { | |
90 const Rfc822Token *ret; | |
91 | |
92 if ((*tokens)->token == 0) | |
93 return NULL; | |
94 | |
95 ret = *tokens; | |
96 (*tokens)++; | |
97 return ret; | |
98 } | |
99 | |
100 int rfc822_parse_date(const char *str, time_t *time) | |
101 { | |
102 struct tm tm; | |
103 const Rfc822Token *tokens, *tok; | |
104 unsigned int i; | |
105 int zone_offset; | |
106 | |
107 if (str == NULL || *str == '\0') | |
108 return FALSE; | |
109 | |
110 /* [weekday_name "," ] dd month_name [yy]yy hh:mi[:ss] timezone | |
111 | |
112 we support comments here even while no-one ever uses them */ | |
113 | |
114 tokens = rfc822_tokenize(str, NULL, NULL, NULL); | |
115 | |
116 memset(&tm, 0, sizeof(tm)); | |
117 | |
118 /* skip the optional weekday */ | |
119 tok = next_token(&tokens); | |
120 if (tok != NULL && tok->token == 'A' && tok->len == 3) { | |
121 tok = next_token(&tokens); | |
122 if (tok == NULL || tok->token != ',') | |
123 return FALSE; | |
124 | |
125 tok = next_token(&tokens); | |
126 } | |
127 | |
128 /* dd */ | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
129 if (tok == NULL || tok->token != 'A' || tok->len != 2 || |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
130 !i_isdigit(tok->ptr[0]) || !i_isdigit(tok->ptr[1])) |
0 | 131 return FALSE; |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
132 tm.tm_mday = (tok->ptr[0]-'0') * 10 + (tok->ptr[1]-'0'); |
0 | 133 |
134 /* month name */ | |
135 tok = next_token(&tokens); | |
136 if (tok == NULL || tok->token != 'A' || tok->len != 3) | |
137 return FALSE; | |
138 | |
139 for (i = 0; i < 12; i++) { | |
140 if (strncasecmp(month_names[i], tok->ptr, 3) == 0) { | |
141 tm.tm_mon = i; | |
142 break; | |
143 } | |
144 } | |
145 if (i == 12) | |
146 return FALSE; | |
147 | |
148 /* [yy]yy */ | |
149 tok = next_token(&tokens); | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
150 if (tok == NULL || tok->token != 'A' || |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
151 (tok->len != 2 && tok->len != 4)) |
0 | 152 return FALSE; |
153 | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
154 for (i = 0; i < tok->len; i++) { |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
155 if (!i_isdigit(tok->ptr[i])) |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
156 return FALSE; |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
157 tm.tm_year = tm.tm_year * 10 + (tok->ptr[i]-'0'); |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
158 } |
0 | 159 |
160 if (tok->len == 2) { | |
161 /* two digit year, assume 1970+ */ | |
162 if (tm.tm_year < 70) | |
163 tm.tm_year += 100; | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
164 } else { |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
165 if (tm.tm_year < 1900) |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
166 return FALSE; |
0 | 167 tm.tm_year -= 1900; |
168 } | |
169 | |
170 /* hh */ | |
171 tok = next_token(&tokens); | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
172 if (tok == NULL || tok->token != 'A' || tok->len != 2 || |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
173 !i_isdigit(tok->ptr[0]) || !i_isdigit(tok->ptr[1])) |
0 | 174 return FALSE; |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
175 tm.tm_hour = (tok->ptr[0]-'0') * 10 + (tok->ptr[1]-'0'); |
0 | 176 |
177 /* :mm */ | |
178 tok = next_token(&tokens); | |
179 if (tok == NULL || tok->token != ':') | |
180 return FALSE; | |
181 tok = next_token(&tokens); | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
182 if (tok == NULL || tok->token != 'A' || tok->len != 2 || |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
183 !i_isdigit(tok->ptr[0]) || !i_isdigit(tok->ptr[1])) |
0 | 184 return FALSE; |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
185 tm.tm_min = (tok->ptr[0]-'0') * 10 + (tok->ptr[1]-'0'); |
0 | 186 |
187 /* [:ss] */ | |
188 tok = next_token(&tokens); | |
189 if (tok != NULL && tok->token == ':') { | |
190 tok = next_token(&tokens); | |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
191 if (tok == NULL || tok->token != 'A' || tok->len != 2 || |
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
192 !i_isdigit(tok->ptr[0]) || !i_isdigit(tok->ptr[1])) |
0 | 193 return FALSE; |
49
6be018ca51ef
Year was set wrong in date parser. We'll also now do full checking for date
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
194 tm.tm_sec = (tok->ptr[0]-'0') * 10 + (tok->ptr[1]-'0'); |
0 | 195 } |
196 | |
197 /* timezone */ | |
198 if (tok == NULL || tok->token != 'A') | |
199 return FALSE; | |
200 | |
201 zone_offset = parse_timezone(tok->ptr, tok->len); | |
202 | |
203 tm.tm_isdst = -1; | |
204 *time = mktime(&tm); | |
205 if (*time < 0) | |
206 return FALSE; | |
207 | |
208 *time -= zone_offset * 60; | |
209 return TRUE; | |
210 } | |
211 | |
212 const char *rfc822_to_date(time_t time) | |
213 { | |
214 struct tm *tm; | |
215 int offset, negative; | |
216 | |
217 tm = localtime(&time); | |
218 offset = gmtoff(tm, time); | |
219 if (offset >= 0) | |
220 negative = 0; | |
221 else { | |
222 negative = 1; | |
223 offset = -offset; | |
224 } | |
225 offset /= 60; | |
226 | |
227 return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d", | |
228 weekday_names[tm->tm_wday], | |
229 tm->tm_mday, | |
230 month_names[tm->tm_mon], | |
231 tm->tm_year+1900, | |
232 tm->tm_hour, tm->tm_min, tm->tm_sec, | |
233 negative ? '-' : '+', offset / 60, offset % 60); | |
234 } |