Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#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;
}