0e4a62d34aec731c088bdd7307742b91a3fba6d5
[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/TLS.");
183                         log_error(LOG_PROTOCOL, _("SSL/TLS 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.");
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 STARTTLS session.");
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, 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
416         session->state = SESSION_SEND;
417         session->write_buf = g_strconcat((strlen(msg) > 0 ? msg : ""), "\r\n", NULL);
418         session->write_buf_p = session->write_buf;
419         session->write_buf_len = strlen(msg) + 2;
420
421         ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
422
423         if (ret == TRUE)
424                 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
425                                                  session_write_msg_cb, session);
426         else if (session->state == SESSION_ERROR)
427                 return -1;
428
429         return 0;
430 }
431
432 gint session_recv_msg(Session *session)
433 {
434         cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
435
436         session->state = SESSION_RECV;
437
438         if (session->read_buf_len > 0)
439                 g_idle_add(session_recv_msg_idle_cb, session);
440         else
441                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
442                                                  session_read_msg_cb, session);
443
444         return 0;
445 }
446
447 static gboolean session_recv_msg_idle_cb(gpointer data)
448 {
449         Session *session = SESSION(data);
450         gboolean ret;
451
452         ret = session_read_msg_cb(session->sock, G_IO_IN, session);
453
454         if (ret == TRUE)
455                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
456                                                  session_read_msg_cb, session);
457
458         return FALSE;
459 }
460
461 /*!
462  *\brief        parent (child?): send data to other process
463  *
464  *\param        session Contains session information
465  *              data Data to send
466  *              size Bytes to send
467  *
468  *\return        0 : success
469  *              -1 : error
470  */
471 gint session_send_data(Session *session, const guchar *data, guint size)
472 {
473         gboolean ret;
474
475         cm_return_val_if_fail(session->write_data == NULL, -1);
476         cm_return_val_if_fail(data != NULL, -1);
477         cm_return_val_if_fail(size != 0, -1);
478
479         session->state = SESSION_SEND;
480
481         session->write_data = data;
482         session->write_data_p = session->write_data;
483         session->write_data_len = size;
484         g_get_current_time(&session->tv_prev);
485
486         ret = session_write_data_cb(session->sock, G_IO_OUT, session);
487
488         if (ret == TRUE)
489                 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
490                                                  session_write_data_cb,
491                                                  session);
492         else if (session->state == SESSION_ERROR)
493                 return -1;
494
495         return 0;
496 }
497
498 gint session_recv_data(Session *session, guint size, const gchar *terminator)
499 {
500         cm_return_val_if_fail(session->read_data_buf->len == 0, -1);
501
502         session->state = SESSION_RECV;
503
504         g_free(session->read_data_terminator);
505         session->read_data_terminator = g_strdup(terminator);
506         g_get_current_time(&session->tv_prev);
507
508         if (session->read_buf_len > 0)
509                 g_idle_add(session_recv_data_idle_cb, session);
510         else
511                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
512                                                  session_read_data_cb, session);
513
514         return 0;
515 }
516
517 static gboolean session_recv_data_idle_cb(gpointer data)
518 {
519         Session *session = SESSION(data);
520         gboolean ret;
521
522         ret = session_read_data_cb(session->sock, G_IO_IN, session);
523
524         if (ret == TRUE)
525                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
526                                                  session_read_data_cb, session);
527
528         return FALSE;
529 }
530
531 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
532                                     gpointer data)
533 {
534         Session *session = SESSION(data);
535         gchar buf[SESSION_BUFFSIZE];
536         gint line_len;
537         gchar *newline;
538         gchar *msg;
539         gint ret;
540
541         cm_return_val_if_fail(condition == G_IO_IN, FALSE);
542
543         session_set_timeout(session, session->timeout_interval);
544
545         if (session->read_buf_len == 0) {
546                 gint read_len = -1;
547
548                 if (session->sock)
549                         read_len = sock_read(session->sock, session->read_buf,
550                                      SESSION_BUFFSIZE - 1);
551
552                 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
553                         g_warning ("sock_read: session disconnected");
554                         if (session->io_tag > 0) {
555                                 g_source_remove(session->io_tag);
556                                 session->io_tag = 0;
557                         }
558                         return FALSE;
559                 }
560                 
561                 if (read_len == 0) {
562                         g_warning("sock_read: received EOF");
563                         session->state = SESSION_EOF;
564                         return FALSE;
565                 }
566
567                 if (read_len < 0) {
568                         switch (errno) {
569                         case EAGAIN:
570                                 return TRUE;
571                         default:
572                                 g_warning("sock_read: %s", g_strerror(errno));
573                                 session->state = SESSION_ERROR;
574                                 return FALSE;
575                         }
576                 }
577
578                 session->read_buf_len = read_len;
579         }
580
581         if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
582                 != NULL)
583                 line_len = newline - session->read_buf_p + 1;
584         else
585                 line_len = session->read_buf_len;
586
587         if (line_len == 0)
588                 return TRUE;
589
590         memcpy(buf, session->read_buf_p, line_len);
591         buf[line_len] = '\0';
592
593         g_string_append(session->read_msg_buf, buf);
594
595         session->read_buf_len -= line_len;
596         if (session->read_buf_len == 0)
597                 session->read_buf_p = session->read_buf;
598         else
599                 session->read_buf_p += line_len;
600
601         /* incomplete read */
602         if (buf[line_len - 1] != '\n')
603                 return TRUE;
604
605         /* complete */
606         if (session->io_tag > 0) {
607                 g_source_remove(session->io_tag);
608                 session->io_tag = 0;
609         }
610
611         /* callback */
612         msg = g_strdup(session->read_msg_buf->str);
613         strretchomp(msg);
614         g_string_truncate(session->read_msg_buf, 0);
615
616         ret = session->recv_msg(session, msg);
617         session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
618
619         g_free(msg);
620
621         if (ret < 0)
622                 session->state = SESSION_ERROR;
623
624         return FALSE;
625 }
626
627 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
628                                      gpointer data)
629 {
630         Session *session = SESSION(data);
631         GByteArray *data_buf;
632         gint terminator_len;
633         gboolean complete = FALSE;
634         guint data_len;
635         gint ret;
636
637         cm_return_val_if_fail(condition == G_IO_IN, FALSE);
638
639         session_set_timeout(session, session->timeout_interval);
640
641         if (session->read_buf_len == 0) {
642                 gint read_len;
643
644                 read_len = sock_read(session->sock, session->read_buf,
645                                      SESSION_BUFFSIZE);
646
647                 if (read_len == 0) {
648                         g_warning("sock_read: received EOF");
649                         session->state = SESSION_EOF;
650                         return FALSE;
651                 }
652
653                 if (read_len < 0) {
654                         switch (errno) {
655                         case EAGAIN:
656                                 return TRUE;
657                         default:
658                                 g_warning("sock_read: %s", g_strerror(errno));
659                                 session->state = SESSION_ERROR;
660                                 return FALSE;
661                         }
662                 }
663
664                 session->read_buf_len = read_len;
665         }
666
667         data_buf = session->read_data_buf;
668         terminator_len = strlen(session->read_data_terminator);
669
670         if (session->read_buf_len == 0)
671                 return TRUE;
672
673         g_byte_array_append(data_buf, session->read_buf_p,
674                             session->read_buf_len);
675
676         session->read_buf_len = 0;
677         session->read_buf_p = session->read_buf;
678
679         /* check if data is terminated */
680         if (data_buf->len >= terminator_len) {
681                 if (memcmp(data_buf->data, session->read_data_terminator,
682                            terminator_len) == 0)
683                         complete = TRUE;
684                 else if (data_buf->len >= terminator_len + 2 &&
685                          memcmp(data_buf->data + data_buf->len -
686                                 (terminator_len + 2), "\r\n", 2) == 0 &&
687                          memcmp(data_buf->data + data_buf->len -
688                                 terminator_len, session->read_data_terminator,
689                                 terminator_len) == 0)
690                         complete = TRUE;
691         }
692
693         /* incomplete read */
694         if (!complete) {
695                 GTimeVal tv_cur;
696
697                 g_get_current_time(&tv_cur);
698                 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
699                     tv_cur.tv_usec - session->tv_prev.tv_usec >
700                     UI_REFRESH_INTERVAL) {
701                         session->recv_data_progressive_notify
702                                 (session, data_buf->len, 0,
703                                  session->recv_data_progressive_notify_data);
704                         g_get_current_time(&session->tv_prev);
705                 }
706                 return TRUE;
707         }
708
709         /* complete */
710         if (session->io_tag > 0) {
711                 g_source_remove(session->io_tag);
712                 session->io_tag = 0;
713         }
714
715         data_len = data_buf->len - terminator_len;
716
717         /* callback */
718         ret = session->recv_data_finished(session, (gchar *)data_buf->data,
719                                           data_len);
720
721         g_byte_array_set_size(data_buf, 0);
722
723         session->recv_data_notify(session, data_len,
724                                   session->recv_data_notify_data);
725
726         if (ret < 0)
727                 session->state = SESSION_ERROR;
728
729         return FALSE;
730 }
731
732 static gint session_write_buf(Session *session)
733 {
734         gint write_len;
735         gint to_write_len;
736
737         cm_return_val_if_fail(session->write_buf != NULL, -1);
738         cm_return_val_if_fail(session->write_buf_p != NULL, -1);
739         cm_return_val_if_fail(session->write_buf_len > 0, -1);
740
741         to_write_len = session->write_buf_len -
742                 (session->write_buf_p - session->write_buf);
743         to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
744
745         write_len = sock_write(session->sock, session->write_buf_p,
746                                to_write_len);
747
748         if (write_len < 0) {
749                 switch (errno) {
750                 case EAGAIN:
751                         write_len = 0;
752                         break;
753                 default:
754                         g_warning("sock_write: %s", g_strerror(errno));
755                         session->state = SESSION_ERROR;
756                         return -1;
757                 }
758         }
759
760         /* incomplete write */
761         if (session->write_buf_p - session->write_buf + write_len <
762             session->write_buf_len) {
763                 session->write_buf_p += write_len;
764                 return 1;
765         }
766
767         g_free(session->write_buf);
768         session->write_buf = NULL;
769         session->write_buf_p = NULL;
770         session->write_buf_len = 0;
771
772         return 0;
773 }
774
775 static gint session_write_data(Session *session)
776 {
777         gint write_len;
778         gint to_write_len;
779
780         cm_return_val_if_fail(session->write_data != NULL, -1);
781         cm_return_val_if_fail(session->write_data_p != NULL, -1);
782         cm_return_val_if_fail(session->write_data_len > 0, -1);
783
784         to_write_len = session->write_data_len -
785                 (session->write_data_p - session->write_data);
786         to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
787
788         write_len = sock_write(session->sock, session->write_data_p,
789                                to_write_len);
790
791         if (write_len < 0) {
792                 switch (errno) {
793                 case EAGAIN:
794                         write_len = 0;
795                         break;
796                 default:
797                         g_warning("sock_write: %s", g_strerror(errno));
798                         session->state = SESSION_ERROR;
799                         return -1;
800                 }
801         }
802
803         /* incomplete write */
804         if (session->write_data_p - session->write_data + write_len <
805             session->write_data_len) {
806                 session->write_data_p += write_len;
807                 return 1;
808         }
809
810         session->write_data = NULL;
811         session->write_data_p = NULL;
812         session->write_data_len = 0;
813
814         return 0;
815 }
816
817 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
818                                      gpointer data)
819 {
820         Session *session = SESSION(data);
821         gint ret;
822
823         cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
824         cm_return_val_if_fail(session->write_buf != NULL, FALSE);
825         cm_return_val_if_fail(session->write_buf_p != NULL, FALSE);
826         cm_return_val_if_fail(session->write_buf_len > 0, FALSE);
827
828         ret = session_write_buf(session);
829
830         if (ret < 0) {
831                 session->state = SESSION_ERROR;
832                 return FALSE;
833         } else if (ret > 0)
834                 return TRUE;
835
836         if (session->io_tag > 0) {
837                 g_source_remove(session->io_tag);
838                 session->io_tag = 0;
839         }
840
841         session_recv_msg(session);
842
843         return FALSE;
844 }
845
846 static gboolean session_write_data_cb(SockInfo *source,
847                                       GIOCondition condition, gpointer data)
848 {
849         Session *session = SESSION(data);
850         guint write_data_len;
851         gint ret;
852
853         cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
854         cm_return_val_if_fail(session->write_data != NULL, FALSE);
855         cm_return_val_if_fail(session->write_data_p != NULL, FALSE);
856         cm_return_val_if_fail(session->write_data_len > 0, FALSE);
857
858         write_data_len = session->write_data_len;
859
860         ret = session_write_data(session);
861
862         if (ret < 0) {
863                 session->state = SESSION_ERROR;
864                 return FALSE;
865         } else if (ret > 0) {
866                 GTimeVal tv_cur;
867
868                 g_get_current_time(&tv_cur);
869                 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
870                     tv_cur.tv_usec - session->tv_prev.tv_usec >
871                     UI_REFRESH_INTERVAL) {
872                         session_set_timeout(session, session->timeout_interval);
873                         session->send_data_progressive_notify
874                                 (session,
875                                  session->write_data_p - session->write_data,
876                                  write_data_len,
877                                  session->send_data_progressive_notify_data);
878                         g_get_current_time(&session->tv_prev);
879                 }
880                 return TRUE;
881         }
882
883         if (session->io_tag > 0) {
884                 g_source_remove(session->io_tag);
885                 session->io_tag = 0;
886         }
887
888         /* callback */
889         ret = session->send_data_finished(session, write_data_len);
890         session->send_data_notify(session, write_data_len,
891                                   session->send_data_notify_data);
892
893         return FALSE;
894 }
895
896 void session_register_ping(Session *session, gboolean (*ping_cb)(gpointer data))
897 {
898         if (!session)
899                 return;
900         if (session->ping_tag > -1)
901                 g_source_remove(session->ping_tag);
902
903         session->ping_tag = -1;
904
905         if (ping_cb != NULL)
906                 session->ping_tag = g_timeout_add_seconds(60, ping_cb, session);
907 }