diff options
Diffstat (limited to 'libmailutils/base/amd.c')
-rw-r--r-- | libmailutils/base/amd.c | 473 |
1 files changed, 296 insertions, 177 deletions
diff --git a/libmailutils/base/amd.c b/libmailutils/base/amd.c index 349a8060a..96be354b1 100644 --- a/libmailutils/base/amd.c +++ b/libmailutils/base/amd.c @@ -1,5 +1,5 @@ /* GNU Mailutils -- a suite of utilities for electronic mail - Copyright (C) 1999-2021 Free Software Foundation, Inc. + Copyright (C) 1999-2024 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -81,7 +81,8 @@ static int amd_close (mu_mailbox_t); static int amd_get_message (mu_mailbox_t, size_t, mu_message_t *); static int amd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid, mu_message_t *pmsg); -static int amd_append_message (mu_mailbox_t, mu_message_t); +static int amd_append_message (mu_mailbox_t, mu_message_t, + mu_envelope_t, mu_attribute_t); static int amd_messages_count (mu_mailbox_t, size_t *); static int amd_messages_recent (mu_mailbox_t, size_t *); static int amd_message_unseen (mu_mailbox_t, size_t *); @@ -500,7 +501,7 @@ amd_open (mu_mailbox_t mailbox, int flags) _amd_prop_create (amd); if (mailbox->locker == NULL) - mu_locker_create (&mailbox->locker, "/dev/null", 0); + mu_locker_create_ext (&mailbox->locker, "/dev/null", NULL); return 0; } @@ -756,7 +757,7 @@ amd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid, } static int -_amd_tempfile (struct _amd_data *amd, FILE **pfile, char **namep) +_amd_tempstream (struct _amd_data *amd, mu_stream_t *pstr, char **namep) { struct mu_tempfile_hints hints; int fd, rc; @@ -764,50 +765,45 @@ _amd_tempfile (struct _amd_data *amd, FILE **pfile, char **namep) hints.tmpdir = amd->name; rc = mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &fd, namep); if (rc == 0) - if ((*pfile = fdopen (fd, "w")) == NULL) - rc = errno; + rc = mu_fd_stream_create (pstr, *namep, fd, MU_STREAM_WRITE); return rc; } static int -_amd_delim (char *str) -{ - if (str[0] == '-') - { - for (; *str == '-'; str++) - ; - for (; *str == ' ' || *str == '\t'; str++) - ; - } - return str[0] == '\n'; -} - -static int _amd_message_save (struct _amd_data *amd, struct _amd_message *mhm, + mu_envelope_t env, int expunge) { mu_stream_t stream = NULL; - char *name = NULL, *buf = NULL, *msg_name, *old_name; - size_t n; - size_t bsize; - size_t nlines, nbytes; + char *name = NULL, *msg_name, *old_name; size_t new_body_start, new_header_lines; - FILE *fp; + mu_stream_t ostr; + mu_stream_stat_buffer stat; mu_message_t msg = mhm->message; mu_header_t hdr; int status; - mu_attribute_t attr; mu_body_t body; - const char *sbuf; - mu_envelope_t env = NULL; - - status = mu_message_size (msg, &bsize); - if (status) - return status; - + static char *exclude_headers_tab[] = { + MU_HEADER_RETURN_PATH, + MU_HEADER_X_IMAPBASE, + MU_HEADER_X_UID, + MU_HEADER_STATUS, + MU_HEADER_ENV_DATE, + MU_HEADER_ENV_SENDER, + NULL, + }; + char **exclude_headers = exclude_headers_tab; + size_t i; + status = amd->new_msg_file_name (mhm, mhm->attr_flags, expunge, &msg_name); if (status) - return status; + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("new_msg_file_name failed: %s", + mu_strerror (status))); + return status; + } + if (!msg_name) { /* Unlink the original file */ @@ -820,133 +816,136 @@ _amd_message_save (struct _amd_data *amd, struct _amd_message *mhm, return status; } - status = _amd_tempfile (mhm->amd, &fp, &name); + status = _amd_tempstream (mhm->amd, &ostr, &name); if (status) { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("_amd_tempstream failed: %s", + mu_strerror (status))); free (msg_name); return status; } - - /* Try to allocate large buffer */ - for (; bsize > 1; bsize /= 2) - if ((buf = malloc (bsize))) - break; - - if (!bsize) - { - unlink (name); - free (name); - free (msg_name); - return ENOMEM; - } + mu_stream_set_stat (ostr, + MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT) | + MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUTLN), + stat); /* Copy flags */ - mu_message_get_header (msg, &hdr); - mu_header_get_streamref (hdr, &stream); - status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); - if (status) - { - /* FIXME: Provide a common exit point for all error - cases */ - unlink (name); - free (name); - free (msg_name); - mu_stream_destroy (&stream); - return status; - } - - nlines = nbytes = 0; - while ((status = mu_stream_readline (stream, buf, bsize, &n)) == 0 - && n != 0) + if (env) { - if (_amd_delim (buf)) - break; + char const *s; - if (!(mu_c_strncasecmp (buf, "status:", 7) == 0 - || mu_c_strncasecmp (buf, - MU_HEADER_ENV_DATE ":", sizeof (MU_HEADER_ENV_DATE)) == 0 - || mu_c_strncasecmp (buf, - MU_HEADER_ENV_SENDER ":", sizeof (MU_HEADER_ENV_SENDER)) == 0)) + if (mu_envelope_sget_sender (env, &s) == 0) + mu_stream_printf (ostr, "%s: %s\n", MU_HEADER_RETURN_PATH, s); + else + exclude_headers++; // Preserve MU_HEADER_RETURN_PATH + if (mu_envelope_sget_date (env, &s) == 0) { - nlines++; - nbytes += fprintf (fp, "%s", buf); + struct tm tm; + struct mu_timezone tz; + + if (mu_parse_date_dtl (s, NULL, NULL, &tm, &tz, NULL) == 0) + { + /* Format a "Received:" header with that date */ + mu_stream_printf (ostr, + "Received: from %s\n" + "\tby %s; ", + "localhost", "localhost"); + mu_c_streamftime (ostr, MU_DATETIME_FORM_RFC822, &tm, &tz); + mu_stream_write (ostr, "\n", 1, NULL); + } } } - mu_stream_destroy (&stream); + else + exclude_headers++; // Preserve MU_HEADER_RETURN_PATH - mu_message_get_envelope (msg, &env); - if (mu_envelope_sget_date (env, &sbuf) == 0) + mu_message_get_header (msg, &hdr); + mu_header_get_streamref (hdr, &stream); + status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); + if (status) { - /* NOTE: buffer might be terminated with \n */ - while (*sbuf && mu_isspace (*sbuf)) - sbuf++; - nbytes += fprintf (fp, "%s: %s", MU_HEADER_ENV_DATE, sbuf); - - if (*sbuf && sbuf[strlen (sbuf) - 1] != '\n') - nbytes += fprintf (fp, "\n"); - - nlines++; + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("mu_stream_seek on header stream failed: %s", + mu_strerror (status))); + goto err; } - - if (mu_envelope_sget_sender (env, &sbuf) == 0) + status = mu_stream_header_copy (ostr, stream, exclude_headers); + mu_stream_destroy (&stream); + if (status) { - fprintf (fp, "%s: %s\n", MU_HEADER_ENV_SENDER, sbuf); - nlines++; + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("mu_stream_header_copy failed: %s", + mu_strerror (status))); + goto err; } - + if (!(amd->capabilities & MU_AMD_STATUS)) { /* Add status */ char statbuf[MU_STATUS_BUF_SIZE]; - mu_message_get_attribute (msg, &attr); - mu_attribute_to_string (attr, statbuf, sizeof (statbuf), &n); - if (n) + if (mu_attribute_flags_to_string (mhm->attr_flags, + statbuf, sizeof (statbuf), NULL) == 0 + && statbuf[0] != 0) { - nbytes += fprintf (fp, "Status: %s\n", statbuf); - nlines++; + mu_stream_printf (ostr, "%s: %s\n", MU_HEADER_STATUS, statbuf); } } - nbytes += fprintf (fp, "\n"); - nlines++; + /* + * Header_size can be less than body_start if MU_AMD_DASHDELIM is set + * and a line of dashes was used originally to delimit body from headers + * (e.g. for draft file in MH format). + */ + for (i = mhm->header_size; i < mhm->body_start; i++) + mu_stream_write (ostr, "-", 1, NULL); + + if (mu_stream_err (ostr)) + { + status = mu_stream_last_error (ostr); + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("output error on temporary stream: %s", + mu_strerror (status))); + goto err; + } - new_header_lines = nlines; - new_body_start = nbytes; + mu_stream_write (ostr, "\n", 1, NULL); - /* Copy message body */ + new_header_lines = stat[MU_STREAM_STAT_OUTLN]; + new_body_start = stat[MU_STREAM_STAT_OUT]; + /* Clear the counters */ + stat[MU_STREAM_STAT_OUT] = stat[MU_STREAM_STAT_OUTLN] = 0; + + /* Copy message body */ mu_message_get_body (msg, &body); mu_body_get_streamref (body, &stream); status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); if (status) { - unlink (name); - free (name); - free (msg_name); - mu_stream_destroy (&stream); - return status; + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("mu_stream_seek on body stream failed: %s", + mu_strerror (status))); + goto err; } - - nlines = 0; - while (mu_stream_read (stream, buf, bsize, &n) == 0 && n != 0) + + status = mu_stream_copy (ostr, stream, 0, NULL); + mu_stream_destroy (&stream); + if (status) { - char *p; - for (p = buf; p < buf + n; p++) - if (*p == '\n') - nlines++; - fwrite (buf, 1, n, fp); - nbytes += n; + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("copying message body failed: %s", + mu_strerror (status))); + goto err; } - mu_stream_destroy (&stream); mhm->header_lines = new_header_lines; + mhm->header_size = new_body_start; mhm->body_start = new_body_start; - mhm->body_lines = nlines; - mhm->body_end = nbytes; - - free (buf); - fclose (fp); + mhm->body_lines = stat[MU_STREAM_STAT_OUTLN]; + mhm->body_end = stat[MU_STREAM_STAT_OUT]; + + mu_stream_destroy (&ostr); status = amd->cur_msg_file_name (mhm, 1, &old_name); if (status == 0) @@ -988,6 +987,10 @@ _amd_message_save (struct _amd_data *amd, struct _amd_message *mhm, } free (old_name); } + err: + mu_stream_destroy (&ostr); + if (status) + unlink (name); free (msg_name); free (name); @@ -995,12 +998,13 @@ _amd_message_save (struct _amd_data *amd, struct _amd_message *mhm, } static int -amd_append_message (mu_mailbox_t mailbox, mu_message_t msg) +amd_append_message (mu_mailbox_t mailbox, mu_message_t msg, + mu_envelope_t env, mu_attribute_t atr) { int status; struct _amd_data *amd = mailbox->data; struct _amd_message *mhm; - + if (!mailbox || !msg) return EINVAL; @@ -1033,7 +1037,11 @@ amd_append_message (mu_mailbox_t mailbox, mu_message_t msg) } mhm->message = msg; - status = _amd_message_save (amd, mhm, 0); + + if (atr) + mu_attribute_get_flags (atr, &mhm->attr_flags); + + status = _amd_message_save (amd, mhm, env, 0); if (status) { free (mhm); @@ -1050,7 +1058,7 @@ amd_append_message (mu_mailbox_t mailbox, mu_message_t msg) } if (amd->msg_finish_delivery) - status = amd->msg_finish_delivery (amd, mhm, msg); + status = amd->msg_finish_delivery (amd, mhm, msg, atr); if (status == 0 && mailbox->observable) { @@ -1292,7 +1300,7 @@ _amd_update_message (struct _amd_data *amd, struct _amd_message *mhm, return rc; } - rc = _amd_message_save (amd, mhm, expunge); + rc = _amd_message_save (amd, mhm, NULL, expunge); if (rc) { mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, @@ -1386,6 +1394,7 @@ amd_expunge (mu_mailbox_t mailbox) pp = amd_pool_lookup (mhm); if (pp) *pp = NULL; + amd_message_stream_close (mhm); mu_message_destroy (&mhm->message, mhm); if (amd->msg_free) amd->msg_free (mhm); @@ -1640,18 +1649,11 @@ static int amd_scan_message (struct _amd_message *mhm) { mu_stream_t stream = mhm->stream; - char buf[1024]; - size_t off; - size_t n; - int status; - int in_header = 1; - size_t hlines = 0; - size_t blines = 0; - size_t body_start = 0; - struct stat st; - char *msg_name; struct _amd_data *amd = mhm->amd; int amd_capa = amd->capabilities; + char *msg_name; + struct stat st; + int status; /* Check if the message was modified after the last scan */ status = mhm->amd->cur_msg_file_name (mhm, 1, &msg_name); @@ -1670,7 +1672,6 @@ amd_scan_message (struct _amd_message *mhm) return 0; } - off = 0; status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); if (status) mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, @@ -1678,53 +1679,168 @@ amd_scan_message (struct _amd_message *mhm) msg_name, mu_strerror (status))); else { - while ((status = mu_stream_readline (stream, buf, sizeof (buf), &n)) == 0 - && n != 0) + enum + { + amd_scan_init, + amd_scan_header, + amd_scan_header_newline, + amd_scan_header_dash, + amd_scan_header_expect, + amd_scan_body, + } state = amd_scan_init; + static char expect[] = "status:"; + int i; + + char cur; + size_t n; + size_t ndash; + + mhm->mtime = st.st_mtime; + mhm->header_lines = 0; + mhm->body_lines = 0; + + while ((status = mu_stream_read (stream, &cur, 1, &n)) == 0) { - if (in_header) + if (n == 0) + break; + + switch (state) { - if (buf[0] == '\n') + case amd_scan_init: + state = amd_scan_header_newline; + i = 0; + break; + + case amd_scan_header: + if (cur == '\n') + state = amd_scan_header_newline; + break; + + case amd_scan_header_newline: + mhm->header_lines++; + if (cur == '\n') + { + status = mu_stream_seek (stream, 0, MU_SEEK_CUR, + &mhm->body_start); + if (status) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_seek", + amd->name, + mu_strerror (status))); + return status; + } + mhm->body_end = mhm->body_start; + mhm->header_size = mhm->body_start; + state = amd_scan_body; + } + else if ((amd_capa & MU_AMD_DASHDELIM) && cur == '-') + { + state = amd_scan_header_dash; + ndash = 1; + } + else { - in_header = 0; - body_start = off + 1; + state = amd_scan_header; + if (!(amd_capa & MU_AMD_STATUS)) + { + i = 0; + if (expect[i] == mu_tolower (cur)) + { + i++; + state = amd_scan_header_expect; + break; + } + } + } + break; + + case amd_scan_header_dash: + if (cur == '\n') + { + status = mu_stream_seek (stream, 0, MU_SEEK_CUR, + &mhm->body_start); + if (status) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_seek", + amd->name, + mu_strerror (status))); + return status; + } + mhm->body_end = mhm->body_start; + mhm->header_size = mhm->body_start - ndash; + state = amd_scan_body; } - if (buf[n - 1] == '\n') - hlines++; + else if (cur == '-') + ndash++; + else + state = amd_scan_header; + break; - /* Process particular attributes */ - if (!(amd_capa & MU_AMD_STATUS) && - mu_c_strncasecmp (buf, "status:", 7) == 0) + case amd_scan_header_expect: + if (cur == '\n') { - int deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; - mu_attribute_string_to_flags (buf, &mhm->attr_flags); - mhm->attr_flags |= deleted; + state = amd_scan_header_newline; } + else + { + int c = mu_tolower (cur); + if (expect[i] != c) + { + state = amd_scan_header; + break; + } + + if (c == ':') + { + char *buf = NULL; + size_t size = 0; + size_t n; + + status = mu_stream_getline (stream, &buf, &size, &n); + if (status) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_getline", + amd->name, + mu_strerror (status))); + return status; + } + if (n > 0) + { + int deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; + buf[n-1] = 0; + mu_attribute_string_to_flags (buf, &mhm->attr_flags); + mhm->attr_flags |= deleted; + } + + free (buf); + state = amd_scan_header_newline; + } + else + { + i++; + if (expect[i] == 0) + state = amd_scan_header_newline; + } + } + break; + + case amd_scan_body: + mhm->body_end++; + if (cur == '\n') + { + mhm->body_lines++; + } + break; } - else - { - if (buf[n - 1] == '\n') - blines++; - } - off += n; } - if (status) - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("amd_scan_message(%s): %s", - msg_name, mu_strerror (status))); - } - + } free (msg_name); - - if (status == 0) - { - mhm->mtime = st.st_mtime; - if (!body_start) - body_start = off; - mhm->header_lines = hlines; - mhm->body_lines = blines; - mhm->body_start = body_start; - mhm->body_end = off; - } return status; } @@ -1882,9 +1998,12 @@ amd_message_stream_open (struct _amd_message *mhm) status = amd_scan_message (mhm); if (status) - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("amd_message_stream_open: amd_scan_message=%s", - mu_strerror (status))); + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("amd_message_stream_open: amd_scan_message=%s", + mu_strerror (status))); + amd_message_stream_close (mhm); + } return status; } @@ -2040,7 +2159,7 @@ amd_header_fill (void *data, char **pbuf, size_t *plen) if (status) return status; - len = mhm->body_start; + len = mhm->header_size; buffer = malloc (len); if (!buffer) return ENOMEM; |