Commit 46e3b517 46e3b517d99ae4c24c000d893095168017129eb5 by Sergey Poznyakoff

Re-implement pop3 client functions.

* include/mailutils/filter.h (mu_filter_io) <eof>: New member.
* mailbox/fltstream.c (init_iobuf): Initialize eof to 0.
(filter_read): Break the loop if the decoder has set eof.
* mailbox/xscript-stream.c (_xscript_ctl) <MU_IOCTL_SWAP_STREAM>: Handle
a special case when the transport does not support stream swapping.

* include/mailutils/pop3.h (mu_pop3_set_debug): Remove.
(MU_POP3_TRACE_CLR, MU_POP3_TRACE_SET, MU_POP3_TRACE_QRY): New macros.
(mu_pop3_trace): New proto.
(mu_pop3_readline): Remove.
(mu_pop3_getline): New proto.
(mu_pop3_capa): Change signature.
* include/mailutils/sys/pop3.h (mu_pop3_state): Remove the *_ACK states.
(mu_pop3_work_buf): Remove.
(MU_POP3_ACK, MU_POP3_TRACE): New defines.
(_mu_pop3): Rewrite the structure.
(mu_pop3_debug_cmd, mu_pop3_debug_ack): Remove functions.
(MU_POP3_FISSET, MU_POP3_FSET, MU_POP3_FCLR): New macros.
(_mu_pop3_trace_enable, _mu_pop3_trace_disable)
(_mu_pop3_init): New protos.

* include/mailutils/tls.h (mu_tls_stream_ctl_fn)
(mu_tls_writeline_fn): Change typedefs.
* libmu_auth/tls.c (mu_tls_begin): Update function calls
accordingly.

* libproto/pop/pop3_debug.c: Remove.
* libproto/pop/pop3_trace.c: New function.
* libproto/pop/Makefile.am (libmu_pop_la_SOURCES): Temporarly
comment out folder.c, mbox.c and url.c.
Remove pop3_debug.c.
Add pop3_trace.c.
* libproto/pop/pop3_capa.c: Rewrite.
* libproto/pop/pop3_create.c: Rewrite.
* libproto/pop/pop3_iterator.c: Rewrite.
* libproto/pop/pop3_response.c: Rewrite.
* libproto/pop/pop3_sendline.c: Rewrite.
* libproto/pop/pop3_stls.c: Rewrite.
* libproto/pop/pop3_stream.c: Rewrite.
* libproto/pop/pop3_apop.c: Reflect changes to the pop3 framework.
* libproto/pop/pop3_carrier.c: Likewise.
* libproto/pop/pop3_connect.c: Likewise.
* libproto/pop/pop3_dele.c: Likewise.
* libproto/pop/pop3_destroy.c: Likewise.
* libproto/pop/pop3_disconnect.c: Likewise.
* libproto/pop/pop3_list.c: Likewise.
* libproto/pop/pop3_lista.c: Likewise.
* libproto/pop/pop3_noop.c: Likewise.
* libproto/pop/pop3_pass.c: Likewise.
* libproto/pop/pop3_quit.c: Likewise.
* libproto/pop/pop3_retr.c: Likewise.
* libproto/pop/pop3_readline.c: Likewise.
* libproto/pop/pop3_rset.c: Likewise.
* libproto/pop/pop3_stat.c: Likewise.
* libproto/pop/pop3_top.c: Likewise.
* libproto/pop/pop3_uidl.c: Likewise.
* libproto/pop/pop3_uidla.c: Likewise.
* libproto/pop/pop3_user.c: Likewise.

* examples/pop3client.c: Implement the stls comand.
(main) [WITH_TLS]: Call mu_init_tls_libs.
(com_verbose): Redo verbose support.
(com_capa): Implement "reread" option.
1 parent c97b96ae
......@@ -48,6 +48,7 @@
#include <mailutils/argcv.h>
#include <mailutils/cctype.h>
#include <mailutils/cstr.h>
#include <mailutils/tls.h>
/* A structure which contains information on the commands this program
can understand. */
......@@ -80,6 +81,7 @@ int com_uidl (char *);
int com_user (char *);
int com_verbose (char *);
int com_prompt (char *);
int com_stls (char *);
void initialize_readline (void);
COMMAND *find_command (char *);
......@@ -106,6 +108,7 @@ COMMAND commands[] = {
{ "retr", com_retr, "Dowload message: RETR msgno" },
{ "rset", com_rset, "Unmark all messages: RSET" },
{ "stat", com_stat, "Get the size and count of mailbox : STAT [msgno]" },
{ "stls", com_stls, "Start TLS negotiation" },
{ "top", com_top, "Get the header of message: TOP msgno [lines]" },
{ "uidl", com_uidl, "Get the unique id of message: UIDL [msgno]" },
{ "user", com_user, "send login: USER user" },
......@@ -310,7 +313,9 @@ main (int argc MU_ARG_UNUSED, char **argv)
mu_set_program_name (argv[0]);
prompt = strdup (DEFAULT_PROMPT);
initialize_readline (); /* Bind our completer. */
#ifdef WITH_TLS
mu_init_tls_libs ();
#endif
/* Loop reading and executing lines until the user quits. */
while (!done)
{
......@@ -393,16 +398,9 @@ com_verbose (char *arg)
if (pop3 != NULL)
{
if (verbose == 1)
{
mu_debug_t debug;
mu_debug_create (&debug, NULL);
mu_debug_set_level (debug, MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT));
status = mu_pop3_set_debug (pop3, debug);
}
mu_pop3_trace (pop3, MU_POP3_TRACE_SET);
else
{
status = mu_pop3_set_debug (pop3, NULL);
}
mu_pop3_trace (pop3, MU_POP3_TRACE_CLR);
}
return status;
}
......@@ -442,10 +440,24 @@ com_apop (char *arg)
}
int
com_capa (char *arg MU_ARG_UNUSED)
com_capa (char *arg)
{
mu_iterator_t iterator = NULL;
int status = mu_pop3_capa (pop3, &iterator);
int status;
int reread = 0;
if (arg && *arg)
{
if (strcmp (arg, "reread") == 0)
reread = 1;
else
{
mu_error ("%s: unknown argument", "capa");
return 0;
}
}
status = mu_pop3_capa (pop3, reread, &iterator);
if (status == 0)
{
......@@ -609,6 +621,12 @@ com_stat (char *arg MU_ARG_UNUSED)
}
int
com_stls (char *arg MU_ARG_UNUSED)
{
return mu_pop3_stls (pop3);
}
int
com_dele (char *arg)
{
unsigned msgno;
......@@ -799,8 +817,7 @@ com_connect (char *arg)
if (verbose)
com_verbose ("on");
status =
mu_tcp_stream_create (&tcp, argv[0], n, MU_STREAM_READ);
status = mu_tcp_stream_create (&tcp, argv[0], n, MU_STREAM_READ);
if (status == 0)
{
mu_pop3_set_carrier (pop3, tcp);
......
......@@ -42,6 +42,7 @@ struct mu_filter_io
char *output;
size_t osize;
int errcode;
int eof;
};
enum mu_filter_command
......@@ -57,7 +58,7 @@ enum mu_filter_result
mu_filter_ok,
mu_filter_falure,
mu_filter_moreinput,
mu_filter_moreoutput,
mu_filter_moreoutput
};
typedef int (*mu_filter_new_data_t) (void **, int, void *);
......
......@@ -29,7 +29,7 @@ extern "C" {
#endif
struct _mu_pop3;
typedef struct _mu_pop3* mu_pop3_t;
typedef struct _mu_pop3 *mu_pop3_t;
#define MU_POP3_DEFAULT_PORT 110
......@@ -45,23 +45,28 @@ extern int mu_pop3_disconnect (mu_pop3_t pop3);
extern int mu_pop3_set_timeout (mu_pop3_t pop3, int timeout);
extern int mu_pop3_get_timeout (mu_pop3_t pop3, int *timeout);
extern int mu_pop3_set_debug (mu_pop3_t pop3, mu_debug_t debug);
#define MU_POP3_TRACE_CLR 0
#define MU_POP3_TRACE_SET 1
#define MU_POP3_TRACE_QRY 2
extern int mu_pop3_trace (mu_pop3_t pop3, int op);
extern int mu_pop3_apop (mu_pop3_t pop3, const char *name, const char *digest);
extern int mu_pop3_stls (mu_pop3_t pop3);
/* It is the responsability of the caller to call mu_iterator_destroy() when done
with the iterator. The items return by the iterator are of type "const char *",
no processing is done on the item except the removal of the trailing newline. */
extern int mu_pop3_capa (mu_pop3_t pop3, mu_iterator_t *iterator);
/* It is the responsability of the caller to call mu_iterator_destroy() when
done with the iterator. The items returned by the iterator are of type
"const char *", no processing is done on the item except the removal of
the trailing newline. */
extern int mu_pop3_capa (mu_pop3_t pop3, int reread,
mu_iterator_t *piter);
extern int mu_pop3_dele (mu_pop3_t pop3, unsigned int mesgno);
extern int mu_pop3_list (mu_pop3_t pop3, unsigned int mesgno, size_t *mesg_octet);
/* An iterator is return with the multi-line answer. It is the responsability of
the caller to call mu_iterator_destroy() to dispose of the iterator. */
/* An iterator is return with the multi-line answer. It is the responsability
of the caller to call mu_iterator_destroy() to dispose of the iterator. */
extern int mu_pop3_list_all (mu_pop3_t pop3, mu_iterator_t *piterator);
extern int mu_pop3_noop (mu_pop3_t pop3);
......@@ -70,49 +75,44 @@ extern int mu_pop3_pass (mu_pop3_t pop3, const char *pass);
extern int mu_pop3_quit (mu_pop3_t pop3);
/* A stream is return with the multi-line answer. It is the responsability of
the caller to call mu_stream_destroy() to dipose of the stream. */
extern int mu_pop3_retr (mu_pop3_t pop3, unsigned int mesgno, mu_stream_t *pstream);
/* A stream is returned with the multi-line answer. It is the responsability
of the caller to call mu_stream_destroy() to dipose of the stream. */
extern int mu_pop3_retr (mu_pop3_t pop3, unsigned int mesgno,
mu_stream_t *pstream);
extern int mu_pop3_rset (mu_pop3_t pop3);
extern int mu_pop3_stat (mu_pop3_t pop3, unsigned int *count, size_t *octets);
/* A stream is return with the multi-line answer. It is the responsability of
the caller to call mu_stream_destroy() to dipose of the stream. */
extern int mu_pop3_top (mu_pop3_t pop3, unsigned int mesgno, unsigned int lines, mu_stream_t *pstream);
/* The uidl is malloc and return in puidl, it is the responsability of caller
to free() the uild when done. */
extern int mu_pop3_uidl (mu_pop3_t pop3, unsigned int mesgno, char **puidl);
/* An iterator is return with the multi-line answer. It is the responsability of
the caller to call mu_iterator_destroy() to dispose of the iterator. */
extern int mu_pop3_stat (mu_pop3_t pop3, unsigned int *count,
size_t *octets);
/* A stream is returned with the multi-line answer. It is the responsability
of the caller to call mu_stream_destroy() to dipose of the stream. */
extern int mu_pop3_top (mu_pop3_t pop3, unsigned int mesgno,
unsigned int lines, mu_stream_t *pstream);
/* The uidl is malloc'ed and returned in puidl; it is the responsability of
the caller to free() the uild when done. */
extern int mu_pop3_uidl (mu_pop3_t pop3, unsigned int mesgno,
char **puidl);
/* An iterator is returned with the multi-line answer. It is the
responsability of the caller to call mu_iterator_destroy() to dispose of
the iterator. */
extern int mu_pop3_uidl_all (mu_pop3_t pop3, mu_iterator_t *piterator);
extern int mu_pop3_user (mu_pop3_t pop3, const char *user);
/* Reads the multi-line response of the server, nread will be 0 when the termination octets
are detected. Clients should not use this function unless they are sending direct command. */
extern int mu_pop3_readline (mu_pop3_t pop3, char *buffer, size_t buflen, size_t *nread);
/* Returns the last command acknowledge. If the server supports RESP-CODE, the message
could be retrieve, but it is up the caller to do the parsing. */
extern int mu_pop3_response (mu_pop3_t pop3, char *buffer, size_t buflen, size_t *nread);
/* Returns the last command acknowledge. If the server supports RESP-CODE,
the message could be retrieved, but it is up to the caller to do the
parsing. */
extern int mu_pop3_response (mu_pop3_t pop3, size_t *nread);
/* pop3_writeline copies the line in the internal buffer, a mu_pop3_send() is
needed to do the actual transmission. */
extern int mu_pop3_writeline (mu_pop3_t pop3, const char *format, ...)
MU_PRINTFLIKE(2,3);
/* mu_pop3_sendline() is equivalent to:
mu_pop3_writeline (pop3, line);
mu_pop3_send (pop3);
*/
extern int mu_pop3_sendline (mu_pop3_t pop3, const char *line);
/* Transmit via the carrier the internal buffer data. */
extern int mu_pop3_send (mu_pop3_t pop3);
extern int mu_pop3_getline (mu_pop3_t pop3);
#ifdef __cplusplus
}
......
......@@ -37,65 +37,63 @@ enum mu_pop3_state
{
MU_POP3_NO_STATE,
MU_POP3_CONNECT, MU_POP3_GREETINGS,
MU_POP3_APOP, MU_POP3_APOP_ACK,
MU_POP3_AUTH, MU_POP3_AUTH_ACK,
MU_POP3_CAPA, MU_POP3_CAPA_ACK, MU_POP3_CAPA_RX,
MU_POP3_DELE, MU_POP3_DELE_ACK,
MU_POP3_LIST, MU_POP3_LIST_ACK, MU_POP3_LIST_RX,
MU_POP3_NOOP, MU_POP3_NOOP_ACK,
MU_POP3_PASS, MU_POP3_PASS_ACK,
MU_POP3_QUIT, MU_POP3_QUIT_ACK,
MU_POP3_RETR, MU_POP3_RETR_ACK, MU_POP3_RETR_RX,
MU_POP3_RSET, MU_POP3_RSET_ACK,
MU_POP3_STAT, MU_POP3_STAT_ACK,
MU_POP3_STLS, MU_POP3_STLS_ACK, MU_POP3_STLS_CONNECT,
MU_POP3_TOP, MU_POP3_TOP_ACK, MU_POP3_TOP_RX,
MU_POP3_UIDL, MU_POP3_UIDL_ACK, MU_POP3_UIDL_RX,
MU_POP3_USER, MU_POP3_USER_ACK,
MU_POP3_DONE, MU_POP3_UNKNOWN, MU_POP3_ERROR
MU_POP3_APOP,
MU_POP3_AUTH,
MU_POP3_CAPA, MU_POP3_CAPA_RX,
MU_POP3_DELE,
MU_POP3_LIST, MU_POP3_LIST_RX,
MU_POP3_NOOP,
MU_POP3_PASS,
MU_POP3_QUIT,
MU_POP3_RETR, MU_POP3_RETR_RX,
MU_POP3_RSET,
MU_POP3_STAT,
MU_POP3_STLS, MU_POP3_STLS_CONNECT,
MU_POP3_TOP, MU_POP3_TOP_RX,
MU_POP3_UIDL, MU_POP3_UIDL_RX,
MU_POP3_USER,
MU_POP3_DONE,
MU_POP3_UNKNOWN,
MU_POP3_ERROR
};
/* Structure holding the data necessary to do proper buffering. */
struct mu_pop3_work_buf
{
char *buf;
char *ptr;
char *nl;
size_t len;
};
#define MU_POP3_ACK 0x01
#define MU_POP3_TRACE 0x02
/* Structure to hold things general to POP3 mailbox, like its state, etc ... */
struct _mu_pop3
{
/* Working I/O buffer.
io.buf: Working io buffer
io.ptr: Points to the end of the buffer, the non consumed chars
io.nl: Points to the '\n' char in the string
io.len: Len of io_buf. */
struct mu_pop3_work_buf io;
/* Holds the first line response of the last command, i.e the ACK:
ack.buf: Buffer for the ack
ack.ptr: Working pointer, indicate the start of the non consumed chars
ack.len: Size 512 according to RFC2449. */
struct mu_pop3_work_buf ack;
int acknowledge;
int flags;
char *timestamp; /* For apop, if supported. */
unsigned timeout; /* Default is 10 minutes. */
/* Holds the first line response of the last command, i.e the ACK */
char *ackbuf;
size_t acksize;
mu_debug_t debug; /* debugging trace. */
char *rdbuf;
size_t rdsize;
enum mu_pop3_state state; /* Indicate the state of the running command. */
char *timestamp; /* For apop, if supported. */
unsigned timeout; /* Default is 10 minutes. */
enum mu_pop3_state state; /* Indicate the state of the running
command. */
mu_list_t capa; /* Capabilities. */
mu_stream_t carrier; /* TCP Connection. */
};
extern int mu_pop3_debug_cmd (mu_pop3_t);
extern int mu_pop3_debug_ack (mu_pop3_t);
#define MU_POP3_FSET(p,f) ((p)->flags |= (f))
#define MU_POP3_FISSET(p,f) ((p)->flags & (f))
#define MU_POP3_FCLR(p,f) ((p)->flags &= ~(f))
extern int mu_pop3_iterator_create (mu_pop3_t pop3, mu_iterator_t *piterator);
extern int mu_pop3_stream_create (mu_pop3_t pop3, mu_stream_t *pstream);
extern int mu_pop3_carrier_is_ready (mu_stream_t carrier, int flag, int timeout);
extern int mu_pop3_carrier_is_ready (mu_stream_t carrier, int flag,
int timeout);
int _mu_pop3_trace_enable (mu_pop3_t pop3);
int _mu_pop3_trace_disable (mu_pop3_t pop3);
int _mu_pop3_init (mu_pop3_t pop3);
/* Check for non recoverable error.
The error is consider not recoverable if not part of the signal set:
......@@ -104,48 +102,46 @@ extern int mu_pop3_carrier_is_ready (mu_stream_t carrier, int flag, int timeout
to the begining of the buffer and setting the state to error.
*/
#define MU_POP3_CHECK_EAGAIN(pop3, status) \
do \
do \
{ \
if (status != 0) \
{ \
if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \
{ \
pop3->io.ptr = pop3->io.buf; \
pop3->state = MU_POP3_ERROR; \
} \
return status; \
} \
} \
while (0)
while (0)
/* If error return.
Check status an reset(see MU_POP2_CHECK_EAGAIN) the buffer.
*/
#define MU_POP3_CHECK_ERROR(pop3, status) \
do \
do \
{ \
if (status != 0) \
{ \
pop3->io.ptr = pop3->io.buf; \
pop3->state = MU_POP3_ERROR; \
return status; \
} \
} \
while (0)
while (0)
/* Check if we got "+OK".
In POP3 protocol and ack of "+OK" means the command was successfull.
*/
#define MU_POP3_CHECK_OK(pop3) \
do \
do \
{ \
if (mu_c_strncasecmp (pop3->ack.buf, "+OK", 3) != 0) \
if (mu_c_strncasecmp (pop3->ackbuf, "+OK", 3) != 0) \
{ \
pop3->state = MU_POP3_NO_STATE; \
return EACCES; \
} \
} \
while (0)
while (0)
#ifdef __cplusplus
}
......
......@@ -47,10 +47,13 @@ extern int mu_check_tls_environment (void);
extern int mu_init_tls_libs (void);
extern void mu_deinit_tls_libs (void);
typedef int (*mu_tls_readline_fn) (void *iodata);
typedef int (*mu_tls_readline_fn) (void *iodata, int n);
typedef int (*mu_tls_writeline_fn) (void *iodata, char *buf);
typedef void (*mu_tls_stream_ctl_fn) (void *iodata, mu_stream_t *pold,
mu_stream_t __new);
#define MU_TLS_SESS_GET_STREAMS 0
#define MU_TLS_SESS_SET_STREAMS 1
typedef int (*mu_tls_stream_ctl_fn) (void *iodata, int __op,
mu_stream_t *pstr);
extern int mu_tls_begin (void *iodata, mu_tls_readline_fn reader,
mu_tls_writeline_fn writer,
......
......@@ -115,11 +115,23 @@ mu_check_tls_environment (void)
int mu_tls_enable = 0;
#if 0
void
_mu_gtls_logger(int level, const char *text)
{
mu_diag_output (MU_DIAG_DEBUG, "GnuTLS(%d): %s", level, text);
}
#endif
int
mu_init_tls_libs (void)
{
if (mu_tls_module_config.enable && !mu_tls_enable)
mu_tls_enable = !gnutls_global_init (); /* Returns 1 on success */
#if 0
gnutls_global_set_log_function (_mu_gtls_logger);
gnutls_global_set_log_level (110);
#endif
return mu_tls_enable;
}
......@@ -161,7 +173,7 @@ mu_tls_begin (void *iodata,
{
int i = 0;
int status;
mu_stream_t oldstr, newstr;
mu_stream_t streams[2], newstr;
if (keywords == NULL)
return EINVAL;
......@@ -181,19 +193,23 @@ mu_tls_begin (void *iodata,
return status;
}
status = reader (iodata);
status = reader (iodata, i);
if (status != 0)
{
mu_error ("mu_tls_begin: reader (0): %s", mu_strerror (status));
return status;
}
stream_ctl (iodata, &oldstr, NULL);
status = mu_tls_client_stream_create (&newstr, oldstr, oldstr, 0);
status = stream_ctl (iodata, MU_TLS_SESS_GET_STREAMS, streams);
if (status)
return status;
status = mu_tls_client_stream_create (&newstr,
streams[0], streams[1], 0);
if (status != 0)
{
mu_error ("mu_tls_begin: mu_tls_client_stream_create(0): %s",
mu_strerror (status));
stream_ctl (iodata, MU_TLS_SESS_SET_STREAMS, streams);
return status;
}
......@@ -202,10 +218,13 @@ mu_tls_begin (void *iodata,
{
mu_error ("mu_tls_begin: mu_stream_open (0): %s",
mu_strerror (status));
stream_ctl (iodata, MU_TLS_SESS_SET_STREAMS, streams);
return status;
}
stream_ctl (iodata, NULL, newstr);
streams[0] = streams[1] = newstr;
stream_ctl (iodata, MU_TLS_SESS_SET_STREAMS, streams);
/* FIXME: Unref newstr */
break;
case 1:
......@@ -219,7 +238,7 @@ mu_tls_begin (void *iodata,
return status;
}
status = reader (iodata);
status = reader (iodata, i);
if (status != 0)
{
mu_error ("mu_tls_begin: reader (1): %s", mu_strerror (status));
......@@ -420,6 +439,7 @@ _tls_stream_push (gnutls_transport_ptr fd, const void *buf, size_t size)
mu_error ("_tls_stream_push: %s", mu_strerror (rc)); /* FIXME */
return -1;
}
mu_stream_flush (stream);
return size;
}
......@@ -475,9 +495,11 @@ _tls_server_open (mu_stream_t stream)
}
static int
prepare_client_session (struct _mu_tls_stream *sp)
prepare_client_session (mu_stream_t stream)
{
struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
int rc;
mu_transport_t transport[2];
static int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0};
static int kx_priority[] = {GNUTLS_KX_RSA, 0};
static int cipher_priority[] = {GNUTLS_CIPHER_3DES_CBC,
......@@ -508,9 +530,10 @@ prepare_client_session (struct _mu_tls_stream *sp)
gnutls_credentials_set (sp->session, GNUTLS_CRD_CERTIFICATE, x509_cred);
mu_stream_ioctl (stream, MU_IOCTL_GET_TRANSPORT, transport);
gnutls_transport_set_ptr2 (sp->session,
(gnutls_transport_ptr) sp->transport[0],
(gnutls_transport_ptr) sp->transport[1]);
(gnutls_transport_ptr) transport[0],
(gnutls_transport_ptr) transport[1]);
gnutls_transport_set_pull_function (sp->session, _tls_stream_pull);
gnutls_transport_set_push_function (sp->session, _tls_stream_push);
......@@ -532,7 +555,7 @@ _tls_client_open (mu_stream_t stream)
/* FALLTHROUGH */
case state_init:
prepare_client_session (sp);
prepare_client_session (stream);
rc = gnutls_handshake (sp->session);
if (rc < 0)
{
......
......@@ -24,17 +24,16 @@ lib_LTLIBRARIES = libmu_pop.la
libmu_pop_la_LDFLAGS=-version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@
libmu_pop_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@
# folder.c\
# mbox.c\
# url.c
libmu_pop_la_SOURCES = \
folder.c\
mbox.c\
url.c \
\
pop3_apop.c \
pop3_capa.c \
pop3_carrier.c \
pop3_connect.c \
pop3_create.c \
pop3_debug.c \
pop3_dele.c \
pop3_destroy.c \
pop3_disconnect.c \
......@@ -54,6 +53,7 @@ libmu_pop_la_SOURCES = \
pop3_stream.c \
pop3_timeout.c \
pop3_top.c \
pop3_trace.c \
pop3_uidla.c \
pop3_uidl.c \
pop3_user.c
......
......@@ -39,15 +39,11 @@ mu_pop3_apop (mu_pop3_t pop3, const char *user, const char *secret)
/* Sanity checks. */
if (pop3 == NULL || user == NULL || secret == NULL)
{
return EINVAL;
}
/* The server did not offer a timestamp in the greeting, bailout early. */
if (pop3->timestamp == NULL)
{
return ENOTSUP;
}
switch (pop3->state)
{
......@@ -61,35 +57,25 @@ mu_pop3_apop (mu_pop3_t pop3, const char *user, const char *secret)
size_t n;
mu_md5_init_ctx (&md5context);
mu_md5_process_bytes (pop3->timestamp, strlen (pop3->timestamp), &md5context);
mu_md5_process_bytes (pop3->timestamp, strlen (pop3->timestamp),
&md5context);
mu_md5_process_bytes (secret, strlen (secret), &md5context);
mu_md5_finish_ctx (&md5context, md5digest);
for (tmp = digest, n = 0; n < 16; n++, tmp += 2)
{
sprintf (tmp, "%02x", md5digest[n]);
}
*tmp = '\0';
status = mu_pop3_writeline (pop3, "APOP %s %s\r\n", user, digest);
/* Obscure the digest, for security reasons. */
memset (digest, '\0', sizeof digest);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_APOP;
}
case MU_POP3_APOP:
status = mu_pop3_send (pop3);
MU_POP3_CHECK_EAGAIN (pop3, status);
/* Obscure the digest, for security reasons. */
memset (pop3->io.buf, '\0', pop3->io.len);
pop3->acknowledge = 0;
pop3->state = MU_POP3_APOP_ACK;
case MU_POP3_APOP_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
break;
......
......@@ -27,47 +27,95 @@
#include <stdlib.h>
#include <errno.h>
#include <mailutils/error.h>
#include <mailutils/list.h>
#include <mailutils/cctype.h>
#include <mailutils/cstr.h>
#include <mailutils/sys/pop3.h>
static int
string_comp (const void *item, const void *value)
{
return strcmp (item, value);
}
int
_mu_pop3_fill_list (mu_pop3_t pop3, mu_list_t list)
{
mu_stream_t stream;
size_t n;
int status = mu_pop3_stream_create (pop3, &stream);
if (status)
return status;
while (mu_stream_getline (stream, &pop3->rdbuf, &pop3->rdsize, &n) == 0
&& n > 0)
{
char *np = strdup (pop3->rdbuf);
if (!np)
{
status = ENOMEM;
break;
}
mu_rtrim_class (np, MU_CTYPE_SPACE);
status = mu_list_append (list, np);
if (status)
break;
}
mu_stream_destroy (&stream);
return status;
}
/*
CAPA command, return a list that contains the result.
It is the responsability of the caller to destroy the list(mu_list_destroy).
*/
int
mu_pop3_capa (mu_pop3_t pop3, mu_iterator_t *piterator)
mu_pop3_capa (mu_pop3_t pop3, int reread, mu_iterator_t *piter)
{
int status = 0;
if (pop3 == NULL)
return EINVAL;
if (piterator == NULL)
return MU_ERR_OUT_PTR_NULL;
if (pop3->capa)
{
if (!reread)
{
if (!piter)
return 0;
return mu_list_get_iterator (pop3->capa, piter);
}
mu_list_destroy (&pop3->capa);
}
status = mu_list_create (&pop3->capa);
if (status)
return status;
mu_list_set_comparator (pop3->capa, string_comp);
mu_list_set_destroy_item (pop3->capa, mu_list_free_item);
switch (pop3->state)
{
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "CAPA\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_CAPA;
case MU_POP3_CAPA:
status = mu_pop3_send (pop3);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_CAPA_ACK;
case MU_POP3_CAPA_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
status = mu_pop3_iterator_create (pop3, piterator);
MU_POP3_CHECK_ERROR (pop3, status);
pop3->state = MU_POP3_CAPA_RX;
case MU_POP3_CAPA_RX:
/* The mu_iterator_t will read the stream and set the state to MU_POP3_NO_STATE when done. */
status = _mu_pop3_fill_list (pop3, pop3->capa);
MU_POP3_CHECK_ERROR (pop3, status);
if (piter)
status = mu_list_get_iterator (pop3->capa, piter);
else
status = 0;
pop3->state = MU_POP3_NO_STATE;
break;
/* They must deal with the error first by reopening. */
......
......@@ -38,10 +38,14 @@ mu_pop3_set_carrier (mu_pop3_t pop3, mu_stream_t carrier)
mu_pop3_disconnect (pop3);
mu_stream_destroy (&pop3->carrier);
}
mu_stream_ref (carrier);
pop3->carrier = carrier;
if (MU_POP3_FISSET (pop3, MU_POP3_TRACE))
_mu_pop3_trace_enable (pop3);
return 0;
}
/* FIXME: Is it needed? */
int
mu_pop3_get_carrier (mu_pop3_t pop3, mu_stream_t *pcarrier)
{
......
......@@ -53,14 +53,15 @@ mu_pop3_connect (mu_pop3_t pop3)
case MU_POP3_NO_STATE:
/* If the stream was previoulsy open this is sudden death:
for many pop servers, it is important to let them time to remove any locks or move
the .user.pop files. This happen when we do close() and immediately open().
For example, the user does not want to read the entire file, and wants to start
to read a new message, closing the connection and immediately
contacting the server again, and he'll end up having
"-ERR Mail Lock busy" or something similar. To prevent this race
condition we sleep 2 seconds. You can see this behaviour in an
environment where QPopper(Qualcomm POP3 server) is use and the user as a big mailbox. */
For many pop servers, it is important to allow them some time to
remove any locks or move the .user.pop files. This happen when we
do close() and immediately open(). For example, the user does not
want to read the entire file, and wants to start to read a new
message, closing the connection and immediately contacting the
server again, and he'll end up having "-ERR Mail Lock busy" or
something similar. To prevent this race condition we sleep 2 seconds.
You can see this behaviour in an environment where QPopper (Qualcomm
POP3 server) is used and the user has a big mailbox. */
status = mu_pop3_disconnect (pop3);
if (status != 0)
mu_pop3_sleep (2);
......@@ -70,7 +71,7 @@ mu_pop3_connect (mu_pop3_t pop3)
/* Establish the connection. */
status = mu_stream_open (pop3->carrier);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_GREETINGS;
case MU_POP3_GREETINGS:
......@@ -78,10 +79,9 @@ mu_pop3_connect (mu_pop3_t pop3)
{
size_t len = 0;
char *right, *left;
status = mu_pop3_response (pop3, NULL, 0, &len);
status = mu_pop3_response (pop3, &len);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
if (mu_c_strncasecmp (pop3->ack.buf, "+OK", 3) != 0)
if (mu_c_strncasecmp (pop3->ackbuf, "+OK", 3) != 0)
{
mu_stream_close (pop3->carrier);
pop3->state = MU_POP3_NO_STATE;
......@@ -89,10 +89,10 @@ mu_pop3_connect (mu_pop3_t pop3)
}
/* Get the timestamp. */
right = memchr (pop3->ack.buf, '<', len);
right = memchr (pop3->ackbuf, '<', len);
if (right)
{
len = len - (right - pop3->ack.buf);
len = len - (right - pop3->ackbuf);
left = memchr (right, '>', len);
if (left)
{
......@@ -121,5 +121,5 @@ mu_pop3_sleep (int seconds)
struct timeval tval;
tval.tv_sec = seconds;
tval.tv_usec = 0;
return select (1, NULL, NULL, NULL, &tval);
return select (0, NULL, NULL, NULL, &tval);
}
......
......@@ -40,34 +40,24 @@ mu_pop3_create (mu_pop3_t *ppop3)
if (pop3 == NULL)
return ENOMEM;
/* Reserve space for the ack(nowledgement) response.
According to RFC 2449: The maximum length of the first line of a
command response (including the initial greeting) is unchanged at
512 octets (including the terminating CRLF). */
pop3->ack.len = 512;
pop3->ack.buf = calloc (pop3->ack.len, 1);
if (pop3->ack.buf == NULL)
{
mu_pop3_destroy (&pop3);
return ENOMEM;
}
pop3->ack.ptr = pop3->ack.buf;
/* Reserve space for the data response/content.
RFC 2449 recommands 255, but we grow it as needed. */
pop3->io.len = 255;
pop3->io.buf = calloc (pop3->io.len, 1);
if (pop3->io.buf == NULL)
{
mu_pop3_destroy (&pop3);
return ENOMEM;
}
pop3->io.ptr = pop3->io.buf;
pop3->state = MU_POP3_NO_STATE; /* Init with no state. */
pop3->timeout = (10 * 60) * 100; /* The default Timeout is 10 minutes. */
pop3->acknowledge = 0; /* No Ack received. */
MU_POP3_FCLR (pop3, MU_POP3_ACK); /* No Ack received. */
*ppop3 = pop3;
return 0; /* Okdoke. */
}
int
_mu_pop3_init (mu_pop3_t pop3)
{
if (pop3 == NULL)
return EINVAL;
if (pop3->carrier == 0)
{
mu_list_destroy (&pop3->capa);
pop3->flags = 0;
}
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2003, 2007, 2010 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, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <mailutils/sys/pop3.h>
int
mu_pop3_set_debug (mu_pop3_t pop3, mu_debug_t debug)
{
if (pop3 == NULL)
return EINVAL;
if (pop3->debug)
mu_debug_destroy (&pop3->debug, NULL);
pop3->debug = debug;
return 0;
}
/* FIXME: This should be a macro */
int
mu_pop3_debug_cmd (mu_pop3_t pop3)
{
MU_DEBUG (pop3->debug, MU_DEBUG_PROT, pop3->io.buf);
return 0;
}
int
mu_pop3_debug_ack (mu_pop3_t pop3)
{
MU_DEBUG1 (pop3->debug, MU_DEBUG_PROT, "%s\n", pop3->ack.buf);
return 0;
}
......@@ -38,19 +38,12 @@ mu_pop3_dele (mu_pop3_t pop3, unsigned msgno)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "DELE %d\r\n", msgno);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_DELE;
case MU_POP3_DELE:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_DELE_ACK;
case MU_POP3_DELE_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
break;
......
......@@ -32,17 +32,18 @@ mu_pop3_destroy (mu_pop3_t *ppop3)
mu_pop3_t pop3 = *ppop3;
/* Free the response buffer. */
if (pop3->ack.buf)
free (pop3->ack.buf);
/* Free the io buffer. */
if (pop3->io.buf)
free (pop3->io.buf);
if (pop3->ackbuf)
free (pop3->ackbuf);
/* Free the read buffer. */
if (pop3->rdbuf)
free (pop3->rdbuf);
/* Free the timestamp use for APOP. */
if (pop3->timestamp)
free (pop3->timestamp);
mu_list_destroy (&pop3->capa);
/* Release the carrier. */
if (pop3->carrier)
mu_stream_destroy (&pop3->carrier);
......
......@@ -36,13 +36,10 @@ mu_pop3_disconnect (mu_pop3_t pop3)
/* We can keep some of the fields, if they decide to pop3_connect() again but
clear the states. */
pop3->state = MU_POP3_NO_STATE;
pop3->acknowledge = 0;
MU_POP3_FCLR (pop3, MU_POP3_ACK);
/* Clear the buffers. */
memset (pop3->io.buf, '\0', pop3->io.len);
pop3->io.ptr = pop3->io.buf;
memset (pop3->ack.buf, '\0', pop3->ack.len);
pop3->ack.ptr = pop3->ack.buf;
if (pop3->rdbuf)
pop3->rdbuf[0] = 0;
/* Free the timestamp, it will be different on each connection. */
if (pop3->timestamp)
......
......@@ -25,7 +25,10 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <mailutils/pop3.h>
#include <mailutils/sys/pop3.h>
#include <mailutils/cctype.h>
#include <mailutils/cstr.h>
static int pop3_itr_dup (void **ptr, void *owner);
static int pop3_itr_destroy (mu_iterator_t itr, void *owner);
......@@ -38,8 +41,11 @@ static int pop3_itr_finished_p (void *owner);
struct pop3_iterator
{
mu_pop3_t pop3;
mu_stream_t stream;
int done;
char *item;
char *rdbuf;
size_t rdsize;
};
int
......@@ -53,9 +59,17 @@ mu_pop3_iterator_create (mu_pop3_t pop3, mu_iterator_t *piterator)
if (pop3_iterator == NULL)
return ENOMEM;
status = mu_pop3_stream_create (pop3, &pop3_iterator->stream);
if (status)
{
free (pop3_iterator);
return status;
}
pop3_iterator->item = NULL;
pop3_iterator->rdbuf = NULL;
pop3_iterator->rdsize = 0;
pop3_iterator->done = 0;
pop3_iterator->pop3= pop3;
pop3_iterator->pop3 = pop3;
status = mu_iterator_create (&iterator, pop3_iterator);
if (status != 0)
......@@ -93,17 +107,21 @@ pop3_itr_destroy (mu_iterator_t iterator, void *owner)
{
struct pop3_iterator *pop3_iterator = (struct pop3_iterator *)owner;
/* Delicate situation if they did not finish to drain the result
We take te approach to do it for the user. FIXME: Not sure
We take the approach to do it for the user. FIXME: Not sure
if this is the rigth thing to do. The other way is to close the stream */
if (!pop3_iterator->done)
{
char buf[128];
size_t n = 0;
while (mu_pop3_readline (pop3_iterator->pop3, buf, sizeof buf, &n) > 0 && n > 0)
mu_stream_t str = pop3_iterator->pop3->carrier;
while (mu_stream_readline (str, buf, sizeof buf, &n) > 0
&& n > 0)
n = 0;
}
if (pop3_iterator->item)
free (pop3_iterator->item);
if (pop3_iterator->rdbuf)
free (pop3_iterator->rdbuf);
pop3_iterator->pop3->state = MU_POP3_NO_STATE;
free (pop3_iterator);
return 0;
......@@ -119,40 +137,27 @@ static int
pop3_itr_next (void *owner)
{
struct pop3_iterator *pop3_iterator = (struct pop3_iterator *)owner;
size_t n = 0;
int status = 0;
size_t n;
if (!pop3_iterator->done)
{
/* The first readline will not consume the buffer, we just need to
know how much to read. */
status = mu_pop3_readline (pop3_iterator->pop3, NULL, 0, &n);
if (status == 0)
{
if (n)
{
char *buf;
buf = calloc (n + 1, 1);
if (buf)
status = mu_stream_getline (pop3_iterator->stream, &pop3_iterator->rdbuf,
&pop3_iterator->rdsize, &n);
if (status || n == 0)
{
/* Consume. */
mu_pop3_readline (pop3_iterator->pop3, buf, n + 1, NULL);
if (buf[n - 1] == '\n')
buf[n - 1] = '\0';
if (pop3_iterator->item)
free (pop3_iterator->item);
pop3_iterator->item = buf;
}
else
status = ENOMEM;
pop3_iterator->done = 1;
pop3_iterator->pop3->state = MU_POP3_NO_STATE;
return 0;
}
else
n = mu_rtrim_class (pop3_iterator->rdbuf, MU_CTYPE_SPACE);
if (n == 1 && pop3_iterator->rdbuf[0] == '.')
{
pop3_iterator->done = 1;
pop3_iterator->pop3->state = MU_POP3_NO_STATE;
}
}
}
else
pop3_iterator->item = pop3_iterator->rdbuf;
return status;
}
......
......@@ -42,25 +42,19 @@ mu_pop3_list (mu_pop3_t pop3, unsigned int msgno, size_t *psize)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "LIST %d\r\n", msgno);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_LIST;
case MU_POP3_LIST:
status = mu_pop3_send (pop3);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_LIST_ACK;
case MU_POP3_LIST_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
mu_pop3_debug_ack (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
/* Parse the answer. */
lv = 0;
sscanf (pop3->ack.buf, "+OK %d %lu", &msgno, &lv);
/* FIXME: Error checking */
sscanf (pop3->ackbuf, "+OK %d %lu", &msgno, &lv);
*psize = lv;
break;
......
......@@ -42,26 +42,20 @@ mu_pop3_list_all (mu_pop3_t pop3, mu_iterator_t *piterator)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "LIST\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_LIST;
case MU_POP3_LIST:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_LIST_ACK;
case MU_POP3_LIST_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
status = mu_pop3_iterator_create (pop3, piterator);
MU_POP3_CHECK_ERROR (pop3, status);
pop3->state = MU_POP3_LIST_RX;
case MU_POP3_LIST_RX:
/* The mu_iterator_t will read the stream and set the state to MU_POP3_NO_STATE when done. */
/* The mu_iterator_t will read the stream and set the state to
MU_POP3_NO_STATE when done. */
break;
/* They must deal with the error first by reopening. */
......
......@@ -38,19 +38,12 @@ mu_pop3_noop (mu_pop3_t pop3)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "NOOP\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_NOOP;
case MU_POP3_NOOP:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_NOOP_ACK;
case MU_POP3_NOOP_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
break;
......
......@@ -37,21 +37,13 @@ mu_pop3_pass (mu_pop3_t pop3, const char *passwd)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "PASS %s\r\n", passwd);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
/* FIXME: how to obscure the passwd in the stream buffer? */
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_PASS;
case MU_POP3_PASS:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
/* Obscure the passwd. */
memset (pop3->io.buf, '\0', pop3->io.len);
pop3->acknowledge = 0;
pop3->state = MU_POP3_PASS_ACK;
case MU_POP3_PASS_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
break;
......
......@@ -38,21 +38,15 @@ mu_pop3_quit (mu_pop3_t pop3)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "QUIT\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_QUIT;
case MU_POP3_QUIT:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_QUIT_ACK;
case MU_POP3_QUIT_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
_mu_pop3_init (pop3);
break;
default:
......
......@@ -29,6 +29,8 @@
#include <errno.h>
#include <mailutils/sys/pop3.h>
#include <mailutils/error.h>
#include <mailutils/cctype.h>
#include <mailutils/cstr.h>
int
mu_pop3_carrier_is_ready (mu_stream_t carrier, int flag, int timeout)
......@@ -52,143 +54,28 @@ mu_pop3_carrier_is_ready (mu_stream_t carrier, int flag, int timeout)
/* Read a complete line from the pop server. Transform CRLF to LF, remove
the stuff byte termination octet ".", put a null in the buffer
when done. And Do a select() (stream_is_readready()) for the timeout. */
static int
/* FIXME: Is it needed? */
int
mu_pop3_getline (mu_pop3_t pop3)
{
size_t n = 0;
size_t total = pop3->io.ptr - pop3->io.buf;
int status = 0;
/* Must get a full line before bailing out. */
do
{
/* Timeout with select(), note that we have to reset select()
since on linux tv is modified when error. */
if (pop3->timeout)
size_t n;
int status = mu_stream_getline (pop3->carrier, &pop3->rdbuf,
&pop3->rdsize, &n);
if (status == 0)
{
int ready = mu_pop3_carrier_is_ready (pop3->carrier,
MU_STREAM_READY_RD,
pop3->timeout);
if (ready == 0)
return ETIMEDOUT;
}
status = mu_stream_readline (pop3->carrier,
pop3->io.buf + total,
pop3->io.len - total, &n);
if (status != 0)
return status;
/* The server went away: It maybe a timeout and some pop server
does not send the -ERR. Consider this like an error. */
if (n == 0)
return EIO;
total += n;
pop3->io.nl = memchr (pop3->io.buf, '\n', total);
if (pop3->io.nl == NULL) /* Do we have a full line. */
{
/* Allocate a bigger buffer ? */
if (total >= pop3->io.len - 1)
{
pop3->io.len *= 2;
pop3->io.buf = realloc (pop3->io.buf, pop3->io.len + 1);
if (pop3->io.buf == NULL)
return ENOMEM;
}
}
pop3->io.ptr = pop3->io.buf + total;
}
while (pop3->io.nl == NULL); /* Bail only if we have a complete line. */
n = mu_rtrim_class (pop3->rdbuf, MU_CTYPE_SPACE);
/* When examining a multi-line response, the client checks to see if the
line begins with the termination octet "."(DOT). If yes and if octets
other than CRLF follow, the first octet of the line (the termination
octet) is stripped away. */
if (total >= 3 && pop3->io.buf[0] == '.')
{
if (pop3->io.buf[1] != '\r' && pop3->io.buf[2] != '\n')
{
memmove (pop3->io.buf, pop3->io.buf + 1, total - 1);
pop3->io.ptr--;
pop3->io.nl--;
}
/* And if CRLF immediately follows the termination character, then
the response from the POP server is ended and the line containing
".CRLF" is not considered part of the multi-line response. */
else if (pop3->io.buf[1] == '\r' && pop3->io.buf[2] == '\n')
{
pop3->io.buf[0] = '\0';
pop3->io.ptr = pop3->io.buf;
pop3->io.nl = NULL;
}
}
/* \r\n --> \n\0, conversion. */
if (pop3->io.nl > pop3->io.buf)
{
*(pop3->io.nl - 1) = '\n';
*(pop3->io.nl) = '\0';
pop3->io.ptr = pop3->io.nl;
if (n >= 2 &&
pop3->rdbuf[0] == '.' &&
pop3->rdbuf[1] != '\n')
memmove (pop3->rdbuf, pop3->rdbuf + 1, n);
}
return status;
}
/* Call pop3_getline() for the dirty work, and consume i.e. put
in the user buffer only buflen. If buflen == 0 or buffer == NULL
nothing is consume, the data is save for another call to pop3_readline()
with a buffer != NULL.
*/
int
mu_pop3_readline (mu_pop3_t pop3, char *buffer, size_t buflen, size_t *pnread)
{
size_t nread = 0;
size_t n = 0;
int status = 0;
/* Do we need to fill up? Yes if no NL or the buffer is empty. */
if (pop3->carrier && (pop3->io.nl == NULL || pop3->io.ptr == pop3->io.buf))
{
status = mu_pop3_getline (pop3);
if (status != 0)
return status;
}
/* How much we can copy ? */
n = pop3->io.ptr - pop3->io.buf;
/* Consume the line? */
if (buffer && buflen)
{
buflen--; /* For the null. */
if (buflen)
{
int nleft = buflen - n;
/* We got more then requested. */
if (nleft < 0)
{
size_t sentinel;
nread = buflen;
sentinel = pop3->io.ptr - (pop3->io.buf + nread);
memcpy (buffer, pop3->io.buf, nread);
memmove (pop3->io.buf, pop3->io.buf + nread, sentinel);
pop3->io.ptr = pop3->io.buf + sentinel;
}
else
{
/* Drain our buffer. */;
nread = n;
memcpy (buffer, pop3->io.buf, nread);
pop3->io.ptr = pop3->io.buf;
/* Clear of all residue. */
memset (pop3->io.buf, '\0', pop3->io.len);
}
}
buffer[nread] = '\0';
}
else
nread = n;
if (pnread)
*pnread = nread;
return status;
}
......
......@@ -20,14 +20,19 @@
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <mailutils/cctype.h>
#include <mailutils/cstr.h>
#include <mailutils/sys/pop3.h>
#define POP3_DEFERR "-ERR POP3 IO ERROR"
/* If we did not grap the ack already, call pop3_readline() but handle
Nonblocking also. */
int
mu_pop3_response (mu_pop3_t pop3, char *buffer, size_t buflen, size_t *pnread)
mu_pop3_response (mu_pop3_t pop3, size_t *pnread)
{
size_t n = 0;
int status = 0;
......@@ -35,37 +40,33 @@ mu_pop3_response (mu_pop3_t pop3, char *buffer, size_t buflen, size_t *pnread)
if (pop3 == NULL)
return EINVAL;
if (!pop3->acknowledge)
if (!MU_POP3_FISSET (pop3, MU_POP3_ACK))
{
size_t len = pop3->ack.len - (pop3->ack.ptr - pop3->ack.buf);
status = mu_pop3_readline (pop3, pop3->ack.ptr, len, &n);
pop3->ack.ptr += n;
status = mu_stream_getline (pop3->carrier, &pop3->ackbuf,
&pop3->acksize, NULL);
if (status == 0)
{
len = pop3->ack.ptr - pop3->ack.buf;
if (len && pop3->ack.buf[len - 1] == '\n')
pop3->ack.buf[len - 1] = '\0';
pop3->acknowledge = 1; /* Flag that we have the ack. */
pop3->ack.ptr = pop3->ack.buf;
n = mu_rtrim_class (pop3->ackbuf, MU_CTYPE_SPACE);
MU_POP3_FSET (pop3, MU_POP3_ACK); /* Flag that we have the ack. */
}
else
{
/* Provide them with an error. */
const char *econ = "-ERR POP3 IO ERROR";
n = strlen (econ);
strcpy (pop3->ack.buf, econ);
if (pop3->acksize < sizeof (POP3_DEFERR))
{
char *p = realloc (pop3->ackbuf, sizeof (POP3_DEFERR));
if (p)
{
pop3->ackbuf = p;
pop3->acksize = sizeof (POP3_DEFERR);
}
}
else
n = strlen (pop3->ack.buf);
if (buffer)
{
buflen--; /* Leave space for the NULL. */
n = (buflen < n) ? buflen : n;
memcpy (buffer, pop3->ack.buf, n);
buffer[n] = '\0';
if (pop3->ackbuf)
strncpy (pop3->ackbuf, POP3_DEFERR, pop3->acksize);
}
}
else if (pop3->ackbuf)
n = strlen (pop3->ackbuf);
if (pnread)
*pnread = n;
......
......@@ -41,25 +41,19 @@ mu_pop3_retr (mu_pop3_t pop3, unsigned int msgno, mu_stream_t *pstream)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "RETR %d\r\n", msgno);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_RETR;
case MU_POP3_RETR:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_RETR_ACK;
case MU_POP3_RETR_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_RETR_RX;
case MU_POP3_RETR_RX:
status = mu_pop3_stream_create (pop3, pstream);
MU_POP3_CHECK_ERROR (pop3, status);
pop3->state = MU_POP3_NO_STATE;
break;
/* They must deal with the error first by reopening. */
......
......@@ -38,19 +38,12 @@ mu_pop3_rset (mu_pop3_t pop3)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "RSET\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_RSET;
case MU_POP3_RSET:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_RSET_ACK;
case MU_POP3_RSET_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
break;
......
......@@ -31,91 +31,23 @@
#include <errno.h>
#include <mailutils/sys/pop3.h>
/* A socket may write less then expected but stream.c:mu_stream_write() will
always try to send the entire buffer unless an error is reported. We have
to cope with nonblocking, it is done by keeping track with the pop3->ptr
pointer if the write failed we keep track and restart where we left. */
int
mu_pop3_send (mu_pop3_t pop3)
{
int status = 0;
if (pop3->carrier && (pop3->io.ptr > pop3->io.buf))
{
size_t n = 0;
size_t len = pop3->io.ptr - pop3->io.buf;
/* Timeout with select(), note that we have to reset select()
since on linux tv is modified when error. */
if (pop3->timeout)
{
int ready = mu_pop3_carrier_is_ready (pop3->carrier,
MU_STREAM_READY_WR,
pop3->timeout);
if (ready == 0)
return ETIMEDOUT;
}
status = mu_stream_write (pop3->carrier, pop3->io.buf, len, &n);
if (n)
{
/* Consume what we sent. */
memmove (pop3->io.buf, pop3->io.buf + n, len - n);
pop3->io.ptr -= n;
}
}
else
pop3->io.ptr = pop3->io.buf;
return status;
}
/* According to RFC 2449: The maximum length of a command is increased from
47 characters (4 character command, single space, 40 character argument,
CRLF) to 255 octets, including the terminating CRLF. But we are flexible
on this and realloc() as needed. NOTE: The terminated CRLF is not
included. */
int
mu_pop3_writeline (mu_pop3_t pop3, const char *format, ...)
{
int len;
int status;
va_list ap;
int done = 1;
va_start(ap, format);
/* C99 says that a conforming implementation of snprintf () should
return the number of char that would have been call but many old
GNU/Linux && BSD implementations return -1 on error. Worse,
QnX/Neutrino actually does not put the terminal null char. So
let's try to cope. */
do
{
len = vsnprintf (pop3->io.buf, pop3->io.len - 1, format, ap);
if (len < 0 || len >= (int)pop3->io.len
|| !memchr (pop3->io.buf, '\0', len + 1))
{
pop3->io.len *= 2;
pop3->io.buf = realloc (pop3->io.buf, pop3->io.len);
if (pop3->io.buf == NULL)
return ENOMEM;
done = 0;
}
else
done = 1;
}
while (!done);
va_start (ap, format);
status = mu_stream_vprintf (pop3->carrier, format, ap);
va_end(ap);
pop3->io.ptr = pop3->io.buf + len;
return 0;
return status;
}
int
mu_pop3_sendline (mu_pop3_t pop3, const char *line)
{
if (line)
{
int status = mu_pop3_writeline (pop3, line);
if (status)
return status;
}
return mu_pop3_send (pop3);
return mu_stream_write (pop3->carrier, line, strlen (line), NULL);
return mu_stream_flush (pop3->carrier);
}
......
......@@ -42,26 +42,20 @@ mu_pop3_stat (mu_pop3_t pop3, unsigned *msg_count, size_t *size)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "STAT\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_STAT;
case MU_POP3_STAT:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_STAT_ACK;
case MU_POP3_STAT_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
/* Parse the answer. */
*msg_count = 0;
lv = 0;
sscanf (pop3->ack.buf, "+OK %d %lu", msg_count, &lv);
/* FIXME: Error checking */
sscanf (pop3->ackbuf, "+OK %d %lu", msg_count, &lv);
*size = lv;
break;
......
......@@ -25,9 +25,29 @@
#include <string.h>
#include <errno.h>
#include <mailutils/pop3.h>
#include <mailutils/sys/pop3.h>
#include <mailutils/tls.h>
#include <mailutils/md5.h>
#include <mailutils/list.h>
static int
pop3_swap_streams (mu_pop3_t pop3, mu_stream_t *streams)
{
int rc;
if (MU_POP3_FISSET (pop3, MU_POP3_TRACE))
rc = mu_stream_ioctl (pop3->carrier, MU_IOCTL_SWAP_STREAM, streams);
else if (streams[0] != streams[1])
rc = EINVAL;
else
{
mu_stream_t str = streams[0];
streams[0] = streams[1] = pop3->carrier;
pop3->carrier = str;
rc = 0;
}
return rc;
}
/*
* STLS
......@@ -38,48 +58,42 @@ mu_pop3_stls (mu_pop3_t pop3)
{
#ifdef WITH_TLS
int status;
mu_stream_t tlsstream, streams[2];
/* Sanity checks. */
if (pop3 == NULL)
{
return EINVAL;
}
switch (pop3->state)
{
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "STLS\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_STLS;
case MU_POP3_STLS:
status = mu_pop3_send (pop3);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_STLS_ACK;
case MU_POP3_STLS_ACK:
{
mu_stream_t tls_stream;
status = mu_pop3_response (pop3, NULL, 0, NULL);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
status = mu_tls_client_stream_create (&tls_stream,
pop3->carrier,
pop3->carrier, 0);
MU_POP3_CHECK_ERROR (pop3, status);
pop3->carrier = tls_stream;
pop3->state = MU_POP3_STLS_CONNECT;
break;
}
case MU_POP3_STLS_CONNECT:
status = mu_stream_open (pop3->carrier);
streams[0] = streams[1] = NULL;
status = pop3_swap_streams (pop3, streams);
MU_POP3_CHECK_EAGAIN (pop3, status);
status = mu_tls_client_stream_create (&tlsstream,
streams[0], streams[1], 0);
MU_POP3_CHECK_EAGAIN (pop3, status);
status = mu_stream_open (tlsstream);
MU_POP3_CHECK_EAGAIN (pop3, status);
streams[0] = streams[1] = tlsstream;
status = pop3_swap_streams (pop3, streams);
MU_POP3_CHECK_EAGAIN (pop3, status);
/* Invalidate the capability list */
mu_list_destroy (&pop3->capa);
pop3->state = MU_POP3_NO_STATE;
break;
return 0;
/* They must deal with the error first by reopening. */
case MU_POP3_ERROR:
......@@ -93,7 +107,6 @@ mu_pop3_stls (mu_pop3_t pop3)
return status;
#else
(void)pop3;
return ENOTSUP;
#endif
}
......
......@@ -23,124 +23,242 @@
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <mailutils/filter.h>
#include <mailutils/stream.h>
#include <mailutils/sys/stream.h>
#include <mailutils/sys/pop3.h>
/* Implementation of the stream for TOP and RETR. */
struct mu_pop3_stream
{
struct _mu_stream stream;
mu_pop3_t pop3;
int done;
};
static void
mu_pop3_stream_destroy (mu_stream_t stream)
{
struct mu_pop3_stream *pop3_stream = mu_stream_get_owner (stream);
if (pop3_stream)
enum pop3_decode_state
{
free (pop3_stream);
}
}
pds_init, /* initial state */
pds_cr, /* prev. char was \r */
pds_crlf, /* 2 prev. char were \r\n */
pds_dot, /* 3 prev. chars were \r\n. */
pds_dotcr, /* 4 prev. chars were \r\n.\r */
pds_end /* final state, a \r\n.\r\n seen. */
};
static int
mu_pop3_stream_read (mu_stream_t stream, char *buf, size_t buflen, mu_off_t offset, size_t *pn)
newstate (int state, int c)
{
struct mu_pop3_stream *pop3_stream = mu_stream_get_owner (stream);
size_t n = 0;
int status = 0;
char *p = buf;
switch (state)
{
case pds_init:
switch (c)
{
case '\r':
return pds_cr;
}
break;
(void)offset;
if (pop3_stream)
case pds_cr:
switch (c)
{
if (!pop3_stream->done)
case '\r':
return pds_cr;
case '\n':
return pds_crlf;
}
break;
case pds_crlf:
switch (c)
{
do
case '\r':
return pds_cr;
case '.':
return pds_dot;
}
case pds_dot:
switch (c)
{
size_t nread = 0;
case '\r':
return pds_dotcr;
}
break;
/* The pop3_readline () function will always read one less to
be able to null terminate the buffer, this will cause
serious grief for mu_stream_read() where it is legitimate to
have a buffer of 1 char. So we must catch it here. */
if (buflen == 1)
case pds_dotcr:
switch (c)
{
char buffer[2];
*buffer = '\0';
status = mu_pop3_readline (pop3_stream->pop3, buffer, 2, &nread);
*p = *buffer;
case '\n':
return pds_end;
}
else
status = mu_pop3_readline (pop3_stream->pop3, p, buflen, &nread);
}
return pds_init;
}
/* Move min(isize,osize) bytes from iptr to optr, replacing each \r\n
with \n. */
static enum mu_filter_result
_pop3_decoder (void *xd,
enum mu_filter_command cmd,
struct mu_filter_io *iobuf)
{
int *pstate = xd;
size_t i, j;
const unsigned char *iptr;
size_t isize;
char *optr;
size_t osize;
switch (cmd)
{
case mu_filter_init:
*pstate = pds_init;
return mu_filter_ok;
case mu_filter_done:
return mu_filter_ok;
default:
break;
}
iptr = (const unsigned char *) iobuf->input;
isize = iobuf->isize;
optr = iobuf->output;
osize = iobuf->osize;
for (i = j = 0; *pstate != pds_end && i < isize && j < osize; i++)
{
unsigned char c = *iptr++;
if (status != 0)
if (c == '\r')
{
if (i + 1 == isize)
break;
if (nread == 0)
*pstate = newstate (*pstate, c);
if (*iptr == '\n')
continue;
}
else if (c == '.' && *pstate == pds_crlf)
{
pop3_stream->pop3->state = MU_POP3_NO_STATE;
pop3_stream->done = 1;
if (i + 1 == isize)
break;
*pstate = newstate (*pstate, c);
if (*iptr != '\r')
continue;
}
n += nread;
buflen -= nread;
p += nread;
else
*pstate = newstate (*pstate, c);
optr[j++] = c;
}
while (buflen > 0);
if (*pstate == pds_end)
iobuf->eof = 1;
iobuf->isize = i;
iobuf->osize = j;
return mu_filter_ok;
}
static int
mu_pop3_filter_create (mu_stream_t *pstream, mu_stream_t stream)
{
int *state = malloc (sizeof (*state));
if (!state)
return ENOMEM;
return mu_filter_stream_create (pstream, stream,
MU_FILTER_DECODE,
_pop3_decoder, state,
MU_STREAM_READ);
}
static int
_mu_pop3_read (struct _mu_stream *str, char *buf, size_t bufsize,
size_t *pnread)
{
struct mu_pop3_stream *sp = (struct mu_pop3_stream *)str;
mu_pop3_t pop3 = sp->pop3;
size_t nread;
int status = 0;
if (sp->done)
nread = 0;
else
{
status = mu_stream_read (pop3->carrier, buf, bufsize, &nread);
if (status == 0 && nread == 0)
{
pop3->state = MU_POP3_NO_STATE;
sp->done = 1;
}
}
if (pn)
*pn = n;
*pnread = nread;
return status;
}
static int
mu_pop3_stream_readline (mu_stream_t stream, char *buf, size_t buflen, mu_off_t offset, size_t *pn)
_mu_pop3_readdelim (struct _mu_stream *str, char *buf, size_t bufsize,
int delim, size_t *pnread)
{
struct mu_pop3_stream *pop3_stream = mu_stream_get_owner (stream);
size_t n = 0;
struct mu_pop3_stream *sp = (struct mu_pop3_stream *)str;
mu_pop3_t pop3 = sp->pop3;
size_t nread;
int status = 0;
(void)offset;
if (pop3_stream)
{
if (!pop3_stream->done)
if (sp->done)
nread = 0;
else
{
status = mu_pop3_readline (pop3_stream->pop3, buf, buflen, &n);
if (n == 0)
status = mu_stream_readdelim (pop3->carrier, buf, bufsize, delim,
&nread);
if (status == 0 && nread == 0)
{
pop3_stream->pop3->state = MU_POP3_NO_STATE;
pop3_stream->done = 1;
pop3->state = MU_POP3_NO_STATE;
sp->done = 1;
}
}
}
if (pn)
*pn = n;
*pnread = nread;
return status;
}
static int
_mu_pop3_flush (struct _mu_stream *str)
{
struct mu_pop3_stream *sp = (struct mu_pop3_stream *)str;
mu_pop3_t pop3 = sp->pop3;
return mu_stream_flush (pop3->carrier);
}
static int
_mu_pop3_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp)
{
struct mu_pop3_stream *sp = (struct mu_pop3_stream *)str;
mu_pop3_t pop3 = sp->pop3;
return mu_stream_wait (pop3->carrier, pflags, tvp);
}
int
mu_pop3_stream_create (mu_pop3_t pop3, mu_stream_t *pstream)
{
struct mu_pop3_stream *pop3_stream;
struct mu_pop3_stream *sp;
mu_stream_t str;
int status;
pop3_stream = malloc (sizeof *pop3_stream);
if (pop3_stream == NULL)
sp = (struct mu_pop3_stream *) _mu_stream_create (sizeof (*sp),
MU_STREAM_READ);
if (!sp)
return ENOMEM;
sp->stream.read = _mu_pop3_read;
sp->stream.readdelim = _mu_pop3_readdelim;
sp->stream.flush = _mu_pop3_flush;
sp->stream.wait = _mu_pop3_wait;
sp->pop3 = pop3;
sp->done = 0;
str = (mu_stream_t) sp;
mu_stream_set_buffer (str, mu_buffer_line, 1024);
pop3_stream->pop3 = pop3;
pop3_stream->done = 0;
status = mu_stream_create (pstream, MU_STREAM_READ, pop3_stream);
if (status != 0)
{
free (pop3_stream);
status = mu_pop3_filter_create (pstream, str);
mu_stream_unref (str);
return status;
}
mu_stream_set_read (*pstream, mu_pop3_stream_read, pop3_stream);
mu_stream_set_readline (*pstream, mu_pop3_stream_readline, pop3_stream);
mu_stream_set_destroy (*pstream, mu_pop3_stream_destroy, pop3_stream);
return 0;
}
......
......@@ -26,7 +26,8 @@
#include <mailutils/sys/pop3.h>
int
mu_pop3_top (mu_pop3_t pop3, unsigned msgno, unsigned int lines, mu_stream_t *pstream)
mu_pop3_top (mu_pop3_t pop3, unsigned msgno, unsigned int lines,
mu_stream_t *pstream)
{
int status;
......@@ -40,25 +41,19 @@ mu_pop3_top (mu_pop3_t pop3, unsigned msgno, unsigned int lines, mu_stream_t *ps
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "TOP %d %d\r\n", msgno, lines);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_TOP;
case MU_POP3_TOP:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_TOP_ACK;
case MU_POP3_TOP_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_TOP_RX;
case MU_POP3_TOP_RX:
status = mu_pop3_stream_create (pop3, pstream);
MU_POP3_CHECK_ERROR (pop3, status);
pop3->state = MU_POP3_NO_STATE;
break;
/* They must deal with the error first by reopening. */
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2007, 2009, 2010 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, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <mailutils/error.h>
#include <mailutils/nls.h>
#include <mailutils/stream.h>
#include <mailutils/sys/pop3.h>
static const char *pop3_prefix[] = {
"S: ", "C: "
};
int
_mu_pop3_trace_enable (mu_pop3_t pop3)
{
int rc = 0;
mu_debug_t debug;
mu_stream_t dstr, xstr;
if (!pop3->carrier)
{
MU_POP3_FSET (pop3, MU_POP3_TRACE);
return 0;
}
mu_diag_get_debug (&debug);
rc = mu_dbgstream_create (&dstr, debug, MU_DIAG_DEBUG, 0);
if (rc)
mu_error (_("cannot create debug stream; transcript disabled: %s"),
mu_strerror (rc));
else
{
rc = mu_xscript_stream_create (&xstr, pop3->carrier, dstr,
pop3_prefix);
if (rc)
mu_error (_("cannot create transcript stream: %s"),
mu_strerror (rc));
else
{
mu_stream_unref (pop3->carrier);
pop3->carrier = xstr;
MU_POP3_FSET (pop3, MU_POP3_TRACE);
}
}
return rc;
}
int
_mu_pop3_trace_disable (mu_pop3_t pop3)
{
mu_stream_t xstr = pop3->carrier;
mu_stream_t stream[2];
int rc;
if (!xstr)
return 0;
rc = mu_stream_ioctl (xstr, MU_IOCTL_GET_TRANSPORT, stream);
if (rc)
return rc;
pop3->carrier = stream[0];
mu_stream_destroy (&xstr);
MU_POP3_FCLR (pop3, MU_POP3_TRACE);
return 0;
}
int
mu_pop3_trace (mu_pop3_t pop3, int op)
{
int trace_on = MU_POP3_FISSET (pop3, MU_POP3_TRACE);
switch (op)
{
case MU_POP3_TRACE_SET:
if (trace_on)
return MU_ERR_EXISTS;
return _mu_pop3_trace_enable (pop3);
case MU_POP3_TRACE_CLR:
if (!trace_on)
return MU_ERR_NOENT;
return _mu_pop3_trace_disable (pop3);
case MU_POP3_TRACE_QRY:
if (!trace_on)
return MU_ERR_NOENT;
return 0;
}
return EINVAL;
}
......@@ -30,6 +30,7 @@ int
mu_pop3_uidl (mu_pop3_t pop3, unsigned int msgno, char **uidl)
{
int status;
char *space;
if (pop3 == NULL)
return EINVAL;
......@@ -41,29 +42,21 @@ mu_pop3_uidl (mu_pop3_t pop3, unsigned int msgno, char **uidl)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "UIDL %d\r\n", msgno);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_UIDL;
case MU_POP3_UIDL:
status = mu_pop3_send (pop3);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_UIDL_ACK;
case MU_POP3_UIDL_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
*uidl = NULL;
{
char *space;
/* Format: +OK msgno uidlstring */
/* Pass the "+OK". */
space = strchr (pop3->ack.buf, ' ');
space = strchr (pop3->ackbuf, ' ');
if (space)
{
/* Skip spaces. */
......@@ -86,7 +79,7 @@ mu_pop3_uidl (mu_pop3_t pop3, unsigned int msgno, char **uidl)
memcpy (*uidl, space, len);
}
}
}
if (*uidl == NULL) /* What can we do? */
{
*uidl = malloc (1);
......
......@@ -40,26 +40,20 @@ mu_pop3_uidl_all (mu_pop3_t pop3, mu_iterator_t *piterator)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "UIDL\r\n");
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_UIDL;
case MU_POP3_UIDL:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_UIDL_ACK;
case MU_POP3_UIDL_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
status = mu_pop3_iterator_create (pop3, piterator);
MU_POP3_CHECK_ERROR (pop3, status);
pop3->state = MU_POP3_UIDL_RX;
case MU_POP3_UIDL_RX:
/* The mu_iterator_t will read the stream and set the state to MU_POP3_NO_STATE when done. */
/* The mu_iterator_t will read the stream and set the state to
MU_POP3_NO_STATE when done. */
break;
/* They must deal with the error first by reopening. */
......
......@@ -37,19 +37,12 @@ mu_pop3_user (mu_pop3_t pop3, const char *user)
case MU_POP3_NO_STATE:
status = mu_pop3_writeline (pop3, "USER %s\r\n", user);
MU_POP3_CHECK_ERROR (pop3, status);
mu_pop3_debug_cmd (pop3);
MU_POP3_FCLR (pop3, MU_POP3_ACK);
pop3->state = MU_POP3_USER;
case MU_POP3_USER:
status = mu_pop3_send (pop3);
status = mu_pop3_response (pop3, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
pop3->acknowledge = 0;
pop3->state = MU_POP3_USER_ACK;
case MU_POP3_USER_ACK:
status = mu_pop3_response (pop3, NULL, 0, NULL);
MU_POP3_CHECK_EAGAIN (pop3, status);
mu_pop3_debug_ack (pop3);
MU_POP3_CHECK_OK (pop3);
pop3->state = MU_POP3_NO_STATE;
break;
......
......@@ -49,6 +49,8 @@ init_iobuf (struct mu_filter_io *io, struct _mu_filter_stream *fs)
io->isize = MFB_LEVEL (fs->inbuf);
io->output = MFB_ENDPTR (fs->outbuf);
io->osize = MFB_FREESIZE (fs->outbuf);
io->errcode = 0;
io->eof = 0;
}
static int
......@@ -114,8 +116,9 @@ filter_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
size_t min_input_level = MU_FILTER_BUF_SIZE;
size_t min_output_size = MU_FILTER_BUF_SIZE;
size_t total = 0;
int stop = 0;
while (total < size && cmd != mu_filter_lastbuf)
while (!stop && total < size && cmd != mu_filter_lastbuf)
{
size_t rdsize;
......@@ -168,6 +171,11 @@ filter_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
if (iobuf.isize > MFB_RDBYTES (fs->inbuf)
|| iobuf.osize > MFB_FREESIZE (fs->outbuf))
return MU_ERR_FAILURE; /* FIXME: special error code? */
if (iobuf.eof)
{
stream->flags |= _MU_STR_EOF;
stop = 1;
}
break;
case mu_filter_falure:
......
......@@ -176,6 +176,7 @@ _xscript_ctl (struct _mu_stream *str, int op, void *arg)
{
struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str;
mu_transport_t *ptrans;
int status = 0;
switch (op)
{
......@@ -198,11 +199,37 @@ _xscript_ctl (struct _mu_stream *str, int op, void *arg)
break;
case MU_IOCTL_SWAP_STREAM:
/* fall through */
if (!arg)
return EINVAL;
status = mu_stream_ioctl (sp->transport, op, arg);
if (status == EINVAL || status == ENOSYS)
{
mu_stream_t *pstr = arg;
mu_stream_t tmp;
if (pstr[0] != pstr[1])
return EINVAL; /* FIXME */
tmp = pstr[0];
pstr[0] = sp->transport;
pstr[1] = sp->transport;
sp->transport = tmp;
if (!(str->flags & MU_STREAM_AUTOCLOSE))
{
if (pstr[0])
mu_stream_unref (pstr[0]);
if (tmp)
mu_stream_ref (tmp);
}
if (tmp)
mu_stream_ref (tmp);
status = 0;
}
break;
default:
return mu_stream_ioctl (sp->transport, op, arg);
}
return 0;
return status;
}
static int
......