Add callback for session connection result
[claws.git] / src / common / session.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
4  *
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.
9  *
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.
14  *
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/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <time.h>
35 #include <errno.h>
36
37 #include "session.h"
38 #include "utils.h"
39 #include "log.h"
40
41 static gint session_connect_cb          (SockInfo       *sock,
42                                          gpointer        data);
43 static gint session_close               (Session        *session);
44
45 static gboolean session_timeout_cb      (gpointer        data);
46
47 static gboolean session_recv_msg_idle_cb        (gpointer        data);
48 static gboolean session_recv_data_idle_cb       (gpointer        data);
49
50 static gboolean session_read_msg_cb     (SockInfo       *source,
51                                          GIOCondition    condition,
52                                          gpointer        data);
53 static gboolean session_read_data_cb    (SockInfo       *source,
54                                          GIOCondition    condition,
55                                          gpointer        data);
56 static gboolean session_write_msg_cb    (SockInfo       *source,
57                                          GIOCondition    condition,
58                                          gpointer        data);
59 static gboolean session_write_data_cb   (SockInfo       *source,
60                                          GIOCondition    condition,
61                                          gpointer        data);
62
63
64 void session_init(Session *session, const void *prefs_account, gboolean is_smtp)
65 {
66         session->type = SESSION_UNKNOWN;
67         session->sock = NULL;
68         session->server = NULL;
69         session->port = 0;
70 #ifdef USE_GNUTLS
71         session->ssl_type = SSL_NONE;
72 #endif
73         session->nonblocking = TRUE;
74         session->state = SESSION_READY;
75         session->last_access_time = time(NULL);
76
77         g_get_current_time(&session->tv_prev);
78
79         session->conn_id = 0;
80
81         session->io_tag = 0;
82
83         session->read_buf_p = session->read_buf;
84         session->read_buf_len = 0;
85
86         session->read_msg_buf = g_string_sized_new(1024);
87         session->read_data_buf = g_byte_array_new();
88
89         session->write_buf = NULL;
90         session->write_buf_p = NULL;
91         session->write_buf_len = 0;
92
93         session->write_data = NULL;
94         session->write_data_p = NULL;
95         session->write_data_len = 0;
96
97         session->timeout_tag = 0;
98         session->timeout_interval = 0;
99
100         session->data = NULL;
101         session->account = prefs_account;
102         session->is_smtp = is_smtp;
103
104         session->ping_tag = -1;
105 }
106
107 /*!
108  *\brief        Set up parent and child process
109  *              Childloop: Read commands from parent,
110  *              send to server, get answer, pass to parent
111  *
112  *\param        session Contains session information
113  *              server to connect to
114  *              port to connect to
115  *
116  *\return        0 : success
117  *              -1 : pipe / fork errors (parent)
118  *               1 : connection error (child)
119  */
120 gint session_connect(Session *session, const gchar *server, gushort port)
121 {
122 #ifdef G_OS_UNIX
123         session->server = g_strdup(server);
124         session->port = port;
125
126         session->conn_id = sock_connect_async(server, port, session_connect_cb,
127                                               session);
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);
133                 return -1;
134         }
135
136         return 0;
137 #else
138         SockInfo *sock;
139
140         session->server = g_strdup(server);
141         session->port = port;
142
143         sock = sock_connect(server, port);
144         if (sock == NULL) {
145                 g_warning("can't connect to server.");
146                 session_close(session);
147                 if (session->connect_finished)
148                         session->connect_finished(session, FALSE);
149                 return -1;
150         }
151         sock->is_smtp = session->is_smtp;
152
153         return session_connect_cb(sock, session);
154 #endif
155 }
156
157 static gint session_connect_cb(SockInfo *sock, gpointer data)
158 {
159         Session *session = SESSION(data);
160
161         session->conn_id = 0;
162
163         if (!sock) {
164                 g_warning("can't connect to server.");
165                 session->state = SESSION_ERROR;
166                 if (session->connect_finished)
167                         session->connect_finished(session, FALSE);
168                 return -1;
169         }
170
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;
175
176 #ifdef USE_GNUTLS
177         sock->gnutls_priority = session->gnutls_priority;
178
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);
187                         return -1;
188                 }
189         }
190 #endif
191
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);
197                 return -1;
198         }
199
200         sock_set_nonblocking_mode(sock, session->nonblocking);
201
202         debug_print("session (%p): connected\n", session);
203
204         session->state = SESSION_RECV;
205         session->io_tag = sock_add_watch(session->sock, G_IO_IN,
206                                          session_read_msg_cb,
207                                          session);
208
209         if (session->connect_finished)
210                 session->connect_finished(session, TRUE);
211         return 0;
212 }
213
214 /*!
215  *\brief        child and parent: send DISCONNECT message to other process
216  *
217  *\param        session Contains session information
218  *
219  *\return        0 : success
220  */
221 gint session_disconnect(Session *session)
222 {
223         session_close(session);
224         return 0;
225 }
226
227 /*!
228  *\brief        parent ?
229  *
230  *\param        session Contains session information
231  */
232 void session_destroy(Session *session)
233 {
234         cm_return_if_fail(session != NULL);
235         cm_return_if_fail(session->destroy != NULL);
236
237         session_register_ping(session, NULL);
238
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);
246 #ifdef USE_GNUTLS
247         g_free(session->gnutls_priority);
248 #endif
249
250         debug_print("session (%p): destroyed\n", session);
251
252         g_free(session);
253 }
254
255 gboolean session_is_running(Session *session)
256 {
257         return (session->state == SESSION_READY ||
258                 session->state == SESSION_SEND ||
259                 session->state == SESSION_RECV);
260 }
261
262 gboolean session_is_connected(Session *session)
263 {
264         return (session->state == SESSION_SEND ||
265                 session->state == SESSION_RECV);
266 }
267
268 void session_set_access_time(Session *session)
269 {
270         session->last_access_time = time(NULL);
271 }
272
273 void session_set_timeout(Session *session, guint interval)
274 {
275         if (session->timeout_tag > 0)
276                 g_source_remove(session->timeout_tag);
277
278         session->timeout_interval = interval;
279         if (interval > 0) {
280                 if (interval % 1000 == 0)
281                         session->timeout_tag =
282                                 g_timeout_add_seconds(interval/1000, session_timeout_cb, session);
283                 else
284                         session->timeout_tag =
285                                 g_timeout_add(interval, session_timeout_cb, session);
286         } else
287                 session->timeout_tag = 0;
288 }
289
290 static gboolean session_timeout_cb(gpointer data)
291 {
292         Session *session = SESSION(data);
293
294         g_warning("session timeout.\n");
295
296         if (session->io_tag > 0) {
297                 g_source_remove(session->io_tag);
298                 session->io_tag = 0;
299         }
300
301         session->timeout_tag = 0;
302         session->state = SESSION_TIMEOUT;
303
304         return FALSE;
305 }
306
307 void session_set_recv_message_notify(Session *session,
308                                      RecvMsgNotify notify_func, gpointer data)
309 {
310         session->recv_msg_notify = notify_func;
311         session->recv_msg_notify_data = data;
312 }
313
314 void session_set_recv_data_progressive_notify
315                                         (Session *session,
316                                          RecvDataProgressiveNotify notify_func,
317                                          gpointer data)
318 {
319         session->recv_data_progressive_notify = notify_func,
320         session->recv_data_progressive_notify_data = data;
321 }
322
323 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
324                                   gpointer data)
325 {
326         session->recv_data_notify = notify_func;
327         session->recv_data_notify_data = data;
328 }
329
330 void session_set_send_data_progressive_notify
331                                         (Session *session,
332                                          SendDataProgressiveNotify notify_func,
333                                          gpointer data)
334 {
335         session->send_data_progressive_notify = notify_func;
336         session->send_data_progressive_notify_data = data;
337 }
338
339 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
340                                   gpointer data)
341 {
342         session->send_data_notify = notify_func;
343         session->send_data_notify_data = data;
344 }
345
346 /*!
347  *\brief        child and parent cleanup (child closes first)
348  *
349  *\param        session Contains session information
350  *
351  *\return        0 : success
352  */
353 static gint session_close(Session *session)
354 {
355         cm_return_val_if_fail(session != NULL, -1);
356
357 #ifdef G_OS_UNIX
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);
362         }
363 #endif
364
365         session_set_timeout(session, 0);
366
367         if (session->io_tag > 0) {
368                 g_source_remove(session->io_tag);
369                 session->io_tag = 0;
370         }
371
372         if (session->sock) {
373                 sock_close(session->sock);
374                 session->sock = NULL;
375                 session->state = SESSION_DISCONNECTED;
376                 debug_print("session (%p): closed\n", session);
377         }
378
379         return 0;
380 }
381
382 #ifdef USE_GNUTLS
383 gint session_start_tls(Session *session)
384 {
385         gboolean nb_mode;
386
387         nb_mode = sock_is_nonblocking_mode(session->sock);
388
389         session->sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
390         session->sock->gnutls_priority = session->gnutls_priority;
391
392         if (nb_mode)
393                 sock_set_nonblocking_mode(session->sock, FALSE);
394
395         if (!ssl_init_socket(session->sock)) {
396                 g_warning("couldn't start TLS session.\n");
397                 if (nb_mode)
398                         sock_set_nonblocking_mode(session->sock, session->nonblocking);
399                 return -1;
400         }
401
402         if (nb_mode)
403                 sock_set_nonblocking_mode(session->sock, session->nonblocking);
404
405         return 0;
406 }
407 #endif
408
409 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
410 {
411         gboolean ret;
412
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);
416
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;
421
422         ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
423
424         if (ret == TRUE)
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)
428                 return -1;
429
430         return 0;
431 }
432
433 gint session_recv_msg(Session *session)
434 {
435         cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
436
437         session->state = SESSION_RECV;
438
439         if (session->read_buf_len > 0)
440                 g_idle_add(session_recv_msg_idle_cb, session);
441         else
442                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
443                                                  session_read_msg_cb, session);
444
445         return 0;
446 }
447
448 static gboolean session_recv_msg_idle_cb(gpointer data)
449 {
450         Session *session = SESSION(data);
451         gboolean ret;
452
453         ret = session_read_msg_cb(session->sock, G_IO_IN, session);
454
455         if (ret == TRUE)
456                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
457                                                  session_read_msg_cb, session);
458
459         return FALSE;
460 }
461
462 /*!
463  *\brief        parent (child?): send data to other process
464  *
465  *\param        session Contains session information
466  *              data Data to send
467  *              size Bytes to send
468  *
469  *\return        0 : success
470  *              -1 : error
471  */
472 gint session_send_data(Session *session, const guchar *data, guint size)
473 {
474         gboolean ret;
475
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);
479
480         session->state = SESSION_SEND;
481
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);
486
487         ret = session_write_data_cb(session->sock, G_IO_OUT, session);
488
489         if (ret == TRUE)
490                 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
491                                                  session_write_data_cb,
492                                                  session);
493         else if (session->state == SESSION_ERROR)
494                 return -1;
495
496         return 0;
497 }
498
499 gint session_recv_data(Session *session, guint size, const gchar *terminator)
500 {
501         cm_return_val_if_fail(session->read_data_buf->len == 0, -1);
502
503         session->state = SESSION_RECV;
504
505         g_free(session->read_data_terminator);
506         session->read_data_terminator = g_strdup(terminator);
507         g_get_current_time(&session->tv_prev);
508
509         if (session->read_buf_len > 0)
510                 g_idle_add(session_recv_data_idle_cb, session);
511         else
512                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
513                                                  session_read_data_cb, session);
514
515         return 0;
516 }
517
518 static gboolean session_recv_data_idle_cb(gpointer data)
519 {
520         Session *session = SESSION(data);
521         gboolean ret;
522
523         ret = session_read_data_cb(session->sock, G_IO_IN, session);
524
525         if (ret == TRUE)
526                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
527                                                  session_read_data_cb, session);
528
529         return FALSE;
530 }
531
532 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
533                                     gpointer data)
534 {
535         Session *session = SESSION(data);
536         gchar buf[SESSION_BUFFSIZE];
537         gint line_len;
538         gchar *newline;
539         gchar *msg;
540         gint ret;
541
542         cm_return_val_if_fail(condition == G_IO_IN, FALSE);
543
544         session_set_timeout(session, session->timeout_interval);
545
546         if (session->read_buf_len == 0) {
547                 gint read_len = -1;
548
549                 if (session->sock)
550                         read_len = sock_read(session->sock, session->read_buf,
551                                      SESSION_BUFFSIZE - 1);
552
553                 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
554                         g_warning ("sock_read: session disconnected\n");
555                         if (session->io_tag > 0) {
556                                 g_source_remove(session->io_tag);
557                                 session->io_tag = 0;
558                         }
559                         return FALSE;
560                 }
561                 
562                 if (read_len == 0) {
563                         g_warning("sock_read: received EOF\n");
564                         session->state = SESSION_EOF;
565                         return FALSE;
566                 }
567
568                 if (read_len < 0) {
569                         switch (errno) {
570                         case EAGAIN:
571                                 return TRUE;
572                         default:
573                                 g_warning("sock_read: %s\n", g_strerror(errno));
574                                 session->state = SESSION_ERROR;
575                                 return FALSE;
576                         }
577                 }
578
579                 session->read_buf_len = read_len;
580         }
581
582         if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
583                 != NULL)
584                 line_len = newline - session->read_buf_p + 1;
585         else
586                 line_len = session->read_buf_len;
587
588         if (line_len == 0)
589                 return TRUE;
590
591         memcpy(buf, session->read_buf_p, line_len);
592         buf[line_len] = '\0';
593
594         g_string_append(session->read_msg_buf, buf);
595
596         session->read_buf_len -= line_len;
597         if (session->read_buf_len == 0)
598                 session->read_buf_p = session->read_buf;
599         else
600                 session->read_buf_p += line_len;
601
602         /* incomplete read */
603         if (buf[line_len - 1] != '\n')
604                 return TRUE;
605
606         /* complete */
607         if (session->io_tag > 0) {
608                 g_source_remove(session->io_tag);
609                 session->io_tag = 0;
610         }
611
612         /* callback */
613         msg = g_strdup(session->read_msg_buf->str);
614         strretchomp(msg);
615         g_string_truncate(session->read_msg_buf, 0);
616
617         ret = session->recv_msg(session, msg);
618         session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
619
620         g_free(msg);
621
622         if (ret < 0)
623                 session->state = SESSION_ERROR;
624
625         return FALSE;
626 }
627
628 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
629                                      gpointer data)
630 {
631         Session *session = SESSION(data);
632         GByteArray *data_buf;
633         gint terminator_len;
634         gboolean complete = FALSE;
635         guint data_len;
636         gint ret;
637
638         cm_return_val_if_fail(condition == G_IO_IN, FALSE);
639
640         session_set_timeout(session, session->timeout_interval);
641
642         if (session->read_buf_len == 0) {
643                 gint read_len;
644
645                 read_len = sock_read(session->sock, session->read_buf,
646                                      SESSION_BUFFSIZE);
647
648                 if (read_len == 0) {
649                         g_warning("sock_read: received EOF\n");
650                         session->state = SESSION_EOF;
651                         return FALSE;
652                 }
653
654                 if (read_len < 0) {
655                         switch (errno) {
656                         case EAGAIN:
657                                 return TRUE;
658                         default:
659                                 g_warning("sock_read: %s\n", g_strerror(errno));
660                                 session->state = SESSION_ERROR;
661                                 return FALSE;
662                         }
663                 }
664
665                 session->read_buf_len = read_len;
666         }
667
668         data_buf = session->read_data_buf;
669         terminator_len = strlen(session->read_data_terminator);
670
671         if (session->read_buf_len == 0)
672                 return TRUE;
673
674         g_byte_array_append(data_buf, session->read_buf_p,
675                             session->read_buf_len);
676
677         session->read_buf_len = 0;
678         session->read_buf_p = session->read_buf;
679
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)
684                         complete = TRUE;
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)
691                         complete = TRUE;
692         }
693
694         /* incomplete read */
695         if (!complete) {
696                 GTimeVal tv_cur;
697
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);
706                 }
707                 return TRUE;
708         }
709
710         /* complete */
711         if (session->io_tag > 0) {
712                 g_source_remove(session->io_tag);
713                 session->io_tag = 0;
714         }
715
716         data_len = data_buf->len - terminator_len;
717
718         /* callback */
719         ret = session->recv_data_finished(session, (gchar *)data_buf->data,
720                                           data_len);
721
722         g_byte_array_set_size(data_buf, 0);
723
724         session->recv_data_notify(session, data_len,
725                                   session->recv_data_notify_data);
726
727         if (ret < 0)
728                 session->state = SESSION_ERROR;
729
730         return FALSE;
731 }
732
733 static gint session_write_buf(Session *session)
734 {
735         gint write_len;
736         gint to_write_len;
737
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);
741
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);
745
746         write_len = sock_write(session->sock, session->write_buf_p,
747                                to_write_len);
748
749         if (write_len < 0) {
750                 switch (errno) {
751                 case EAGAIN:
752                         write_len = 0;
753                         break;
754                 default:
755                         g_warning("sock_write: %s\n", g_strerror(errno));
756                         session->state = SESSION_ERROR;
757                         return -1;
758                 }
759         }
760
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;
765                 return 1;
766         }
767
768         g_free(session->write_buf);
769         session->write_buf = NULL;
770         session->write_buf_p = NULL;
771         session->write_buf_len = 0;
772
773         return 0;
774 }
775
776 static gint session_write_data(Session *session)
777 {
778         gint write_len;
779         gint to_write_len;
780
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);
784
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);
788
789         write_len = sock_write(session->sock, session->write_data_p,
790                                to_write_len);
791
792         if (write_len < 0) {
793                 switch (errno) {
794                 case EAGAIN:
795                         write_len = 0;
796                         break;
797                 default:
798                         g_warning("sock_write: %s\n", g_strerror(errno));
799                         session->state = SESSION_ERROR;
800                         return -1;
801                 }
802         }
803
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;
808                 return 1;
809         }
810
811         session->write_data = NULL;
812         session->write_data_p = NULL;
813         session->write_data_len = 0;
814
815         return 0;
816 }
817
818 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
819                                      gpointer data)
820 {
821         Session *session = SESSION(data);
822         gint ret;
823
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);
828
829         ret = session_write_buf(session);
830
831         if (ret < 0) {
832                 session->state = SESSION_ERROR;
833                 return FALSE;
834         } else if (ret > 0)
835                 return TRUE;
836
837         if (session->io_tag > 0) {
838                 g_source_remove(session->io_tag);
839                 session->io_tag = 0;
840         }
841
842         session_recv_msg(session);
843
844         return FALSE;
845 }
846
847 static gboolean session_write_data_cb(SockInfo *source,
848                                       GIOCondition condition, gpointer data)
849 {
850         Session *session = SESSION(data);
851         guint write_data_len;
852         gint ret;
853
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);
858
859         write_data_len = session->write_data_len;
860
861         ret = session_write_data(session);
862
863         if (ret < 0) {
864                 session->state = SESSION_ERROR;
865                 return FALSE;
866         } else if (ret > 0) {
867                 GTimeVal tv_cur;
868
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
875                                 (session,
876                                  session->write_data_p - session->write_data,
877                                  write_data_len,
878                                  session->send_data_progressive_notify_data);
879                         g_get_current_time(&session->tv_prev);
880                 }
881                 return TRUE;
882         }
883
884         if (session->io_tag > 0) {
885                 g_source_remove(session->io_tag);
886                 session->io_tag = 0;
887         }
888
889         /* callback */
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);
893
894         return FALSE;
895 }
896
897 void session_register_ping(Session *session, gboolean (*ping_cb)(gpointer data))
898 {
899         if (!session)
900                 return;
901         if (session->ping_tag > -1)
902                 g_source_remove(session->ping_tag);
903
904         session->ping_tag = -1;
905
906         if (ping_cb != NULL)
907                 session->ping_tag = g_timeout_add_seconds(60, ping_cb, session);
908 }