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