managesieve: prevent session list corruption
[claws.git] / src / plugins / managesieve / managesieve.c
index 323c52deedc31b58741b07739ec6a8a5ccb91a72..4e7508a764d14a5df580ee23fb4c8f815c97b4d4 100644 (file)
@@ -46,8 +46,9 @@ static void command_cb(SieveCommand *cmd, gpointer result);
 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);
        }
 }
 
@@ -296,7 +297,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;
@@ -403,6 +404,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;
 
@@ -412,8 +418,6 @@ 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)
                return SE_ERROR;
@@ -466,6 +470,8 @@ static void parse_response(gchar *msg, SieveResult *result)
 {
        gchar *end;
 
+       cm_return_if_fail(msg != NULL);
+
        /* response status */
        if (isalpha(msg[0])) {
                end = strchr(msg, ' ');
@@ -528,7 +534,7 @@ 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:
@@ -570,7 +576,7 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                                        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;
                        }
@@ -581,7 +587,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 */
@@ -604,7 +609,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:
@@ -626,7 +630,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:
@@ -640,14 +643,12 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                        /* got an error. probably not authenticated. */
                        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;
                        command_cb(sieve_session->current_cmd,
                                        (gpointer)&(SieveScript){0});
-                       ret = sieve_pop_send_queue(sieve_session);
                } else {
                        /* got a script name */
                        SieveScript script;
@@ -659,7 +660,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
 
                        command_cb(sieve_session->current_cmd,
                                        (gpointer)&script);
-                       ret = SE_OK;
                }
                break;
        case SIEVE_RENAMESCRIPT:
@@ -674,15 +674,35 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                sieve_session->state = SIEVE_READY;
                break;
        case SIEVE_SETACTIVE:
-               if (response_is_no(msg)) {
-                       /* error */
+               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 (response_is_ok(msg)) {
-                       command_cb(sieve_session->current_cmd, (void*)TRUE);
+               } 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) {
+                       sieve_session->octets_remaining = result.octets;
+                       sieve_session->state = SIEVE_SETACTIVE_DATA;
+               } else {
+                       sieve_session->state = SIEVE_READY;
+               }
+               break;
+       case SIEVE_SETACTIVE_DATA:
+               /* Dovecot shows a script's warnings when making it active */
+               sieve_session->octets_remaining -= strlen(msg) + 1;
+               if (sieve_session->octets_remaining > 0) {
+                       /* TODO: buffer multi-line message */
+                       sieve_error(sieve_session, msg);
+               } else {
+                       sieve_session->state = SIEVE_READY;
+               }
                break;
        case SIEVE_GETSCRIPT:
                if (response_is_no(msg)) {
@@ -694,7 +714,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                        /* account for newline */
                        sieve_session->octets_remaining = result.octets + 1;
                }
-               ret = SE_OK;
                break;
        case SIEVE_GETSCRIPT_DATA:
                if (sieve_session->octets_remaining > 0) {
@@ -706,7 +725,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                } else {
                        log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
                }
-               ret = SE_OK;
                break;
        case SIEVE_PUTSCRIPT:
                parse_response((gchar *)msg, &result);
@@ -716,7 +734,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                        sieve_session->state = SIEVE_READY;
                }
                sieve_session_putscript_cb(sieve_session, &result);
-               ret = SE_OK;
                break;
        case SIEVE_PUTSCRIPT_DATA:
                if (!msg[0]) {
@@ -729,7 +746,6 @@ static gint sieve_session_recv_msg(Session *session, const gchar *msg)
                        result.description = (gchar *)msg;
                        sieve_session_putscript_cb(sieve_session, &result);
                }
-               ret = SE_OK;
                break;
        case SIEVE_DELETESCRIPT:
                parse_response((gchar *)msg, &result);
@@ -757,9 +773,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;
@@ -980,15 +999,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)
@@ -1027,8 +1037,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);
 }
@@ -1037,8 +1048,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);
 }