diff options
author | Michael Zucci <zucchi@src.gnome.org> | 2003-08-02 05:07:43 +0800 |
---|---|---|
committer | Michael Zucci <zucchi@src.gnome.org> | 2003-08-02 05:07:43 +0800 |
commit | e2cd78ca9b706f30f51a648db9da220e9b5a68a2 (patch) | |
tree | a1a211c5f2e2447b22eb2d610c25f58173961c6f /camel/providers/imapp/camel-imapp-stream.c | |
parent | 330ecafed407197d31c30170e1c76674d3ed91e8 (diff) | |
download | gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.gz gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.bz2 gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.lz gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.xz gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.zst gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.zip |
experimental, non-working imap implementation
svn path=/trunk/; revision=22061
Diffstat (limited to 'camel/providers/imapp/camel-imapp-stream.c')
-rw-r--r-- | camel/providers/imapp/camel-imapp-stream.c | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/camel/providers/imapp/camel-imapp-stream.c b/camel/providers/imapp/camel-imapp-stream.c new file mode 100644 index 0000000000..a9567bf4f8 --- /dev/null +++ b/camel/providers/imapp/camel-imapp-stream.c @@ -0,0 +1,761 @@ +/* -*- 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 <string.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +#include <glib.h> + +#include <camel/camel-stream-mem.h> + +#include "camel-imapp-stream.h" +#include "camel-imapp-exception.h" + +#define t(x) +#define io(x) x + +static void setup_table(void); + +static CamelObjectClass *parent_class = NULL; + +/* Returns the class for a CamelStream */ +#define CS_CLASS(so) CAMEL_IMAPP_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so)) + +#define CAMEL_IMAPP_STREAM_SIZE (4096) +#define CAMEL_IMAPP_STREAM_TOKEN (4096) /* maximum token size */ + +static int +stream_fill(CamelIMAPPStream *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_IMAPP_STREAM_SIZE - (is->end - is->buf)); + if (left > 0) { + is->end += left; + io(printf("camel_imapp_read: buffer is '%.*s'\n", is->end - is->ptr, is->ptr)); + return is->end - is->ptr; + } else { + io(printf("camel_imapp_read: -1\n")); + return -1; + } + } + + printf("camel_imapp_read: 0\n"); + + return 0; +} + +static ssize_t +stream_read(CamelStream *stream, char *buffer, size_t n) +{ + CamelIMAPPStream *is = (CamelIMAPPStream *)stream; + ssize_t max; + + if (is->literal == 0 || n == 0) + return 0; + + max = is->end - is->ptr; + if (max > 0) { + max = MIN(max, is->literal); + max = MIN(max, n); + memcpy(buffer, is->ptr, max); + is->ptr += max; + } else { + max = MIN(is->literal, n); + max = camel_stream_read(is->source, buffer, max); + if (max <= 0) + return max; + } + + is->literal -= max; + + return max; +} + +static ssize_t +stream_write(CamelStream *stream, const char *buffer, size_t n) +{ + CamelIMAPPStream *is = (CamelIMAPPStream *)stream; + + 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) +{ + CamelIMAPPStream *is = (CamelIMAPPStream *)stream; + + return is->literal == 0; +} + +static int +stream_reset(CamelStream *stream) +{ + /* nop? reset literal mode? */ + return 0; +} + +static void +camel_imapp_stream_class_init (CamelStreamClass *camel_imapp_stream_class) +{ + CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_imapp_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_imapp_stream_init(CamelIMAPPStream *is, CamelIMAPPStreamClass *isclass) +{ + /* +1 is room for appending a 0 if we need to for a token */ + is->ptr = is->end = is->buf = g_malloc(CAMEL_IMAPP_STREAM_SIZE+1); + is->tokenptr = is->tokenbuf = g_malloc(CAMEL_IMAPP_STREAM_SIZE+1); + is->tokenend = is->tokenbuf + CAMEL_IMAPP_STREAM_SIZE; +} + +static void +camel_imapp_stream_finalise(CamelIMAPPStream *is) +{ + g_free(is->buf); + if (is->source) + camel_object_unref((CamelObject *)is->source); +} + +CamelType +camel_imapp_stream_get_type (void) +{ + static CamelType camel_imapp_stream_type = CAMEL_INVALID_TYPE; + + if (camel_imapp_stream_type == CAMEL_INVALID_TYPE) { + setup_table(); + camel_imapp_stream_type = camel_type_register( camel_stream_get_type(), + "CamelIMAPPStream", + sizeof( CamelIMAPPStream ), + sizeof( CamelIMAPPStreamClass ), + (CamelObjectClassInitFunc) camel_imapp_stream_class_init, + NULL, + (CamelObjectInitFunc) camel_imapp_stream_init, + (CamelObjectFinalizeFunc) camel_imapp_stream_finalise ); + } + + return camel_imapp_stream_type; +} + +/** + * camel_imapp_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_imapp_stream_new(CamelStream *source) +{ + CamelIMAPPStream *is; + + is = (CamelIMAPPStream *)camel_object_new(camel_imapp_stream_get_type ()); + camel_object_ref((CamelObject *)source); + is->source = source; + + return (CamelStream *)is; +} + + +/* + From rfc2060 + +ATOM_CHAR ::= <any CHAR except atom_specials> + +atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / + quoted_specials + +CHAR ::= <any 7-bit US-ASCII character except NUL, + 0x01 - 0x7f> + +CTL ::= <any ASCII control character and DEL, + 0x00 - 0x1f, 0x7f> + +SPACE ::= <ASCII SP, space, 0x20> + +list_wildcards ::= "%" / "*" + +quoted_specials ::= <"> / "\" +*/ + +static unsigned char imap_specials[256] = { +/* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 20 */0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, +/* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +/* 40 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +/* 50 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, +/* 60 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +/* 70 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define imap_is_atom(c) ((imap_specials[(c)&0xff] & 0x01) != 0) +#define imap_is_simple(c) ((imap_specials[(c)&0xff] & 0x02) != 0) +#define imap_not_id(c) ((imap_specials[(c)&0xff] & 0x04) != 0) + +/* could be pregenerated, but this is cheap */ +static struct { + unsigned char *chars; + unsigned char mask; +} is_masks[] = { + { "\n*()[]+", 2 }, + { " \r\n()[]+", 4 }, +}; + +static void setup_table(void) +{ + int i; + unsigned char *p, c; + + for (i=0;i<(int)(sizeof(is_masks)/sizeof(is_masks[0]));i++) { + p = is_masks[i].chars; + while ((c = *p++)) + imap_specials[c] |= is_masks[i].mask; + } +} + +#if 0 + +static int +skip_ws(CamelIMAPPStream *is, unsigned char *pp, unsigned char *pe) +{ + register unsigned char c, *p; + unsigned char *e; + + p = is->ptr; + e = is->end; + + do { + while (p >= e ) { + is->ptr = p; + if (stream_fill(is) == IMAP_TOK_ERROR) + return IMAP_TOK_ERROR; + p = is->ptr; + e = is->end; + } + c = *p++; + } while (c == ' ' || c == '\r'); + + is->ptr = p; + is->end = e; + + return c; +} +#endif + +/* FIXME: these should probably handle it themselves, + and get rid of the token interface? */ +int +camel_imapp_stream_atom(CamelIMAPPStream *is, unsigned char **data, unsigned int *lenp) +{ + unsigned char *p, c; + + /* this is only 'approximate' atom */ + switch(camel_imapp_stream_token(is, data, lenp)) { + case IMAP_TOK_TOKEN: + p = *data; + while ((c = *p)) + *p++ = toupper(c); + case IMAP_TOK_INT: + return 0; + case IMAP_TOK_ERROR: + return IMAP_TOK_ERROR; + default: + camel_exception_throw(1, "expecting atom"); + printf("expecting atom!\n"); + return IMAP_TOK_PROTOCOL; + } +} + +/* gets an atom, a quoted_string, or a literal */ +int +camel_imapp_stream_astring(CamelIMAPPStream *is, unsigned char **data) +{ + unsigned char *p, *start; + unsigned int len, inlen; + + switch(camel_imapp_stream_token(is, data, &len)) { + case IMAP_TOK_TOKEN: + case IMAP_TOK_INT: + case IMAP_TOK_STRING: + return 0; + case IMAP_TOK_LITERAL: + /* FIXME: just grow buffer */ + if (len >= CAMEL_IMAPP_STREAM_TOKEN) { + camel_exception_throw(1, "astring: literal too long"); + printf("astring too long\n"); + return IMAP_TOK_PROTOCOL; + } + p = is->tokenptr; + camel_imapp_stream_set_literal(is, len); + do { + len = camel_imapp_stream_getl(is, &start, &inlen); + if (len < 0) + return len; + memcpy(p, start, inlen); + p += inlen; + } while (len > 0); + *data = is->tokenptr; + return 0; + case IMAP_TOK_ERROR: + /* wont get unless no exception hanlder*/ + return IMAP_TOK_ERROR; + default: + camel_exception_throw(1, "expecting astring"); + printf("expecting astring!\n"); + return IMAP_TOK_PROTOCOL; + } +} + +/* check for NIL or (small) quoted_string or literal */ +int +camel_imapp_stream_nstring(CamelIMAPPStream *is, unsigned char **data) +{ + unsigned char *p, *start; + unsigned int len, inlen; + + switch(camel_imapp_stream_token(is, data, &len)) { + case IMAP_TOK_STRING: + return 0; + case IMAP_TOK_LITERAL: + /* FIXME: just grow buffer */ + if (len >= CAMEL_IMAPP_STREAM_TOKEN) { + camel_exception_throw(1, "nstring: literal too long"); + return IMAP_TOK_PROTOCOL; + } + p = is->tokenptr; + camel_imapp_stream_set_literal(is, len); + do { + len = camel_imapp_stream_getl(is, &start, &inlen); + if (len < 0) + return len; + memcpy(p, start, inlen); + p += inlen; + } while (len > 0); + *data = is->tokenptr; + return 0; + case IMAP_TOK_TOKEN: + p = *data; + if (toupper(p[0]) == 'N' && toupper(p[1]) == 'I' && toupper(p[2]) == 'L' && p[3] == 0) { + *data = NULL; + return 0; + } + default: + camel_exception_throw(1, "expecting nstring"); + return IMAP_TOK_PROTOCOL; + case IMAP_TOK_ERROR: + /* we'll never get this unless there are no exception handlers anyway */ + return IMAP_TOK_ERROR; + + } +} + +/* parse an nstring as a stream */ +int +camel_imapp_stream_nstring_stream(CamelIMAPPStream *is, CamelStream **stream) +/* throws IO,PARSE exception */ +{ + unsigned char *token; + unsigned int len; + int ret = 0; + CamelStream * volatile mem = NULL; + + *stream = NULL; + + CAMEL_TRY { + switch(camel_imapp_stream_token(is, &token, &len)) { + case IMAP_TOK_STRING: + mem = camel_stream_mem_new_with_buffer(token, len); + *stream = mem; + break; + case IMAP_TOK_LITERAL: + /* if len is big, we could automatically use a file backing */ + camel_imapp_stream_set_literal(is, len); + mem = camel_stream_mem_new(); + if (camel_stream_write_to_stream((CamelStream *)is, mem) == -1) + camel_exception_throw(1, "nstring: io error: %s", strerror(errno)); + camel_stream_reset(mem); + *stream = mem; + break; + case IMAP_TOK_TOKEN: + if (toupper(token[0]) == 'N' && toupper(token[1]) == 'I' && toupper(token[2]) == 'L' && token[3] == 0) { + *stream = NULL; + break; + } + default: + ret = -1; + camel_exception_throw(1, "nstring: token not string"); + } + } CAMEL_CATCH(ex) { + if (mem) + camel_object_unref((CamelObject *)mem); + camel_exception_throw_ex(ex); + } CAMEL_DONE; + + /* never reaches here anyway */ + return ret; +} + +guint32 +camel_imapp_stream_number(CamelIMAPPStream *is) +{ + unsigned char *token; + unsigned int len; + + if (camel_imapp_stream_token(is, &token, &len) != IMAP_TOK_INT) { + camel_exception_throw(1, "expecting number"); + return 0; + } + + return strtoul(token, 0, 10); +} + +int +camel_imapp_stream_text(CamelIMAPPStream *is, unsigned char **text) +{ + GByteArray *build = g_byte_array_new(); + unsigned char *token; + unsigned int len; + int tok; + + CAMEL_TRY { + while (is->unget > 0) { + switch (is->unget_tok) { + case IMAP_TOK_TOKEN: + case IMAP_TOK_STRING: + case IMAP_TOK_INT: + g_byte_array_append(build, is->unget_token, is->unget_len); + g_byte_array_append(build, " ", 1); + default: /* invalid, but we'll ignore */ + break; + } + is->unget--; + } + + do { + tok = camel_imapp_stream_gets(is, &token, &len); + if (tok < 0) + camel_exception_throw(1, "io error: %s", strerror(errno)); + if (len) + g_byte_array_append(build, token, len); + } while (tok > 0); + } CAMEL_CATCH(ex) { + *text = NULL; + g_byte_array_free(build, TRUE); + camel_exception_throw_ex(ex); + } CAMEL_DONE; + + g_byte_array_append(build, "", 1); + *text = build->data; + g_byte_array_free(build, FALSE); + + return 0; +} + +/* Get one token from the imap stream */ +camel_imapp_token_t +/* throws IO,PARSE exception */ +camel_imapp_stream_token(CamelIMAPPStream *is, unsigned char **data, unsigned int *len) +{ + register unsigned char c, *p, *o, *oe; + unsigned char *e; + unsigned int literal; + int digits; + + if (is->unget > 0) { + is->unget--; + *data = is->unget_token; + *len = is->unget_len; + /*printf("token UNGET '%c' %s\n", is->unget_tok, is->unget_token);*/ + return is->unget_tok; + } + + if (is->literal > 0) + g_warning("stream_token called with literal %d", is->literal); + + p = is->ptr; + e = is->end; + + /* skip whitespace/prefill buffer */ + do { + while (p >= e ) { + is->ptr = p; + if (stream_fill(is) == IMAP_TOK_ERROR) + goto io_error; + p = is->ptr; + e = is->end; + } + c = *p++; + } while (c == ' ' || c == '\r'); + + /*strchr("\n*()[]+", c)*/ + if (imap_is_simple(c)) { + is->ptr = p; + t(printf("token '%c'\n", c)); + return c; + } else if (c == '{') { + literal = 0; + *data = p; + while (1) { + while (p < e) { + c = *p++; + if (isdigit(c) && literal < (UINT_MAX/10)) { + literal = literal * 10 + (c - '0'); + } else if (c == '}') { + while (1) { + while (p < e) { + c = *p++; + if (c == '\n') { + *len = literal; + is->ptr = p; + is->literal = literal; + t(printf("token LITERAL %d\n", literal)); + return IMAP_TOK_LITERAL; + } + } + is->ptr = p; + if (stream_fill(is) == IMAP_TOK_ERROR) + goto io_error; + p = is->ptr; + e = is->end; + } + } else { + if (isdigit(c)) + printf("Protocol error: literal too big\n"); + else + printf("Protocol error: literal contains invalid char %02x '%c'\n", c, isprint(c)?c:c); + goto protocol_error; + } + } + is->ptr = p; + if (stream_fill(is) == IMAP_TOK_ERROR) + goto io_error; + p = is->ptr; + e = is->end; + } + } else if (c == '"') { + o = is->tokenptr; + oe = is->tokenptr + CAMEL_IMAPP_STREAM_TOKEN - 1; + while (1) { + while (p < e) { + c = *p++; + if (c == '\\') { + while (p >= e) { + is->ptr = p; + if (stream_fill(is) == IMAP_TOK_ERROR) + goto io_error; + p = is->ptr; + e = is->end; + } + c = *p++; + } else if (c == '\"') { + is->ptr = p; + *o = 0; + *data = is->tokenbuf; + *len = o - is->tokenbuf; + t(printf("token STRING '%s'\n", is->tokenbuf)); + return IMAP_TOK_STRING; + } + + if (c == '\n' || c == '\r' || o>=oe) { + if (o >= oe) + printf("Protocol error: string too long\n"); + else + printf("Protocol error: truncated string\n"); + goto protocol_error; + } else { + *o++ = c; + } + } + is->ptr = p; + if (stream_fill(is) == IMAP_TOK_ERROR) + goto io_error; + p = is->ptr; + e = is->end; + } + } else { + o = is->tokenptr; + oe = is->tokenptr + CAMEL_IMAPP_STREAM_TOKEN - 1; + digits = isdigit(c); + *o++ = c; + while (1) { + while (p < e) { + c = *p++; + /*if (strchr(" \r\n*()[]+", c) != NULL) {*/ + if (imap_not_id(c)) { + if (c == ' ' || c == '\r') + is->ptr = p; + else + is->ptr = p-1; + *o = 0; + *data = is->tokenbuf; + *len = o - is->tokenbuf; + t(printf("token TOKEN '%s'\n", is->tokenbuf)); + return digits?IMAP_TOK_INT:IMAP_TOK_TOKEN; + } else if (o < oe) { + digits &= isdigit(c); + *o++ = c; + } else { + printf("Protocol error: token too long\n"); + goto protocol_error; + } + } + is->ptr = p; + if (stream_fill(is) == IMAP_TOK_ERROR) + goto io_error; + p = is->ptr; + e = is->end; + } + } + + /* Had an i/o erorr */ +io_error: + printf("Got io error\n"); + camel_exception_throw(1, "io error"); + return IMAP_TOK_ERROR; + + /* Protocol error, skip until next lf? */ +protocol_error: + printf("Got protocol error\n"); + + if (c == '\n') + is->ptr = p-1; + else + is->ptr = p; + + camel_exception_throw(1, "protocol error"); + return IMAP_TOK_PROTOCOL; +} + +void +camel_imapp_stream_ungettoken(CamelIMAPPStream *is, camel_imapp_token_t tok, unsigned char *token, unsigned int len) +{ + /*printf("ungettoken: '%c' '%s'\n", tok, token);*/ + is->unget_tok = tok; + is->unget_token = token; + is->unget_len = len; + is->unget++; +} + +/* returns -1 on error, 0 if last lot of data, >0 if more remaining */ +int camel_imapp_stream_gets(CamelIMAPPStream *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; + + return end == NULL?1:0; +} + +void camel_imapp_stream_set_literal(CamelIMAPPStream *is, unsigned int literal) +{ + is->literal = literal; +} + +/* returns -1 on erorr, 0 if last data, >0 if more data left */ +int camel_imapp_stream_getl(CamelIMAPPStream *is, unsigned char **start, unsigned int *len) +{ + int max; + + *len = 0; + + if (is->literal > 0) { + max = is->end - is->ptr; + if (max == 0) { + max = stream_fill(is); + if (max <= 0) + return max; + } + + max = MIN(max, is->literal); + *start = is->ptr; + *len = max; + is->ptr += max; + is->literal -= max; + } + + if (is->literal > 0) + return 1; + + return 0; +} |