Mercurial > dovecot > original-hg > dovecot-1.2
comparison src/lib-mail/rfc822-date.c @ 0:3b1985cbc908 HEAD
Initial revision
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Aug 2002 12:15:38 +0300 |
parents | |
children | 6be018ca51ef |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3b1985cbc908 |
---|---|
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 */ | |
26 offset = (str[1]-'0') * 1000 + (str[2]-'0') * 100 + | |
27 (str[3]-'0') * 10 + (str[4]-'0'); | |
28 return *str == '+' ? offset : -offset; | |
29 } | |
30 | |
31 if (len == 1) { | |
32 /* military zone - handle them the correct way, not as | |
33 RFC822 says. RFC2822 though suggests that they'd be | |
34 considered as unspecified.. */ | |
35 chr = i_toupper(*str); | |
36 if (chr < 'J') | |
37 return (*str-'A'+1) * 60; | |
38 if (chr == 'J') | |
39 return 0; | |
40 if (chr <= 'M') | |
41 return (*str-'A') * 60; | |
42 if (chr < 'Z') | |
43 return ('M'-*str) * 60; | |
44 return 0; | |
45 } | |
46 | |
47 if (len == 2 && i_toupper(str[0]) == 'U' && i_toupper(str[1]) == 'T') { | |
48 /* UT - Universal Time */ | |
49 return 0; | |
50 } | |
51 | |
52 if (len == 3) { | |
53 /* GMT | [ECMP][DS]T */ | |
54 if (str[2] != 'T') | |
55 return 0; | |
56 | |
57 switch (i_toupper(*str)) { | |
58 case 'E': | |
59 offset = -5 * 60; | |
60 break; | |
61 case 'C': | |
62 offset = -6 * 60; | |
63 break; | |
64 case 'M': | |
65 offset = -7 * 60; | |
66 break; | |
67 case 'P': | |
68 offset = -8 * 60; | |
69 break; | |
70 default: | |
71 /* GMT and others */ | |
72 return 0; | |
73 } | |
74 | |
75 if (i_toupper(str[1]) == 'D') | |
76 return offset + 60; | |
77 if (i_toupper(str[1] == 'S')) | |
78 return offset; | |
79 } | |
80 | |
81 return 0; | |
82 } | |
83 | |
84 static const Rfc822Token *next_token(const Rfc822Token **tokens) | |
85 { | |
86 const Rfc822Token *ret; | |
87 | |
88 if ((*tokens)->token == 0) | |
89 return NULL; | |
90 | |
91 ret = *tokens; | |
92 (*tokens)++; | |
93 return ret; | |
94 } | |
95 | |
96 int rfc822_parse_date(const char *str, time_t *time) | |
97 { | |
98 struct tm tm; | |
99 const Rfc822Token *tokens, *tok; | |
100 unsigned int i; | |
101 int zone_offset; | |
102 | |
103 if (str == NULL || *str == '\0') | |
104 return FALSE; | |
105 | |
106 /* [weekday_name "," ] dd month_name [yy]yy hh:mi[:ss] timezone | |
107 | |
108 don't waste time checking it too properly, if there's errors we | |
109 either pick them up at mktime() or have an invalid timestamp, | |
110 which would happen anyway. | |
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 */ | |
129 if (tok == NULL || tok->token != 'A' || tok->len != 2) | |
130 return FALSE; | |
131 tm.tm_mday = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; | |
132 | |
133 /* month name */ | |
134 tok = next_token(&tokens); | |
135 if (tok == NULL || tok->token != 'A' || tok->len != 3) | |
136 return FALSE; | |
137 | |
138 for (i = 0; i < 12; i++) { | |
139 if (strncasecmp(month_names[i], tok->ptr, 3) == 0) { | |
140 tm.tm_mon = i; | |
141 break; | |
142 } | |
143 } | |
144 if (i == 12) | |
145 return FALSE; | |
146 | |
147 /* [yy]yy */ | |
148 tok = next_token(&tokens); | |
149 if (tok == NULL || tok->token != 'A') | |
150 return FALSE; | |
151 | |
152 for (i = 0; i < tok->len; i++) | |
153 tm.tm_year = tm.tm_year * 10 + tok->ptr[i]-'0'; | |
154 | |
155 if (tok->len == 2) { | |
156 /* two digit year, assume 1970+ */ | |
157 if (tm.tm_year < 70) | |
158 tm.tm_year += 100; | |
159 } else if (tok->len != 4) { | |
160 /* y10k bug here */ | |
161 tm.tm_year -= 1900; | |
162 return FALSE; | |
163 } | |
164 | |
165 /* hh */ | |
166 tok = next_token(&tokens); | |
167 if (tok == NULL || tok->token != 'A' || tok->len != 2) | |
168 return FALSE; | |
169 tm.tm_hour = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; | |
170 | |
171 /* :mm */ | |
172 tok = next_token(&tokens); | |
173 if (tok == NULL || tok->token != ':') | |
174 return FALSE; | |
175 tok = next_token(&tokens); | |
176 if (tok == NULL || (tok->token != 'A' && tok->len != 2)) | |
177 return FALSE; | |
178 tm.tm_min = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; | |
179 | |
180 /* [:ss] */ | |
181 tok = next_token(&tokens); | |
182 if (tok != NULL && tok->token == ':') { | |
183 tok = next_token(&tokens); | |
184 if (tok == NULL || (tok->token != 'A' && tok->len != 2)) | |
185 return FALSE; | |
186 tm.tm_sec = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; | |
187 } | |
188 | |
189 /* timezone */ | |
190 if (tok == NULL || tok->token != 'A') | |
191 return FALSE; | |
192 | |
193 zone_offset = parse_timezone(tok->ptr, tok->len); | |
194 | |
195 tm.tm_isdst = -1; | |
196 *time = mktime(&tm); | |
197 if (*time < 0) | |
198 return FALSE; | |
199 | |
200 *time -= zone_offset * 60; | |
201 return TRUE; | |
202 } | |
203 | |
204 const char *rfc822_to_date(time_t time) | |
205 { | |
206 struct tm *tm; | |
207 int offset, negative; | |
208 | |
209 tm = localtime(&time); | |
210 offset = gmtoff(tm, time); | |
211 if (offset >= 0) | |
212 negative = 0; | |
213 else { | |
214 negative = 1; | |
215 offset = -offset; | |
216 } | |
217 offset /= 60; | |
218 | |
219 return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d", | |
220 weekday_names[tm->tm_wday], | |
221 tm->tm_mday, | |
222 month_names[tm->tm_mon], | |
223 tm->tm_year+1900, | |
224 tm->tm_hour, tm->tm_min, tm->tm_sec, | |
225 negative ? '-' : '+', offset / 60, offset % 60); | |
226 } |