talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

session.c (22351B)


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