#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);
e_dlist_init(&ie->done);
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 */
static 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);
e_dlist_addtail(&imap->done, (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);
e_dlist_addtail(&imap->done, (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;
int found = 0;
iw = (CamelIMAPPCommand *)imap->active.head;
while (iw->next) {
if (iw == ic) {
found = 1;
g_warning("command '%s' still in active queue", iw->name);
break;
}
iw = iw->next;
}
iw = (CamelIMAPPCommand *)imap->queue.head;
while (iw->next) {
if (iw == ic) {
found = 1;
g_warning("command '%s' still in waiting queue", iw->name);
break;
}
iw = iw->next;
}
iw = (CamelIMAPPCommand *)imap->done.head;
while (iw->next) {
if (iw == ic) {
found = 1;
break;
}
iw = iw->next;
}
if (!found) {
g_warning("command '%s' not found anywhere", ic->name);
abort();
}
}
e_dlist_remove((EDListNode *)ic);
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);
}
}