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-engine.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-engine.c')
-rw-r--r-- | camel/providers/imapp/camel-imapp-engine.c | 1156 |
1 files changed, 1156 insertions, 0 deletions
diff --git a/camel/providers/imapp/camel-imapp-engine.c b/camel/providers/imapp/camel-imapp-engine.c new file mode 100644 index 0000000000..f6cfcbf86c --- /dev/null +++ b/camel/providers/imapp/camel-imapp-engine.c @@ -0,0 +1,1156 @@ + +#include "config.h" + +#include <stdio.h> +#include <string.h> + +#include "camel-imapp-engine.h" +#include "camel-imapp-stream.h" +#include "camel-imapp-utils.h" +#include "camel-imapp-exception.h" + +#include <camel/camel-folder-summary.h> +#include <camel/camel-stream-mem.h> +#include <camel/camel-stream-null.h> +#include <camel/camel-data-wrapper.h> +#include <camel/camel-sasl.h> + +#include <ctype.h> + +#define e(x) +#define c(x) /* command build debug */ + +static void imap_engine_command_addv(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, const char *fmt, va_list ap); +static void imap_engine_command_complete(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic); + +struct _handler { + CamelIMAPPEngineFunc func; + void *data; + char name[1]; +}; + +static void +class_init(CamelIMAPPEngineClass *ieclass) +{ + ieclass->tagprefix = 'A'; + + camel_object_class_add_event((CamelObjectClass *)ieclass, "status", NULL); +} + +static void +object_init(CamelIMAPPEngine *ie, CamelIMAPPEngineClass *ieclass) +{ + ie->handlers = g_hash_table_new(g_str_hash, g_str_equal); + e_dlist_init(&ie->active); + e_dlist_init(&ie->queue); + + ie->tagprefix = ieclass->tagprefix; + ieclass->tagprefix++; + if (ieclass->tagprefix > 'Z') + ieclass->tagprefix = 'A'; + ie->tagprefix = 'A'; + + ie->state = IMAP_ENGINE_DISCONNECT; +} + +static void +handler_free(void *key, void *mem, void *data) +{ + g_free(mem); +} + +static void +object_finalise(CamelIMAPPEngine *ie, CamelIMAPPEngineClass *ieclass) +{ + /* FIXME: need to free the commands ... */ + while (camel_imapp_engine_iterate(ie, NULL) > 0) + ; + + g_hash_table_foreach(ie->handlers, (GHFunc)handler_free, NULL); + g_hash_table_destroy(ie->handlers); +} + +CamelType +camel_imapp_engine_get_type (void) +{ + static CamelType type = CAMEL_INVALID_TYPE; + + if (type == CAMEL_INVALID_TYPE) { + type = camel_type_register ( + camel_object_get_type (), + "CamelIMAPPEngine", + sizeof (CamelIMAPPEngine), + sizeof (CamelIMAPPEngineClass), + (CamelObjectClassInitFunc) class_init, + NULL, + (CamelObjectInitFunc) object_init, + (CamelObjectFinalizeFunc) object_finalise); + } + + return type; +} + +/* FIXME: check this, just taken from old code, not rfc */ +struct { + char *name; + guint32 flag; +} capa_table[] = { + { "IMAP4", IMAP_CAPABILITY_IMAP4 }, + { "IMAP4REV1", IMAP_CAPABILITY_IMAP4REV1 }, + { "STATUS", IMAP_CAPABILITY_STATUS } , + { "NAMESPACE", IMAP_CAPABILITY_NAMESPACE }, + { "UIDPLUS", IMAP_CAPABILITY_UIDPLUS }, + { "LITERAL+", IMAP_CAPABILITY_LITERALPLUS }, + { "STARTTLS", IMAP_CAPABILITY_STARTTLS }, +}; + + +/* +capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1" + [SPACE 1#capability] + ;; IMAP4rev1 servers which offer RFC 1730 + ;; compatibility MUST list "IMAP4" as the first + ;; capability. +*/ +static int resp_capability(CamelIMAPPEngine *ie, guint32 id, void *data) +{ + int tok, len, i; + unsigned char *token, *p, c; + + /* FIXME: handle auth types */ + + printf("got capability response:\n"); + while (1) { + tok = camel_imapp_stream_token(ie->stream, &token, &len); + switch(tok) { + case IMAP_TOK_TOKEN: + p = token; + while ((c = *p)) + *p++ = toupper(c); + case IMAP_TOK_INT: + printf(" cap: '%s'\n", token); + for (i=0;i<(int)(sizeof(capa_table)/sizeof(capa_table[0]));i++) + if (strcmp(token, capa_table[i].name)) + ie->capa |= capa_table[i].flag; + break; + case '\n': + return 0; + case IMAP_TOK_ERROR: + case IMAP_TOK_PROTOCOL: + camel_imapp_engine_skip(ie); + return -1; + default: + printf("Unknown Response token %02x '%c'\n", tok, isprint(tok)?tok:'.'); + } + } while (tok != '\n'); + + return 0; +} + +/* expunge command, id is expunged seq number */ +/* message_data ::= nz_number SPACE ("EXPUNGE" / + ("FETCH" SPACE msg_att)) */ +static int resp_expunge(CamelIMAPPEngine *ie, guint32 id, void *data) +{ + printf("message expunged: %d\n", id); + + return camel_imapp_engine_skip(ie); +} + +static int resp_flags(CamelIMAPPEngine *ie, guint32 id, void *data) +{ + guint32 flags; + + imap_parse_flags(ie->stream, &flags); + + printf("flags: %08x\n", flags); + + return camel_imapp_engine_skip(ie); +} + +/* exists count */ +static int resp_exists(CamelIMAPPEngine *ie, guint32 id, void *data) +{ + printf("messages exist: %d\n", id); + + if (ie->select_response) + ie->select_response->exists = id; + + return camel_imapp_engine_skip(ie); +} + +static int resp_recent(CamelIMAPPEngine *ie, guint32 id, void *data) +{ + printf("messages recent: %d\n", id); + + if (ie->select_response) + ie->select_response->recent = id; + + return camel_imapp_engine_skip(ie); +} + +static int resp_fetch(CamelIMAPPEngine *ie, guint32 id, void *data) +{ + struct _fetch_info *finfo; + + finfo = imap_parse_fetch(ie->stream); + imap_dump_fetch(finfo); + imap_free_fetch(finfo); + + return camel_imapp_engine_skip(ie); +} + +#if 0 +static int resp_list(CamelIMAPPEngine *ie, guint32 id, void *data) +{ + struct _list_info *linfo; + + linfo = imap_parse_list(ie->stream); + printf("list: '%s' (%c)\n", linfo->name, linfo->separator); + imap_free_list(linfo); + + return camel_imapp_engine_skip(ie); +} +#endif + +CamelIMAPPEngine * +camel_imapp_engine_new(CamelIMAPPStream *stream) +{ + CamelIMAPPEngine * volatile engine; + + engine = CAMEL_IMAPP_ENGINE (camel_object_new (CAMEL_IMAPP_ENGINE_TYPE)); + engine->stream = stream; + camel_object_ref((CamelObject *)stream); + + camel_imapp_engine_add_handler(engine, "CAPABILITY", resp_capability, engine); + + /* mailbox_data */ + camel_imapp_engine_add_handler(engine, "FLAGS", (CamelIMAPPEngineFunc)resp_flags, engine); + camel_imapp_engine_add_handler(engine, "EXISTS", (CamelIMAPPEngineFunc)resp_exists, engine); + camel_imapp_engine_add_handler(engine, "RECENT", (CamelIMAPPEngineFunc)resp_recent, engine); + +#if 0 + camel_imapp_engine_add_handler(engine, "LIST", (CamelIMAPPEngineFunc)resp_list, engine); + camel_imapp_engine_add_handler(engine, "LSUB", (CamelIMAPPEngineFunc)resp_list, engine); +#endif + /* message_data */ + camel_imapp_engine_add_handler(engine, "EXPUNGE", (CamelIMAPPEngineFunc)resp_expunge, engine); + camel_imapp_engine_add_handler(engine, "FETCH", (CamelIMAPPEngineFunc)resp_fetch, engine); + + /* TODO: move this to a driver:connect call? */ + CAMEL_TRY { + unsigned char *token; + unsigned int len; + int tok; + + tok = camel_imapp_stream_token(stream, &token, &len); + if (tok == '*') { + struct _status_info *sinfo = imap_parse_status(stream); + + switch (sinfo->result) { + case IMAP_OK: + engine->state = IMAP_ENGINE_CONNECT; + printf("Server connected ok: %s\n", sinfo->text); + break; + case IMAP_PREAUTH: + printf("pre-authenticated ...\n"); + engine->state = IMAP_ENGINE_AUTH; + break; + default: + imap_free_status(sinfo); + camel_exception_throw(1, "Server refused connection: %s", sinfo->text); + break; + } + imap_free_status(sinfo); + } else { + engine->state = IMAP_ENGINE_CONNECT; + printf("unknwon server greeting, ignored\n"); + camel_imapp_engine_skip(engine); + } + camel_imapp_engine_capabilities(engine); + } CAMEL_CATCH(ex) { + printf("connection failed: %s\n", ex->desc); + camel_object_unref((CamelObject *)engine); + engine = NULL; + } CAMEL_DONE; + + return engine; +} + +void +camel_imapp_engine_add_handler(CamelIMAPPEngine *imap, const char *response, CamelIMAPPEngineFunc func, void *data) +{ + struct _handler *h; + const unsigned char *p; + unsigned char *o, c; + + h = g_malloc0(sizeof(*h) + strlen(response)); + h->func = func; + h->data = data; + + p = response; + o = h->name; + while ((c = *p++)) + *o++ = toupper(c); + *o = 0; + + g_hash_table_insert(imap->handlers, h->name, h); +} + +int +camel_imapp_engine_capabilities(CamelIMAPPEngine *ie) +{ + CamelIMAPPCommand *ic; + + /* reset capabilities */ + ie->capa = 0; + + ic = camel_imapp_engine_command_new(ie, "CAPABILITY", NULL, "CAPABILITY"); + camel_imapp_engine_command_queue(ie, ic); + while (camel_imapp_engine_iterate(ie, ic)>0) + ; + camel_imapp_engine_command_free(ie, ic); + + return 0; +} + +/* skip the rest of the line of tokens */ +int +camel_imapp_engine_skip(CamelIMAPPEngine *imap) +{ + int tok; + unsigned char *token; + unsigned int len; + + do { + tok = camel_imapp_stream_token(imap->stream, &token, &len); + if (tok == IMAP_TOK_LITERAL) { + camel_imapp_stream_set_literal(imap->stream, len); + while ((tok = camel_imapp_stream_getl(imap->stream, &token, &len)) > 0) { + printf("Skip literal data '%.*s'\n", (int)len, token); + } + } + } while (tok != '\n' && tok >= 0); + + if (tok < 0) + return -1; + + return 0; +} + +/* handle any untagged responses */ +int +iterate_untagged(CamelIMAPPEngine *imap) +{ + unsigned int id, len; + unsigned char *token, *p, c; + int tok; + struct _handler *h; + struct _status_info *sinfo; + + e(printf("got untagged response\n")); + id = 0; + tok = camel_imapp_stream_token(imap->stream, &token, &len); + if (tok == IMAP_TOK_INT) { + id = strtoul(token, NULL, 10); + tok = camel_imapp_stream_token(imap->stream, &token, &len); + } + + if (tok == '\n') + camel_exception_throw(1, "truncated server response"); + + e(printf("Have token '%s' id %d\n", token, id)); + p = token; + while ((c = *p)) + *p++ = toupper(c); + + /* first, check for generic unsolicited response */ + h = g_hash_table_lookup(imap->handlers, token); + if (h) { + tok = h->func(imap, id, h->data); + if (tok < 0) + return tok; + return 1; + } + + /* TODO: apart from bye/preauth, these could be callbacks/events? */ + + /* now, check for status responses */ + switch (imap_tokenise(token, len)) { + case IMAP_BYE: + case IMAP_OK: + case IMAP_NO: + case IMAP_BAD: + case IMAP_PREAUTH: + /* TODO: validate which ones of these can happen as unsolicited responses */ + /* TODO: handle bye/preauth differently */ + /* FIXME: free sinfo */ + camel_imapp_stream_ungettoken(imap->stream, tok, token, len); + sinfo = imap_parse_status(imap->stream); + camel_object_trigger_event(imap, "status", sinfo); + imap_free_status(sinfo); +#if 0 + switch(sinfo->condition) { + case IMAP_READ_WRITE: + printf("folder is read-write\n"); + break; + case IMAP_READ_ONLY: + printf("folder is read-only\n"); + break; + case IMAP_UIDVALIDITY: + if (imap->select_response) + imap->select_response->uidvalidity = sinfo->u.uidvalidity; + break; +#if 0 + /* not defined yet ... */ + case IMAP_UIDNEXT: + printf("got uidnext for folder: %d\n", sinfo->u.uidnext); + break; +#endif + case IMAP_UNSEEN: + if (imap->select_response) + imap->select_response->unseen = sinfo->u.unseen; + break; + case IMAP_PERMANENTFLAGS: + if (imap->select_response) + imap->select_response->permanentflags = sinfo->u.permanentflags; + break; + case IMAP_ALERT: + printf("ALERT!: %s\n", sinfo->text); + break; + case IMAP_PARSE: + printf("PARSE: %s\n", sinfo->text); + break; + default: + break; + } +#endif + break; + default: + printf("unknown token: %s\n", token); + camel_imapp_engine_skip(imap); + /* unknown response, just ignore it */ + } + + return 1; +} + +/* handle any continuation requests + either data continuations, or auth continuation */ +int +iterate_continuation(CamelIMAPPEngine *imap) +{ + CamelIMAPPCommand *ic; + CamelIMAPPCommandPart *cp; + + printf("got continuation response\n"); + + ic = imap->literal; + imap->literal = NULL; + if (ic == NULL) { + camel_imapp_engine_skip(imap); + printf("got continuation response with no outstanding continuation requests?\n"); + return 1; + } + + printf("got continuation response for data\n"); + cp = ic->current; + switch(cp->type & CAMEL_IMAPP_COMMAND_MASK) { + case CAMEL_IMAPP_COMMAND_DATAWRAPPER: + printf("writing data wrapper to literal\n"); + camel_data_wrapper_write_to_stream((CamelDataWrapper *)cp->ob, (CamelStream *)imap->stream); + break; + case CAMEL_IMAPP_COMMAND_STREAM: + printf("writing stream to literal\n"); + camel_stream_write_to_stream((CamelStream *)cp->ob, (CamelStream *)imap->stream); + break; + case CAMEL_IMAPP_COMMAND_AUTH: { + CamelException *ex = camel_exception_new(); + char *resp; + unsigned char *token; + int tok, len; + + tok = camel_imapp_stream_token(imap->stream, &token, &len); + resp = camel_sasl_challenge_base64((CamelSasl *)cp->ob, token, ex); + if (camel_exception_is_set(ex)) + camel_exception_throw_ex(ex); + camel_exception_free(ex); + + printf("got auth continuation, feeding token '%s' back to auth mech\n", resp); + + camel_stream_write((CamelStream *)imap->stream, resp, strlen(resp)); + + /* we want to keep getting called until we get a status reponse from the server + ignore what sasl tells us */ + imap->literal = ic; + + break; } + default: + /* should we just ignore? */ + camel_exception_throw(1, "continuation response for non-continuation request"); + } + + camel_imapp_engine_skip(imap); + + cp = cp->next; + if (cp->next) { + ic->current = cp; + printf("next part of command \"A%05u: %s\"\n", ic->tag, cp->data); + camel_stream_printf((CamelStream *)imap->stream, "%s\r\n", cp->data); + if (cp->type & CAMEL_IMAPP_COMMAND_CONTINUATION) { + imap->literal = ic; + } else { + g_assert(cp->next->next == NULL); + } + } else { + printf("%p: queueing continuation\n", ic); + camel_stream_printf((CamelStream *)imap->stream, "\r\n"); + } + + if (imap->literal == NULL) { + ic = (CamelIMAPPCommand *)e_dlist_remhead(&imap->queue); + if (ic) { + printf("found outstanding op, queueing\n"); + camel_imapp_engine_command_queue(imap, ic); + } + } + + return 1; +} + +/* handle a completion line */ +int +iterate_completion(CamelIMAPPEngine *imap, unsigned char *token) +{ + CamelIMAPPCommand *ic; + unsigned int tag; + + if (token[0] != imap->tagprefix) + camel_exception_throw(1, "Server sent unexpected response: %s", token); + + tag = strtoul(token+1, NULL, 10); + ic = camel_imapp_engine_command_find_tag(imap, tag); + if (ic) { + printf("Got completion response for command %05u '%s'\n", ic->tag, ic->name); + printf("%p: removing command from qwueue, we were at '%s'\n", ic, ic->current->data); + printf("%p: removing command\n", ic); + e_dlist_remove((EDListNode *)ic); + if (imap->literal == ic) + imap->literal = NULL; + ic->status = imap_parse_status(imap->stream); + printf("got response code: %s\n", ic->status->text); + + /* TODO: remove this stuff and use a completion handler? */ + /* TODO: handle 'SELECT' command cleanup here */ + /* FIXME: have this use tokeniser, have this handle close/logout/select etc as well */ + /* ok response from login/authenticate, then we're in happy land */ + if ((!strcmp(ic->name, "LOGIN") || !strcmp(ic->name, "AUTHENTICATE")) + && ic->status->result == IMAP_OK) + imap->state = IMAP_ENGINE_AUTH; + + if (ic->complete) + ic->complete(imap, ic, ic->complete_data); + } else { + camel_exception_throw(1, "got response tag unexpectedly: %s", token); + } + + if (imap->literal != NULL) { + printf("Warning: continuation command '%s' finished with outstanding continuation\n", imap->literal->name); + ic = imap->literal; + /* set the command complete with a failure code? */ + e_dlist_remove((EDListNode *)ic); + imap->literal = NULL; + } + + ic = (CamelIMAPPCommand *)e_dlist_remhead(&imap->queue); + if (ic) { + printf("found outstanding op, queueing\n"); + camel_imapp_engine_command_queue(imap, ic); + } + + return 1; +} + + +/* Do work if there's any to do */ +int +camel_imapp_engine_iterate(CamelIMAPPEngine *imap, CamelIMAPPCommand *icwait) +/* throws IO,PARSE exception */ +{ + unsigned int len; + unsigned char *token; + int tok; + + if ((icwait && icwait->status != NULL) || e_dlist_empty(&imap->active)) + return 0; + + /* handle exceptions here? */ + + /* lock here? */ + + tok = camel_imapp_stream_token(imap->stream, &token, &len); + if (tok == '*') + iterate_untagged(imap); + else if (tok == IMAP_TOK_TOKEN) + iterate_completion(imap, token); + else if (tok == '+') + iterate_continuation(imap); + else + camel_exception_throw(1, "unexpected server response: %s", token); + + if (e_dlist_empty(&imap->active)) + return 0; + + return 1; +} + +CamelIMAPPCommand * +camel_imapp_engine_command_new(CamelIMAPPEngine *imap, const char *name, const char *select, const char *fmt, ...) +{ + CamelIMAPPCommand *ic; + va_list ap; + + ic = g_malloc0(sizeof(*ic)); + ic->tag = imap->tag++; + ic->name = name; + ic->mem = (CamelStreamMem *)camel_stream_mem_new(); + ic->select = g_strdup(select); + e_dlist_init(&ic->parts); + + if (fmt && fmt[0]) { + va_start(ap, fmt); + imap_engine_command_addv(imap, ic, fmt, ap); + va_end(ap); + } + + return ic; +} + +void +camel_imapp_engine_command_add(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, const char *fmt, ...) +{ + va_list ap; + + g_assert(ic->mem); /* gets reset on queue */ + + if (fmt && fmt[0]) { + va_start(ap, fmt); + imap_engine_command_addv(imap, ic, fmt, ap); + va_end(ap); + } +} + +void +camel_imapp_engine_command_complete(CamelIMAPPEngine *imap, struct _CamelIMAPPCommand *ic, CamelIMAPPCommandFunc func, void *data) +{ + ic->complete = func; + ic->complete_data = data; +} + +/* FIXME: make imap command's refcounted */ +void +camel_imapp_engine_command_free (CamelIMAPPEngine *imap, CamelIMAPPCommand *ic) +{ + CamelIMAPPCommandPart *cp, *cn; + + if (ic == NULL) + return; + + /* validity check - we cant' free items still in any queue ... */ + /* maybe we should just have another queue to keep them? */ + { + CamelIMAPPCommand *iw; + + iw = (CamelIMAPPCommand *)imap->active.head; + while (iw->next) { + if (iw == ic) + abort(); + iw = iw->next; + } + iw = (CamelIMAPPCommand *)imap->queue.head; + while (iw->next) { + if (iw == ic) + abort(); + iw = iw->next; + } + } + + if (ic->mem) + camel_object_unref((CamelObject *)ic->mem); + imap_free_status(ic->status); + g_free(ic->select); + + cp = (CamelIMAPPCommandPart *)ic->parts.head; + cn = cp->next; + while (cn) { + g_free(cp->data); + if (cp->ob) + camel_object_unref(cp->ob); + g_free(cp); + cp = cn; + cn = cn->next; + } + + g_free(ic); +} + +/* FIXME: error handling */ +void +camel_imapp_engine_command_queue(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic) +{ + CamelIMAPPCommandPart *cp; + + if (ic->mem) + imap_engine_command_complete(imap, ic); + + /* FIXME: remove select stuff */ + + /* see if we need to pre-queue a select command to select the right folder first */ + if (ic->select && (imap->last_select == NULL || strcmp(ic->select, imap->last_select) != 0)) { + CamelIMAPPCommand *select; + + /* of course ... we can't do anything like store/search if we have to select + first, because it'll mess up all the sequence numbers ... hrm ... bugger */ + + select = camel_imapp_engine_command_new(imap, "SELECT", NULL, "SELECT %s", ic->select); + g_free(imap->last_select); + imap->last_select = g_strdup(ic->select); + camel_imapp_engine_command_queue(imap, select); + /* how does it get freed? handle inside engine? */ + } + + /* first, check if command can be sent yet ... queue if not */ + if (imap->literal != NULL) { + printf("%p: queueing while literal active\n", ic); + e_dlist_addtail(&imap->queue, (EDListNode *)ic); + return; + } + + cp = (CamelIMAPPCommandPart *)ic->parts.head; + g_assert(cp); + ic->current = cp; + + /* how to handle exceptions here? */ + + printf("queueing command \"%c%05u %s\"\n", imap->tagprefix, ic->tag, cp->data); + camel_stream_printf((CamelStream *)imap->stream, "%c%05u %s\r\n", imap->tagprefix, ic->tag, cp->data); + + if (cp->type & CAMEL_IMAPP_COMMAND_CONTINUATION) { + printf("%p: active literal\n", ic); + g_assert(cp->next); + imap->literal = ic; + e_dlist_addtail(&imap->active, (EDListNode *)ic); + } else { + printf("%p: active non-literal\n", ic); + g_assert(cp->next && cp->next->next == NULL); + e_dlist_addtail(&imap->active, (EDListNode *)ic); + } +} + +CamelIMAPPCommand * +camel_imapp_engine_command_find (CamelIMAPPEngine *imap, const char *name) +{ + CamelIMAPPCommand *ic, *in; + + ic = imap->literal; + if (ic && strcmp(ic->name, name) == 0) + return ic; + + /* first, try active */ + ic = (CamelIMAPPCommand *)imap->active.head; + in = ic->next; + while (in) { + if (strcmp(ic->name, name) == 0) + return ic; + ic = in; + in = in->next; + } + + return NULL; +} + +CamelIMAPPCommand * +camel_imapp_engine_command_find_tag(CamelIMAPPEngine *imap, unsigned int tag) +{ + CamelIMAPPCommand *ic, *in; + + ic = imap->literal; + if (ic && ic->tag == tag) + return ic; + + ic = (CamelIMAPPCommand *)imap->active.head; + in = ic->next; + while (in) { + if (ic->tag == tag) + return ic; + ic = in; + in = in->next; + } + + return NULL; +} + +/* ********************************************************************** */ + +CamelIMAPPSelectResponse * +camel_imapp_engine_select(CamelIMAPPEngine *imap, const char *name) +{ + CamelIMAPPSelectResponse * volatile resp; + CamelIMAPPCommand * volatile ic = NULL; + + resp = g_malloc0(sizeof(*resp)); + imap->select_response = resp; + + CAMEL_TRY { + ic = camel_imapp_engine_command_new(imap, "SELECT", NULL, "SELECT %s", name); + camel_imapp_engine_command_queue(imap, ic); + while (camel_imapp_engine_iterate(imap, ic) > 0) + ; + + if (ic->status->result != IMAP_OK) + camel_exception_throw(1, "select failed: %s", ic->status->text); + resp->status = ic->status; + ic->status = NULL; + } CAMEL_CATCH (e) { + camel_imapp_engine_command_free(imap, ic); + camel_imapp_engine_select_free(imap, resp); + imap->select_response = NULL; + camel_exception_throw_ex(e); + } CAMEL_DONE; + + camel_imapp_engine_command_free(imap, ic); + imap->select_response = NULL; + + return resp; +} + +void +camel_imapp_engine_select_free(CamelIMAPPEngine *imap, CamelIMAPPSelectResponse *select) +{ + if (select) { + imap_free_status(select->status); + g_free(select); + } +} + +/* ********************************************************************** */ + +static void +imap_engine_command_add_part(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, camel_imapp_command_part_t type, CamelObject *ob) +{ + CamelIMAPPCommandPart *cp; + CamelStreamNull *null; + unsigned int ob_size = 0; + + switch(type & CAMEL_IMAPP_COMMAND_MASK) { + case CAMEL_IMAPP_COMMAND_DATAWRAPPER: + case CAMEL_IMAPP_COMMAND_STREAM: + null = (CamelStreamNull *)camel_stream_null_new(); + if ( (type & CAMEL_IMAPP_COMMAND_MASK) == CAMEL_IMAPP_COMMAND_DATAWRAPPER) { + camel_data_wrapper_write_to_stream((CamelDataWrapper *)ob, (CamelStream *)null); + } else { + camel_stream_reset((CamelStream *)ob); + camel_stream_write_to_stream((CamelStream *)ob, (CamelStream *)null); + camel_stream_reset((CamelStream *)ob); + } + type |= CAMEL_IMAPP_COMMAND_CONTINUATION; + camel_object_ref(ob); + ob_size = null->written; + camel_object_unref((CamelObject *)null); + camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size); + break; + case CAMEL_IMAPP_COMMAND_AUTH: + /* we presume we'll need to get additional data only if we're not authenticated yet */ + camel_object_ref(ob); + camel_stream_printf((CamelStream *)ic->mem, "%s", ((CamelSasl *)ob)->mech); + if (!camel_sasl_authenticated((CamelSasl *)ob)) + type |= CAMEL_IMAPP_COMMAND_CONTINUATION; + break; + default: + ob_size = 0; + } + + cp = g_malloc0(sizeof(*cp)); + cp->type = type; + cp->ob_size = ob_size; + cp->ob = ob; + cp->data_size = ic->mem->buffer->len; + cp->data = g_malloc(cp->data_size+1); + memcpy(cp->data, ic->mem->buffer->data, cp->data_size); + cp->data[cp->data_size] = 0; + + camel_stream_reset((CamelStream *)ic->mem); + /* FIXME: hackish? */ + g_byte_array_set_size(ic->mem->buffer, 0); + + e_dlist_addtail(&ic->parts, (EDListNode *)cp); +} + +static int len(EDList *list) +{ + int count = 0; + EDListNode *n = list->head; + + while (n->next) { + n = n->next; + count++; + } + return count; +} + +static void +imap_engine_command_complete(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic) +{ + c(printf("completing command buffer is [%d] '%.*s'\n", ic->mem->buffer->len, (int)ic->mem->buffer->len, ic->mem->buffer->data)); + c(printf("command has %d parts\n", len(&ic->parts))); + if (ic->mem->buffer->len > 0) + imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_SIMPLE, NULL); + + c(printf("command has %d parts\n", len(&ic->parts))); + + camel_object_unref((CamelObject *)ic->mem); + ic->mem = NULL; +} + +static void +imap_engine_command_addv(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, const char *fmt, va_list ap) +{ + const unsigned char *p, *ps, *start; + unsigned char c; + unsigned int width; + char ch; + int llong; + int left; + int fill; + int zero; + char *s; + int d; + long int l; + guint32 f; + CamelStream *S; + CamelDataWrapper *D; + CamelSasl *A; + char buffer[16]; + + c(printf("adding command, fmt = '%s'\n", fmt)); + + p = fmt; + ps = fmt; + while ( ( c = *p++ ) ) { + switch(c) { + case '%': + if (*p == '%') { + camel_stream_write((CamelStream *)ic->mem, ps, p-ps); + p++; + ps = p; + } else { + camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1); + start = p-1; + width = 0; + left = FALSE; + fill = FALSE; + zero = FALSE; + llong = FALSE; + + do { + c = *p++; + if (c == '0') + zero = TRUE; + else if ( c== '-') + left = TRUE; + else + break; + } while (c); + + do { + if (isdigit(c)) + width = width * 10 + (c-'0'); + else + break; + } while ((c = *p++)); + + if (c == 'l') { + llong = TRUE; + c = *p++; + } + + switch(c) { + case 'A': /* auth object - sasl auth, treat as special kind of continuation */ + A = va_arg(ap, CamelSasl *); + imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_AUTH, (CamelObject *)A); + break; + case 'S': /* stream */ + S = va_arg(ap, CamelStream *); + c(printf("got stream '%p'\n", S)); + imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_STREAM, (CamelObject *)S); + break; + case 'D': /* datawrapper */ + D = va_arg(ap, CamelDataWrapper *); + c(printf("got data wrapper '%p'\n", D)); + imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_DATAWRAPPER, (CamelObject *)D); + break; + case 't': /* token */ + s = va_arg(ap, char *); + camel_stream_write((CamelStream *)ic->mem, s, strlen(s)); + break; + case 's': /* simple string */ + s = va_arg(ap, char *); + c(printf("got string '%s'\n", s)); + /* FIXME: escpae chars, convert to literal or literal+, etc */ + camel_stream_printf((CamelStream *)ic->mem, "\"%s\"", s); + break; + case 'f': /* imap folder name */ + s = va_arg(ap, char *); + c(printf("got folder '%s'\n", s)); + /* FIXME: encode folder name */ + /* FIXME: namespace? */ + camel_stream_printf((CamelStream *)ic->mem, "\"%s\"", s?s:""); + break; + case 'F': /* IMAP flags set */ + f = va_arg(ap, guint32); + imap_write_flags((CamelStream *)ic->mem, f); + break; + case 'c': + d = va_arg(ap, int); + ch = d; + camel_stream_write((CamelStream *)ic->mem, &ch, 1); + break; + case 'd': /* int/unsigned */ + case 'u': + if (llong) { + l = va_arg(ap, long int); + c(printf("got long int '%d'\n", (int)l)); + memcpy(buffer, start, p-start); + buffer[p-start] = 0; + camel_stream_printf((CamelStream *)ic->mem, buffer, l); + } else { + d = va_arg(ap, int); + c(printf("got int '%d'\n", d)); + memcpy(buffer, start, p-start); + buffer[p-start] = 0; + camel_stream_printf((CamelStream *)ic->mem, buffer, d); + } + break; + } + + ps = p; + } + break; + case '\\': /* only for \\ really, we dont support \n\r etc at all */ + c = *p; + if (c) { + g_assert(c == '\\'); + camel_stream_write((CamelStream *)ic->mem, ps, p-ps); + p++; + ps = p; + } + } + } + + camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1); +} + + +/* here temporarily while its experimental */ + + +#ifdef ENABLE_THREADS +#include <pthread.h> + +static pthread_key_t handler_key = 0; + +void camel_exception_setup(void) +{ + pthread_key_create(&handler_key, NULL); +} + +#else +/* this is per-thread in threaded mode */ +static struct _CamelExceptionEnv *handler = NULL; + +void camel_exception_setup(void) +{ +} +#endif + +void +camel_exception_try(struct _CamelExceptionEnv *env) +{ +#ifdef ENABLE_THREADS + struct _CamelExceptionEnv *handler; + + handler = pthread_getspecific(handler_key); +#endif + env->parent = handler; + handler = env; + env->ex = NULL; + +#ifdef ENABLE_THREADS + pthread_setspecific(handler_key, handler); +#endif +} + +void +camel_exception_throw_ex(CamelException *ex) +{ + struct _CamelExceptionEnv *env; +#ifdef ENABLE_THREADS + struct _CamelExceptionEnv *handler; + + handler = pthread_getspecific(handler_key); +#endif + printf("throwing exception '%s'\n", ex->desc); + + env = handler; + if (env != NULL) { + env->ex = ex; + handler = env->parent; +#ifdef ENABLE_THREADS + pthread_setspecific(handler_key, handler); +#endif + longjmp(env->env, ex->id); + } else { + g_warning("Uncaught exception: %s\n", ex->desc); + /* we just crash and burn, this is a code problem */ + /* we dont use g_assert_not_reached() since its not a noreturn function */ + abort(); + } +} + +void +camel_exception_throw(int id, char *fmt, ...) +{ + CamelException *ex; + va_list ap; + + ex = camel_exception_new(); + ex->id = id; + va_start(ap, fmt); + ex->desc = g_strdup_vprintf(fmt, ap); + va_end(ap); + + camel_exception_throw_ex(ex); +} + +void +camel_exception_drop(struct _CamelExceptionEnv *env) +{ +#ifdef ENABLE_THREADS + pthread_setspecific(handler_key, env->parent); +#else + handler = env->parent; +#endif +} + +void +camel_exception_done(struct _CamelExceptionEnv *env) +{ +#ifdef ENABLE_THREADS + pthread_setspecific(handler_key, env->parent); +#else + handler = env->parent; +#endif + if (env->ex != NULL) { + camel_exception_free(env->ex); + } +} |