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"
34 #include "prefs_common.h"
36 #include "managesieve.h"
37 #include "sieve_editor.h"
38 #include "sieve_prefs.h"
40 GSList *sessions = NULL;
42 static void sieve_session_destroy(Session *session);
43 static gint sieve_pop_send_queue(SieveSession *session);
44 static void sieve_session_reset(SieveSession *session);
45 static void command_free(SieveCommand *cmd);
46 static void command_abort(SieveCommand *cmd);
47 static void command_cb(SieveCommand *cmd, gpointer result);
48 static gint sieve_session_recv_chunk(SieveSession *, guint len);
49 static void sieve_read_chunk(SieveSession *, gchar *data, guint len);
50 static gint sieve_read_chunk_done(SieveSession *session);
52 void sieve_sessions_close()
55 GSList *list = sessions;
57 g_slist_free_full(list, (GDestroyNotify)session_destroy);
61 /* remove all command callbacks with a given data pointer */
62 void sieve_sessions_discard_callbacks(gpointer user_data)
67 SieveSession *session;
70 for (item = sessions; item; item = item->next) {
71 session = (SieveSession *)item->data;
72 cmd = session->current_cmd;
73 /* abort current command handler */
74 if (cmd && cmd->data == user_data) {
76 session->current_cmd = NULL;
78 /* abort queued command handlers */
79 for (queue = session->send_queue; queue; queue = queue->next) {
80 cmd = (SieveCommand *)queue->data;
81 if (cmd && cmd->data == user_data) {
83 prev->next = queue->next;
85 session->send_queue = NULL;
87 g_slist_free_1(queue);
95 static void command_cb(SieveCommand *cmd, gpointer result)
98 cmd->cb(cmd->session, FALSE, result, cmd->data);
101 static void command_abort(SieveCommand *cmd)
103 cmd->cb(cmd->session, TRUE, NULL, cmd->data);
108 static void command_free(SieveCommand *cmd)
114 void sieve_session_handle_status(SieveSession *session,
115 sieve_session_error_cb_fn on_error,
116 sieve_session_connected_cb_fn on_connected,
119 session->on_error = on_error;
120 session->on_connected = on_connected;
121 session->cb_data = data;
124 static void sieve_error(SieveSession *session, const gchar *msg)
126 if (session->on_error)
127 session->on_error(session, msg, session->cb_data);
130 static void sieve_connected(SieveSession *session, gboolean connected)
132 if (session->on_connected)
133 session->on_connected(session, connected, session->cb_data);
136 static gboolean sieve_read_chunk_cb(SockInfo *source,
137 GIOCondition condition, gpointer data)
139 SieveSession *sieve_session = SIEVE_SESSION(data);
140 Session *session = &sieve_session->session;
144 cm_return_val_if_fail(condition == G_IO_IN, FALSE);
146 session_set_timeout(session, session->timeout_interval);
148 if (session->read_buf_len == 0) {
152 read_len = sock_read(session->sock,
154 SESSION_BUFFSIZE - 1);
156 if (read_len == -1 &&
157 session->state == SESSION_DISCONNECTED) {
158 g_warning("sock_read: session disconnected");
159 if (session->io_tag > 0) {
160 g_source_remove(session->io_tag);
167 g_warning("sock_read: received EOF");
168 session->state = SESSION_EOF;
177 g_warning("sock_read: %s",
179 session->state = SESSION_ERROR;
184 session->read_buf_len = read_len;
187 data_len = MIN(session->read_buf_len,
188 sieve_session->octets_remaining);
189 sieve_session->octets_remaining -= data_len;
190 session->read_buf_len -= data_len;
191 session->read_buf_p[data_len] = '\0';
193 /* progress callback */
194 sieve_read_chunk(sieve_session, session->read_buf_p, data_len);
196 if (session->read_buf_len == 0) {
197 session->read_buf_p = session->read_buf;
199 session->read_buf_p += data_len;
202 /* incomplete read */
203 if (sieve_session->octets_remaining > 0)
207 if (session->io_tag > 0) {
208 g_source_remove(session->io_tag);
212 /* completion callback */
213 ret = sieve_read_chunk_done(sieve_session);
216 session->state = SESSION_ERROR;
221 static gboolean sieve_read_chunk_idle_cb(gpointer data)
223 Session *session = SESSION(data);
226 ret = sieve_read_chunk_cb(session->sock, G_IO_IN, session);
229 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
230 sieve_read_chunk_cb, session);
235 /* Get data of specified length.
236 * If needed elsewhere, this should be put in session.c */
237 static gint sieve_session_recv_chunk(SieveSession *sieve_session,
240 Session *session = &sieve_session->session;
241 cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
243 session->state = SESSION_RECV;
244 sieve_session->octets_remaining = bytes;
246 if (session->read_buf_len > 0)
247 g_idle_add(sieve_read_chunk_idle_cb, session);
249 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
250 sieve_read_chunk_cb, session);
255 static gint sieve_auth_recv(SieveSession *session, const gchar *msg)
257 gchar buf[MESSAGEBUFSIZE], *tmp;
259 switch (session->auth_type) {
260 case SIEVEAUTH_LOGIN:
261 session->state = SIEVE_AUTH_LOGIN_USER;
263 if (strstr(msg, "VXNlcm5hbWU6")) {
264 tmp = g_base64_encode(session->user, strlen(session->user));
265 g_snprintf(buf, sizeof(buf), "\"%s\"", tmp);
267 if (session_send_msg(SESSION(session), buf) < 0) {
272 log_print(LOG_PROTOCOL, "Sieve> [USERID]\n");
274 /* Server rejects AUTH */
275 if (session_send_msg(SESSION(session), "\"*\"") < 0)
277 log_print(LOG_PROTOCOL, "Sieve> *\n");
280 case SIEVEAUTH_CRAM_MD5:
281 session->state = SIEVE_AUTH_CRAM_MD5;
286 gchar *challenge, *tmp;
288 guchar hexdigest[33];
290 tmp = g_base64_decode(msg + 1, &challengelen);
291 challenge = g_strndup(tmp, challengelen);
293 log_print(LOG_PROTOCOL, "Sieve< [Decoded: %s]\n", challenge);
295 g_snprintf(buf, sizeof(buf), "%s", session->pass);
296 md5_hex_hmac(hexdigest, challenge, challengelen,
297 buf, strlen(session->pass));
300 response = g_strdup_printf
301 ("%s %s", session->user, hexdigest);
302 log_print(LOG_PROTOCOL, "Sieve> [Encoded: %s]\n", response);
304 response64 = g_base64_encode(response, strlen(response));
307 response = g_strdup_printf("\"%s\"", response64);
310 if (session_send_msg(SESSION(session), response) < 0) {
314 log_print(LOG_PROTOCOL, "Sieve> %s\n", response);
317 /* Server rejects AUTH */
318 if (session_send_msg(SESSION(session), "\"*\"") < 0)
320 log_print(LOG_PROTOCOL, "Sieve> *\n");
324 /* stop sieve_auth when no correct authtype */
325 if (session_send_msg(SESSION(session), "*") < 0)
327 log_print(LOG_PROTOCOL, "Sieve> *\n");
334 static gint sieve_auth_login_user_recv(SieveSession *session, const gchar *msg)
338 session->state = SIEVE_AUTH_LOGIN_PASS;
340 if (strstr(msg, "UGFzc3dvcmQ6")) {
341 tmp2 = g_base64_encode(session->pass, strlen(session->pass));
342 tmp = g_strdup_printf("\"%s\"", tmp2);
345 /* Server rejects AUTH */
346 tmp = g_strdup("\"*\"");
349 if (session_send_msg(SESSION(session), tmp) < 0) {
355 log_print(LOG_PROTOCOL, "Sieve> [PASSWORD]\n");
361 static gint sieve_auth_cram_md5(SieveSession *session)
363 session->state = SIEVE_AUTH;
364 session->auth_type = SIEVEAUTH_CRAM_MD5;
366 if (session_send_msg(SESSION(session), "Authenticate \"CRAM-MD5\"") < 0)
368 log_print(LOG_PROTOCOL, "Sieve> Authenticate CRAM-MD5\n");
373 static gint sieve_auth_plain(SieveSession *session)
375 gchar buf[MESSAGEBUFSIZE], *b64buf, *out;
378 session->state = SIEVE_AUTH_PLAIN;
379 session->auth_type = SIEVEAUTH_PLAIN;
381 memset(buf, 0, sizeof buf);
383 /* "\0user\0password" */
384 len = sprintf(buf, "%c%s%c%s", '\0', session->user, '\0', session->pass);
385 b64buf = g_base64_encode(buf, len);
386 out = g_strconcat("Authenticate \"PLAIN\" \"", b64buf, "\"", NULL);
389 if (session_send_msg(SESSION(session), out) < 0) {
396 log_print(LOG_PROTOCOL, "Sieve> [Authenticate PLAIN]\n");
401 static gint sieve_auth_login(SieveSession *session)
403 session->state = SIEVE_AUTH;
404 session->auth_type = SIEVEAUTH_LOGIN;
406 if (session_send_msg(SESSION(session), "Authenticate \"LOGIN\"") < 0)
408 log_print(LOG_PROTOCOL, "Sieve> Authenticate LOGIN\n");
413 static gint sieve_auth(SieveSession *session)
415 SieveAuthType forced_auth_type = session->forced_auth_type;
417 if (!session->use_auth) {
418 session->state = SIEVE_READY;
419 sieve_connected(session, TRUE);
423 session->state = SIEVE_AUTH;
424 sieve_error(session, _("Authenticating..."));
426 if ((forced_auth_type == SIEVEAUTH_CRAM_MD5 || forced_auth_type == 0) &&
427 (session->avail_auth_type & SIEVEAUTH_CRAM_MD5) != 0)
428 return sieve_auth_cram_md5(session);
429 else if ((forced_auth_type == SIEVEAUTH_LOGIN || forced_auth_type == 0) &&
430 (session->avail_auth_type & SIEVEAUTH_LOGIN) != 0)
431 return sieve_auth_login(session);
432 else if ((forced_auth_type == SIEVEAUTH_PLAIN || forced_auth_type == 0) &&
433 (session->avail_auth_type & SIEVEAUTH_PLAIN) != 0)
434 return sieve_auth_plain(session);
435 else if (forced_auth_type == 0) {
436 log_warning(LOG_PROTOCOL, _("No Sieve auth method available\n"));
437 session->state = SIEVE_RETRY_AUTH;
440 log_warning(LOG_PROTOCOL, _("Selected Sieve auth method not available\n"));
441 session->state = SIEVE_RETRY_AUTH;
448 static void sieve_session_putscript_cb(SieveSession *session, SieveResult *result)
450 /* Remove script name from the beginning the response,
451 * which is added by Dovecot/Pigeonhole */
452 gchar *start, *desc = result->description;
455 /* callback just for the status */
456 command_cb(session->current_cmd, result);
458 while (desc && desc[0]) {
459 if ((end = strchr(desc, '\r')) ||
460 (end = strchr(desc, '\n')))
461 while (*end == '\n' || *end == '\r')
463 if (g_str_has_prefix(desc, "NULL_") && (start = strchr(desc+5, ':'))) {
467 /* TODO: match against known script name, in case it contains
468 * weird text like ": line " */
469 } else if ((start = strstr(desc, ": line ")) ||
470 (start = strstr(desc, ": error"))) {
473 result->description = desc;
474 command_cb(session->current_cmd, result);
479 static inline gboolean response_is_ok(const char *msg)
481 return !strncmp(msg, "OK", 2) && (!msg[2] || msg[2] == ' ');
484 static inline gboolean response_is_no(const char *msg)
486 return !strncmp(msg, "NO", 2) && (!msg[2] || msg[2] == ' ');
489 static inline gboolean response_is_bye(const char *msg)
491 return !strncmp(msg, "BYE", 3) && (!msg[3] || msg[3] == ' ');
494 static void sieve_got_capability(SieveSession *session, gchar *cap_name,
497 if (strcmp(cap_name, "SASL") == 0) {
498 SieveAuthType auth_type = 0;
500 for (auth = cap_value; auth && auth[0]; auth = end) {
501 if ((end = strchr(auth, ' ')))
503 if (strcmp(auth, "PLAIN") == 0) {
504 auth_type |= SIEVEAUTH_PLAIN;
505 } else if (strcmp(auth, "CRAM-MD5") == 0) {
506 auth_type |= SIEVEAUTH_CRAM_MD5;
507 } else if (strcmp(auth, "LOGIN") == 0) {
508 auth_type |= SIEVEAUTH_LOGIN;
511 session->avail_auth_type = auth_type;
513 } else if (strcmp(cap_name, "STARTTLS") == 0) {
514 session->capability.starttls = TRUE;
518 static void log_send(SieveSession *session, SieveCommand *cmd)
520 gchar *end, *msg = cmd->msg;
521 if (cmd->next_state == SIEVE_PUTSCRIPT && (end = strchr(msg, '\n'))) {
522 /* Don't log the script data */
523 msg = g_strndup(msg, end - msg);
524 log_print(LOG_PROTOCOL, "Sieve> %s\n", msg);
528 log_print(LOG_PROTOCOL, "Sieve> %s\n", msg);
531 static gint sieve_pop_send_queue(SieveSession *session)
534 GSList *send_queue = session->send_queue;
536 if (session->current_cmd) {
537 command_free(session->current_cmd);
538 session->current_cmd = NULL;
544 cmd = (SieveCommand *)send_queue->data;
545 session->send_queue = g_slist_next(send_queue);
546 g_slist_free_1(send_queue);
548 log_send(session, cmd);
549 session->state = cmd->next_state;
550 session->current_cmd = cmd;
551 if (session_send_msg(SESSION(session), cmd->msg) < 0)
557 static void parse_split(gchar *line, gchar **first_word, gchar **second_word)
564 if (line[0] == '"' && ((second = strchr(line + 1, '"')))) {
567 if (second[0] == ' ')
569 } else if ((second = strchr(line, ' '))) {
574 if (second && second[0] == '"' &&
575 ((end = strchr(second + 1, '"')))) {
581 *second_word = second;
584 static void unquote_inplace(gchar *str)
589 for (src = str+1, dest = str; src && *src && *src != '"'; src++) {
598 static void parse_response(gchar *msg, SieveResult *result)
602 cm_return_if_fail(msg != NULL);
604 /* response status */
605 if (isalpha(msg[0])) {
606 end = strchr(msg, ' ');
612 result->success = strcmp(msg, "OK") == 0;
613 result->has_status = TRUE;
616 result->has_status = FALSE;
620 if (msg && msg[0] == '(' && (end = strchr(msg, ')'))) {
624 strcmp(msg, "WARNINGS") == 0 ? SIEVE_CODE_WARNINGS :
625 strcmp(msg, "TRYLATER") == 0 ? SIEVE_CODE_TRYLATER :
631 result->code = SIEVE_CODE_NONE;
635 if (msg && msg[0] == '{' && (end = strchr(msg, '}'))) {
638 if (msg[0] == '0' && msg+1 == end) {
639 result->has_octets = TRUE;
643 (result->octets = g_ascii_strtoll(msg, NULL, 10)) != 0;
649 result->has_octets = FALSE;
655 unquote_inplace(msg);
656 result->description = msg;
658 result->description = NULL;
662 static gint sieve_session_recv_msg(Session *session, const gchar *msg)
664 SieveSession *sieve_session = SIEVE_SESSION(session);
668 log_print(LOG_PROTOCOL, "Sieve< %s\n", msg);
669 if (response_is_bye(msg)) {
671 parse_response((gchar *)msg, &result);
672 if (!result.description)
673 status = g_strdup(_("Disconnected"));
674 else if (g_str_has_prefix(result.description, "Disconnected"))
675 status = g_strdup(result.description);
677 status = g_strdup_printf(_("Disconnected: %s"), result.description);
678 sieve_session->error = SE_ERROR;
679 sieve_error(sieve_session, status);
680 sieve_session->state = SIEVE_DISCONNECTED;
685 switch (sieve_session->state) {
686 case SIEVE_CAPABILITIES:
687 if (response_is_ok(msg)) {
688 /* capabilities list done */
691 if (sieve_session->tls_init_done == FALSE &&
692 sieve_session->config->tls_type != SIEVE_TLS_NO) {
693 if (sieve_session->capability.starttls) {
694 if (session_send_msg(session, "STARTTLS") < 0)
695 sieve_session->state = SIEVE_ERROR;
697 sieve_session->state = SIEVE_STARTTLS;
698 } else if (sieve_session->config->tls_type == SIEVE_TLS_YES) {
699 log_warning(LOG_PROTOCOL, "Sieve: does not support STARTTLS\n");
700 sieve_session->state = SIEVE_ERROR;
702 log_warning(LOG_PROTOCOL, "Sieve: continuing unencrypted\n");
703 sieve_session->state = SIEVE_READY;
708 /* authenticate after getting capabilities */
709 if (!sieve_session->authenticated) {
710 ret = sieve_auth(sieve_session);
712 sieve_session->state = SIEVE_READY;
713 sieve_connected(sieve_session, TRUE);
716 /* got a capability */
717 gchar *cap_name, *cap_value;
718 parse_split((gchar *)msg, &cap_name, &cap_value);
719 sieve_got_capability(sieve_session, cap_name, cap_value);
725 log_warning(LOG_PROTOCOL,
726 _("unhandled message on Sieve session: %s\n"), msg);
730 if (session_start_tls(session) < 0) {
731 sieve_session->state = SIEVE_ERROR;
732 sieve_session->error = SE_ERROR;
733 sieve_error(sieve_session, _("STARTTLS failed"));
736 sieve_session->tls_init_done = TRUE;
737 sieve_session->state = SIEVE_CAPABILITIES;
741 ret = sieve_auth_recv(sieve_session, msg);
743 case SIEVE_AUTH_LOGIN_USER:
744 ret = sieve_auth_login_user_recv(sieve_session, msg);
746 case SIEVE_AUTH_PLAIN:
747 case SIEVE_AUTH_LOGIN_PASS:
748 case SIEVE_AUTH_CRAM_MD5:
749 if (response_is_no(msg)) {
750 log_print(LOG_PROTOCOL, "Sieve auth failed\n");
751 sieve_session->state = SIEVE_RETRY_AUTH;
753 } else if (response_is_ok(msg)) {
754 log_print(LOG_PROTOCOL, "Sieve auth completed\n");
755 sieve_error(sieve_session, "");
756 sieve_session->authenticated = TRUE;
757 sieve_session->state = SIEVE_READY;
758 sieve_connected(sieve_session, TRUE);
762 if (!response_is_ok(msg)) {
763 sieve_session->state = SIEVE_ERROR;
765 sieve_session->state = SIEVE_READY;
767 case SIEVE_LISTSCRIPTS:
768 if (response_is_no(msg)) {
769 /* got an error. probably not authenticated. */
770 command_cb(sieve_session->current_cmd, NULL);
771 sieve_session->state = SIEVE_READY;
772 } else if (response_is_ok(msg)) {
774 sieve_session->state = SIEVE_READY;
775 sieve_session->error = SE_OK;
776 command_cb(sieve_session->current_cmd,
777 (gpointer)&(SieveScript){0});
779 /* got a script name */
781 gchar *script_status;
783 parse_split((gchar *)msg, &script.name, &script_status);
784 script.active = (script_status &&
785 strcasecmp(script_status, "active") == 0);
787 command_cb(sieve_session->current_cmd,
791 case SIEVE_RENAMESCRIPT:
792 if (response_is_no(msg)) {
794 command_cb(sieve_session->current_cmd, NULL);
795 } else if (response_is_ok(msg)) {
796 command_cb(sieve_session->current_cmd, (void*)TRUE);
798 log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
800 sieve_session->state = SIEVE_READY;
802 case SIEVE_SETACTIVE:
803 parse_response((gchar *)msg, &result);
804 if (result.success) {
805 /* clear status possibly set when setting another
806 * script active. TODO: give textual feedback */
807 sieve_error(sieve_session, "");
809 command_cb(sieve_session->current_cmd, NULL);
810 } else if (result.description) {
811 command_cb(sieve_session->current_cmd,
814 log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
816 if (result.has_octets) {
817 return sieve_session_recv_chunk(sieve_session,
820 sieve_session->state = SIEVE_READY;
823 case SIEVE_GETSCRIPT:
824 if (response_is_no(msg)) {
825 command_cb(sieve_session->current_cmd, (void *)-1);
826 sieve_session->state = SIEVE_READY;
828 parse_response((gchar *)msg, &result);
829 sieve_session->state = SIEVE_GETSCRIPT_DATA;
830 return sieve_session_recv_chunk(sieve_session,
834 case SIEVE_GETSCRIPT_DATA:
837 sieve_session->state = SIEVE_READY;
838 if (response_is_ok(msg)) {
839 command_cb(sieve_session->current_cmd, NULL);
841 log_warning(LOG_PROTOCOL, _("error occurred on SIEVE session\n"));
844 case SIEVE_PUTSCRIPT:
847 parse_response((gchar *)msg, &result);
848 sieve_session_putscript_cb(sieve_session, &result);
849 if (result.has_octets) {
850 return sieve_session_recv_chunk(sieve_session,
853 sieve_session->state = SIEVE_READY;
856 case SIEVE_DELETESCRIPT:
857 parse_response((gchar *)msg, &result);
858 if (!result.success) {
859 command_cb(sieve_session->current_cmd,
862 command_cb(sieve_session->current_cmd, NULL);
864 sieve_session->state = SIEVE_READY;
867 log_warning(LOG_PROTOCOL, _("error occurred on Sieve session. data: %s\n"), msg);
868 sieve_session->error = SE_ERROR;
870 case SIEVE_RETRY_AUTH:
871 log_warning(LOG_PROTOCOL, _("unhandled message on Sieve session: %s\n"),
873 ret = sieve_auth(sieve_session);
876 log_warning(LOG_PROTOCOL, _("unhandled message on Sieve session: %d\n"),
877 sieve_session->state);
878 sieve_session->error = SE_ERROR;
882 if (ret == SE_OK && sieve_session->state == SIEVE_READY)
883 ret = sieve_pop_send_queue(sieve_session);
886 return session_recv_msg(session);
887 } else if (ret == SE_AUTHFAIL) {
888 sieve_error(sieve_session, _("Auth failed"));
889 sieve_session->state = SIEVE_ERROR;
890 sieve_session->error = SE_ERROR;
896 static gint sieve_recv_message(Session *session, const gchar *msg,
902 static void sieve_read_chunk(SieveSession *session, gchar *data, guint len)
904 log_print(LOG_PROTOCOL, "Sieve< [%u bytes]\n", len);
906 switch (session->state) {
907 case SIEVE_GETSCRIPT_DATA:
908 command_cb(session->current_cmd, (gchar *)data);
910 case SIEVE_SETACTIVE:
911 /* Dovecot shows a script's warnings when making it active */
912 /* TODO: append message in case it is very long*/
914 sieve_error(session, data);
916 case SIEVE_PUTSCRIPT: {
917 SieveResult result = {.description = (gchar *)data};
918 sieve_session_putscript_cb(session, &result);
922 log_warning(LOG_PROTOCOL,
923 _("error occurred on SIEVE session\n"));
927 static gint sieve_read_chunk_done(SieveSession *session)
931 switch (session->state) {
932 case SIEVE_GETSCRIPT_DATA:
933 /* wait for ending "OK" response */
935 case SIEVE_SETACTIVE:
936 case SIEVE_PUTSCRIPT:
937 session->state = SIEVE_READY;
940 log_warning(LOG_PROTOCOL,
941 _("error occurred on SIEVE session\n"));
944 if (ret == SE_OK && session->state == SIEVE_READY)
945 ret = sieve_pop_send_queue(session);
948 return session_recv_msg(SESSION(session));
953 static gint sieve_cmd_noop(SieveSession *session)
955 log_print(LOG_PROTOCOL, "Sieve> NOOP\n");
956 session->state = SIEVE_NOOP;
957 if (session_send_msg(SESSION(session), "NOOP") < 0) {
958 session->state = SIEVE_ERROR;
959 session->error = SE_ERROR;
965 static gboolean sieve_ping(gpointer data)
967 Session *session = SESSION(data);
968 SieveSession *sieve_session = SIEVE_SESSION(session);
970 if (sieve_session->state == SIEVE_ERROR || session->state == SESSION_ERROR)
972 if (sieve_session->state != SIEVE_READY)
975 return sieve_cmd_noop(sieve_session) == 0;
978 static void sieve_session_destroy(Session *session)
980 SieveSession *sieve_session = SIEVE_SESSION(session);
981 g_free(sieve_session->pass);
982 if (sieve_session->current_cmd)
983 command_abort(sieve_session->current_cmd);
984 sessions = g_slist_remove(sessions, (gconstpointer)session);
985 g_slist_free_full(sieve_session->send_queue,
986 (GDestroyNotify)command_abort);
987 if (sieve_session->config)
988 sieve_prefs_account_free_config(sieve_session->config);
991 static void sieve_connect_finished(Session *session, gboolean success)
994 sieve_connected(SIEVE_SESSION(session), FALSE);
998 static gint sieve_session_connect(SieveSession *session)
1000 PrefsAccount *ac = session->account;
1001 ProxyInfo *proxy_info = NULL;
1003 session->state = SIEVE_CAPABILITIES;
1004 session->authenticated = FALSE;
1006 session->tls_init_done = FALSE;
1009 if (ac->use_proxy) {
1010 if (ac->use_default_proxy) {
1011 proxy_info = (ProxyInfo *)&(prefs_common_get_prefs()->proxy_info);
1012 if (proxy_info->use_proxy_auth)
1013 proxy_info->proxy_pass = passwd_store_get(PWS_CORE, PWS_CORE_PROXY,
1014 PWS_CORE_PROXY_PASS);
1016 proxy_info = (ProxyInfo *)&(ac->proxy_info);
1017 if (proxy_info->use_proxy_auth)
1018 proxy_info->proxy_pass = passwd_store_get_account(ac->account_id,
1019 PWS_ACCOUNT_PROXY_PASS);
1022 SESSION(session)->proxy_info = proxy_info;
1024 return session_connect(SESSION(session), session->host,
1028 static SieveSession *sieve_session_new(PrefsAccount *account)
1030 SieveSession *session;
1031 session = g_new0(SieveSession, 1);
1032 session_init(SESSION(session), account, FALSE);
1034 session->account = account;
1036 SESSION(session)->recv_msg = sieve_session_recv_msg;
1037 SESSION(session)->destroy = sieve_session_destroy;
1038 SESSION(session)->connect_finished = sieve_connect_finished;
1039 session_set_recv_message_notify(SESSION(session), sieve_recv_message, NULL);
1041 session->config = NULL;
1042 sieve_session_reset(session);
1046 static void sieve_session_reset(SieveSession *session)
1048 PrefsAccount *account = session->account;
1049 SieveAccountConfig *config = sieve_prefs_account_get_config(account);
1050 gboolean reuse_auth = (config->auth == SIEVEAUTH_REUSE);
1052 g_slist_free_full(session->send_queue, (GDestroyNotify)command_abort);
1054 session_disconnect(SESSION(session));
1056 SESSION(session)->ssl_cert_auto_accept = account->ssl_certs_auto_accept;
1057 SESSION(session)->nonblocking = account->use_nonblocking_ssl;
1058 session->authenticated = FALSE;
1059 session->current_cmd = NULL;
1060 session->send_queue = NULL;
1061 session->state = SIEVE_CAPABILITIES;
1063 session->tls_init_done = FALSE;
1064 SESSION(session)->use_tls_sni = account->use_tls_sni;
1066 session->avail_auth_type = 0;
1067 session->auth_type = 0;
1068 if (session->config)
1069 sieve_prefs_account_free_config(session->config);
1070 session->config = config;
1071 session->host = config->use_host ? config->host : account->recv_server;
1072 session->port = config->use_port ? config->port : SIEVE_PORT;
1073 session->user = reuse_auth ? account->userid : session->config->userid;
1074 session->forced_auth_type = config->auth_type;
1075 session_register_ping(SESSION(session), sieve_ping);
1078 g_free(session->pass);
1079 if (config->auth == SIEVEAUTH_NONE) {
1080 session->pass = NULL;
1081 } else if (reuse_auth && (session->pass = passwd_store_get_account(
1082 account->account_id, PWS_ACCOUNT_RECV))) {
1083 } else if ((session->pass = passwd_store_get_account(
1084 account->account_id, "sieve"))) {
1085 } else if (password_get(session->user, session->host, "sieve",
1086 session->port, &session->pass)) {
1088 session->pass = input_dialog_query_password_keep(session->host,
1089 session->user, &(session->pass));
1091 if (!session->pass) {
1092 session->pass = g_strdup("");
1093 session->use_auth = FALSE;
1095 session->use_auth = TRUE;
1099 SESSION(session)->ssl_type =
1100 (config->tls_type == SIEVE_TLS_NO) ? SSL_NONE : SSL_STARTTLS;
1104 /* When an account config is changed, reset associated sessions. */
1105 void sieve_account_prefs_updated(PrefsAccount *account)
1108 SieveSession *session;
1110 for (item = sessions; item; item = item->next) {
1111 session = (SieveSession *)item->data;
1112 if (session->account == account) {
1113 log_print(LOG_PROTOCOL, "Sieve: resetting session\n");
1114 sieve_session_reset(session);
1119 SieveSession *sieve_session_get_for_account(PrefsAccount *account)
1121 SieveSession *session;
1125 for (item = sessions; item; item = item->next) {
1126 session = (SieveSession *)item->data;
1127 if (session->account == account) {
1133 session = sieve_session_new(account);
1134 sessions = g_slist_prepend(sessions, session);
1139 static void sieve_queue_send(SieveSession *session, SieveState next_state,
1140 gchar *msg, sieve_session_data_cb_fn cb, gpointer data)
1142 gboolean queue = FALSE;
1143 SieveCommand *cmd = g_new0(SieveCommand, 1);
1144 cmd->session = session;
1145 cmd->next_state = next_state;
1150 if (!session_is_connected(SESSION(session))) {
1151 log_print(LOG_PROTOCOL, "Sieve: connecting to %s:%hu\n",
1152 session->host, session->port);
1153 if (sieve_session_connect(session) < 0) {
1154 sieve_connect_finished(SESSION(session), FALSE);
1157 } else if (session->state == SIEVE_RETRY_AUTH) {
1158 log_print(LOG_PROTOCOL, _("Sieve: retrying auth\n"));
1159 if (sieve_auth(session) == SE_AUTHFAIL)
1160 sieve_error(session, _("Auth method not available"));
1162 } else if (session->state != SIEVE_READY) {
1163 log_print(LOG_PROTOCOL, "Sieve: in state %d\n", session->state);
1168 session->send_queue = g_slist_prepend(session->send_queue, cmd);
1170 if (session->current_cmd)
1171 command_free(session->current_cmd);
1172 session->current_cmd = cmd;
1173 session->state = next_state;
1174 log_send(session, cmd);
1175 if (session_send_msg(SESSION(session), cmd->msg) < 0) {
1176 log_warning(LOG_PROTOCOL,
1177 _("sending error on Sieve session: %s\n"), cmd->msg);
1182 void sieve_session_list_scripts(SieveSession *session,
1183 sieve_session_data_cb_fn cb, gpointer data)
1185 gchar *msg = g_strdup("LISTSCRIPTS");
1186 sieve_queue_send(session, SIEVE_LISTSCRIPTS, msg, cb, data);
1189 void sieve_session_set_active_script(SieveSession *session,
1190 const gchar *filter_name,
1191 sieve_session_data_cb_fn cb, gpointer data)
1193 gchar *msg = g_strdup_printf("SETACTIVE \"%s\"",
1194 filter_name ? filter_name : "");
1196 cb(session, FALSE, (void*)FALSE, data);
1200 sieve_queue_send(session, SIEVE_SETACTIVE, msg, cb, data);
1203 void sieve_session_rename_script(SieveSession *session,
1204 const gchar *name_old, const char *name_new,
1205 sieve_session_data_cb_fn cb, gpointer data)
1207 gchar *msg = g_strdup_printf("RENAMESCRIPT \"%s\" \"%s\"",
1208 name_old, name_new);
1210 sieve_queue_send(session, SIEVE_RENAMESCRIPT, msg, cb, data);
1213 void sieve_session_get_script(SieveSession *session, const gchar *filter_name,
1214 sieve_session_data_cb_fn cb, gpointer data)
1216 gchar *msg = g_strdup_printf("GETSCRIPT \"%s\"",
1219 sieve_queue_send(session, SIEVE_GETSCRIPT, msg, cb, data);
1222 void sieve_session_put_script(SieveSession *session, const gchar *filter_name,
1223 gint len, const gchar *script_contents,
1224 sieve_session_data_cb_fn cb, gpointer data)
1226 /* TODO: refactor so don't have to copy the whole script here */
1227 gchar *msg = g_strdup_printf("PUTSCRIPT \"%s\" {%u+}%s%s",
1228 filter_name, len, len > 0 ? "\r\n" : "",
1231 sieve_queue_send(session, SIEVE_PUTSCRIPT, msg, cb, data);
1234 void sieve_session_check_script(SieveSession *session,
1235 gint len, const gchar *script_contents,
1236 sieve_session_data_cb_fn cb, gpointer data)
1238 gchar *msg = g_strdup_printf("CHECKSCRIPT {%u+}%s%s",
1239 len, len > 0 ? "\r\n" : "", script_contents);
1241 sieve_queue_send(session, SIEVE_PUTSCRIPT, msg, cb, data);
1244 void sieve_session_delete_script(SieveSession *session,
1245 const gchar *filter_name,
1246 sieve_session_data_cb_fn cb, gpointer data)
1248 gchar *msg = g_strdup_printf("DELETESCRIPT \"%s\"",
1251 sieve_queue_send(session, SIEVE_DELETESCRIPT, msg, cb, data);