Mercurial > dovecot > original-hg > dovecot-1.2
diff src/lib-mail/rfc822-date.c @ 0:3b1985cbc908 HEAD
Initial revision
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Aug 2002 12:15:38 +0300 |
parents | |
children | 6be018ca51ef |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-mail/rfc822-date.c Fri Aug 09 12:15:38 2002 +0300 @@ -0,0 +1,226 @@ +/* Copyright (C) 2002 Timo Sirainen */ + +#include "lib.h" +#include "gmtoff.h" +#include "rfc822-date.h" +#include "rfc822-tokenize.h" + +#include <ctype.h> + +static const char *month_names[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const char *weekday_names[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static int parse_timezone(const char *str, unsigned int len) +{ + int offset; + char chr; + + if (len == 5 && (*str == '+' || *str == '-')) { + /* numeric offset */ + offset = (str[1]-'0') * 1000 + (str[2]-'0') * 100 + + (str[3]-'0') * 10 + (str[4]-'0'); + return *str == '+' ? offset : -offset; + } + + if (len == 1) { + /* military zone - handle them the correct way, not as + RFC822 says. RFC2822 though suggests that they'd be + considered as unspecified.. */ + chr = i_toupper(*str); + if (chr < 'J') + return (*str-'A'+1) * 60; + if (chr == 'J') + return 0; + if (chr <= 'M') + return (*str-'A') * 60; + if (chr < 'Z') + return ('M'-*str) * 60; + return 0; + } + + if (len == 2 && i_toupper(str[0]) == 'U' && i_toupper(str[1]) == 'T') { + /* UT - Universal Time */ + return 0; + } + + if (len == 3) { + /* GMT | [ECMP][DS]T */ + if (str[2] != 'T') + return 0; + + switch (i_toupper(*str)) { + case 'E': + offset = -5 * 60; + break; + case 'C': + offset = -6 * 60; + break; + case 'M': + offset = -7 * 60; + break; + case 'P': + offset = -8 * 60; + break; + default: + /* GMT and others */ + return 0; + } + + if (i_toupper(str[1]) == 'D') + return offset + 60; + if (i_toupper(str[1] == 'S')) + return offset; + } + + return 0; +} + +static const Rfc822Token *next_token(const Rfc822Token **tokens) +{ + const Rfc822Token *ret; + + if ((*tokens)->token == 0) + return NULL; + + ret = *tokens; + (*tokens)++; + return ret; +} + +int rfc822_parse_date(const char *str, time_t *time) +{ + struct tm tm; + const Rfc822Token *tokens, *tok; + unsigned int i; + int zone_offset; + + if (str == NULL || *str == '\0') + return FALSE; + + /* [weekday_name "," ] dd month_name [yy]yy hh:mi[:ss] timezone + + don't waste time checking it too properly, if there's errors we + either pick them up at mktime() or have an invalid timestamp, + which would happen anyway. + + we support comments here even while no-one ever uses them */ + + tokens = rfc822_tokenize(str, NULL, NULL, NULL); + + memset(&tm, 0, sizeof(tm)); + + /* skip the optional weekday */ + tok = next_token(&tokens); + if (tok != NULL && tok->token == 'A' && tok->len == 3) { + tok = next_token(&tokens); + if (tok == NULL || tok->token != ',') + return FALSE; + + tok = next_token(&tokens); + } + + /* dd */ + if (tok == NULL || tok->token != 'A' || tok->len != 2) + return FALSE; + tm.tm_mday = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; + + /* month name */ + tok = next_token(&tokens); + if (tok == NULL || tok->token != 'A' || tok->len != 3) + return FALSE; + + for (i = 0; i < 12; i++) { + if (strncasecmp(month_names[i], tok->ptr, 3) == 0) { + tm.tm_mon = i; + break; + } + } + if (i == 12) + return FALSE; + + /* [yy]yy */ + tok = next_token(&tokens); + if (tok == NULL || tok->token != 'A') + return FALSE; + + for (i = 0; i < tok->len; i++) + tm.tm_year = tm.tm_year * 10 + tok->ptr[i]-'0'; + + if (tok->len == 2) { + /* two digit year, assume 1970+ */ + if (tm.tm_year < 70) + tm.tm_year += 100; + } else if (tok->len != 4) { + /* y10k bug here */ + tm.tm_year -= 1900; + return FALSE; + } + + /* hh */ + tok = next_token(&tokens); + if (tok == NULL || tok->token != 'A' || tok->len != 2) + return FALSE; + tm.tm_hour = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; + + /* :mm */ + tok = next_token(&tokens); + if (tok == NULL || tok->token != ':') + return FALSE; + tok = next_token(&tokens); + if (tok == NULL || (tok->token != 'A' && tok->len != 2)) + return FALSE; + tm.tm_min = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; + + /* [:ss] */ + tok = next_token(&tokens); + if (tok != NULL && tok->token == ':') { + tok = next_token(&tokens); + if (tok == NULL || (tok->token != 'A' && tok->len != 2)) + return FALSE; + tm.tm_sec = (tok->ptr[0]-'0') * 10 + tok->ptr[1]-'0'; + } + + /* timezone */ + if (tok == NULL || tok->token != 'A') + return FALSE; + + zone_offset = parse_timezone(tok->ptr, tok->len); + + tm.tm_isdst = -1; + *time = mktime(&tm); + if (*time < 0) + return FALSE; + + *time -= zone_offset * 60; + return TRUE; +} + +const char *rfc822_to_date(time_t time) +{ + struct tm *tm; + int offset, negative; + + tm = localtime(&time); + offset = gmtoff(tm, time); + if (offset >= 0) + negative = 0; + else { + negative = 1; + offset = -offset; + } + offset /= 60; + + return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d", + weekday_names[tm->tm_wday], + tm->tm_mday, + month_names[tm->tm_mon], + tm->tm_year+1900, + tm->tm_hour, tm->tm_min, tm->tm_sec, + negative ? '-' : '+', offset / 60, offset % 60); +}