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