Commit 48832b4a 48832b4ab9ea10b93c70ad756d9814c261b55f7e by Alain Magloire

UIDL is not looking for "X-UIDL" if not do an md5 of the message.

When expunging check if the message as been modified, for example
"X-UIDL" maybe add in the header it will now be save in the message.
expunge reuse the same code of mbox_append_message().
add a new header_set_fill() to get the header of the messages form the mailbox.
1 parent 4737689b
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
......@@ -25,6 +25,8 @@
#include <attribute0.h>
static int flags_to_string __P ((int, char *, size_t, size_t *));
int
attribute_create (attribute_t *pattr, void *owner)
{
......@@ -59,6 +61,12 @@ attribute_get_owner (attribute_t attr)
}
int
attribute_is_modified (attribute_t attr)
{
return (attr) ? attr->flags != 0 : 0;
}
int
attribute_get_flags (attribute_t attr, int *pflags)
{
if (attr == NULL)
......@@ -76,7 +84,7 @@ attribute_set_flags (attribute_t attr, int flags)
if (attr == NULL)
return EINVAL;
if (attr->_set_flags)
return attr->_set_flags (attr, flags);
attr->_set_flags (attr, flags);
attr->flags |= flags;
return 0;
}
......@@ -446,7 +454,15 @@ string_to_flags (const char *buffer, int *pflags)
return 0;
}
int
int
attribute_to_string (attribute_t attr, char *buffer, size_t len, size_t *pn)
{
int flags = 0;;
attribute_get_flags (attr, &flags);
return flags_to_string (flags, buffer, len, pn);
}
static int
flags_to_string (int flags, char *buffer, size_t len, size_t *pn)
{
char status[32];
......
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
......@@ -307,22 +307,10 @@ body_get_fd (stream_t stream, int *pfd)
static FILE *
lazy_create ()
{
FILE *file;
#ifdef HAVE_MKSTEMP
char tmpbuf[L_tmpnam + 1];
int fd;
if (tmpnam (tmpbuf) == NULL ||
(fd = mkstemp (tmpbuf)) == -1 ||
(file = fdopen(fd, "w+")) == NULL)
return NULL;
(void)remove(tmpbuf);
#else
file = tmpfile ();
FILE *file = tmpfile ();
//file = fopen ("/tmp/mystuff", "w+");
/* make sure the mode is right */
if (file)
fchmod (fileno (file), 0600);
#endif
return file;
}
......
......@@ -334,11 +334,36 @@ folder_list (folder_t folder, const char *dirname, const char *basename,
struct folder_list *pflist)
{
if (folder == NULL || folder->_list == NULL)
return ENOSYS;
return EINVAL;
return folder->_list (folder, dirname, basename, pflist);
}
int
folder_lsub (folder_t folder, const char *dirname, const char *basename,
struct folder_list *pflist)
{
if (folder == NULL || folder->_lsub == NULL)
return ENOSYS;
return folder->_lsub (folder, dirname, basename, pflist);
}
int
folder_subscribe (folder_t folder, const char *name)
{
if (folder == NULL || folder->_subscribe == NULL)
return EINVAL;
return folder->_subscribe (folder, name);
}
int
folder_unsubscribe (folder_t folder, const char *name)
{
if (folder == NULL || folder->_unsubscribe == NULL)
return EINVAL;
return folder->_unsubscribe (folder, name);
}
int
folder_list_destroy (struct folder_list *pflist)
{
size_t i;
......@@ -353,7 +378,10 @@ folder_list_destroy (struct folder_list *pflist)
free (pflist->element[i]);
}
}
free (pflist->element);
if (i > 0)
free (pflist->element);
pflist->element = NULL;
pflist->num = 0;
return 0;
}
......
......@@ -24,6 +24,7 @@
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <fnmatch.h>
#include <imap0.h>
......@@ -384,17 +385,93 @@ folder_imap_list (folder_t folder, const char *ref, const char *name,
{
f_imap_t f_imap = folder->data;
int status = 0;
char *path = NULL;
/* NOOP. */
if (pflist == NULL)
return EINVAL;
if (ref == NULL)
ref = "";
if (name == NULL)
name = "";
path = strdup ("");
if (path == NULL)
return ENOMEM;
/* We break the string to pieces and change the occurences of "*?[" for
the imap magic "%" for expansion. Then reassemble the string:
"/home/?/Mail/a*lain*" --> "/usr/%/Mail/%". */
{
int done = 0;
size_t i;
char **node = NULL;
size_t nodelen = 0;
const char *p = name;
/* Disassemble. */
while (!done && *p)
{
char **n;
n = realloc (node, (nodelen + 1) * sizeof (*node));
if (n == NULL)
break;
node = n;
if (*p == '/')
{
node[nodelen] = strdup ("/");
p++;
}
else
{
const char *s = strchr (p, '/');
if (s)
{
node[nodelen] = calloc (s - p + 1, sizeof (char));
if (node[nodelen])
memcpy (node[nodelen], p, s - p);
p = s;
}
else
{
node[nodelen] = strdup (p);
done = 1;
}
if (node[nodelen] && strpbrk (node[nodelen], "*?["))
{
free (node[nodelen]);
node[nodelen] = strdup ("%");
}
}
nodelen++;
if (done)
break;
}
/* Reassemble. */
for (i = 0; i < nodelen; i++)
{
if (node[i])
{
char *pth;
pth = realloc (path, strlen (path) + strlen (node[i]) + 1);
if (pth)
{
path = pth;
strcat (path, node[i]);
}
free (node[i]);
}
}
if (node)
free (node);
}
switch (f_imap->state)
{
case IMAP_NO_STATE:
status = imap_writeline (f_imap, "g%d LIST \"%s\" \"%s\"\r\n",
f_imap->seq++, ref, name);
f_imap->seq++, ref, path);
free (path);
CHECK_ERROR (f_imap, status);
FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
f_imap->state = IMAP_LIST;
......@@ -412,6 +489,39 @@ folder_imap_list (folder_t folder, const char *ref, const char *name,
default:
break;
}
/* Build the folder list. */
if (f_imap->callback.flist.num > 0)
{
struct list_response **plist = NULL;
size_t num = f_imap->callback.flist.num;
size_t j = 0;
plist = calloc (num, sizeof (*plist));
if (plist)
{
size_t i;
for (i = 0; i < num; i++)
{
struct list_response *lr = f_imap->callback.flist.element[i];
//printf ("%s --> %s\n", lr->name, name);
if (fnmatch (name, lr->name, 0) == 0)
{
plist[i] = calloc (1, sizeof (**plist));
if (plist[i] == NULL
|| (plist[i]->name = strdup (lr->name)) == NULL)
{
break;
}
plist[i]->type = lr->type;
plist[i]->separator = lr->separator;
j++;
}
}
}
pflist->element = plist;
pflist->num = j;
}
folder_list_destroy (&(f_imap->callback.flist));
f_imap->state = IMAP_NO_STATE;
return status;
}
......@@ -423,10 +533,13 @@ folder_imap_lsub (folder_t folder, const char *ref, const char *name,
f_imap_t f_imap = folder->data;
int status = 0;
if (ref == NULL)
ref = "";
if (name == NULL)
name = "";
/* NOOP. */
if (pflist == NULL)
return EINVAL;
if (ref == NULL) ref = "";
if (name == NULL) name = "";
switch (f_imap->state)
{
case IMAP_NO_STATE:
......@@ -449,6 +562,37 @@ folder_imap_lsub (folder_t folder, const char *ref, const char *name,
default:
break;
}
/* Build the folder list. */
if (f_imap->callback.flist.num > 0)
{
struct list_response **plist = NULL;
size_t num = f_imap->callback.flist.num;
size_t j = 0;
plist = calloc (num, sizeof (*plist));
if (plist)
{
size_t i;
for (i = 0; i < num; i++)
{
struct list_response *lr = f_imap->callback.flist.element[i];
//printf ("%s --> %s\n", lr->name, name);
plist[i] = calloc (1, sizeof (**plist));
if (plist[i] == NULL
|| (plist[i]->name = strdup (lr->name)) == NULL)
{
break;
}
plist[i]->type = lr->type;
plist[i]->separator = lr->separator;
j++;
}
}
pflist->element = plist;
pflist->num = j;
folder_list_destroy (&(f_imap->callback.flist));
}
f_imap->state = IMAP_NO_STATE;
f_imap->state = IMAP_NO_STATE;
return 0;
}
......@@ -755,6 +899,86 @@ imap_string (f_imap_t f_imap)
return status;
}
/* FIXME: does not worl for nobloking. */
static int
imap_list (f_imap_t f_imap)
{
char *tok;
char *sp = NULL;
size_t len = f_imap->nl - f_imap->buffer - 1;
char *buffer;
struct list_response **plr;
struct list_response *lr;
buffer = alloca (len);
memcpy (buffer, f_imap->buffer, len);
buffer[len] = '\0';
plr = realloc (f_imap->callback.flist.element,
(f_imap->callback.flist.num + 1) * sizeof (*plr));
if (plr == NULL)
return ENOMEM;
f_imap->callback.flist.element = plr;
lr = plr[f_imap->callback.flist.num] = calloc (1, sizeof (*lr));
if (lr == NULL)
return ENOMEM;
(f_imap->callback.flist.num)++;
/* Glob untag. */
tok = strtok_r (buffer, " ", &sp);
/* Glob LIST. */
tok = strtok_r (NULL, " ", &sp);
/* Get the attibutes. */
tok = strtok_r (NULL, ")", &sp);
if (tok)
{
char *s = NULL;
char *p = tok;
while ((tok = strtok_r (p, " ()", &s)) != NULL)
{
if (strcasecmp (tok, "\\Noselect") == 0)
{
}
else if (strcasecmp (tok, "\\Marked") == 0)
{
}
else if (strcasecmp (tok, "\\Unmarked") == 0)
{
}
else if (strcasecmp (tok, "\\Noinferiors") == 0)
{
lr->type |= MU_FOLDER_ATTRIBUTE_FILE;
}
else
{
lr->type |= MU_FOLDER_ATTRIBUTE_DIRECTORY;
}
p = NULL;
}
}
/* Hiearchy delimeter. */
tok = strtok_r (NULL, " ", &sp);
if (tok && strlen (tok) > 2)
lr->separator = tok[1];
/* The path. */
tok = strtok_r (NULL, " ", &sp);
if (tok)
{
char *s = strchr (tok, '{');
if (s)
{
size_t n = strtoul (s + 1, NULL, 10);
lr->name = calloc (n + 1, sizeof (char));
f_imap->ptr = f_imap->buffer;
imap_readline (f_imap);
memcpy (lr->name, f_imap->buffer, n);
lr->name[n] = '\0';
}
else
lr->name = strdup (tok);
}
return 0;
}
/* Helping function to figure out the section name of the message: for example
a 2 part message with the first part being sub in two will be:
{1}, {1,1} {1,2} The first subpart of the message and its sub parts
......@@ -803,6 +1027,7 @@ section_name (msg_imap_t msg_imap)
return section;
}
/* FIXME: This does not work for nonblocking. */
/* Recursive call, to parse the dismay of parentesis "()" in a BODYSTRUCTURE
call, not we use the short form of BODYSTRUCTURE, BODY with no argument. */
static int
......@@ -1231,12 +1456,12 @@ imap_parse (f_imap_t f_imap)
char *tag, *response, *remainder;
char *buffer;
status = imap_readline (f_imap);
/* Comment out to see all reading traffic. */
//fprintf (stderr, "\t\t%s", f_imap->buffer);
if (status != 0)
{
break;
}
/* Comment out to see all reading traffic. */
fprintf (stderr, "\t\t%s", f_imap->buffer);
/* We do not want to step over f_imap->buffer since it can be use
further down the chain. */
......@@ -1429,9 +1654,11 @@ imap_parse (f_imap_t f_imap)
}
else if (strcmp (response, "LIST") == 0)
{
status = imap_list (f_imap);
}
else if (strcmp (response, "LSUB") == 0)
{
status = imap_list (f_imap);
}
else if (strcmp (remainder, "RECENT") == 0)
{
......
......@@ -25,6 +25,7 @@
#include <unistd.h>
#include <string.h>
#include <glob.h>
#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>
......@@ -83,19 +84,27 @@ static struct _record _path_record =
record_t path_record = &_path_record;
/* lsub/subscribe/unsubscribe are not needed. */
static void folder_mbox_destroy __P ((folder_t));
static int folder_mbox_open __P ((folder_t, int));
static int folder_mbox_close __P ((folder_t));
static int folder_mbox_delete __P ((folder_t, const char *));
static int folder_mbox_rename __P ((folder_t , const char *, const char *));
static int folder_mbox_list __P ((folder_t, const char *, const char *,
struct folder_list *));
static void folder_mbox_destroy __P ((folder_t));
static int folder_mbox_open __P ((folder_t, int));
static int folder_mbox_close __P ((folder_t));
static int folder_mbox_delete __P ((folder_t, const char *));
static int folder_mbox_rename __P ((folder_t , const char *,
const char *));
static int folder_mbox_list __P ((folder_t, const char *, const char *,
struct folder_list *));
static int folder_mbox_subscribe __P ((folder_t, const char *));
static int folder_mbox_unsubscribe __P ((folder_t, const char *));
static int folder_mbox_lsub __P ((folder_t, const char *, const char *,
struct folder_list *));
static char *get_pathname __P ((const char *, const char *));
struct _fmbox
{
char *dirname;
char **subscribe;
size_t sublen;
};
typedef struct _fmbox *fmbox_t;
......@@ -126,9 +135,11 @@ _folder_mbox_init (folder_t folder)
folder->_close = folder_mbox_close;
folder->_list = folder_mbox_list;
folder->_lsub = folder_mbox_lsub;
folder->_subscribe = folder_mbox_subscribe;
folder->_unsubscribe = folder_mbox_unsubscribe;
folder->_delete = folder_mbox_delete;
folder->_rename = folder_mbox_rename;
return 0;
}
......@@ -140,6 +151,8 @@ folder_mbox_destroy (folder_t folder)
fmbox_t fmbox = folder->data;
if (fmbox->dirname)
free (fmbox->dirname);
if (fmbox->subscribe)
free (fmbox->subscribe);
free (folder->data);
folder->data = NULL;
}
......@@ -150,7 +163,6 @@ static int
folder_mbox_open (folder_t folder, int flags)
{
(void)(folder);
(void)(flags);
return 0;
}
......@@ -281,6 +293,84 @@ folder_mbox_list (folder_t folder, const char *dirname, const char *pattern,
return status;
}
static int
folder_mbox_lsub (folder_t folder, const char *ref, const char *name,
struct folder_list *pflist)
{
fmbox_t fmbox = folder->data;
size_t j = 0;
if (pflist == NULL)
return EINVAL;
(void)ref;
if (name == NULL || *name == '\0')
name = "*";
if (fmbox->sublen > 0)
{
struct list_response **plist;
size_t i;
plist = calloc (fmbox->sublen, sizeof (*plist));
for (i = 0; i < fmbox->sublen; i++)
{
if (fmbox->subscribe[i]
&& fnmatch (name, fmbox->subscribe[i], 0) == 0)
{
plist[i] = calloc (1, sizeof (**plist));
if (plist[i] == NULL
|| (plist[i]->name = strdup (fmbox->subscribe[i])) == NULL)
break;
plist[i]->type = MU_FOLDER_ATTRIBUTE_FILE;
plist[i]->separator = '/';
j++;
}
}
pflist->element = plist;
}
pflist->num = j;
return 0;
}
static int
folder_mbox_subscribe (folder_t folder, const char *name)
{
fmbox_t fmbox = folder->data;
char **tmp;
size_t i;
for (i = 0; i < fmbox->sublen; i++)
{
if (fmbox->subscribe[i] && strcmp (fmbox->subscribe[i], name) == 0)
return 0;
}
tmp = realloc (fmbox->subscribe, (fmbox->sublen + 1) * sizeof (*tmp));
if (tmp == NULL)
return ENOMEM;
fmbox->subscribe = tmp;
fmbox->subscribe[fmbox->sublen] = strdup (name);
if (fmbox->subscribe[fmbox->sublen] == NULL)
return ENOMEM;
fmbox->sublen++;
return 0;
}
static int
folder_mbox_unsubscribe (folder_t folder, const char *name)
{
fmbox_t fmbox = folder->data;
size_t i;
for (i = 0; i < fmbox->sublen; i++)
{
if (fmbox->subscribe[i] && strcmp (fmbox->subscribe[i], name) == 0)
{
free (fmbox->subscribe[i]);
fmbox->subscribe[i] = NULL;
return 0;
}
}
return ENOENT;
}
static char *
get_pathname (const char *dirname, const char *basename)
{
......
......@@ -28,12 +28,14 @@
#include <mailutils/stream.h>
#include <header0.h>
static int header_parse (header_t h, const char *blurb, int len);
static int header_read (stream_t is, char *buf, size_t buflen,
off_t off, size_t *pnread);
static int header_write (stream_t os, const char *buf, size_t buflen,
off_t off, size_t *pnwrite);
static int fill_blurb (header_t header);
#define HEADER_MODIFIED 1
static int header_parse __P ((header_t, const char *, int));
static int header_read __P ((stream_t, char *, size_t, off_t, size_t *));
static int header_readline __P ((stream_t, char *, size_t, off_t, size_t *));
static int header_write __P ((stream_t, const char *, size_t, off_t,
size_t *));
static int fill_blurb __P ((header_t));
int
header_create (header_t *ph, const char *blurb, size_t len, void *owner)
......@@ -79,6 +81,12 @@ header_get_owner (header_t header)
return (header) ? header->owner : NULL;
}
int
header_is_modified (header_t header)
{
return (header) ? (header->flags & HEADER_MODIFIED) : 0;
}
/* Parsing is done in a rather simple fashion.
meaning we just consider an entry to be
a field-name an a field-value. So they
......@@ -298,6 +306,7 @@ header_set_value (header_t header, const char *fn, const char *fv, int replace)
}
header_parse (header, blurb, len + header->blurb_len);
free (blurb);
header->flags |= HEADER_MODIFIED;
return 0;
}
......@@ -498,23 +507,43 @@ header_set_stream (header_t header, stream_t stream, void *owner)
return 0;
}
int
header_set_fill (header_t header, int
(*_fill) __P ((header_t, char *, size_t, off_t, size_t *)),
void *owner)
{
if (header == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->_fill = _fill;
return 0;
}
static int
fill_blurb (header_t header)
{
stream_t is;
stream_t is = NULL;
int status;
char buf[1024];
char *tbuf;
size_t nread = 0;
status = header_get_stream (header, &is);
if (status != 0)
return status;
if (header->_fill == NULL)
{
status = header_get_stream (header, &is);
if (status != 0)
return status;
}
do
{
status = stream_read (is, buf, sizeof (buf), header->temp_blurb_len,
&nread);
if (header->_fill)
status = header->_fill (header, buf, sizeof (buf),
header->temp_blurb_len, &nread) ;
else
status = stream_read (is, buf, sizeof (buf),
header->temp_blurb_len, &nread);
if (status != 0)
{
if (status != EAGAIN && status != EINTR)
......@@ -572,8 +601,7 @@ header_write (stream_t os, const char *buf, size_t buflen,
}
static int
header_read (stream_t is, char *buf, size_t buflen,
off_t off, size_t *pnread)
header_read (stream_t is, char *buf, size_t buflen, off_t off, size_t *pnread)
{
header_t header = stream_get_owner (is);
int len;
......@@ -581,6 +609,14 @@ header_read (stream_t is, char *buf, size_t buflen,
if (is == NULL || header == NULL)
return EINVAL;
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
{
int err = fill_blurb (header);
if (err != 0)
return err;
}
len = header->blurb_len - off;
if (len > 0)
{
......@@ -598,6 +634,52 @@ header_read (stream_t is, char *buf, size_t buflen,
return 0;
}
static int
header_readline (stream_t is, char *buf, size_t buflen, off_t off, size_t *pn)
{
header_t header = stream_get_owner (is);
int len;
if (is == NULL || header == NULL)
return EINVAL;
if (buf == NULL || buflen == 0)
{
if (pn)
*pn = 0;
return 0;
}
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
{
int err = fill_blurb (header);
if (err != 0)
return err;
}
buflen--; /* Space for the null. */
len = header->blurb_len - off;
if (len > 0)
{
char *nl = memchr (header->blurb + off, '\n', len);
if (nl)
len = nl - (header->blurb + off) + 1;
len = (buflen < (size_t)len) ? buflen : len;
memcpy (buf, header->blurb + off, len);
buf[len] = '\0';
}
else
len = 0;
if (pn)
*pn = len;
return 0;
}
int
header_get_stream (header_t header, stream_t *pstream)
{
......@@ -608,7 +690,8 @@ header_get_stream (header_t header, stream_t *pstream)
int status = stream_create (&(header->stream), MU_STREAM_RDWR, header);
if (status != 0)
return status;
stream_set_read (header->stream, header_read, header);
stream_set_read (header->stream, header_read, header);
stream_set_readline (header->stream, header_readline, header);
stream_set_write (header->stream, header_write, header);
}
*pstream = header->stream;
......
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
......@@ -61,6 +61,7 @@ struct _header
size_t blurb_len;
size_t hdr_count;
struct _hdr *hdr;
int flags;
/* Streams. */
stream_t stream;
......@@ -68,6 +69,7 @@ struct _header
int (*_set_value) __P ((header_t, const char *, const char *, int));
int (*_lines) __P ((header_t, size_t *));
int (*_size) __P ((header_t, size_t *));
int (*_fill) __P ((header_t, char *, size_t, off_t, size_t *));
};
#ifdef _cplusplus
......
......@@ -121,6 +121,7 @@ struct literal_string
size_t total;
msg_imap_t msg_imap;
enum imap_state type;
struct folder_list flist;
size_t nleft; /* nleft to read in the literal. */
};
......
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
......@@ -32,7 +32,7 @@
/*
*/
int
mailer_create (mailer_t *pmailer, const char *name, int id)
mailer_create (mailer_t *pmailer, const char *name)
{
int status = EINVAL;
record_t record = NULL;
......@@ -42,7 +42,6 @@ mailer_create (mailer_t *pmailer, const char *name, int id)
iterator_t iterator;
int found = 0;
(void)id;
if (pmailer == NULL)
return EINVAL;
......
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
......@@ -183,15 +183,37 @@ _mapfile_size (stream_t stream, off_t *psize)
{
struct _mapfile_stream *mfs = stream_get_owner (stream);
struct stat stbuf;
int err = 0;
if (mfs == NULL || mfs->ptr == NULL)
return EINVAL;
msync (mfs->ptr, mfs->size, MS_SYNC);
if (fstat(mfs->fd, &stbuf) != 0)
return errno;
if (psize)
*psize = stbuf.st_size;
return 0;
if (mfs->size != (size_t)stbuf.st_size)
{
if (munmap (mfs->ptr, mfs->size) == 0)
{
mfs->size = stbuf.st_size;
mfs->ptr = mmap (0, mfs->size, mfs->flags , MAP_SHARED, mfs->fd, 0);
if (mfs->ptr == MAP_FAILED)
err = errno;
}
else
err = errno;
}
if (err != 0)
{
mfs->ptr = NULL;
close (mfs->fd);
mfs->fd = -1;
}
else
{
if (psize)
*psize = stbuf.st_size;
}
return err;
}
static int
......@@ -277,6 +299,7 @@ _mapfile_open (stream_t stream, const char *filename, int port, int flags)
{
int err = errno;
close (mfs->fd);
mfs->ptr = NULL;
return err;
}
mfs->flags = mflag;
......
......@@ -105,7 +105,10 @@ struct _mbox_data
/* The variables below are use to hold the state when appending messages. */
enum mbox_state
{
MBOX_NO_STATE=0, MBOX_STATE_FROM, MBOX_STATE_DATE, MBOX_STATE_APPEND
MBOX_NO_STATE = 0,
MBOX_STATE_APPEND_SENDER, MBOX_STATE_APPEND_DATE, MBOX_STATE_APPEND_HEADER,
MBOX_STATE_APPEND_ATTRIBUTE, MBOX_STATE_APPEND_BODY,
MBOX_STATE_APPEND_MESSAGE
} state ;
char *sender;
char *date;
......@@ -114,35 +117,47 @@ struct _mbox_data
};
/* Mailbox concrete implementation. */
static int mbox_open __P ((mailbox_t, int));
static int mbox_close __P ((mailbox_t));
static int mbox_get_message __P ((mailbox_t, size_t, message_t *));
static int mbox_append_message __P ((mailbox_t, message_t));
static int mbox_messages_count __P ((mailbox_t, size_t *));
static int mbox_expunge __P ((mailbox_t));
static int mbox_scan __P ((mailbox_t, size_t, size_t *));
static int mbox_is_updated __P ((mailbox_t));
static int mbox_size __P ((mailbox_t, off_t *));
static int mbox_open __P ((mailbox_t, int));
static int mbox_close __P ((mailbox_t));
static int mbox_get_message __P ((mailbox_t, size_t, message_t *));
static int mbox_append_message __P ((mailbox_t, message_t));
static int mbox_messages_count __P ((mailbox_t, size_t *));
static int mbox_expunge __P ((mailbox_t));
static int mbox_scan __P ((mailbox_t, size_t, size_t *));
static int mbox_is_updated __P ((mailbox_t));
static int mbox_size __P ((mailbox_t, off_t *));
/* private stuff */
static int mbox_scan0 __P ((mailbox_t, size_t, size_t *, int));
static int mbox_get_header_read __P ((stream_t, char *, size_t, off_t, size_t *));
static int mbox_get_hdr_fd __P ((stream_t, int *));
static int mbox_get_body_fd __P ((stream_t, int *));
static int mbox_get_fd __P ((mbox_message_t, int *));
static int mbox_get_attr_flags __P ((attribute_t, int *));
static int mbox_set_attr_flags __P ((attribute_t, int));
static int mbox_unset_attr_flags __P ((attribute_t, int));
static int mbox_readstream __P ((stream_t, char *, size_t, off_t, size_t *));
static int mbox_header_size __P ((header_t, size_t *));
static int mbox_header_lines __P ((header_t, size_t *));
static int mbox_body_size __P ((body_t, size_t *));
static int mbox_body_lines __P ((body_t, size_t *));
static int mbox_envelope_sender __P ((envelope_t, char *, size_t, size_t *));
static int mbox_envelope_date __P ((envelope_t, char *, size_t, size_t *));
static int mbox_scan0 __P ((mailbox_t, size_t, size_t *, int));
static int mbox_append_message0 __P ((mailbox_t, message_t, off_t *,
int));
static int mbox_header_fill __P ((header_t, char *, size_t, off_t,
size_t *));
static int mbox_get_header_readstream __P ((message_t, char *, size_t, off_t,
size_t *, int));
static int mbox_get_hdr_fd __P ((stream_t, int *));
static int mbox_get_body_fd __P ((stream_t, int *));
static int mbox_get_fd __P ((mbox_message_t, int *));
static int mbox_get_attr_flags __P ((attribute_t, int *));
static int mbox_set_attr_flags __P ((attribute_t, int));
static int mbox_unset_attr_flags __P ((attribute_t, int));
static int mbox_body_read __P ((stream_t, char *, size_t, off_t,
size_t *));
static int mbox_body_readline __P ((stream_t, char *, size_t, off_t,
size_t *));
static int mbox_body_readstream __P ((stream_t, char *, size_t, off_t,
size_t *, int));
static int mbox_header_size __P ((header_t, size_t *));
static int mbox_header_lines __P ((header_t, size_t *));
static int mbox_body_size __P ((body_t, size_t *));
static int mbox_body_lines __P ((body_t, size_t *));
static int mbox_envelope_sender __P ((envelope_t, char *, size_t,
size_t *));
static int mbox_envelope_date __P ((envelope_t, char *, size_t,
size_t *));
static int mbox_tmpfile __P ((mailbox_t, char **pbox));
#ifdef WITH_PTHREAD
static void mbox_cleanup __P ((void *));
static void mbox_cleanup __P ((void *));
#endif
/* We allocate the mbox_data_t struct, but don't do any parsing on the name or
......@@ -253,7 +268,9 @@ mbox_open (mailbox_t mailbox, int flags)
{
/* FIXME: for small mbox we should try to mmap (). */
status = (flags & MU_STREAM_CREAT) || (mailbox->flags & MU_STREAM_APPEND);
/* We do not try to mmap for CREAT or APPEND, it is not supported. */
status = (flags & MU_STREAM_CREAT)
|| (mailbox->flags & MU_STREAM_APPEND);
if (status == 0)
status = mapfile_stream_create (&(mailbox->stream));
if (status != 0)
......@@ -359,14 +376,12 @@ mbox_is_updated (mailbox_t mailbox)
return (mud->size == size);
}
/* FIXME: the use of tmpfile() on some system can lead to race condition, We
should use a safer approach. */
static FILE *
/* Try to create an uniq file. */
static int
mbox_tmpfile (mailbox_t mailbox, char **pbox)
{
const char *tmpdir;
int fd;
FILE *fp;
const char *basename;
mbox_data_t mud = mailbox->data;
......@@ -375,53 +390,53 @@ mbox_tmpfile (mailbox_t mailbox, char **pbox)
# define P_tmpdir "/tmp"
#endif
tmpdir = getenv ("TEMPDIR") ? getenv ("TEMPDIR") : P_tmpdir;
/* FIXME: Use a macro for the separator to be portable. */
basename = strrchr (mud->name, '/');
if (basename)
basename++;
else
basename = mud->name;
*pbox = calloc (strlen (tmpdir) + strlen ("MBOX_") +
strlen (basename) + 2/* separator + null */, sizeof (char));
if (*pbox == NULL)
return NULL;
sprintf (*pbox, "%s/%s%s", tmpdir, "MBOX_", basename);
/* FIXME: I don't think this is the righ approach, creating an anonymous
file would be better ? no trace left behind. */
tmpdir = getenv ("TMPDIR") ? getenv ("TMPDIR") : P_tmpdir;
/* (separator + null) == 2 + XXXXXX == 6 + ... */
*pbox = calloc (strlen (tmpdir) + /* '/' */ 1 + /*strlen ("MU_")*/ 3 +
strlen (basename) + /*strlen ("_XXXXXX")*/ 7 + /*null*/1,
sizeof (**pbox));
if (*pbox == NULL)
return -1;
sprintf (*pbox, "%s/MU_%s_XXXXXX", tmpdir, basename);
#ifdef HAVE_MKSTEMP
fd = mkstemp (*pbox);
#else
/* Create the file. It must not exist. If it does exist, fail. */
fd = open (*pbox, O_RDWR|O_CREAT|O_EXCL, 0600);
if (fd < 0)
if (mktemp (*pbox))
{
fprintf (stderr,"Can't create %s\n", *pbox);
fprintf (stderr,"delete file <%s>, Please\n", *pbox);
fprintf (stderr, "It was likely due to an error when expunging\n");
return NULL;
fd = open (*pbox, O_RDWR|O_CREAT|O_EXCL, 0600);
}
fp = fdopen (fd, "w+");
if (fp == 0)
else
{
close(fd);
free (*pbox);
*pbox = NULL;
fd = -1;
}
/* Really I should just remove the file here. */
/* remove(*pbox); */
return fp;
#endif
return fd;
}
/* For the expunge bits we took a very cautionnary approach, meaning
we create temporary file in the tmpdir copy all the file not mark deleted,
we create a temporary mailbox in the tmpdir copy all the message not mark
deleted(Actually we copy all the message that may have been modified, we
do not have(yet) an api call to tell whether a message was modified)
and skip the deleted ones, truncate the real mailbox to the desired size
and overwrite with the tmp mailbox. The approach to do everyting
in core is tempting but require to much memory, it is not rare now
a day to have 30 Megs mailbox, also there is danger for filesystems
with quotas, or some program may not respect the advisory lock and
decide to append a new message while your expunging etc ...
in core is tempting but require
- to much memory, it is not rare now a day to have 30 Megs mailbox,
- also there is danger for filesystems with quotas,
- if we run out of disk space everything is lost.
- or some program may not respect the advisory lock and decide to append
a new message while your expunging etc ...
The real downside to the approach we take is that when things go wrong
the temporary file bay be left in /tmp, which is not all that bad
because at least have something to recuparate when failure. */
the temporary file may be left in /tmp, which is not all that bad
because at least, we have something to recuparate when failure. */
static int
mbox_expunge (mailbox_t mailbox)
{
......@@ -429,13 +444,12 @@ mbox_expunge (mailbox_t mailbox)
mbox_message_t mum;
int status = 0;
sigset_t signalset;
FILE *tempfile;
size_t nread;
size_t i, j, dirty, first;
int tempfile;
size_t i, j, dirty;
off_t marker = 0;
off_t total = 0;
char buffer [BUFSIZ];
char *tmpmbox = NULL;
char *tmpmboxname = NULL;
mailbox_t tmpmailbox = NULL;
#ifdef WITH_PTHREAD
int state;
#endif
......@@ -449,57 +463,76 @@ mbox_expunge (mailbox_t mailbox)
if (mud->messages_count == 0)
return 0;
/* Do we have a consistent view of the mailbox. */
if (! mbox_is_updated (mailbox))
{
fprintf (stderr, "mailbox is not updated, try again.\n");
return EAGAIN;
}
monitor_wrlock (mailbox->monitor);
/* Mark dirty the first mail with an attribute change. */
/* Mark dirty the first mum(concrete message) with a message pointer. */
/* FIXME: This is not right, the way to really know if a message is modified
is by changing the API, we should have an message_is_modified (). */
for (dirty = 0; dirty < mud->messages_count; dirty++)
{
mum = mud->umessages[dirty];
#if 0
/* No, cheking if the attribute was changed is not enough, for example
They may have modified a header, say adding the X-UIDL field. Those
changes are not save yet but still in memory part of the header blurb.
So unless we have a message_is_modified() call we should assume that
the message was modified. */
if (mum->new_flags &&
! ATTRIBUTE_IS_EQUAL (mum->old_flags, mum->new_flags))
break;
#else
/* Message may have been tampered, break here. */
if (mum->message && message_is_modified (mum->message))
break;
#endif
}
/* Did something change ? */
if (dirty == mud->messages_count)
{
monitor_unlock (mailbox->monitor);
return 0; /* Nothing change, bail out. */
}
return 0; /* Nothing change, bail out. */
/* This is redundant, we go to the loop again. But it's more secure here
since we don't want to be disturb when expunging. */
since we don't want to be disturb when expunging. Destroy all the
messages mark for deletion. */
for (j = 0; j < mud->messages_count; j++)
{
mum = mud->umessages[j];
if (mum && mum->new_flags && ATTRIBUTE_IS_DELETED (mum->new_flags))
if (mum && mum->message && mum->new_flags &&
ATTRIBUTE_IS_DELETED (mum->new_flags))
message_destroy (&(mum->message), mum);
}
/* Create a tempory file. */
tempfile = mbox_tmpfile (mailbox, &tmpmbox);
if (tempfile == NULL)
/* Create a temporary file. */
tempfile = mbox_tmpfile (mailbox, &tmpmboxname);
if (tempfile == -1)
{
free (tmpmbox);
monitor_unlock (mailbox->monitor);
free (tmpmboxname);
fprintf (stderr, "Failed to create temporary file when expunging.\n");
return errno;
}
else
{
/* strlen ("mbox:") == 5 + strlen (tmpmboxname) + 1 */
char *m = alloca (5 + strlen (tmpmboxname) + 1);
sprintf (m, "mbox:%s", tmpmboxname);
/* Must put create if not the open will try to mmap() the file. */
if ((status = mailbox_create (&tmpmailbox, m)) != 0
|| (status
= mailbox_open (tmpmailbox, MU_STREAM_CREAT | MU_STREAM_RDWR)) != 0)
{
close (tempfile);
remove (tmpmboxname);
free (tmpmboxname);
return status;
}
close (tempfile);
}
/* Get the File lock. */
if (locker_lock (mailbox->locker, MU_LOCKER_WRLOCK) < 0)
{
fclose (tempfile);
remove (tmpmbox);
free (tmpmbox);
monitor_unlock (mailbox->monitor);
mailbox_close (tmpmailbox);
mailbox_destroy (&tmpmailbox);
remove (tmpmboxname);
free (tmpmboxname);
fprintf (stderr, "Failed to grab the lock\n");
return ENOLCK;
}
......@@ -517,10 +550,11 @@ mbox_expunge (mailbox_t mailbox)
sigprocmask (SIG_BLOCK, &signalset, 0);
/* Set the marker position. */
total = marker = mud->umessages[dirty]->header_from;
marker = mud->umessages[dirty]->header_from;
total = 0;
/* Copy to tempfile emails not mark deleted. */
for (first = 1, i = dirty; i < mud->messages_count; i++)
for (i = dirty; i < mud->messages_count; i++)
{
mum = mud->umessages[i];
......@@ -528,78 +562,31 @@ mbox_expunge (mailbox_t mailbox)
if (ATTRIBUTE_IS_DELETED (mum->new_flags))
continue;
/* Add a NL separator between messages. */
if (first)
first = 0;
else
if (mum->message)
{
fputc ('\n', tempfile);
total++;
}
/* Begining of header copy. */
{
off_t current;
/* This is done in two parts first we check if the attribute changed,
if yes then we have to update the "Status:" field. Unfortunately
there is no requirement for the "Status:" to be the last field, so
we take the long approach; Copy up to the Status, update the
Status and copy the rest. */
/* Attribute change ? */
if (mum->new_flags
&&! ATTRIBUTE_IS_EQUAL (mum->old_flags, mum->new_flags)
&& mum->header_status > mum->header_from)
{
size_t n = 0;
off_t offset = mum->header_from;
size_t len = mum->header_status - mum->header_from;
current = mum->header_status_end;
while (len > 0)
{
nread = (len < sizeof (buffer)) ? len : sizeof (buffer);
if ((status = stream_read (mailbox->stream, buffer,
nread, offset, &n) != 0)
|| fwrite(buffer, sizeof(*buffer), n, tempfile) != n)
{
if (status == 0)
status = errno;
fprintf (stderr, "Error expunge:%d: (%s)\n", __LINE__,
strerror (status));
goto bailout0;
}
len -= n;
total += n;
offset += n;
}
/* Put the new attributes. */
status = mbox_append_message0 (tmpmailbox, mum->message, &total, 1);
if (status != 0)
{
char abuf[64];
size_t na = 0;
abuf[0] = '\0';
flags_to_string (mum->new_flags, abuf, sizeof(abuf), &na);
fputs (abuf, tempfile);
total += na;
fprintf (stderr, "Error expunge:%d: %s", __LINE__,
strerror (status));
goto bailout0;
}
}
else /* Attribute did not change. */
current = mum->header_from;
/* Copy the rest of header without changes. */
}
else
{
size_t n = 0;
off_t offset = current;
size_t len = mum->body - current;
/* Nothing changed copy the message straight. */
char buffer [1024];
size_t n;
off_t offset = mum->header_from;
size_t len = mum->body_end - mum->header_from;
while (len > 0)
{
nread = (len < sizeof (buffer)) ? len : sizeof (buffer);
if ((status = stream_read (mailbox->stream, buffer, nread,
offset, &n) != 0)
|| fwrite(buffer, sizeof(*buffer), n, tempfile) != n)
n = (len < sizeof (buffer)) ? len : sizeof (buffer);
if ((status = stream_read (mailbox->stream, buffer, n, offset,
&n) != 0)
|| (status = stream_write (tmpmailbox->stream, buffer, n,
total, &n) != 0))
{
if (status == 0)
status = errno;
fprintf (stderr, "Error expunge:%d: %s", __LINE__,
strerror (status));
goto bailout0;
......@@ -608,110 +595,102 @@ mbox_expunge (mailbox_t mailbox)
total += n;
offset += n;
}
/* Add the newline separator. */
status = stream_write (tmpmailbox->stream, "\n", 1, total, &n);
if (status != 0)
{
fprintf (stderr, "Error expunge:%d: %s", __LINE__,
strerror (status));
goto bailout0;
}
total++;
}
} /* End of header copy. */
/* Copy the body. */
{
size_t n = 0;
off_t offset = mum->body;
size_t len = mum->body_end - mum->body;
while (len > 0)
{
nread = (len < sizeof (buffer)) ? len : sizeof (buffer);
if ((status = stream_read (mailbox->stream, buffer, nread,
offset, &n) != 0)
|| fwrite(buffer, sizeof(*buffer), n, tempfile) != n)
{
if (status == 0)
status = errno;
fprintf (stderr, "Error expunge:%d: %s", __LINE__,
strerror (status));
goto bailout0;
}
len -= n;
total += n;
offset += n;
}
} /* End of body copy. */
} /* for (;;) */
/* Caution: before ftruncate()ing the file see if we've receiving new mail
Some program may not respect the lock, or the lock was held for too
long. */
/* Caution: before ftruncate()ing the file see
- if we've receive new mails.
Some programs may not respect the lock, or the lock was held for too
long.
- The mailbox may not have been properly updated before expunging. */
{
off_t size = 0;
if (stream_size (mailbox->stream, &size) == 0)
{
off_t len = size - mud->size;
off_t offset = mud->size;
char buffer [1024];
size_t n = 0;
off_t offset = size;
size_t len = size - mud->size;
while (len > 0)
if (len > 0 )
{
nread = (len < sizeof (buffer)) ? len : sizeof (buffer);
if ((status = stream_read (mailbox->stream, buffer, nread,
offset, &n) != 0)
|| fwrite(buffer, sizeof(*buffer), n, tempfile) != n)
while ((status = stream_read (mailbox->stream, buffer,
sizeof (buffer), offset, &n)) == 0
&& n > 0)
{
if (status == 0)
status = errno;
fprintf (stderr, "Error expunge:%d: %s", __LINE__,
strerror (status));
goto bailout0;
status = stream_write (tmpmailbox->stream, buffer, n,
total, &n);
if (status != 0)
{
fprintf (stderr, "Error expunge:%d: %s", __LINE__,
strerror (status));
goto bailout0;
}
total += n;
offset += n;
}
len -= n;
total += n;
offset += n;
}
else if (len < 0)
{
/* Corrupted mailbox. */
fprintf (stderr, "Error expunge:%d: %s", __LINE__,
strerror (status));
goto bailout0;
}
}
} /* End of precaution. */
/* Seek and rewrite it. */
rewind (tempfile);
if (total > 0)
{
char buffer [1024];
size_t n = 0;
off_t off = 0;
off_t offset = marker;
while ((nread = fread (buffer, sizeof (*buffer),
sizeof (buffer), tempfile)) != 0)
while ((status = stream_read (tmpmailbox->stream, buffer,
sizeof (buffer), off, &n)) == 0
&& n > 0)
{
while (nread)
status = stream_write (mailbox->stream, buffer, n, offset, &n);
if (status != 0)
{
status = stream_write (mailbox->stream, buffer, nread, offset, &n);
if (status != 0)
{
fprintf (stderr, "Error expunge:%d: %s\n", __LINE__,
strerror (status));
goto bailout;
}
nread -= n;
offset += n;
fprintf (stderr, "Error expunge:%d: %s\n", __LINE__,
strerror (status));
goto bailout;
}
off += n;
offset += n;
}
}
/* How can I handle error here ?? */
clearerr (tempfile);
/* Flush/truncation. */
/* Flush/truncation. Need to flush before truncate. */
stream_flush (mailbox->stream);
status = stream_truncate (mailbox->stream, total);
status = stream_truncate (mailbox->stream, total + marker);
if (status != 0)
{
fprintf (stderr, "Error expunging:%d: %s", __LINE__, strerror (status));
fprintf (stderr, "Error expunging:%d: %s\n", __LINE__, strerror (status));
goto bailout;
}
bailout0:
/* Don't remove the tmp mbox in case of errors, when writing back. */
remove (tmpmbox);
bailout0:
remove (tmpmboxname);
bailout:
free (tmpmbox);
free (tmpmboxname);
/* Release the File lock. */
locker_unlock (mailbox->locker);
fclose (tempfile);
mailbox_close (tmpmailbox);
mailbox_destroy (&tmpmailbox);
#ifdef WITH_PTHREAD
pthread_setcancelstate (state, &state);
#endif
......@@ -721,6 +700,7 @@ mbox_expunge (mailbox_t mailbox)
if (status == 0)
{
size_t dlast;
monitor_wrlock (mailbox->monitor);
for (j = dirty, dlast = mud->messages_count - 1;
j <= dlast; j++)
{
......@@ -758,13 +738,11 @@ mbox_expunge (mailbox_t mailbox)
mum->body = mum->body_end = 0;
mum->header_lines = mum->body_lines = 0;
}
monitor_unlock (mailbox->monitor);
/* This is should reset the messages_count, the last argument 0 means
not to send event notification. */
monitor_unlock (mailbox->monitor);
mbox_scan0 (mailbox, dirty, NULL, 0);
}
else
monitor_unlock (mailbox->monitor);
return status;
}
......@@ -834,8 +812,22 @@ mbox_unset_attr_flags (attribute_t attr, int flags)
}
static int
mbox_readstream (stream_t is, char *buffer, size_t buflen,
off_t off, size_t *pnread)
mbox_body_readline (stream_t is, char *buffer, size_t buflen,
off_t off, size_t *pnread)
{
return mbox_body_readstream (is, buffer, buflen, off, pnread, 1);
}
static int
mbox_body_read (stream_t is, char *buffer, size_t buflen,
off_t off, size_t *pnread)
{
return mbox_body_readstream (is, buffer, buflen, off, pnread, 0);
}
static int
mbox_body_readstream (stream_t is, char *buffer, size_t buflen,
off_t off, size_t *pnread, int isreadline)
{
body_t body = stream_get_owner (is);
message_t msg = body_get_owner (body);
......@@ -866,7 +858,9 @@ mbox_readstream (stream_t is, char *buffer, size_t buflen,
{
nread = ((size_t)ln < buflen) ? ln : buflen;
/* Position the file pointer and the buffer. */
status = stream_read (mum->stream, buffer, nread, mum->body + off, &n);
status = (isreadline) ?
stream_readline (mum->stream, buffer, nread, mum->body + off, &n) :
stream_read (mum->stream, buffer, nread, mum->body + off, &n);
}
}
monitor_unlock (mum->mud->mailbox->monitor);
......@@ -880,11 +874,17 @@ mbox_readstream (stream_t is, char *buffer, size_t buflen,
}
static int
mbox_get_header_read (stream_t is, char *buffer, size_t len,
off_t off, size_t *pnread)
mbox_header_fill (header_t header, char *buffer, size_t len,
off_t off, size_t *pnread)
{
header_t header = stream_get_owner (is);
message_t msg = header_get_owner (header);
return mbox_get_header_readstream (msg, buffer, len, off, pnread, 0);
}
static int
mbox_get_header_readstream (message_t msg, char *buffer, size_t len,
off_t off, size_t *pnread, int isreadline)
{
mbox_message_t mum = message_get_owner (msg);
size_t nread = 0;
int status = 0;
......@@ -904,8 +904,11 @@ mbox_get_header_read (stream_t is, char *buffer, size_t len,
{
nread = ((size_t)ln < len) ? ln : len;
/* Position the file pointer and the buffer. */
status = stream_read (mum->stream, buffer, nread,
mum->header_from_end + off, &nread);
status = (isreadline) ?
stream_readline (mum->stream, buffer, nread,
mum->header_from_end + off, &nread) :
stream_read (mum->stream, buffer, nread,
mum->header_from_end + off, &nread);
}
monitor_unlock (mum->mud->mailbox->monitor);
#ifdef WITH_PTHREAD
......@@ -966,13 +969,14 @@ mbox_body_lines (body_t body, size_t *plines)
static int
mbox_envelope_date (envelope_t envelope, char *buf, size_t len,
size_t *pnwrite)
size_t *pnwrite)
{
message_t msg = envelope_get_owner (envelope);
mbox_message_t mum = message_get_owner (msg);
size_t n = 0;
int status;
char buffer[512];
char *s;
if (mum == NULL)
return EINVAL;
......@@ -983,30 +987,27 @@ mbox_envelope_date (envelope_t envelope, char *buf, size_t len,
{
if (pnwrite)
*pnwrite = 0;
if (buf)
*buf = '\0';
return status;
}
if (n > 5)
/* Format: "From [sender] [date]" */
/* strlen ("From ") == 5 */
if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL)
{
char *s = strchr (buffer + 5, ' ');
if (s)
if (buf && len > 0)
{
if (buf && len > 0)
{
strncpy (buf, s + 1, len);
buffer [len - 1] = '\0';
}
if (pnwrite)
*pnwrite = strlen (s + 1);
return 0;
len--; /* Leave space for the null. */
strncpy (buf, s + 1, len)[len] = '\0';
len = strlen (buf);
}
else
len = strlen (s + 1);
}
else
len = 0;
if (pnwrite)
*pnwrite = 0;
if (buf)
*buf = '\0';
*pnwrite = len;
return 0;
}
......@@ -1019,6 +1020,7 @@ mbox_envelope_sender (envelope_t envelope, char *buf, size_t len,
size_t n = 0;
int status;
char buffer[512];
char *s;
if (mum == NULL)
return EINVAL;
......@@ -1029,31 +1031,29 @@ mbox_envelope_sender (envelope_t envelope, char *buf, size_t len,
{
if (pnwrite)
*pnwrite = 0;
if (buf)
*buf = '\0';
return status;
}
if (n > 5)
/* Format: "From [sender] [date]" */
/* strlen ("From ") == 5 */
if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL)
{
char *s = strchr (buffer + 5, ' ');
if (s)
/* Put a NULL to isolate the sender string, make a C string. */
*s = '\0';
if (buf && len > 0)
{
*s = '\0';
if (buf && len > 0)
{
strncpy (buf, buffer + 5, len);
buffer [len - 1] = '\0';
}
if (pnwrite)
*pnwrite = strlen (buffer + 5);
return 0;
len--; /* leave space for the null */
strncpy (buf, buffer + 5, len)[len] = '\0';
len = strlen (buf);
}
else
len = strlen (buffer + 5);
}
else
len = 0;
if (pnwrite)
*pnwrite = 0;
if (buf)
*buf = '\0';
*pnwrite = len;
return 0;
}
......@@ -1105,17 +1105,15 @@ mbox_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
{
header_t header = NULL;
stream_t stream = NULL;
if ((status = header_create (&header, NULL, 0, msg)) != 0
|| (status = stream_create (&stream, mailbox->flags, header)) != 0)
status = header_create (&header, NULL, 0, msg);
if (status != 0)
{
stream_destroy (&stream, header);
//stream_destroy (&stream, header);
header_destroy (&header, msg);
message_destroy (&msg, mum);
return status;
}
stream_set_read (stream, mbox_get_header_read, header);
stream_set_fd (stream, mbox_get_hdr_fd, header);
header_set_stream (header, stream, msg);
header_set_fill (header, mbox_header_fill, msg);
header_set_size (header, mbox_header_size, msg);
header_set_lines (header, mbox_header_lines, msg);
message_set_header (msg, header, mum);
......@@ -1149,7 +1147,8 @@ mbox_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
message_destroy (&msg, mum);
return status;
}
stream_set_read (stream, mbox_readstream, body);
stream_set_read (stream, mbox_body_read, body);
stream_set_readline (stream, mbox_body_readline, body);
stream_set_fd (stream, mbox_get_body_fd, body);
body_set_stream (body, stream, msg);
body_set_size (body, mbox_body_size, msg);
......@@ -1179,7 +1178,6 @@ mbox_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
return 0;
}
/* FIXME: We have to generate a Message-ID: if the message have none. */
static int
mbox_append_message (mailbox_t mailbox, message_t msg)
{
......@@ -1190,163 +1188,299 @@ mbox_append_message (mailbox_t mailbox, message_t msg)
MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_append_message (%s)\n",
mud->name);
monitor_wrlock (mailbox->monitor);
#ifdef WITH_PTHREAD
/* write()/read() are cancellation points. Since we're doing a potentially
long operation. Lets make sure we clean the state. */
pthread_cleanup_push (mbox_cleanup, (void *)mailbox);
#endif
locker_lock (mailbox->locker, MU_LOCKER_WRLOCK);
{
off_t size;
int status;
size_t n = 0;
char nl = '\n';
switch (mud->state)
{
case MBOX_NO_STATE:
locker_lock (mailbox->locker, MU_LOCKER_WRLOCK);
/* Move to the end of the file, not necesary if _APPEND mode. */
status = stream_size (mailbox->stream, &size);
if (status != 0)
default:
{
locker_unlock (mailbox->locker);
monitor_unlock (mailbox->monitor);
return status;
off_t size;
int status;
/* Move to the end of the file, not necesary if _APPEND mode. */
if ((status = stream_size (mailbox->stream, &size)) != 0
|| (status = mbox_append_message0 (mailbox, msg, &size, 0)) != 0)
{
if (status != EAGAIN)
locker_unlock (mailbox->locker);
return status;
}
}
}
locker_unlock (mailbox->locker);
return 0;
}
switch (mud->state)
static int
mbox_append_message0 (mailbox_t mailbox, message_t msg, off_t *psize,
int is_expunging)
{
mbox_data_t mud = mailbox->data;
int status = 0;
size_t n = 0;
char nl = '\n';
switch (mud->state)
{
case MBOX_NO_STATE:
/* Allocate memory for the sender/date buffers. */
mud->sender = calloc (128, sizeof (char));
if (mud->sender == NULL)
return ENOMEM;
mud->date = calloc (128, sizeof (char));
if (mud->date == NULL)
{
free (mud->sender);
mud->sender = NULL;
return ENOMEM;
}
mud->off = 0;
mud->state = MBOX_STATE_APPEND_SENDER;
case MBOX_STATE_APPEND_SENDER:
/* Generate the sender for the "From " separator. */
{
case MBOX_NO_STATE:
mud->sender = calloc (128, sizeof (char));
if (mud->sender == NULL)
char *s;
size_t len = 0;
envelope_t envelope;
message_get_envelope (msg, &envelope);
status = envelope_sender (envelope, mud->sender, 128, &len);
if (status != 0)
{
locker_unlock (mailbox->locker);
monitor_unlock (mailbox->monitor);
return ENOMEM;
if (status != EAGAIN)
{
free (mud->sender);
free (mud->date);
mud->date = mud->sender = NULL;
mud->state = MBOX_NO_STATE;
}
return status;
}
mud->date = calloc (128, sizeof (char));
if (mud->date == NULL)
/* Nuke trailing newline. */
s = memchr (mud->sender, nl, len);
if (s)
*s = '\0';
mud->state = MBOX_STATE_APPEND_DATE;
}
case MBOX_STATE_APPEND_DATE:
/* Generate a date for the "From " separator. */
{
char *s;
size_t len = 0;
envelope_t envelope;
message_get_envelope (msg, &envelope);
status = envelope_date (envelope, mud->date, 128, &len);
if (status != 0)
{
free (mud->sender);
mud->sender = NULL;
mud->state = MBOX_NO_STATE;
locker_unlock (mailbox->locker);
monitor_unlock (mailbox->monitor);
return ENOMEM;
if (status != EAGAIN)
{
free (mud->sender);
free (mud->date);
mud->date = mud->sender = NULL;
mud->state = MBOX_NO_STATE;
}
return status;
}
mud->off = 0;
mud->state = MBOX_STATE_FROM;
case MBOX_STATE_FROM:
/* Generate a "From " separator. */
{
char *s;
size_t len = 0;
envelope_t envelope;
message_get_envelope (msg, &envelope);
status = envelope_sender (envelope, mud->sender, 127, &len);
if (status != 0)
{
if (status != EAGAIN)
{
free (mud->sender);
free (mud->date);
mud->date = mud->sender = NULL;
mud->state = MBOX_NO_STATE;
locker_unlock (mailbox->locker);
}
monitor_unlock (mailbox->monitor);
return status;
}
/* Nuke trailing newline. */
s = memchr (mud->sender, nl, len);
if (s)
*s = '\0';
mud->state = MBOX_STATE_DATE;
}
/* Nuke trailing newline. */
s = memchr (mud->date, nl, len);
if (s)
*s = '\0';
case MBOX_STATE_DATE:
/* Generate a date for the "From " separator. */
{
char *s;
size_t len = 0;
envelope_t envelope;
message_get_envelope (msg, &envelope);
status = envelope_date (envelope, mud->date, 127, &len);
if (status != 0)
{
if (status != EAGAIN)
{
free (mud->sender);
free (mud->date);
mud->date = mud->sender = NULL;
mud->state = MBOX_NO_STATE;
locker_unlock (mailbox->locker);
}
monitor_unlock (mailbox->monitor);
return status;
}
/* Nuke trailing newline. */
s = memchr (mud->date, nl, len);
if (s)
*s = '\0';
/* Write the separator to the mailbox. */
stream_write (mailbox->stream, "From ", 5, size, &n);
size += n;
stream_write (mailbox->stream, mud->sender, strlen (mud->sender), size, &n);
size += n;
stream_write (mailbox->stream, " ", 1, size, &n);
size += n;
stream_write (mailbox->stream, mud->date, strlen (mud->date), size, &n);
size += n;
stream_write (mailbox->stream, &nl , 1, size, &n);
size += n;
free (mud->sender);
free (mud->date);
mud->sender = mud->date = NULL;
mud->state = MBOX_STATE_APPEND;
}
/* Write the separator to the mailbox. */
status = stream_write (mailbox->stream, "From ", 5, *psize, &n);
if (status != 0)
break;
*psize += n;
/* Write sender. */
status = stream_write (mailbox->stream, mud->sender,
strlen (mud->sender), *psize, &n);
if (status != 0)
break;
*psize += n;
status = stream_write (mailbox->stream, " ", 1, *psize, &n);
if (status != 0)
break;
*psize += n;
/* Write date. */
status = stream_write (mailbox->stream, mud->date, strlen(mud->date),
*psize, &n);
if (status != 0)
break;
*psize += n;
status = stream_write (mailbox->stream, &nl , 1, *psize, &n);
if (status != 0)
break;
*psize += n;
free (mud->sender);
free (mud->date);
mud->sender = mud->date = NULL;
/* If we are not expunging get the message in one block via the stream
message instead of the header/body. This is good for example for
POP where there is no separtation between header and body. */
if (! is_expunging)
{
mud->state = MBOX_STATE_APPEND_MESSAGE;
break;
}
mud->state = MBOX_STATE_APPEND_HEADER;
}
case MBOX_STATE_APPEND:
/* Append the Message. */
{
char buffer[BUFSIZ];
size_t nread = 0;
stream_t is;
message_get_stream (msg, &is);
do
{
status = stream_read (is, buffer, sizeof (buffer), mud->off,
&nread);
if (status != 0)
{
if (status != EAGAIN)
{
mud->state = MBOX_NO_STATE;
locker_unlock (mailbox->locker);
}
stream_flush (mailbox->stream);
monitor_unlock (mailbox->monitor);
return status;
}
stream_write (mailbox->stream, buffer, nread, size, &n);
mud->off += nread;
size += n;
}
while (nread > 0);
stream_write (mailbox->stream, &nl, 1, size, &n);
}
case MBOX_STATE_APPEND_HEADER:
/* Append the Header. */
{
char buffer[1024];
size_t nread = 0;
stream_t is;
header_t header;
message_get_header (msg, &header);
header_get_stream (header, &is);
do
{
status = stream_readline (is, buffer, sizeof (buffer), mud->off,
&nread);
if (status != 0)
{
if (status != EAGAIN)
{
mud->state = MBOX_NO_STATE;
mud->off = 0;
}
return status;
}
mud->off += nread;
if (*buffer == '\n')
break;
/* FIXME: We have a problem here the header field may not fit the
buffer. */
/* We do not copy the Status since this is for the attribute. */
if (strncasecmp (buffer, "Status", 6) == 0
|| strncasecmp (buffer, "X-Status", 8) == 0)
continue;
status = stream_write (mailbox->stream, buffer, nread,
*psize, &n);
if (status != 0)
break;
*psize += n;
}
while (nread > 0);
mud->off = 0;
mud->state = MBOX_STATE_APPEND_ATTRIBUTE;
}
default:
break;
case MBOX_STATE_APPEND_ATTRIBUTE:
/* Put the new attributes. */
{
char abuf[64];
size_t na = 0;
attribute_t attr = NULL;
abuf[0] = '\0';
message_get_attribute (msg, &attr);
attribute_to_string (attr, abuf, sizeof(abuf), &na);
status = stream_write (mailbox->stream, abuf, na, *psize, &n);
if (status != 0)
break;
*psize += n;
status = stream_write (mailbox->stream, &nl , 1, *psize, &n);
if (status != 0)
break;
*psize += n;
mud->state = MBOX_STATE_APPEND_BODY;
}
}
stream_flush (mailbox->stream);
case MBOX_STATE_APPEND_BODY:
/* Append the Body. */
{
char buffer[1024];
size_t nread = 0;
stream_t is;
body_t body;
message_get_body (msg, &body);
body_get_stream (body, &is);
do
{
status = stream_read (is, buffer, sizeof (buffer), mud->off,
&nread);
if (status != 0)
{
if (status != EAGAIN)
{
mud->state = MBOX_NO_STATE;
mud->off = 0;
}
return status;
}
mud->off += nread;
status = stream_write (mailbox->stream, buffer, nread, *psize, &n);
if (status != 0)
break;
*psize += n;
}
while (nread > 0);
mud->off = 0;
n = 0;
stream_write (mailbox->stream, &nl, 1, *psize, &n);
*psize += n;
}
default:
break;
}
/* If not expunging we are taking the stream message. */
if (!is_expunging)
{
switch (mud->state)
{
case MBOX_STATE_APPEND_MESSAGE:
{
/* Append the Message. */
char buffer[1024];
size_t nread = 0;
stream_t is;
message_get_stream (msg, &is);
do
{
status = stream_read (is, buffer, sizeof (buffer), mud->off,
&nread);
if (status != 0)
{
if (status != EAGAIN)
{
mud->state = MBOX_NO_STATE;
mud->off = 0;
}
stream_flush (mailbox->stream);
return status;
}
stream_write (mailbox->stream, buffer, nread, *psize, &n);
mud->off += nread;
*psize += n;
}
while (nread > 0);
n = 0;
stream_write (mailbox->stream, &nl, 1, *psize, &n);
*psize += n;
}
default:
break;
}
} /* is_expunging */
mud->state = MBOX_NO_STATE;
locker_unlock (mailbox->locker);
monitor_unlock (mailbox->monitor);
#ifdef WITH_PTHREAD
pthread_cleanup_pop (0);
#endif
return 0;
stream_flush (mailbox->stream);
return status;
}
static int
......
......@@ -28,6 +28,8 @@
#include <string.h>
#include <ctype.h>
#include "md5.h"
#include <misc.h>
#include <message0.h>
......@@ -155,6 +157,15 @@ message_get_owner (message_t msg)
}
int
message_is_modified (message_t msg)
{
int mod = 0;
mod |= header_is_modified (msg->header);
mod |= attribute_is_modified (msg->attribute);
return mod;
}
int
message_set_mailbox (message_t msg, mailbox_t mailbox)
{
if (msg == NULL)
......@@ -400,6 +411,7 @@ message_set_attribute (message_t msg, attribute_t attribute, void *owner)
return 0;
}
/* FIXME: not nonblocking safe. */
int
message_get_uid (message_t msg, char *buffer, size_t buflen, size_t *pwriten)
{
......@@ -411,16 +423,19 @@ message_get_uid (message_t msg, char *buffer, size_t buflen, size_t *pwriten)
return EINVAL;
buffer[0] = '\0';
/* Try the function overload if error fallback. */
if (msg->_get_uid)
return msg->_get_uid (msg, buffer, buflen, pwriten);
{
status = msg->_get_uid (msg, buffer, buflen, pwriten);
if (status == 0)
return status;
}
/* Be compatible with Qpopper ? qppoper saves the UIDL in "X-UIDL".
We use "Message-ID" as a fallback. Is this bad ? should we generate
a chksum or do the same as Qpopper save it in the header. */
We generate a chksum and save it in the header. */
message_get_header (msg, &header);
if ((status = header_get_value (header, "X-UIDL", buffer, buflen, &n)) == 0
|| (status = header_get_value (header, "Message-ID", buffer,
buflen, &n)) == 0)
status = header_get_value (header, "X-UIDL", buffer, buflen, &n);
if (status == 0 && n > 0)
{
/* FIXME: Is header_get_value suppose to do this ? */
/* We need to collapse the header if it was mutiline. e points to the
......@@ -437,6 +452,32 @@ message_get_uid (message_t msg, char *buffer, size_t buflen, size_t *pwriten)
}
}
}
else
{
struct md5_ctx md5context;
stream_t stream = NULL;
char buf[1024];
off_t offset = 0;
unsigned char md5digest[16];
char *tmp;
n = 0;
message_get_stream (msg, &stream);
md5_init_ctx (&md5context);
while (stream_read (stream, buf, sizeof (buf), offset, &n) == 0
&& n > 0)
{
md5_process_bytes (buf, n, &md5context);
offset += n;
}
md5_finish_ctx (&md5context, md5digest);
tmp = buf;
for (n = 0; n < 16; n++, tmp += 2)
sprintf (tmp, "%02x", md5digest[n]);
*tmp = '\0';
header_set_value (header, "X-UIDL", buf, 1);
buflen--; /* leave space for the NULL. */
strncpy (buffer, buf, buflen)[buflen] = '\0';
}
return status;
}
......
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
......@@ -39,7 +39,7 @@
* Need to prevent re-entry into mime lib, but allow non-blocking re-entry into lib.
*/
static int
static int
_mime_is_multipart_digest(mime_t mime)
{
if ( mime->content_type )
......@@ -47,7 +47,7 @@ _mime_is_multipart_digest(mime_t mime)
return 0;
}
static int
static int
_mime_append_part(mime_t mime, message_t msg, int offset, int len, int lines)
{
struct _mime_part *mime_part, **part_arr;
......@@ -131,7 +131,7 @@ char *_strtrim(char *str);
|| ((c) == '\\') || ((c) == '.') || ((c) == '[') \
|| ((c) == ']') )
static void
static void
_mime_munge_content_header(char *field_body )
{
char *p, *e, *str = field_body;
......@@ -195,7 +195,7 @@ _mime_get_param(char *field_body, const char *param, int *len)
return NULL;
}
static int
static int
_mime_setup_buffers(mime_t mime)
{
if ( mime->cur_buf == NULL && ( mime->cur_buf = malloc( mime->buf_size ) ) == NULL ) {
......@@ -208,7 +208,7 @@ _mime_setup_buffers(mime_t mime)
return 0;
}
static void
static void
_mime_append_header_line(mime_t mime)
{
if ( mime->header_length + mime->line_ndx > mime->header_buf_size) {
......@@ -222,7 +222,7 @@ _mime_append_header_line(mime_t mime)
mime->header_length += mime->line_ndx;
}
static int
static int
_mime_parse_mpart_message(mime_t mime)
{
char *cp, *cp2;
......@@ -323,7 +323,7 @@ _mime_parse_mpart_message(mime_t mime)
/*------ Mime message functions for READING a multipart message -----*/
static int
static int
_mimepart_body_read(stream_t stream, char *buf, size_t buflen, off_t off, size_t *nbytes)
{
body_t body = stream_get_owner(stream);
......@@ -343,7 +343,7 @@ _mimepart_body_read(stream_t stream, char *buf, size_t buflen, off_t off, size_t
return stream_read(mime_part->mime->stream, buf, read_len, mime_part->offset + off, nbytes );
}
static int
static int
_mimepart_body_fd(stream_t stream, int *fd)
{
body_t body = stream_get_owner(stream);
......@@ -353,7 +353,7 @@ _mimepart_body_fd(stream_t stream, int *fd)
return stream_get_fd(mime_part->mime->stream, fd);
}
static int
static int
_mimepart_body_size (body_t body, size_t *psize)
{
message_t msg = body_get_owner(body);
......@@ -366,7 +366,7 @@ _mimepart_body_size (body_t body, size_t *psize)
return 0;
}
static int
static int
_mimepart_body_lines (body_t body, size_t *plines)
{
message_t msg = body_get_owner(body);
......@@ -380,14 +380,14 @@ _mimepart_body_lines (body_t body, size_t *plines)
}
/*------ Mime message/header functions for CREATING multipart message -----*/
static int
static int
_mime_set_content_type(mime_t mime)
{
char content_type[256];
char boundary[128];
header_t hdr = NULL;
size_t size;
if ( mime->nmtp_parts > 1 ) {
if ( mime->flags & MIME_ADDED_MULTIPART_CT )
return 0;
......@@ -421,7 +421,7 @@ _mime_set_content_type(mime_t mime)
#define ADD_CHAR(buf, c, offset, buflen, nbytes) {*(buf)++ = c; (offset)++; (nbytes)++;if (--(buflen) == 0) return 0;}
static int
static int
_mime_body_read(stream_t stream, char *buf, size_t buflen, off_t off, size_t *nbytes)
{
body_t body = stream_get_owner(stream);
......@@ -470,7 +470,7 @@ _mime_body_read(stream_t stream, char *buf, size_t buflen, off_t off, size_t *nb
}
while(mime->postamble) {
mime->postamble--;
ADD_CHAR(buf, '-', mime->cur_offset, buflen, *nbytes);
ADD_CHAR(buf, '-', mime->cur_offset, buflen, *nbytes);
}
mime->flags &= ~(MIME_INSERT_BOUNDARY|MIME_ADDING_BOUNDARY);
mime->part_offset = 0;
......@@ -502,7 +502,7 @@ _mime_body_read(stream_t stream, char *buf, size_t buflen, off_t off, size_t *nb
return ret;
}
static int
static int
_mime_body_fd(stream_t stream, int *fd)
{
body_t body = stream_get_owner(stream);
......@@ -516,7 +516,7 @@ _mime_body_fd(stream_t stream, int *fd)
return stream_get_fd(msg_stream, fd);
}
static int
static int
_mime_body_size (body_t body, size_t *psize)
{
message_t msg = body_get_owner(body);
......@@ -541,7 +541,7 @@ _mime_body_size (body_t body, size_t *psize)
return 0;
}
static int
static int
_mime_body_lines (body_t body, size_t *plines)
{
message_t msg = body_get_owner(body);
......@@ -563,7 +563,7 @@ _mime_body_lines (body_t body, size_t *plines)
return 0;
}
int
int
mime_create(mime_t *pmime, message_t msg, int flags)
{
mime_t mime = NULL;
......@@ -611,7 +611,7 @@ mime_create(mime_t *pmime, message_t msg, int flags)
return ret;
}
void
void
mime_destroy(mime_t *pmime)
{
mime_t mime;
......@@ -648,8 +648,8 @@ mime_destroy(mime_t *pmime)
}
}
int
mime_get_part(mime_t mime, int part, message_t *msg)
int
mime_get_part(mime_t mime, size_t part, message_t *msg)
{
size_t nmtp_parts;
int ret = 0;
......@@ -681,7 +681,7 @@ mime_get_part(mime_t mime, int part, message_t *msg)
return ret;
}
int
int
mime_get_num_parts(mime_t mime, size_t *nmtp_parts)
{
int ret = 0;
......@@ -698,11 +698,11 @@ mime_get_num_parts(mime_t mime, size_t *nmtp_parts)
}
int
int
mime_add_part(mime_t mime, message_t msg)
{
int ret;
if ( mime == NULL || msg == NULL || ( mime->flags & MIME_NEW_MESSAGE ) == 0 )
return EINVAL;
if ( ( ret = _mime_append_part(mime, msg, 0, 0, 0) ) == 0 )
......@@ -710,7 +710,7 @@ mime_add_part(mime_t mime, message_t msg)
return ret;
}
int
int
mime_get_message(mime_t mime, message_t *msg)
{
stream_t body_stream;
......@@ -749,7 +749,7 @@ mime_get_message(mime_t mime, message_t *msg)
return ret;
}
int
int
mime_is_multipart(mime_t mime)
{
if ( mime->content_type )
......