Commit e4dc7185 e4dc7185c62a3042b9a05828be83c1f1ca308dcb by Sergey Poznyakoff

Implement imap mailbox (read-only, so far).

* include/mailutils/sys/imap.h (_mu_imap_mailbox_init): New proto.
(_mu_imap_message,_mu_imap_mailbox): New structures.
* libproto/imap/mbox.c: New file.
* libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Put back mbox.c
* libproto/imap/fetch.c: Fix body/bodystructure parsing.
* libproto/imap/folder.c (_mu_imap_folder_destroy): Close folder.
(_mu_imap_folder_open): Implement STARTTLS.
(_imap_record): Accept parameters in URL, use _mu_imap_mailbox_init
to initialize mailbox.
1 parent 22ec4913
......@@ -235,9 +235,46 @@ int _mu_imap_url_init (mu_url_t url);
int _mu_imaps_url_init (mu_url_t url);
/* ----------------------------- */
/* Folder interface */
/* Mailbox interface */
/* ----------------------------- */
int _mu_imap_mailbox_init (mu_mailbox_t mailbox);
#define _MU_IMAP_MSG_SCANNED 0x01
#define _MU_IMAP_MSG_CACHED 0x02
#define _MU_IMAP_MSG_LINES 0x04
struct _mu_imap_message
{
int flags;
size_t uid; /* Message UID */
int attr_flags; /* Attributes */
mu_off_t offset; /* Offset in the message cache stream */
mu_off_t body_start; /* Start of message, relative to offset */
mu_off_t body_end; /* End of message, relative to offset */
size_t header_lines; /* Number of lines in the header */
size_t body_lines; /* Number of lines in the body */
size_t message_size; /* Message size */
size_t message_lines; /* Number of lines in the message */
struct mu_imapenvelope *env; /* IMAP envelope */
mu_message_t message; /* Pointer to the message structure */
struct _mu_imap_mailbox *imbx; /* Back pointer. */
};
#define _MU_IMAP_MBX_UPTODATE 0x01
struct _mu_imap_mailbox
{
int flags;
mu_off_t total_size; /* Total mailbox size. */
struct mu_imap_stat stats; /* Mailbox statistics */
struct _mu_imap_message *msgs; /* Array of messages */
size_t msgs_cnt; /* Number of used slots in msgs */
size_t msgs_max; /* Number of slots in msgs */
mu_stream_t cache; /* Message cache stream */
int last_error; /* Last error code */
mu_mailbox_t mbox;
};
# ifdef __cplusplus
}
......
......@@ -23,8 +23,6 @@ libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@
SUBDIRS = . tests
# FIXME: Put this back when ready
# mbox.c
libmu_imap_la_SOURCES = \
appmsg.c\
appstr.c\
......@@ -68,4 +66,5 @@ libmu_imap_la_SOURCES = \
unselect.c\
unsubscribe.c\
folder.c\
mbox.c\
url.c
......
......@@ -618,7 +618,7 @@ _parse_bodystructure_simple (struct imap_list_element *elt,
struct body_field_map *map;
mu_list_count (elt->v.list, &n);
if (n < 8)
if (n < 7)
return MU_ERR_PARSE;
tok = _mu_imap_list_at (elt->v.list, BSTOK_BODY_TYPE);
......@@ -976,6 +976,7 @@ _extract_string (void **itmv, size_t itmc, void *call_data)
static int
_fetch_fold (void *item, void *data)
{
int rc;
struct imap_list_element *elt = item;
struct parse_response_env *env = data;
......@@ -983,7 +984,6 @@ _fetch_fold (void *item, void *data)
{
case resp_kw:
{
int rc;
char *kw;
size_t kwlen;
struct mapper_tab *mt;
......@@ -1020,6 +1020,26 @@ _fetch_fold (void *item, void *data)
break;
}
case resp_body:
if (_mu_imap_list_element_is_string (elt, "["))
{
env->state = resp_body_section;
break;
}
else
{
env->mapper = _bodystructure_mapper;
_free_fetch_response (env->resp);
rc = alloc_response (&env->resp, MU_IMAP_FETCH_BODYSTRUCTURE);
if (rc)
{
env->status = rc;
return MU_ERR_FAILURE;
}
env->state = resp_val;
}
/* fall through */
case resp_val:
if (env->mapper)
{
......@@ -1034,16 +1054,6 @@ _fetch_fold (void *item, void *data)
env->state = resp_kw;
break;
case resp_body:
if (_mu_imap_list_element_is_string (elt, "["))
env->state = resp_body_section;
else
{
env->mapper = _bodystructure_mapper;
env->state = resp_val;
}
break;
case resp_body_section:
if (elt->type != imap_eltype_string)
{
......
......@@ -39,21 +39,13 @@
#include <mailutils/sys/imap.h>
#include <mailutils/sys/folder.h>
/* Placeholders. */
#define _mu_imap_mailbox_init NULL
#define _mu_imaps_mailbox_init NULL
static void
_mu_imap_folder_destroy (mu_folder_t folder)
{
mu_imap_t imap = folder->data;
if (imap)
{
/*
mu_imap_logout (imap);
mu_imap_disconnect (imap);
*/
mu_folder_close (folder);
mu_imap_destroy (&imap);
folder->data = imap;
}
......@@ -90,13 +82,6 @@ _mu_folder_bad_callback (void *data, int code, size_t sdat, void *pdat)
mu_error (_("This probably indicates a bug in Mailutils client code."));
mu_error (_("Please, report that to <%s>."), PACKAGE_BUGREPORT);
}
#if 0
static void
_mu_folder_fetch_callback (void *data, int code, size_t sdat, void *pdat)
{
mu_folder_t folder = data;
}
#endif
/* Set up an IMAP(S) connection for this folder */
static int
......@@ -187,11 +172,6 @@ _mu_imap_folder_open (mu_folder_t folder, int flags)
mu_imap_register_callback_function (imap, MU_IMAP_CB_BAD,
_mu_folder_bad_callback,
folder);
#if 0
mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
_mu_folder_fetch_callback,
folder);
#endif
rc = mu_imap_connect (imap);
if (rc)
{
......@@ -206,6 +186,32 @@ _mu_imap_folder_open (mu_folder_t folder, int flags)
return rc;
}
#ifdef WITH_TLS
if (!tls && mu_imap_capability_test (imap, "STARTTLS", NULL) == 0)
{
size_t parmc = 0;
char **parmv = NULL;
tls = 1;
if (mu_url_sget_fvpairs (folder->url, &parmc, &parmv) == 0)
{
size_t i;
for (i = 0; i < parmc; i++)
{
if (strcmp (parmv[i], "notls") == 0)
tls = 0;
/*FIXME:
else if (strncmp (parmv[i], "auth=", 5) == 0)
*/
/* unrecognized arguments silently ignored */
}
}
if (tls)
mu_imap_starttls (imap);
}
#endif
if (mu_imap_session_state (imap) == MU_IMAP_SESSION_NONAUTH)
{
rc = mu_authority_authenticate (folder->authority);
......@@ -551,7 +557,7 @@ static struct _mu_record _imap_record =
MU_IMAP_PRIO,
MU_IMAP_SCHEME,
MU_RECORD_DEFAULT,
MU_URL_SCHEME | MU_URL_CRED | MU_URL_INET | MU_URL_PATH,
MU_URL_SCHEME | MU_URL_CRED | MU_URL_INET | MU_URL_PATH | MU_URL_PARAM,
MU_URL_HOST,
_mu_imap_url_init, /* url entry. */
_mu_imap_mailbox_init, /* Mailbox entry. */
......@@ -576,7 +582,7 @@ static struct _mu_record _imaps_record =
MU_URL_SCHEME | MU_URL_CRED | MU_URL_INET | MU_URL_PATH | MU_URL_PARAM,
MU_URL_HOST,
_mu_imaps_url_init, /* url entry. */
_mu_imaps_mailbox_init, /* Mailbox entry. */
_mu_imap_mailbox_init, /* Mailbox entry. */
NULL, /* Mailer entry. */
_mu_imap_folder_init, /* Folder entry. */
NULL, /* No need for a back pointer. */
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2011 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
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <mailutils/errno.h>
#include <mailutils/stream.h>
#include <mailutils/message.h>
#include <mailutils/folder.h>
#include <mailutils/assoc.h>
#include <mailutils/url.h>
#include <mailutils/io.h>
#include <mailutils/nls.h>
#include <mailutils/diag.h>
#include <mailutils/filter.h>
#include <mailutils/observer.h>
#include <mailutils/envelope.h>
#include <mailutils/address.h>
#include <mailutils/attribute.h>
#include <mailutils/header.h>
#include <mailutils/body.h>
#include <mailutils/sys/imap.h>
#define _imap_mbx_clrerr(imbx) ((imbx)->last_error = 0)
#define _imap_mbx_errno(imbx) ((imbx)->last_error)
#define _imap_mbx_uptodate(imbx) ((imbx)->flags & _MU_IMAP_MBX_UPTODATE)
static int _imap_mbx_scan (mu_mailbox_t mbox, size_t msgno, size_t *pcount);
static int _imap_mbx_is_updated (mu_mailbox_t mbox);
/* ------------------------------- */
/* Auxiliary message functions */
/* ------------------------------- */
static size_t
_imap_msg_no (struct _mu_imap_message *imsg)
{
return imsg - imsg->imbx->msgs + 1;
}
static int
_imap_fetch_with_callback (mu_imap_t imap, char *msgset, char *items,
mu_imap_callback_t cb, void *data)
{
int rc;
mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH, cb, data);
rc = mu_imap_fetch (imap, 0, msgset, items);
mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH, NULL, NULL);
return rc;
}
static void
_imap_msg_free (struct _mu_imap_message *msg)
{
mu_message_imapenvelope_free (msg->env);
mu_message_destroy (&msg->message, msg);
}
struct save_closure
{
mu_stream_t save_stream;
size_t size;
struct _mu_imap_message *imsg;
};
static void
_save_message (void *data, int code, size_t sdat, void *pdat)
{
struct save_closure *clos = data;
struct _mu_imap_message *imsg = clos->imsg;
struct _mu_imap_mailbox *imbx = imsg->imbx;
mu_list_t list = pdat;
union mu_imap_fetch_response *resp;
int rc;
mu_stream_t istr, flt;
mu_off_t size;
rc = mu_list_get (list, 0, (void**)&resp);
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("cannot get response item: %s"),
mu_strerror (rc)));
imbx->last_error = rc;
return;
}
if (resp->type != MU_IMAP_FETCH_BODY)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("fetch returned a not requested item %d"), resp->type));
imbx->last_error = MU_ERR_FAILURE;
return;
}
rc = mu_static_memory_stream_create (&istr, resp->body.text,
strlen (resp->body.text));
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("mu_static_memory_stream_create: %s"),
mu_strerror (rc)));
imbx->last_error = rc;
return;
}
rc = mu_filter_create (&flt, istr, "CRLF", MU_FILTER_DECODE, MU_STREAM_READ);
mu_stream_unref (istr);
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("mu_filter_create: %s"), mu_strerror (rc)));
imbx->last_error = rc;
return;
}
rc = mu_stream_copy (clos->save_stream, flt, 0, &size);
mu_stream_destroy (&flt);
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("copying to cache failed: %s"), mu_strerror (rc)));
imbx->last_error = rc;
return;
}
clos->size = size;
}
static int
__imap_msg_get_stream (struct _mu_imap_message *imsg, size_t msgno,
mu_stream_t *pstr)
{
int rc;
struct _mu_imap_mailbox *imbx = imsg->imbx;
mu_folder_t folder = imbx->mbox->folder;
mu_imap_t imap = folder->data;
if (!(imsg->flags & _MU_IMAP_MSG_CACHED))
{
char *msgset;
if (!imbx->cache)
{
rc = mu_temp_file_stream_create (&imbx->cache, NULL, 0);
if (rc)
/* FIXME: Try to recover first */
return rc;
mu_stream_set_buffer (imbx->cache, mu_buffer_full, 8192);
}
rc = mu_stream_size (imbx->cache, &imsg->offset);
if (rc)
return rc;
rc = mu_asprintf (&msgset, "%lu", msgno);
if (rc == 0)
{
struct save_closure clos;
clos.imsg = imsg;
clos.save_stream = imbx->cache;
_imap_mbx_clrerr (imbx);
rc = _imap_fetch_with_callback (imap, msgset, "BODY[]",
_save_message, &clos);
free (msgset);
if (rc == 0 && !_imap_mbx_errno (imbx))
imsg->message_size = clos.size;
}
if (rc)
return rc;
imsg->flags |= _MU_IMAP_MSG_CACHED;
}
return mu_streamref_create_abridged (pstr, imbx->cache,
imsg->offset,
imsg->offset + imsg->message_size - 1);
}
static int
_imap_msg_scan (struct _mu_imap_message *imsg)
{
int rc;
mu_stream_t stream;
struct mu_message_scan scan;
size_t msgno = _imap_msg_no (imsg);
if (imsg->flags & _MU_IMAP_MSG_SCANNED)
return 0;
rc = __imap_msg_get_stream (imsg, msgno, &stream);
if (rc)
return rc;
scan.flags = MU_SCAN_SEEK | MU_SCAN_SIZE;
scan.message_start = 0;
scan.message_size = imsg->message_size;
rc = mu_stream_scan_message (stream, &scan);
mu_stream_unref (stream);
if (rc == 0)
{
imsg->body_start = scan.body_start;
imsg->body_end = scan.body_end;
imsg->header_lines = scan.header_lines;
imsg->body_lines = scan.body_lines;
imsg->message_lines = imsg->header_lines + 1 + imsg->body_lines;
imsg->flags |= _MU_IMAP_MSG_SCANNED;
}
return rc;
}
/* ------------------------------- */
/* Message envelope */
/* ------------------------------- */
static int
_imap_env_date (mu_envelope_t env, char *buf, size_t len,
size_t *pnwrite)
{
struct _mu_imap_message *imsg = mu_envelope_get_owner (env);
mu_stream_t str;
int rc;
if (!buf)
rc = mu_nullstream_create (&str, MU_STREAM_WRITE);
else
rc = mu_fixed_memory_stream_create (&str, buf, len, MU_STREAM_WRITE);
if (rc == 0)
{
mu_stream_stat_buffer statbuf;
mu_stream_set_stat (str, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT),
statbuf);
rc = mu_c_streamftime (str, MU_DATETIME_FROM,
&imsg->env->date, &imsg->env->tz);
mu_stream_destroy (&str);
if (pnwrite)
*pnwrite = statbuf[MU_STREAM_STAT_OUT];
}
return rc;
}
static int
_imap_env_sender (mu_envelope_t env, char *buf, size_t len,
size_t *pnwrite)
{
struct _mu_imap_message *imsg = mu_envelope_get_owner (env);
mu_address_t addr = imsg->env->sender ? imsg->env->sender : imsg->env->from;
if (!addr)
return MU_ERR_NOENT;
return mu_address_get_email (addr, 1, buf, len, pnwrite);
}
static int
_imap_msg_env_setup (struct _mu_imap_message *imsg, mu_message_t message)
{
mu_envelope_t env;
int rc = mu_envelope_create (&env, imsg);
if (rc == 0)
{
mu_envelope_set_sender (env, _imap_env_sender, imsg);
mu_envelope_set_date (env, _imap_env_date, imsg);
rc = mu_message_set_envelope (message, env, imsg);
}
return rc;
}
/* ------------------------------- */
/* Message attributes */
/* ------------------------------- */
static int
_imap_attr_get_flags (mu_attribute_t attr, int *pflags)
{
struct _mu_imap_message *imsg = mu_attribute_get_owner (attr);
if (!imsg)
return EINVAL;
if (pflags)
*pflags = imsg->attr_flags;
return 0;
}
static int
_imap_attr_set_flags (mu_attribute_t attr, int flags)
{
struct _mu_imap_message *imsg = mu_attribute_get_owner (attr);
if (!imsg)
return EINVAL;
imsg->attr_flags |= flags;
return 0;
}
static int
_imap_attr_clr_flags (mu_attribute_t attr, int flags)
{
struct _mu_imap_message *imsg = mu_attribute_get_owner (attr);
if (!imsg)
return EINVAL;
imsg->attr_flags |= flags;
return 0;
}
int
_imap_msg_attr_setup (struct _mu_imap_message *imsg, mu_message_t message)
{
mu_attribute_t attribute;
int rc = mu_attribute_create (&attribute, imsg);
if (rc == 0)
{
mu_attribute_set_get_flags (attribute, _imap_attr_get_flags, imsg);
mu_attribute_set_set_flags (attribute, _imap_attr_set_flags, imsg);
mu_attribute_set_unset_flags (attribute, _imap_attr_clr_flags, imsg);
rc = mu_message_set_attribute (message, attribute, imsg);
}
return rc;
}
/* ------------------------------- */
/* Header functions */
/* ------------------------------- */
static int
_imap_hdr_fill (void *data, char **pbuf, size_t *plen)
{
struct _mu_imap_message *imsg = data;
struct _mu_imap_mailbox *imbx = imsg->imbx;
mu_imap_t imap = imbx->mbox->folder->data;
struct save_closure clos;
char *msgset;
unsigned long msgno = _imap_msg_no (imsg);
int rc;
clos.imsg = imsg;
rc = mu_memory_stream_create (&clos.save_stream, MU_STREAM_RDWR);
_imap_mbx_clrerr (imbx);
rc = mu_asprintf (&msgset, "%lu", msgno);
if (rc == 0)
{
rc = _imap_fetch_with_callback (imap, msgset, "BODY.PEEK[HEADER]",
_save_message, &clos);
free (msgset);
if (rc == 0)
{
char *buf;
mu_off_t size;
mu_stream_size (clos.save_stream, &size);
buf = malloc (size + 1);
if (!buf)
rc = ENOMEM;
else
{
mu_stream_seek (clos.save_stream, 0, MU_SEEK_SET, NULL);
rc = mu_stream_read (clos.save_stream, buf, size, NULL);
if (rc == 0)
{
*pbuf = buf;
*plen = size;
}
else
free (buf);
}
}
}
mu_stream_destroy (&clos.save_stream);
return rc;
}
static int
_imap_msg_header_setup (struct _mu_imap_message *imsg, mu_message_t message)
{
int rc;
mu_header_t header = NULL;
rc = mu_header_create (&header, NULL, 0);
if (rc)
return rc;
mu_header_set_fill (header, _imap_hdr_fill, imsg);
return mu_message_set_header (message, header, imsg);
}
/* ------------------------------- */
/* Body functions */
/* ------------------------------- */
static int
_imap_body_get_stream (mu_body_t body, mu_stream_t *pstr)
{
mu_message_t msg = mu_body_get_owner (body);
struct _mu_imap_message *imsg = mu_message_get_owner (msg);
struct _mu_imap_mailbox *imbx = imsg->imbx;
int rc;
rc = _imap_msg_scan (imsg);
if (rc)
return rc;
return mu_streamref_create_abridged (pstr, imbx->cache,
imsg->offset + imsg->body_start,
imsg->offset + imsg->body_end - 1);
}
static int
_imap_body_size (mu_body_t body, size_t *psize)
{
mu_message_t msg = mu_body_get_owner (body);
struct _mu_imap_message *imsg = mu_message_get_owner (msg);
int rc;
rc = _imap_msg_scan (imsg);
if (rc)
return rc;
*psize = imsg->body_end - imsg->body_start;
return 0;
}
static int
_imap_body_lines (mu_body_t body, size_t *psize)
{
mu_message_t msg = mu_body_get_owner (body);
struct _mu_imap_message *imsg = mu_message_get_owner (msg);
int rc;
rc = _imap_msg_scan (imsg);
if (rc)
return rc;
*psize = imsg->body_lines;
return 0;
}
static int
_imap_mbx_body_setup (struct _mu_imap_message *imsg, mu_message_t message)
{
int rc;
mu_body_t body;
/* FIXME: The owner of the body *must* be the message it belongs to. */
rc = mu_body_create (&body, message);
if (rc)
return rc;
mu_body_set_get_stream (body, _imap_body_get_stream, message);
mu_body_set_size (body, _imap_body_size, message);
mu_body_set_lines (body, _imap_body_lines, message);
return mu_message_set_body (message, body, imsg);
}
/* ------------------------------- */
/* Message functions */
/* ------------------------------- */
static int
_imap_msg_get_stream (mu_message_t msg, mu_stream_t *pstr)
{
struct _mu_imap_message *imsg = mu_message_get_owner (msg);
return __imap_msg_get_stream (imsg, _imap_msg_no (imsg), pstr);
}
static int
_imap_msg_size (mu_message_t msg, size_t *psize)
{
struct _mu_imap_message *imsg = mu_message_get_owner (msg);
*psize = imsg->message_size;
return 0;
}
static int
_imap_msg_lines (mu_message_t msg, size_t *plines, int quick)
{
struct _mu_imap_message *imsg = mu_message_get_owner (msg);
struct _mu_imap_mailbox *imbx = imsg->imbx;
mu_mailbox_t mbox = imbx->mbox;
if (!(imsg->flags & _MU_IMAP_MSG_LINES))
{
int rc;
if (quick && !(imsg->flags & _MU_IMAP_MSG_CACHED))
return MU_ERR_INFO_UNAVAILABLE;
if (!_imap_mbx_uptodate (imbx))
_imap_mbx_scan (mbox, 1, NULL);
rc = _imap_msg_scan (imsg);
if (rc)
return rc;
}
*plines = imsg->message_lines;
return 0;
}
static int
_imap_mbx_get_message (mu_mailbox_t mailbox, size_t msgno, mu_message_t *pmsg)
{
struct _mu_imap_mailbox *imbx = mailbox->data;
struct _mu_imap_message *imsg;
int rc;
/* If we did not start a scanning yet do it now. */
if (!_imap_mbx_uptodate (imbx))
_imap_mbx_scan (mailbox, 1, NULL);
if (msgno > imbx->msgs_cnt)
return MU_ERR_NOENT;
imsg = imbx->msgs + msgno - 1;
if (!imsg->message)
{
mu_message_t msg;
rc = mu_message_create (&msg, imsg);
if (rc)
return rc;
mu_message_set_get_stream (msg, _imap_msg_get_stream, imsg);
mu_message_set_size (msg, _imap_msg_size, imsg);
mu_message_set_lines (msg, _imap_msg_lines, imsg);
do
{
rc = _imap_msg_env_setup (imsg, msg);
if (rc)
break;
rc = _imap_msg_attr_setup (imsg, msg);
if (rc)
break;
rc = _imap_msg_header_setup (imsg, msg);
if (rc)
break;
rc = _imap_mbx_body_setup (imsg, msg);
}
while (0);
if (rc)
{
mu_message_destroy (&msg, imsg);
return rc;
}
imsg->message = msg;
}
*pmsg = imsg->message;
return 0;
}
/* ------------------------------- */
/* Mailbox functions */
/* ------------------------------- */
static int
_imap_realloc_messages (struct _mu_imap_mailbox *imbx, size_t count)
{
if (count > imbx->msgs_max)
{
struct _mu_imap_message *newmsgs = realloc (imbx->msgs,
count * sizeof (*newmsgs));
if (!newmsgs)
return ENOMEM;
memset (newmsgs + imbx->msgs_max, 0,
sizeof (*newmsgs) * (count - imbx->msgs_max));
imbx->msgs = newmsgs;
imbx->msgs_max = count;
}
return 0;
}
static void
_imap_mbx_destroy (mu_mailbox_t mailbox)
{
size_t i;
struct _mu_imap_mailbox *imbx = mailbox->data;
if (!imbx)
return;
if (imbx->msgs)
{
for (i = 0; i < imbx->msgs_cnt; i++)
_imap_msg_free (imbx->msgs + i);
free (imbx->msgs);
}
mu_stream_unref (imbx->cache);
free (imbx);
mailbox->data = NULL;
}
static void
_imap_update_callback (void *data, int code, size_t sdat, void *pdat)
{
struct _mu_imap_mailbox *imbx = data;
memcpy (&imbx->stats, pdat, sizeof (imbx->stats));
imbx->flags &= ~_MU_IMAP_MBX_UPTODATE;
}
static int
_imap_mbx_open (mu_mailbox_t mbox, int flags)
{
struct _mu_imap_mailbox *imbx = mbox->data;
mu_folder_t folder = mbox->folder;
int rc;
const char *mbox_name;
mu_url_t url;
mu_imap_t imap;
rc = mu_mailbox_get_url (mbox, &url);
if (rc)
return rc;
rc = mu_url_sget_path (url, &mbox_name);
if (rc == MU_ERR_NOENT)
mbox_name = "INBOX";
else if (rc)
return rc;
rc = mu_folder_open (folder, flags);
if (rc)
return rc;
imap = folder->data;
mu_imap_register_callback_function (imap, MU_IMAP_CB_RECENT_COUNT,
_imap_update_callback,
imbx);
mu_imap_register_callback_function (imap, MU_IMAP_STAT_MESSAGE_COUNT,
_imap_update_callback,
imbx);
rc = mu_imap_select (imap, mbox_name,
flags & (MU_STREAM_WRITE|MU_STREAM_APPEND),
&imbx->stats);
if (rc)
return rc;
if (imbx->stats.flags & MU_IMAP_STAT_MESSAGE_COUNT)
rc = _imap_realloc_messages (imbx, imbx->stats.message_count);
return rc;
}
static int
_imap_mbx_close (mu_mailbox_t mbox)
{
int rc;
mu_folder_t folder = mbox->folder;
mu_imap_t imap = folder->data;
if (mu_imap_capability_test (imap, "UNSELECT", NULL) == 0)
rc = mu_imap_unselect (imap);
else
rc = mu_imap_close (imap);
return rc;
}
static int
_imap_messages_count (mu_mailbox_t mbox, size_t *pcount)
{
struct _mu_imap_mailbox *imbx = mbox->data;
if (imbx->stats.flags & MU_IMAP_STAT_MESSAGE_COUNT)
*pcount = imbx->stats.message_count;
else
return MU_ERR_INFO_UNAVAILABLE;
return 0;
}
static int
_imap_messages_recent (mu_mailbox_t mbox, size_t *pcount)
{
struct _mu_imap_mailbox *imbx = mbox->data;
if (imbx->stats.flags & MU_IMAP_STAT_RECENT_COUNT)
*pcount = imbx->stats.recent_count;
else
return MU_ERR_INFO_UNAVAILABLE;
return 0;
}
static int
_imap_uidnext (mu_mailbox_t mbox, size_t *pn)
{
struct _mu_imap_mailbox *imbx = mbox->data;
if (imbx->stats.flags & MU_IMAP_STAT_UIDNEXT)
*pn = imbx->stats.uidnext;
else
return MU_ERR_INFO_UNAVAILABLE;
return 0;
}
static int
_imap_message_unseen (mu_mailbox_t mbox, size_t *pn)
{
struct _mu_imap_mailbox *imbx = mbox->data;
if (imbx->stats.flags & MU_IMAP_STAT_FIRST_UNSEEN)
*pn = imbx->stats.uidnext;
else
return MU_ERR_INFO_UNAVAILABLE;
return 0;
}
static int
_imap_uidvalidity (mu_mailbox_t mbox, unsigned long *pn)
{
struct _mu_imap_mailbox *imbx = mbox->data;
if (imbx->stats.flags & MU_IMAP_STAT_UIDVALIDITY)
*pn = imbx->stats.uidvalidity;
else
return MU_ERR_INFO_UNAVAILABLE;
return 0;
}
static int
_imap_mbx_expunge (mu_mailbox_t mbox)
{
mu_folder_t folder = mbox->folder;
mu_imap_t imap = folder->data;
return mu_imap_expunge (imap);
}
static int _compute_lines (struct mu_bodystructure *bs, size_t *pcount);
static int
sum_lines (void *item, void *data)
{
struct mu_bodystructure *bs = item;
size_t *pn = data;
size_t n;
if (_compute_lines (bs, &n))
return 1;
*pn += n;
return 0;
}
static int
_compute_lines (struct mu_bodystructure *bs, size_t *pcount)
{
switch (bs->body_message_type)
{
case mu_message_other:
break;
case mu_message_text:
*pcount = bs->v.text.body_lines;
return 0;
case mu_message_rfc822:
*pcount = bs->v.rfc822.body_lines;
return 0;
case mu_message_multipart:
*pcount = 0;
return mu_list_foreach (bs->v.multipart.body_parts, sum_lines, pcount);
}
return 1;
}
static int
fetch_response_parser (void *item, void *data)
{
union mu_imap_fetch_response *resp = item;
struct _mu_imap_message *imsg = data;
switch (resp->type)
{
case MU_IMAP_FETCH_UID:
imsg->uid = resp->uid.uid;
break;
case MU_IMAP_FETCH_FLAGS:
imsg->attr_flags = resp->flags.flags;
break;
case MU_IMAP_FETCH_ENVELOPE:
imsg->env = resp->envelope.imapenvelope;
resp->envelope.imapenvelope = NULL; /* Steal the envelope */
break;
case MU_IMAP_FETCH_RFC822_SIZE:
imsg->message_size = resp->rfc822_size.size;
break;
case MU_IMAP_FETCH_BODYSTRUCTURE:
{
size_t n;
if (_compute_lines (resp->bodystructure.bs, &n) == 0)
{
imsg->message_lines = n;
imsg->flags |= _MU_IMAP_MSG_LINES;
}
}
break;
default:
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE0,
(_("fetch returned a not requested item %d"),
resp->type));
break;
}
return 0;
}
static void
_imap_fetch_callback (void *data, int code, size_t sdat, void *pdat)
{
struct _mu_imap_mailbox *imbx = data;
mu_mailbox_t mbox = imbx->mbox;
mu_list_t list = pdat;
int rc;
struct _mu_imap_message *imsg;
rc = _imap_realloc_messages (imbx, sdat - 1);
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("cannot reallocate array of messages: %s"),
mu_strerror (rc)));
imbx->last_error = rc;
return;
}
if (imbx->msgs_cnt < sdat)
imbx->msgs_cnt = sdat;
imsg = imbx->msgs + sdat - 1;
imsg->imbx = imbx;
mu_list_foreach (list, fetch_response_parser, imsg);
if (mbox->observable)
{
if (((sdat + 1) % 10) == 0)
mu_observable_notify (imbx->mbox->observable,
MU_EVT_MAILBOX_PROGRESS, NULL);
}
}
static int
_imap_mbx_scan (mu_mailbox_t mbox, size_t msgno, size_t *pcount)
{
struct _mu_imap_mailbox *imbx = mbox->data;
mu_folder_t folder = mbox->folder;
mu_imap_t imap = folder->data;
char *msgset;
int rc;
static char _imap_scan_items[] = "(UID FLAGS ENVELOPE RFC822.SIZE BODY)";
rc = mu_asprintf (&msgset, "%lu:*", (unsigned long) msgno);
if (rc)
return rc;
_imap_mbx_clrerr (imbx);
rc = _imap_fetch_with_callback (imap, msgset, _imap_scan_items,
_imap_fetch_callback, imbx);
free (msgset);
if (rc == 0)
rc = _imap_mbx_errno (imbx);
if (rc == 0)
{
size_t i;
mu_off_t total = 0;
imbx->flags |= _MU_IMAP_MBX_UPTODATE;
for (i = 1; i <= imbx->msgs_cnt; i++)
{
total += imbx->msgs[i-1].message_size;
/* MU_EVT_MESSAGE_ADD must be delivered only when it is already
possible to retrieve the message in question. It could not be
done in the fetch handler for obvious reasons. Hence the extra
loop. */
if (mbox->observable)
mu_observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD, &i);
}
imbx->total_size = total;
if (pcount)
*pcount = imbx->msgs_cnt;
}
return rc;
}
static int
_imap_mbx_is_updated (mu_mailbox_t mbox)
{
struct _mu_imap_mailbox *imbx = mbox->data;
mu_folder_t folder = mbox->folder;
mu_imap_t imap = folder->data;
int rc;
rc = mu_imap_noop (imap);
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("mu_imap_noop: %s"), mu_strerror (rc)));
imbx->last_error = rc;
}
return imbx->flags & _MU_IMAP_MBX_UPTODATE;
}
int
_mu_imap_mailbox_init (mu_mailbox_t mailbox)
{
struct _mu_imap_mailbox *mbx = calloc (1, sizeof (*mbx));
if (!mbx)
return ENOMEM;
mbx->mbox = mailbox;
mailbox->data = mbx;
mailbox->_destroy = _imap_mbx_destroy;
mailbox->_open = _imap_mbx_open;
mailbox->_close = _imap_mbx_close;
mailbox->_expunge = _imap_mbx_expunge;
mailbox->_messages_count = _imap_messages_count;
mailbox->_messages_recent = _imap_messages_recent;
mailbox->_message_unseen = _imap_message_unseen;
mailbox->_uidvalidity = _imap_uidvalidity;
mailbox->_uidnext = _imap_uidnext;
mailbox->_scan = _imap_mbx_scan;
mailbox->_is_updated = _imap_mbx_is_updated;
mailbox->_get_message = _imap_mbx_get_message;
//FIXME
#if 0
/* Messages. */
mailbox->_append_message = _mu_imap_append_message;
#endif
return 0;
}