message-date.c revision ba3dc5075c0851302032edefb9015207c3b57cd4
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
c4390dad33b03dd51ba2a475f550347c86ebdb9aTimo Sirainen/* RFC specifies ':' as the only allowed separator,
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen but be forgiving also for some broken ones */
78a5b3e697af5db96fe0dffed600b0d6370bb8e5Timo Sirainenstatic const char *month_names[] = {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic const char *weekday_names[] = {
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic int parse_timezone(const unsigned char *str, size_t len)
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen if (len == 5 && (*str == '+' || *str == '-')) {
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen /* numeric offset */
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if (!i_isdigit(str[1]) || !i_isdigit(str[2]) ||
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen offset = ((str[1]-'0') * 10 + (str[2]-'0')) * 60 +
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen /* military zone - handle them the correct way, not as
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen RFC822 says. RFC2822 though suggests that they'd be
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen considered as unspecified.. */
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if (len == 2 && i_toupper(str[0]) == 'U' && i_toupper(str[1]) == 'T') {
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen /* UT - Universal Time */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen /* GMT | [ECMP][DS]T */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen /* GMT and others */
e93ab1b206e3792a8dabc460ad2ee60aaf6830b1Timo Sirainenstatic int next_token(struct message_date_parser_context *ctx,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen const unsigned char **value, size_t *value_len)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen ret = ctx->parser.data == ctx->parser.end ? 0 :
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenmessage_date_parser_tokens(struct message_date_parser_context *ctx,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen const unsigned char *value;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* [weekday_name "," ] dd month_name [yy]yy hh:mi[:ss] timezone */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* skip the optional weekday */
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen if (len < 1 || len > 2 || !i_isdigit(value[0]))
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen tm.tm_mday = (tm.tm_mday * 10) + (value[1]-'0');
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen /* month name */
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen if (next_token(ctx, &value, &len) <= 0 || len < 3)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen for (i = 0; i < 12; i++) {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (i_memcasecmp(month_names[i], value, 3) == 0) {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (next_token(ctx, &value, &len) <= 0 || (len != 2 && len != 4))
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen for (i = 0; i < len; i++) {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen tm.tm_year = tm.tm_year * 10 + (value[i]-'0');
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* two digit year, assume 1970+ */
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen /* hh, allow also single digit */
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen tm.tm_hour = tm.tm_hour * 10 + (value[1]-'0');
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen /* :mm (may be the last token) */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (next_token(ctx, &value, &len) < 0 || len != 2 ||
86fafb22c02f4b85a9d59f2b60059a48286286fbTimo Sirainen tm.tm_min = (value[0]-'0') * 10 + (value[1]-'0');
86fafb22c02f4b85a9d59f2b60059a48286286fbTimo Sirainen if (next_token(ctx, &value, &len) <= 0 || len != 2 ||
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen tm.tm_sec = (value[0]-'0') * 10 + (value[1]-'0');
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if ((ret = next_token(ctx, &value, &len)) < 0)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* missing timezone */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* timezone. invalid timezones are treated as GMT, because
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen we may not know all the possible timezones that are used
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen and it's better to give at least a mostly correct reply.
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen FIXME: perhaps some different strict version of this
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen function would be useful? */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen *timezone_offset_r = parse_timezone(value, len);
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainenbool message_date_parse(const unsigned char *data, size_t size,
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen rfc822_parser_init(&ctx.parser, data, size, NULL);
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen success = message_date_parser_tokens(&ctx, timestamp_r,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenconst char *message_date_create(time_t timestamp)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d",
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen negative ? '-' : '+', offset / 60, offset % 60);