2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2021 the Claws Mail team and Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
28 #include <glib/gi18n.h>
41 static gint
session_connect_cb (SockInfo
*sock
,
43 static gint
session_close (Session
*session
);
45 static gboolean
session_timeout_cb (gpointer data
);
47 static gboolean
session_recv_msg_idle_cb (gpointer data
);
48 static gboolean
session_recv_data_idle_cb (gpointer data
);
50 static gboolean
session_read_msg_cb (SockInfo
*source
,
51 GIOCondition condition
,
53 static gboolean
session_read_data_cb (SockInfo
*source
,
54 GIOCondition condition
,
56 static gboolean
session_write_msg_cb (SockInfo
*source
,
57 GIOCondition condition
,
59 static gboolean
session_write_data_cb (SockInfo
*source
,
60 GIOCondition condition
,
64 void session_init(Session
*session
, const void *prefs_account
, gboolean is_smtp
)
66 session
->type
= SESSION_UNKNOWN
;
68 session
->server
= NULL
;
71 session
->ssl_type
= SSL_NONE
;
72 session
->use_tls_sni
= TRUE
;
74 session
->nonblocking
= TRUE
;
75 session
->state
= SESSION_READY
;
76 session
->last_access_time
= time(NULL
);
78 session
->tv_prev
= g_date_time_new_now_local();
84 session
->read_buf_p
= session
->read_buf
;
85 session
->read_buf_len
= 0;
87 session
->read_msg_buf
= g_string_sized_new(1024);
88 session
->read_data_buf
= g_byte_array_new();
90 session
->write_buf
= NULL
;
91 session
->write_buf_p
= NULL
;
92 session
->write_buf_len
= 0;
94 session
->write_data
= NULL
;
95 session
->write_data_p
= NULL
;
96 session
->write_data_len
= 0;
98 session
->timeout_tag
= 0;
99 session
->timeout_interval
= 0;
101 session
->data
= NULL
;
102 session
->account
= prefs_account
;
103 session
->is_smtp
= is_smtp
;
105 session
->ping_tag
= -1;
107 session
->proxy_info
= NULL
;
111 *\brief Set up parent and child process
112 * Childloop: Read commands from parent,
113 * send to server, get answer, pass to parent
115 *\param session Contains session information
116 * server to connect to
120 * -1 : pipe / fork errors (parent)
121 * 1 : connection error (child)
123 gint
session_connect(Session
*session
, const gchar
*server
, gushort port
)
125 session
->server
= g_strdup(server
);
126 session
->port
= port
;
128 if (session
->proxy_info
) {
129 server
= session
->proxy_info
->proxy_host
;
130 port
= session
->proxy_info
->proxy_port
;
134 session
->conn_id
= sock_connect_async(server
, port
, session_connect_cb
,
136 if (session
->conn_id
< 0) {
137 g_warning("can't connect to server");
138 session_close(session
);
139 if (session
->connect_finished
)
140 session
->connect_finished(session
, FALSE
);
148 sock
= sock_connect(server
, port
);
150 g_warning("can't connect to server");
151 session_close(session
);
152 if (session
->connect_finished
)
153 session
->connect_finished(session
, FALSE
);
156 sock
->is_smtp
= session
->is_smtp
;
158 return session_connect_cb(sock
, session
);
162 static gint
session_connect_cb(SockInfo
*sock
, gpointer data
)
164 Session
*session
= SESSION(data
);
166 session
->conn_id
= 0;
169 g_warning("can't connect to server");
170 session
->state
= SESSION_ERROR
;
171 if (session
->connect_finished
)
172 session
->connect_finished(session
, FALSE
);
176 session
->sock
= sock
;
177 sock
->account
= session
->account
;
178 sock
->is_smtp
= session
->is_smtp
;
179 sock
->ssl_cert_auto_accept
= session
->ssl_cert_auto_accept
;
181 if (session
->proxy_info
) {
182 debug_print("connecting through socks\n");
183 sock_set_nonblocking_mode(sock
, FALSE
);
184 if (proxy_connect(sock
, session
->server
, session
->port
,
185 session
->proxy_info
) < 0) {
186 g_warning("can't establish SOCKS connection");
187 session
->state
= SESSION_ERROR
;
194 sock
->gnutls_priority
= session
->gnutls_priority
;
195 sock
->use_tls_sni
= session
->use_tls_sni
;
197 if (session
->ssl_type
== SSL_TUNNEL
) {
198 sock_set_nonblocking_mode(sock
, FALSE
);
199 if (!ssl_init_socket(sock
)) {
200 g_warning("can't initialize TLS");
201 log_error(LOG_PROTOCOL
, _("TLS handshake failed\n"));
202 session
->state
= SESSION_ERROR
;
203 if (session
->connect_finished
)
204 session
->connect_finished(session
, FALSE
);
210 /* we could have gotten a timeout while waiting for user input in
211 * an SSL certificate dialog */
212 if (session
->state
== SESSION_TIMEOUT
) {
213 if (session
->connect_finished
)
214 session
->connect_finished(session
, FALSE
);
218 sock_set_nonblocking_mode(sock
, session
->nonblocking
);
220 debug_print("session (%p): connected\n", session
);
222 session
->state
= SESSION_RECV
;
223 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
227 if (session
->connect_finished
)
228 session
->connect_finished(session
, TRUE
);
233 *\brief child and parent: send DISCONNECT message to other process
235 *\param session Contains session information
239 gint
session_disconnect(Session
*session
)
241 session_close(session
);
248 *\param session Contains session information
250 void session_destroy(Session
*session
)
252 cm_return_if_fail(session
!= NULL
);
253 cm_return_if_fail(session
->destroy
!= NULL
);
255 session_register_ping(session
, NULL
);
257 session_close(session
);
258 session
->destroy(session
);
259 g_free(session
->server
);
260 g_string_free(session
->read_msg_buf
, TRUE
);
261 g_byte_array_free(session
->read_data_buf
, TRUE
);
262 g_free(session
->read_data_terminator
);
263 g_free(session
->write_buf
);
265 g_free(session
->gnutls_priority
);
267 g_date_time_unref(session
->tv_prev
);
268 debug_print("session (%p): destroyed\n", session
);
273 gboolean
session_is_running(Session
*session
)
275 return (session
->state
== SESSION_READY
||
276 session
->state
== SESSION_SEND
||
277 session
->state
== SESSION_RECV
);
280 gboolean
session_is_connected(Session
*session
)
282 return (session
->state
== SESSION_SEND
||
283 session
->state
== SESSION_RECV
);
286 void session_set_access_time(Session
*session
)
288 session
->last_access_time
= time(NULL
);
291 void session_set_timeout(Session
*session
, guint interval
)
293 if (session
->timeout_tag
> 0)
294 g_source_remove(session
->timeout_tag
);
296 session
->timeout_interval
= interval
;
298 if (interval
% 1000 == 0)
299 session
->timeout_tag
=
300 g_timeout_add_seconds(interval
/1000, session_timeout_cb
, session
);
302 session
->timeout_tag
=
303 g_timeout_add(interval
, session_timeout_cb
, session
);
305 session
->timeout_tag
= 0;
308 static gboolean
session_timeout_cb(gpointer data
)
310 Session
*session
= SESSION(data
);
312 g_warning("session timeout");
314 if (session
->io_tag
> 0) {
315 g_source_remove(session
->io_tag
);
319 session
->timeout_tag
= 0;
320 session
->state
= SESSION_TIMEOUT
;
325 void session_set_recv_message_notify(Session
*session
,
326 RecvMsgNotify notify_func
, gpointer data
)
328 session
->recv_msg_notify
= notify_func
;
329 session
->recv_msg_notify_data
= data
;
332 void session_set_recv_data_progressive_notify
334 RecvDataProgressiveNotify notify_func
,
337 session
->recv_data_progressive_notify
= notify_func
,
338 session
->recv_data_progressive_notify_data
= data
;
341 void session_set_recv_data_notify(Session
*session
, RecvDataNotify notify_func
,
344 session
->recv_data_notify
= notify_func
;
345 session
->recv_data_notify_data
= data
;
348 void session_set_send_data_progressive_notify
350 SendDataProgressiveNotify notify_func
,
353 session
->send_data_progressive_notify
= notify_func
;
354 session
->send_data_progressive_notify_data
= data
;
357 void session_set_send_data_notify(Session
*session
, SendDataNotify notify_func
,
360 session
->send_data_notify
= notify_func
;
361 session
->send_data_notify_data
= data
;
365 *\brief child and parent cleanup (child closes first)
367 *\param session Contains session information
371 static gint
session_close(Session
*session
)
373 cm_return_val_if_fail(session
!= NULL
, -1);
376 if (session
->conn_id
> 0) {
377 sock_connect_async_cancel(session
->conn_id
);
378 session
->conn_id
= 0;
379 debug_print("session (%p): connection cancelled\n", session
);
383 session_set_timeout(session
, 0);
385 if (session
->io_tag
> 0) {
386 g_source_remove(session
->io_tag
);
391 sock_close(session
->sock
, TRUE
);
392 session
->sock
= NULL
;
393 session
->state
= SESSION_DISCONNECTED
;
394 debug_print("session (%p): closed\n", session
);
401 gint
session_start_tls(Session
*session
)
405 nb_mode
= sock_is_nonblocking_mode(session
->sock
);
407 session
->sock
->ssl_cert_auto_accept
= session
->ssl_cert_auto_accept
;
408 session
->sock
->gnutls_priority
= session
->gnutls_priority
;
409 session
->sock
->use_tls_sni
= session
->use_tls_sni
;
412 sock_set_nonblocking_mode(session
->sock
, FALSE
);
414 if (!ssl_init_socket(session
->sock
)) {
415 g_warning("couldn't start STARTTLS session");
417 sock_set_nonblocking_mode(session
->sock
, session
->nonblocking
);
421 if (0 < session
->read_buf_len
) {
422 g_warning("protocol violation: suffix data after STARTTLS detected");
424 sock_set_nonblocking_mode(session
->sock
, session
->nonblocking
);
429 sock_set_nonblocking_mode(session
->sock
, session
->nonblocking
);
435 gint
session_send_msg(Session
*session
, const gchar
*msg
)
439 cm_return_val_if_fail(session
->write_buf
== NULL
, -1);
440 cm_return_val_if_fail(msg
!= NULL
, -1);
442 session
->state
= SESSION_SEND
;
443 session
->write_buf
= g_strconcat((strlen(msg
) > 0 ? msg
: ""), "\r\n", NULL
);
444 session
->write_buf_p
= session
->write_buf
;
445 session
->write_buf_len
= strlen(msg
) + 2;
447 ret
= session_write_msg_cb(session
->sock
, G_IO_OUT
, session
);
450 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_OUT
,
451 session_write_msg_cb
, session
);
452 else if (session
->state
== SESSION_ERROR
)
458 gint
session_recv_msg(Session
*session
)
460 cm_return_val_if_fail(session
->read_msg_buf
->len
== 0, -1);
462 session
->state
= SESSION_RECV
;
464 if (session
->read_buf_len
> 0)
465 g_idle_add(session_recv_msg_idle_cb
, session
);
467 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
468 session_read_msg_cb
, session
);
473 static gboolean
session_recv_msg_idle_cb(gpointer data
)
475 Session
*session
= SESSION(data
);
478 ret
= session_read_msg_cb(session
->sock
, G_IO_IN
, session
);
481 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
482 session_read_msg_cb
, session
);
488 *\brief parent (child?): send data to other process
490 *\param session Contains session information
497 gint
session_send_data(Session
*session
, const guchar
*data
, guint size
)
501 cm_return_val_if_fail(session
->write_data
== NULL
, -1);
502 cm_return_val_if_fail(data
!= NULL
, -1);
503 cm_return_val_if_fail(size
!= 0, -1);
505 session
->state
= SESSION_SEND
;
507 session
->write_data
= data
;
508 session
->write_data_p
= session
->write_data
;
509 session
->write_data_len
= size
;
510 g_date_time_unref(session
->tv_prev
);
511 session
->tv_prev
= g_date_time_new_now_local();
513 ret
= session_write_data_cb(session
->sock
, G_IO_OUT
, session
);
516 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_OUT
,
517 session_write_data_cb
,
519 else if (session
->state
== SESSION_ERROR
)
525 gint
session_recv_data(Session
*session
, guint size
, const gchar
*terminator
)
527 cm_return_val_if_fail(session
->read_data_buf
->len
== 0, -1);
529 session
->state
= SESSION_RECV
;
531 g_free(session
->read_data_terminator
);
532 session
->read_data_terminator
= g_strdup(terminator
);
533 g_date_time_unref(session
->tv_prev
);
534 session
->tv_prev
= g_date_time_new_now_local();
536 if (session
->read_buf_len
> 0)
537 g_idle_add(session_recv_data_idle_cb
, session
);
539 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
540 session_read_data_cb
, session
);
545 static gboolean
session_recv_data_idle_cb(gpointer data
)
547 Session
*session
= SESSION(data
);
550 ret
= session_read_data_cb(session
->sock
, G_IO_IN
, session
);
553 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
554 session_read_data_cb
, session
);
559 static gboolean
session_read_msg_cb(SockInfo
*source
, GIOCondition condition
,
562 Session
*session
= SESSION(data
);
563 gchar buf
[SESSION_BUFFSIZE
];
569 cm_return_val_if_fail(condition
== G_IO_IN
, FALSE
);
571 session_set_timeout(session
, session
->timeout_interval
);
573 if (session
->read_buf_len
== 0) {
577 read_len
= sock_read(session
->sock
, session
->read_buf
,
578 SESSION_BUFFSIZE
- 1);
580 if (read_len
== -1 && session
->state
== SESSION_DISCONNECTED
) {
581 g_warning("sock_read: session disconnected");
582 if (session
->io_tag
> 0) {
583 g_source_remove(session
->io_tag
);
590 g_warning("sock_read: received EOF");
591 session
->state
= SESSION_EOF
;
600 g_warning("sock_read: %s", g_strerror(errno
));
601 session
->state
= SESSION_ERROR
;
606 session
->read_buf_len
= read_len
;
609 if ((newline
= memchr(session
->read_buf_p
, '\n', session
->read_buf_len
))
611 line_len
= newline
- session
->read_buf_p
+ 1;
613 line_len
= session
->read_buf_len
;
618 memcpy(buf
, session
->read_buf_p
, line_len
);
619 buf
[line_len
] = '\0';
621 g_string_append(session
->read_msg_buf
, buf
);
623 session
->read_buf_len
-= line_len
;
624 if (session
->read_buf_len
== 0)
625 session
->read_buf_p
= session
->read_buf
;
627 session
->read_buf_p
+= line_len
;
629 /* incomplete read */
630 if (buf
[line_len
- 1] != '\n')
634 if (session
->io_tag
> 0) {
635 g_source_remove(session
->io_tag
);
640 msg
= g_strdup(session
->read_msg_buf
->str
);
642 g_string_truncate(session
->read_msg_buf
, 0);
644 ret
= session
->recv_msg(session
, msg
);
645 session
->recv_msg_notify(session
, msg
, session
->recv_msg_notify_data
);
650 session
->state
= SESSION_ERROR
;
655 static gboolean
session_read_data_cb(SockInfo
*source
, GIOCondition condition
,
658 Session
*session
= SESSION(data
);
659 GByteArray
*data_buf
;
661 gboolean complete
= FALSE
;
665 cm_return_val_if_fail(condition
== G_IO_IN
, FALSE
);
667 session_set_timeout(session
, session
->timeout_interval
);
669 if (session
->read_buf_len
== 0) {
672 read_len
= sock_read(session
->sock
, session
->read_buf
,
676 g_warning("sock_read: received EOF");
677 session
->state
= SESSION_EOF
;
686 g_warning("sock_read: %s", g_strerror(errno
));
687 session
->state
= SESSION_ERROR
;
692 session
->read_buf_len
= read_len
;
695 data_buf
= session
->read_data_buf
;
696 terminator_len
= strlen(session
->read_data_terminator
);
698 if (session
->read_buf_len
== 0)
701 g_byte_array_append(data_buf
, session
->read_buf_p
,
702 session
->read_buf_len
);
704 session
->read_buf_len
= 0;
705 session
->read_buf_p
= session
->read_buf
;
707 /* check if data is terminated */
708 if (data_buf
->len
>= terminator_len
) {
709 if (memcmp(data_buf
->data
, session
->read_data_terminator
,
710 terminator_len
) == 0)
712 else if (data_buf
->len
>= terminator_len
+ 2 &&
713 memcmp(data_buf
->data
+ data_buf
->len
-
714 (terminator_len
+ 2), "\r\n", 2) == 0 &&
715 memcmp(data_buf
->data
+ data_buf
->len
-
716 terminator_len
, session
->read_data_terminator
,
717 terminator_len
) == 0)
721 /* incomplete read */
723 GDateTime
*tv_cur
= g_date_time_new_now_local();
725 GTimeSpan ts
= g_date_time_difference(tv_cur
, session
->tv_prev
);
726 if (1000 - ts
< 0 || ts
> UI_REFRESH_INTERVAL
) {
727 session
->recv_data_progressive_notify
728 (session
, data_buf
->len
, 0,
729 session
->recv_data_progressive_notify_data
);
730 g_date_time_unref(session
->tv_prev
);
731 session
->tv_prev
= g_date_time_new_now_local();
733 g_date_time_unref(tv_cur
);
738 if (session
->io_tag
> 0) {
739 g_source_remove(session
->io_tag
);
743 data_len
= data_buf
->len
- terminator_len
;
746 ret
= session
->recv_data_finished(session
, (gchar
*)data_buf
->data
,
749 g_byte_array_set_size(data_buf
, 0);
751 session
->recv_data_notify(session
, data_len
,
752 session
->recv_data_notify_data
);
755 session
->state
= SESSION_ERROR
;
760 static gint
session_write_buf(Session
*session
)
765 cm_return_val_if_fail(session
->write_buf
!= NULL
, -1);
766 cm_return_val_if_fail(session
->write_buf_p
!= NULL
, -1);
767 cm_return_val_if_fail(session
->write_buf_len
> 0, -1);
769 to_write_len
= session
->write_buf_len
-
770 (session
->write_buf_p
- session
->write_buf
);
771 to_write_len
= MIN(to_write_len
, SESSION_BUFFSIZE
);
773 write_len
= sock_write(session
->sock
, session
->write_buf_p
,
782 g_warning("sock_write: %s", g_strerror(errno
));
783 session
->state
= SESSION_ERROR
;
788 /* incomplete write */
789 if (session
->write_buf_p
- session
->write_buf
+ write_len
<
790 session
->write_buf_len
) {
791 session
->write_buf_p
+= write_len
;
795 g_free(session
->write_buf
);
796 session
->write_buf
= NULL
;
797 session
->write_buf_p
= NULL
;
798 session
->write_buf_len
= 0;
803 static gint
session_write_data(Session
*session
)
808 cm_return_val_if_fail(session
->write_data
!= NULL
, -1);
809 cm_return_val_if_fail(session
->write_data_p
!= NULL
, -1);
810 cm_return_val_if_fail(session
->write_data_len
> 0, -1);
812 to_write_len
= session
->write_data_len
-
813 (session
->write_data_p
- session
->write_data
);
814 to_write_len
= MIN(to_write_len
, SESSION_BUFFSIZE
);
816 write_len
= sock_write(session
->sock
, session
->write_data_p
,
825 g_warning("sock_write: %s", g_strerror(errno
));
826 session
->state
= SESSION_ERROR
;
831 /* incomplete write */
832 if (session
->write_data_p
- session
->write_data
+ write_len
<
833 session
->write_data_len
) {
834 session
->write_data_p
+= write_len
;
838 session
->write_data
= NULL
;
839 session
->write_data_p
= NULL
;
840 session
->write_data_len
= 0;
845 static gboolean
session_write_msg_cb(SockInfo
*source
, GIOCondition condition
,
848 Session
*session
= SESSION(data
);
851 cm_return_val_if_fail(condition
== G_IO_OUT
, FALSE
);
852 cm_return_val_if_fail(session
->write_buf
!= NULL
, FALSE
);
853 cm_return_val_if_fail(session
->write_buf_p
!= NULL
, FALSE
);
854 cm_return_val_if_fail(session
->write_buf_len
> 0, FALSE
);
856 ret
= session_write_buf(session
);
859 session
->state
= SESSION_ERROR
;
864 if (session
->io_tag
> 0) {
865 g_source_remove(session
->io_tag
);
869 session_recv_msg(session
);
874 static gboolean
session_write_data_cb(SockInfo
*source
,
875 GIOCondition condition
, gpointer data
)
877 Session
*session
= SESSION(data
);
878 guint write_data_len
;
881 cm_return_val_if_fail(condition
== G_IO_OUT
, FALSE
);
882 cm_return_val_if_fail(session
->write_data
!= NULL
, FALSE
);
883 cm_return_val_if_fail(session
->write_data_p
!= NULL
, FALSE
);
884 cm_return_val_if_fail(session
->write_data_len
> 0, FALSE
);
886 write_data_len
= session
->write_data_len
;
888 ret
= session_write_data(session
);
891 session
->state
= SESSION_ERROR
;
893 } else if (ret
> 0) {
894 GDateTime
*tv_cur
= g_date_time_new_now_local();
896 GTimeSpan ts
= g_date_time_difference(tv_cur
, session
->tv_prev
);
897 if (1000 - ts
< 0 || ts
> UI_REFRESH_INTERVAL
) {
898 session_set_timeout(session
, session
->timeout_interval
);
899 session
->send_data_progressive_notify
901 session
->write_data_p
- session
->write_data
,
903 session
->send_data_progressive_notify_data
);
904 g_date_time_unref(session
->tv_prev
);
905 session
->tv_prev
= g_date_time_new_now_local();
907 g_date_time_unref(tv_cur
);
911 if (session
->io_tag
> 0) {
912 g_source_remove(session
->io_tag
);
917 ret
= session
->send_data_finished(session
, write_data_len
);
918 session
->send_data_notify(session
, write_data_len
,
919 session
->send_data_notify_data
);
924 void session_register_ping(Session
*session
, gboolean (*ping_cb
)(gpointer data
))
928 if (session
->ping_tag
> -1)
929 g_source_remove(session
->ping_tag
);
931 session
->ping_tag
= -1;
934 session
->ping_tag
= g_timeout_add_seconds(60, ping_cb
, session
);