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
);
138 session
->server
= g_strdup(server
);
139 session
->port
= port
;
141 sock
= sock_connect(server
, port
);
143 g_warning("can't connect to server.");
144 session_close(session
);
147 sock
->is_smtp
= session
->is_smtp
;
149 return session_connect_cb(sock
, session
);
153 static gint
session_connect_cb(SockInfo
*sock
, gpointer data
)
155 Session
*session
= SESSION(data
);
157 session
->conn_id
= 0;
160 g_warning("can't connect to server.");
161 session
->state
= SESSION_ERROR
;
165 session
->sock
= sock
;
166 sock
->account
= session
->account
;
167 sock
->is_smtp
= session
->is_smtp
;
168 sock
->ssl_cert_auto_accept
= session
->ssl_cert_auto_accept
;
171 sock
->gnutls_priority
= session
->gnutls_priority
;
173 if (session
->ssl_type
== SSL_TUNNEL
) {
174 sock_set_nonblocking_mode(sock
, FALSE
);
175 if (!ssl_init_socket(sock
)) {
176 g_warning("can't initialize SSL.");
177 log_error(LOG_PROTOCOL
, _("SSL handshake failed\n"));
178 session
->state
= SESSION_ERROR
;
184 /* we could have gotten a timeout while waiting for user input in
185 * an SSL certificate dialog */
186 if (session
->state
== SESSION_TIMEOUT
)
189 sock_set_nonblocking_mode(sock
, session
->nonblocking
);
191 debug_print("session (%p): connected\n", session
);
193 session
->state
= SESSION_RECV
;
194 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
202 *\brief child and parent: send DISCONNECT message to other process
204 *\param session Contains session information
208 gint
session_disconnect(Session
*session
)
210 session_close(session
);
217 *\param session Contains session information
219 void session_destroy(Session
*session
)
221 cm_return_if_fail(session
!= NULL
);
222 cm_return_if_fail(session
->destroy
!= NULL
);
224 session_register_ping(session
, NULL
);
226 session_close(session
);
227 session
->destroy(session
);
228 g_free(session
->server
);
229 g_string_free(session
->read_msg_buf
, TRUE
);
230 g_byte_array_free(session
->read_data_buf
, TRUE
);
231 g_free(session
->read_data_terminator
);
232 g_free(session
->write_buf
);
234 g_free(session
->gnutls_priority
);
237 debug_print("session (%p): destroyed\n", session
);
242 gboolean
session_is_running(Session
*session
)
244 return (session
->state
== SESSION_READY
||
245 session
->state
== SESSION_SEND
||
246 session
->state
== SESSION_RECV
);
249 gboolean
session_is_connected(Session
*session
)
251 return (session
->state
== SESSION_SEND
||
252 session
->state
== SESSION_RECV
);
255 void session_set_access_time(Session
*session
)
257 session
->last_access_time
= time(NULL
);
260 void session_set_timeout(Session
*session
, guint interval
)
262 if (session
->timeout_tag
> 0)
263 g_source_remove(session
->timeout_tag
);
265 session
->timeout_interval
= interval
;
267 if (interval
% 1000 == 0)
268 session
->timeout_tag
=
269 g_timeout_add_seconds(interval
/1000, session_timeout_cb
, session
);
271 session
->timeout_tag
=
272 g_timeout_add(interval
, session_timeout_cb
, session
);
274 session
->timeout_tag
= 0;
277 static gboolean
session_timeout_cb(gpointer data
)
279 Session
*session
= SESSION(data
);
281 g_warning("session timeout.\n");
283 if (session
->io_tag
> 0) {
284 g_source_remove(session
->io_tag
);
288 session
->timeout_tag
= 0;
289 session
->state
= SESSION_TIMEOUT
;
294 void session_set_recv_message_notify(Session
*session
,
295 RecvMsgNotify notify_func
, gpointer data
)
297 session
->recv_msg_notify
= notify_func
;
298 session
->recv_msg_notify_data
= data
;
301 void session_set_recv_data_progressive_notify
303 RecvDataProgressiveNotify notify_func
,
306 session
->recv_data_progressive_notify
= notify_func
,
307 session
->recv_data_progressive_notify_data
= data
;
310 void session_set_recv_data_notify(Session
*session
, RecvDataNotify notify_func
,
313 session
->recv_data_notify
= notify_func
;
314 session
->recv_data_notify_data
= data
;
317 void session_set_send_data_progressive_notify
319 SendDataProgressiveNotify notify_func
,
322 session
->send_data_progressive_notify
= notify_func
;
323 session
->send_data_progressive_notify_data
= data
;
326 void session_set_send_data_notify(Session
*session
, SendDataNotify notify_func
,
329 session
->send_data_notify
= notify_func
;
330 session
->send_data_notify_data
= data
;
334 *\brief child and parent cleanup (child closes first)
336 *\param session Contains session information
340 static gint
session_close(Session
*session
)
342 cm_return_val_if_fail(session
!= NULL
, -1);
345 if (session
->conn_id
> 0) {
346 sock_connect_async_cancel(session
->conn_id
);
347 session
->conn_id
= 0;
348 debug_print("session (%p): connection cancelled\n", session
);
352 session_set_timeout(session
, 0);
354 if (session
->io_tag
> 0) {
355 g_source_remove(session
->io_tag
);
360 sock_close(session
->sock
);
361 session
->sock
= NULL
;
362 session
->state
= SESSION_DISCONNECTED
;
363 debug_print("session (%p): closed\n", session
);
370 gint
session_start_tls(Session
*session
)
374 nb_mode
= sock_is_nonblocking_mode(session
->sock
);
376 session
->sock
->ssl_cert_auto_accept
= session
->ssl_cert_auto_accept
;
379 sock_set_nonblocking_mode(session
->sock
, FALSE
);
381 if (!ssl_init_socket_with_method(session
->sock
, SSL_METHOD_TLSv1
)) {
382 g_warning("couldn't start TLS session.\n");
384 sock_set_nonblocking_mode(session
->sock
, session
->nonblocking
);
389 sock_set_nonblocking_mode(session
->sock
, session
->nonblocking
);
395 gint
session_send_msg(Session
*session
, SessionMsgType type
, const gchar
*msg
)
399 cm_return_val_if_fail(session
->write_buf
== NULL
, -1);
400 cm_return_val_if_fail(msg
!= NULL
, -1);
401 cm_return_val_if_fail(msg
[0] != '\0', -1);
403 session
->state
= SESSION_SEND
;
404 session
->write_buf
= g_strconcat(msg
, "\r\n", NULL
);
405 session
->write_buf_p
= session
->write_buf
;
406 session
->write_buf_len
= strlen(msg
) + 2;
408 ret
= session_write_msg_cb(session
->sock
, G_IO_OUT
, session
);
411 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_OUT
,
412 session_write_msg_cb
, session
);
413 else if (session
->state
== SESSION_ERROR
)
419 gint
session_recv_msg(Session
*session
)
421 cm_return_val_if_fail(session
->read_msg_buf
->len
== 0, -1);
423 session
->state
= SESSION_RECV
;
425 if (session
->read_buf_len
> 0)
426 g_idle_add(session_recv_msg_idle_cb
, session
);
428 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
429 session_read_msg_cb
, session
);
434 static gboolean
session_recv_msg_idle_cb(gpointer data
)
436 Session
*session
= SESSION(data
);
439 ret
= session_read_msg_cb(session
->sock
, G_IO_IN
, session
);
442 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
443 session_read_msg_cb
, session
);
449 *\brief parent (child?): send data to other process
451 *\param session Contains session information
458 gint
session_send_data(Session
*session
, const guchar
*data
, guint size
)
462 cm_return_val_if_fail(session
->write_data
== NULL
, -1);
463 cm_return_val_if_fail(data
!= NULL
, -1);
464 cm_return_val_if_fail(size
!= 0, -1);
466 session
->state
= SESSION_SEND
;
468 session
->write_data
= data
;
469 session
->write_data_p
= session
->write_data
;
470 session
->write_data_len
= size
;
471 g_get_current_time(&session
->tv_prev
);
473 ret
= session_write_data_cb(session
->sock
, G_IO_OUT
, session
);
476 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_OUT
,
477 session_write_data_cb
,
479 else if (session
->state
== SESSION_ERROR
)
485 gint
session_recv_data(Session
*session
, guint size
, const gchar
*terminator
)
487 cm_return_val_if_fail(session
->read_data_buf
->len
== 0, -1);
489 session
->state
= SESSION_RECV
;
491 g_free(session
->read_data_terminator
);
492 session
->read_data_terminator
= g_strdup(terminator
);
493 g_get_current_time(&session
->tv_prev
);
495 if (session
->read_buf_len
> 0)
496 g_idle_add(session_recv_data_idle_cb
, session
);
498 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
499 session_read_data_cb
, session
);
504 static gboolean
session_recv_data_idle_cb(gpointer data
)
506 Session
*session
= SESSION(data
);
509 ret
= session_read_data_cb(session
->sock
, G_IO_IN
, session
);
512 session
->io_tag
= sock_add_watch(session
->sock
, G_IO_IN
,
513 session_read_data_cb
, session
);
518 static gboolean
session_read_msg_cb(SockInfo
*source
, GIOCondition condition
,
521 Session
*session
= SESSION(data
);
522 gchar buf
[SESSION_BUFFSIZE
];
528 cm_return_val_if_fail(condition
== G_IO_IN
, FALSE
);
530 session_set_timeout(session
, session
->timeout_interval
);
532 if (session
->read_buf_len
== 0) {
536 read_len
= sock_read(session
->sock
, session
->read_buf
,
537 SESSION_BUFFSIZE
- 1);
539 if (read_len
== -1 && session
->state
== SESSION_DISCONNECTED
) {
540 g_warning ("sock_read: session disconnected\n");
541 if (session
->io_tag
> 0) {
542 g_source_remove(session
->io_tag
);
549 g_warning("sock_read: received EOF\n");
550 session
->state
= SESSION_EOF
;
559 g_warning("sock_read: %s\n", g_strerror(errno
));
560 session
->state
= SESSION_ERROR
;
565 session
->read_buf_len
= read_len
;
568 if ((newline
= memchr(session
->read_buf_p
, '\n', session
->read_buf_len
))
570 line_len
= newline
- session
->read_buf_p
+ 1;
572 line_len
= session
->read_buf_len
;
577 memcpy(buf
, session
->read_buf_p
, line_len
);
578 buf
[line_len
] = '\0';
580 g_string_append(session
->read_msg_buf
, buf
);
582 session
->read_buf_len
-= line_len
;
583 if (session
->read_buf_len
== 0)
584 session
->read_buf_p
= session
->read_buf
;
586 session
->read_buf_p
+= line_len
;
588 /* incomplete read */
589 if (buf
[line_len
- 1] != '\n')
593 if (session
->io_tag
> 0) {
594 g_source_remove(session
->io_tag
);
599 msg
= g_strdup(session
->read_msg_buf
->str
);
601 g_string_truncate(session
->read_msg_buf
, 0);
603 ret
= session
->recv_msg(session
, msg
);
604 session
->recv_msg_notify(session
, msg
, session
->recv_msg_notify_data
);
609 session
->state
= SESSION_ERROR
;
614 static gboolean
session_read_data_cb(SockInfo
*source
, GIOCondition condition
,
617 Session
*session
= SESSION(data
);
618 GByteArray
*data_buf
;
620 gboolean complete
= FALSE
;
624 cm_return_val_if_fail(condition
== G_IO_IN
, FALSE
);
626 session_set_timeout(session
, session
->timeout_interval
);
628 if (session
->read_buf_len
== 0) {
631 read_len
= sock_read(session
->sock
, session
->read_buf
,
635 g_warning("sock_read: received EOF\n");
636 session
->state
= SESSION_EOF
;
645 g_warning("sock_read: %s\n", g_strerror(errno
));
646 session
->state
= SESSION_ERROR
;
651 session
->read_buf_len
= read_len
;
654 data_buf
= session
->read_data_buf
;
655 terminator_len
= strlen(session
->read_data_terminator
);
657 if (session
->read_buf_len
== 0)
660 g_byte_array_append(data_buf
, session
->read_buf_p
,
661 session
->read_buf_len
);
663 session
->read_buf_len
= 0;
664 session
->read_buf_p
= session
->read_buf
;
666 /* check if data is terminated */
667 if (data_buf
->len
>= terminator_len
) {
668 if (memcmp(data_buf
->data
, session
->read_data_terminator
,
669 terminator_len
) == 0)
671 else if (data_buf
->len
>= terminator_len
+ 2 &&
672 memcmp(data_buf
->data
+ data_buf
->len
-
673 (terminator_len
+ 2), "\r\n", 2) == 0 &&
674 memcmp(data_buf
->data
+ data_buf
->len
-
675 terminator_len
, session
->read_data_terminator
,
676 terminator_len
) == 0)
680 /* incomplete read */
684 g_get_current_time(&tv_cur
);
685 if (tv_cur
.tv_sec
- session
->tv_prev
.tv_sec
> 0 ||
686 tv_cur
.tv_usec
- session
->tv_prev
.tv_usec
>
687 UI_REFRESH_INTERVAL
) {
688 session
->recv_data_progressive_notify
689 (session
, data_buf
->len
, 0,
690 session
->recv_data_progressive_notify_data
);
691 g_get_current_time(&session
->tv_prev
);
697 if (session
->io_tag
> 0) {
698 g_source_remove(session
->io_tag
);
702 data_len
= data_buf
->len
- terminator_len
;
705 ret
= session
->recv_data_finished(session
, (gchar
*)data_buf
->data
,
708 g_byte_array_set_size(data_buf
, 0);
710 session
->recv_data_notify(session
, data_len
,
711 session
->recv_data_notify_data
);
714 session
->state
= SESSION_ERROR
;
719 static gint
session_write_buf(Session
*session
)
724 cm_return_val_if_fail(session
->write_buf
!= NULL
, -1);
725 cm_return_val_if_fail(session
->write_buf_p
!= NULL
, -1);
726 cm_return_val_if_fail(session
->write_buf_len
> 0, -1);
728 to_write_len
= session
->write_buf_len
-
729 (session
->write_buf_p
- session
->write_buf
);
730 to_write_len
= MIN(to_write_len
, SESSION_BUFFSIZE
);
732 write_len
= sock_write(session
->sock
, session
->write_buf_p
,
741 g_warning("sock_write: %s\n", g_strerror(errno
));
742 session
->state
= SESSION_ERROR
;
747 /* incomplete write */
748 if (session
->write_buf_p
- session
->write_buf
+ write_len
<
749 session
->write_buf_len
) {
750 session
->write_buf_p
+= write_len
;
754 g_free(session
->write_buf
);
755 session
->write_buf
= NULL
;
756 session
->write_buf_p
= NULL
;
757 session
->write_buf_len
= 0;
762 static gint
session_write_data(Session
*session
)
767 cm_return_val_if_fail(session
->write_data
!= NULL
, -1);
768 cm_return_val_if_fail(session
->write_data_p
!= NULL
, -1);
769 cm_return_val_if_fail(session
->write_data_len
> 0, -1);
771 to_write_len
= session
->write_data_len
-
772 (session
->write_data_p
- session
->write_data
);
773 to_write_len
= MIN(to_write_len
, SESSION_BUFFSIZE
);
775 write_len
= sock_write(session
->sock
, session
->write_data_p
,
784 g_warning("sock_write: %s\n", g_strerror(errno
));
785 session
->state
= SESSION_ERROR
;
790 /* incomplete write */
791 if (session
->write_data_p
- session
->write_data
+ write_len
<
792 session
->write_data_len
) {
793 session
->write_data_p
+= write_len
;
797 session
->write_data
= NULL
;
798 session
->write_data_p
= NULL
;
799 session
->write_data_len
= 0;
804 static gboolean
session_write_msg_cb(SockInfo
*source
, GIOCondition condition
,
807 Session
*session
= SESSION(data
);
810 cm_return_val_if_fail(condition
== G_IO_OUT
, FALSE
);
811 cm_return_val_if_fail(session
->write_buf
!= NULL
, FALSE
);
812 cm_return_val_if_fail(session
->write_buf_p
!= NULL
, FALSE
);
813 cm_return_val_if_fail(session
->write_buf_len
> 0, FALSE
);
815 ret
= session_write_buf(session
);
818 session
->state
= SESSION_ERROR
;
823 if (session
->io_tag
> 0) {
824 g_source_remove(session
->io_tag
);
828 session_recv_msg(session
);
833 static gboolean
session_write_data_cb(SockInfo
*source
,
834 GIOCondition condition
, gpointer data
)
836 Session
*session
= SESSION(data
);
837 guint write_data_len
;
840 cm_return_val_if_fail(condition
== G_IO_OUT
, FALSE
);
841 cm_return_val_if_fail(session
->write_data
!= NULL
, FALSE
);
842 cm_return_val_if_fail(session
->write_data_p
!= NULL
, FALSE
);
843 cm_return_val_if_fail(session
->write_data_len
> 0, FALSE
);
845 write_data_len
= session
->write_data_len
;
847 ret
= session_write_data(session
);
850 session
->state
= SESSION_ERROR
;
852 } else if (ret
> 0) {
855 g_get_current_time(&tv_cur
);
856 if (tv_cur
.tv_sec
- session
->tv_prev
.tv_sec
> 0 ||
857 tv_cur
.tv_usec
- session
->tv_prev
.tv_usec
>
858 UI_REFRESH_INTERVAL
) {
859 session_set_timeout(session
, session
->timeout_interval
);
860 session
->send_data_progressive_notify
862 session
->write_data_p
- session
->write_data
,
864 session
->send_data_progressive_notify_data
);
865 g_get_current_time(&session
->tv_prev
);
870 if (session
->io_tag
> 0) {
871 g_source_remove(session
->io_tag
);
876 ret
= session
->send_data_finished(session
, write_data_len
);
877 session
->send_data_notify(session
, write_data_len
,
878 session
->send_data_notify_data
);
883 void session_register_ping(Session
*session
, gboolean (*ping_cb
)(gpointer data
))
887 if (session
->ping_tag
> -1)
888 g_source_remove(session
->ping_tag
);
890 session
->ping_tag
= -1;
893 session
->ping_tag
= g_timeout_add_seconds(60, ping_cb
, session
);