1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include "imap4d.h"
6
7 char *
8 wdayname[7] =
9 {
10 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
11 };
12
13 char *
14 monname[12] =
15 {
16 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
17 };
18
19 static void time2tm(Tm *tm, char *s);
20 static void zone2tm(Tm *tm, char *s);
21 static int dateindex(char *d, char **tab, int n);
22
23 int
rfc822date(char * s,int n,Tm * tm)24 rfc822date(char *s, int n, Tm *tm)
25 {
26 char *plus;
27 int m;
28
29 plus = "+";
30 if(tm->tzoff < 0)
31 plus = "";
32 m = 0;
33 if(0 <= tm->wday && tm->wday < 7){
34 m = snprint(s, n, "%s, ", wdayname[tm->wday]);
35 if(m < 0)
36 return m;
37 }
38 return snprint(s+m, n-m, "%.2d %s %.4d %.2d:%.2d:%.2d %s%.4d",
39 tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec,
40 plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
41 }
42
43 int
imap4date(char * s,int n,Tm * tm)44 imap4date(char *s, int n, Tm *tm)
45 {
46 char *plus;
47
48 plus = "+";
49 if(tm->tzoff < 0)
50 plus = "";
51 return snprint(s, n, "%2d-%s-%.4d %2.2d:%2.2d:%2.2d %s%4.4d",
52 tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
53 }
54
55 int
imap4Date(Tm * tm,char * date)56 imap4Date(Tm *tm, char *date)
57 {
58 char *flds[4];
59
60 if(getfields(date, flds, 3, 0, "-") != 3)
61 return 0;
62
63 tm->mday = strtol(flds[0], nil, 10);
64 tm->mon = dateindex(flds[1], monname, 12);
65 tm->year = strtol(flds[2], nil, 10) - 1900;
66 return 1;
67 }
68
69 /*
70 * parse imap4 dates
71 */
72 ulong
imap4DateTime(char * date)73 imap4DateTime(char *date)
74 {
75 Tm tm;
76 char *flds[4], *sflds[4];
77 ulong t;
78
79 if(getfields(date, flds, 4, 0, " ") != 3)
80 return ~0;
81
82 if(!imap4Date(&tm, flds[0]))
83 return ~0;
84
85 if(getfields(flds[1], sflds, 3, 0, ":") != 3)
86 return ~0;
87
88 tm.hour = strtol(sflds[0], nil, 10);
89 tm.min = strtol(sflds[1], nil, 10);
90 tm.sec = strtol(sflds[2], nil, 10);
91
92 strcpy(tm.zone, "GMT");
93 tm.yday = 0;
94 t = tm2sec(&tm);
95 zone2tm(&tm, flds[2]);
96 t -= tm.tzoff;
97 return t;
98 }
99
100 /*
101 * parse dates of formats
102 * [Wkd[,]] DD Mon YYYY HH:MM:SS zone
103 * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY
104 * plus anything similar
105 * return nil for a failure
106 */
107 Tm*
date2tm(Tm * tm,char * date)108 date2tm(Tm *tm, char *date)
109 {
110 Tm gmt, *atm;
111 char *flds[7], *s, dstr[64];
112 int n;
113
114 /*
115 * default date is Thu Jan 1 00:00:00 GMT 1970
116 */
117 tm->wday = 4;
118 tm->mday = 1;
119 tm->mon = 1;
120 tm->hour = 0;
121 tm->min = 0;
122 tm->sec = 0;
123 tm->year = 70;
124 strcpy(tm->zone, "GMT");
125 tm->tzoff = 0;
126
127 strncpy(dstr, date, sizeof(dstr));
128 dstr[sizeof(dstr)-1] = '\0';
129 n = tokenize(dstr, flds, 7);
130 if(n != 6 && n != 5)
131 return nil;
132
133 if(n == 5){
134 for(n = 5; n >= 1; n--)
135 flds[n] = flds[n - 1];
136 n = 5;
137 }else{
138 /*
139 * Wday[,]
140 */
141 s = strchr(flds[0], ',');
142 if(s != nil)
143 *s = '\0';
144 tm->wday = dateindex(flds[0], wdayname, 7);
145 if(tm->wday < 0)
146 return nil;
147 }
148
149 /*
150 * check for the two major formats:
151 * Month first or day first
152 */
153 tm->mon = dateindex(flds[1], monname, 12);
154 if(tm->mon >= 0){
155 tm->mday = strtoul(flds[2], nil, 10);
156 time2tm(tm, flds[3]);
157 zone2tm(tm, flds[4]);
158 tm->year = strtoul(flds[5], nil, 10);
159 if(strlen(flds[5]) > 2)
160 tm->year -= 1900;
161 }else{
162 tm->mday = strtoul(flds[1], nil, 10);
163 tm->mon = dateindex(flds[2], monname, 12);
164 tm->year = strtoul(flds[3], nil, 10);
165 if(strlen(flds[3]) > 2)
166 tm->year -= 1900;
167 time2tm(tm, flds[4]);
168 zone2tm(tm, flds[5]);
169 }
170
171 if(n == 5){
172 gmt = *tm;
173 strncpy(gmt.zone, "", 4);
174 gmt.tzoff = 0;
175 atm = gmtime(tm2sec(&gmt));
176 tm->wday = atm->wday;
177 }else{
178 /*
179 * Wday[,]
180 */
181 s = strchr(flds[0], ',');
182 if(s != nil)
183 *s = '\0';
184 tm->wday = dateindex(flds[0], wdayname, 7);
185 if(tm->wday < 0)
186 return nil;
187 }
188 return tm;
189 }
190
191 /*
192 * zone : [A-Za-z][A-Za-z][A-Za-z] some time zone names
193 * | [A-IK-Z] military time; rfc1123 says the rfc822 spec is wrong.
194 * | "UT" universal time
195 * | [+-][0-9][0-9][0-9][0-9]
196 * zones is the rfc-822 list of time zone names
197 */
198 static NamedInt zones[] =
199 {
200 {"A", -1 * 3600},
201 {"B", -2 * 3600},
202 {"C", -3 * 3600},
203 {"CDT", -5 * 3600},
204 {"CST", -6 * 3600},
205 {"D", -4 * 3600},
206 {"E", -5 * 3600},
207 {"EDT", -4 * 3600},
208 {"EST", -5 * 3600},
209 {"F", -6 * 3600},
210 {"G", -7 * 3600},
211 {"GMT", 0},
212 {"H", -8 * 3600},
213 {"I", -9 * 3600},
214 {"K", -10 * 3600},
215 {"L", -11 * 3600},
216 {"M", -12 * 3600},
217 {"MDT", -6 * 3600},
218 {"MST", -7 * 3600},
219 {"N", +1 * 3600},
220 {"O", +2 * 3600},
221 {"P", +3 * 3600},
222 {"PDT", -7 * 3600},
223 {"PST", -8 * 3600},
224 {"Q", +4 * 3600},
225 {"R", +5 * 3600},
226 {"S", +6 * 3600},
227 {"T", +7 * 3600},
228 {"U", +8 * 3600},
229 {"UT", 0},
230 {"V", +9 * 3600},
231 {"W", +10 * 3600},
232 {"X", +11 * 3600},
233 {"Y", +12 * 3600},
234 {"Z", 0},
235 {nil, 0}
236 };
237
238 static void
zone2tm(Tm * tm,char * s)239 zone2tm(Tm *tm, char *s)
240 {
241 Tm aux, *atm;
242 int i;
243
244 if(*s == '+' || *s == '-'){
245 i = strtol(s, &s, 10);
246 tm->tzoff = (i / 100) * 3600 + i % 100;
247 strncpy(tm->zone, "", 4);
248 return;
249 }
250
251 /*
252 * look it up in the standard rfc822 table
253 */
254 strncpy(tm->zone, s, 3);
255 tm->zone[3] = '\0';
256 tm->tzoff = 0;
257 for(i = 0; zones[i].name != nil; i++){
258 if(cistrcmp(zones[i].name, s) == 0){
259 tm->tzoff = zones[i].v;
260 return;
261 }
262 }
263
264 /*
265 * one last try: look it up in the current local timezone
266 * probe a couple of times to get daylight/standard time change.
267 */
268 aux = *tm;
269 memset(aux.zone, 0, 4);
270 aux.hour--;
271 for(i = 0; i < 2; i++){
272 atm = localtime(tm2sec(&aux));
273 if(cistrcmp(tm->zone, atm->zone) == 0){
274 tm->tzoff = atm->tzoff;
275 return;
276 }
277 aux.hour++;
278 }
279
280 strncpy(tm->zone, "GMT", 4);
281 tm->tzoff = 0;
282 }
283
284 /*
285 * hh[:mm[:ss]]
286 */
287 static void
time2tm(Tm * tm,char * s)288 time2tm(Tm *tm, char *s)
289 {
290 tm->hour = strtoul(s, &s, 10);
291 if(*s++ != ':')
292 return;
293 tm->min = strtoul(s, &s, 10);
294 if(*s++ != ':')
295 return;
296 tm->sec = strtoul(s, &s, 10);
297 }
298
299 static int
dateindex(char * d,char ** tab,int n)300 dateindex(char *d, char **tab, int n)
301 {
302 int i;
303
304 for(i = 0; i < n; i++)
305 if(cistrcmp(d, tab[i]) == 0)
306 return i;
307 return -1;
308 }
309