#include #include #include #include #include "imap4d.h" char * wdayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; char * monname[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static void time2tm(Tm *tm, char *s); static void zone2tm(Tm *tm, char *s); static int dateindex(char *d, char **tab, int n); int rfc822date(char *s, int n, Tm *tm) { char *plus; int m; plus = "+"; if(tm->tzoff < 0) plus = ""; m = 0; if(0 <= tm->wday && tm->wday < 7){ m = snprint(s, n, "%s, ", wdayname[tm->wday]); if(m < 0) return m; } return snprint(s+m, n-m, "%.2d %s %.4d %.2d:%.2d:%.2d %s%.4d", tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60); } int imap4date(char *s, int n, Tm *tm) { char *plus; plus = "+"; if(tm->tzoff < 0) plus = ""; return snprint(s, n, "%2d-%s-%.4d %2.2d:%2.2d:%2.2d %s%4.4d", tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60); } int imap4Date(Tm *tm, char *date) { char *flds[4]; if(getfields(date, flds, 3, 0, "-") != 3) return 0; tm->mday = strtol(flds[0], nil, 10); tm->mon = dateindex(flds[1], monname, 12); tm->year = strtol(flds[2], nil, 10) - 1900; return 1; } /* * parse imap4 dates */ ulong imap4DateTime(char *date) { Tm tm; char *flds[4], *sflds[4]; ulong t; if(getfields(date, flds, 4, 0, " ") != 3) return ~0; if(!imap4Date(&tm, flds[0])) return ~0; if(getfields(flds[1], sflds, 3, 0, ":") != 3) return ~0; tm.hour = strtol(sflds[0], nil, 10); tm.min = strtol(sflds[1], nil, 10); tm.sec = strtol(sflds[2], nil, 10); strcpy(tm.zone, "GMT"); tm.yday = 0; t = tm2sec(&tm); zone2tm(&tm, flds[2]); t -= tm.tzoff; return t; } /* * parse dates of formats * [Wkd[,]] DD Mon YYYY HH:MM:SS zone * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY * plus anything similar * return nil for a failure */ Tm* date2tm(Tm *tm, char *date) { Tm gmt, *atm; char *flds[7], *s, dstr[64]; int n; /* * default date is Thu Jan 1 00:00:00 GMT 1970 */ tm->wday = 4; tm->mday = 1; tm->mon = 1; tm->hour = 0; tm->min = 0; tm->sec = 0; tm->year = 70; strcpy(tm->zone, "GMT"); tm->tzoff = 0; strncpy(dstr, date, sizeof(dstr)); dstr[sizeof(dstr)-1] = '\0'; n = tokenize(dstr, flds, 7); if(n != 6 && n != 5) return nil; if(n == 5){ for(n = 5; n >= 1; n--) flds[n] = flds[n - 1]; n = 5; }else{ /* * Wday[,] */ s = strchr(flds[0], ','); if(s != nil) *s = '\0'; tm->wday = dateindex(flds[0], wdayname, 7); if(tm->wday < 0) return nil; } /* * check for the two major formats: * Month first or day first */ tm->mon = dateindex(flds[1], monname, 12); if(tm->mon >= 0){ tm->mday = strtoul(flds[2], nil, 10); time2tm(tm, flds[3]); zone2tm(tm, flds[4]); tm->year = strtoul(flds[5], nil, 10); if(strlen(flds[5]) > 2) tm->year -= 1900; }else{ tm->mday = strtoul(flds[1], nil, 10); tm->mon = dateindex(flds[2], monname, 12); tm->year = strtoul(flds[3], nil, 10); if(strlen(flds[3]) > 2) tm->year -= 1900; time2tm(tm, flds[4]); zone2tm(tm, flds[5]); } if(n == 5){ gmt = *tm; strncpy(gmt.zone, "", 4); gmt.tzoff = 0; atm = gmtime(tm2sec(&gmt)); tm->wday = atm->wday; }else{ /* * Wday[,] */ s = strchr(flds[0], ','); if(s != nil) *s = '\0'; tm->wday = dateindex(flds[0], wdayname, 7); if(tm->wday < 0) return nil; } return tm; } /* * zone : [A-Za-z][A-Za-z][A-Za-z] some time zone names * | [A-IK-Z] military time; rfc1123 says the rfc822 spec is wrong. * | "UT" universal time * | [+-][0-9][0-9][0-9][0-9] * zones is the rfc-822 list of time zone names */ static NamedInt zones[] = { {"A", -1 * 3600}, {"B", -2 * 3600}, {"C", -3 * 3600}, {"CDT", -5 * 3600}, {"CST", -6 * 3600}, {"D", -4 * 3600}, {"E", -5 * 3600}, {"EDT", -4 * 3600}, {"EST", -5 * 3600}, {"F", -6 * 3600}, {"G", -7 * 3600}, {"GMT", 0}, {"H", -8 * 3600}, {"I", -9 * 3600}, {"K", -10 * 3600}, {"L", -11 * 3600}, {"M", -12 * 3600}, {"MDT", -6 * 3600}, {"MST", -7 * 3600}, {"N", +1 * 3600}, {"O", +2 * 3600}, {"P", +3 * 3600}, {"PDT", -7 * 3600}, {"PST", -8 * 3600}, {"Q", +4 * 3600}, {"R", +5 * 3600}, {"S", +6 * 3600}, {"T", +7 * 3600}, {"U", +8 * 3600}, {"UT", 0}, {"V", +9 * 3600}, {"W", +10 * 3600}, {"X", +11 * 3600}, {"Y", +12 * 3600}, {"Z", 0}, {nil, 0} }; static void zone2tm(Tm *tm, char *s) { Tm aux, *atm; int i; if(*s == '+' || *s == '-'){ i = strtol(s, &s, 10); tm->tzoff = (i / 100) * 3600 + i % 100; strncpy(tm->zone, "", 4); return; } /* * look it up in the standard rfc822 table */ strncpy(tm->zone, s, 3); tm->zone[3] = '\0'; tm->tzoff = 0; for(i = 0; zones[i].name != nil; i++){ if(cistrcmp(zones[i].name, s) == 0){ tm->tzoff = zones[i].v; return; } } /* * one last try: look it up in the current local timezone * probe a couple of times to get daylight/standard time change. */ aux = *tm; memset(aux.zone, 0, 4); aux.hour--; for(i = 0; i < 2; i++){ atm = localtime(tm2sec(&aux)); if(cistrcmp(tm->zone, atm->zone) == 0){ tm->tzoff = atm->tzoff; return; } aux.hour++; } strncpy(tm->zone, "GMT", 4); tm->tzoff = 0; } /* * hh[:mm[:ss]] */ static void time2tm(Tm *tm, char *s) { tm->hour = strtoul(s, &s, 10); if(*s++ != ':') return; tm->min = strtoul(s, &s, 10); if(*s++ != ':') return; tm->sec = strtoul(s, &s, 10); } static int dateindex(char *d, char **tab, int n) { int i; for(i = 0; i < n; i++) if(cistrcmp(d, tab[i]) == 0) return i; return -1; }