Removed SessionMsgType enum and its use, since it is useless.
[claws.git] / src / plugins / managesieve / managesieve.c
index 4c67904e4d9d06c09b5fd5c25251a0a25ade246f..6dac66f8f38fc401f6a255a6238445acb1c02100 100644 (file)
 
 #include <glib.h>
 #include <glib/gi18n.h>
+#include <ctype.h>
+#include <errno.h>
 
 #include "claws.h"
 #include "account.h"
+#include "passwordstore.h"
 #include "gtk/inputdialog.h"
 #include "md5.h"
 #include "utils.h"
@@ -38,43 +41,71 @@ GSList *sessions = NULL;
 static void sieve_session_destroy(Session *session);
 static gint sieve_pop_send_queue(SieveSession *session);
 static void sieve_session_reset(SieveSession *session);
+static void command_free(SieveCommand *cmd);
+static void command_abort(SieveCommand *cmd);
+static void command_cb(SieveCommand *cmd, gpointer result);
+static gint sieve_session_recv_chunk(SieveSession *, guint len);
+static void sieve_read_chunk(SieveSession *, gchar *data, guint len);
+static gint sieve_read_chunk_done(SieveSession *session);
 
 void sieve_sessions_close()
 {
        if (sessions) {
-               g_slist_free_full(sessions, (GDestroyNotify)session_destroy);
+               GSList *list = sessions;
                sessions = NULL;
+               g_slist_free_full(list, (GDestroyNotify)session_destroy);
        }
 }
 
-void noop_data_cb_fn(SieveSession *session, gpointer cb_data,
-               gpointer user_data)
-{
-       /* noop */
-}
-
 /* remove all command callbacks with a given data pointer */
 void sieve_sessions_discard_callbacks(gpointer user_data)
 {
        GSList *item;
        GSList *queue;
+       GSList *prev = NULL;
        SieveSession *session;
        SieveCommand *cmd;
 
        for (item = sessions; item; item = item->next) {
                session = (SieveSession *)item->data;
                cmd = session->current_cmd;
-               if (cmd && cmd->data == user_data)
-                       cmd->cb = noop_data_cb_fn;
+               /* abort current command handler */
+               if (cmd && cmd->data == user_data) {
+                       command_abort(cmd);
+                       session->current_cmd = NULL;
+               }
+               /* abort queued command handlers */
                for (queue = session->send_queue; queue; queue = queue->next) {
-                       cmd = (SieveCommand *)item->data;
-                       if (cmd && cmd->data == user_data)
-                               cmd->cb = noop_data_cb_fn;
+                       cmd = (SieveCommand *)queue->data;
+                       if (cmd && cmd->data == user_data) {
+                               if (prev)
+                                       prev->next = queue->next;
+                               else
+                                       session->send_queue = NULL;
+                               command_abort(cmd);
+                               g_slist_free_1(queue);
+                       } else {
+                               prev = queue;
+                       }
                }
        }
 }
 
-void command_free(SieveCommand *cmd) {
+static void command_cb(SieveCommand *cmd, gpointer result)
+{
+       if (cmd)
+               cmd->cb(cmd->session, FALSE, result, cmd->data);
+}
+
+static void command_abort(SieveCommand *cmd)
+{
+       cmd->cb(cmd->session, TRUE, NULL, cmd->data);
+       g_free(cmd->msg);
+       g_free(cmd);
+}
+
+static void command_free(SieveCommand *cmd)
+{
        g_free(cmd->msg);
        g_free(cmd);
 }
@@ -101,6 +132,125 @@ static void sieve_connected(SieveSession *session, gboolean connected)
                session->on_connected(session, connected, session->cb_data);
 }
 
+static gboolean sieve_read_chunk_cb(SockInfo *source,
+               GIOCondition condition, gpointer data)
+{
+       SieveSession *sieve_session = SIEVE_SESSION(data);
+       Session *session = &sieve_session->session;
+       gint data_len;
+       gint ret;
+
+       cm_return_val_if_fail(condition == G_IO_IN, FALSE);
+
+       session_set_timeout(session, session->timeout_interval);
+
+       if (session->read_buf_len == 0) {
+               gint read_len = -1;
+
+               if (session->sock)
+                       read_len = sock_read(session->sock,
+                                       session->read_buf,
+                                       SESSION_BUFFSIZE - 1);
+
+               if (read_len == -1 &&
+                               session->state == SESSION_DISCONNECTED) {
+                       g_warning ("sock_read: session disconnected");
+                       if (session->io_tag > 0) {
+                               g_source_remove(session->io_tag);
+                               session->io_tag = 0;
+                       }
+                       return FALSE;
+               }
+
+               if (read_len == 0) {
+                       g_warning("sock_read: received EOF");
+                       session->state = SESSION_EOF;
+                       return FALSE;
+               }
+
+               if (read_len < 0) {
+                       switch (errno) {
+                       case EAGAIN:
+                               return TRUE;
+                       default:
+                               g_warning("sock_read: %s",
+                                               g_strerror(errno));
+                               session->state = SESSION_ERROR;
+                               return FALSE;
+                       }
+               }
+
+               session->read_buf_len = read_len;
+       }
+
+       data_len = MIN(session->read_buf_len,
+                       sieve_session->octets_remaining);
+       sieve_session->octets_remaining -= data_len;
+       session->read_buf_len -= data_len;
+       session->read_buf_p[data_len] = '\0';
+
+       /* progress callback */
+       sieve_read_chunk(sieve_session, session->read_buf_p, data_len);
+
+       if (session->read_buf_len == 0) {
+               session->read_buf_p = session->read_buf;
+       } else {
+               session->read_buf_p += data_len;
+       }
+
+       /* incomplete read */
+       if (sieve_session->octets_remaining > 0)
+               return TRUE;
+
+       /* complete */
+       if (session->io_tag > 0) {
+               g_source_remove(session->io_tag);
+               session->io_tag = 0;
+       }
+
+       /* completion callback */
+       ret = sieve_read_chunk_done(sieve_session);
+
+       if (ret < 0)
+               session->state = SESSION_ERROR;
+
+       return FALSE;
+}
+
+static gboolean sieve_read_chunk_idle_cb(gpointer data)
+{
+       Session *session = SESSION(data);
+       gboolean ret;
+
+       ret = sieve_read_chunk_cb(session->sock, G_IO_IN, session);
+
+       if (ret == TRUE)
+               session->io_tag = sock_add_watch(session->sock, G_IO_IN,
+                               sieve_read_chunk_cb, session);
+
+       return FALSE;
+}
+
+/* Get data of specified length.
+ * If needed elsewhere, this should be put in session.c */
+static gint sieve_session_recv_chunk(SieveSession *sieve_session,
+               guint bytes)
+{
+       Session *session = &sieve_session->session;
+       cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
+
+       session->state = SESSION_RECV;
+       sieve_session->octets_remaining = bytes;
+
+       if (session->read_buf_len > 0)
+               g_idle_add(sieve_read_chunk_idle_cb, session);
+       else
+               session->io_tag = sock_add_watch(session->sock, G_IO_IN,
+                                                sieve_read_chunk_cb, session);
+       return 0;
+}
+
+
 static gint sieve_auth_recv(SieveSession *session, const gchar *msg)
 {
        gchar buf[MESSAGEBUFSIZE], *tmp;
@@ -113,7 +263,7 @@ static gint sieve_auth_recv(SieveSession *session, const gchar *msg)
                        tmp = g_base64_encode(session->user, strlen(session->user));
                        g_snprintf(buf, sizeof(buf), "\"%s\"", tmp);
 
-                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0) {
+                       if (session_send_msg(SESSION(session), buf) < 0) {
                                g_free(tmp);
                                return SE_ERROR;
                        }
@@ -121,8 +271,7 @@ static gint sieve_auth_recv(SieveSession *session, const gchar *msg)
                        log_print(LOG_PROTOCOL, "Sieve> [USERID]\n");
                } else {
                        /* Server rejects AUTH */
-                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                                        "\"*\"") < 0)
+                       if (session_send_msg(SESSION(session), "\"*\"") < 0)
                                return SE_ERROR;
                        log_print(LOG_PROTOCOL, "Sieve> *\n");
                }
@@ -157,8 +306,7 @@ static gint sieve_auth_recv(SieveSession *session, const gchar *msg)
                        response = g_strdup_printf("\"%s\"", response64);
                        g_free(response64);
 
-                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                                        response) < 0) {
+                       if (session_send_msg(SESSION(session), response) < 0) {
                                g_free(response);
                                return SE_ERROR;
                        }
@@ -166,15 +314,14 @@ static gint sieve_auth_recv(SieveSession *session, const gchar *msg)
                        g_free(response);
                } else {
                        /* Server rejects AUTH */
-                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                                        "\"*\"") < 0)
+                       if (session_send_msg(SESSION(session), "\"*\"") < 0)
                                return SE_ERROR;
                        log_print(LOG_PROTOCOL, "Sieve> *\n");
                }
                break;
        default:
                /* stop sieve_auth when no correct authtype */
-               if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*") < 0)
+               if (session_send_msg(SESSION(session), "*") < 0)
                        return SE_ERROR;
                log_print(LOG_PROTOCOL, "Sieve> *\n");
                break;
@@ -198,7 +345,7 @@ static gint sieve_auth_login_user_recv(SieveSession *session, const gchar *msg)
                tmp = g_strdup("\"*\"");
        }
 
-       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, tmp) < 0) {
+       if (session_send_msg(SESSION(session), tmp) < 0) {
                g_free(tmp);
                return SE_ERROR;
        }
@@ -215,8 +362,7 @@ static gint sieve_auth_cram_md5(SieveSession *session)
        session->state = SIEVE_AUTH;
        session->auth_type = SIEVEAUTH_CRAM_MD5;
 
-       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                               "Authenticate \"CRAM-MD5\"") < 0)
+       if (session_send_msg(SESSION(session), "Authenticate \"CRAM-MD5\"") < 0)
                return SE_ERROR;
        log_print(LOG_PROTOCOL, "Sieve> Authenticate CRAM-MD5\n");
 
@@ -239,7 +385,7 @@ static gint sieve_auth_plain(SieveSession *session)
        out = g_strconcat("Authenticate \"PLAIN\" \"", b64buf, "\"", NULL);
        g_free(b64buf);
 
-       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, out) < 0) {
+       if (session_send_msg(SESSION(session), out) < 0) {
                g_free(out);
                return SE_ERROR;
        }
@@ -256,8 +402,7 @@ static gint sieve_auth_login(SieveSession *session)
        session->state = SIEVE_AUTH;
        session->auth_type = SIEVEAUTH_LOGIN;
 
-       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                               "Authenticate \"LOGIN\"") < 0)
+       if (session_send_msg(SESSION(session), "Authenticate \"LOGIN\"") < 0)
                return SE_ERROR;
        log_print(LOG_PROTOCOL, "Sieve> Authenticate LOGIN\n");
 
@@ -271,7 +416,7 @@ static gint sieve_auth(SieveSession *session)
        if (!session->use_auth) {
                session->state = SIEVE_READY;
                sieve_connected(session, TRUE);
-               return sieve_pop_send_queue(session);
+               return SE_OK;
        }
 
        session->state = SIEVE_AUTH;
@@ -302,9 +447,18 @@ static gint sieve_auth(SieveSession *session)
 static void sieve_session_putscript_cb(SieveSession *session, SieveResult *result)
 {
        /* Remove script name from the beginning the response,
-        * which are added by Dovecot/Pigeonhole */
+        * which is added by Dovecot/Pigeonhole */
        gchar *start, *desc = result->description;
-       if (desc) {
+       gchar *end = NULL;
+       if (!desc) {
+               /* callback just for the status */
+               command_cb(session->current_cmd, result);
+       }
+       while (desc && desc[0]) {
+               if ((end = strchr(desc, '\r')) ||
+                   (end = strchr(desc, '\n')))
+                       while (*end == '\n' || *end == '\r')
+                               *end++ = '\0';
                if (g_str_has_prefix(desc, "NULL_") && (start = strchr(desc+5, ':'))) {
                        desc = start+1;
                        while (*desc == ' ')
@@ -316,9 +470,9 @@ static void sieve_session_putscript_cb(SieveSession *session, SieveResult *resul
                        desc = start+2;
                }
                result->description = desc;
+               command_cb(session->current_cmd, result);
+               desc = end;
        }
-       /* pass along the callback */
-       session->current_cmd->cb(session, result, session->current_cmd->data);
 }
 
 static inline gboolean response_is_ok(const char *msg)
@@ -378,6 +532,11 @@ static gint sieve_pop_send_queue(SieveSession *session)
        SieveCommand *cmd;
        GSList *send_queue = session->send_queue;
 
+       if (session->current_cmd) {
+               command_free(session->current_cmd);
+               session->current_cmd = NULL;
+       }
+
        if (!send_queue)
                return SE_OK;
 
@@ -387,10 +546,8 @@ static gint sieve_pop_send_queue(SieveSession *session)
 
        log_send(session, cmd);
        session->state = cmd->next_state;
-       if (session->current_cmd)
-               command_free(session->current_cmd);
        session->current_cmd = cmd;
-       if (session_send_msg(SESSION(session), SESSION_SEND, cmd->msg) < 0)
+       if (session_send_msg(SESSION(session), cmd->msg) < 0)
                return SE_ERROR;
 
        return SE_OK;
@@ -439,25 +596,27 @@ static void unquote_inplace(gchar *str)
 
 static void parse_response(gchar *msg, SieveResult *result)
 {
+       gchar *end;
+
+       cm_return_if_fail(msg != NULL);
+
        /* response status */
-       gchar *end = strchr(msg, ' ');
-       if (end)
-               *end++ = '\0';
-       result->success = strcmp(msg, "OK") == 0;
-       result->has_status = TRUE;
-       if (!end) {
-               result->code = SIEVE_CODE_NONE;
-               result->description = NULL;
-               result->has_octets = FALSE;
-               result->octets = 0;
-               return;
+       if (isalpha(msg[0])) {
+               end = strchr(msg, ' ');
+               if (end) {
+                       *end++ = '\0';
+                       while (*end == ' ')
+                               end++;
+               }
+               result->success = strcmp(msg, "OK") == 0;
+               result->has_status = TRUE;
+               msg = end;
+       } else {
+               result->has_status = FALSE;
        }
-       while (*end == ' ')
-               end++;
-       msg = end;
 
        /* response code */
-       if (msg[0] == '(' && (end = strchr(msg, ')'))) {
+       if (msg && msg[0] == '(' && (end = strchr(msg, ')'))) {
                msg++;
                *end++ = '\0';
                result->code =
@@ -472,7 +631,7 @@ static void parse_response(gchar *msg, SieveResult *result)
        }
 
        /* s2c octets */
-       if (msg[0] == '{' && (end = strchr(msg, '}'))) {
+       if (msg && msg[0] == '{' && (end = strchr(msg, '}'))) {
                msg++;
                *end++ = '\0';
                if (msg[0] == '0' && msg+1 == end) {
@@ -491,7 +650,7 @@ static void parse_response(gchar *msg, SieveResult *result)
        }
 
        /* text */
-       if (*msg) {
+       if (msg && *msg) {
                unquote_inplace(msg);
                result->description = msg;
        } else {
@@ -503,29 +662,23 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
 {
        SieveSession *sieve_session = SIEVE_SESSION(session);
        SieveResult result;
-       gint ret = 0;
+       gint ret = SE_OK;
 
-       switch (sieve_session->state) {
-       case SIEVE_GETSCRIPT_DATA:
-               log_print(LOG_PROTOCOL, "Sieve< [GETSCRIPT data]\n");
-               break;
-       default:
-               log_print(LOG_PROTOCOL, "Sieve< %s\n", msg);
-               if (response_is_bye(msg)) {
-                       gchar *status;
-                       parse_response((gchar *)msg, &result);
-                       if (!result.description)
-                               status = g_strdup(_("Disconnected"));
-                       else if (g_str_has_prefix(result.description, "Disconnected"))
-                               status = g_strdup(result.description);
-                       else
-                               status = g_strdup_printf(_("Disconnected: %s"), result.description);
-                       sieve_session->error = SE_ERROR;
-                       sieve_error(sieve_session, status);
-                       sieve_session->state = SIEVE_DISCONNECTED;
-                       g_free(status);
-                       return -1;
-               }
+       log_print(LOG_PROTOCOL, "Sieve< %s\n", msg);
+       if (response_is_bye(msg)) {
+               gchar *status;
+               parse_response((gchar *)msg, &result);
+               if (!result.description)
+                       status = g_strdup(_("Disconnected"));
+               else if (g_str_has_prefix(result.description, "Disconnected"))
+                       status = g_strdup(result.description);
+               else
+                       status = g_strdup_printf(_("Disconnected: %s"), result.description);
+               sieve_session->error = SE_ERROR;
+               sieve_error(sieve_session, status);
+               sieve_session->state = SIEVE_DISCONNECTED;
+               g_free(status);
+               return -1;
        }
 
        switch (sieve_session->state) {
@@ -538,14 +691,14 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                                        sieve_session->config->tls_type != SIEVE_TLS_NO) {
                                if (sieve_session->capability.starttls) {
                                        log_print(LOG_PROTOCOL, "Sieve> STARTTLS\n");
-                                       session_send_msg(session, SESSION_SEND, "STARTTLS");
+                                       session_send_msg(session, "STARTTLS");
                                        sieve_session->state = SIEVE_STARTTLS;
                                } else if (sieve_session->config->tls_type == SIEVE_TLS_YES) {
                                        log_warning(LOG_PROTOCOL, "Sieve: does not support STARTTLS\n");
                                        sieve_session->state = SIEVE_ERROR;
                                } else {
                                        log_warning(LOG_PROTOCOL, "Sieve: continuing without TLS\n");
-                                       sieve_session->state = SIEVE_CAPABILITIES;
+                                       sieve_session->state = SIEVE_READY;
                                }
                                break;
                        }
@@ -556,7 +709,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                        } else {
                                sieve_session->state = SIEVE_READY;
                                sieve_connected(sieve_session, TRUE);
-                               ret = sieve_pop_send_queue(sieve_session);
                        }
                } else {
                        /* got a capability */
@@ -566,6 +718,8 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                }
                break;
        case SIEVE_READY:
+               if (!msg[0])
+                       break;
                log_warning(LOG_PROTOCOL,
                                _("unhandled message on Sieve session: %s\n"), msg);
                break;
@@ -579,7 +733,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                }
                sieve_session->tls_init_done = TRUE;
                sieve_session->state = SIEVE_CAPABILITIES;
-               ret = SE_OK;
 #endif
                break;
        case SIEVE_AUTH:
@@ -601,7 +754,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                        sieve_session->authenticated = TRUE;
                        sieve_session->state = SIEVE_READY;
                        sieve_connected(sieve_session, TRUE);
-                       ret = sieve_pop_send_queue(sieve_session);
                }
                break;
        case SIEVE_NOOP:
@@ -613,18 +765,14 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
        case SIEVE_LISTSCRIPTS:
                if (response_is_no(msg)) {
                        /* got an error. probably not authenticated. */
-                       sieve_session->current_cmd->cb(sieve_session, NULL,
-                                       sieve_session->current_cmd->data);
+                       command_cb(sieve_session->current_cmd, NULL);
                        sieve_session->state = SIEVE_READY;
-                       ret = sieve_pop_send_queue(sieve_session);
                } else if (response_is_ok(msg)) {
                        /* end of list */
                        sieve_session->state = SIEVE_READY;
                        sieve_session->error = SE_OK;
-                       sieve_session->current_cmd->cb(sieve_session,
-                                       (gpointer)&(SieveScript){0},
-                                       sieve_session->current_cmd->data);
-                       ret = sieve_pop_send_queue(sieve_session);
+                       command_cb(sieve_session->current_cmd,
+                                       (gpointer)&(SieveScript){0});
                } else {
                        /* got a script name */
                        SieveScript script;
@@ -634,90 +782,82 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                        script.active = (script_status &&
                                        strcasecmp(script_status, "active") == 0);
 
-                       sieve_session->current_cmd->cb(sieve_session, (gpointer)&script,
-                                       sieve_session->current_cmd->data);
-                       ret = SE_OK;
+                       command_cb(sieve_session->current_cmd,
+                                       (gpointer)&script);
                }
                break;
        case SIEVE_RENAMESCRIPT:
                if (response_is_no(msg)) {
                        /* error */
-                       sieve_session->current_cmd->cb(sieve_session, NULL,
-                                       sieve_session->current_cmd->data);
+                       command_cb(sieve_session->current_cmd, NULL);
                } else if (response_is_ok(msg)) {
-                       sieve_session->current_cmd->cb(sieve_session, (void*)TRUE,
-                                       sieve_session->current_cmd->data);
+                       command_cb(sieve_session->current_cmd, (void*)TRUE);
                } else {
                        log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
                }
                sieve_session->state = SIEVE_READY;
                break;
        case SIEVE_SETACTIVE:
-               if (response_is_no(msg)) {
-                       /* error */
-                       sieve_session->current_cmd->cb(sieve_session, NULL,
-                                       sieve_session->current_cmd->data);
-               } else if (response_is_ok(msg)) {
-                       sieve_session->current_cmd->cb(sieve_session, (void*)TRUE,
-                                       sieve_session->current_cmd->data);
+               parse_response((gchar *)msg, &result);
+               if (result.success) {
+                       /* clear status possibly set when setting another
+                        * script active. TODO: give textual feedback */
+                       sieve_error(sieve_session, "");
+
+                       command_cb(sieve_session->current_cmd, NULL);
+               } else if (result.description) {
+                       command_cb(sieve_session->current_cmd,
+                                       result.description);
                } else {
                        log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
                }
-               sieve_session->state = SIEVE_READY;
+               if (result.has_octets) {
+                       return sieve_session_recv_chunk(sieve_session,
+                                       result.octets);
+               } else {
+                       sieve_session->state = SIEVE_READY;
+               }
                break;
        case SIEVE_GETSCRIPT:
                if (response_is_no(msg)) {
-                       sieve_session->current_cmd->cb(sieve_session, (void *)-1,
-                                       sieve_session->current_cmd->data);
+                       command_cb(sieve_session->current_cmd, (void *)-1);
                        sieve_session->state = SIEVE_READY;
                } else {
                        parse_response((gchar *)msg, &result);
                        sieve_session->state = SIEVE_GETSCRIPT_DATA;
+                       return sieve_session_recv_chunk(sieve_session,
+                                       result.octets);
                }
-               ret = SE_OK;
                break;
        case SIEVE_GETSCRIPT_DATA:
+               if (!msg[0])
+                       break;
+               sieve_session->state = SIEVE_READY;
                if (response_is_ok(msg)) {
-                       sieve_session->state = SIEVE_READY;
-                       sieve_session->current_cmd->cb(sieve_session, NULL,
-                                       sieve_session->current_cmd->data);
-               } else {
-                       sieve_session->current_cmd->cb(sieve_session, (gchar *)msg,
-                                       sieve_session->current_cmd->data);
+                       command_cb(sieve_session->current_cmd, NULL);
+               } else if (msg[0]) {
+                       log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
                }
-               ret = SE_OK;
                break;
        case SIEVE_PUTSCRIPT:
+               if (!msg[0])
+                       break;
                parse_response((gchar *)msg, &result);
+               sieve_session_putscript_cb(sieve_session, &result);
                if (result.has_octets) {
-                       sieve_session->state = SIEVE_PUTSCRIPT_DATA;
+                       return sieve_session_recv_chunk(sieve_session,
+                                       result.octets);
                } else {
                        sieve_session->state = SIEVE_READY;
                }
-               sieve_session_putscript_cb(sieve_session, &result);
-               ret = SE_OK;
-               break;
-       case SIEVE_PUTSCRIPT_DATA:
-               if (!msg[0]) {
-                       sieve_session->state = SIEVE_READY;
-               } else {
-                       result.has_status = FALSE;
-                       result.has_octets = FALSE;
-                       result.success = -1;
-                       result.code = SIEVE_CODE_NONE;
-                       result.description = (gchar *)msg;
-                       sieve_session_putscript_cb(sieve_session, &result);
-               }
-               ret = SE_OK;
                break;
        case SIEVE_DELETESCRIPT:
                parse_response((gchar *)msg, &result);
                if (!result.success) {
-                       sieve_session->current_cmd->cb(sieve_session, result.description,
-                                       sieve_session->current_cmd->data);
+                       command_cb(sieve_session->current_cmd,
+                                       result.description);
                } else {
-                       sieve_session->current_cmd->cb(sieve_session, NULL,
-                                       sieve_session->current_cmd->data);
+                       command_cb(sieve_session->current_cmd, NULL);
                }
                sieve_session->state = SIEVE_READY;
                break;
@@ -737,9 +877,12 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                return -1;
        }
 
-       if (ret == SE_OK)
+       if (ret == SE_OK && sieve_session->state == SIEVE_READY)
+               ret = sieve_pop_send_queue(sieve_session);
+
+       if (ret == SE_OK) {
                return session_recv_msg(session);
-       else if (ret == SE_AUTHFAIL) {
+       else if (ret == SE_AUTHFAIL) {
                sieve_error(sieve_session, _("Auth failed"));
                sieve_session->state = SIEVE_ERROR;
                sieve_session->error = SE_ERROR;
@@ -754,11 +897,62 @@ static gint sieve_recv_message(Session *session, const gchar *msg,
        return 0;
 }
 
+static void sieve_read_chunk(SieveSession *session, gchar *data, guint len)
+{
+       log_print(LOG_PROTOCOL, "Sieve< [%u bytes]\n", len);
+
+       switch (session->state) {
+       case SIEVE_GETSCRIPT_DATA:
+               command_cb(session->current_cmd, (gchar *)data);
+               break;
+       case SIEVE_SETACTIVE:
+               /* Dovecot shows a script's warnings when making it active */
+               /* TODO: append message in case it is very long*/
+               strretchomp(data);
+               sieve_error(session, data);
+               break;
+       case SIEVE_PUTSCRIPT: {
+               SieveResult result = {.description = (gchar *)data};
+               sieve_session_putscript_cb(session, &result);
+               break;
+       }
+       default:
+               log_warning(LOG_PROTOCOL,
+                               _("error occurred on SIEVE session\n"));
+       }
+}
+
+static gint sieve_read_chunk_done(SieveSession *session)
+{
+       gint ret = SE_OK;
+
+       switch (session->state) {
+       case SIEVE_GETSCRIPT_DATA:
+               /* wait for ending "OK" response */
+               break;
+       case SIEVE_SETACTIVE:
+       case SIEVE_PUTSCRIPT:
+               session->state = SIEVE_READY;
+               break;
+       default:
+               log_warning(LOG_PROTOCOL,
+                               _("error occurred on SIEVE session\n"));
+       }
+
+       if (ret == SE_OK && session->state == SIEVE_READY)
+               ret = sieve_pop_send_queue(session);
+
+       if (ret == SE_OK)
+               return session_recv_msg(SESSION(session));
+
+       return 0;
+}
+
 static gint sieve_cmd_noop(SieveSession *session)
 {
        log_print(LOG_PROTOCOL, "Sieve> NOOP\n");
        session->state = SIEVE_NOOP;
-       if (session_send_msg(SESSION(session), SESSION_SEND, "NOOP") < 0) {
+       if (session_send_msg(SESSION(session), "NOOP") < 0) {
                session->state = SIEVE_ERROR;
                session->error = SE_ERROR;
                return 1;
@@ -784,8 +978,10 @@ static void sieve_session_destroy(Session *session)
        SieveSession *sieve_session = SIEVE_SESSION(session);
        g_free(sieve_session->pass);
        if (sieve_session->current_cmd)
-               command_free(sieve_session->current_cmd);
+               command_abort(sieve_session->current_cmd);
        sessions = g_slist_remove(sessions, (gconstpointer)session);
+       g_slist_free_full(sieve_session->send_queue,
+                       (GDestroyNotify)command_abort);
 }
 
 static void sieve_connect_finished(Session *session, gboolean success)
@@ -799,7 +995,9 @@ static gint sieve_session_connect(SieveSession *session)
 {
        session->state = SIEVE_CAPABILITIES;
        session->authenticated = FALSE;
+#ifdef USE_GNUTLS
        session->tls_init_done = FALSE;
+#endif
        return session_connect(SESSION(session), session->host,
                        session->port);
 }
@@ -827,7 +1025,7 @@ static void sieve_session_reset(SieveSession *session)
        SieveAccountConfig *config = sieve_prefs_account_get_config(account);
        gboolean reuse_auth = (config->auth == SIEVEAUTH_REUSE);
 
-       g_slist_free_full(session->send_queue, (GDestroyNotify)command_free);
+       g_slist_free_full(session->send_queue, (GDestroyNotify)command_abort);
 
        session_disconnect(SESSION(session));
 
@@ -837,7 +1035,9 @@ static void sieve_session_reset(SieveSession *session)
        session->current_cmd = NULL;
        session->send_queue = NULL;
        session->state = SIEVE_CAPABILITIES;
+#ifdef USE_GNUTLS
        session->tls_init_done = FALSE;
+#endif
        session->avail_auth_type = 0;
        session->auth_type = 0;
        session->config = config;
@@ -851,10 +1051,10 @@ static void sieve_session_reset(SieveSession *session)
                g_free(session->pass);
        if (config->auth == SIEVEAUTH_NONE) {
                session->pass = NULL;
-       } else if (reuse_auth && account->passwd) {
-               session->pass = g_strdup(account->passwd);
-       } else if (config->passwd && config->passwd[0]) {
-               session->pass = g_strdup(config->passwd);
+       } else if (reuse_auth && (session->pass = passwd_store_get_account(
+                                account->account_id, PWS_ACCOUNT_RECV))) {
+       } else if ((session->pass = passwd_store_get_account(
+                                account->account_id, "sieve"))) {
        } else if (password_get(session->user, session->host, "sieve",
                                session->port, &session->pass)) {
        } else {
@@ -914,6 +1114,7 @@ static void sieve_queue_send(SieveSession *session, SieveState next_state,
 {
        gboolean queue = FALSE;
        SieveCommand *cmd = g_new0(SieveCommand, 1);
+       cmd->session = session;
        cmd->next_state = next_state;
        cmd->msg = msg;
        cmd->data = data;
@@ -944,7 +1145,7 @@ static void sieve_queue_send(SieveSession *session, SieveState next_state,
                session->current_cmd = cmd;
                session->state = next_state;
                log_send(session, cmd);
-               if (session_send_msg(SESSION(session), SESSION_SEND, cmd->msg) < 0) {
+               if (session_send_msg(SESSION(session), cmd->msg) < 0) {
                        /* error */
                }
        }
@@ -957,15 +1158,6 @@ void sieve_session_list_scripts(SieveSession *session,
        sieve_queue_send(session, SIEVE_LISTSCRIPTS, msg, cb, data);
 }
 
-void sieve_session_add_script(SieveSession *session, const gchar *filter_name,
-               sieve_session_data_cb_fn cb, gpointer data)
-{
-/*
-       gchar *msg = g_strdup("LISTSCRIPTS");
-       sieve_queue_send(session, SIEVE_LISTSCRIPTS, msg, cb, data);
-*/
-}
-
 void sieve_session_set_active_script(SieveSession *session,
                const gchar *filter_name,
                sieve_session_data_cb_fn cb, gpointer data)
@@ -973,7 +1165,7 @@ void sieve_session_set_active_script(SieveSession *session,
        gchar *msg = g_strdup_printf("SETACTIVE \"%s\"",
                        filter_name ? filter_name : "");
        if (!msg) {
-               cb(session, (void*)FALSE, data);
+               cb(session, FALSE, (void*)FALSE, data);
                return;
        }
 
@@ -1004,8 +1196,9 @@ void sieve_session_put_script(SieveSession *session, const gchar *filter_name,
                sieve_session_data_cb_fn cb, gpointer data)
 {
        /* TODO: refactor so don't have to copy the whole script here */
-       gchar *msg = g_strdup_printf("PUTSCRIPT \"%s\" {%u+}\r\n%s",
-                       filter_name, len, script_contents);
+       gchar *msg = g_strdup_printf("PUTSCRIPT \"%s\" {%u+}%s%s",
+                       filter_name, len, len > 0 ? "\r\n" : "",
+                       script_contents);
 
        sieve_queue_send(session, SIEVE_PUTSCRIPT, msg, cb, data);
 }
@@ -1014,8 +1207,8 @@ void sieve_session_check_script(SieveSession *session,
                gint len, const gchar *script_contents,
                sieve_session_data_cb_fn cb, gpointer data)
 {
-       gchar *msg = g_strdup_printf("CHECKSCRIPT {%u+}\r\n%s",
-                       len, script_contents);
+       gchar *msg = g_strdup_printf("CHECKSCRIPT {%u+}%s%s",
+                       len, len > 0 ? "\r\n" : "", script_contents);
 
        sieve_queue_send(session, SIEVE_PUTSCRIPT, msg, cb, data);
 }