diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-11-26 07:30:41 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-11-26 07:30:41 +0200 |
commit | 92ef2f82491ab16a9393fb7c83adaf2f1396e232 (patch) | |
tree | 0fd23a299e7e9483d63ac9dab1afd64d0dc172ca | |
parent | 1bcece404bf0ec7a2d2941511350e8f2a6f65736 (diff) | |
download | mailutils-92ef2f82491ab16a9393fb7c83adaf2f1396e232.tar.gz mailutils-92ef2f82491ab16a9393fb7c83adaf2f1396e232.tar.bz2 |
mboxrb: Keep timestamps in normalized form
* include/mailutils/datetime.h (mu_timezone_offset): New proto.
* libmailutils/datetime/scantime.c: New conversion: %Z (timezone
abbreviation).
* libmailutils/datetime/parsedate.y (mu_timezone_offset): New function.
Returns time offset in seconds corresponding to the given timezone
abbreviation.
* include/mailutils/sys/mboxrb.h (mu_mboxrb_hdr): Remove unneeded
enum.
(mu_mboxrb_message): Remove env_date_start,
New member "date" holds the string representation of the envelope
timestamp in normalized form - ctime(3), UTC.
* libproto/mbox/mboxrb.c (scan_message_finalize): New function.
(scan_message_begin): New function. When initializing the
message, timestamps in obsolete forms are converted to normalized
representation.
(mboxrb_rescan_unlocked): Use scan_message_finalize and
scan_message_begin.
* libproto/mbox/message.c (mboxrb_envelope_date): Get normalized
timestamp from the date member,
* libproto/mbox/tests/env.at: Reflect the above changes.
-rw-r--r-- | include/mailutils/datetime.h | 1 | ||||
-rw-r--r-- | include/mailutils/sys/mboxrb.h | 14 | ||||
-rw-r--r-- | libmailutils/datetime/parsedate.y | 14 | ||||
-rw-r--r-- | libmailutils/datetime/scantime.c | 26 | ||||
-rw-r--r-- | libproto/mbox/mboxrb.c | 251 | ||||
-rw-r--r-- | libproto/mbox/message.c | 38 | ||||
-rw-r--r-- | libproto/mbox/tests/env.at | 18 |
7 files changed, 216 insertions, 146 deletions
diff --git a/include/mailutils/datetime.h b/include/mailutils/datetime.h index dd346ac29..cd8e3369c 100644 --- a/include/mailutils/datetime.h +++ b/include/mailutils/datetime.h @@ -82,6 +82,7 @@ int mu_parse_date_dtl (const char *p, const time_t *now, int mu_parse_date (const char *p, time_t *rettime, const time_t *now); int mu_utc_offset (void); +int mu_timezone_offset (const char *buf, int *off); void mu_datetime_tz_local (struct mu_timezone *tz); void mu_datetime_tz_utc (struct mu_timezone *tz); time_t mu_datetime_to_utc (struct tm *timeptr, struct mu_timezone *tz); diff --git a/include/mailutils/sys/mboxrb.h b/include/mailutils/sys/mboxrb.h index 0e3af71e8..22574330a 100644 --- a/include/mailutils/sys/mboxrb.h +++ b/include/mailutils/sys/mboxrb.h @@ -18,27 +18,21 @@ #define _MAILUTILS_SYS_MBOX2_H # include <mailutils/types.h> - -enum mu_mboxrb_hdr - { - mu_mboxrb_hdr_status, - mu_mboxrb_hdr_x_imapbase, - mu_mboxrb_hdr_x_uid, - MU_MBOXRB_HDR_MAX - }; +# include <mailutils/datetime.h> struct mu_mboxrb_message { /* Offsets in the mailbox */ mu_off_t message_start; /* Start of message */ size_t from_length; /* Length of the From_ line */ - int env_sender_len; - int env_date_start; + int env_sender_len; /* Length of the sender email in the From_ line */ mu_off_t body_start; /* Start of body */ mu_off_t message_end; /* Offset of the last byte of the message */ /* Additional info */ unsigned long uid; /* IMAP-style uid. */ + char date[MU_DATETIME_FROM_LENGTH+1]; /* Envelope date in normalized form */ + unsigned body_lines_scanned:1; /* True if body_lines is initialized */ unsigned body_from_escaped:1; /* True if body is from-escaped (valid if body_lines_scanned is true) */ diff --git a/libmailutils/datetime/parsedate.y b/libmailutils/datetime/parsedate.y index 977713001..372cdb2eb 100644 --- a/libmailutils/datetime/parsedate.y +++ b/libmailutils/datetime/parsedate.y @@ -1209,6 +1209,20 @@ mu_parse_date (const char *p, time_t *rettime, const time_t *now) return mu_parse_date_dtl (p, now, rettime, NULL, NULL, NULL); } +int +mu_timezone_offset (const char *buf, int *off) +{ + SYMBOL const *tp; + + for (tp = tz_tab; tp->name; tp++) + if (mu_c_strcasecmp (buf, tp->name) == 0) + { + *off = - tp->value * 60; + return 0; + } + return -1; +} + #ifdef STANDALONE int main (int argc, char *argv[]) diff --git a/libmailutils/datetime/scantime.c b/libmailutils/datetime/scantime.c index 0b118b765..8e19a803b 100644 --- a/libmailutils/datetime/scantime.c +++ b/libmailutils/datetime/scantime.c @@ -538,7 +538,31 @@ mu_scan_datetime (const char *input, const char *fmt, } } break; - + + case 'Z': + /* Time-zone in abbreviated form */ + { + char tzs[6]; + p = mu_str_skip_class_comp (input, MU_CTYPE_SPACE); + n = p - input; + if (n > sizeof (tzs) - 1) + { + rc = MU_ERR_PARSE; + break; + } + memcpy (tzs, input, n); + tzs[n] = 0; + if (mu_timezone_offset (tzs, &n)) + { + rc = MU_ERR_PARSE; + break; + } + if (tz) + tz->utc_offset = n; + input = p; + } + break; + case '%': if (*input == '%') input++; diff --git a/libproto/mbox/mboxrb.c b/libproto/mbox/mboxrb.c index 412f58f61..a64a5f1ce 100644 --- a/libproto/mbox/mboxrb.c +++ b/libproto/mbox/mboxrb.c @@ -383,7 +383,8 @@ mboxrb_message_alloc_uid (struct mu_mboxrb_message *dmsg) * email address functions won't tolerate it). * * Input: S - line read from the mailbox (with \n terminator). - * Output: ZP - points to the time zone information in S. + * Output: ZP - points to the space character preceding the time zone + * information in S. If there is no TZ, points to the terminating \n. * * Return value: If S is a valid From_ line, a pointer to the time * information in S. Otherwise, NULL. @@ -456,6 +457,152 @@ parse_from_line (char const *s, char **zp) return NULL; } +/* Finalize current message */ +static inline int +scan_message_finalize (struct mu_mboxrb_mailbox *dmp, + struct mu_mboxrb_message *dmsg, mu_stream_t stream, + size_t n, int *force_init_uids) +{ + int rc; + size_t count; + + rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_end); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_seek", dmp->name, + mu_strerror (rc))); + return -1; + } + dmsg->message_end -= n + 1; + if (dmsg->uid == 0) + *force_init_uids = 1; + if (*force_init_uids) + mboxrb_message_alloc_uid (dmsg); + + /* Every 100 mesgs update the lock, it should be every minute. */ + if (dmp->mailbox->locker && (dmp->mesg_count % 100) == 0) + mu_locker_touchlock (dmp->mailbox->locker); + + count = dmp->mesg_count; + mboxrb_dispatch (dmp->mailbox, MU_EVT_MESSAGE_ADD, &count); + return 0; +} + +static inline struct mu_mboxrb_message * +scan_message_begin (struct mu_mboxrb_mailbox *dmp, mu_stream_t stream, + char *buf, size_t n, char *ti, char *zn) +{ + int rc; + struct mu_mboxrb_message *dmsg; + + /* Create new message */ + rc = mboxrb_alloc_message (dmp, &dmsg); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mboxrb_alloc_message", dmp->name, + mu_strerror (rc))); + return NULL; + } + rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_start); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_seek", dmp->name, + mu_strerror (rc))); + return NULL; + } + dmsg->message_start -= n; + dmsg->from_length = n; + dmsg->env_sender_len = ti - buf - 10; + while (dmsg->env_sender_len > 6 && buf[dmsg->env_sender_len-1] == ' ') + dmsg->env_sender_len--; + dmsg->env_sender_len -= 5; + + if (zn[0] != '\n') + { + /* + * Ideally, From_ line should not contain time zone info. If it does, + * zn points to the first space before the TZ. The latter can be an + * abbreviated time zone or a numeric offset from UTC (see the comment + * to parse_from_line above). Parse the date line and convert it to + * a normalized form (ctime(3)). + */ + struct tm tm; + char const *fmt; + struct mu_timezone tz; + char *te; + time_t t; + + int numeric_zone = zn[1] == '+' || zn[1] == '-' || mu_isdigit (zn[1]); + if (zn[-3] == ':') + { + if (zn[-6] == ':') + { + if (numeric_zone) + fmt = "%a %b %e %H:%M:%S %z %Y"; + else + fmt = "%a %b %e %H:%M:%S %Z %Y"; + } + else + { + if (numeric_zone) + fmt = "%a %b %e %H:%M %z %Y"; + else + fmt = "%a %b %e %H:%M %Z %Y"; + } + } + else + { + if (zn[-11] == ':') + { + if (numeric_zone) + fmt = "%a %b %e %H:%M:%S %Y %z"; + else + fmt = "%a %b %e %H:%M:%S %Y %Z"; + } + else + { + if (numeric_zone) + fmt = "%a %b %e %H:%M %Y %z"; + else + fmt = "%a %b %e %H:%M %Y %Z"; + } + } + + if (mu_scan_datetime (ti - 10, fmt, &tm, &tz, &te) == 0) + t = mu_datetime_to_utc (&tm, &tz); + else + t = time (NULL); + gmtime_r (&t, &tm); + mu_strftime (dmsg->date, sizeof (dmsg->date), MU_DATETIME_FROM, &tm); + } + else + { + /* + * No zone information in the timestamp. Copy the timestamp to + * the date buffer. The timestamp may or may not contain seconds. + * In the latter case, assume '0' seconds. + */ + if (ti[6] == ':') + memcpy (dmsg->date, ti - 10, MU_DATETIME_FROM_LENGTH); + else + { + memcpy (dmsg->date, ti - 10, 16); + memcpy (dmsg->date + 16, ":00", 3); + dmsg->date[19] = ' '; + memcpy (dmsg->date + 20, ti + 7, 4); + } + dmsg->date[24] = 0; + } + + return dmsg; +} + /* Scan the mailbox starting from the given offset. * * Notes on the mailbox format: @@ -528,7 +675,6 @@ mboxrb_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) char *zn, *ti; int force_init_uids = 0; size_t numlines = 0; - size_t count; # define IS_HEADER(h,b,n) \ ((n) > sizeof (h) - 1 \ @@ -578,32 +724,8 @@ mboxrb_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) rc = MU_ERR_PARSE; goto err; } - - rc = mboxrb_alloc_message (dmp, &dmsg); - if (rc) - { - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s (%s): %s", - __func__, "mboxrb_alloc_message", dmp->name, - mu_strerror (rc))); - goto err; - } - rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_start); - if (rc) - { - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s (%s): %s", - __func__, "mu_stream_seek", dmp->name, - mu_strerror (rc))); - goto err; - } - dmsg->message_start -= n; - dmsg->from_length = n; - dmsg->env_date_start = ti - buf - 10; - dmsg->env_sender_len = dmsg->env_date_start; - while (dmsg->env_sender_len > 6 && buf[dmsg->env_sender_len-1] == ' ') - dmsg->env_sender_len--; - dmsg->env_sender_len -= 5; + if ((dmsg = scan_message_begin (dmp, stream, buf, n, ti, zn)) == NULL) + goto err; state = mboxrb_scan_header; break; @@ -674,55 +796,10 @@ mboxrb_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) case mboxrb_scan_empty_line: if ((ti = parse_from_line (buf, &zn)) != 0) { - /* Finalize current message */ - rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_end); - if (rc) - { - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s (%s): %s", - __func__, "mu_stream_seek", dmp->name, - mu_strerror (rc))); - goto err; - } - dmsg->message_end -= n + 1; - if (dmsg->uid == 0) - force_init_uids = 1; - if (force_init_uids) - mboxrb_message_alloc_uid (dmsg); - - /* Every 100 mesgs update the lock, it should be every minute. */ - if (mailbox->locker && (dmp->mesg_count % 100) == 0) - mu_locker_touchlock (mailbox->locker); - - count = dmp->mesg_count; - mboxrb_dispatch (mailbox, MU_EVT_MESSAGE_ADD, &count); - - /* Create new message */ - rc = mboxrb_alloc_message (dmp, &dmsg); - if (rc) - { - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s (%s): %s", - __func__, "mboxrb_alloc_message", dmp->name, - mu_strerror (rc))); - goto err; - } - rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_start); - if (rc) - { - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s (%s): %s", - __func__, "mu_stream_seek", dmp->name, - mu_strerror (rc))); - goto err; - } - dmsg->message_start -= n; - dmsg->from_length = n; - dmsg->env_date_start = ti - buf - 10; - dmsg->env_sender_len = dmsg->env_date_start; - while (dmsg->env_sender_len > 6 && buf[dmsg->env_sender_len-1] == ' ') - dmsg->env_sender_len--; - dmsg->env_sender_len -= 5; + if (scan_message_finalize (dmp, dmsg, stream, n, &force_init_uids)) + goto err; + if ((dmsg = scan_message_begin (dmp, stream, buf, n, ti, zn)) == NULL) + goto err; state = mboxrb_scan_header; } else if (n == 1 && buf[0] == '\n') @@ -736,24 +813,8 @@ mboxrb_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) if (dmsg) { - /* Finalize the last message */ - rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_end); - if (rc) - { - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s (%s): %s", - __func__, "mu_stream_seek", dmp->name, - mu_strerror (rc))); - goto err; - } - dmsg->message_end--; - if (dmsg->uid == 0) - force_init_uids = 1; - if (force_init_uids) - mboxrb_message_alloc_uid (dmsg); - - count = dmp->mesg_count; - mboxrb_dispatch (mailbox, MU_EVT_MESSAGE_ADD, &count); + if (scan_message_finalize (dmp, dmsg, stream, 0, &force_init_uids)) + goto err; } err: diff --git a/libproto/mbox/message.c b/libproto/mbox/message.c index d33694222..a0e8dd076 100644 --- a/libproto/mbox/message.c +++ b/libproto/mbox/message.c @@ -29,6 +29,7 @@ #include <mailutils/attribute.h> #include <mailutils/envelope.h> #include <mailutils/io.h> +#include <mailutils/util.h> void mu_mboxrb_message_free (struct mu_mboxrb_message *dmsg) @@ -211,45 +212,20 @@ static int mboxrb_envelope_date (mu_envelope_t env, char *buf, size_t len, size_t *pnwrite) { - int rc = 0; - size_t date_len; mu_message_t msg = mu_envelope_get_owner (env); struct mu_mboxrb_message *dmsg = mu_message_get_owner (msg); - if (!dmsg) - return EINVAL; - date_len = dmsg->from_length - dmsg->env_date_start - 1; - if (!buf || len <= 1) { - len = date_len; + len = MU_DATETIME_FROM_LENGTH; } else { - mu_stream_t stream; - - rc = mu_streamref_create_abridged (&stream, - dmsg->mbox->mailbox->stream, - dmsg->message_start + dmsg->env_date_start, - dmsg->message_start + dmsg->from_length); - if (rc) - return rc; - len--; - if (len > date_len) - len = date_len; - rc = mu_stream_read (stream, buf, len, &len); - if (rc == 0) - { - buf[len] = 0; - } - mu_stream_destroy (&stream); - } - if (rc == 0) - { - if (pnwrite) - *pnwrite = len; + len = mu_cpystr (buf, dmsg->date, len); } - return rc; + if (pnwrite) + *pnwrite = len; + return 0; } static int @@ -564,7 +540,7 @@ env_to_stream (struct mu_mboxrb_message const *dmsg, mu_stream_seek (dst, 0, MU_SEEK_CUR, &off); ref->from_length = off - ref->message_start; ref->env_sender_len = strlen (sender); - ref->env_date_start = strlen (sender) + 6; + strncpy (ref->date, date, sizeof (ref->date)); } return rc; diff --git a/libproto/mbox/tests/env.at b/libproto/mbox/tests/env.at index 689cb4ba4..203f10969 100644 --- a/libproto/mbox/tests/env.at +++ b/libproto/mbox/tests/env.at @@ -92,7 +92,7 @@ I believe I can guess that AT_CHECK([mbop -r -m inbox 1 \; env_date \; env_sender], [0], [1 current message -1 env_date: Wed Dec 2 05:53 1992 +1 env_date: Wed Dec 2 05:53:00 1992 1 env_sender: hare@wonder.land ]) @@ -106,56 +106,56 @@ AT_CHECK([mbop -r -m inbox 2 \; env_date \; env_sender], AT_CHECK([mbop -r -m inbox 3 \; env_date \; env_sender], [0], [3 current message -3 env_date: Wed Dec 2 05:53 PST 1992 +3 env_date: Wed Dec 2 13:53:00 1992 3 env_sender: hare@wonder.land ]) AT_CHECK([mbop -r -m inbox 4 \; env_date \; env_sender], [0], [4 current message -4 env_date: Wed Dec 2 05:53:22 PST 1992 +4 env_date: Wed Dec 2 13:53:22 1992 4 env_sender: alice@wonder.land ]) AT_CHECK([mbop -r -m inbox 5 \; env_date \; env_sender], [0], [5 current message -5 env_date: Wed Dec 2 05:53 -0700 1992 +5 env_date: Wed Dec 2 12:53:00 1992 5 env_sender: hare@wonder.land ]) AT_CHECK([mbop -r -m inbox 6 \; env_date \; env_sender], [0], [6 current message -6 env_date: Wed Dec 2 05:53:22 -0700 1992 +6 env_date: Wed Dec 2 12:53:22 1992 6 env_sender: alice@wonder.land ]) AT_CHECK([mbop -r -m inbox 7 \; env_date \; env_sender], [0], [7 current message -7 env_date: Wed Dec 2 05:53 1992 PST +7 env_date: Wed Dec 2 13:53:00 1992 7 env_sender: hare@wonder.land ]) AT_CHECK([mbop -r -m inbox 8 \; env_date \; env_sender], [0], [8 current message -8 env_date: Wed Dec 2 05:53:22 1992 PST +8 env_date: Wed Dec 2 13:53:22 1992 8 env_sender: alice@wonder.land ]) AT_CHECK([mbop -r -m inbox 9 \; env_date \; env_sender], [0], [9 current message -9 env_date: Wed Dec 2 05:53 1992 -0700 +9 env_date: Wed Dec 2 12:53:00 1992 9 env_sender: hare@wonder.land ]) AT_CHECK([mbop -r -m inbox 10 \; env_date \; env_sender], [0], [10 current message -10 env_date: Wed Dec 2 05:53:22 1992 -0700 +10 env_date: Wed Dec 2 12:53:22 1992 10 env_sender: alice@wonder.land ]) |