summaryrefslogtreecommitdiff
path: root/libsylph/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsylph/session.c')
-rw-r--r--libsylph/session.c1320
1 files changed, 1320 insertions, 0 deletions
diff --git a/libsylph/session.c b/libsylph/session.c
new file mode 100644
index 0000000..08a493e
--- /dev/null
+++ b/libsylph/session.c
@@ -0,0 +1,1320 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 <unistd.h>
32#include <time.h>
33#include <errno.h>
34
35#include "session.h"
36#include "utils.h"
37
38typedef struct _SessionPrivData SessionPrivData;
39
40struct _SessionPrivData {
41 Session *session;
42 SocksInfo *socks_info;
43 SessionErrorValue error_val;
44 gpointer data;
45};
46
47static GList *priv_list = NULL;
48
49static SessionPrivData *session_get_priv(Session *session);
50
51static gint session_connect_cb (SockInfo *sock,
52 gpointer data);
53static gint session_close (Session *session);
54
55static gboolean session_timeout_cb (gpointer data);
56
57#ifdef G_OS_WIN32
58static gboolean session_ping_cb (gpointer data);
59#endif
60
61static gboolean session_recv_msg_idle_cb (gpointer data);
62static gboolean session_recv_data_idle_cb (gpointer data);
63
64static gboolean session_recv_data_as_file_idle_cb (gpointer data);
65
66static gboolean session_read_msg_cb (SockInfo *source,
67 GIOCondition condition,
68 gpointer data);
69static gboolean session_read_data_cb (SockInfo *source,
70 GIOCondition condition,
71 gpointer data);
72
73static gboolean session_read_data_as_file_cb (SockInfo *source,
74 GIOCondition condition,
75 gpointer data);
76
77static gboolean session_write_msg_cb (SockInfo *source,
78 GIOCondition condition,
79 gpointer data);
80static gboolean session_write_data_cb (SockInfo *source,
81 GIOCondition condition,
82 gpointer data);
83
84
85void session_init(Session *session)
86{
87 SessionPrivData *priv;
88
89 session->type = SESSION_UNKNOWN;
90 session->sock = NULL;
91 session->server = NULL;
92 session->port = 0;
93#if USE_SSL
94 session->ssl_type = SSL_NONE;
95#endif
96 session->nonblocking = TRUE;
97 session->state = SESSION_READY;
98 session->last_access_time = time(NULL);
99
100 g_get_current_time(&session->tv_prev);
101
102 session->conn_id = 0;
103
104 session->io_tag = 0;
105
106 session->read_buf_p = session->read_buf;
107 session->read_buf_len = 0;
108
109 session->read_msg_buf = g_string_sized_new(1024);
110 session->read_data_buf = g_byte_array_new();
111 session->read_data_terminator = NULL;
112
113 session->read_data_fp = NULL;
114 session->read_data_pos = 0;
115
116 session->preread_len = 0;
117
118 session->write_buf = NULL;
119 session->write_buf_p = NULL;
120 session->write_buf_len = 0;
121
122 session->write_data_fp = NULL;
123 session->write_data_pos = 0;
124 session->write_data_len = 0;
125
126 session->timeout_tag = 0;
127 session->timeout_interval = 0;
128
129 session->idle_tag = 0;
130 session->ping_tag = 0;
131
132 session->data = NULL;
133
134 priv = g_new0(SessionPrivData, 1);
135 priv->session = session;
136 priv->socks_info = NULL;
137 priv->error_val = SESSION_ERROR_OK;
138 priv_list = g_list_prepend(priv_list, priv);
139}
140
141static SessionPrivData *session_get_priv(Session *session)
142{
143 SessionPrivData *priv;
144 GList *cur;
145
146 g_return_val_if_fail(session != NULL, NULL);
147
148 for (cur = priv_list; cur != NULL; cur = cur->next) {
149 priv = (SessionPrivData *)cur->data;
150 if (priv->session == session)
151 return priv;
152 }
153
154 return NULL;
155}
156
157gint session_connect(Session *session, const gchar *server, gushort port)
158{
159 return session_connect_full(session, server, port, NULL);
160}
161
162gint session_connect_full(Session *session, const gchar *server, gushort port,
163 SocksInfo *socks_info)
164{
165 SessionPrivData *priv;
166#ifndef G_OS_UNIX
167 SockInfo *sock = NULL;
168#endif
169 g_return_val_if_fail(session != NULL, -1);
170 g_return_val_if_fail(server != NULL, -1);
171 g_return_val_if_fail(port > 0, -1);
172
173 priv = session_get_priv(session);
174 g_return_val_if_fail(priv != NULL, -1);
175 priv->socks_info = socks_info;
176
177 if (session->server != server) {
178 g_free(session->server);
179 session->server = g_strdup(server);
180 }
181 session->port = port;
182
183 if (socks_info) {
184 g_return_val_if_fail(socks_info->proxy_host != NULL, -1);
185 server = socks_info->proxy_host;
186 port = socks_info->proxy_port;
187 }
188
189#ifdef G_OS_UNIX
190 session->conn_id = sock_connect_async(server, port, session_connect_cb,
191 session);
192 if (session->conn_id < 0) {
193 g_warning("can't connect to server.");
194 session->state = SESSION_ERROR;
195 priv->error_val = SESSION_ERROR_CONNFAIL;
196 return -1;
197 }
198#elif USE_THREADS
199 session->conn_id = sock_connect_async_thread(server, port);
200 if (session->conn_id < 0) {
201 g_warning("can't connect to server.");
202 session->state = SESSION_ERROR;
203 priv->error_val = SESSION_ERROR_CONNFAIL;
204 return -1;
205 }
206 if (sock_info_connect_async_thread_wait(session->conn_id, &sock) < 0) {
207 session_connect_cb(sock, session);
208 if (sock)
209 sock_close(sock);
210 return -1;
211 }
212#else /* !USE_THREADS */
213 sock = sock_new(server, port);
214 if (sock_info_connect(sock) < 0) {
215 session_connect_cb(sock, session);
216 sock_close(sock);
217 return -1;
218 }
219#endif
220
221#ifdef G_OS_UNIX
222 return 0;
223#else
224 return session_connect_cb(sock, session);
225#endif
226}
227
228static gint session_connect_cb(SockInfo *sock, gpointer data)
229{
230 Session *session = SESSION(data);
231 SessionPrivData *priv;
232
233 priv = session_get_priv(session);
234 session->conn_id = 0;
235
236 if (!sock) {
237 g_warning("can't connect to server.");
238 session->state = SESSION_ERROR;
239 priv->error_val = SESSION_ERROR_CONNFAIL;
240 return -1;
241 }
242 if (sock->state == CONN_LOOKUPFAILED) {
243 g_warning("DNS lookup failed.");
244 session->state = SESSION_ERROR;
245 priv->error_val = SESSION_ERROR_LOOKUP;
246 return -1;
247 } else if (sock->state != CONN_ESTABLISHED) {
248 g_warning("can't connect to server (ConnectionState: %d).",
249 sock->state);
250 session->state = SESSION_ERROR;
251 priv->error_val = SESSION_ERROR_CONNFAIL;
252 return -1;
253 }
254
255 session->sock = sock;
256
257 if (priv->socks_info) {
258 sock_set_nonblocking_mode(sock, FALSE);
259 if (socks_connect(sock, session->server, session->port,
260 priv->socks_info) < 0) {
261 g_warning("can't establish SOCKS connection.");
262 session->state = SESSION_ERROR;
263 priv->error_val = SESSION_ERROR_CONNFAIL;
264 return -1;
265 }
266 }
267
268#if USE_SSL
269 if (session->ssl_type == SSL_TUNNEL) {
270 sock_set_nonblocking_mode(sock, FALSE);
271 if (!ssl_init_socket(sock)) {
272 g_warning("can't initialize SSL.");
273 session->state = SESSION_ERROR;
274 priv->error_val = SESSION_ERROR_SOCKET;
275 return -1;
276 }
277 }
278#endif
279
280 debug_print("session (%p): connected\n", session);
281
282 sock_set_nonblocking_mode(sock, session->nonblocking);
283
284 session->state = SESSION_RECV;
285 priv->error_val = SESSION_ERROR_OK;
286 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
287 session_read_msg_cb,
288 session);
289
290#ifdef G_OS_WIN32
291 session->ping_tag = g_timeout_add_full(G_PRIORITY_LOW, 1000, session_ping_cb, session, NULL);
292#endif
293
294 return 0;
295}
296
297gint session_disconnect(Session *session)
298{
299 session_close(session);
300 return 0;
301}
302
303void session_destroy(Session *session)
304{
305 SessionPrivData *priv;
306
307 g_return_if_fail(session != NULL);
308 g_return_if_fail(session->destroy != NULL);
309
310 session_close(session);
311 session->destroy(session);
312 g_free(session->server);
313 g_string_free(session->read_msg_buf, TRUE);
314 g_byte_array_free(session->read_data_buf, TRUE);
315 g_free(session->read_data_terminator);
316 if (session->read_data_fp)
317 fclose(session->read_data_fp);
318 g_free(session->write_buf);
319
320 priv = session_get_priv(session);
321 if (priv) {
322 priv_list = g_list_remove(priv_list, priv);
323 socks_info_free(priv->socks_info);
324 g_free(priv);
325 }
326
327 debug_print("session (%p): destroyed\n", session);
328
329 g_free(session);
330}
331
332gboolean session_is_connected(Session *session)
333{
334 return (session->state == SESSION_READY ||
335 session->state == SESSION_SEND ||
336 session->state == SESSION_RECV);
337}
338
339SessionErrorValue session_get_error(Session *session)
340{
341 SessionPrivData *priv;
342
343 priv = session_get_priv(session);
344 if (priv)
345 return priv->error_val;
346 else
347 return SESSION_ERROR_ERROR;
348}
349
350void session_set_access_time(Session *session)
351{
352 session->last_access_time = time(NULL);
353}
354
355void session_set_timeout(Session *session, guint interval)
356{
357 if (session->timeout_tag > 0)
358 g_source_remove(session->timeout_tag);
359
360 session->timeout_interval = interval;
361 if (interval > 0)
362 session->timeout_tag =
363 g_timeout_add_full(G_PRIORITY_LOW, interval, session_timeout_cb, session, NULL);
364 else
365 session->timeout_tag = 0;
366}
367
368static gboolean session_timeout_cb(gpointer data)
369{
370 Session *session = SESSION(data);
371 SessionPrivData *priv;
372
373 g_warning("session timeout.\n");
374
375 if (session->io_tag > 0) {
376 g_source_remove(session->io_tag);
377 session->io_tag = 0;
378 }
379
380 session->timeout_tag = 0;
381 session->state = SESSION_TIMEOUT;
382 priv = session_get_priv(session);
383 priv->error_val = SESSION_ERROR_TIMEOUT;
384
385 return FALSE;
386}
387
388#ifdef G_OS_WIN32
389/* hack for state machine freeze problem in GLib >= 2.8.x */
390static gboolean session_ping_cb(gpointer data)
391{
392 Session *session = SESSION(data);
393 SockInfo *sock = session->sock;
394
395 if (!session_is_connected(session))
396 return FALSE;
397
398 if (session->io_tag > 0 && sock && sock->callback) {
399 GTimeVal tv_cur, tv_result;
400
401 g_get_current_time(&tv_cur);
402 tv_result.tv_sec = tv_cur.tv_sec - session->tv_prev.tv_sec;
403 tv_result.tv_usec = tv_cur.tv_usec - session->tv_prev.tv_usec;
404 if (tv_result.tv_usec < 0) {
405 tv_result.tv_sec--;
406 tv_result.tv_usec += G_USEC_PER_SEC;
407 }
408 if (tv_result.tv_sec * G_USEC_PER_SEC + tv_result.tv_usec >
409 G_USEC_PER_SEC) {
410 SockFlags save_flags;
411
412 debug_print("state machine freeze for 1 second detected, forcing dispatch.\n");
413 save_flags = sock->flags;
414 SOCK_UNSET_FLAGS(sock->flags, SYL_SOCK_CHECK_IO);
415 sock->callback(sock, sock->condition, sock->data);
416 sock->flags = save_flags;
417 }
418 }
419
420 return TRUE;
421}
422#endif
423
424void session_set_recv_message_notify(Session *session,
425 RecvMsgNotify notify_func, gpointer data)
426{
427 session->recv_msg_notify = notify_func;
428 session->recv_msg_notify_data = data;
429}
430
431void session_set_recv_data_progressive_notify
432 (Session *session,
433 RecvDataProgressiveNotify notify_func,
434 gpointer data)
435{
436 session->recv_data_progressive_notify = notify_func,
437 session->recv_data_progressive_notify_data = data;
438}
439
440void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
441 gpointer data)
442{
443 session->recv_data_notify = notify_func;
444 session->recv_data_notify_data = data;
445}
446
447void session_set_send_data_progressive_notify
448 (Session *session,
449 SendDataProgressiveNotify notify_func,
450 gpointer data)
451{
452 session->send_data_progressive_notify = notify_func;
453 session->send_data_progressive_notify_data = data;
454}
455
456void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
457 gpointer data)
458{
459 session->send_data_notify = notify_func;
460 session->send_data_notify_data = data;
461}
462
463static gint session_close(Session *session)
464{
465 g_return_val_if_fail(session != NULL, -1);
466
467#ifdef G_OS_UNIX
468 if (session->conn_id > 0) {
469 sock_connect_async_cancel(session->conn_id);
470 session->conn_id = 0;
471 debug_print("session (%p): connection cancelled\n", session);
472 }
473#endif
474
475 session_set_timeout(session, 0);
476
477 if (session->idle_tag > 0) {
478 g_source_remove(session->idle_tag);
479 session->idle_tag = 0;
480 }
481
482#ifdef G_OS_WIN32
483 if (session->ping_tag > 0) {
484 g_source_remove(session->ping_tag);
485 session->ping_tag = 0;
486 }
487#endif
488
489 if (session->io_tag > 0) {
490 g_source_remove(session->io_tag);
491 session->io_tag = 0;
492 }
493
494 if (session->sock) {
495 sock_close(session->sock);
496 session->sock = NULL;
497 session->state = SESSION_DISCONNECTED;
498 debug_print("session (%p): closed\n", session);
499 }
500
501 return 0;
502}
503
504#if USE_SSL
505gint session_start_tls(Session *session)
506{
507 gboolean nb_mode;
508
509 nb_mode = sock_is_nonblocking_mode(session->sock);
510
511 sock_set_nonblocking_mode(session->sock, FALSE);
512
513 if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
514 g_warning("can't start TLS session.\n");
515 if (nb_mode)
516 sock_set_nonblocking_mode(session->sock, TRUE);
517 return -1;
518 }
519
520 sock_set_nonblocking_mode(session->sock, session->nonblocking);
521
522 return 0;
523}
524#endif
525
526gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
527{
528 gboolean ret;
529
530 g_return_val_if_fail(session->sock != NULL, -1);
531 g_return_val_if_fail(session->write_buf == NULL, -1);
532 g_return_val_if_fail(msg != NULL, -1);
533 g_return_val_if_fail(msg[0] != '\0', -1);
534
535 session->state = SESSION_SEND;
536 session->write_buf = g_strconcat(msg, "\r\n", NULL);
537 session->write_buf_p = session->write_buf;
538 session->write_buf_len = strlen(msg) + 2;
539
540 ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
541
542 if (ret == TRUE)
543 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
544 session_write_msg_cb, session);
545 else if (session->state == SESSION_ERROR)
546 return -1;
547
548 return 0;
549}
550
551gint session_recv_msg(Session *session)
552{
553 g_return_val_if_fail(session->sock != NULL, -1);
554 g_return_val_if_fail(session->read_msg_buf->len == 0, -1);
555
556 session->state = SESSION_RECV;
557
558 if (session->read_buf_len > 0)
559 session->idle_tag = g_idle_add(session_recv_msg_idle_cb,
560 session);
561 else
562 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
563 session_read_msg_cb, session);
564
565 return 0;
566}
567
568static gboolean session_recv_msg_idle_cb(gpointer data)
569{
570 Session *session = SESSION(data);
571 gboolean ret;
572
573#if GLIB_CHECK_VERSION(2, 12, 0)
574 if (g_source_is_destroyed(g_main_current_source()))
575 return FALSE;
576#endif
577
578 session->idle_tag = 0;
579 ret = session_read_msg_cb(session->sock, G_IO_IN, session);
580
581 if (ret == TRUE)
582 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
583 session_read_msg_cb, session);
584
585 return FALSE;
586}
587
588gint session_send_data(Session *session, FILE *data_fp, guint size)
589{
590 gboolean ret;
591
592 g_return_val_if_fail(session->sock != NULL, -1);
593 g_return_val_if_fail(session->write_data_fp == NULL, -1);
594 g_return_val_if_fail(data_fp != NULL, -1);
595 g_return_val_if_fail(size != 0, -1);
596
597 session->state = SESSION_SEND;
598
599 session->write_data_fp = data_fp;
600 session->write_data_pos = 0;
601 session->write_data_len = size;
602 g_get_current_time(&session->tv_prev);
603
604#ifdef G_OS_WIN32
605 sock_set_nonblocking_mode(session->sock, FALSE);
606#endif
607
608 ret = session_write_data_cb(session->sock, G_IO_OUT, session);
609
610 if (ret == TRUE)
611#ifdef G_OS_WIN32
612 session->io_tag = sock_add_watch_poll(session->sock, G_IO_OUT,
613 session_write_data_cb,
614 session);
615#else
616 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
617 session_write_data_cb,
618 session);
619#endif
620 else if (session->state == SESSION_ERROR)
621 return -1;
622
623 return 0;
624}
625
626gint session_recv_data(Session *session, guint size, const gchar *terminator)
627{
628 g_return_val_if_fail(session->sock != NULL, -1);
629 g_return_val_if_fail(session->read_data_buf->len == 0, -1);
630
631 session->state = SESSION_RECV;
632
633 g_free(session->read_data_terminator);
634 session->read_data_terminator = g_strdup(terminator);
635 g_get_current_time(&session->tv_prev);
636
637 if (session->read_buf_len > 0)
638 session->idle_tag = g_idle_add(session_recv_data_idle_cb,
639 session);
640 else
641 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
642 session_read_data_cb, session);
643
644 return 0;
645}
646
647static gboolean session_recv_data_idle_cb(gpointer data)
648{
649 Session *session = SESSION(data);
650 gboolean ret;
651
652#if GLIB_CHECK_VERSION(2, 12, 0)
653 if (g_source_is_destroyed(g_main_current_source()))
654 return FALSE;
655#endif
656
657 session->idle_tag = 0;
658 ret = session_read_data_cb(session->sock, G_IO_IN, session);
659
660 if (ret == TRUE)
661 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
662 session_read_data_cb, session);
663
664 return FALSE;
665}
666
667gint session_recv_data_as_file(Session *session, guint size,
668 const gchar *terminator)
669{
670 g_return_val_if_fail(session->sock != NULL, -1);
671 g_return_val_if_fail(session->read_data_pos == 0, -1);
672 g_return_val_if_fail(session->read_data_fp == NULL, -1);
673
674 session->state = SESSION_RECV;
675
676 g_free(session->read_data_terminator);
677 session->read_data_terminator = g_strdup(terminator);
678 g_get_current_time(&session->tv_prev);
679
680 session->read_data_fp = my_tmpfile();
681 if (!session->read_data_fp) {
682 FILE_OP_ERROR("session_recv_data_as_file", "my_tmpfile");
683 return -1;
684 }
685
686 if (session->read_buf_len > 0)
687 session->idle_tag =
688 g_idle_add(session_recv_data_as_file_idle_cb, session);
689 else
690 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
691 session_read_data_as_file_cb,
692 session);
693
694 return 0;
695}
696
697static gboolean session_recv_data_as_file_idle_cb(gpointer data)
698{
699 Session *session = SESSION(data);
700 gboolean ret;
701
702#if GLIB_CHECK_VERSION(2, 12, 0)
703 if (g_source_is_destroyed(g_main_current_source()))
704 return FALSE;
705#endif
706
707 session->idle_tag = 0;
708 ret = session_read_data_as_file_cb(session->sock, G_IO_IN, session);
709
710 if (ret == TRUE)
711 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
712 session_read_data_as_file_cb,
713 session);
714
715 return FALSE;
716}
717
718static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
719 gpointer data)
720{
721 Session *session = SESSION(data);
722 SessionPrivData *priv;
723 gchar buf[SESSION_BUFFSIZE];
724 gint line_len;
725 gchar *newline;
726 gchar *msg;
727 gint ret;
728
729 g_return_val_if_fail(condition == G_IO_IN, FALSE);
730
731 if (session->read_buf_len == 0) {
732 gint read_len;
733
734 read_len = sock_read(session->sock, session->read_buf,
735 SESSION_BUFFSIZE - 1);
736
737 if (read_len == 0) {
738 g_warning("sock_read: received EOF\n");
739 session->state = SESSION_EOF;
740 return FALSE;
741 }
742
743 if (read_len < 0) {
744 switch (errno) {
745 case EAGAIN:
746 return TRUE;
747 default:
748 g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno));
749 session->state = SESSION_ERROR;
750 priv = session_get_priv(session);
751 priv->error_val = SESSION_ERROR_SOCKET;
752 return FALSE;
753 }
754 }
755
756 session->read_buf_len = read_len;
757 }
758
759 session_set_timeout(session, session->timeout_interval);
760
761 if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
762 != NULL)
763 line_len = newline - session->read_buf_p + 1;
764 else
765 line_len = session->read_buf_len;
766
767 if (line_len == 0)
768 return TRUE;
769
770 memcpy(buf, session->read_buf_p, line_len);
771 buf[line_len] = '\0';
772
773 g_string_append(session->read_msg_buf, buf);
774
775 session->read_buf_len -= line_len;
776 if (session->read_buf_len == 0)
777 session->read_buf_p = session->read_buf;
778 else
779 session->read_buf_p += line_len;
780
781 /* incomplete read */
782 if (buf[line_len - 1] != '\n')
783 return TRUE;
784
785 /* complete */
786 if (session->io_tag > 0) {
787 g_source_remove(session->io_tag);
788 session->io_tag = 0;
789 }
790
791 /* callback */
792 msg = g_strdup(session->read_msg_buf->str);
793 strretchomp(msg);
794 g_string_truncate(session->read_msg_buf, 0);
795
796 ret = session->recv_msg(session, msg);
797 if (session->recv_msg_notify)
798 session->recv_msg_notify(session, msg,
799 session->recv_msg_notify_data);
800
801 g_free(msg);
802
803 if (ret < 0) {
804 session->state = SESSION_ERROR;
805 priv = session_get_priv(session);
806 priv->error_val = SESSION_ERROR_SOCKET;
807 }
808
809 return FALSE;
810}
811
812static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
813 gpointer data)
814{
815 Session *session = SESSION(data);
816 SessionPrivData *priv;
817 GByteArray *data_buf;
818 gint terminator_len;
819 gboolean complete = FALSE;
820 guint data_len;
821 gint ret;
822
823 g_return_val_if_fail(condition == G_IO_IN, FALSE);
824
825 if (session->read_buf_len == 0) {
826 gint read_len;
827
828 read_len = sock_read(session->sock, session->read_buf,
829 SESSION_BUFFSIZE);
830
831 if (read_len == 0) {
832 g_warning("sock_read: received EOF\n");
833 session->state = SESSION_EOF;
834 return FALSE;
835 }
836
837 if (read_len < 0) {
838 switch (errno) {
839 case EAGAIN:
840 return TRUE;
841 default:
842 g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno));
843 session->state = SESSION_ERROR;
844 priv = session_get_priv(session);
845 priv->error_val = SESSION_ERROR_SOCKET;
846 return FALSE;
847 }
848 }
849
850 session->read_buf_len = read_len;
851 }
852
853 session_set_timeout(session, session->timeout_interval);
854
855 data_buf = session->read_data_buf;
856 terminator_len = strlen(session->read_data_terminator);
857
858 if (session->read_buf_len == 0)
859 return TRUE;
860
861 g_byte_array_append(data_buf, (guchar *)session->read_buf_p,
862 session->read_buf_len);
863
864 session->read_buf_len = 0;
865 session->read_buf_p = session->read_buf;
866
867 /* check if data is terminated */
868 if (data_buf->len >= terminator_len) {
869 if (memcmp(data_buf->data, session->read_data_terminator,
870 terminator_len) == 0)
871 complete = TRUE;
872 else if (data_buf->len >= terminator_len + 2 &&
873 memcmp(data_buf->data + data_buf->len -
874 (terminator_len + 2), "\r\n", 2) == 0 &&
875 memcmp(data_buf->data + data_buf->len -
876 terminator_len, session->read_data_terminator,
877 terminator_len) == 0)
878 complete = TRUE;
879 }
880
881 /* incomplete read */
882 if (!complete) {
883 GTimeVal tv_cur;
884
885 g_get_current_time(&tv_cur);
886 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
887 tv_cur.tv_usec - session->tv_prev.tv_usec >
888 UI_REFRESH_INTERVAL) {
889 if (session->recv_data_progressive_notify)
890 session->recv_data_progressive_notify
891 (session, data_buf->len, 0,
892 session->recv_data_progressive_notify_data);
893 g_get_current_time(&session->tv_prev);
894 }
895 return TRUE;
896 }
897
898 /* complete */
899 if (session->io_tag > 0) {
900 g_source_remove(session->io_tag);
901 session->io_tag = 0;
902 }
903
904 data_len = data_buf->len - terminator_len;
905
906 /* callback */
907 ret = session->recv_data_finished(session, (guchar *)data_buf->data,
908 data_len);
909
910 g_byte_array_set_size(data_buf, 0);
911
912 if (session->recv_data_notify)
913 session->recv_data_notify(session, data_len,
914 session->recv_data_notify_data);
915
916 if (ret < 0) {
917 session->state = SESSION_ERROR;
918 priv = session_get_priv(session);
919 priv->error_val = SESSION_ERROR_SOCKET;
920 }
921
922 return FALSE;
923}
924
925#define READ_BUF_LEFT() \
926 (SESSION_BUFFSIZE - (session->read_buf_p - session->read_buf) - \
927 session->read_buf_len)
928#define PREREAD_SIZE 8
929
930static gboolean session_read_data_as_file_cb(SockInfo *source,
931 GIOCondition condition,
932 gpointer data)
933{
934 Session *session = SESSION(data);
935 SessionPrivData *priv;
936 gint terminator_len;
937 gchar *data_begin_p;
938 gint buf_data_len;
939 gboolean complete = FALSE;
940 gint read_len;
941 gint write_len;
942 gint ret;
943
944 g_return_val_if_fail(condition == G_IO_IN, FALSE);
945
946 if (session->read_buf_len == 0) {
947 read_len = sock_read(session->sock, session->read_buf_p,
948 READ_BUF_LEFT());
949
950 if (read_len == 0) {
951 g_warning("sock_read: received EOF\n");
952 session->state = SESSION_EOF;
953 return FALSE;
954 }
955
956 if (read_len < 0) {
957 switch (errno) {
958 case EAGAIN:
959 return TRUE;
960 default:
961 g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno));
962 session->state = SESSION_ERROR;
963 priv = session_get_priv(session);
964 priv->error_val = SESSION_ERROR_SOCKET;
965 return FALSE;
966 }
967 }
968
969 session->read_buf_len = read_len;
970 }
971
972 session_set_timeout(session, session->timeout_interval);
973
974 terminator_len = strlen(session->read_data_terminator);
975
976 if (session->read_buf_len == 0)
977 return TRUE;
978
979 /* +---------------buf_data_len---------------+
980 * +--preread_len--+-------read_buf_len-------+
981 * +---------------+--------------------------+-------------------+ *
982 * ^data_begin_p ^read_buf_p
983 * ^read_buf
984 */
985
986 data_begin_p = session->read_buf_p - session->preread_len;
987 buf_data_len = session->preread_len + session->read_buf_len;
988
989 /* check if data is terminated */
990 if (buf_data_len >= terminator_len) {
991 if (session->read_data_pos == 0 &&
992 buf_data_len == terminator_len &&
993 memcmp(data_begin_p, session->read_data_terminator,
994 terminator_len) == 0)
995 complete = TRUE;
996 else if (buf_data_len >= terminator_len + 2 &&
997 memcmp(data_begin_p + buf_data_len -
998 (terminator_len + 2), "\r\n", 2) == 0 &&
999 memcmp(data_begin_p + buf_data_len -
1000 terminator_len, session->read_data_terminator,
1001 terminator_len) == 0)
1002 complete = TRUE;
1003 }
1004
1005 /* incomplete read */
1006 if (!complete) {
1007 GTimeVal tv_cur;
1008
1009 if (buf_data_len <= PREREAD_SIZE) {
1010 if (data_begin_p > session->read_buf) {
1011 g_memmove(session->read_buf, data_begin_p,
1012 buf_data_len);
1013 data_begin_p = session->read_buf;
1014 session->read_buf_p = session->read_buf +
1015 session->preread_len;
1016 }
1017 session->read_buf_p += session->read_buf_len;
1018 session->preread_len = buf_data_len;
1019 session->read_buf_len = 0;
1020 return TRUE;
1021 }
1022
1023 if (READ_BUF_LEFT() >= (SESSION_BUFFSIZE / 2)) {
1024 session->read_buf_p += session->read_buf_len;
1025 session->preread_len = buf_data_len;
1026 session->read_buf_len = 0;
1027 return TRUE;
1028 }
1029
1030 write_len = buf_data_len - PREREAD_SIZE;
1031 if (fwrite(data_begin_p, write_len, 1,
1032 session->read_data_fp) < 1) {
1033 g_warning("session_read_data_as_file_cb: "
1034 "writing data to file failed\n");
1035 session->state = SESSION_ERROR;
1036 priv = session_get_priv(session);
1037 priv->error_val = SESSION_ERROR_IO;
1038 return FALSE;
1039 }
1040 session->read_data_pos += write_len;
1041
1042 g_memmove(session->read_buf, data_begin_p + write_len,
1043 PREREAD_SIZE);
1044 session->read_buf_p = session->read_buf + PREREAD_SIZE;
1045 session->preread_len = PREREAD_SIZE;
1046 session->read_buf_len = 0;
1047
1048 g_get_current_time(&tv_cur);
1049 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
1050 tv_cur.tv_usec - session->tv_prev.tv_usec >
1051 UI_REFRESH_INTERVAL) {
1052 if (session->recv_data_progressive_notify)
1053 session->recv_data_progressive_notify
1054 (session, session->read_data_pos, 0,
1055 session->recv_data_progressive_notify_data);
1056 g_get_current_time(&session->tv_prev);
1057 }
1058
1059 return TRUE;
1060 }
1061
1062 /* complete */
1063 if (session->io_tag > 0) {
1064 g_source_remove(session->io_tag);
1065 session->io_tag = 0;
1066 }
1067
1068 write_len = buf_data_len - terminator_len;
1069 if (write_len > 0 && fwrite(data_begin_p, write_len, 1,
1070 session->read_data_fp) < 1) {
1071 g_warning("session_read_data_as_file_cb: "
1072 "writing data to file failed\n");
1073 session->state = SESSION_ERROR;
1074 priv = session_get_priv(session);
1075 priv->error_val = SESSION_ERROR_IO;
1076 return FALSE;
1077 }
1078 session->read_data_pos += write_len;
1079
1080 if (fflush(session->read_data_fp) == EOF) {
1081 perror("fflush");
1082 g_warning("session_read_data_as_file_cb: "
1083 "writing data to file failed\n");
1084 session->state = SESSION_ERROR;
1085 priv = session_get_priv(session);
1086 priv->error_val = SESSION_ERROR_IO;
1087 return FALSE;
1088 }
1089 rewind(session->read_data_fp);
1090
1091 session->preread_len = 0;
1092 session->read_buf_len = 0;
1093 session->read_buf_p = session->read_buf;
1094
1095 /* callback */
1096 ret = session->recv_data_as_file_finished
1097 (session, session->read_data_fp, session->read_data_pos);
1098
1099 fclose(session->read_data_fp);
1100 session->read_data_fp = NULL;
1101
1102 if (session->recv_data_notify)
1103 session->recv_data_notify(session, session->read_data_pos,
1104 session->recv_data_notify_data);
1105
1106 session->read_data_pos = 0;
1107
1108 if (ret < 0) {
1109 session->state = SESSION_ERROR;
1110 priv = session_get_priv(session);
1111 priv->error_val = SESSION_ERROR_IO;
1112 }
1113
1114 return FALSE;
1115}
1116
1117static gint session_write_buf(Session *session)
1118{
1119 gint write_len;
1120 gint to_write_len;
1121 SessionPrivData *priv;
1122
1123 g_return_val_if_fail(session->write_buf != NULL, -1);
1124 g_return_val_if_fail(session->write_buf_p != NULL, -1);
1125 g_return_val_if_fail(session->write_buf_len > 0, -1);
1126
1127 to_write_len = session->write_buf_len -
1128 (session->write_buf_p - session->write_buf);
1129 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
1130
1131 write_len = sock_write(session->sock, session->write_buf_p,
1132 to_write_len);
1133
1134 if (write_len < 0) {
1135 switch (errno) {
1136 case EAGAIN:
1137 write_len = 0;
1138 break;
1139 default:
1140 g_warning("sock_write: %s\n", g_strerror(errno));
1141 session->state = SESSION_ERROR;
1142 priv = session_get_priv(session);
1143 priv->error_val = SESSION_ERROR_SOCKET;
1144 return -1;
1145 }
1146 }
1147
1148 /* incomplete write */
1149 if (session->write_buf_p - session->write_buf + write_len <
1150 session->write_buf_len) {
1151 session->write_buf_p += write_len;
1152 return 1;
1153 }
1154
1155 g_free(session->write_buf);
1156 session->write_buf = NULL;
1157 session->write_buf_p = NULL;
1158 session->write_buf_len = 0;
1159
1160 return 0;
1161}
1162
1163#define WRITE_DATA_BUFFSIZE 8192
1164
1165static gint session_write_data(Session *session, gint *nwritten)
1166{
1167 gchar buf[WRITE_DATA_BUFFSIZE];
1168 gint write_len;
1169 gint to_write_len;
1170 SessionPrivData *priv;
1171
1172 g_return_val_if_fail(session->write_data_fp != NULL, -1);
1173 g_return_val_if_fail(session->write_data_pos >= 0, -1);
1174 g_return_val_if_fail(session->write_data_len > 0, -1);
1175
1176 to_write_len = session->write_data_len - session->write_data_pos;
1177 to_write_len = MIN(to_write_len, WRITE_DATA_BUFFSIZE);
1178 if (fread(buf, to_write_len, 1, session->write_data_fp) < 1) {
1179 g_warning("session_write_data: reading data from file failed\n");
1180 session->state = SESSION_ERROR;
1181 priv = session_get_priv(session);
1182 priv->error_val = SESSION_ERROR_IO;
1183 return -1;
1184 }
1185
1186 write_len = sock_write(session->sock, buf, to_write_len);
1187
1188 if (write_len < 0) {
1189 switch (errno) {
1190 case EAGAIN:
1191 write_len = 0;
1192 break;
1193 default:
1194 g_warning("sock_write: %s\n", g_strerror(errno));
1195 session->state = SESSION_ERROR;
1196 priv = session_get_priv(session);
1197 priv->error_val = SESSION_ERROR_SOCKET;
1198 *nwritten = write_len;
1199 return -1;
1200 }
1201 }
1202
1203 *nwritten = write_len;
1204
1205 /* incomplete write */
1206 if (session->write_data_pos + write_len < session->write_data_len) {
1207 session->write_data_pos += write_len;
1208 if (write_len < to_write_len) {
1209 if (fseek(session->write_data_fp,
1210 session->write_data_pos, SEEK_SET) < 0) {
1211 g_warning("session_write_data: file seek failed\n");
1212 session->state = SESSION_ERROR;
1213 priv = session_get_priv(session);
1214 priv->error_val = SESSION_ERROR_IO;
1215 return -1;
1216 }
1217 }
1218 return 1;
1219 }
1220
1221 session->write_data_fp = NULL;
1222 session->write_data_pos = 0;
1223 session->write_data_len = 0;
1224
1225 return 0;
1226}
1227
1228static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
1229 gpointer data)
1230{
1231 Session *session = SESSION(data);
1232 SessionPrivData *priv;
1233 gint ret;
1234
1235 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
1236 g_return_val_if_fail(session->write_buf != NULL, FALSE);
1237 g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
1238 g_return_val_if_fail(session->write_buf_len > 0, FALSE);
1239
1240 ret = session_write_buf(session);
1241
1242 if (ret < 0) {
1243 session->state = SESSION_ERROR;
1244 priv = session_get_priv(session);
1245 if (priv->error_val == SESSION_ERROR_OK)
1246 priv->error_val = SESSION_ERROR_IO;
1247 return FALSE;
1248 } else if (ret > 0)
1249 return TRUE;
1250
1251 if (session->io_tag > 0) {
1252 g_source_remove(session->io_tag);
1253 session->io_tag = 0;
1254 }
1255
1256 session_recv_msg(session);
1257
1258 return FALSE;
1259}
1260
1261static gboolean session_write_data_cb(SockInfo *source,
1262 GIOCondition condition, gpointer data)
1263{
1264 Session *session = SESSION(data);
1265 SessionPrivData *priv;
1266 guint write_data_len;
1267 gint write_len;
1268 gint ret;
1269
1270 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
1271 g_return_val_if_fail(session->write_data_fp != NULL, FALSE);
1272 g_return_val_if_fail(session->write_data_pos >= 0, FALSE);
1273 g_return_val_if_fail(session->write_data_len > 0, FALSE);
1274
1275 write_data_len = session->write_data_len;
1276
1277 ret = session_write_data(session, &write_len);
1278
1279 if (ret < 0) {
1280 session->state = SESSION_ERROR;
1281 priv = session_get_priv(session);
1282 if (priv->error_val == SESSION_ERROR_OK)
1283 priv->error_val = SESSION_ERROR_IO;
1284 return FALSE;
1285 } else if (ret > 0) {
1286 GTimeVal tv_cur;
1287
1288 g_get_current_time(&tv_cur);
1289 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
1290 tv_cur.tv_usec - session->tv_prev.tv_usec >
1291 UI_REFRESH_INTERVAL) {
1292 session_set_timeout(session, session->timeout_interval);
1293 if (session->send_data_progressive_notify)
1294 session->send_data_progressive_notify
1295 (session,
1296 session->write_data_pos,
1297 write_data_len,
1298 session->send_data_progressive_notify_data);
1299 g_get_current_time(&session->tv_prev);
1300 }
1301 return TRUE;
1302 }
1303
1304 if (session->io_tag > 0) {
1305 g_source_remove(session->io_tag);
1306 session->io_tag = 0;
1307 }
1308
1309 /* callback */
1310 ret = session->send_data_finished(session, write_data_len);
1311 if (session->send_data_notify)
1312 session->send_data_notify(session, write_data_len,
1313 session->send_data_notify_data);
1314
1315#ifdef G_OS_WIN32
1316 sock_set_nonblocking_mode(session->sock, session->nonblocking);
1317#endif
1318
1319 return FALSE;
1320}