aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/pop3/camel-pop3-engine.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/providers/pop3/camel-pop3-engine.c')
-rw-r--r--camel/providers/pop3/camel-pop3-engine.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/camel/providers/pop3/camel-pop3-engine.c b/camel/providers/pop3/camel-pop3-engine.c
new file mode 100644
index 0000000000..22d3ab5930
--- /dev/null
+++ b/camel/providers/pop3/camel-pop3-engine.c
@@ -0,0 +1,348 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
+ *
+ * Author:
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "camel-pop3-engine.h"
+#include "camel-pop3-stream.h"
+#include <camel/camel-service.h>
+#include <camel/camel-sasl.h>
+
+/* max 'outstanding' bytes in output stream, so we can't deadlock waiting
+ for the server to accept our data when pipelining */
+#define CAMEL_POP3_SEND_LIMIT (1024)
+
+
+extern int camel_verbose_debug;
+#define dd(x) (camel_verbose_debug?(x):0)
+
+static void get_capabilities(CamelPOP3Engine *pe);
+
+static CamelObjectClass *parent_class = NULL;
+
+/* Returns the class for a CamelStream */
+#define CS_CLASS(so) CAMEL_POP3_ENGINE_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+static void
+camel_pop3_engine_class_init (CamelPOP3EngineClass *camel_pop3_engine_class)
+{
+ parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
+}
+
+static void
+camel_pop3_engine_init(CamelPOP3Engine *pe, CamelPOP3EngineClass *peclass)
+{
+ e_dlist_init(&pe->active);
+ e_dlist_init(&pe->queue);
+ e_dlist_init(&pe->done);
+ pe->state = CAMEL_POP3_ENGINE_DISCONNECT;
+}
+
+static void
+camel_pop3_engine_finalise(CamelPOP3Engine *pe)
+{
+ /* FIXME: Also flush/free any outstanding requests, etc */
+
+ if (pe->stream)
+ camel_object_unref((CamelObject *)pe->stream);
+}
+
+CamelType
+camel_pop3_engine_get_type (void)
+{
+ static CamelType camel_pop3_engine_type = CAMEL_INVALID_TYPE;
+
+ if (camel_pop3_engine_type == CAMEL_INVALID_TYPE) {
+ camel_pop3_engine_type = camel_type_register(camel_object_get_type(),
+ "CamelPOP3Engine",
+ sizeof( CamelPOP3Engine ),
+ sizeof( CamelPOP3EngineClass ),
+ (CamelObjectClassInitFunc) camel_pop3_engine_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_pop3_engine_init,
+ (CamelObjectFinalizeFunc) camel_pop3_engine_finalise );
+ }
+
+ return camel_pop3_engine_type;
+}
+
+/**
+ * camel_pop3_engine_new:
+ *
+ * Returns a NULL stream. A null stream is always at eof, and
+ * always returns success for all reads and writes.
+ *
+ * Return value: the stream
+ **/
+CamelPOP3Engine *
+camel_pop3_engine_new(CamelStream *source)
+{
+ CamelPOP3Engine *pe;
+
+ pe = (CamelPOP3Engine *)camel_object_new(camel_pop3_engine_get_type ());
+
+ pe->stream = (CamelPOP3Stream *)camel_pop3_stream_new(source);
+ pe->state = CAMEL_POP3_ENGINE_AUTH;
+
+ get_capabilities(pe);
+
+ return pe;
+}
+
+/* TODO: read implementation too?
+ STARTLS?
+ etc? */
+struct {
+ char *cap;
+ guint32 flag;
+} capa[] = {
+ { "APOP" , CAMEL_POP3_CAP_APOP },
+ { "TOP" , CAMEL_POP3_CAP_TOP },
+ { "UIDL", CAMEL_POP3_CAP_UIDL },
+ { "PIPELINING", CAMEL_POP3_CAP_PIPE },
+};
+
+static void
+cmd_capa(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
+{
+ unsigned char *line, *tok, *next;
+ unsigned int len;
+ int ret;
+ int i;
+ CamelServiceAuthType *auth;
+
+ dd(printf("cmd_capa\n"));
+
+ do {
+ ret = camel_pop3_stream_line(stream, &line, &len);
+ if (ret >= 0) {
+ if (strncmp(line, "SASL ", 5) == 0) {
+ tok = line+5;
+ dd(printf("scanning tokens '%s'\n", tok));
+ while (tok) {
+ next = strchr(tok, ' ');
+ if (next)
+ *next++ = 0;
+ auth = camel_sasl_authtype(tok);
+ if (auth) {
+ dd(printf("got auth type '%s'\n", tok));
+ pe->auth = g_list_prepend(pe->auth, auth);
+ } else {
+ dd(printf("unsupported auth type '%s'\n", tok));
+ }
+ tok = next;
+ }
+ } else {
+ for (i=0;i<sizeof(capa)/sizeof(capa[0]);i++) {
+ if (strcmp(capa[i].cap, line) == 0)
+ pe->capa |= capa[i].flag;
+ }
+ }
+ }
+ } while (ret>0);
+}
+
+static void
+get_capabilities(CamelPOP3Engine *pe)
+{
+ CamelPOP3Command *pc;
+ unsigned char *line, *apop, *apopend;
+ unsigned int len;
+ extern CamelServiceAuthType camel_pop3_password_authtype;
+ extern CamelServiceAuthType camel_pop3_apop_authtype;
+
+ /* first, read the greeting */
+ if (camel_pop3_stream_line(pe->stream, &line, &len) == -1
+ || strncmp(line, "+OK", 3) != 0)
+ return;
+
+ if ((apop = strchr(line+3, '<'))
+ && (apopend = strchr(apop, '>'))) {
+ *apopend = 0;
+ pe->apop = g_strdup(apop+1);
+ pe->capa = CAMEL_POP3_CAP_APOP;
+ pe->auth = g_list_append(pe->auth, &camel_pop3_apop_authtype);
+ }
+
+ pe->auth = g_list_prepend(pe->auth, &camel_pop3_password_authtype);
+
+ pc = camel_pop3_engine_command_new(pe, CAMEL_POP3_COMMAND_MULTI, cmd_capa, NULL, "CAPA\r\n");
+ while (camel_pop3_engine_iterate(pe, pc) > 0)
+ ;
+ camel_pop3_engine_command_free(pe, pc);
+}
+
+/* returns true if the command was sent, false if it was just queued */
+static int
+engine_command_queue(CamelPOP3Engine *pe, CamelPOP3Command *pc)
+{
+ if (((pe->capa & CAMEL_POP3_CAP_PIPE) == 0 || (pe->sentlen + strlen(pc->data)) > CAMEL_POP3_SEND_LIMIT)
+ && pe->current != NULL) {
+ e_dlist_addtail(&pe->queue, (EDListNode *)pc);
+ return FALSE;
+ } else {
+ /* ??? */
+ if (camel_stream_write((CamelStream *)pe->stream, pc->data, strlen(pc->data)) == -1) {
+ e_dlist_addtail(&pe->queue, (EDListNode *)pc);
+ return FALSE;
+ }
+
+ pe->sentlen += strlen(pc->data);
+
+ pc->state = CAMEL_POP3_COMMAND_DISPATCHED;
+
+ if (pe->current == NULL)
+ pe->current = pc;
+ else
+ e_dlist_addtail(&pe->active, (EDListNode *)pc);
+
+ return TRUE;
+ }
+}
+
+/* returns -1 on error (sets errno), 0 when no work to do, or >0 if work remaining */
+int
+camel_pop3_engine_iterate(CamelPOP3Engine *pe, CamelPOP3Command *pcwait)
+{
+ unsigned char *p;
+ unsigned int len;
+ CamelPOP3Command *pc, *pw, *pn;
+
+ if (pcwait && pcwait->state >= CAMEL_POP3_COMMAND_OK)
+ return 0;
+
+ pc = pe->current;
+ if (pc == NULL)
+ return 0;
+
+ /* LOCK */
+
+ if (camel_pop3_stream_line(pe->stream, &pe->line, &pe->linelen) == -1)
+ return -1;
+
+ p = pe->line;
+ switch (p[0]) {
+ case '+':
+ dd(printf("Got + response\n"));
+ if (pc->flags & CAMEL_POP3_COMMAND_MULTI) {
+ pc->state = CAMEL_POP3_COMMAND_DATA;
+ camel_pop3_stream_set_mode(pe->stream, CAMEL_POP3_STREAM_DATA);
+
+ if (pc->func)
+ pc->func(pe, pe->stream, pc->func_data);
+
+ /* Make sure we get all data before going back to command mode */
+ while (camel_pop3_stream_getd(pe->stream, &p, &len) > 0)
+ ;
+ camel_pop3_stream_set_mode(pe->stream, CAMEL_POP3_STREAM_LINE);
+ } else {
+ pc->state = CAMEL_POP3_COMMAND_OK;
+ }
+ break;
+ case '-':
+ pc->state = CAMEL_POP3_COMMAND_ERR;
+ break;
+ default:
+ /* what do we do now? f'knows! */
+ g_warning("Bad server response: %s\n", p);
+ errno = EIO;
+ return -1;
+ }
+
+ e_dlist_addtail(&pe->done, (EDListNode *)pc);
+ pe->sentlen -= strlen(pc->data);
+
+ /* Set next command */
+ pe->current = (CamelPOP3Command *)e_dlist_remhead(&pe->active);
+
+ /* check the queue for sending any we can now send also */
+ pw = (CamelPOP3Command *)pe->queue.head;
+ pn = pw->next;
+ while (pn) {
+ if (((pe->capa & CAMEL_POP3_CAP_PIPE) == 0 || (pe->sentlen + strlen(pw->data)) > CAMEL_POP3_SEND_LIMIT)
+ && pe->current != NULL)
+ break;
+
+ if (camel_stream_write((CamelStream *)pe->stream, pw->data, strlen(pw->data)) == -1)
+ return -1;
+
+ e_dlist_remove((EDListNode *)pw);
+
+
+ pe->sentlen += strlen(pw->data);
+ pw->state = CAMEL_POP3_COMMAND_DISPATCHED;
+
+ if (pe->current == NULL)
+ pe->current = pw;
+ else
+ e_dlist_addtail(&pe->active, (EDListNode *)pw);
+
+ pw = pn;
+ pn = pn->next;
+ }
+
+ /* UNLOCK */
+
+ if (pcwait && pcwait->state >= CAMEL_POP3_COMMAND_OK)
+ return 0;
+
+ return pe->current==NULL?0:1;
+}
+
+CamelPOP3Command *
+camel_pop3_engine_command_new(CamelPOP3Engine *pe, guint32 flags, CamelPOP3CommandFunc func, void *data, const char *fmt, ...)
+{
+ CamelPOP3Command *pc;
+ va_list ap;
+
+ pc = g_malloc0(sizeof(*pc));
+ pc->func = func;
+ pc->func_data = data;
+ pc->flags = flags;
+
+ va_start(ap, fmt);
+ pc->data = g_strdup_vprintf(fmt, ap);
+ pc->state = CAMEL_POP3_COMMAND_IDLE;
+
+ /* TODO: what abou write errors? */
+ engine_command_queue(pe, pc);
+
+ return pc;
+}
+
+void
+camel_pop3_engine_command_free(CamelPOP3Engine *pe, CamelPOP3Command *pc)
+{
+ if (pe->current != pc)
+ e_dlist_remove((EDListNode *)pc);
+ g_free(pc->data);
+ g_free(pc);
+}