summaryrefslogtreecommitdiff
path: root/libmailutils/base/amd.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/base/amd.c')
-rw-r--r--libmailutils/base/amd.c473
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;

Return to:

Send suggestions and report system problems to the System administrator.