c7a42f0843b35be29ec4dbe550dca064c04e5bf4
[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         session->use_tls_sni = TRUE;
73 #endif
74         session->nonblocking = TRUE;
75         session->state = SESSION_READY;
76         session->last_access_time = time(NULL);
77
78 #if GLIB_CHECK_VERSION(2,61,2)
79         session->tv_prev = g_date_time_new_now_local();
80 #else
81         g_get_current_time(&session->tv_prev);
82 #endif
83
84         session->conn_id = 0;
85
86         session->io_tag = 0;
87
88         session->read_buf_p = session->read_buf;
89         session->read_buf_len = 0;
90
91         session->read_msg_buf = g_string_sized_new(1024);
92         session->read_data_buf = g_byte_array_new();
93
94         session->write_buf = NULL;
95         session->write_buf_p = NULL;
96         session->write_buf_len = 0;
97
98         session->write_data = NULL;
99         session->write_data_p = NULL;
100         session->write_data_len = 0;
101
102         session->timeout_tag = 0;
103         session->timeout_interval = 0;
104
105         session->data = NULL;
106         session->account = prefs_account;
107         session->is_smtp = is_smtp;
108
109         session->ping_tag = -1;
110
111         session->proxy_info = NULL;
112 }
113
114 /*!
115  *\brief        Set up parent and child process
116  *              Childloop: Read commands from parent,
117  *              send to server, get answer, pass to parent
118  *
119  *\param        session Contains session information
120  *              server to connect to
121  *              port to connect to
122  *
123  *\return        0 : success
124  *              -1 : pipe / fork errors (parent)
125  *               1 : connection error (child)
126  */
127 gint session_connect(Session *session, const gchar *server, gushort port)
128 {
129         session->server = g_strdup(server);
130         session->port = port;
131
132         if (session->proxy_info) {
133                 server = session->proxy_info->proxy_host;
134                 port = session->proxy_info->proxy_port;
135         }
136
137 #ifdef G_OS_UNIX
138         session->conn_id = sock_connect_async(server, port, session_connect_cb,
139                                               session);
140         if (session->conn_id < 0) {
141                 g_warning("can't connect to server.");
142                 session_close(session);
143                 if (session->connect_finished)
144                         session->connect_finished(session, FALSE);
145                 return -1;
146         }
147
148         return 0;
149 #else
150         SockInfo *sock;
151
152         sock = sock_connect(server, port);
153         if (sock == NULL) {
154                 g_warning("can't connect to server.");
155                 session_close(session);
156                 if (session->connect_finished)
157                         session->connect_finished(session, FALSE);
158                 return -1;
159         }
160         sock->is_smtp = session->is_smtp;
161
162         return session_connect_cb(sock, session);
163 #endif
164 }
165
166 static gint session_connect_cb(SockInfo *sock, gpointer data)
167 {
168         Session *session = SESSION(data);
169
170         session->conn_id = 0;
171
172         if (!sock) {
173                 g_warning("can't connect to server.");
174                 session->state = SESSION_ERROR;
175                 if (session->connect_finished)
176                         session->connect_finished(session, FALSE);
177                 return -1;
178         }
179
180         session->sock = sock;
181         sock->account = session->account;
182         sock->is_smtp = session->is_smtp;
183         sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
184
185         if (session->proxy_info) {
186                 debug_print("connecting through socks\n");
187                 sock_set_nonblocking_mode(sock, FALSE);
188                 if (proxy_connect(sock, session->server, session->port,
189                                         session->proxy_info) < 0) {
190                         g_warning("can't establish SOCKS connection.");
191                         session->state = SESSION_ERROR;
192                         return -1;
193                 }
194         }
195
196
197 #ifdef USE_GNUTLS
198         sock->gnutls_priority = session->gnutls_priority;
199         sock->use_tls_sni = session->use_tls_sni;
200
201         if (session->ssl_type == SSL_TUNNEL) {
202                 sock_set_nonblocking_mode(sock, FALSE);
203                 if (!ssl_init_socket(sock)) {
204                         g_warning("can't initialize SSL/TLS.");
205                         log_error(LOG_PROTOCOL, _("SSL/TLS handshake failed\n"));
206                         session->state = SESSION_ERROR;
207                         if (session->connect_finished)
208                                 session->connect_finished(session, FALSE);
209                         return -1;
210                 }
211         }
212 #endif
213
214         /* we could have gotten a timeout while waiting for user input in 
215          * an SSL certificate dialog */
216         if (session->state == SESSION_TIMEOUT) {
217                 if (session->connect_finished)
218                         session->connect_finished(session, FALSE);
219                 return -1;
220         }
221
222         sock_set_nonblocking_mode(sock, session->nonblocking);
223
224         debug_print("session (%p): connected\n", session);
225
226         session->state = SESSION_RECV;
227         session->io_tag = sock_add_watch(session->sock, G_IO_IN,
228                                          session_read_msg_cb,
229                                          session);
230
231         if (session->connect_finished)
232                 session->connect_finished(session, TRUE);
233         return 0;
234 }
235
236 /*!
237  *\brief        child and parent: send DISCONNECT message to other process
238  *
239  *\param        session Contains session information
240  *
241  *\return        0 : success
242  */
243 gint session_disconnect(Session *session)
244 {
245         session_close(session);
246         return 0;
247 }
248
249 /*!
250  *\brief        parent ?
251  *
252  *\param        session Contains session information
253  */
254 void session_destroy(Session *session)
255 {
256         cm_return_if_fail(session != NULL);
257         cm_return_if_fail(session->destroy != NULL);
258
259         session_register_ping(session, NULL);
260
261         session_close(session);
262         session->destroy(session);
263         g_free(session->server);
264         g_string_free(session->read_msg_buf, TRUE);
265         g_byte_array_free(session->read_data_buf, TRUE);
266         g_free(session->read_data_terminator);
267         g_free(session->write_buf);
268 #ifdef USE_GNUTLS
269         g_free(session->gnutls_priority);
270 #endif
271 #if GLIB_CHECK_VERSION(2,61,2)
272         g_date_time_unref(session->tv_prev);
273 #endif
274         debug_print("session (%p): destroyed\n", session);
275
276         g_free(session);
277 }
278
279 gboolean session_is_running(Session *session)
280 {
281         return (session->state == SESSION_READY ||
282                 session->state == SESSION_SEND ||
283                 session->state == SESSION_RECV);
284 }
285
286 gboolean session_is_connected(Session *session)
287 {
288         return (session->state == SESSION_SEND ||
289                 session->state == SESSION_RECV);
290 }
291
292 void session_set_access_time(Session *session)
293 {
294         session->last_access_time = time(NULL);
295 }
296
297 void session_set_timeout(Session *session, guint interval)
298 {
299         if (session->timeout_tag > 0)
300                 g_source_remove(session->timeout_tag);
301
302         session->timeout_interval = interval;
303         if (interval > 0) {
304                 if (interval % 1000 == 0)
305                         session->timeout_tag =
306                                 g_timeout_add_seconds(interval/1000, session_timeout_cb, session);
307                 else
308                         session->timeout_tag =
309                                 g_timeout_add(interval, session_timeout_cb, session);
310         } else
311                 session->timeout_tag = 0;
312 }
313
314 static gboolean session_timeout_cb(gpointer data)
315 {
316         Session *session = SESSION(data);
317
318         g_warning("session timeout.");
319
320         if (session->io_tag > 0) {
321                 g_source_remove(session->io_tag);
322                 session->io_tag = 0;
323         }
324
325         session->timeout_tag = 0;
326         session->state = SESSION_TIMEOUT;
327
328         return FALSE;
329 }
330
331 void session_set_recv_message_notify(Session *session,
332                                      RecvMsgNotify notify_func, gpointer data)
333 {
334         session->recv_msg_notify = notify_func;
335         session->recv_msg_notify_data = data;
336 }
337
338 void session_set_recv_data_progressive_notify
339                                         (Session *session,
340                                          RecvDataProgressiveNotify notify_func,
341                                          gpointer data)
342 {
343         session->recv_data_progressive_notify = notify_func,
344         session->recv_data_progressive_notify_data = data;
345 }
346
347 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
348                                   gpointer data)
349 {
350         session->recv_data_notify = notify_func;
351         session->recv_data_notify_data = data;
352 }
353
354 void session_set_send_data_progressive_notify
355                                         (Session *session,
356                                          SendDataProgressiveNotify notify_func,
357                                          gpointer data)
358 {
359         session->send_data_progressive_notify = notify_func;
360         session->send_data_progressive_notify_data = data;
361 }
362
363 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
364                                   gpointer data)
365 {
366         session->send_data_notify = notify_func;
367         session->send_data_notify_data = data;
368 }
369
370 /*!
371  *\brief        child and parent cleanup (child closes first)
372  *
373  *\param        session Contains session information
374  *
375  *\return        0 : success
376  */
377 static gint session_close(Session *session)
378 {
379         cm_return_val_if_fail(session != NULL, -1);
380
381 #ifdef G_OS_UNIX
382         if (session->conn_id > 0) {
383                 sock_connect_async_cancel(session->conn_id);
384                 session->conn_id = 0;
385                 debug_print("session (%p): connection cancelled\n", session);
386         }
387 #endif
388
389         session_set_timeout(session, 0);
390
391         if (session->io_tag > 0) {
392                 g_source_remove(session->io_tag);
393                 session->io_tag = 0;
394         }
395
396         if (session->sock) {
397                 sock_close(session->sock, TRUE);
398                 session->sock = NULL;
399                 session->state = SESSION_DISCONNECTED;
400                 debug_print("session (%p): closed\n", session);
401         }
402
403         return 0;
404 }
405
406 #ifdef USE_GNUTLS
407 gint session_start_tls(Session *session)
408 {
409         gboolean nb_mode;
410
411         nb_mode = sock_is_nonblocking_mode(session->sock);
412
413         session->sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
414         session->sock->gnutls_priority = session->gnutls_priority;
415         session->sock->use_tls_sni = session->use_tls_sni;
416
417         if (nb_mode)
418                 sock_set_nonblocking_mode(session->sock, FALSE);
419
420         if (!ssl_init_socket(session->sock)) {
421                 g_warning("couldn't start STARTTLS session.");
422                 if (nb_mode)
423                         sock_set_nonblocking_mode(session->sock, session->nonblocking);
424                 return -1;
425         }
426
427         if (nb_mode)
428                 sock_set_nonblocking_mode(session->sock, session->nonblocking);
429
430         return 0;
431 }
432 #endif
433
434 gint session_send_msg(Session *session, const gchar *msg)
435 {
436         gboolean ret;
437
438         cm_return_val_if_fail(session->write_buf == NULL, -1);
439         cm_return_val_if_fail(msg != NULL, -1);
440
441         session->state = SESSION_SEND;
442         session->write_buf = g_strconcat((strlen(msg) > 0 ? msg : ""), "\r\n", NULL);
443         session->write_buf_p = session->write_buf;
444         session->write_buf_len = strlen(msg) + 2;
445
446         ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
447
448         if (ret == TRUE)
449                 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
450                                                  session_write_msg_cb, session);
451         else if (session->state == SESSION_ERROR)
452                 return -1;
453
454         return 0;
455 }
456
457 gint session_recv_msg(Session *session)
458 {
459         cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
460
461         session->state = SESSION_RECV;
462
463         if (session->read_buf_len > 0)
464                 g_idle_add(session_recv_msg_idle_cb, session);
465         else
466                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
467                                                  session_read_msg_cb, session);
468
469         return 0;
470 }
471
472 static gboolean session_recv_msg_idle_cb(gpointer data)
473 {
474         Session *session = SESSION(data);
475         gboolean ret;
476
477         ret = session_read_msg_cb(session->sock, G_IO_IN, session);
478
479         if (ret == TRUE)
480                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
481                                                  session_read_msg_cb, session);
482
483         return FALSE;
484 }
485
486 /*!
487  *\brief        parent (child?): send data to other process
488  *
489  *\param        session Contains session information
490  *              data Data to send
491  *              size Bytes to send
492  *
493  *\return        0 : success
494  *              -1 : error
495  */
496 gint session_send_data(Session *session, const guchar *data, guint size)
497 {
498         gboolean ret;
499
500         cm_return_val_if_fail(session->write_data == NULL, -1);
501         cm_return_val_if_fail(data != NULL, -1);
502         cm_return_val_if_fail(size != 0, -1);
503
504         session->state = SESSION_SEND;
505
506         session->write_data = data;
507         session->write_data_p = session->write_data;
508         session->write_data_len = size;
509 #if GLIB_CHECK_VERSION(2,61,2)
510         g_date_time_unref(session->tv_prev);
511         session->tv_prev = g_date_time_new_now_local();
512 #else
513         g_get_current_time(&session->tv_prev);
514 #endif
515
516         ret = session_write_data_cb(session->sock, G_IO_OUT, session);
517
518         if (ret == TRUE)
519                 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
520                                                  session_write_data_cb,
521                                                  session);
522         else if (session->state == SESSION_ERROR)
523                 return -1;
524
525         return 0;
526 }
527
528 gint session_recv_data(Session *session, guint size, const gchar *terminator)
529 {
530         cm_return_val_if_fail(session->read_data_buf->len == 0, -1);
531
532         session->state = SESSION_RECV;
533
534         g_free(session->read_data_terminator);
535         session->read_data_terminator = g_strdup(terminator);
536 #if GLIB_CHECK_VERSION(2,61,2)
537         g_date_time_unref(session->tv_prev);
538         session->tv_prev = g_date_time_new_now_local();
539 #else
540         g_get_current_time(&session->tv_prev);
541 #endif
542
543         if (session->read_buf_len > 0)
544                 g_idle_add(session_recv_data_idle_cb, session);
545         else
546                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
547                                                  session_read_data_cb, session);
548
549         return 0;
550 }
551
552 static gboolean session_recv_data_idle_cb(gpointer data)
553 {
554         Session *session = SESSION(data);
555         gboolean ret;
556
557         ret = session_read_data_cb(session->sock, G_IO_IN, session);
558
559         if (ret == TRUE)
560                 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
561                                                  session_read_data_cb, session);
562
563         return FALSE;
564 }
565
566 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
567                                     gpointer data)
568 {
569         Session *session = SESSION(data);
570         gchar buf[SESSION_BUFFSIZE];
571         gint line_len;
572         gchar *newline;
573         gchar *msg;
574         gint ret;
575
576         cm_return_val_if_fail(condition == G_IO_IN, FALSE);
577
578         session_set_timeout(session, session->timeout_interval);
579
580         if (session->read_buf_len == 0) {
581                 gint read_len = -1;
582
583                 if (session->sock)
584                         read_len = sock_read(session->sock, session->read_buf,
585                                      SESSION_BUFFSIZE - 1);
586
587                 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
588                         g_warning ("sock_read: session disconnected");
589                         if (session->io_tag > 0) {
590                                 g_source_remove(session->io_tag);
591                                 session->io_tag = 0;
592                         }
593                         return FALSE;
594                 }
595                 
596                 if (read_len == 0) {
597                         g_warning("sock_read: received EOF");
598                         session->state = SESSION_EOF;
599                         return FALSE;
600                 }
601
602                 if (read_len < 0) {
603                         switch (errno) {
604                         case EAGAIN:
605                                 return TRUE;
606                         default:
607                                 g_warning("sock_read: %s", g_strerror(errno));
608                                 session->state = SESSION_ERROR;
609                                 return FALSE;
610                         }
611                 }
612
613                 session->read_buf_len = read_len;
614         }
615
616         if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
617                 != NULL)
618                 line_len = newline - session->read_buf_p + 1;
619         else
620                 line_len = session->read_buf_len;
621
622         if (line_len == 0)
623                 return TRUE;
624
625         memcpy(buf, session->read_buf_p, line_len);
626         buf[line_len] = '\0';
627
628         g_string_append(session->read_msg_buf, buf);
629
630         session->read_buf_len -= line_len;
631         if (session->read_buf_len == 0)
632                 session->read_buf_p = session->read_buf;
633         else
634                 session->read_buf_p += line_len;
635
636         /* incomplete read */
637         if (buf[line_len - 1] != '\n')
638                 return TRUE;
639
640         /* complete */
641         if (session->io_tag > 0) {
642                 g_source_remove(session->io_tag);
643                 session->io_tag = 0;
644         }
645
646         /* callback */
647         msg = g_strdup(session->read_msg_buf->str);
648         strretchomp(msg);
649         g_string_truncate(session->read_msg_buf, 0);
650
651         ret = session->recv_msg(session, msg);
652         session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
653
654         g_free(msg);
655
656         if (ret < 0)
657                 session->state = SESSION_ERROR;
658
659         return FALSE;
660 }
661
662 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
663                                      gpointer data)
664 {
665         Session *session = SESSION(data);
666         GByteArray *data_buf;
667         gint terminator_len;
668         gboolean complete = FALSE;
669         guint data_len;
670         gint ret;
671
672         cm_return_val_if_fail(condition == G_IO_IN, FALSE);
673
674         session_set_timeout(session, session->timeout_interval);
675
676         if (session->read_buf_len == 0) {
677                 gint read_len;
678
679                 read_len = sock_read(session->sock, session->read_buf,
680                                      SESSION_BUFFSIZE);
681
682                 if (read_len == 0) {
683                         g_warning("sock_read: received EOF");
684                         session->state = SESSION_EOF;
685                         return FALSE;
686                 }
687
688                 if (read_len < 0) {
689                         switch (errno) {
690                         case EAGAIN:
691                                 return TRUE;
692                         default:
693                                 g_warning("sock_read: %s", g_strerror(errno));
694                                 session->state = SESSION_ERROR;
695                                 return FALSE;
696                         }
697                 }
698
699                 session->read_buf_len = read_len;
700         }
701
702         data_buf = session->read_data_buf;
703         terminator_len = strlen(session->read_data_terminator);
704
705         if (session->read_buf_len == 0)
706                 return TRUE;
707
708         g_byte_array_append(data_buf, session->read_buf_p,
709                             session->read_buf_len);
710
711         session->read_buf_len = 0;
712         session->read_buf_p = session->read_buf;
713
714         /* check if data is terminated */
715         if (data_buf->len >= terminator_len) {
716                 if (memcmp(data_buf->data, session->read_data_terminator,
717                            terminator_len) == 0)
718                         complete = TRUE;
719                 else if (data_buf->len >= terminator_len + 2 &&
720                          memcmp(data_buf->data + data_buf->len -
721                                 (terminator_len + 2), "\r\n", 2) == 0 &&
722                          memcmp(data_buf->data + data_buf->len -
723                                 terminator_len, session->read_data_terminator,
724                                 terminator_len) == 0)
725                         complete = TRUE;
726         }
727
728         /* incomplete read */
729         if (!complete) {
730 #if GLIB_CHECK_VERSION(2,61,2)
731                 GDateTime *tv_cur = g_date_time_new_now_local();
732
733                 GTimeSpan ts = g_date_time_difference(tv_cur, session->tv_prev);
734                 if (1000 - ts < 0 || ts > UI_REFRESH_INTERVAL) {
735                         session->recv_data_progressive_notify
736                                 (session, data_buf->len, 0,
737                                  session->recv_data_progressive_notify_data);
738                         g_date_time_unref(session->tv_prev);
739                         session->tv_prev = g_date_time_new_now_local();
740                 }
741                 g_date_time_unref(tv_cur);
742 #else
743                 GTimeVal tv_cur;
744
745                 g_get_current_time(&tv_cur);
746                 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
747                     tv_cur.tv_usec - session->tv_prev.tv_usec >
748                     UI_REFRESH_INTERVAL) {
749                         session->recv_data_progressive_notify
750                                 (session, data_buf->len, 0,
751                                  session->recv_data_progressive_notify_data);
752                         g_get_current_time(&session->tv_prev);
753                 }
754 #endif
755                 return TRUE;
756         }
757
758         /* complete */
759         if (session->io_tag > 0) {
760                 g_source_remove(session->io_tag);
761                 session->io_tag = 0;
762         }
763
764         data_len = data_buf->len - terminator_len;
765
766         /* callback */
767         ret = session->recv_data_finished(session, (gchar *)data_buf->data,
768                                           data_len);
769
770         g_byte_array_set_size(data_buf, 0);
771
772         session->recv_data_notify(session, data_len,
773                                   session->recv_data_notify_data);
774
775         if (ret < 0)
776                 session->state = SESSION_ERROR;
777
778         return FALSE;
779 }
780
781 static gint session_write_buf(Session *session)
782 {
783         gint write_len;
784         gint to_write_len;
785
786         cm_return_val_if_fail(session->write_buf != NULL, -1);
787         cm_return_val_if_fail(session->write_buf_p != NULL, -1);
788         cm_return_val_if_fail(session->write_buf_len > 0, -1);
789
790         to_write_len = session->write_buf_len -
791                 (session->write_buf_p - session->write_buf);
792         to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
793
794         write_len = sock_write(session->sock, session->write_buf_p,
795                                to_write_len);
796
797         if (write_len < 0) {
798                 switch (errno) {
799                 case EAGAIN:
800                         write_len = 0;
801                         break;
802                 default:
803                         g_warning("sock_write: %s", g_strerror(errno));
804                         session->state = SESSION_ERROR;
805                         return -1;
806                 }
807         }
808
809         /* incomplete write */
810         if (session->write_buf_p - session->write_buf + write_len <
811             session->write_buf_len) {
812                 session->write_buf_p += write_len;
813                 return 1;
814         }
815
816         g_free(session->write_buf);
817         session->write_buf = NULL;
818         session->write_buf_p = NULL;
819         session->write_buf_len = 0;
820
821         return 0;
822 }
823
824 static gint session_write_data(Session *session)
825 {
826         gint write_len;
827         gint to_write_len;
828
829         cm_return_val_if_fail(session->write_data != NULL, -1);
830         cm_return_val_if_fail(session->write_data_p != NULL, -1);
831         cm_return_val_if_fail(session->write_data_len > 0, -1);
832
833         to_write_len = session->write_data_len -
834                 (session->write_data_p - session->write_data);
835         to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
836
837         write_len = sock_write(session->sock, session->write_data_p,
838                                to_write_len);
839
840         if (write_len < 0) {
841                 switch (errno) {
842                 case EAGAIN:
843                         write_len = 0;
844                         break;
845                 default:
846                         g_warning("sock_write: %s", g_strerror(errno));
847                         session->state = SESSION_ERROR;
848                         return -1;
849                 }
850         }
851
852         /* incomplete write */
853         if (session->write_data_p - session->write_data + write_len <
854             session->write_data_len) {
855                 session->write_data_p += write_len;
856                 return 1;
857         }
858
859         session->write_data = NULL;
860         session->write_data_p = NULL;
861         session->write_data_len = 0;
862
863         return 0;
864 }
865
866 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
867                                      gpointer data)
868 {
869         Session *session = SESSION(data);
870         gint ret;
871
872         cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
873         cm_return_val_if_fail(session->write_buf != NULL, FALSE);
874         cm_return_val_if_fail(session->write_buf_p != NULL, FALSE);
875         cm_return_val_if_fail(session->write_buf_len > 0, FALSE);
876
877         ret = session_write_buf(session);
878
879         if (ret < 0) {
880                 session->state = SESSION_ERROR;
881                 return FALSE;
882         } else if (ret > 0)
883                 return TRUE;
884
885         if (session->io_tag > 0) {
886                 g_source_remove(session->io_tag);
887                 session->io_tag = 0;
888         }
889
890         session_recv_msg(session);
891
892         return FALSE;
893 }
894
895 static gboolean session_write_data_cb(SockInfo *source,
896                                       GIOCondition condition, gpointer data)
897 {
898         Session *session = SESSION(data);
899         guint write_data_len;
900         gint ret;
901
902         cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
903         cm_return_val_if_fail(session->write_data != NULL, FALSE);
904         cm_return_val_if_fail(session->write_data_p != NULL, FALSE);
905         cm_return_val_if_fail(session->write_data_len > 0, FALSE);
906
907         write_data_len = session->write_data_len;
908
909         ret = session_write_data(session);
910
911         if (ret < 0) {
912                 session->state = SESSION_ERROR;
913                 return FALSE;
914         } else if (ret > 0) {
915 #if GLIB_CHECK_VERSION(2,61,2)
916                 GDateTime *tv_cur = g_date_time_new_now_local();
917
918                 GTimeSpan ts = g_date_time_difference(tv_cur, session->tv_prev);
919                 if (1000 - ts < 0 || ts > UI_REFRESH_INTERVAL) {
920                         session_set_timeout(session, session->timeout_interval);
921                         session->send_data_progressive_notify
922                                 (session,
923                                  session->write_data_p - session->write_data,
924                                  write_data_len,
925                                  session->send_data_progressive_notify_data);
926                         g_date_time_unref(session->tv_prev);
927                         session->tv_prev = g_date_time_new_now_local();
928                 }
929                 g_date_time_unref(tv_cur);
930 #else
931                 GTimeVal tv_cur;
932
933                 g_get_current_time(&tv_cur);
934                 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
935                     tv_cur.tv_usec - session->tv_prev.tv_usec >
936                     UI_REFRESH_INTERVAL) {
937                         session_set_timeout(session, session->timeout_interval);
938                         session->send_data_progressive_notify
939                                 (session,
940                                  session->write_data_p - session->write_data,
941                                  write_data_len,
942                                  session->send_data_progressive_notify_data);
943                         g_get_current_time(&session->tv_prev);
944                 }
945
946 #endif
947                 return TRUE;
948         }
949
950         if (session->io_tag > 0) {
951                 g_source_remove(session->io_tag);
952                 session->io_tag = 0;
953         }
954
955         /* callback */
956         ret = session->send_data_finished(session, write_data_len);
957         session->send_data_notify(session, write_data_len,
958                                   session->send_data_notify_data);
959
960         return FALSE;
961 }
962
963 void session_register_ping(Session *session, gboolean (*ping_cb)(gpointer data))
964 {
965         if (!session)
966                 return;
967         if (session->ping_tag > -1)
968                 g_source_remove(session->ping_tag);
969
970         session->ping_tag = -1;
971
972         if (ping_cb != NULL)
973                 session->ping_tag = g_timeout_add_seconds(60, ping_cb, session);
974 }