aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/imap4/camel-imap-command.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/providers/imap4/camel-imap-command.c')
-rw-r--r--camel/providers/imap4/camel-imap-command.c661
1 files changed, 661 insertions, 0 deletions
diff --git a/camel/providers/imap4/camel-imap-command.c b/camel/providers/imap4/camel-imap-command.c
new file mode 100644
index 0000000000..4dfcb8faee
--- /dev/null
+++ b/camel/providers/imap4/camel-imap-command.c
@@ -0,0 +1,661 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Camel
+ * Copyright (C) 1999-2004 Jeffrey Stedfast
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <camel/camel-stream-null.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-mime-filter-crlf.h>
+
+#include "camel-imap-stream.h"
+#include "camel-imap-engine.h"
+/*#include "camel-imap-folder.h"*/
+#include "camel-imap-specials.h"
+
+#include "camel-imap-command.h"
+
+
+#define d(x) x
+
+
+enum {
+ IMAP_STRING_ATOM,
+ IMAP_STRING_QSTRING,
+ IMAP_STRING_LITERAL,
+};
+
+static int
+imap_string_get_type (const char *str)
+{
+ int type = 0;
+
+ while (*str) {
+ if (!is_atom (*str)) {
+ if (is_qsafe (*str))
+ type = IMAP_STRING_QSTRING;
+ else
+ return IMAP_STRING_LITERAL;
+ }
+ str++;
+ }
+
+ return type;
+}
+
+static gboolean
+imap_string_is_atom_safe (const char *str)
+{
+ while (is_atom (*str))
+ str++;
+
+ return *str == '\0';
+}
+
+static gboolean
+imap_string_is_quote_safe (const char *str)
+{
+ while (is_qsafe (*str))
+ str++;
+
+ return *str == '\0';
+}
+
+static size_t
+camel_imap_literal_length (CamelIMAPLiteral *literal)
+{
+ CamelStream *stream, *null;
+ CamelMimeFilter *crlf;
+ size_t len;
+
+ if (literal->type == CAMEL_IMAP_LITERAL_STRING)
+ return strlen (literal->literal.string);
+
+ null = camel_stream_null_new ();
+ crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
+ stream = (CamelStream *) camel_stream_filter_new_with_stream (null);
+ camel_stream_filter_add ((CamelStreamFilter *) stream, crlf);
+ camel_object_unref (crlf);
+
+ switch (literal->type) {
+ case CAMEL_IMAP_LITERAL_STREAM:
+ camel_stream_write_to_stream (literal->literal.stream, stream);
+ camel_stream_reset (literal->literal.stream);
+ break;
+ case CAMEL_IMAP_LITERAL_WRAPPER:
+ camel_data_wrapper_write_to_stream (literal->literal.wrapper, stream);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ len = ((CamelStreamNull *) null)->written;
+
+ camel_object_unref (stream);
+ camel_object_unref (null);
+
+ return len;
+}
+
+static CamelIMAPCommandPart *
+command_part_new (void)
+{
+ CamelIMAPCommandPart *part;
+
+ part = g_new (CamelIMAPCommandPart, 1);
+ part->next = NULL;
+ part->buffer = NULL;
+ part->buflen = 0;
+ part->literal = NULL;
+
+ return part;
+}
+
+static void
+imap_command_append_string (CamelIMAPEngine *engine, CamelIMAPCommandPart **tail, GString *str, const char *string)
+{
+ CamelIMAPCommandPart *part;
+ CamelIMAPLiteral *literal;
+
+ switch (imap_string_get_type (string)) {
+ case IMAP_STRING_ATOM:
+ /* string is safe as it is... */
+ g_string_append (str, string);
+ break;
+ case IMAP_STRING_QSTRING:
+ /* we need to quote the string */
+ /* FIXME: need to escape stuff */
+ g_string_append_printf (str, "\"%s\"", string);
+ break;
+ case IMAP_STRING_LITERAL:
+ if (engine->capa & CAMEL_IMAP_CAPABILITY_LITERALPLUS) {
+ /* we have to send a literal, but the server supports LITERAL+ so use that */
+ g_string_append_printf (str, "{%u+}\r\n%s", strlen (string), string);
+ } else {
+ /* we have to make it a literal */
+ literal = g_new (CamelIMAPLiteral, 1);
+ literal->type = CAMEL_IMAP_LITERAL_STRING;
+ literal->literal.string = g_strdup (string);
+
+ g_string_append_printf (str, "{%u}\r\n", strlen (string));
+
+ (*tail)->buffer = g_strdup (str->str);
+ (*tail)->buflen = str->len;
+ (*tail)->literal = literal;
+
+ part = command_part_new ();
+ (*tail)->next = part;
+ (*tail) = part;
+
+ g_string_truncate (str, 0);
+ }
+ break;
+ }
+}
+
+CamelIMAPCommand *
+camel_imap_command_newv (CamelIMAPEngine *engine, CamelIMAPFolder *imap_folder, const char *format, va_list args)
+{
+ CamelIMAPCommandPart *parts, *part, *tail;
+ CamelIMAPCommand *ic;
+ const char *start;
+ GString *str;
+
+ tail = parts = command_part_new ();
+
+ str = g_string_new ("");
+ start = format;
+
+ while (*format) {
+ register char ch = *format++;
+
+ if (ch == '%') {
+ CamelIMAPLiteral *literal;
+ CamelIMAPFolder *folder;
+ CamelDataWrapper *wrapper;
+ CamelStream *stream;
+ unsigned int u;
+ char *string;
+ size_t len;
+ int c, d;
+
+ g_string_append_len (str, start, format - start - 1);
+
+ switch (*format) {
+ case '%':
+ /* literal % */
+ g_string_append_c (str, '%');
+ break;
+ case 'c':
+ /* character */
+ c = va_arg (args, int);
+ g_string_append_c (str, c);
+ break;
+ case 'd':
+ /* integer */
+ d = va_arg (args, int);
+ g_string_append_printf (str, "%d", d);
+ break;
+ case 'u':
+ /* unsigned integer */
+ u = va_arg (args, unsigned int);
+ g_string_append_printf (str, "%u", u);
+ break;
+ case 'F':
+ /* CamelIMAPFolder */
+ folder = va_arg (args, CamelIMAPFolder *);
+ string = (char *) camel_imap_folder_utf7_name (folder);
+ imap_command_append_string (engine, &tail, str, string);
+ break;
+ case 'L':
+ /* Literal */
+ literal = va_arg (args, CamelIMAPLiteral *);
+ len = camel_imap_literal_length (literal);
+ g_string_append_printf (str, "{%u}\r\n", len);
+
+ tail->buffer = g_strdup (str->str);
+ tail->buflen = str->len;
+ tail->literal = literal;
+
+ part = command_part_new ();
+ tail->next = part;
+ tail = part;
+
+ g_string_truncate (str, 0);
+
+ break;
+ case 'S':
+ /* string which may need to be quoted or made into a literal */
+ string = va_arg (args, char *);
+ imap_command_append_string (engine, &tail, str, string);
+ break;
+ case 's':
+ /* safe atom string */
+ string = va_arg (args, char *);
+ g_string_append (str, string);
+ break;
+ default:
+ g_warning ("unknown formatter %%c", *format);
+ g_string_append_c (str, '%');
+ g_string_append_c (str, *format);
+ break;
+ }
+
+ format++;
+
+ start = format;
+ }
+ }
+
+ g_string_append (str, start);
+ tail->buffer = str->str;
+ tail->buflen = str->len;
+ tail->literal = NULL;
+ g_string_free (str, FALSE);
+
+ ic = g_new0 (CamelIMAPCommand, 1);
+ ((EDListNode *) ic)->next = NULL;
+ ((EDListNode *) ic)->prev = NULL;
+ ic->untagged = g_hash_table_new (g_str_hash, g_str_equal);
+ ic->status = CAMEL_IMAP_COMMAND_QUEUED;
+ ic->resp_codes = g_ptr_array_new ();
+ ic->engine = engine;
+ ic->ref_count = 1;
+ ic->parts = parts;
+ ic->part = parts;
+
+ camel_exception_init (&ic->ex);
+
+ if (imap_folder) {
+ camel_object_ref (imap_folder);
+ ic->folder = imap_folder;
+ } else
+ ic->folder = NULL;
+
+ return ic;
+}
+
+CamelIMAPCommand *
+camel_imap_command_new (CamelIMAPEngine *engine, CamelIMAPFolder *folder, const char *format, ...)
+{
+ CamelIMAPCommand *command;
+ va_list args;
+
+ va_start (args, format);
+ command = camel_imap_command_newv (engine, folder, format, args);
+ va_end (args);
+
+ return command;
+}
+
+void
+camel_imap_command_register_untagged (CamelIMAPCommand *ic, const char *atom, CamelIMAPUntaggedCallback untagged)
+{
+ g_hash_table_insert (ic->untagged, g_strdup (atom), untagged);
+}
+
+void
+camel_imap_command_ref (CamelIMAPCommand *ic)
+{
+ ic->ref_count++;
+}
+
+void
+camel_imap_command_unref (CamelIMAPCommand *ic)
+{
+ CamelIMAPCommandPart *part, *next;
+ CamelIMAPLiteral *literal;
+ int i;
+
+ if (ic == NULL)
+ return;
+
+ ic->ref_count--;
+ if (ic->ref_count == 0) {
+ if (ic->folder)
+ camel_object_unref (ic->folder);
+
+ g_free (ic->tag);
+
+ for (i = 0; i < ic->resp_codes->len; i++) {
+ CamelIMAPRespCode *resp_code;
+
+ resp_code = ic->resp_codes->pdata[i];
+ camel_imap_resp_code_free (resp_code);
+ }
+ g_ptr_array_free (ic->resp_codes, TRUE);
+
+ g_hash_table_foreach (ic->untagged, (GHFunc) g_free, NULL);
+ g_hash_table_destroy (ic->untagged);
+
+ camel_exception_clear (&ic->ex);
+
+ part = ic->parts;
+ while (part != NULL) {
+ g_free (part->buffer);
+ if (part->literal) {
+ switch (part->literal->type) {
+ case CAMEL_IMAP_LITERAL_STRING:
+ g_free (part->literal->literal.string);
+ break;
+ case CAMEL_IMAP_LITERAL_STREAM:
+ camel_object_unref (part->literal->literal.stream);
+ break;
+ case CAMEL_IMAP_LITERAL_WRAPPER:
+ camel_object_unref (part->literal->literal.wrapper);
+ break;
+ }
+
+ g_free (part->literal);
+ }
+
+ next = part->next;
+ g_free (part);
+ part = next;
+ }
+
+ g_free (ic);
+ }
+}
+
+
+static int
+imap_literal_write_to_stream (CamelIMAPLiteral *literal, CamelStream *stream)
+{
+ CamelStream *istream, *ostream = NULL;
+ CamelDataWrapper *wrapper;
+ CamelMimeFilter *crlf;
+ char *string;
+
+ if (literal->type == CAMEL_IMAP_LITERAL_STRING) {
+ string = literal->literal.string;
+ if (camel_stream_write (stream, string, strlen (string)) == -1)
+ return -1;
+
+ return 0;
+ }
+
+ crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
+ ostream = (CamelStream *) camel_stream_filter_new_with_stream (stream);
+ camel_stream_filter_add ((CamelStreamFilter *) ostream, crlf);
+ camel_object_unref (crlf);
+
+ /* write the literal */
+ switch (literal->type) {
+ case CAMEL_IMAP_LITERAL_STREAM:
+ istream = literal->literal.stream;
+ if (camel_stream_write_to_stream (istream, ostream) == -1)
+ goto exception;
+ break;
+ case CAMEL_IMAP_LITERAL_WRAPPER:
+ wrapper = literal->literal.wrapper;
+ if (camel_data_wrapper_write_to_stream (wrapper, ostream) == -1)
+ goto exception;
+ break;
+ }
+
+ camel_object_unref (ostream);
+ ostream = NULL;
+
+#if 0
+ if (camel_stream_write (stream, "\r\n", 2) == -1)
+ return -1;
+#endif
+
+ return 0;
+
+ exception:
+
+ camel_object_unref (ostream);
+
+ return -1;
+}
+
+
+static void
+unexpected_token (camel_imap_token_t *token)
+{
+ switch (token->token) {
+ case CAMEL_IMAP_TOKEN_NO_DATA:
+ fprintf (stderr, "*** NO DATA ***");
+ break;
+ case CAMEL_IMAP_TOKEN_ERROR:
+ fprintf (stderr, "*** ERROR ***");
+ break;
+ case CAMEL_IMAP_TOKEN_NIL:
+ fprintf (stderr, "NIL");
+ break;
+ case CAMEL_IMAP_TOKEN_ATOM:
+ fprintf (stderr, "%s", token->v.atom);
+ break;
+ case CAMEL_IMAP_TOKEN_QSTRING:
+ fprintf (stderr, "\"%s\"", token->v.qstring);
+ break;
+ case CAMEL_IMAP_TOKEN_LITERAL:
+ fprintf (stderr, "{%u}", token->v.literal);
+ break;
+ default:
+ fprintf (stderr, "%c", (unsigned char) (token->token & 0xff));
+ break;
+ }
+}
+
+int
+camel_imap_command_step (CamelIMAPCommand *ic)
+{
+ CamelIMAPEngine *engine = ic->engine;
+ int result = CAMEL_IMAP_RESULT_NONE;
+ CamelIMAPUntaggedCallback untagged;
+ CamelIMAPLiteral *literal;
+ camel_imap_token_t token;
+ unsigned char *linebuf;
+ ssize_t nwritten;
+ size_t len;
+ int ret;
+
+ g_assert (ic->part != NULL);
+
+ if (ic->part == ic->parts) {
+ ic->tag = g_strdup_printf ("%c%0.5u", engine->tagprefix, engine->tag++);
+ camel_stream_printf (engine->ostream, "%s ", ic->tag);
+ d(fprintf (stderr, "sending : %s ", ic->tag));
+ }
+
+#if d(!)0
+ {
+ int sending = ic->part != ic->parts;
+ unsigned char *eoln, *eob;
+
+ linebuf = ic->part->buffer;
+ eob = linebuf + ic->part->buflen;
+
+ do {
+ eoln = linebuf;
+ while (eoln < eob && *eoln != '\n')
+ eoln++;
+
+ if (eoln < eob)
+ eoln++;
+
+ if (sending)
+ fwrite ("sending : ", 1, 10, stderr);
+ fwrite (linebuf, 1, eoln - linebuf, stderr);
+
+ linebuf = eoln + 1;
+ sending = 1;
+ } while (linebuf < eob);
+ }
+#endif
+
+ linebuf = ic->part->buffer;
+ len = ic->part->buflen;
+
+ if ((nwritten = camel_stream_write (engine->ostream, linebuf, len)) == -1)
+ goto exception;
+
+ if (camel_stream_flush (engine->ostream) == -1)
+ goto exception;
+
+ /* now we need to read the response(s) from the IMAP server */
+
+ do {
+ if (camel_imap_engine_next_token (engine, &token, &ic->ex) == -1)
+ goto exception;
+
+ if (token.token == '+') {
+ /* we got a continuation response from the server */
+ literal = ic->part->literal;
+
+ if (camel_imap_engine_line (engine, &linebuf, &len, &ic->ex) == -1)
+ goto exception;
+
+ if (literal) {
+ if (imap_literal_write_to_stream (literal, engine->ostream) == -1)
+ goto exception;
+
+ g_free (linebuf);
+ linebuf = NULL;
+
+ break;
+ } else if (ic->plus) {
+ /* command expected a '+' response - probably AUTHENTICATE? */
+ if (ic->plus (engine, ic, linebuf, len, &ic->ex) == -1) {
+ g_free (linebuf);
+ return -1;
+ }
+
+ /* now we need to wait for a "<tag> OK/NO/BAD" response */
+ } else {
+ /* FIXME: error?? */
+ g_assert_not_reached ();
+ }
+
+ g_free (linebuf);
+ linebuf = NULL;
+ } else if (token.token == '*') {
+ /* we got an untagged response, let the engine handle this */
+ if (camel_imap_engine_handle_untagged_1 (engine, &token, &ic->ex) == -1)
+ goto exception;
+ } else if (token.token == CAMEL_IMAP_TOKEN_ATOM && !strcmp (token.v.atom, ic->tag)) {
+ /* we got "<tag> OK/NO/BAD" */
+ fprintf (stderr, "got %s response\n", token.v.atom);
+
+ if (camel_imap_engine_next_token (engine, &token, &ic->ex) == -1)
+ goto exception;
+
+ if (token.token == CAMEL_IMAP_TOKEN_ATOM) {
+ if (!strcmp (token.v.atom, "OK"))
+ result = CAMEL_IMAP_RESULT_OK;
+ else if (!strcmp (token.v.atom, "NO"))
+ result = CAMEL_IMAP_RESULT_NO;
+ else if (!strcmp (token.v.atom, "BAD"))
+ result = CAMEL_IMAP_RESULT_BAD;
+
+ if (result == CAMEL_IMAP_RESULT_NONE) {
+ fprintf (stderr, "expected OK/NO/BAD but got %s\n", token.v.atom);
+ goto unexpected;
+ }
+
+ if (camel_imap_engine_next_token (engine, &token, &ic->ex) == -1)
+ goto exception;
+
+ if (token.token == '[') {
+ /* we have a response code */
+ camel_imap_stream_unget_token (engine->istream, &token);
+ if (camel_imap_engine_parse_resp_code (engine, &ic->ex) == -1)
+ goto exception;
+ } else if (token.token != '\n') {
+ /* just gobble up the rest of the line */
+ if (camel_imap_engine_line (engine, NULL, NULL, &ic->ex) == -1)
+ goto exception;
+ }
+ } else {
+ fprintf (stderr, "expected anything but this: ");
+ unexpected_token (&token);
+ fprintf (stderr, "\n");
+
+ goto unexpected;
+ }
+
+ break;
+ } else {
+ fprintf (stderr, "wtf is this: ");
+ unexpected_token (&token);
+ fprintf (stderr, "\n");
+
+ unexpected:
+
+ /* no fucking clue what we got... */
+ if (camel_imap_engine_line (engine, &linebuf, &len, &ic->ex) == -1)
+ goto exception;
+
+ camel_exception_setv (&ic->ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Unexpected response from IMAP server %s: %s"),
+ engine->url->host, linebuf);
+
+ g_free (linebuf);
+
+ goto exception;
+ }
+ } while (1);
+
+ /* status should always be ACTIVE here... */
+ if (ic->status == CAMEL_IMAP_COMMAND_ACTIVE) {
+ ic->part = ic->part->next;
+ if (ic->part == NULL || result) {
+ ic->status = CAMEL_IMAP_COMMAND_COMPLETE;
+ ic->result = result;
+ return 1;
+ }
+ }
+
+ return 0;
+
+ exception:
+
+ ic->status = CAMEL_IMAP_COMMAND_ERROR;
+
+ return -1;
+}
+
+
+void
+camel_imap_command_reset (CamelIMAPCommand *ic)
+{
+ int i;
+
+ for (i = 0; i < ic->resp_codes->len; i++)
+ camel_imap_resp_code_free (ic->resp_codes->pdata[i]);
+ g_ptr_array_set_size (ic->resp_codes, 0);
+
+ ic->status = CAMEL_IMAP_COMMAND_QUEUED;
+ ic->result = CAMEL_IMAP_RESULT_NONE;
+ ic->part = ic->parts;
+ g_free (ic->tag);
+ ic->tag = NULL;
+
+ camel_exception_clear (&ic->ex);
+}