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 }