summaryrefslogtreecommitdiff
path: root/libproto/pop/mbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'libproto/pop/mbox.c')
-rw-r--r--libproto/pop/mbox.c2503
1 files changed, 434 insertions, 2069 deletions
diff --git a/libproto/pop/mbox.c b/libproto/pop/mbox.c
index 5268a9f35..3b538a0ef 100644
--- a/libproto/pop/mbox.c
+++ b/libproto/pop/mbox.c
@@ -23,21 +23,13 @@
#ifdef ENABLE_POP
-#include <termios.h>
-#include <errno.h>
#include <stdlib.h>
-#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
-
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
+#include <mailutils/pop3.h>
#include <mailutils/attribute.h>
#include <mailutils/auth.h>
#include <mailutils/body.h>
@@ -57,737 +49,61 @@
#include <mailutils/mutil.h>
#include <mailutils/cstr.h>
#include <mailutils/cctype.h>
+#include <mailutils/opool.h>
#include <mailutils/sys/folder.h>
#include <mailutils/sys/mailbox.h>
#include <mailutils/sys/registrar.h>
#include <mailutils/sys/url.h>
-#define PROP_RFC822 1
-
-/* Advance declarations. */
-struct _pop_data;
-struct _pop_message;
+#define _POP3_MSG_INBODY 0x01
+#define _POP3_MSG_SKIPHDR 0x02
+#define _POP3_MSG_SKIPBDY 0x04
-typedef struct _pop_data * pop_data_t;
-typedef struct _pop_message * pop_message_t;
-
-/* The different possible states of a Pop client, Note that POP3 is not
- reentrant i.e. it is only one channel, so it is not possible to start
- Another operation while one is running. The only resort is to close the
- connection and reopen it again. This is what we do, the downside is that
- the client as to get the authentication again user/pass. */
-enum pop_state
+struct _pop3_message
{
- POP_NO_STATE, POP_STATE_DONE,
- POP_OPEN_CONNECTION,
- POP_GREETINGS,
- POP_CAPA, POP_CAPA_ACK,
- POP_APOP, POP_APOP_ACK,
- POP_DELE, POP_DELE_ACK,
- POP_LIST, POP_LIST_ACK, POP_LIST_RX,
- POP_QUIT, POP_QUIT_ACK,
- POP_NOOP, POP_NOOP_ACK,
- POP_RETR, POP_RETR_ACK, POP_RETR_RX_HDR, POP_RETR_RX_BODY,
- POP_RSET, POP_RSET_ACK,
- POP_STAT, POP_STAT_ACK,
- POP_STLS, POP_STLS_ACK,
- POP_TOP, POP_TOP_ACK, POP_TOP_RX,
- POP_UIDL, POP_UIDL_ACK,
- POP_AUTH, POP_AUTH_DONE,
- POP_AUTH_USER, POP_AUTH_USER_ACK,
- POP_AUTH_PASS, POP_AUTH_PASS_ACK
-};
-
-/* POP3 capabilities */
-#define CAPA_TOP 0x00000001
-#define CAPA_USER 0x00000002
-#define CAPA_UIDL 0x00000004
-#define CAPA_RESP_CODES 0x00000008
-#define CAPA_LOGIN_DELAY 0x00000010
-#define CAPA_PIPELINING 0x00000020
-#define CAPA_EXPIRE 0x00000040
-#define CAPA_SASL 0x00000080
-#define CAPA_STLS 0x00000100
-#define CAPA_IMPLEMENTATION 0x00000200
-
-static void pop_destroy (mu_mailbox_t);
-static int pop_capa (mu_mailbox_t);
-static int pop_stls (mu_mailbox_t);
-
-/* Functions/Methods that implements the mu_mailbox_t API. */
-static int pop_open (mu_mailbox_t, int);
-static int pop_close (mu_mailbox_t);
-static int pop_get_message (mu_mailbox_t, size_t, mu_message_t *);
-static int pop_messages_count (mu_mailbox_t, size_t *);
-static int pop_messages_recent (mu_mailbox_t, size_t *);
-static int pop_message_unseen (mu_mailbox_t, size_t *);
-static int pop_expunge (mu_mailbox_t);
-static int pop_scan (mu_mailbox_t, size_t, size_t *);
-static int pop_is_updated (mu_mailbox_t);
-
-/* The implementation of mu_message_t */
-int _pop_user (mu_authority_t);
-int _pop_apop (mu_authority_t);
-static int pop_get_size (mu_mailbox_t, mu_off_t *);
-/* We use pop_top for retreiving headers. */
-/* static int pop_header_read (mu_header_t, char *, size_t, mu_off_t, size_t *); */
-static int pop_body_transport (mu_stream_t, mu_transport_t *, mu_transport_t *);
-static int pop_body_size (mu_body_t, size_t *);
-static int pop_body_lines (mu_body_t, size_t *);
-static int pop_body_read (mu_stream_t, char *, size_t, mu_off_t, size_t *);
-static int pop_message_read (mu_stream_t, char *, size_t, mu_off_t, size_t *);
-static int pop_message_size (mu_message_t, size_t *);
-static int pop_message_transport (mu_stream_t, mu_transport_t *, mu_transport_t *);
-static int pop_top (mu_header_t, char *, size_t, mu_off_t, size_t *);
-static int pop_retr (pop_message_t, char *, size_t, mu_off_t, size_t *);
-static int pop_get_transport2 (pop_message_t, mu_transport_t *, mu_transport_t *);
-static int pop_get_attribute (mu_attribute_t, int *);
-static int pop_set_attribute (mu_attribute_t, int);
-static int pop_unset_attribute (mu_attribute_t, int);
-static int pop_uidl (mu_message_t, char *, size_t, size_t *);
-static int pop_uid (mu_message_t, size_t *);
-static int fill_buffer (pop_data_t, char *, size_t);
-static int pop_sleep (int);
-static int pop_readline (pop_data_t);
-static int pop_read_ack (pop_data_t);
-static int pop_writeline (pop_data_t, const char *, ...)
- MU_PRINTFLIKE(2,3);
-static int pop_write (pop_data_t);
-static int pop_get_user (mu_authority_t);
-static int pop_get_passwd (mu_authority_t);
-static char *pop_get_timestamp (pop_data_t);
-static int pop_get_md5 (pop_data_t);
-
-/* This structure holds the info for a message. The pop_message_t
- type, will serve as the owner of the mu_message_t and contains the command to
- send to "RETR"eive the specify message. The problem comes from the header.
- If the POP server supports TOP, we can cleanly fetch the header.
- But otherwise we use the clumsy approach. .i.e for the header we read 'til
- ^\n then discard the rest, for the body we read after ^\n and discard the
- beginning. This is a waste, Pop was not conceive for this obviously. */
-struct _pop_message
-{
- int inbody;
- int skip_header;
- int skip_body;
+ int flags;
size_t body_size;
size_t header_size;
size_t body_lines;
size_t header_lines;
- size_t mu_message_size;
+ size_t message_size;
size_t num;
char *uidl; /* Cache the uidl string. */
int attr_flags;
mu_message_t message;
- pop_data_t mpd; /* Back pointer. */
+ struct _pop3_mailbox *mpd; /* Back pointer. */
};
-
-/* Structure to hold things general to the POP mailbox, like its state, how
- many messages we have so far etc ... */
-struct _pop_data
-{
- void *func; /* Indicate a command is in operation, busy. */
- size_t id; /* A second level of distincion, we maybe in the same function
- but working on a different message. */
- int pops; /* POPS or POP? */
- char *greeting_banner; /* A greeting banner */
- unsigned long capa; /* Server capabilities */
- enum pop_state state;
- pop_message_t *pmessages;
- size_t pmessages_count;
- size_t messages_count;
- size_t size;
-
- /* Working I/O buffers. */
- char *buffer;
- size_t buflen; /* Len of buffer. */
- char *ptr; /* Points to the end of the buffer i.e the non consume chars. */
- char *nl; /* Points to the '\n' char in te string. */
- mu_off_t offset; /* Dummy, this is use because of the stream buffering.
- The mu_stream_t maintains and offset and the offset we use must
- be in sync. */
-
- int is_updated;
- char *user; /* Temporary holders for user and passwd. */
- mu_secret_t secret;
- char *digest;
- mu_mailbox_t mbox; /* Back pointer. */
-} ;
-
-/* Usefull little Macros, since these are very repetitive. */
-
-/* Check if we're busy ? */
-/* POP is a one channel download protocol, so if someone
- is trying to execute a command while another is running
- something is seriously incorrect, So the best course
- of action is to close down the connection and start a new one.
- For example mu_mime_t only reads part of the message. If a client
- wants to read different part of the message via mime it should
- download it first. POP does not have the features of IMAP for
- multipart messages.
- Let see a concrete example:
- {
- mu_mailbox_t mbox; mu_message_t msg; mu_stream_t stream; char buffer[105];
- mu_mailbox_create (&mbox, "pop://qnx.com");
- mu_mailbox_get_message (mbox, 1, &msg);
- mu_message_get_stream (msg, &stream);
- while (mu_stream_readline (stream, buffer, sizeof(buffer), NULL) != 0) { ..}
- }
- if in the while of the readline, one try to get another email. The pop
- server will get seriously confused, and the second message will still
- be the first one, There is no way to tell POP servers yo! stop/abort.
- The approach is to close the stream and reopen again. So every time
- we go in to a function our state is preserve by the triplets
- mpd->{func,state,id}. The macro CHECK_BUSY checks if we are not
- in another operation if not you get access if yes the stream is close
- and pop_open() is recall again for a new connection.
- */
-#define CHECK_BUSY(mbox, mpd, function, identity) \
-do \
- { \
- int err = mu_monitor_wrlock (mbox->monitor); \
- if (err != 0) \
- return err; \
- if ((mpd->func && mpd->func != function) \
- || (mpd->id && mpd->id != (size_t)identity)) \
- { \
- mpd->id = 0; \
- mpd->func = (void *)pop_open; \
- mpd->state = POP_NO_STATE; \
- mu_monitor_unlock (mbox->monitor); \
- err = pop_open (mbox, mbox->flags); \
- if (err != 0) \
- { \
- return err; \
- } \
- } \
- else \
- { \
- mpd->id = (size_t)identity; \
- mpd->func = func; \
- mu_monitor_unlock (mbox->monitor); \
- } \
- } \
-while (0)
-
-/* Clear the state. */
-#define CLEAR_STATE(mpd) \
- mpd->id = 0, mpd->func = NULL, mpd->state = POP_NO_STATE
-
-/* Clear the state and close the stream. */
-#define CHECK_ERROR_CLOSE(mbox, mpd, status) \
-do \
- { \
- if (status != 0) \
- { \
- mu_stream_close (mbox->stream); \
- CLEAR_STATE (mpd); \
- mpd->func = (void *)-1; \
- MU_DEBUG1 (mbox->debug, MU_DEBUG_PROT, \
- "CHECK_ERROR_CLOSE: %s\n", mu_strerror (status));\
- return status; \
- } \
- } \
-while (0)
-
-/* If error, clear the state and return. */
-#define CHECK_ERROR(mpd, status) \
-do \
- { \
- if (status != 0) \
- { \
- CLEAR_STATE (mpd); \
- mpd->func = (void*)-1; \
- MU_DEBUG1(mpd->mbox->debug, MU_DEBUG_PROT, \
- "CHECK_ERROR: %s\n", mu_strerror (status));\
- return status; \
- } \
- } \
-while (0)
-
-/* Clear the state for non recoverable error. */
-#define CHECK_EAGAIN(mpd, status) \
-do \
- { \
- if (status != 0) \
- { \
- if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \
- { \
- CLEAR_STATE (mpd); \
- mpd->func = (void *)-1; \
- MU_DEBUG1(mpd->mbox->debug, MU_DEBUG_PROT, \
- "CHECK_EAGAIN: %s\n", mu_strerror (status));\
- } \
- return status; \
- } \
- } \
-while (0)
-
-
-/* Allocate mu_mailbox_t, allocate pop internal structures. */
-static int
-_mailbox_pop_and_pops_init (mu_mailbox_t mbox, int pops)
-{
- pop_data_t mpd;
- int status = 0;
-
- /* Allocate specifics for pop data. */
- mpd = mbox->data = calloc (1, sizeof (*mpd));
- if (mbox->data == NULL)
- return ENOMEM;
-
- mpd->mbox = mbox; /* Back pointer. */
- mpd->state = POP_NO_STATE; /* Init with no state. */
- mpd->pops = pops;
-
- /* Initialize the structure. */
- mbox->_destroy = pop_destroy;
-
- mbox->_open = pop_open;
- mbox->_close = pop_close;
-
- /* Messages. */
- mbox->_get_message = pop_get_message;
- mbox->_messages_count = pop_messages_count;
- mbox->_messages_recent = pop_messages_recent;
- mbox->_message_unseen = pop_message_unseen;
- mbox->_expunge = pop_expunge;
-
- mbox->_scan = pop_scan;
- mbox->_is_updated = pop_is_updated;
-
- mbox->_get_size = pop_get_size;
-
- /* Set our properties. */
- {
- mu_property_t property = NULL;
- mu_mailbox_get_property (mbox, &property);
- mu_property_set_value (property, "TYPE", "POP3", 1);
- }
-
- /* Hack! POP does not really have a folder. */
- mbox->folder->data = mbox;
-
- return status;
-}
-
-int
-_mailbox_pop_init (mu_mailbox_t mbox)
-{
- return _mailbox_pop_and_pops_init (mbox, 0);
-}
-
-int
-_mailbox_pops_init (mu_mailbox_t mbox)
-{
- return _mailbox_pop_and_pops_init (mbox, 1);
-}
-
-/* Cleaning up all the ressources associate with a pop mailbox. */
-static void
-pop_destroy (mu_mailbox_t mbox)
-{
- if (mbox->data)
- {
- pop_data_t mpd = mbox->data;
- size_t i;
- mu_monitor_wrlock (mbox->monitor);
- /* Destroy the pop messages and ressources associated to them. */
- for (i = 0; i < mpd->pmessages_count; i++)
- {
- if (mpd->pmessages[i])
- {
- mu_message_destroy (&(mpd->pmessages[i]->message),
- mpd->pmessages[i]);
- if (mpd->pmessages[i]->uidl)
- free (mpd->pmessages[i]->uidl);
- free (mpd->pmessages[i]);
- mpd->pmessages[i] = NULL;
- }
- }
- if (mpd->greeting_banner)
- free (mpd->greeting_banner);
- if (mpd->buffer)
- free (mpd->buffer);
- if (mpd->pmessages)
- free (mpd->pmessages);
- free (mpd);
- mbox->data = NULL;
- mu_monitor_unlock (mbox->monitor);
- }
-}
-
-static int
-pop_mbox_uidls (mu_mailbox_t mbox, mu_list_t list)
-{
- pop_data_t mpd = mbox->data;
- int status;
-
- status = pop_writeline (mpd, "UIDL\r\n");
- CHECK_ERROR (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
-
- status = pop_write (mpd);
- CHECK_EAGAIN (mpd, status);
-
- status = pop_read_ack (mpd);
- CHECK_EAGAIN (mpd, status);
- MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer);
-
- if (!mu_c_strncasecmp (mpd->buffer, "+OK", 3))
- {
- do
- {
- char *p;
- size_t num;
- struct mu_uidl *uidl;
-
- status = pop_read_ack (mpd);
- MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer);
-
- num = strtoul (mpd->buffer, &p, 10);
- if (*p == 0 || !mu_isblank (*p))
- continue; /* FIXME: or error? */
- p = mu_str_skip_class (p, MU_CTYPE_SPACE);
- mu_rtrim_cset (p, "\r\n");
-
- uidl = malloc (sizeof (uidl[0]));
- if (!uidl)
- {
- status = ENOMEM;
- break;
- }
- uidl->msgno = num;
- strncpy (uidl->uidl, p, MU_UIDL_BUFFER_SIZE);
- status = mu_list_append (list, uidl);
- }
- while (mpd->nl);
- }
- else
- status = ENOSYS;
- return status;
-}
-
-/*
- POP3 CAPA support.
- */
-
-static int
-pop_parse_capa (pop_data_t mpd)
-{
- int status;
- if (!mu_c_strncasecmp (mpd->buffer, "+OK", 3))
- {
- mpd->capa = 0;
- do
- {
- status = pop_read_ack (mpd);
- MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer);
-
- /* Here we check some common capabilities like TOP, USER, UIDL,
- and STLS. The rest are ignored. Please note that some
- capabilities might have an extra arguments. For instance,
- SASL can have CRAM-MD5 and/or KERBEROS_V4, and etc.
- This is why I suggest adding (in a future) an extra variable,
- for example `capa_sasl'. It would hold the following flags:
- SASL_CRAM_MD5, SASL_KERBEROS_V4, and so on. Also the EXPIRE
- and LOGIN-DELAY capabilities have an extra arguments!
- Note that there is no APOP capability, even though APOP
- is an optional command in POP3. -- W.P. */
-
- if (!mu_c_strncasecmp (mpd->buffer, "TOP", 3))
- mpd->capa |= CAPA_TOP;
- else if (!mu_c_strncasecmp (mpd->buffer, "USER", 4))
- mpd->capa |= CAPA_USER;
- else if (!mu_c_strncasecmp (mpd->buffer, "UIDL", 4))
- mpd->capa |= CAPA_UIDL;
- else if (!mu_c_strncasecmp (mpd->buffer, "STLS", 4))
- mpd->capa |= CAPA_STLS;
- }
- while (mpd->nl);
-
- if (mpd->capa & CAPA_UIDL)
- mpd->mbox->_get_uidls = pop_mbox_uidls;
- return status;
- }
- else
- {
- /* mu_error ("CAPA not implemented"); */ /* FIXME */
- return ENOSYS;
- }
-}
-
-static int
-pop_capa (mu_mailbox_t mbox)
-{
- pop_data_t mpd = mbox->data;
- int status;
-
- status = pop_writeline (mpd, "CAPA\r\n");
- CHECK_ERROR (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
-
- status = pop_write (mpd);
- CHECK_EAGAIN (mpd, status);
- mpd->state = POP_CAPA_ACK;
-
- /* POP_CAPA_ACK */
- status = pop_read_ack (mpd);
- CHECK_EAGAIN (mpd, status);
- MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer);
-
- return pop_parse_capa (mpd);
-}
-
-/* Simple User/pass authentication for pop. We ask for the info
- from the standard input. */
-int
-_pop_user (mu_authority_t auth)
-{
- mu_folder_t folder = mu_authority_get_owner (auth);
- mu_mailbox_t mbox = folder->data;
- pop_data_t mpd = mbox->data;
- int status;
-
- switch (mpd->state)
- {
- case POP_AUTH:
- /* Fetch the user from them. */
- status = pop_get_user (auth);
- if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
- {
- pop_writeline (mpd, "QUIT\r\n");
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- pop_write (mpd);
- CHECK_ERROR_CLOSE (mbox, mpd, MU_ERR_NOUSERNAME);
- }
- status = pop_writeline (mpd, "USER %s\r\n", mpd->user);
- CHECK_ERROR_CLOSE(mbox, mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- free (mpd->user);
- mpd->user = NULL;
- mpd->state = POP_AUTH_USER;
-
- case POP_AUTH_USER:
- /* Send username. */
- status = pop_write (mpd);
- CHECK_EAGAIN (mpd, status);
- mpd->state = POP_AUTH_USER_ACK;
-
- case POP_AUTH_USER_ACK:
- /* Get the user ack. */
- status = pop_read_ack (mpd);
- CHECK_EAGAIN (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0)
- {
- mu_observable_t observable = NULL;
- mu_mailbox_get_observable (mbox, &observable);
- CLEAR_STATE (mpd);
- mu_observable_notify (observable, MU_EVT_AUTHORITY_FAILED, NULL);
- CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
- }
- status = pop_get_passwd (auth);
- if (status != 0 || mpd->secret == NULL)
- {
- pop_writeline (mpd, "QUIT\r\n");
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- pop_write (mpd);
- CHECK_ERROR_CLOSE (mbox, mpd, MU_ERR_NOPASSWORD);
- }
- status = pop_writeline (mpd, "PASS %s\r\n",
- mu_secret_password (mpd->secret));
- mu_secret_password_unref (mpd->secret);
- mu_secret_unref (mpd->secret);
- mpd->secret = NULL;
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, "PASS ***\n");
- CHECK_ERROR_CLOSE (mbox, mpd, status);
- mpd->state = POP_AUTH_PASS;
- /* FIXME: Merge these two cases */
-
- case POP_AUTH_PASS:
- /* Send passwd. */
- status = pop_write (mpd);
- CHECK_EAGAIN (mpd, status);
- /* Clear the buffer it contains the passwd. */
- memset (mpd->buffer, '\0', mpd->buflen);
- mpd->state = POP_AUTH_PASS_ACK;
-
- case POP_AUTH_PASS_ACK:
- /* Get the ack from passwd. */
- status = pop_read_ack (mpd);
- CHECK_EAGAIN (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0)
- {
- mu_observable_t observable = NULL;
- mu_mailbox_get_observable (mbox, &observable);
- CLEAR_STATE (mpd);
- mu_observable_notify (observable, MU_EVT_AUTHORITY_FAILED, NULL);
- return MU_ERR_AUTH_FAILURE;
- }
- mpd->state = POP_AUTH_DONE;
- break; /* We're outta here. */
-
- default:
- break;
- }
- CLEAR_STATE (mpd);
- return 0;
-}
-
-int
-_pop_apop (mu_authority_t auth)
-{
- mu_folder_t folder = mu_authority_get_owner (auth);
- mu_mailbox_t mbox = folder->data;
- pop_data_t mpd = mbox->data;
- int status;
-
- switch (mpd->state)
- {
- case POP_AUTH:
- /* Fetch the user from them. */
- status = pop_get_user (auth);
- if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
- {
- CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
- }
-
- /* Fetch the secret from them. */
- status = pop_get_passwd (auth);
- if (status != 0 || mpd->secret == NULL)
- {
- CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
- }
-
- /* Make the MD5 digest string. */
- status = pop_get_md5 (mpd);
- if (status != 0)
- {
- CHECK_ERROR_CLOSE (mbox, mpd, status);
- }
- status = pop_writeline (mpd, "APOP %s %s\r\n", mpd->user, mpd->digest);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- /* We have to obscure the md5 string. */
- memset (mpd->digest, '\0', strlen (mpd->digest));
- free (mpd->user);
- free (mpd->digest);
- mpd->user = NULL;
- mpd->digest = NULL;
- CHECK_ERROR_CLOSE (mbox, mpd, status);
- mpd->state = POP_APOP;
-
- case POP_APOP:
- /* Send apop. */
- status = pop_write (mpd);
- CHECK_EAGAIN (mpd, status);
- /* Clear the buffer it contains the md5. */
- memset (mpd->buffer, '\0', mpd->buflen);
- mpd->state = POP_APOP_ACK;
-
- case POP_APOP_ACK:
- status = pop_read_ack (mpd);
- CHECK_EAGAIN (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0)
- {
- mu_observable_t observable = NULL;
- mu_mailbox_get_observable (mbox, &observable);
- CLEAR_STATE (mpd);
- mu_observable_notify (observable, MU_EVT_AUTHORITY_FAILED, NULL);
- CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
- }
- mpd->state = POP_AUTH_DONE;
- break; /* We're outta here. */
-
- default:
- break;
- }
- CLEAR_STATE (mpd);
- return 0;
-}
-
-/*
- Client side STLS support.
- */
-
-static int
-pop_reader (void *iodata)
+struct _pop3_mailbox
{
- int status = 0;
- pop_data_t iop = iodata;
- status = pop_read_ack (iop);
- CHECK_EAGAIN (iop, status);
- MU_DEBUG (iop->mbox->debug, MU_DEBUG_PROT, iop->buffer);
- return status;/*mu_c_strncasecmp (iop->buffer, "+OK", 3) == 0;*/
-}
-
-static int
-pop_writer (void *iodata, char *buf)
-{
- pop_data_t iop = iodata;
- int status;
+ mu_pop3_t pop3; /* mu_pop3_t is the working horse */
+ int pops; /* true if pop3 over SSL is being used */
+ int is_updated; /* true if the mailbox info is up to date */
- MU_DEBUG1 (iop->mbox->debug, MU_DEBUG_PROT, "%s\n", buf);
- status = pop_writeline (iop, "%s\r\n", buf);
- CHECK_ERROR (iop, status);
- status = pop_write (iop);
- CHECK_ERROR (iop, status);
- return status;
-}
+ size_t msg_count; /* Number of messages in the mailbox */
+ mu_off_t total_size; /* Total mailbox size. */
+ struct _pop3_message **msg; /* Array of messages */
+ size_t msg_max; /* Actual size of the array */
+ mu_mailbox_t mbox; /* MU mailbox corresponding to this one. */
-static void
-pop_stream_ctl (void *iodata, mu_stream_t *pold, mu_stream_t new)
-{
- pop_data_t iop = iodata;
- if (pold)
- *pold = iop->mbox->stream;
- if (new)
- iop->mbox->stream = new;
-}
-
-static int
-pop_stls (mu_mailbox_t mbox)
-{
-#ifdef WITH_TLS
- int status;
- pop_data_t mpd = mbox->data;
- char *keywords[] = { "STLS", "CAPA", NULL };
-
- if (!mu_tls_enable || !(mpd->capa & CAPA_STLS))
- return -1;
-
- status = mu_tls_begin (mpd, pop_reader, pop_writer,
- pop_stream_ctl, keywords);
-
- MU_DEBUG1 (mbox->debug, MU_DEBUG_PROT, "TLS negotiation %s\n",
- status == 0 ? "succeeded" : "failed");
-
- if (status == 0)
- pop_parse_capa (mpd);
-
- return status;
-#else
- return -1;
-#endif /* WITH_TLS */
-}
+ char *user; /* Temporary holders for user and passwd. */
+ mu_secret_t secret;
+};
-/* Open the connection to the sever, and send the authentication. */
static int
pop_open (mu_mailbox_t mbox, int flags)
{
- pop_data_t mpd = mbox->data;
+ struct _pop3_mailbox *mpd = mbox->data;
int status;
const char *host;
long port = mpd->pops ? MU_POPS_PORT : MU_POP_PORT;
-
- /* Sanity checks. */
+ mu_stream_t stream;
+
+ /* Sanity checks. */
if (mpd == NULL)
return EINVAL;
-
+
/* Fetch the pop server name and the port in the mu_url_t. */
status = mu_url_sget_host (mbox->url, &host);
if (status != 0)
@@ -796,519 +112,154 @@ pop_open (mu_mailbox_t mbox, int flags)
mbox->flags = flags;
- /* Do not check for reconnect here. */
- /* CHECK_BUSY (mbox, mpd, func, 0); */
-
- /* Enter the pop state machine, and boogy: AUTHORISATION State. */
- switch (mpd->state)
+ status = mu_tcp_stream_create (&stream, host, port, mbox->flags);
+ if (status)
+ return status;
+#ifdef WITH_TLS
+ if (mpd->pops)
{
- case POP_NO_STATE:
- /* Allocate a working io buffer. */
- if (mpd->buffer == NULL)
+ mu_stream_t newstr;
+
+ status = mu_stream_open (stream);
+ if (status)
{
- /* 255 is the limit lenght of a POP3 command according to RFCs. */
- mpd->buflen = 255;
- mpd->buffer = calloc (mpd->buflen + 1, sizeof (char));
- if (mpd->buffer == NULL)
- {
- CHECK_ERROR (mpd, ENOMEM);
- }
+ mu_stream_destroy (&stream);
+ return status;
}
- else
+
+ status = mu_tls_client_stream_create (&newstr, stream, stream, 0);
+ mu_stream_unref (stream);
+ if (status)
{
- /* Clear any residual from a previous connection. */
- memset (mpd->buffer, '\0', mpd->buflen);
+ mu_error ("pop_open: mu_tls_client_stream_create: %s",
+ mu_strerror (status));
+ return status;
}
- mpd->ptr = mpd->buffer;
-
- /* Create the networking stack. */
- if (mbox->stream == NULL)
- {
- status = mu_tcp_stream_create (&mbox->stream, host, port,
- mbox->flags);
- CHECK_ERROR (mpd, status);
- /* FIXME: How to configure the buffer size? */
- mu_stream_set_buffer (mbox->stream, mu_buffer_line, 1024);
-
-#ifdef WITH_TLS
- if (mpd->pops)
- {
- mu_stream_t newstr;
-
- status = mu_stream_open (mbox->stream);
- CHECK_EAGAIN (mpd, status);
- CHECK_ERROR_CLOSE (mbox, mpd, status);
-
- status = mu_tls_client_stream_create (&newstr,
- mbox->stream,
- mbox->stream, 0);
- if (status != 0)
- {
- mu_error ("pop_open: mu_tls_client_stream_create: %s",
- mu_strerror (status));
- return status;
- }
- mbox->stream = newstr;
- }
+ stream = newstr;
+ }
#endif /* WITH_TLS */
- }
- else
- {
- /* This is sudden death: for many pop servers, it is important to
- let them time to remove locks or move the .user.pop files. This
- happen when we do BUSY_CHECK(). For example, the user does not
- want to read the entire file, and wants start to read a new
- message, closing the connection and immediately contact the
- server again, and we'll end up having "-ERR Mail Lock busy" or
- something similar. To prevent this race condition we sleep 2
- seconds. */
- mu_stream_close (mbox->stream);
- pop_sleep (2);
- }
- mpd->state = POP_OPEN_CONNECTION;
-
- case POP_OPEN_CONNECTION:
- /* Establish the connection. */
- MU_DEBUG2 (mbox->debug, MU_DEBUG_PROT, "open (%s:%ld)\n", host, port);
- status = mu_stream_open (mbox->stream);
- CHECK_EAGAIN (mpd, status);
- /* Can't recover bailout. */
- CHECK_ERROR_CLOSE (mbox, mpd, status);
- mpd->state = POP_GREETINGS;
-
- case POP_GREETINGS:
- {
- int gblen = 0;
- status = pop_read_ack (mpd);
- CHECK_EAGAIN (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0)
- {
- CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
- }
- gblen = strlen (mpd->buffer);
- mpd->greeting_banner = calloc (gblen, 1);
- if (mpd->greeting_banner == NULL)
- {
- CHECK_ERROR (mpd, ENOMEM);
- }
- memcpy (mpd->greeting_banner, mpd->buffer, gblen);
- mpd->state = POP_CAPA;
- }
-
- case POP_CAPA:
- case POP_CAPA_ACK:
- pop_capa (mbox);
- mpd->state = POP_STLS;
-
- case POP_STLS:
- case POP_STLS_ACK:
- if (!mpd->pops)
- pop_stls (mbox);
- mpd->state = POP_AUTH;
-
- case POP_AUTH:
- case POP_AUTH_USER:
- case POP_AUTH_USER_ACK:
- case POP_AUTH_PASS:
- case POP_AUTH_PASS_ACK:
- case POP_APOP:
- case POP_APOP_ACK:
- /* Authenticate. */
- status = mu_authority_authenticate (mbox->folder->authority);
- CHECK_EAGAIN (mpd, status);
-
- case POP_AUTH_DONE:
- break;
- default:
- /*
- mu_error ("pop_open: unknown state");
- */
- break;
- }/* End AUTHORISATION state. */
+ /* FIXME: How to configure buffer size? */
+ mu_stream_set_buffer (stream, mu_buffer_line, 1024);
- /* Clear any state. */
- CLEAR_STATE (mpd);
- return 0;
-}
-
-/* Send the QUIT and close the socket. */
-static int
-pop_close (mu_mailbox_t mbox)
-{
- pop_data_t mpd = mbox->data;
- void *func = (void *)pop_close;
- int status;
- size_t i;
-
- if (mpd == NULL)
- return EINVAL;
-
- /* Should not check for Busy, we're shuting down anyway. */
- /* CHECK_BUSY (mbox, mpd, func, 0); */
- mu_monitor_wrlock (mbox->monitor);
- if (mpd->func && mpd->func != func)
- mpd->state = POP_NO_STATE;
- mpd->id = 0;
- mpd->func = func;
- mu_monitor_unlock (mbox->monitor);
-
- /* Ok boys, it's a wrap: UPDATE State. */
- switch (mpd->state)
- {
- case POP_NO_STATE:
- /* Initiate the close. */
- status = pop_writeline (mpd, "QUIT\r\n");
- CHECK_ERROR (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- mpd->state = POP_QUIT;
-
- case POP_QUIT:
- /* Send the quit. */
- status = pop_write (mpd);
- CHECK_EAGAIN (mpd, status);
- mpd->state = POP_QUIT_ACK;
-
- case POP_QUIT_ACK:
- /* Glob the acknowledge. */
- status = pop_read_ack (mpd);
- CHECK_EAGAIN (mpd, status);
- MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer);
- /* Now what ! and how can we tell them about errors ? So far now
- lets just be verbose about the error but close the connection
- anyway. */
- if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0)
- mu_error ("pop_close: %s", mpd->buffer);
- mu_stream_close (mbox->stream);
- break;
-
- default:
- /*
- mu_error ("pop_close: unknown state");
- */
- break;
- } /* UPDATE state. */
-
- /* Free the messages. */
- for (i = 0; i < mpd->pmessages_count; i++)
+ status = mu_pop3_create (&mpd->pop3);
+ if (status)
{
- if (mpd->pmessages[i])
- {
- mu_message_destroy (&(mpd->pmessages[i]->message),
- mpd->pmessages[i]);
- if (mpd->pmessages[i]->uidl)
- free (mpd->pmessages[i]->uidl);
- free (mpd->pmessages[i]);
- mpd->pmessages[i] = NULL;
- }
+ mu_stream_destroy (&stream);
+ return status;
}
- /* And clear any residue. */
- if (mpd->greeting_banner)
- free (mpd->greeting_banner);
- mpd->greeting_banner = NULL;
- if (mpd->pmessages)
- free (mpd->pmessages);
- mpd->pmessages = NULL;
- mpd->pmessages_count = 0;
- mpd->is_updated = 0;
- if (mpd->buffer)
- free (mpd->buffer);
- mpd->buffer = NULL;
-
- CLEAR_STATE (mpd);
- return 0;
-}
-
-/* Only build/setup the mu_message_t structure for a mesgno. pop_message_t,
- will act as the owner of messages. */
-static int
-pop_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *pmsg)
-{
- pop_data_t mpd = mbox->data;
- mu_message_t msg = NULL;
- pop_message_t mpm;
- int status;
- size_t i;
+ mu_pop3_set_carrier (mpd->pop3, stream);
- /* Sanity. */
- if (pmsg == NULL || mpd == NULL)
- return EINVAL;
+ if (mu_debug_check_level (mbox->debug, MU_DEBUG_PROT))
+ mu_pop3_trace (mpd->pop3, MU_POP3_TRACE_SET);
- /* If we did not start a scanning yet do it now. */
- if (!pop_is_updated (mbox))
- pop_scan (mbox, 1, NULL);
+ do
+ {
+ status = mu_pop3_connect (mpd->pop3);
+ if (status)
+ break;
- if (msgno > mpd->messages_count)
- return EINVAL;
+ status = mu_pop3_capa (mpd->pop3, 1, NULL);
+ if (status)
+ break;
- mu_monitor_rdlock (mbox->monitor);
- /* See if we have already this message. */
- for (i = 0; i < mpd->pmessages_count; i++)
- {
- if (mpd->pmessages[i])
+ if (WITH_TLS && !mpd->pops &&
+ mu_pop3_capa_test (mpd->pop3, "STLS", NULL) == 0)
{
- if (mpd->pmessages[i]->num == msgno)
- {
- *pmsg = mpd->pmessages[i]->message;
- mu_monitor_unlock (mbox->monitor);
- return 0;
- }
+ status = mu_pop3_stls (mpd->pop3);
+ if (status)
+ break;
}
- }
- mu_monitor_unlock (mbox->monitor);
- mpm = calloc (1, sizeof (*mpm));
- if (mpm == NULL)
- return ENOMEM;
-
- /* Back pointer. */
- mpm->mpd = mpd;
- mpm->num = msgno;
-
- /* Create the message. */
- {
- mu_stream_t stream = NULL;
- if ((status = mu_message_create (&msg, mpm)) != 0
- || (status = mu_stream_create (&stream, mbox->flags, msg)) != 0)
- {
- mu_stream_destroy (&stream, msg);
- mu_message_destroy (&msg, mpm);
- free (mpm);
- return status;
- }
- /* Help for the readline()s */
- mu_stream_setbufsiz (stream, 128);
- mu_stream_set_read (stream, pop_message_read, msg);
- mu_stream_set_get_transport2 (stream, pop_message_transport, msg);
- mu_message_set_stream (msg, stream, mpm);
- mu_message_set_size