2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2015 the Claws Mail Team
4 * Copyright (C) 2014-2015 Charles Lehner
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <glib/gi18n.h>
28 #include "passwordstore.h"
29 #include "gtk/inputdialog.h"
35 #include "managesieve.h"
36 #include "sieve_editor.h"
37 #include "sieve_prefs.h"
39 GSList *sessions = NULL;
41 static void sieve_session_destroy(Session *session);
42 static gint sieve_pop_send_queue(SieveSession *session);
43 static void sieve_session_reset(SieveSession *session);
44 static void command_free(SieveCommand *cmd);
45 static void command_abort(SieveCommand *cmd);
46 static void command_cb(SieveCommand *cmd, gpointer result);
47 static gint sieve_session_recv_chunk(SieveSession *, guint len);
48 static void sieve_read_chunk(SieveSession *, gchar *data, guint len);
49 static gint sieve_read_chunk_done(SieveSession *session);
51 void sieve_sessions_close()
54 GSList *list = sessions;
56 g_slist_free_full(list, (GDestroyNotify)session_destroy);
60 /* remove all command callbacks with a given data pointer */
61 void sieve_sessions_discard_callbacks(gpointer user_data)
66 SieveSession *session;
69 for (item = sessions; item; item = item->next) {
70 session = (SieveSession *)item->data;
71 cmd = session->current_cmd;
72 /* abort current command handler */
73 if (cmd && cmd->data == user_data) {
75 session->current_cmd = NULL;
77 /* abort queued command handlers */
78 for (queue = session->send_queue; queue; queue = queue->next) {
79 cmd = (SieveCommand *)queue->data;
80 if (cmd && cmd->data == user_data) {
82 prev->next = queue->next;
84 session->send_queue = NULL;
86 g_slist_free_1(queue);
94 static void command_cb(SieveCommand *cmd, gpointer result)
97 cmd->cb(cmd->session, FALSE, result, cmd->data);
100 static void command_abort(SieveCommand *cmd)
102 cmd->cb(cmd->session, TRUE, NULL, cmd->data);
107 static void command_free(SieveCommand *cmd)
113 void sieve_session_handle_status(SieveSession *session,
114 sieve_session_error_cb_fn on_error,
115 sieve_session_connected_cb_fn on_connected,
118 session->on_error = on_error;
119 session->on_connected = on_connected;
120 session->cb_data = data;
123 static void sieve_error(SieveSession *session, const gchar *msg)
125 if (session->on_error)
126 session->on_error(session, msg, session->cb_data);
129 static void sieve_connected(SieveSession *session, gboolean connected)
131 if (session->on_connected)
132 session->on_connected(session, connected, session->cb_data);
135 static gboolean sieve_read_chunk_cb(SockInfo *source,
136 GIOCondition condition, gpointer data)
138 SieveSession *sieve_session = SIEVE_SESSION(data);
139 Session *session = &sieve_session->session;
143 cm_return_val_if_fail(condition == G_IO_IN, FALSE);
145 session_set_timeout(session, session->timeout_interval);
147 if (session->read_buf_len == 0) {
151 read_len = sock_read(session->sock,
153 SESSION_BUFFSIZE - 1);
155 if (read_len == -1 &&
156 session->state == SESSION_DISCONNECTED) {
157 g_warning ("sock_read: session disconnected");
158 if (session->io_tag > 0) {
159 g_source_remove(session->io_tag);
166 g_warning("sock_read: received EOF");
167 session->state = SESSION_EOF;
176 g_warning("sock_read: %s",
178 session->state = SESSION_ERROR;
183 session->read_buf_len = read_len;
186 data_len = MIN(session->read_buf_len,
187 sieve_session->octets_remaining);
188 sieve_session->octets_remaining -= data_len;
189 session->read_buf_len -= data_len;
190 session->read_buf_p[data_len] = '\0';
192 /* progress callback */
193 sieve_read_chunk(sieve_session, session->read_buf_p, data_len);
195 if (session->read_buf_len == 0) {
196 session->read_buf_p = session->read_buf;
198 session->read_buf_p += data_len;
201 /* incomplete read */
202 if (sieve_session->octets_remaining > 0)
206 if (session->io_tag > 0) {
207 g_source_remove(session->io_tag);
211 /* completion callback */
212 ret = sieve_read_chunk_done(sieve_session);
215 session->state = SESSION_ERROR;
220 static gboolean sieve_read_chunk_idle_cb(gpointer data)
222 Session *session = SESSION(data);
225 ret = sieve_read_chunk_cb(session->sock, G_IO_IN, session);
228 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
229 sieve_read_chunk_cb, session);
234 /* Get data of specified length.
235 * If needed elsewhere, this should be put in session.c */
236 static gint sieve_session_recv_chunk(SieveSession *sieve_session,
239 Session *session = &sieve_session->session;
240 cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
242 session->state = SESSION_RECV;
243 sieve_session->octets_remaining = bytes;
245 if (session->read_buf_len > 0)
246 g_idle_add(sieve_read_chunk_idle_cb, session);
248 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
249 sieve_read_chunk_cb, session);
254 static gint sieve_auth_recv(SieveSession *session, const gchar *msg)
256 gchar buf[MESSAGEBUFSIZE], *tmp;
258 switch (session->auth_type) {
259 case SIEVEAUTH_LOGIN:
260 session->state = SIEVE_AUTH_LOGIN_USER;
262 if (strstr(msg, "VXNlcm5hbWU6")) {
263 tmp = g_base64_encode(session->user, strlen(session->user));
264 g_snprintf(buf, sizeof(buf), "\"%s\"", tmp);
266 if (session_send_msg(SESSION(session), buf) < 0) {
271 log_print(LOG_PROTOCOL, "Sieve> [USERID]\n");
273 /* Server rejects AUTH */
274 if (session_send_msg(SESSION(session), "\"*\"") < 0)
276 log_print(LOG_PROTOCOL, "Sieve> *\n");
279 case SIEVEAUTH_CRAM_MD5:
280 session->state = SIEVE_AUTH_CRAM_MD5;
285 gchar *challenge, *tmp;
287 guchar hexdigest[33];
289 tmp = g_base64_decode(msg + 1, &challengelen);
290 challenge = g_strndup(tmp, challengelen);
292 log_print(LOG_PROTOCOL, "Sieve< [Decoded: %s]\n", challenge);
294 g_snprintf(buf, sizeof(buf), "%s", session->pass);
295 md5_hex_hmac(hexdigest, challenge, challengelen,
296 buf, strlen(session->pass));
299 response = g_strdup_printf
300 ("%s %s", session->user, hexdigest);
301 log_print(LOG_PROTOCOL, "Sieve> [Encoded: %s]\n", response);
303 response64 = g_base64_encode(response, strlen(response));
306 response = g_strdup_printf("\"%s\"", response64);
309 if (session_send_msg(SESSION(session), response) < 0) {
313 log_print(LOG_PROTOCOL, "Sieve> %s\n", response);
316 /* Server rejects AUTH */
317 if (session_send_msg(SESSION(session), "\"*\"") < 0)
319 log_print(LOG_PROTOCOL, "Sieve> *\n");
323 /* stop sieve_auth when no correct authtype */
324 if (session_send_msg(SESSION(session), "*") < 0)
326 log_print(LOG_PROTOCOL, "Sieve> *\n");
333 static gint sieve_auth_login_user_recv(SieveSession *session, const gchar *msg)
337 session->state = SIEVE_AUTH_LOGIN_PASS;
339 if (strstr(msg, "UGFzc3dvcmQ6")) {
340 tmp2 = g_base64_encode(session->pass, strlen(session->pass));
341 tmp = g_strdup_printf("\"%s\"", tmp2);
344 /* Server rejects AUTH */
345 tmp = g_strdup("\"*\"");
348 if (session_send_msg(SESSION(session), tmp) < 0) {
354 log_print(LOG_PROTOCOL, "Sieve> [PASSWORD]\n");
360 static gint sieve_auth_cram_md5(SieveSession *session)
362 session->state = SIEVE_AUTH;
363 session->auth_type = SIEVEAUTH_CRAM_MD5;
365 if (session_send_msg(SESSION(session), "Authenticate \"CRAM-MD5\"") < 0)
367 log_print(LOG_PROTOCOL, "Sieve> Authenticate CRAM-MD5\n");
372 static gint sieve_auth_plain(SieveSession *session)
374 gchar buf[MESSAGEBUFSIZE], *b64buf, *out;
377 session->state = SIEVE_AUTH_PLAIN;
378 session->auth_type = SIEVEAUTH_PLAIN;
380 memset(buf, 0, sizeof buf);
382 /* "\0user\0password" */
383 len = sprintf(buf, "%c%s%c%s", '\0', session->user, '\0', session->pass);
384 b64buf = g_base64_encode(buf, len);
385 out = g_strconcat("Authenticate \"PLAIN\" \"", b64buf, "\"", NULL);
388 if (session_send_msg(SESSION(session), out) < 0) {
395 log_print(LOG_PROTOCOL, "Sieve> [Authenticate PLAIN]\n");
400 static gint sieve_auth_login(SieveSession *session)
402 session->state = SIEVE_AUTH;
403 session->auth_type = SIEVEAUTH_LOGIN;
405 if (session_send_msg(SESSION(session), "Authenticate \"LOGIN\"") < 0)
407 log_print(LOG_PROTOCOL, "Sieve> Authenticate LOGIN\n");
412 static gint sieve_auth(SieveSession *session)
414 SieveAuthType forced_auth_type = session->forced_auth_type;
416 if (!session->use_auth) {
417 session->state = SIEVE_READY;
418 sieve_connected(session, TRUE);
422 session->state = SIEVE_AUTH;
423 sieve_error(session, _("Authenticating..."));
425 if ((forced_auth_type == SIEVEAUTH_CRAM_MD5 || forced_auth_type == 0) &&
426 (session->avail_auth_type & SIEVEAUTH_CRAM_MD5) != 0)
427 return sieve_auth_cram_md5(session);
428 else if ((forced_auth_type == SIEVEAUTH_LOGIN || forced_auth_type == 0) &&
429 (session->avail_auth_type & SIEVEAUTH_LOGIN) != 0)
430 return sieve_auth_login(session);
431 else if ((forced_auth_type == SIEVEAUTH_PLAIN || forced_auth_type == 0) &&
432 (session->avail_auth_type & SIEVEAUTH_PLAIN) != 0)
433 return sieve_auth_plain(session);
434 else if (forced_auth_type == 0) {
435 log_warning(LOG_PROTOCOL, _("No Sieve auth method available\n"));
436 session->state = SIEVE_RETRY_AUTH;
439 log_warning(LOG_PROTOCOL, _("Selected Sieve auth method not available\n"));
440 session->state = SIEVE_RETRY_AUTH;
447 static void sieve_session_putscript_cb(SieveSession *session, SieveResult *result)
449 /* Remove script name from the beginning the response,
450 * which is added by Dovecot/Pigeonhole */
451 gchar *start, *desc = result->description;
454 /* callback just for the status */
455 command_cb(session->current_cmd, result);
457 while (desc && desc[0]) {
458 if ((end = strchr(desc, '\r')) ||
459 (end = strchr(desc, '\n')))
460 while (*end == '\n' || *end == '\r')
462 if (g_str_has_prefix(desc, "NULL_") && (start = strchr(desc+5, ':'))) {
466 /* TODO: match against known script name, in case it contains
467 * weird text like ": line " */
468 } else if ((start = strstr(desc, ": line ")) ||
469 (start = strstr(desc, ": error"))) {
472 result->description = desc;
473 command_cb(session->current_cmd, result);
478 static inline gboolean response_is_ok(const char *msg)
480 return !strncmp(msg, "OK", 2) && (!msg[2] || msg[2] == ' ');
483 static inline gboolean response_is_no(const char *msg)
485 return !strncmp(msg, "NO", 2) && (!msg[2] || msg[2] == ' ');
488 static inline gboolean response_is_bye(const char *msg)
490 return !strncmp(msg, "BYE", 3) && (!msg[3] || msg[3] == ' ');
493 static void sieve_got_capability(SieveSession *session, gchar *cap_name,
496 if (strcmp(cap_name, "SASL") == 0) {
497 SieveAuthType auth_type = 0;
499 for (auth = cap_value; auth && auth[0]; auth = end) {
500 if ((end = strchr(auth, ' ')))
502 if (strcmp(auth, "PLAIN") == 0) {
503 auth_type |= SIEVEAUTH_PLAIN;
504 } else if (strcmp(auth, "CRAM-MD5") == 0) {
505 auth_type |= SIEVEAUTH_CRAM_MD5;
506 } else if (strcmp(auth, "LOGIN") == 0) {
507 auth_type |= SIEVEAUTH_LOGIN;
510 session->avail_auth_type = auth_type;
512 } else if (strcmp(cap_name, "STARTTLS") == 0) {
513 session->capability.starttls = TRUE;
517 static void log_send(SieveSession *session, SieveCommand *cmd)
519 gchar *end, *msg = cmd->msg;
520 if (cmd->next_state == SIEVE_PUTSCRIPT && (end = strchr(msg, '\n'))) {
521 /* Don't log the script data */
522 msg = g_strndup(msg, end - msg);
523 log_print(LOG_PROTOCOL, "Sieve> %s\n", msg);
527 log_print(LOG_PROTOCOL, "Sieve> %s\n", msg);
530 static gint sieve_pop_send_queue(SieveSession *session)
533 GSList *send_queue = session->send_queue;
535 if (session->current_cmd) {
536 command_free(session->current_cmd);
537 session->current_cmd = NULL;
543 cmd = (SieveCommand *)send_queue->data;
544 session->send_queue = g_slist_next(send_queue);
545 g_slist_free_1(send_queue);
547 log_send(session, cmd);
548 session->state = cmd->next_state;
549 session->current_cmd = cmd;
550 if (session_send_msg(SESSION(session), cmd->msg) < 0)
556 static void parse_split(gchar *line, gchar **first_word, gchar **second_word)
563 if (line[0] == '"' && ((second = strchr(line + 1, '"')))) {
566 if (second[0] == ' ')
568 } else if ((second = strchr(line, ' '))) {
573 if (second && second[0] == '"' &&
574 ((end = strchr(second + 1, '"')))) {
580 *second_word = second;
583 static void unquote_inplace(gchar *str)
588 for (src = str+1, dest = str; src && *src && *src != '"'; src++) {
597 static void parse_response(gchar *msg, SieveResult *result)
601 cm_return_if_fail(msg != NULL);
603 /* response status */
604 if (isalpha(msg[0])) {
605 end = strchr(msg, ' ');
611 result->success = strcmp(msg, "OK") == 0;
612 result->has_status = TRUE;
615 result->has_status = FALSE;
619 if (msg && msg[0] == '(' && (end = strchr(msg, ')'))) {
623 strcmp(msg, "WARNINGS") == 0 ? SIEVE_CODE_WARNINGS :
624 strcmp(msg, "TRYLATER") == 0 ? SIEVE_CODE_TRYLATER :
630 result->code = SIEVE_CODE_NONE;
634 if (msg && msg[0] == '{' && (end = strchr(msg, '}'))) {
637 if (msg[0] == '0' && msg+1 == end) {
638 result->has_octets = TRUE;
642 (result->octets = g_ascii_strtoll(msg, NULL, 10)) != 0;
648 result->has_octets = FALSE;
654 unquote_inplace(msg);
655 result->description = msg;
657 result->description = NULL;
661 static gint sieve_session_recv_msg(Session *session, const gchar *msg)
663 SieveSession *sieve_session = SIEVE_SESSION(session);
667 log_print(LOG_PROTOCOL, "Sieve< %s\n", msg);
668 if (response_is_bye(msg)) {
670 parse_response((gchar *)msg, &result);
671 if (!result.description)
672 status = g_strdup(_("Disconnected"));
673 else if (g_str_has_prefix(result.description, "Disconnected"))
674 status = g_strdup(result.description);
676 status = g_strdup_printf(_("Disconnected: %s"), result.description);
677 sieve_session->error = SE_ERROR;
678 sieve_error(sieve_session, status);
679 sieve_session->state = SIEVE_DISCONNECTED;
684 switch (sieve_session->state) {
685 case SIEVE_CAPABILITIES:
686 if (response_is_ok(msg)) {
687 /* capabilities list done */
690 if (sieve_session->tls_init_done == FALSE &&
691 sieve_session->config->tls_type != SIEVE_TLS_NO) {
692 if (sieve_session->capability.starttls) {
693 log_print(LOG_PROTOCOL, "Sieve> STARTTLS\n");
694 session_send_msg(session, "STARTTLS");
695 sieve_session->state = SIEVE_STARTTLS;
696 } else if (sieve_session->config->tls_type == SIEVE_TLS_YES) {
697 log_warning(LOG_PROTOCOL, "Sieve: does not support STARTTLS\n");
698 sieve_session->state = SIEVE_ERROR;
700 log_warning(LOG_PROTOCOL, "Sieve: continuing without TLS\n");
701 sieve_session->state = SIEVE_READY;
706 /* authenticate after getting capabilities */
707 if (!sieve_session->authenticated) {
708 ret = sieve_auth(sieve_session);
710 sieve_session->state = SIEVE_READY;
711 sieve_connected(sieve_session, TRUE);
714 /* got a capability */
715 gchar *cap_name, *cap_value;
716 parse_split((gchar *)msg, &cap_name, &cap_value);
717 sieve_got_capability(sieve_session, cap_name, cap_value);
723 log_warning(LOG_PROTOCOL,
724 _("unhandled message on Sieve session: %s\n"), msg);
728 if (session_start_tls(session) < 0) {
729 sieve_session->state = SIEVE_ERROR;
730 sieve_session->error = SE_ERROR;
731 sieve_error(sieve_session, _("TLS failed"));
734 sieve_session->tls_init_done = TRUE;
735 sieve_session->state = SIEVE_CAPABILITIES;
739 ret = sieve_auth_recv(sieve_session, msg);
741 case SIEVE_AUTH_LOGIN_USER:
742 ret = sieve_auth_login_user_recv(sieve_session, msg);
744 case SIEVE_AUTH_PLAIN:
745 case SIEVE_AUTH_LOGIN_PASS:
746 case SIEVE_AUTH_CRAM_MD5:
747 if (response_is_no(msg)) {
748 log_print(LOG_PROTOCOL, "Sieve auth failed\n");
749 session->state = SIEVE_RETRY_AUTH;
751 } else if (response_is_ok(msg)) {
752 log_print(LOG_PROTOCOL, "Sieve auth completed\n");
753 sieve_error(sieve_session, "");
754 sieve_session->authenticated = TRUE;
755 sieve_session->state = SIEVE_READY;
756 sieve_connected(sieve_session, TRUE);
760 if (!response_is_ok(msg)) {
761 sieve_session->state = SIEVE_ERROR;
763 sieve_session->state = SIEVE_READY;
765 case SIEVE_LISTSCRIPTS:
766 if (response_is_no(msg)) {
767 /* got an error. probably not authenticated. */
768 command_cb(sieve_session->current_cmd, NULL);
769 sieve_session->state = SIEVE_READY;
770 } else if (response_is_ok(msg)) {
772 sieve_session->state = SIEVE_READY;
773 sieve_session->error = SE_OK;
774 command_cb(sieve_session->current_cmd,
775 (gpointer)&(SieveScript){0});
777 /* got a script name */
779 gchar *script_status;
781 parse_split((gchar *)msg, &script.name, &script_status);
782 script.active = (script_status &&
783 strcasecmp(script_status, "active") == 0);
785 command_cb(sieve_session->current_cmd,
789 case SIEVE_RENAMESCRIPT:
790 if (response_is_no(msg)) {
792 command_cb(sieve_session->current_cmd, NULL);
793 } else if (response_is_ok(msg)) {
794 command_cb(sieve_session->current_cmd, (void*)TRUE);
796 log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
798 sieve_session->state = SIEVE_READY;
800 case SIEVE_SETACTIVE:
801 parse_response((gchar *)msg, &result);
802 if (result.success) {
803 /* clear status possibly set when setting another
804 * script active. TODO: give textual feedback */
805 sieve_error(sieve_session, "");
807 command_cb(sieve_session->current_cmd, NULL);
808 } else if (result.description) {
809 command_cb(sieve_session->current_cmd,
812 log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
814 if (result.has_octets) {
815 return sieve_session_recv_chunk(sieve_session,
818 sieve_session->state = SIEVE_READY;
821 case SIEVE_GETSCRIPT:
822 if (response_is_no(msg)) {
823 command_cb(sieve_session->current_cmd, (void *)-1);
824 sieve_session->state = SIEVE_READY;
826 parse_response((gchar *)msg, &result);
827 sieve_session->state = SIEVE_GETSCRIPT_DATA;
828 return sieve_session_recv_chunk(sieve_session,
832 case SIEVE_GETSCRIPT_DATA:
835 sieve_session->state = SIEVE_READY;
836 if (response_is_ok(msg)) {
837 command_cb(sieve_session->current_cmd, NULL);
839 log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
842 case SIEVE_PUTSCRIPT:
845 parse_response((gchar *)msg, &result);
846 sieve_session_putscript_cb(sieve_session, &result);
847 if (result.has_octets) {
848 return sieve_session_recv_chunk(sieve_session,
851 sieve_session->state = SIEVE_READY;
854 case SIEVE_DELETESCRIPT:
855 parse_response((gchar *)msg, &result);
856 if (!result.success) {
857 command_cb(sieve_session->current_cmd,
860 command_cb(sieve_session->current_cmd, NULL);
862 sieve_session->state = SIEVE_READY;
865 log_warning(LOG_PROTOCOL, _("error occurred on Sieve session. data: %s\n"), msg);
866 sieve_session->error = SE_ERROR;
868 case SIEVE_RETRY_AUTH:
869 log_warning(LOG_PROTOCOL, _("unhandled message on Sieve session: %s\n"),
871 ret = sieve_auth(sieve_session);
874 log_warning(LOG_PROTOCOL, _("unhandled message on Sieve session: %d\n"),
875 sieve_session->state);
876 sieve_session->error = SE_ERROR;
880 if (ret == SE_OK && sieve_session->state == SIEVE_READY)
881 ret = sieve_pop_send_queue(sieve_session);
884 return session_recv_msg(session);
885 } else if (ret == SE_AUTHFAIL) {
886 sieve_error(sieve_session, _("Auth failed"));
887 sieve_session->state = SIEVE_ERROR;
888 sieve_session->error = SE_ERROR;
894 static gint sieve_recv_message(Session *session, const gchar *msg,
900 static void sieve_read_chunk(SieveSession *session, gchar *data, guint len)
902 log_print(LOG_PROTOCOL, "Sieve< [%u bytes]\n", len);
904 switch (session->state) {
905 case SIEVE_GETSCRIPT_DATA:
906 command_cb(session->current_cmd, (gchar *)data);
908 case SIEVE_SETACTIVE:
909 /* Dovecot shows a script's warnings when making it active */
910 /* TODO: append message in case it is very long*/
912 sieve_error(session, data);
914 case SIEVE_PUTSCRIPT: {
915 SieveResult result = {.description = (gchar *)data};
916 sieve_session_putscript_cb(session, &result);
920 log_warning(LOG_PROTOCOL,
921 _("error occurred on SIEVE session\n"));
925 static gint sieve_read_chunk_done(SieveSession *session)
929 switch (session->state) {
930 case SIEVE_GETSCRIPT_DATA:
931 /* wait for ending "OK" response */
933 case SIEVE_SETACTIVE:
934 case SIEVE_PUTSCRIPT:
935 session->state = SIEVE_READY;
938 log_warning(LOG_PROTOCOL,
939 _("error occurred on SIEVE session\n"));
942 if (ret == SE_OK && session->state == SIEVE_READY)
943 ret = sieve_pop_send_queue(session);
946 return session_recv_msg(SESSION(session));
951 static gint sieve_cmd_noop(SieveSession *session)
953 log_print(LOG_PROTOCOL, "Sieve> NOOP\n");
954 session->state = SIEVE_NOOP;
955 if (session_send_msg(SESSION(session), "NOOP") < 0) {
956 session->state = SIEVE_ERROR;
957 session->error = SE_ERROR;
963 static gboolean sieve_ping(gpointer data)
965 Session *session = SESSION(data);
966 SieveSession *sieve_session = SIEVE_SESSION(session);
968 if (sieve_session->state == SIEVE_ERROR || session->state == SESSION_ERROR)
970 if (sieve_session->state != SIEVE_READY)
973 return sieve_cmd_noop(sieve_session) == 0;
976 static void sieve_session_destroy(Session *session)
978 SieveSession *sieve_session = SIEVE_SESSION(session);
979 g_free(sieve_session->pass);
980 if (sieve_session->current_cmd)
981 command_abort(sieve_session->current_cmd);
982 sessions = g_slist_remove(sessions, (gconstpointer)session);
983 g_slist_free_full(sieve_session->send_queue,
984 (GDestroyNotify)command_abort);
987 static void sieve_connect_finished(Session *session, gboolean success)
990 sieve_connected(SIEVE_SESSION(session), FALSE);
994 static gint sieve_session_connect(SieveSession *session)
996 session->state = SIEVE_CAPABILITIES;
997 session->authenticated = FALSE;
999 session->tls_init_done = FALSE;
1001 return session_connect(SESSION(session), session->host,
1005 static SieveSession *sieve_session_new(PrefsAccount *account)
1007 SieveSession *session;
1008 session = g_new0(SieveSession, 1);
1009 session_init(SESSION(session), account, FALSE);
1011 session->account = account;
1013 SESSION(session)->recv_msg = sieve_session_recv_msg;
1014 SESSION(session)->destroy = sieve_session_destroy;
1015 SESSION(session)->connect_finished = sieve_connect_finished;
1016 session_set_recv_message_notify(SESSION(session), sieve_recv_message, NULL);
1018 sieve_session_reset(session);
1022 static void sieve_session_reset(SieveSession *session)
1024 PrefsAccount *account = session->account;
1025 SieveAccountConfig *config = sieve_prefs_account_get_config(account);
1026 gboolean reuse_auth = (config->auth == SIEVEAUTH_REUSE);
1028 g_slist_free_full(session->send_queue, (GDestroyNotify)command_abort);
1030 session_disconnect(SESSION(session));
1032 SESSION(session)->ssl_cert_auto_accept = account->ssl_certs_auto_accept;
1033 SESSION(session)->nonblocking = account->use_nonblocking_ssl;
1034 session->authenticated = FALSE;
1035 session->current_cmd = NULL;
1036 session->send_queue = NULL;
1037 session->state = SIEVE_CAPABILITIES;
1039 session->tls_init_done = FALSE;
1041 session->avail_auth_type = 0;
1042 session->auth_type = 0;
1043 session->config = config;
1044 session->host = config->use_host ? config->host : account->recv_server;
1045 session->port = config->use_port ? config->port : SIEVE_PORT;
1046 session->user = reuse_auth ? account->userid : session->config->userid;
1047 session->forced_auth_type = config->auth_type;
1048 session_register_ping(SESSION(session), sieve_ping);
1051 g_free(session->pass);
1052 if (config->auth == SIEVEAUTH_NONE) {
1053 session->pass = NULL;
1054 } else if (reuse_auth && (session->pass = passwd_store_get_account(
1055 account->account_id, PWS_ACCOUNT_RECV))) {
1056 } else if ((session->pass = passwd_store_get_account(
1057 account->account_id, "sieve"))) {
1058 } else if (password_get(session->user, session->host, "sieve",
1059 session->port, &session->pass)) {
1061 session->pass = input_dialog_query_password_keep(session->host,
1062 session->user, &(session->pass));
1064 if (!session->pass) {
1065 session->pass = g_strdup("");
1066 session->use_auth = FALSE;
1068 session->use_auth = TRUE;
1072 SESSION(session)->ssl_type =
1073 (config->tls_type == SIEVE_TLS_NO) ? SSL_NONE : SSL_STARTTLS;
1077 /* When an account config is changed, reset associated sessions. */
1078 void sieve_account_prefs_updated(PrefsAccount *account)
1081 SieveSession *session;
1083 for (item = sessions; item; item = item->next) {
1084 session = (SieveSession *)item->data;
1085 if (session->account == account) {
1086 log_print(LOG_PROTOCOL, "Sieve: resetting session\n");
1087 sieve_session_reset(session);
1092 SieveSession *sieve_session_get_for_account(PrefsAccount *account)
1094 SieveSession *session;
1098 for (item = sessions; item; item = item->next) {
1099 session = (SieveSession *)item->data;
1100 if (session->account == account) {
1106 session = sieve_session_new(account);
1107 sessions = g_slist_prepend(sessions, session);
1112 static void sieve_queue_send(SieveSession *session, SieveState next_state,
1113 gchar *msg, sieve_session_data_cb_fn cb, gpointer data)
1115 gboolean queue = FALSE;
1116 SieveCommand *cmd = g_new0(SieveCommand, 1);
1117 cmd->session = session;
1118 cmd->next_state = next_state;
1123 if (!session_is_connected(SESSION(session))) {
1124 log_print(LOG_PROTOCOL, "Sieve: connecting to %s:%hu\n",
1125 session->host, session->port);
1126 if (sieve_session_connect(session) < 0) {
1127 sieve_connect_finished(SESSION(session), FALSE);
1130 } else if (session->state == SIEVE_RETRY_AUTH) {
1131 log_print(LOG_PROTOCOL, _("Sieve: retrying auth\n"));
1132 if (sieve_auth(session) == SE_AUTHFAIL)
1133 sieve_error(session, _("Auth method not available"));
1135 } else if (session->state != SIEVE_READY) {
1136 log_print(LOG_PROTOCOL, "Sieve: in state %d\n", session->state);
1141 session->send_queue = g_slist_prepend(session->send_queue, cmd);
1143 if (session->current_cmd)
1144 command_free(session->current_cmd);
1145 session->current_cmd = cmd;
1146 session->state = next_state;
1147 log_send(session, cmd);
1148 if (session_send_msg(SESSION(session), cmd->msg) < 0) {
1154 void sieve_session_list_scripts(SieveSession *session,
1155 sieve_session_data_cb_fn cb, gpointer data)
1157 gchar *msg = g_strdup("LISTSCRIPTS");
1158 sieve_queue_send(session, SIEVE_LISTSCRIPTS, msg, cb, data);
1161 void sieve_session_set_active_script(SieveSession *session,
1162 const gchar *filter_name,
1163 sieve_session_data_cb_fn cb, gpointer data)
1165 gchar *msg = g_strdup_printf("SETACTIVE \"%s\"",
1166 filter_name ? filter_name : "");
1168 cb(session, FALSE, (void*)FALSE, data);
1172 sieve_queue_send(session, SIEVE_SETACTIVE, msg, cb, data);
1175 void sieve_session_rename_script(SieveSession *session,
1176 const gchar *name_old, const char *name_new,
1177 sieve_session_data_cb_fn cb, gpointer data)
1179 gchar *msg = g_strdup_printf("RENAMESCRIPT \"%s\" \"%s\"",
1180 name_old, name_new);
1182 sieve_queue_send(session, SIEVE_RENAMESCRIPT, msg, cb, data);
1185 void sieve_session_get_script(SieveSession *session, const gchar *filter_name,
1186 sieve_session_data_cb_fn cb, gpointer data)
1188 gchar *msg = g_strdup_printf("GETSCRIPT \"%s\"",
1191 sieve_queue_send(session, SIEVE_GETSCRIPT, msg, cb, data);
1194 void sieve_session_put_script(SieveSession *session, const gchar *filter_name,
1195 gint len, const gchar *script_contents,
1196 sieve_session_data_cb_fn cb, gpointer data)
1198 /* TODO: refactor so don't have to copy the whole script here */
1199 gchar *msg = g_strdup_printf("PUTSCRIPT \"%s\" {%u+}%s%s",
1200 filter_name, len, len > 0 ? "\r\n" : "",
1203 sieve_queue_send(session, SIEVE_PUTSCRIPT, msg, cb, data);
1206 void sieve_session_check_script(SieveSession *session,
1207 gint len, const gchar *script_contents,
1208 sieve_session_data_cb_fn cb, gpointer data)
1210 gchar *msg = g_strdup_printf("CHECKSCRIPT {%u+}%s%s",
1211 len, len > 0 ? "\r\n" : "", script_contents);
1213 sieve_queue_send(session, SIEVE_PUTSCRIPT, msg, cb, data);
1216 void sieve_session_delete_script(SieveSession *session,
1217 const gchar *filter_name,
1218 sieve_session_data_cb_fn cb, gpointer data)
1220 gchar *msg = g_strdup_printf("DELETESCRIPT \"%s\"",
1223 sieve_queue_send(session, SIEVE_DELETESCRIPT, msg, cb, data);