aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/pop3/camel-pop3-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/providers/pop3/camel-pop3-stream.c')
-rw-r--r--camel/providers/pop3/camel-pop3-stream.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/camel/providers/pop3/camel-pop3-stream.c b/camel/providers/pop3/camel-pop3-stream.c
new file mode 100644
index 0000000000..5b0dd979ca
--- /dev/null
+++ b/camel/providers/pop3/camel-pop3-stream.c
@@ -0,0 +1,468 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
+ *
+ * Author:
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2002 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
+ */
+
+/* This is *identical* to the camel-nntp-stream, so should probably
+ work out a way to merge them */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "camel-pop3-stream.h"
+
+extern int camel_verbose_debug;
+#define dd(x) (camel_verbose_debug?(x):0)
+
+static CamelObjectClass *parent_class = NULL;
+
+/* Returns the class for a CamelStream */
+#define CS_CLASS(so) CAMEL_POP3_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+#define CAMEL_POP3_STREAM_SIZE (4096)
+#define CAMEL_POP3_STREAM_LINE (1024) /* maximum line size */
+
+static int
+stream_fill(CamelPOP3Stream *is)
+{
+ int left = 0;
+
+ if (is->source) {
+ left = is->end - is->ptr;
+ memcpy(is->buf, is->ptr, left);
+ is->end = is->buf + left;
+ is->ptr = is->buf;
+ left = camel_stream_read(is->source, is->end, CAMEL_POP3_STREAM_SIZE - (is->end - is->buf));
+ if (left > 0) {
+ is->end += left;
+ is->end[0] = '\n';
+ return is->end - is->ptr;
+ } else {
+ dd(printf("POP3_STREAM_FILL(ERROR): '%s'\n", strerror(errno)));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t
+stream_read(CamelStream *stream, char *buffer, size_t n)
+{
+ CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
+ char *o, *oe;
+ unsigned char *p, *e, c;
+ int state;
+
+ if (is->mode != CAMEL_POP3_STREAM_DATA || n == 0)
+ return 0;
+
+ o = buffer;
+ oe = buffer + n;
+ state = is->state;
+
+ /* Need to copy/strip '.'s and whatnot */
+ p = is->ptr;
+ e = is->end;
+
+ switch(state) {
+ state_0:
+ case 0: /* start of line, always read at least 3 chars */
+ while (e - p < 3) {
+ is->ptr = p;
+ if (stream_fill(is) == -1)
+ return -1;
+ p = is->ptr;
+ e = is->end;
+ }
+ if (p[0] == '.') {
+ if (p[1] == '\r' && p[2] == '\n') {
+ is->ptr = p+3;
+ is->mode = CAMEL_POP3_STREAM_EOD;
+ is->state = 0;
+ dd(printf("POP3_STREAM_READ(%d):\n%.*s\n", o-buffer, o-buffer, buffer));
+ return o-buffer;
+ }
+ p++;
+ }
+ state = 1;
+ /* FALLS THROUGH */
+ case 1: /* looking for next sol */
+ while (o < oe) {
+ c = *p++;
+ if (c == '\n') {
+ /* end of input sentinal check */
+ if (p > e) {
+ is->ptr = e;
+ if (stream_fill(is) == -1)
+ return -1;
+ p = is->ptr;
+ e = is->end;
+ } else {
+ *o++ = '\n';
+ state = 0;
+ goto state_0;
+ }
+ } else if (c != '\r') {
+ *o++ = c;
+ }
+ }
+ break;
+ }
+
+ is->ptr = p;
+ is->state = state;
+
+ dd(printf("POP3_STREAM_READ(%d):\n%.*s\n", o-buffer, o-buffer, buffer));
+
+ return o-buffer;
+}
+
+static ssize_t
+stream_write(CamelStream *stream, const char *buffer, size_t n)
+{
+ CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
+
+ dd(printf("POP3_STREAM_WRITE(%d):\n%.*s\n", n, (int)n, buffer));
+
+ return camel_stream_write(is->source, buffer, n);
+}
+
+static int
+stream_close(CamelStream *stream)
+{
+ /* nop? */
+ return 0;
+}
+
+static int
+stream_flush(CamelStream *stream)
+{
+ /* nop? */
+ return 0;
+}
+
+static gboolean
+stream_eos(CamelStream *stream)
+{
+ CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
+
+ return is->mode != CAMEL_POP3_STREAM_DATA;
+}
+
+static int
+stream_reset(CamelStream *stream)
+{
+ /* nop? reset literal mode? */
+ return 0;
+}
+
+static void
+camel_pop3_stream_class_init (CamelStreamClass *camel_pop3_stream_class)
+{
+ CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_pop3_stream_class;
+
+ parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
+
+ /* virtual method definition */
+ camel_stream_class->read = stream_read;
+ camel_stream_class->write = stream_write;
+ camel_stream_class->close = stream_close;
+ camel_stream_class->flush = stream_flush;
+ camel_stream_class->eos = stream_eos;
+ camel_stream_class->reset = stream_reset;
+}
+
+static void
+camel_pop3_stream_init(CamelPOP3Stream *is, CamelPOP3StreamClass *isclass)
+{
+ /* +1 is room for appending a 0 if we need to for a line */
+ is->ptr = is->end = is->buf = g_malloc(CAMEL_POP3_STREAM_SIZE+1);
+ is->lineptr = is->linebuf = g_malloc(CAMEL_POP3_STREAM_LINE+1);
+ is->lineend = is->linebuf + CAMEL_POP3_STREAM_LINE;
+
+ /* init sentinal */
+ is->ptr[0] = '\n';
+
+ is->state = 0;
+ is->mode = CAMEL_POP3_STREAM_LINE;
+}
+
+static void
+camel_pop3_stream_finalise(CamelPOP3Stream *is)
+{
+ g_free(is->buf);
+ g_free(is->linebuf);
+ if (is->source)
+ camel_object_unref((CamelObject *)is->source);
+}
+
+CamelType
+camel_pop3_stream_get_type (void)
+{
+ static CamelType camel_pop3_stream_type = CAMEL_INVALID_TYPE;
+
+ if (camel_pop3_stream_type == CAMEL_INVALID_TYPE) {
+ camel_pop3_stream_type = camel_type_register( camel_stream_get_type(),
+ "CamelPOP3Stream",
+ sizeof( CamelPOP3Stream ),
+ sizeof( CamelPOP3StreamClass ),
+ (CamelObjectClassInitFunc) camel_pop3_stream_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_pop3_stream_init,
+ (CamelObjectFinalizeFunc) camel_pop3_stream_finalise );
+ }
+
+ return camel_pop3_stream_type;
+}
+
+/**
+ * camel_pop3_stream_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
+ **/
+CamelStream *
+camel_pop3_stream_new(CamelStream *source)
+{
+ CamelPOP3Stream *is;
+
+ is = (CamelPOP3Stream *)camel_object_new(camel_pop3_stream_get_type ());
+ camel_object_ref((CamelObject *)source);
+ is->source = source;
+
+ return (CamelStream *)is;
+}
+
+/* Get one line from the pop3 stream */
+int
+camel_pop3_stream_line(CamelPOP3Stream *is, unsigned char **data, unsigned int *len)
+{
+ register unsigned char c, *p, *o, *oe;
+ int newlen, oldlen;
+ unsigned char *e;
+
+ if (is->mode == CAMEL_POP3_STREAM_EOD) {
+ *data = is->linebuf;
+ *len = 0;
+ return 0;
+ }
+
+ o = is->linebuf;
+ oe = is->lineend - 1;
+ p = is->ptr;
+ e = is->end;
+
+ /* Data mode, convert leading '..' to '.', and stop when we reach a solitary '.' */
+ if (is->mode == CAMEL_POP3_STREAM_DATA) {
+ /* need at least 3 chars in buffer */
+ while (e-p < 3) {
+ is->ptr = p;
+ if (stream_fill(is) == -1)
+ return -1;
+ p = is->ptr;
+ e = is->end;
+ }
+
+ /* check for isolated '.\r\n' or begging of line '.' */
+ if (p[0] == '.') {
+ if (p[1] == '\r' && p[2] == '\n') {
+ is->ptr = p+3;
+ is->mode = CAMEL_POP3_STREAM_EOD;
+ *data = is->linebuf;
+ *len = 0;
+ is->linebuf[0] = 0;
+
+ dd(printf("POP3_STREAM_LINE(END)\n"));
+
+ return 0;
+ }
+ p++;
+ }
+ }
+
+ while (1) {
+ while (o < oe) {
+ c = *p++;
+ if (c == '\n') {
+ /* sentinal? */
+ if (p> e) {
+ is->ptr = e;
+ if (stream_fill(is) == -1)
+ return -1;
+ p = is->ptr;
+ e = is->end;
+ } else {
+ is->ptr = p;
+ *data = is->linebuf;
+ *len = o - is->linebuf;
+ *o = 0;
+
+ dd(printf("POP3_STREAM_LINE(%d): '%s'\n", *len, *data));
+
+ return 1;
+ }
+ } else if (c != '\r') {
+ *o++ = c;
+ }
+ }
+
+ /* limit this for bad server data? */
+ oldlen = o - is->linebuf;
+ newlen = (is->lineend - is->linebuf) * 3 / 2;
+ is->lineptr = is->linebuf = g_realloc(is->linebuf, newlen);
+ is->lineend = is->linebuf + newlen;
+ oe = is->lineend - 1;
+ o = is->linebuf + oldlen;
+ }
+
+ return -1;
+}
+
+/* returns -1 on error, 0 if last lot of data, >0 if more remaining */
+int camel_pop3_stream_gets(CamelPOP3Stream *is, unsigned char **start, unsigned int *len)
+{
+ int max;
+ unsigned char *end;
+
+ *len = 0;
+
+ max = is->end - is->ptr;
+ if (max == 0) {
+ max = stream_fill(is);
+ if (max <= 0)
+ return max;
+ }
+
+ *start = is->ptr;
+ end = memchr(is->ptr, '\n', max);
+ if (end)
+ max = (end - is->ptr) + 1;
+ *start = is->ptr;
+ *len = max;
+ is->ptr += max;
+
+ dd(printf("POP3_STREAM_GETS(%s,%d): '%.*s'\n", end==NULL?"more":"last", *len, (int)*len, *start));
+
+ return end == NULL?1:0;
+}
+
+void camel_pop3_stream_set_mode(CamelPOP3Stream *is, camel_pop3_stream_mode_t mode)
+{
+ is->mode = mode;
+}
+
+/* returns -1 on erorr, 0 if last data, >0 if more data left */
+int camel_pop3_stream_getd(CamelPOP3Stream *is, unsigned char **start, unsigned int *len)
+{
+ unsigned char *p, *e, *s;
+ int state;
+
+ *len = 0;
+
+ if (is->mode == CAMEL_POP3_STREAM_EOD)
+ return 0;
+
+ if (is->mode == CAMEL_POP3_STREAM_LINE) {
+ g_warning("pop3_stream reading data in line mode\n");
+ return 0;
+ }
+
+ state = is->state;
+ p = is->ptr;
+ e = is->end;
+
+ while (e - p < 3) {
+ is->ptr = p;
+ if (stream_fill(is) == -1)
+ return -1;
+ p = is->ptr;
+ e = is->end;
+ }
+
+ s = p;
+
+ do {
+ switch(state) {
+ case 0:
+ /* check leading '.', ... */
+ if (p[0] == '.') {
+ if (p[1] == '\r' && p[2] == '\n') {
+ is->ptr = p+3;
+ *len = p-s;
+ *start = s;
+ is->mode = CAMEL_POP3_STREAM_EOD;
+ is->state = 0;
+
+ dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "last", *len, (int)*len, *start));
+
+ return 0;
+ }
+
+ /* If at start, just skip '.', else return data upto '.' but skip it */
+ if (p == s) {
+ s++;
+ p++;
+ } else {
+ is->ptr = p+1;
+ *len = p-s;
+ *start = s;
+ is->state = 1;
+
+ dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
+
+ return 1;
+ }
+ }
+ state = 1;
+ case 1:
+ /* Scan for sentinal */
+ while ((*p++)!='\n')
+ ;
+
+ if (p > e) {
+ p = e;
+ } else {
+ state = 0;
+ }
+ break;
+ }
+ } while ((e-p) >= 3);
+
+ is->state = state;
+ is->ptr = p;
+ *len = p-s;
+ *start = s;
+
+ dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
+
+ return 1;
+}