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 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 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 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 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* 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 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 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 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