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/>.
24 #include <glib/gi18n.h>
30 #include "passwordstore.h"
31 #include "gtk/inputdialog.h"
36 #include "prefs_common.h"
38 #include "managesieve.h"
39 #include "sieve_editor.h"
40 #include "sieve_prefs.h"
42 GSList
*sessions
= NULL
;
44 static void sieve_session_destroy(Session
*session
);
45 static gint
sieve_pop_send_queue(SieveSession
*session
);
46 static void sieve_session_reset(SieveSession
*session
);
47 static void command_free(SieveCommand
*cmd
);
48 static void command_abort(SieveCommand
*cmd
);
49 static void command_cb(SieveCommand
*cmd
, gpointer result
);
50 static gint
sieve_session_recv_chunk(SieveSession
*, guint len
);
51 static void sieve_read_chunk(SieveSession
*, gchar
*data
, guint len
);
52 static gint
sieve_read_chunk_done(SieveSession
*session
);
54 void sieve_sessions_close()
57 GSList
*list
= sessions
;
59 g_slist_free_full(list
, (GDestroyNotify
)session_destroy
);
63 /* remove all command callbacks with a given data pointer */
64 void sieve_sessions_discard_callbacks(gpointer user_data
)
69 SieveSession
*session
;
72 for (item
= sessions
; item
; item
= item
->next
) {
73 session
= (SieveSession
*)item
->data
;
74 cmd
= session
->current_cmd
;
75 /* abort current command handler */
76 if (cmd
&& cmd
->data
== user_data
) {
78 session
->current_cmd
= NULL
;
80 /* abort queued command handlers */
81 for (queue
= session
->send_queue
; queue
; queue
= queue
->next
) {
82 cmd
= (SieveCommand
*)queue
->data
;
83 if (cmd
&& cmd
->data
== user_data
) {
85 prev
->next
= queue
->next
;
87 session
->send_queue
= NULL
;
89 g_slist_free_1(queue
);
97 static void command_cb(SieveCommand
*cmd
, gpointer result
)
100 cmd
->cb(cmd
->session
, FALSE
, result
, cmd
->data
);
103 static void command_abort(SieveCommand
*cmd
)
105 cmd
->cb(cmd
->session
, TRUE
, NULL
, cmd
->data
);
110 static void command_free(SieveCommand
*cmd
)
116 void sieve_session_handle_status(SieveSession
*session
,
117 sieve_session_error_cb_fn on_error
,
118 sieve_session_connected_cb_fn on_connected
,
121 session
->on_error
= on_error
;
122 session
->on_connected
= on_connected
;
123 session
->cb_data
= data
;
126 static void sieve_error(SieveSession
*session
, const gchar
*msg
)
128 if (session
->on_error
)
129 session
->on_error(session
, msg
, session
->cb_data
);
132 static void sieve_connected(SieveSession
*session
, gboolean connected
)
134 if (session
->on_connected
)
135 session
->on_connected(session
, connected
, session
->cb_data
);
138 static gboolean
sieve_read_chunk_cb(SockInfo
*source
,
139 GIOCondition condition
, gpointer data
)
141 SieveSession
*sieve_session
= SIEVE_SESSION(data
);
142 Session
*session
= &sieve_session
->session
;
146 cm_return_val_if_fail(condition
== G_IO_IN
, FALSE
);
148 session_set_timeout(session
, session
->timeout_interval
);
150 if (session
->read_buf_len
== 0) {
154 read_len
= sock_read(session
->sock
,
156 SESSION_BUFFSIZE
- 1);
158 if (read_len
== -1 &&
159 session
->state
== SESSION_DISCONNECTED
) {
160 g_warning("sock_read: session disconnected");
161 if (session
->io_tag
> 0) {
162 g_source_remove(session
->io_tag
);
169 g_warning("sock_read: received EOF");
170 session
->state
= SESSION_EOF
;
179 g_warning("sock_read: %s",
181 session
->state
= SESSION_ERROR
;
186 session
->read_buf_len
= read_len
;
189 data_len
= MIN(session
->read_buf_len
,
190 sieve_session
->octets_remaining
);
191 sieve_session
->octets_remaining
-= data_len
;
192 session
->read_buf_len
-= data_len
;
193 session
->read_buf_p
[data_len
] = '\0';
195 /* progress callback */
196 sieve_read_chunk(sieve_session
, session
->read_buf_p
, data_len
);
198 if (session
->read_buf_len
== 0) {
199 session
->read_buf_p
= session
->read_buf
;
201 session
->read_buf_p
+= data_len
;
204 /* incomplete read */
205 if (sieve_session
->octets_remaining
> 0)
209 if (session
->io_tag
> 0) {
210 g_source_remove(session
->io_tag
);
214 /* completion callback */
215 ret
= sieve_read_chunk_done(sieve_session
);
218 session
->state
= SESSION_ERROR
;
223 static gboolean
sieve_read_chunk_idle_cb(gpointer data
)
225 Session
*session
= SESSION(data
);
228 ret
= sieve_read_chunk_cb(session
->sock
, G_IO_IN
, session
);
231 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
232 sieve_read_chunk_cb
, session
);
237 /* Get data of specified length.
238 * If needed elsewhere, this should be put in session.c */
239 static gint
sieve_session_recv_chunk(SieveSession
*sieve_session
,
242 Session
*session
= &sieve_session
->session
;
243 cm_return_val_if_fail(session
->read_msg_buf
->len
== 0, -1);
245 session
->state
= SESSION_RECV
;
246 sieve_session
->octets_remaining
= bytes
;
248 if (session
->read_buf_len
> 0)
249 g_idle_add(sieve_read_chunk_idle_cb
, session
);
251 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
252 sieve_read_chunk_cb
, session
);
257 static gint
sieve_auth_recv(SieveSession
*session
, const gchar
*msg
)
259 gchar buf
[MESSAGEBUFSIZE
], *tmp
;
261 switch (session
->auth_type
) {
262 case SIEVEAUTH_LOGIN
:
263 session
->state
= SIEVE_AUTH_LOGIN_USER
;
265 if (strstr(msg
, "VXNlcm5hbWU6")) {
266 tmp
= g_base64_encode(session
->user
, strlen(session
->user
));
267 g_snprintf(buf
, sizeof(buf
), "\"%s\"", tmp
);
269 if (session_send_msg(SESSION(session
), buf
) < 0) {
274 log_print(LOG_PROTOCOL
, "Sieve> [USERID]\n");
276 /* Server rejects AUTH */
277 if (session_send_msg(SESSION(session
), "\"*\"") < 0)
279 log_print(LOG_PROTOCOL
, "Sieve> *\n");
282 case SIEVEAUTH_CRAM_MD5
:
283 session
->state
= SIEVE_AUTH_CRAM_MD5
;
288 gchar
*challenge
, *tmp
;
290 guchar hexdigest
[33];
292 tmp
= g_base64_decode(msg
+ 1, &challengelen
);
293 challenge
= g_strndup(tmp
, challengelen
);
295 log_print(LOG_PROTOCOL
, "Sieve< [Decoded: %s]\n", challenge
);
297 g_snprintf(buf
, sizeof(buf
), "%s", session
->pass
);
298 md5_hex_hmac(hexdigest
, challenge
, challengelen
,
299 buf
, strlen(session
->pass
));
302 response
= g_strdup_printf
303 ("%s %s", session
->user
, hexdigest
);
304 log_print(LOG_PROTOCOL
, "Sieve> [Encoded: %s]\n", response
);
306 response64
= g_base64_encode(response
, strlen(response
));
309 response
= g_strdup_printf("\"%s\"", response64
);
312 if (session_send_msg(SESSION(session
), response
) < 0) {
316 log_print(LOG_PROTOCOL
, "Sieve> %s\n", response
);
319 /* Server rejects AUTH */
320 if (session_send_msg(SESSION(session
), "\"*\"") < 0)
322 log_print(LOG_PROTOCOL
, "Sieve> *\n");
326 /* stop sieve_auth when no correct authtype */
327 if (session_send_msg(SESSION(session
), "*") < 0)
329 log_print(LOG_PROTOCOL
, "Sieve> *\n");
336 static gint
sieve_auth_login_user_recv(SieveSession
*session
, const gchar
*msg
)
340 session
->state
= SIEVE_AUTH_LOGIN_PASS
;
342 if (strstr(msg
, "UGFzc3dvcmQ6")) {
343 tmp2
= g_base64_encode(session
->pass
, strlen(session
->pass
));
344 tmp
= g_strdup_printf("\"%s\"", tmp2
);
347 /* Server rejects AUTH */
348 tmp
= g_strdup("\"*\"");
351 if (session_send_msg(SESSION(session
), tmp
) < 0) {
357 log_print(LOG_PROTOCOL
, "Sieve> [PASSWORD]\n");
363 static gint
sieve_auth_cram_md5(SieveSession
*session
)
365 session
->state
= SIEVE_AUTH
;
366 session
->auth_type
= SIEVEAUTH_CRAM_MD5
;
368 if (session_send_msg(SESSION(session
), "Authenticate \"CRAM-MD5\"") < 0)
370 log_print(LOG_PROTOCOL
, "Sieve> Authenticate CRAM-MD5\n");
375 static gint
sieve_auth_plain(SieveSession
*session
)
377 gchar buf
[MESSAGEBUFSIZE
], *b64buf
, *out
;
380 session
->state
= SIEVE_AUTH_PLAIN
;
381 session
->auth_type
= SIEVEAUTH_PLAIN
;
383 memset(buf
, 0, sizeof buf
);
385 /* "\0user\0password" */
386 len
= sprintf(buf
, "%c%s%c%s", '\0', session
->user
, '\0', session
->pass
);
387 b64buf
= g_base64_encode(buf
, len
);
388 out
= g_strconcat("Authenticate \"PLAIN\" \"", b64buf
, "\"", NULL
);
391 if (session_send_msg(SESSION(session
), out
) < 0) {
398 log_print(LOG_PROTOCOL
, "Sieve> [Authenticate PLAIN]\n");
403 static gint
sieve_auth_login(SieveSession
*session
)
405 session
->state
= SIEVE_AUTH
;
406 session
->auth_type
= SIEVEAUTH_LOGIN
;
408 if (session_send_msg(SESSION(session
), "Authenticate \"LOGIN\"") < 0)
410 log_print(LOG_PROTOCOL
, "Sieve> Authenticate LOGIN\n");
415 static gint
sieve_auth(SieveSession
*session
)
417 SieveAuthType forced_auth_type
= session
->forced_auth_type
;
419 if (!session
->use_auth
) {
420 session
->state
= SIEVE_READY
;
421 sieve_connected(session
, TRUE
);
425 session
->state
= SIEVE_AUTH
;
426 sieve_error(session
, _("Authenticating..."));
428 if ((forced_auth_type
== SIEVEAUTH_CRAM_MD5
|| forced_auth_type
== 0) &&
429 (session
->avail_auth_type
& SIEVEAUTH_CRAM_MD5
) != 0)
430 return sieve_auth_cram_md5(session
);
431 else if ((forced_auth_type
== SIEVEAUTH_LOGIN
|| forced_auth_type
== 0) &&
432 (session
->avail_auth_type
& SIEVEAUTH_LOGIN
) != 0)
433 return sieve_auth_login(session
);
434 else if ((forced_auth_type
== SIEVEAUTH_PLAIN
|| forced_auth_type
== 0) &&
435 (session
->avail_auth_type
& SIEVEAUTH_PLAIN
) != 0)
436 return sieve_auth_plain(session
);
437 else if (forced_auth_type
== 0) {
438 log_warning(LOG_PROTOCOL
, _("No Sieve auth method available\n"));
439 session
->state
= SIEVE_RETRY_AUTH
;
442 log_warning(LOG_PROTOCOL
, _("Selected Sieve auth method not available\n"));
443 session
->state
= SIEVE_RETRY_AUTH
;
450 static void sieve_session_putscript_cb(SieveSession
*session
, SieveResult
*result
)
452 /* Remove script name from the beginning the response,
453 * which is added by Dovecot/Pigeonhole */
454 gchar
*start
, *desc
= result
->description
;
457 /* callback just for the status */
458 command_cb(session
->current_cmd
, result
);
460 while (desc
&& desc
[0]) {
461 if ((end
= strchr(desc
, '\r')) ||
462 (end
= strchr(desc
, '\n')))
463 while (*end
== '\n' || *end
== '\r')
465 if (g_str_has_prefix(desc
, "NULL_") && (start
= strchr(desc
+5, ':'))) {
469 /* TODO: match against known script name, in case it contains
470 * weird text like ": line " */
471 } else if ((start
= strstr(desc
, ": line ")) ||
472 (start
= strstr(desc
, ": error"))) {
475 result
->description
= desc
;
476 command_cb(session
->current_cmd
, result
);
481 static inline gboolean
response_is_ok(const char *msg
)
483 return !strncmp(msg
, "OK", 2) && (!msg
[2] || msg
[2] == ' ');
486 static inline gboolean
response_is_no(const char *msg
)
488 return !strncmp(msg
, "NO", 2) && (!msg
[2] || msg
[2] == ' ');
491 static inline gboolean
response_is_bye(const char *msg
)
493 return !strncmp(msg
, "BYE", 3) && (!msg
[3] || msg
[3] == ' ');
496 static void sieve_got_capability(SieveSession
*session
, gchar
*cap_name
,
499 if (strcmp(cap_name
, "SASL") == 0) {
500 SieveAuthType auth_type
= 0;
502 for (auth
= cap_value
; auth
&& auth
[0]; auth
= end
) {
503 if ((end
= strchr(auth
, ' ')))
505 if (strcmp(auth
, "PLAIN") == 0) {
506 auth_type
|= SIEVEAUTH_PLAIN
;
507 } else if (strcmp(auth
, "CRAM-MD5") == 0) {
508 auth_type
|= SIEVEAUTH_CRAM_MD5
;
509 } else if (strcmp(auth
, "LOGIN") == 0) {
510 auth_type
|= SIEVEAUTH_LOGIN
;
513 session
->avail_auth_type
= auth_type
;
515 } else if (strcmp(cap_name
, "STARTTLS") == 0) {
516 session
->capability
.starttls
= TRUE
;
520 static void log_send(SieveSession
*session
, SieveCommand
*cmd
)
522 gchar
*end
, *msg
= cmd
->msg
;
523 if (cmd
->next_state
== SIEVE_PUTSCRIPT
&& (end
= strchr(msg
, '\n'))) {
524 /* Don't log the script data */
525 msg
= g_strndup(msg
, end
- msg
);
526 log_print(LOG_PROTOCOL
, "Sieve> %s\n", msg
);
530 log_print(LOG_PROTOCOL
, "Sieve> %s\n", msg
);
533 static gint
sieve_pop_send_queue(SieveSession
*session
)
536 GSList
*send_queue
= session
->send_queue
;
538 if (session
->current_cmd
) {
539 command_free(session
->current_cmd
);
540 session
->current_cmd
= NULL
;
546 cmd
= (SieveCommand
*)send_queue
->data
;
547 session
->send_queue
= g_slist_next(send_queue
);
548 g_slist_free_1(send_queue
);
550 log_send(session
, cmd
);
551 session
->state
= cmd
->next_state
;
552 session
->current_cmd
= cmd
;
553 if (session_send_msg(SESSION(session
), cmd
->msg
) < 0)
559 static void parse_split(gchar
*line
, gchar
**first_word
, gchar
**second_word
)
566 if (line
[0] == '"' && ((second
= strchr(line
+ 1, '"')))) {
569 if (second
[0] == ' ')
571 } else if ((second
= strchr(line
, ' '))) {
576 if (second
&& second
[0] == '"' &&
577 ((end
= strchr(second
+ 1, '"')))) {
583 *second_word
= second
;
586 static void unquote_inplace(gchar
*str
)
591 for (src
= str
+1, dest
= str
; src
&& *src
&& *src
!= '"'; src
++) {
600 static void parse_response(gchar
*msg
, SieveResult
*result
)
604 cm_return_if_fail(msg
!= NULL
);
606 /* response status */
607 if (isalpha(msg
[0])) {
608 end
= strchr(msg
, ' ');
614 result
->success
= strcmp(msg
, "OK") == 0;
615 result
->has_status
= TRUE
;
618 result
->has_status
= FALSE
;
622 if (msg
&& msg
[0] == '(' && (end
= strchr(msg
, ')'))) {
626 strcmp(msg
, "WARNINGS") == 0 ? SIEVE_CODE_WARNINGS
:
627 strcmp(msg
, "TRYLATER") == 0 ? SIEVE_CODE_TRYLATER
:
633 result
->code
= SIEVE_CODE_NONE
;
637 if (msg
&& msg
[0] == '{' && (end
= strchr(msg
, '}'))) {
640 if (msg
[0] == '0' && msg
+1 == end
) {
641 result
->has_octets
= TRUE
;
645 (result
->octets
= g_ascii_strtoll(msg
, NULL
, 10)) != 0;
651 result
->has_octets
= FALSE
;
657 unquote_inplace(msg
);
658 result
->description
= msg
;
660 result
->description
= NULL
;
664 static gint
sieve_session_recv_msg(Session
*session
, const gchar
*msg
)
666 SieveSession
*sieve_session
= SIEVE_SESSION(session
);
670 log_print(LOG_PROTOCOL
, "Sieve< %s\n", msg
);
671 if (response_is_bye(msg
)) {
673 parse_response((gchar
*)msg
, &result
);
674 if (!result
.description
)
675 status
= g_strdup(_("Disconnected"));
676 else if (g_str_has_prefix(result
.description
, "Disconnected"))
677 status
= g_strdup(result
.description
);
679 status
= g_strdup_printf(_("Disconnected: %s"), result
.description
);
680 sieve_session
->error
= SE_ERROR
;
681 sieve_error(sieve_session
, status
);
682 sieve_session
->state
= SIEVE_DISCONNECTED
;
687 switch (sieve_session
->state
) {
688 case SIEVE_CAPABILITIES
:
689 if (response_is_ok(msg
)) {
690 /* capabilities list done */
693 if (sieve_session
->tls_init_done
== FALSE
&&
694 sieve_session
->config
->tls_type
!= SIEVE_TLS_NO
) {
695 if (sieve_session
->capability
.starttls
) {
696 if (session_send_msg(session
, "STARTTLS") < 0)
697 sieve_session
->state
= SIEVE_ERROR
;
699 sieve_session
->state
= SIEVE_STARTTLS
;
700 } else if (sieve_session
->config
->tls_type
== SIEVE_TLS_YES
) {
701 log_warning(LOG_PROTOCOL
, "Sieve: does not support STARTTLS\n");
702 sieve_session
->state
= SIEVE_ERROR
;
704 log_warning(LOG_PROTOCOL
, "Sieve: continuing unencrypted\n");
705 sieve_session
->state
= SIEVE_READY
;
710 /* authenticate after getting capabilities */
711 if (!sieve_session
->authenticated
) {
712 ret
= sieve_auth(sieve_session
);
714 sieve_session
->state
= SIEVE_READY
;
715 sieve_connected(sieve_session
, TRUE
);
718 /* got a capability */
719 gchar
*cap_name
, *cap_value
;
720 parse_split((gchar
*)msg
, &cap_name
, &cap_value
);
721 sieve_got_capability(sieve_session
, cap_name
, cap_value
);
727 log_warning(LOG_PROTOCOL
,
728 _("unhandled message on Sieve session: %s\n"), msg
);
732 if (session_start_tls(session
) < 0) {
733 sieve_session
->state
= SIEVE_ERROR
;
734 sieve_session
->error
= SE_ERROR
;
735 sieve_error(sieve_session
, _("STARTTLS failed"));
738 sieve_session
->tls_init_done
= TRUE
;
739 sieve_session
->state
= SIEVE_CAPABILITIES
;
743 ret
= sieve_auth_recv(sieve_session
, msg
);
745 case SIEVE_AUTH_LOGIN_USER
:
746 ret
= sieve_auth_login_user_recv(sieve_session
, msg
);
748 case SIEVE_AUTH_PLAIN
:
749 case SIEVE_AUTH_LOGIN_PASS
:
750 case SIEVE_AUTH_CRAM_MD5
:
751 if (response_is_no(msg
)) {
752 log_print(LOG_PROTOCOL
, "Sieve auth failed\n");
753 sieve_session
->state
= SIEVE_RETRY_AUTH
;
755 } else if (response_is_ok(msg
)) {
756 log_print(LOG_PROTOCOL
, "Sieve auth completed\n");
757 sieve_error(sieve_session
, "");
758 sieve_session
->authenticated
= TRUE
;
759 sieve_session
->state
= SIEVE_READY
;
760 sieve_connected(sieve_session
, TRUE
);
764 if (!response_is_ok(msg
)) {
765 sieve_session
->state
= SIEVE_ERROR
;
767 sieve_session
->state
= SIEVE_READY
;
769 case SIEVE_LISTSCRIPTS
:
770 if (response_is_no(msg
)) {
771 /* got an error. probably not authenticated. */
772 command_cb(sieve_session
->current_cmd
, NULL
);
773 sieve_session
->state
= SIEVE_READY
;
774 } else if (response_is_ok(msg
)) {
776 sieve_session
->state
= SIEVE_READY
;
777 sieve_session
->error
= SE_OK
;
778 command_cb(sieve_session
->current_cmd
,
779 (gpointer
)&(SieveScript
){0});
781 /* got a script name */
783 gchar
*script_status
;
785 parse_split((gchar
*)msg
, &script
.name
, &script_status
);
786 script
.active
= (script_status
&&
787 strcasecmp(script_status
, "active") == 0);
789 command_cb(sieve_session
->current_cmd
,
793 case SIEVE_RENAMESCRIPT
:
794 if (response_is_no(msg
)) {
796 command_cb(sieve_session
->current_cmd
, NULL
);
797 } else if (response_is_ok(msg
)) {
798 command_cb(sieve_session
->current_cmd
, (void*)TRUE
);
800 log_warning(LOG_PROTOCOL
, _("error occurred on SIEVE session\n"));
802 sieve_session
->state
= SIEVE_READY
;
804 case SIEVE_SETACTIVE
:
805 parse_response((gchar
*)msg
, &result
);
806 if (result
.success
) {
807 /* clear status possibly set when setting another
808 * script active. TODO: give textual feedback */
809 sieve_error(sieve_session
, "");
811 command_cb(sieve_session
->current_cmd
, NULL
);
812 } else if (result
.description
) {
813 command_cb(sieve_session
->current_cmd
,
816 log_warning(LOG_PROTOCOL
, _("error occurred on SIEVE session\n"));
818 if (result
.has_octets
) {
819 return sieve_session_recv_chunk(sieve_session
,
822 sieve_session
->state
= SIEVE_READY
;
825 case SIEVE_GETSCRIPT
:
826 if (response_is_no(msg
)) {
827 command_cb(sieve_session
->current_cmd
, (void *)-1);
828 sieve_session
->state
= SIEVE_READY
;
830 parse_response((gchar
*)msg
, &result
);
831 sieve_session
->state
= SIEVE_GETSCRIPT_DATA
;
832 return sieve_session_recv_chunk(sieve_session
,
836 case SIEVE_GETSCRIPT_DATA
:
839 sieve_session
->state
= SIEVE_READY
;
840 if (response_is_ok(msg
)) {
841 command_cb(sieve_session
->current_cmd
, NULL
);
843 log_warning(LOG_PROTOCOL
, _("error occurred on SIEVE session\n"));
846 case SIEVE_PUTSCRIPT
:
849 parse_response((gchar
*)msg
, &result
);
850 sieve_session_putscript_cb(sieve_session
, &result
);
851 if (result
.has_octets
) {
852 return sieve_session_recv_chunk(sieve_session
,
855 sieve_session
->state
= SIEVE_READY
;
858 case SIEVE_DELETESCRIPT
:
859 parse_response((gchar
*)msg
, &result
);
860 if (!result
.success
) {
861 command_cb(sieve_session
->current_cmd
,
864 command_cb(sieve_session
->current_cmd
, NULL
);
866 sieve_session
->state
= SIEVE_READY
;
869 log_warning(LOG_PROTOCOL
, _("error occurred on Sieve session. data: %s\n"), msg
);
870 sieve_session
->error
= SE_ERROR
;
872 case SIEVE_RETRY_AUTH
:
873 log_warning(LOG_PROTOCOL
, _("unhandled message on Sieve session: %s\n"),
875 ret
= sieve_auth(sieve_session
);
878 log_warning(LOG_PROTOCOL
, _("unhandled message on Sieve session: %d\n"),
879 sieve_session
->state
);
880 sieve_session
->error
= SE_ERROR
;
884 if (ret
== SE_OK
&& sieve_session
->state
== SIEVE_READY
)
885 ret
= sieve_pop_send_queue(sieve_session
);
888 return session_recv_msg(session
);
889 } else if (ret
== SE_AUTHFAIL
) {
890 sieve_error(sieve_session
, _("Auth failed"));
891 sieve_session
->state
= SIEVE_ERROR
;
892 sieve_session
->error
= SE_ERROR
;
898 static gint
sieve_recv_message(Session
*session
, const gchar
*msg
,
904 static void sieve_read_chunk(SieveSession
*session
, gchar
*data
, guint len
)
906 log_print(LOG_PROTOCOL
, "Sieve< [%u bytes]\n", len
);
908 switch (session
->state
) {
909 case SIEVE_GETSCRIPT_DATA
:
910 command_cb(session
->current_cmd
, (gchar
*)data
);
912 case SIEVE_SETACTIVE
:
913 /* Dovecot shows a script's warnings when making it active */
914 /* TODO: append message in case it is very long*/
916 sieve_error(session
, data
);
918 case SIEVE_PUTSCRIPT
: {
919 SieveResult result
= {.description
= (gchar
*)data
};
920 sieve_session_putscript_cb(session
, &result
);
924 log_warning(LOG_PROTOCOL
,
925 _("error occurred on SIEVE session\n"));
929 static gint
sieve_read_chunk_done(SieveSession
*session
)
933 switch (session
->state
) {
934 case SIEVE_GETSCRIPT_DATA
:
935 /* wait for ending "OK" response */
937 case SIEVE_SETACTIVE
:
938 case SIEVE_PUTSCRIPT
:
939 session
->state
= SIEVE_READY
;
942 log_warning(LOG_PROTOCOL
,
943 _("error occurred on SIEVE session\n"));
946 if (ret
== SE_OK
&& session
->state
== SIEVE_READY
)
947 ret
= sieve_pop_send_queue(session
);
950 return session_recv_msg(SESSION(session
));
955 static gint
sieve_cmd_noop(SieveSession
*session
)
957 log_print(LOG_PROTOCOL
, "Sieve> NOOP\n");
958 session
->state
= SIEVE_NOOP
;
959 if (session_send_msg(SESSION(session
), "NOOP") < 0) {
960 session
->state
= SIEVE_ERROR
;
961 session
->error
= SE_ERROR
;
967 static gboolean
sieve_ping(gpointer data
)
969 Session
*session
= SESSION(data
);
970 SieveSession
*sieve_session
= SIEVE_SESSION(session
);
972 if (sieve_session
->state
== SIEVE_ERROR
|| session
->state
== SESSION_ERROR
)
974 if (sieve_session
->state
!= SIEVE_READY
)
977 return sieve_cmd_noop(sieve_session
) == 0;
980 static void sieve_session_destroy(Session
*session
)
982 SieveSession
*sieve_session
= SIEVE_SESSION(session
);
983 g_free(sieve_session
->pass
);
984 if (sieve_session
->current_cmd
)
985 command_abort(sieve_session
->current_cmd
);
986 sessions
= g_slist_remove(sessions
, (gconstpointer
)session
);
987 g_slist_free_full(sieve_session
->send_queue
,
988 (GDestroyNotify
)command_abort
);
989 if (sieve_session
->config
)
990 sieve_prefs_account_free_config(sieve_session
->config
);
993 static void sieve_connect_finished(Session
*session
, gboolean success
)
996 sieve_connected(SIEVE_SESSION(session
), FALSE
);
1000 static gint
sieve_session_connect(SieveSession
*session
)
1002 PrefsAccount
*ac
= session
->account
;
1003 ProxyInfo
*proxy_info
= NULL
;
1005 session
->state
= SIEVE_CAPABILITIES
;
1006 session
->authenticated
= FALSE
;
1008 session
->tls_init_done
= FALSE
;
1011 if (ac
->use_proxy
) {
1012 if (ac
->use_default_proxy
) {
1013 proxy_info
= (ProxyInfo
*)&(prefs_common_get_prefs()->proxy_info
);
1014 if (proxy_info
->use_proxy_auth
)
1015 proxy_info
->proxy_pass
= passwd_store_get(PWS_CORE
, PWS_CORE_PROXY
,
1016 PWS_CORE_PROXY_PASS
);
1018 proxy_info
= (ProxyInfo
*)&(ac
->proxy_info
);
1019 if (proxy_info
->use_proxy_auth
)
1020 proxy_info
->proxy_pass
= passwd_store_get_account(ac
->account_id
,
1021 PWS_ACCOUNT_PROXY_PASS
);
1024 SESSION(session
)->proxy_info
= proxy_info
;
1026 return session_connect(SESSION(session
), session
->host
,
1030 static SieveSession
*sieve_session_new(PrefsAccount
*account
)
1032 SieveSession
*session
;
1033 session
= g_new0(SieveSession
, 1);
1034 session_init(SESSION(session
), account
, FALSE
);
1036 session
->account
= account
;
1038 SESSION(session
)->recv_msg
= sieve_session_recv_msg
;
1039 SESSION(session
)->destroy
= sieve_session_destroy
;
1040 SESSION(session
)->connect_finished
= sieve_connect_finished
;
1041 session_set_recv_message_notify(SESSION(session
), sieve_recv_message
, NULL
);
1043 session
->config
= NULL
;
1044 sieve_session_reset(session
);
1048 static void sieve_session_reset(SieveSession
*session
)
1050 PrefsAccount
*account
= session
->account
;
1051 SieveAccountConfig
*config
= sieve_prefs_account_get_config(account
);
1052 gboolean reuse_auth
= (config
->auth
== SIEVEAUTH_REUSE
);
1054 g_slist_free_full(session
->send_queue
, (GDestroyNotify
)command_abort
);
1056 session_disconnect(SESSION(session
));
1058 SESSION(session
)->ssl_cert_auto_accept
= account
->ssl_certs_auto_accept
;
1059 SESSION(session
)->nonblocking
= account
->use_nonblocking_ssl
;
1060 session
->authenticated
= FALSE
;
1061 session
->current_cmd
= NULL
;
1062 session
->send_queue
= NULL
;
1063 session
->state
= SIEVE_CAPABILITIES
;
1065 session
->tls_init_done
= FALSE
;
1066 SESSION(session
)->use_tls_sni
= account
->use_tls_sni
;
1068 session
->avail_auth_type
= 0;
1069 session
->auth_type
= 0;
1070 if (session
->config
)
1071 sieve_prefs_account_free_config(session
->config
);
1072 session
->config
= config
;
1073 session
->host
= config
->use_host
? config
->host
: account
->recv_server
;
1074 session
->port
= config
->use_port
? config
->port
: SIEVE_PORT
;
1075 session
->user
= reuse_auth
? account
->userid
: session
->config
->userid
;
1076 session
->forced_auth_type
= config
->auth_type
;
1077 session_register_ping(SESSION(session
), sieve_ping
);
1080 g_free(session
->pass
);
1081 if (config
->auth
== SIEVEAUTH_NONE
) {
1082 session
->pass
= NULL
;
1083 } else if (reuse_auth
&& (session
->pass
= passwd_store_get_account(
1084 account
->account_id
, PWS_ACCOUNT_RECV
))) {
1085 } else if ((session
->pass
= passwd_store_get_account(
1086 account
->account_id
, "sieve"))) {
1087 } else if (password_get(session
->user
, session
->host
, "sieve",
1088 session
->port
, &session
->pass
)) {
1090 session
->pass
= input_dialog_query_password_keep(session
->host
,
1091 session
->user
, &(session
->pass
));
1093 if (!session
->pass
) {
1094 session
->pass
= g_strdup("");
1095 session
->use_auth
= FALSE
;
1097 session
->use_auth
= TRUE
;
1101 SESSION(session
)->ssl_type
=
1102 (config
->tls_type
== SIEVE_TLS_NO
) ? SSL_NONE
: SSL_STARTTLS
;
1106 /* When an account config is changed, reset associated sessions. */
1107 void sieve_account_prefs_updated(PrefsAccount
*account
)
1110 SieveSession
*session
;
1112 for (item
= sessions
; item
; item
= item
->next
) {
1113 session
= (SieveSession
*)item
->data
;
1114 if (session
->account
== account
) {
1115 log_print(LOG_PROTOCOL
, "Sieve: resetting session\n");
1116 sieve_session_reset(session
);
1121 SieveSession
*sieve_session_get_for_account(PrefsAccount
*account
)
1123 SieveSession
*session
;
1127 for (item
= sessions
; item
; item
= item
->next
) {
1128 session
= (SieveSession
*)item
->data
;
1129 if (session
->account
== account
) {
1135 session
= sieve_session_new(account
);
1136 sessions
= g_slist_prepend(sessions
, session
);
1141 static void sieve_queue_send(SieveSession
*session
, SieveState next_state
,
1142 gchar
*msg
, sieve_session_data_cb_fn cb
, gpointer data
)
1144 gboolean queue
= FALSE
;
1145 SieveCommand
*cmd
= g_new0(SieveCommand
, 1);
1146 cmd
->session
= session
;
1147 cmd
->next_state
= next_state
;
1152 if (!session_is_connected(SESSION(session
))) {
1153 log_print(LOG_PROTOCOL
, "Sieve: connecting to %s:%hu\n",
1154 session
->host
, session
->port
);
1155 if (sieve_session_connect(session
) < 0) {
1156 sieve_connect_finished(SESSION(session
), FALSE
);
1159 } else if (session
->state
== SIEVE_RETRY_AUTH
) {
1160 log_print(LOG_PROTOCOL
, _("Sieve: retrying auth\n"));
1161 if (sieve_auth(session
) == SE_AUTHFAIL
)
1162 sieve_error(session
, _("Auth method not available"));
1164 } else if (session
->state
!= SIEVE_READY
) {
1165 log_print(LOG_PROTOCOL
, "Sieve: in state %d\n", session
->state
);
1170 session
->send_queue
= g_slist_prepend(session
->send_queue
, cmd
);
1172 if (session
->current_cmd
)
1173 command_free(session
->current_cmd
);
1174 session
->current_cmd
= cmd
;
1175 session
->state
= next_state
;
1176 log_send(session
, cmd
);
1177 if (session_send_msg(SESSION(session
), cmd
->msg
) < 0) {
1178 log_warning(LOG_PROTOCOL
,
1179 _("sending error on Sieve session: %s\n"), cmd
->msg
);
1184 void sieve_session_list_scripts(SieveSession
*session
,
1185 sieve_session_data_cb_fn cb
, gpointer data
)
1187 gchar
*msg
= g_strdup("LISTSCRIPTS");
1188 sieve_queue_send(session
, SIEVE_LISTSCRIPTS
, msg
, cb
, data
);
1191 void sieve_session_set_active_script(SieveSession
*session
,
1192 const gchar
*filter_name
,
1193 sieve_session_data_cb_fn cb
, gpointer data
)
1195 gchar
*msg
= g_strdup_printf("SETACTIVE \"%s\"",
1196 filter_name
? filter_name
: "");
1198 cb(session
, FALSE
, (void*)FALSE
, data
);
1202 sieve_queue_send(session
, SIEVE_SETACTIVE
, msg
, cb
, data
);
1205 void sieve_session_rename_script(SieveSession
*session
,
1206 const gchar
*name_old
, const char *name_new
,
1207 sieve_session_data_cb_fn cb
, gpointer data
)
1209 gchar
*msg
= g_strdup_printf("RENAMESCRIPT \"%s\" \"%s\"",
1210 name_old
, name_new
);
1212 sieve_queue_send(session
, SIEVE_RENAMESCRIPT
, msg
, cb
, data
);
1215 void sieve_session_get_script(SieveSession
*session
, const gchar
*filter_name
,
1216 sieve_session_data_cb_fn cb
, gpointer data
)
1218 gchar
*msg
= g_strdup_printf("GETSCRIPT \"%s\"",
1221 sieve_queue_send(session
, SIEVE_GETSCRIPT
, msg
, cb
, data
);
1224 void sieve_session_put_script(SieveSession
*session
, const gchar
*filter_name
,
1225 gint len
, const gchar
*script_contents
,
1226 sieve_session_data_cb_fn cb
, gpointer data
)
1228 /* TODO: refactor so don't have to copy the whole script here */
1229 gchar
*msg
= g_strdup_printf("PUTSCRIPT \"%s\" {%u+}%s%s",
1230 filter_name
, len
, len
> 0 ? "\r\n" : "",
1233 sieve_queue_send(session
, SIEVE_PUTSCRIPT
, msg
, cb
, data
);
1236 void sieve_session_check_script(SieveSession
*session
,
1237 gint len
, const gchar
*script_contents
,
1238 sieve_session_data_cb_fn cb
, gpointer data
)
1240 gchar
*msg
= g_strdup_printf("CHECKSCRIPT {%u+}%s%s",
1241 len
, len
> 0 ? "\r\n" : "", script_contents
);
1243 sieve_queue_send(session
, SIEVE_PUTSCRIPT
, msg
, cb
, data
);
1246 void sieve_session_delete_script(SieveSession
*session
,
1247 const gchar
*filter_name
,
1248 sieve_session_data_cb_fn cb
, gpointer data
)
1250 gchar
*msg
= g_strdup_printf("DELETESCRIPT \"%s\"",
1253 sieve_queue_send(session
, SIEVE_DELETESCRIPT
, msg
, cb
, data
);