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(tm->wday != 8){ 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 t = tm2sec(&tm); 94 zone2tm(&tm, flds[2]); 95 t -= tm.tzoff; 96 return t; 97 } 98 99 /* 100 * parse dates of formats 101 * [Wkd[,]] DD Mon YYYY HH:MM:SS zone 102 * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY 103 * plus anything similar 104 * return nil for a failure 105 */ 106 Tm* 107 date2tm(Tm *tm, char *date) 108 { 109 Tm gmt, *atm; 110 char *flds[7], *s, dstr[64]; 111 int n; 112 113 /* 114 * default date is Thu Jan 1 00:00:00 GMT 1970 115 */ 116 tm->wday = 4; 117 tm->mday = 1; 118 tm->mon = 1; 119 tm->hour = 0; 120 tm->min = 0; 121 tm->sec = 0; 122 tm->year = 70; 123 strcpy(tm->zone, "GMT"); 124 tm->tzoff = 0; 125 126 strncpy(dstr, date, sizeof(dstr)); 127 dstr[sizeof(dstr)-1] = '\0'; 128 n = tokenize(dstr, flds, 7); 129 if(n != 6 && n != 5) 130 return nil; 131 132 if(n == 5){ 133 for(n = 5; n >= 0; n--) 134 flds[n] = flds[n - 1]; 135 n = 5; 136 }else{ 137 /* 138 * Wday[,] 139 */ 140 s = strchr(flds[0], ','); 141 if(s != nil) 142 *s = '\0'; 143 tm->wday = dateindex(flds[0], wdayname, 7); 144 if(tm->wday < 0) 145 return nil; 146 } 147 148 /* 149 * check for the two major formats: 150 * Month first or day first 151 */ 152 tm->mon = dateindex(flds[1], monname, 12); 153 if(tm->mon >= 0){ 154 tm->mday = strtoul(flds[2], nil, 10); 155 time2tm(tm, flds[3]); 156 zone2tm(tm, flds[4]); 157 tm->year = strtoul(flds[5], nil, 10); 158 if(strlen(flds[5]) > 2) 159 tm->year -= 1900; 160 }else{ 161 tm->mday = strtoul(flds[1], nil, 10); 162 tm->mon = dateindex(flds[2], monname, 12); 163 tm->year = strtoul(flds[3], nil, 10); 164 if(strlen(flds[3]) > 2) 165 tm->year -= 1900; 166 time2tm(tm, flds[4]); 167 zone2tm(tm, flds[5]); 168 } 169 170 if(n == 5){ 171 gmt = *tm; 172 strncpy(gmt.zone, "", 4); 173 gmt.tzoff = 0; 174 atm = gmtime(tm2sec(&gmt)); 175 tm->wday = atm->wday; 176 }else{ 177 /* 178 * Wday[,] 179 */ 180 s = strchr(flds[0], ','); 181 if(s != nil) 182 *s = '\0'; 183 tm->wday = dateindex(flds[0], wdayname, 7); 184 if(tm->wday < 0) 185 return nil; 186 } 187 return tm; 188 } 189 190 /* 191 * zone : [A-Za-z][A-Za-z][A-Za-z] some time zone names 192 * | [A-IK-Z] military time; rfc1123 says the rfc822 spec is wrong. 193 * | "UT" universal time 194 * | [+-][0-9][0-9][0-9][0-9] 195 * zones is the rfc-822 list of time zone names 196 */ 197 static NamedInt zones[] = 198 { 199 {"A", -1 * 3600}, 200 {"B", -2 * 3600}, 201 {"C", -3 * 3600}, 202 {"CDT", -5 * 3600}, 203 {"CST", -6 * 3600}, 204 {"D", -4 * 3600}, 205 {"E", -5 * 3600}, 206 {"EDT", -4 * 3600}, 207 {"EST", -5 * 3600}, 208 {"F", -6 * 3600}, 209 {"G", -7 * 3600}, 210 {"GMT", 0}, 211 {"H", -8 * 3600}, 212 {"I", -9 * 3600}, 213 {"K", -10 * 3600}, 214 {"L", -11 * 3600}, 215 {"M", -12 * 3600}, 216 {"MDT", -6 * 3600}, 217 {"MST", -7 * 3600}, 218 {"N", +1 * 3600}, 219 {"O", +2 * 3600}, 220 {"P", +3 * 3600}, 221 {"PDT", -7 * 3600}, 222 {"PST", -8 * 3600}, 223 {"Q", +4 * 3600}, 224 {"R", +5 * 3600}, 225 {"S", +6 * 3600}, 226 {"T", +7 * 3600}, 227 {"U", +8 * 3600}, 228 {"UT", 0}, 229 {"V", +9 * 3600}, 230 {"W", +10 * 3600}, 231 {"X", +11 * 3600}, 232 {"Y", +12 * 3600}, 233 {"Z", 0}, 234 {nil, 0} 235 }; 236 237 static void 238 zone2tm(Tm *tm, char *s) 239 { 240 Tm aux, *atm; 241 int i; 242 243 if(*s == '+' || *s == '-'){ 244 i = strtol(s, &s, 10); 245 tm->tzoff = (i / 100) * 3600 + i % 100; 246 strncpy(tm->zone, "", 4); 247 return; 248 } 249 250 /* 251 * look it up in the standard rfc822 table 252 */ 253 strncpy(tm->zone, s, 3); 254 tm->zone[3] = '\0'; 255 tm->tzoff = 0; 256 for(i = 0; zones[i].name != nil; i++){ 257 if(cistrcmp(zones[i].name, s) == 0){ 258 tm->tzoff = zones[i].v; 259 return; 260 } 261 } 262 263 /* 264 * one last try: look it up in the current local timezone 265 * probe a couple of times to get daylight/standard time change. 266 */ 267 aux = *tm; 268 memset(aux.zone, 0, 4); 269 aux.hour--; 270 for(i = 0; i < 2; i++){ 271 atm = localtime(tm2sec(&aux)); 272 if(cistrcmp(tm->zone, atm->zone) == 0){ 273 tm->tzoff = atm->tzoff; 274 return; 275 } 276 aux.hour++; 277 } 278 279 strncpy(tm->zone, "GMT", 4); 280 tm->tzoff = 0; 281 } 282 283 /* 284 * hh[:mm[:ss]] 285 */ 286 static void 287 time2tm(Tm *tm, char *s) 288 { 289 tm->hour = strtoul(s, &s, 10); 290 if(*s++ != ':') 291 return; 292 tm->min = strtoul(s, &s, 10); 293 if(*s++ != ':') 294 return; 295 tm->sec = strtoul(s, &s, 10); 296 } 297 298 static int 299 dateindex(char *d, char **tab, int n) 300 { 301 int i; 302 303 for(i = 0; i < n; i++) 304 if(cistrcmp(d, tab[i]) == 0) 305 return i; 306 return -1; 307 } 308