2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
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
;
73 session
->nonblocking
= TRUE
;
74 session
->state
= SESSION_READY
;
75 session
->last_access_time
= time(NULL
);
77 g_get_current_time(&session
->tv_prev
);
83 session
->read_buf_p
= session
->read_buf
;
84 session
->read_buf_len
= 0;
86 session
->read_msg_buf
= g_string_sized_new(1024);
87 session
->read_data_buf
= g_byte_array_new();
89 session
->write_buf
= NULL
;
90 session
->write_buf_p
= NULL
;
91 session
->write_buf_len
= 0;
93 session
->write_data
= NULL
;
94 session
->write_data_p
= NULL
;
95 session
->write_data_len
= 0;
97 session
->timeout_tag
= 0;
98 session
->timeout_interval
= 0;
100 session
->data
= NULL
;
101 session
->account
= prefs_account
;
102 session
->is_smtp
= is_smtp
;
104 session
->ping_tag
= -1;
108 *\brief Set up parent and child process
109 * Childloop: Read commands from parent,
110 * send to server, get answer, pass to parent
112 *\param session Contains session information
113 * server to connect to
117 * -1 : pipe / fork errors (parent)
118 * 1 : connection error (child)
120 gint
session_connect(Session
*session
, const gchar
*server
, gushort port
)
123 session
->server
= g_strdup(server
);
124 session
->port
= port
;
126 session
->conn_id
= sock_connect_async(server
, port
, session_connect_cb
,
128 if (session
->conn_id
< 0) {
129 g_warning("can't connect to server.");
130 session_close(session
);
131 if (session
->connect_finished
)
132 session
->connect_finished(session
, FALSE
);
140 session
->server
= g_strdup(server
);
141 session
->port
= port
;
143 sock
= sock_connect(server
, port
);
145 g_warning("can't connect to server.");
146 session_close(session
);
147 if (session
->connect_finished
)
148 session
->connect_finished(session
, FALSE
);
151 sock
->is_smtp
= session
->is_smtp
;
153 return session_connect_cb(sock
, session
);
157 static gint
session_connect_cb(SockInfo
*sock
, gpointer data
)
159 Session
*session
= SESSION(data
);
161 session
->conn_id
= 0;
164 g_warning("can't connect to server.");
165 session
->state
= SESSION_ERROR
;
166 if (session
->connect_finished
)
167 session
->connect_finished(session
, FALSE
);
171 session
->sock
= sock
;
172 sock
->account
= session
->account
;
173 sock
->is_smtp
= session
->is_smtp
;
174 sock
->ssl_cert_auto_accept
= session
->ssl_cert_auto_accept
;
177 sock
->gnutls_priority
= session
->gnutls_priority
;
179 if (session
->ssl_type
== SSL_TUNNEL
) {
180 sock_set_nonblocking_mode(sock
, FALSE
);
181 if (!ssl_init_socket(sock
)) {
182 g_warning("can't initialize SSL.");
183 log_error(LOG_PROTOCOL
, _("SSL handshake failed\n"));
184 session
->state
= SESSION_ERROR
;
185 if (session
->connect_finished
)
186 session
->connect_finished(session
, FALSE
);
192 /* we could have gotten a timeout while waiting for user input in
193 * an SSL certificate dialog */
194 if (session
->state
== SESSION_TIMEOUT
) {
195 if (session
->connect_finished
)
196 session
->connect_finished(session
, FALSE
);
200 sock_set_nonblocking_mode(sock
, session
->nonblocking
);
202 debug_print("session (%p): connected\n", session
);
204 session
->state
= SESSION_RECV
;
205 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
209 if (session
->connect_finished
)
210 session
->connect_finished(session
, TRUE
);
215 *\brief child and parent: send DISCONNECT message to other process
217 *\param session Contains session information
221 gint
session_disconnect(Session
*session
)
223 session_close(session
);
230 *\param session Contains session information
232 void session_destroy(Session
*session
)
234 cm_return_if_fail(session
!= NULL
);
235 cm_return_if_fail(session
->destroy
!= NULL
);
237 session_register_ping(session
, NULL
);
239 session_close(session
);
240 session
->destroy(session
);
241 g_free(session
->server
);
242 g_string_free(session
->read_msg_buf
, TRUE
);
243 g_byte_array_free(session
->read_data_buf
, TRUE
);
244 g_free(session
->read_data_terminator
);
245 g_free(session
->write_buf
);
247 g_free(session
->gnutls_priority
);
250 debug_print("session (%p): destroyed\n", session
);
255 gboolean
session_is_running(Session
*session
)
257 return (session
->state
== SESSION_READY
||
258 session
->state
== SESSION_SEND
||
259 session
->state
== SESSION_RECV
);
262 gboolean
session_is_connected(Session
*session
)
264 return (session
->state
== SESSION_SEND
||
265 session
->state
== SESSION_RECV
);
268 void session_set_access_time(Session
*session
)
270 session
->last_access_time
= time(NULL
);
273 void session_set_timeout(Session
*session
, guint interval
)
275 if (session
->timeout_tag
> 0)
276 g_source_remove(session
->timeout_tag
);
278 session
->timeout_interval
= interval
;
280 if (interval
% 1000 == 0)
281 session
->timeout_tag
=
282 g_timeout_add_seconds(interval
/1000, session_timeout_cb
, session
);
284 session
->timeout_tag
=
285 g_timeout_add(interval
, session_timeout_cb
, session
);
287 session
->timeout_tag
= 0;
290 static gboolean
session_timeout_cb(gpointer data
)
292 Session
*session
= SESSION(data
);
294 g_warning("session timeout.");
296 if (session
->io_tag
> 0) {
297 g_source_remove(session
->io_tag
);
301 session
->timeout_tag
= 0;
302 session
->state
= SESSION_TIMEOUT
;
307 void session_set_recv_message_notify(Session
*session
,
308 RecvMsgNotify notify_func
, gpointer data
)
310 session
->recv_msg_notify
= notify_func
;
311 session
->recv_msg_notify_data
= data
;
314 void session_set_recv_data_progressive_notify
316 RecvDataProgressiveNotify notify_func
,
319 session
->recv_data_progressive_notify
= notify_func
,
320 session
->recv_data_progressive_notify_data
= data
;
323 void session_set_recv_data_notify(Session
*session
, RecvDataNotify notify_func
,
326 session
->recv_data_notify
= notify_func
;
327 session
->recv_data_notify_data
= data
;
330 void session_set_send_data_progressive_notify
332 SendDataProgressiveNotify notify_func
,
335 session
->send_data_progressive_notify
= notify_func
;
336 session
->send_data_progressive_notify_data
= data
;
339 void session_set_send_data_notify(Session
*session
, SendDataNotify notify_func
,
342 session
->send_data_notify
= notify_func
;
343 session
->send_data_notify_data
= data
;
347 *\brief child and parent cleanup (child closes first)
349 *\param session Contains session information
353 static gint
session_close(Session
*session
)
355 cm_return_val_if_fail(session
!= NULL
, -1);
358 if (session
->conn_id
> 0) {
359 sock_connect_async_cancel(session
->conn_id
);
360 session
->conn_id
= 0;
361 debug_print("session (%p): connection cancelled\n", session
);
365 session_set_timeout(session
, 0);
367 if (session
->io_tag
> 0) {
368 g_source_remove(session
->io_tag
);
373 sock_close(session
->sock
);
374 session
->sock
= NULL
;
375 session
->state
= SESSION_DISCONNECTED
;
376 debug_print("session (%p): closed\n", session
);
383 gint
session_start_tls(Session
*session
)
387 nb_mode
= sock_is_nonblocking_mode(session
->sock
);
389 session
->sock
->ssl_cert_auto_accept
= session
->ssl_cert_auto_accept
;
390 session
->sock
->gnutls_priority
= session
->gnutls_priority
;
393 sock_set_nonblocking_mode(session
->sock
, FALSE
);
395 if (!ssl_init_socket(session
->sock
)) {
396 g_warning("couldn't start TLS session.");
398 sock_set_nonblocking_mode(session
->sock
, session
->nonblocking
);
403 sock_set_nonblocking_mode(session
->sock
, session
->nonblocking
);
409 gint
session_send_msg(Session
*session
, const gchar
*msg
)
413 cm_return_val_if_fail(session
->write_buf
== NULL
, -1);
414 cm_return_val_if_fail(msg
!= NULL
, -1);
415 cm_return_val_if_fail(msg
[0] != '\0', -1);
417 session
->state
= SESSION_SEND
;
418 session
->write_buf
= g_strconcat(msg
, "\r\n", NULL
);
419 session
->write_buf_p
= session
->write_buf
;
420 session
->write_buf_len
= strlen(msg
) + 2;
422 ret
= session_write_msg_cb(session
->sock
, G_IO_OUT
, session
);
425 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_OUT
,
426 session_write_msg_cb
, session
);
427 else if (session
->state
== SESSION_ERROR
)
433 gint
session_recv_msg(Session
*session
)
435 cm_return_val_if_fail(session
->read_msg_buf
->len
== 0, -1);
437 session
->state
= SESSION_RECV
;
439 if (session
->read_buf_len
> 0)
440 g_idle_add(session_recv_msg_idle_cb
, session
);
442 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
443 session_read_msg_cb
, session
);
448 static gboolean
session_recv_msg_idle_cb(gpointer data
)
450 Session
*session
= SESSION(data
);
453 ret
= session_read_msg_cb(session
->sock
, G_IO_IN
, session
);
456 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
457 session_read_msg_cb
, session
);
463 *\brief parent (child?): send data to other process
465 *\param session Contains session information
472 gint
session_send_data(Session
*session
, const guchar
*data
, guint size
)
476 cm_return_val_if_fail(session
->write_data
== NULL
, -1);
477 cm_return_val_if_fail(data
!= NULL
, -1);
478 cm_return_val_if_fail(size
!= 0, -1);
480 session
->state
= SESSION_SEND
;
482 session
->write_data
= data
;
483 session
->write_data_p
= session
->write_data
;
484 session
->write_data_len
= size
;
485 g_get_current_time(&session
->tv_prev
);
487 ret
= session_write_data_cb(session
->sock
, G_IO_OUT
, session
);
490 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_OUT
,
491 session_write_data_cb
,
493 else if (session
->state
== SESSION_ERROR
)
499 gint
session_recv_data(Session
*session
, guint size
, const gchar
*terminator
)
501 cm_return_val_if_fail(session
->read_data_buf
->len
== 0, -1);
503 session
->state
= SESSION_RECV
;
505 g_free(session
->read_data_terminator
);
506 session
->read_data_terminator
= g_strdup(terminator
);
507 g_get_current_time(&session
->tv_prev
);
509 if (session
->read_buf_len
> 0)
510 g_idle_add(session_recv_data_idle_cb
, session
);
512 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
513 session_read_data_cb
, session
);
518 static gboolean
session_recv_data_idle_cb(gpointer data
)
520 Session
*session
= SESSION(data
);
523 ret
= session_read_data_cb(session
->sock
, G_IO_IN
, session
);
526 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
527 session_read_data_cb
, session
);
532 static gboolean
session_read_msg_cb(SockInfo
*source
, GIOCondition condition
,
535 Session
*session
= SESSION(data
);
536 gchar buf
[SESSION_BUFFSIZE
];
542 cm_return_val_if_fail(condition
== G_IO_IN
, FALSE
);
544 session_set_timeout(session
, session
->timeout_interval
);
546 if (session
->read_buf_len
== 0) {
550 read_len
= sock_read(session
->sock
, session
->read_buf
,
551 SESSION_BUFFSIZE
- 1);
553 if (read_len
== -1 && session
->state
== SESSION_DISCONNECTED
) {
554 g_warning ("sock_read: session disconnected");
555 if (session
->io_tag
> 0) {
556 g_source_remove(session
->io_tag
);
563 g_warning("sock_read: received EOF");
564 session
->state
= SESSION_EOF
;
573 g_warning("sock_read: %s", g_strerror(errno
));
574 session
->state
= SESSION_ERROR
;
579 session
->read_buf_len
= read_len
;
582 if ((newline
= memchr(session
->read_buf_p
, '\n', session
->read_buf_len
))
584 line_len
= newline
- session
->read_buf_p
+ 1;
586 line_len
= session
->read_buf_len
;
591 memcpy(buf
, session
->read_buf_p
, line_len
);
592 buf
[line_len
] = '\0';
594 g_string_append(session
->read_msg_buf
, buf
);
596 session
->read_buf_len
-= line_len
;
597 if (session
->read_buf_len
== 0)
598 session
->read_buf_p
= session
->read_buf
;
600 session
->read_buf_p
+= line_len
;
602 /* incomplete read */
603 if (buf
[line_len
- 1] != '\n')
607 if (session
->io_tag
> 0) {
608 g_source_remove(session
->io_tag
);
613 msg
= g_strdup(session
->read_msg_buf
->str
);
615 g_string_truncate(session
->read_msg_buf
, 0);
617 ret
= session
->recv_msg(session
, msg
);
618 session
->recv_msg_notify(session
, msg
, session
->recv_msg_notify_data
);
623 session
->state
= SESSION_ERROR
;
628 static gboolean
session_read_data_cb(SockInfo
*source
, GIOCondition condition
,
631 Session
*session
= SESSION(data
);
632 GByteArray
*data_buf
;
634 gboolean complete
= FALSE
;
638 cm_return_val_if_fail(condition
== G_IO_IN
, FALSE
);
640 session_set_timeout(session
, session
->timeout_interval
);
642 if (session
->read_buf_len
== 0) {
645 read_len
= sock_read(session
->sock
, session
->read_buf
,
649 g_warning("sock_read: received EOF");
650 session
->state
= SESSION_EOF
;
659 g_warning("sock_read: %s", g_strerror(errno
));
660 session
->state
= SESSION_ERROR
;
665 session
->read_buf_len
= read_len
;
668 data_buf
= session
->read_data_buf
;
669 terminator_len
= strlen(session
->read_data_terminator
);
671 if (session
->read_buf_len
== 0)
674 g_byte_array_append(data_buf
, session
->read_buf_p
,
675 session
->read_buf_len
);
677 session
->read_buf_len
= 0;
678 session
->read_buf_p
= session
->read_buf
;
680 /* check if data is terminated */
681 if (data_buf
->len
>= terminator_len
) {
682 if (memcmp(data_buf
->data
, session
->read_data_terminator
,
683 terminator_len
) == 0)
685 else if (data_buf
->len
>= terminator_len
+ 2 &&
686 memcmp(data_buf
->data
+ data_buf
->len
-
687 (terminator_len
+ 2), "\r\n", 2) == 0 &&
688 memcmp(data_buf
->data
+ data_buf
->len
-
689 terminator_len
, session
->read_data_terminator
,
690 terminator_len
) == 0)
694 /* incomplete read */
698 g_get_current_time(&tv_cur
);
699 if (tv_cur
.tv_sec
- session
->tv_prev
.tv_sec
> 0 ||
700 tv_cur
.tv_usec
- session
->tv_prev
.tv_usec
>
701 UI_REFRESH_INTERVAL
) {
702 session
->recv_data_progressive_notify
703 (session
, data_buf
->len
, 0,
704 session
->recv_data_progressive_notify_data
);
705 g_get_current_time(&session
->tv_prev
);
711 if (session
->io_tag
> 0) {
712 g_source_remove(session
->io_tag
);
716 data_len
= data_buf
->len
- terminator_len
;
719 ret
= session
->recv_data_finished(session
, (gchar
*)data_buf
->data
,
722 g_byte_array_set_size(data_buf
, 0);
724 session
->recv_data_notify(session
, data_len
,
725 session
->recv_data_notify_data
);
728 session
->state
= SESSION_ERROR
;
733 static gint
session_write_buf(Session
*session
)
738 cm_return_val_if_fail(session
->write_buf
!= NULL
, -1);
739 cm_return_val_if_fail(session
->write_buf_p
!= NULL
, -1);
740 cm_return_val_if_fail(session
->write_buf_len
> 0, -1);
742 to_write_len
= session
->write_buf_len
-
743 (session
->write_buf_p
- session
->write_buf
);
744 to_write_len
= MIN(to_write_len
, SESSION_BUFFSIZE
);
746 write_len
= sock_write(session
->sock
, session
->write_buf_p
,
755 g_warning("sock_write: %s", g_strerror(errno
));
756 session
->state
= SESSION_ERROR
;
761 /* incomplete write */
762 if (session
->write_buf_p
- session
->write_buf
+ write_len
<
763 session
->write_buf_len
) {
764 session
->write_buf_p
+= write_len
;
768 g_free(session
->write_buf
);
769 session
->write_buf
= NULL
;
770 session
->write_buf_p
= NULL
;
771 session
->write_buf_len
= 0;
776 static gint
session_write_data(Session
*session
)
781 cm_return_val_if_fail(session
->write_data
!= NULL
, -1);
782 cm_return_val_if_fail(session
->write_data_p
!= NULL
, -1);
783 cm_return_val_if_fail(session
->write_data_len
> 0, -1);
785 to_write_len
= session
->write_data_len
-
786 (session
->write_data_p
- session
->write_data
);
787 to_write_len
= MIN(to_write_len
, SESSION_BUFFSIZE
);
789 write_len
= sock_write(session
->sock
, session
->write_data_p
,
798 g_warning("sock_write: %s", g_strerror(errno
));
799 session
->state
= SESSION_ERROR
;
804 /* incomplete write */
805 if (session
->write_data_p
- session
->write_data
+ write_len
<
806 session
->write_data_len
) {
807 session
->write_data_p
+= write_len
;
811 session
->write_data
= NULL
;
812 session
->write_data_p
= NULL
;
813 session
->write_data_len
= 0;
818 static gboolean
session_write_msg_cb(SockInfo
*source
, GIOCondition condition
,
821 Session
*session
= SESSION(data
);
824 cm_return_val_if_fail(condition
== G_IO_OUT
, FALSE
);
825 cm_return_val_if_fail(session
->write_buf
!= NULL
, FALSE
);
826 cm_return_val_if_fail(session
->write_buf_p
!= NULL
, FALSE
);
827 cm_return_val_if_fail(session
->write_buf_len
> 0, FALSE
);
829 ret
= session_write_buf(session
);
832 session
->state
= SESSION_ERROR
;
837 if (session
->io_tag
> 0) {
838 g_source_remove(session
->io_tag
);
842 session_recv_msg(session
);
847 static gboolean
session_write_data_cb(SockInfo
*source
,
848 GIOCondition condition
, gpointer data
)
850 Session
*session
= SESSION(data
);
851 guint write_data_len
;
854 cm_return_val_if_fail(condition
== G_IO_OUT
, FALSE
);
855 cm_return_val_if_fail(session
->write_data
!= NULL
, FALSE
);
856 cm_return_val_if_fail(session
->write_data_p
!= NULL
, FALSE
);
857 cm_return_val_if_fail(session
->write_data_len
> 0, FALSE
);
859 write_data_len
= session
->write_data_len
;
861 ret
= session_write_data(session
);
864 session
->state
= SESSION_ERROR
;
866 } else if (ret
> 0) {
869 g_get_current_time(&tv_cur
);
870 if (tv_cur
.tv_sec
- session
->tv_prev
.tv_sec
> 0 ||
871 tv_cur
.tv_usec
- session
->tv_prev
.tv_usec
>
872 UI_REFRESH_INTERVAL
) {
873 session_set_timeout(session
, session
->timeout_interval
);
874 session
->send_data_progressive_notify
876 session
->write_data_p
- session
->write_data
,
878 session
->send_data_progressive_notify_data
);
879 g_get_current_time(&session
->tv_prev
);
884 if (session
->io_tag
> 0) {
885 g_source_remove(session
->io_tag
);
890 ret
= session
->send_data_finished(session
, write_data_len
);
891 session
->send_data_notify(session
, write_data_len
,
892 session
->send_data_notify_data
);
897 void session_register_ping(Session
*session
, gboolean (*ping_cb
)(gpointer data
))
901 if (session
->ping_tag
> -1)
902 g_source_remove(session
->ping_tag
);
904 session
->ping_tag
= -1;
907 session
->ping_tag
= g_timeout_add_seconds(60, ping_cb
, session
);