Statistics
| Revision:

root / libsylph / session.c @ 1230

History | View | Annotate | Download (25.7 kB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2006 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
38
static gint session_connect_cb                (SockInfo        *sock,
39
                                         gpointer         data);
40
static gint session_close                (Session        *session);
41
42
static gboolean session_timeout_cb        (gpointer         data);
43
44
#ifdef G_OS_WIN32
45
static gboolean session_ping_cb                (gpointer         data);
46
#endif
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_recv_data_as_file_idle_cb        (gpointer         data);
52
53
static gboolean session_read_msg_cb        (SockInfo        *source,
54
                                         GIOCondition         condition,
55
                                         gpointer         data);
56
static gboolean session_read_data_cb        (SockInfo        *source,
57
                                         GIOCondition         condition,
58
                                         gpointer         data);
59
60
static gboolean session_read_data_as_file_cb        (SockInfo        *source,
61
                                                 GIOCondition         condition,
62
                                                 gpointer         data);
63
64
static gboolean session_write_msg_cb        (SockInfo        *source,
65
                                         GIOCondition         condition,
66
                                         gpointer         data);
67
static gboolean session_write_data_cb        (SockInfo        *source,
68
                                         GIOCondition         condition,
69
                                         gpointer         data);
70
71
72
void session_init(Session *session)
73
{
74
        session->type = SESSION_UNKNOWN;
75
        session->sock = NULL;
76
        session->server = NULL;
77
        session->port = 0;
78
#if USE_SSL
79
        session->ssl_type = SSL_NONE;
80
#endif
81
        session->nonblocking = TRUE;
82
        session->state = SESSION_READY;
83
        session->last_access_time = time(NULL);
84
85
        g_get_current_time(&session->tv_prev);
86
87
        session->conn_id = 0;
88
89
        session->io_tag = 0;
90
91
        session->read_buf_p = session->read_buf;
92
        session->read_buf_len = 0;
93
94
        session->read_msg_buf = g_string_sized_new(1024);
95
        session->read_data_buf = g_byte_array_new();
96
        session->read_data_terminator = NULL;
97
98
        session->read_data_fp = NULL;
99
        session->read_data_pos = 0;
100
101
        session->preread_len = 0;
102
103
        session->write_buf = NULL;
104
        session->write_buf_p = NULL;
105
        session->write_buf_len = 0;
106
107
        session->write_data_fp = NULL;
108
        session->write_data_pos = 0;
109
        session->write_data_len = 0;
110
111
        session->timeout_tag = 0;
112
        session->timeout_interval = 0;
113
114
        session->ping_tag = 0;
115
116
        session->data = NULL;
117
}
118
119
gint session_connect(Session *session, const gchar *server, gushort port)
120
{
121
#ifdef G_OS_UNIX
122
        session->server = g_strdup(server);
123
        session->port = port;
124
125
        session->conn_id = sock_connect_async(server, port, session_connect_cb,
126
                                              session);
127
        if (session->conn_id < 0) {
128
                g_warning("can't connect to server.");
129
                session_close(session);
130
                return -1;
131
        }
132
133
        return 0;
134
#else
135
        SockInfo *sock;
136
137
        session->server = g_strdup(server);
138
        session->port = port;
139
140
        sock = sock_connect(server, port);
141
        if (sock == NULL) {
142
                g_warning("can't connect to server.");
143
                session_close(session);
144
                return -1;
145
        }
146
147
        return session_connect_cb(sock, session);
148
#endif
149
}
150
151
static gint session_connect_cb(SockInfo *sock, gpointer data)
152
{
153
        Session *session = SESSION(data);
154
155
        session->conn_id = 0;
156
157
        if (!sock) {
158
                g_warning("can't connect to server.");
159
                session->state = SESSION_ERROR;
160
                return -1;
161
        }
162
163
        session->sock = sock;
164
165
#if USE_SSL
166
        if (session->ssl_type == SSL_TUNNEL) {
167
                sock_set_nonblocking_mode(sock, FALSE);
168
                if (!ssl_init_socket(sock)) {
169
                        g_warning("can't initialize SSL.");
170
                        session->state = SESSION_ERROR;
171
                        return -1;
172
                }
173
        }
174
#endif
175
176
        debug_print("session (%p): connected\n", session);
177
178
        sock_set_nonblocking_mode(sock, session->nonblocking);
179
180
        session->state = SESSION_RECV;
181
        session->io_tag = sock_add_watch(session->sock, G_IO_IN,
182
                                         session_read_msg_cb,
183
                                         session);
184
185
#ifdef G_OS_WIN32
186
        session->ping_tag = g_timeout_add(1000, session_ping_cb, session);
187
#endif
188
189
        return 0;
190
}
191
192
gint session_disconnect(Session *session)
193
{
194
        session_close(session);
195
        return 0;
196
}
197
198
void session_destroy(Session *session)
199
{
200
        g_return_if_fail(session != NULL);
201
        g_return_if_fail(session->destroy != NULL);
202
203
        session_close(session);
204
        session->destroy(session);
205
        g_free(session->server);
206
        g_string_free(session->read_msg_buf, TRUE);
207
        g_byte_array_free(session->read_data_buf, TRUE);
208
        g_free(session->read_data_terminator);
209
        if (session->read_data_fp)
210
                fclose(session->read_data_fp);
211
        g_free(session->write_buf);
212
213
        debug_print("session (%p): destroyed\n", session);
214
215
        g_free(session);
216
}
217
218
gboolean session_is_connected(Session *session)
219
{
220
        return (session->state == SESSION_READY ||
221
                session->state == SESSION_SEND ||
222
                session->state == SESSION_RECV);
223
}
224
225
void session_set_access_time(Session *session)
226
{
227
        session->last_access_time = time(NULL);
228
}
229
230
void session_set_timeout(Session *session, guint interval)
231
{
232
        if (session->timeout_tag > 0)
233
                g_source_remove(session->timeout_tag);
234
235
        session->timeout_interval = interval;
236
        if (interval > 0)
237
                session->timeout_tag =
238
                        g_timeout_add(interval, session_timeout_cb, session);
239
        else
240
                session->timeout_tag = 0;
241
}
242
243
static gboolean session_timeout_cb(gpointer data)
244
{
245
        Session *session = SESSION(data);
246
247
        g_warning("session timeout.\n");
248
249
        if (session->io_tag > 0) {
250
                g_source_remove(session->io_tag);
251
                session->io_tag = 0;
252
        }
253
254
        session->timeout_tag = 0;
255
        session->state = SESSION_TIMEOUT;
256
257
        return FALSE;
258
}
259
260
#ifdef G_OS_WIN32
261
/* hack for state machine freeze problem in GLib >= 2.8.x */
262
static gboolean session_ping_cb(gpointer data)
263
{
264
        Session *session = SESSION(data);
265
        SockInfo *sock = session->sock;
266
267
        if (session->io_tag > 0 && sock && sock->callback) {
268
                GTimeVal tv_cur, tv_result;
269
270
                g_get_current_time(&tv_cur);
271
                tv_result.tv_sec = tv_cur.tv_sec - session->tv_prev.tv_sec;
272
                tv_result.tv_usec = tv_cur.tv_usec - session->tv_prev.tv_usec;
273
                if (tv_result.tv_usec < 0) {
274
                        tv_result.tv_sec--;
275
                        tv_result.tv_usec += G_USEC_PER_SEC;
276
                }
277
                if (tv_result.tv_sec * G_USEC_PER_SEC + tv_result.tv_usec >
278
                    G_USEC_PER_SEC) {
279
                        debug_print("state machine freeze for 1 second detected, forcing dispatch.\n");
280
                        sock->callback(sock, sock->condition, sock->data);
281
                }
282
        }
283
284
        return TRUE;
285
}
286
#endif
287
288
void session_set_recv_message_notify(Session *session,
289
                                     RecvMsgNotify notify_func, gpointer data)
290
{
291
        session->recv_msg_notify = notify_func;
292
        session->recv_msg_notify_data = data;
293
}
294
295
void session_set_recv_data_progressive_notify
296
                                        (Session *session,
297
                                         RecvDataProgressiveNotify notify_func,
298
                                         gpointer data)
299
{
300
        session->recv_data_progressive_notify = notify_func,
301
        session->recv_data_progressive_notify_data = data;
302
}
303
304
void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
305
                                  gpointer data)
306
{
307
        session->recv_data_notify = notify_func;
308
        session->recv_data_notify_data = data;
309
}
310
311
void session_set_send_data_progressive_notify
312
                                        (Session *session,
313
                                         SendDataProgressiveNotify notify_func,
314
                                         gpointer data)
315
{
316
        session->send_data_progressive_notify = notify_func;
317
        session->send_data_progressive_notify_data = data;
318
}
319
320
void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
321
                                  gpointer data)
322
{
323
        session->send_data_notify = notify_func;
324
        session->send_data_notify_data = data;
325
}
326
327
static gint session_close(Session *session)
328
{
329
        g_return_val_if_fail(session != NULL, -1);
330
331
#ifdef G_OS_UNIX
332
        if (session->conn_id > 0) {
333
                sock_connect_async_cancel(session->conn_id);
334
                session->conn_id = 0;
335
                debug_print("session (%p): connection cancelled\n", session);
336
        }
337
#endif
338
339
        session_set_timeout(session, 0);
340
341
#ifdef G_OS_WIN32
342
        if (session->ping_tag > 0) {
343
                g_source_remove(session->ping_tag);
344
                session->ping_tag = 0;
345
        }
346
#endif
347
348
        if (session->io_tag > 0) {
349
                g_source_remove(session->io_tag);
350
                session->io_tag = 0;
351
        }
352
353
        if (session->sock) {
354
                sock_close(session->sock);
355
                session->sock = NULL;
356
                session->state = SESSION_DISCONNECTED;
357
                debug_print("session (%p): closed\n", session);
358
        }
359
360
        return 0;
361
}
362
363
#if USE_SSL
364
gint session_start_tls(Session *session)
365
{
366
        gboolean nb_mode;
367
368
        nb_mode = sock_is_nonblocking_mode(session->sock);
369
370
        sock_set_nonblocking_mode(session->sock, FALSE);
371
372
        if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
373
                g_warning("can't start TLS session.\n");
374
                if (nb_mode)
375
                        sock_set_nonblocking_mode(session->sock, TRUE);
376
                return -1;
377
        }
378
379
        sock_set_nonblocking_mode(session->sock, session->nonblocking);
380
381
        return 0;
382
}
383
#endif
384
385
gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
386
{
387
        gboolean ret;
388
389
        g_return_val_if_fail(session->write_buf == NULL, -1);
390
        g_return_val_if_fail(msg != NULL, -1);
391
        g_return_val_if_fail(msg[0] != '\0', -1);
392
393
        session->state = SESSION_SEND;
394
        session->write_buf = g_strconcat(msg, "\r\n", NULL);
395
        session->write_buf_p = session->write_buf;
396
        session->write_buf_len = strlen(msg) + 2;
397
398
        ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
399
400
        if (ret == TRUE)
401
                session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
402
                                                 session_write_msg_cb, session);
403
        else if (session->state == SESSION_ERROR)
404
                return -1;
405
406
        return 0;
407
}
408
409
gint session_recv_msg(Session *session)
410
{
411
        g_return_val_if_fail(session->read_msg_buf->len == 0, -1);
412
413
        session->state = SESSION_RECV;
414
415
        if (session->read_buf_len > 0)
416
                g_idle_add(session_recv_msg_idle_cb, session);
417
        else
418
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
419
                                                 session_read_msg_cb, session);
420
421
        return 0;
422
}
423
424
static gboolean session_recv_msg_idle_cb(gpointer data)
425
{
426
        Session *session = SESSION(data);
427
        gboolean ret;
428
429
        ret = session_read_msg_cb(session->sock, G_IO_IN, session);
430
431
        if (ret == TRUE)
432
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
433
                                                 session_read_msg_cb, session);
434
435
        return FALSE;
436
}
437
438
gint session_send_data(Session *session, FILE *data_fp, guint size)
439
{
440
        gboolean ret;
441
442
        g_return_val_if_fail(session->write_data_fp == NULL, -1);
443
        g_return_val_if_fail(data_fp != NULL, -1);
444
        g_return_val_if_fail(size != 0, -1);
445
446
        session->state = SESSION_SEND;
447
448
        session->write_data_fp = data_fp;
449
        session->write_data_pos = 0;
450
        session->write_data_len = size;
451
        g_get_current_time(&session->tv_prev);
452
453
        ret = session_write_data_cb(session->sock, G_IO_OUT, session);
454
455
        if (ret == TRUE)
456
                session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
457
                                                 session_write_data_cb,
458
                                                 session);
459
        else if (session->state == SESSION_ERROR)
460
                return -1;
461
462
        return 0;
463
}
464
465
gint session_recv_data(Session *session, guint size, const gchar *terminator)
466
{
467
        g_return_val_if_fail(session->read_data_buf->len == 0, -1);
468
469
        session->state = SESSION_RECV;
470
471
        g_free(session->read_data_terminator);
472
        session->read_data_terminator = g_strdup(terminator);
473
        g_get_current_time(&session->tv_prev);
474
475
        if (session->read_buf_len > 0)
476
                g_idle_add(session_recv_data_idle_cb, session);
477
        else
478
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
479
                                                 session_read_data_cb, session);
480
481
        return 0;
482
}
483
484
static gboolean session_recv_data_idle_cb(gpointer data)
485
{
486
        Session *session = SESSION(data);
487
        gboolean ret;
488
489
        ret = session_read_data_cb(session->sock, G_IO_IN, session);
490
491
        if (ret == TRUE)
492
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
493
                                                 session_read_data_cb, session);
494
495
        return FALSE;
496
}
497
498
gint session_recv_data_as_file(Session *session, guint size,
499
                               const gchar *terminator)
500
{
501
        g_return_val_if_fail(session->read_data_pos == 0, -1);
502
        g_return_val_if_fail(session->read_data_fp == NULL, -1);
503
504
        session->state = SESSION_RECV;
505
506
        g_free(session->read_data_terminator);
507
        session->read_data_terminator = g_strdup(terminator);
508
        g_get_current_time(&session->tv_prev);
509
510
        session->read_data_fp = my_tmpfile();
511
        if (!session->read_data_fp) {
512
                FILE_OP_ERROR("session_recv_data_as_file", "my_tmpfile");
513
                return -1;
514
        }
515
516
        if (session->read_buf_len > 0)
517
                g_idle_add(session_recv_data_as_file_idle_cb, session);
518
        else
519
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
520
                                                 session_read_data_as_file_cb,
521
                                                 session);
522
523
        return 0;
524
}
525
526
static gboolean session_recv_data_as_file_idle_cb(gpointer data)
527
{
528
        Session *session = SESSION(data);
529
        gboolean ret;
530
531
        ret = session_read_data_as_file_cb(session->sock, G_IO_IN, session);
532
533
        if (ret == TRUE)
534
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
535
                                                 session_read_data_as_file_cb,
536
                                                 session);
537
538
        return FALSE;
539
}
540
541
static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
542
                                    gpointer data)
543
{
544
        Session *session = SESSION(data);
545
        gchar buf[SESSION_BUFFSIZE];
546
        gint line_len;
547
        gchar *newline;
548
        gchar *msg;
549
        gint ret;
550
551
        g_return_val_if_fail(condition == G_IO_IN, FALSE);
552
553
        session_set_timeout(session, session->timeout_interval);
554
555
        if (session->read_buf_len == 0) {
556
                gint read_len;
557
558
                read_len = sock_read(session->sock, session->read_buf,
559
                                     SESSION_BUFFSIZE - 1);
560
561
                if (read_len == 0) {
562
                        g_warning("sock_read: received EOF\n");
563
                        session->state = SESSION_EOF;
564
                        return FALSE;
565
                }
566
567
                if (read_len < 0) {
568
                        switch (errno) {
569
                        case EAGAIN:
570
                                return TRUE;
571
                        default:
572
                                g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno));
573
                                session->state = SESSION_ERROR;
574
                                return FALSE;
575
                        }
576
                }
577
578
                session->read_buf_len = read_len;
579
        }
580
581
        if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
582
                != NULL)
583
                line_len = newline - session->read_buf_p + 1;
584
        else
585
                line_len = session->read_buf_len;
586
587
        if (line_len == 0)
588
                return TRUE;
589
590
        memcpy(buf, session->read_buf_p, line_len);
591
        buf[line_len] = '\0';
592
593
        g_string_append(session->read_msg_buf, buf);
594
595
        session->read_buf_len -= line_len;
596
        if (session->read_buf_len == 0)
597
                session->read_buf_p = session->read_buf;
598
        else
599
                session->read_buf_p += line_len;
600
601
        /* incomplete read */
602
        if (buf[line_len - 1] != '\n')
603
                return TRUE;
604
605
        /* complete */
606
        if (session->io_tag > 0) {
607
                g_source_remove(session->io_tag);
608
                session->io_tag = 0;
609
        }
610
611
        /* callback */
612
        msg = g_strdup(session->read_msg_buf->str);
613
        strretchomp(msg);
614
        g_string_truncate(session->read_msg_buf, 0);
615
616
        ret = session->recv_msg(session, msg);
617
        session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
618
619
        g_free(msg);
620
621
        if (ret < 0)
622
                session->state = SESSION_ERROR;
623
624
        return FALSE;
625
}
626
627
static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
628
                                     gpointer data)
629
{
630
        Session *session = SESSION(data);
631
        GByteArray *data_buf;
632
        gint terminator_len;
633
        gboolean complete = FALSE;
634
        guint data_len;
635
        gint ret;
636
637
        g_return_val_if_fail(condition == G_IO_IN, FALSE);
638
639
        session_set_timeout(session, session->timeout_interval);
640
641
        if (session->read_buf_len == 0) {
642
                gint read_len;
643
644
                read_len = sock_read(session->sock, session->read_buf,
645
                                     SESSION_BUFFSIZE);
646
647
                if (read_len == 0) {
648
                        g_warning("sock_read: received EOF\n");
649
                        session->state = SESSION_EOF;
650
                        return FALSE;
651
                }
652
653
                if (read_len < 0) {
654
                        switch (errno) {
655
                        case EAGAIN:
656
                                return TRUE;
657
                        default:
658
                                g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno));
659
                                session->state = SESSION_ERROR;
660
                                return FALSE;
661
                        }
662
                }
663
664
                session->read_buf_len = read_len;
665
        }
666
667
        data_buf = session->read_data_buf;
668
        terminator_len = strlen(session->read_data_terminator);
669
670
        if (session->read_buf_len == 0)
671
                return TRUE;
672
673
        g_byte_array_append(data_buf, (guchar *)session->read_buf_p,
674
                            session->read_buf_len);
675
676
        session->read_buf_len = 0;
677
        session->read_buf_p = session->read_buf;
678
679
        /* check if data is terminated */
680
        if (data_buf->len >= terminator_len) {
681
                if (memcmp(data_buf->data, session->read_data_terminator,
682
                           terminator_len) == 0)
683
                        complete = TRUE;
684
                else if (data_buf->len >= terminator_len + 2 &&
685
                         memcmp(data_buf->data + data_buf->len -
686
                                (terminator_len + 2), "\r\n", 2) == 0 &&
687
                         memcmp(data_buf->data + data_buf->len -
688
                                terminator_len, session->read_data_terminator,
689
                                terminator_len) == 0)
690
                        complete = TRUE;
691
        }
692
693
        /* incomplete read */
694
        if (!complete) {
695
                GTimeVal tv_cur;
696
697
                g_get_current_time(&tv_cur);
698
                if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
699
                    tv_cur.tv_usec - session->tv_prev.tv_usec >
700
                    UI_REFRESH_INTERVAL) {
701
                        session->recv_data_progressive_notify
702
                                (session, data_buf->len, 0,
703
                                 session->recv_data_progressive_notify_data);
704
                        g_get_current_time(&session->tv_prev);
705
                }
706
                return TRUE;
707
        }
708
709
        /* complete */
710
        if (session->io_tag > 0) {
711
                g_source_remove(session->io_tag);
712
                session->io_tag = 0;
713
        }
714
715
        data_len = data_buf->len - terminator_len;
716
717
        /* callback */
718
        ret = session->recv_data_finished(session, (guchar *)data_buf->data,
719
                                          data_len);
720
721
        g_byte_array_set_size(data_buf, 0);
722
723
        session->recv_data_notify(session, data_len,
724
                                  session->recv_data_notify_data);
725
726
        if (ret < 0)
727
                session->state = SESSION_ERROR;
728
729
        return FALSE;
730
}
731
732
#define READ_BUF_LEFT() \
733
        (SESSION_BUFFSIZE - (session->read_buf_p - session->read_buf) - \
734
         session->read_buf_len)
735
#define PREREAD_SIZE        8
736
737
static gboolean session_read_data_as_file_cb(SockInfo *source,
738
                                             GIOCondition condition,
739
                                             gpointer data)
740
{
741
        Session *session = SESSION(data);
742
        gint terminator_len;
743
        gchar *data_begin_p;
744
        gint buf_data_len;
745
        gboolean complete = FALSE;
746
        gint read_len;
747
        gint write_len;
748
        gint ret;
749
750
        g_return_val_if_fail(condition == G_IO_IN, FALSE);
751
752
        session_set_timeout(session, session->timeout_interval);
753
754
        if (session->read_buf_len == 0) {
755
                read_len = sock_read(session->sock, session->read_buf_p,
756
                                     READ_BUF_LEFT());
757
758
                if (read_len == 0) {
759
                        g_warning("sock_read: received EOF\n");
760
                        session->state = SESSION_EOF;
761
                        return FALSE;
762
                }
763
764
                if (read_len < 0) {
765
                        switch (errno) {
766
                        case EAGAIN:
767
                                return TRUE;
768
                        default:
769
                                g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno));
770
                                session->state = SESSION_ERROR;
771
                                return FALSE;
772
                        }
773
                }
774
775
                session->read_buf_len = read_len;
776
        }
777
778
        terminator_len = strlen(session->read_data_terminator);
779
780
        if (session->read_buf_len == 0)
781
                return TRUE;
782
783
        /* +---------------buf_data_len---------------+
784
         * +--preread_len--+-------read_buf_len-------+
785
         * +---------------+--------------------------+-------------------+ *
786
         * ^data_begin_p   ^read_buf_p
787
         * ^read_buf
788
         */
789
790
        data_begin_p = session->read_buf_p - session->preread_len;
791
        buf_data_len = session->preread_len + session->read_buf_len;
792
793
        /* check if data is terminated */
794
        if (buf_data_len >= terminator_len) {
795
                if (session->read_data_pos == 0 &&
796
                    buf_data_len == terminator_len &&
797
                    memcmp(data_begin_p, session->read_data_terminator,
798
                           terminator_len) == 0)
799
                        complete = TRUE;
800
                else if (buf_data_len >= terminator_len + 2 &&
801
                         memcmp(data_begin_p + buf_data_len -
802
                                (terminator_len + 2), "\r\n", 2) == 0 &&
803
                         memcmp(data_begin_p + buf_data_len -
804
                                terminator_len, session->read_data_terminator,
805
                                terminator_len) == 0)
806
                        complete = TRUE;
807
        }
808
809
        /* incomplete read */
810
        if (!complete) {
811
                GTimeVal tv_cur;
812
813
                if (buf_data_len <= PREREAD_SIZE) {
814
                        if (data_begin_p > session->read_buf) {
815
                                g_memmove(session->read_buf, data_begin_p,
816
                                          buf_data_len);
817
                                data_begin_p = session->read_buf;
818
                                session->read_buf_p = session->read_buf +
819
                                        session->preread_len;
820
                        }
821
                        session->read_buf_p += session->read_buf_len;
822
                        session->preread_len = buf_data_len;
823
                        session->read_buf_len = 0;
824
                        return TRUE;
825
                }
826
827
                if (READ_BUF_LEFT() >= (SESSION_BUFFSIZE / 2)) {
828
                        session->read_buf_p += session->read_buf_len;
829
                        session->preread_len = buf_data_len;
830
                        session->read_buf_len = 0;
831
                        return TRUE;
832
                }
833
834
                write_len = buf_data_len - PREREAD_SIZE;
835
                if (fwrite(data_begin_p, write_len, 1,
836
                           session->read_data_fp) < 1) {
837
                        g_warning("session_read_data_as_file_cb: "
838
                                  "writing data to file failed\n");
839
                        session->state = SESSION_ERROR;
840
                        return FALSE;
841
                }
842
                session->read_data_pos += write_len;
843
844
                g_memmove(session->read_buf, data_begin_p + write_len,
845
                          PREREAD_SIZE);
846
                session->read_buf_p = session->read_buf + PREREAD_SIZE;
847
                session->preread_len = PREREAD_SIZE;
848
                session->read_buf_len = 0;
849
850
                g_get_current_time(&tv_cur);
851
                if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
852
                    tv_cur.tv_usec - session->tv_prev.tv_usec >
853
                    UI_REFRESH_INTERVAL) {
854
                        session->recv_data_progressive_notify
855
                                (session, session->read_data_pos, 0,
856
                                 session->recv_data_progressive_notify_data);
857
                        g_get_current_time(&session->tv_prev);
858
                }
859
860
                return TRUE;
861
        }
862
863
        /* complete */
864
        if (session->io_tag > 0) {
865
                g_source_remove(session->io_tag);
866
                session->io_tag = 0;
867
        }
868
869
        write_len = buf_data_len - terminator_len;
870
        if (write_len > 0 && fwrite(data_begin_p, write_len, 1,
871
                                    session->read_data_fp) < 1) {
872
                g_warning("session_read_data_as_file_cb: "
873
                          "writing data to file failed\n");
874
                session->state = SESSION_ERROR;
875
                return FALSE;
876
        }
877
        session->read_data_pos += write_len;
878
879
        if (fflush(session->read_data_fp) == EOF) {
880
                perror("fflush");
881
                g_warning("session_read_data_as_file_cb: "
882
                          "writing data to file failed\n");
883
                session->state = SESSION_ERROR;
884
                return FALSE;
885
        }
886
        rewind(session->read_data_fp);
887
888
        session->preread_len = 0;
889
        session->read_buf_len = 0;
890
        session->read_buf_p = session->read_buf;
891
892
        /* callback */
893
        ret = session->recv_data_as_file_finished
894
                (session, session->read_data_fp, session->read_data_pos);
895
896
        fclose(session->read_data_fp);
897
        session->read_data_fp = NULL;
898
899
        session->recv_data_notify(session, session->read_data_pos,
900
                                  session->recv_data_notify_data);
901
902
        session->read_data_pos = 0;
903
904
        if (ret < 0)
905
                session->state = SESSION_ERROR;
906
907
        return FALSE;
908
}
909
910
static gint session_write_buf(Session *session)
911
{
912
        gint write_len;
913
        gint to_write_len;
914
915
        g_return_val_if_fail(session->write_buf != NULL, -1);
916
        g_return_val_if_fail(session->write_buf_p != NULL, -1);
917
        g_return_val_if_fail(session->write_buf_len > 0, -1);
918
919
        to_write_len = session->write_buf_len -
920
                (session->write_buf_p - session->write_buf);
921
        to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
922
923
        write_len = sock_write(session->sock, session->write_buf_p,
924
                               to_write_len);
925
926
        if (write_len < 0) {
927
                switch (errno) {
928
                case EAGAIN:
929
                        write_len = 0;
930
                        break;
931
                default:
932
                        g_warning("sock_write: %s\n", g_strerror(errno));
933
                        session->state = SESSION_ERROR;
934
                        return -1;
935
                }
936
        }
937
938
        /* incomplete write */
939
        if (session->write_buf_p - session->write_buf + write_len <
940
            session->write_buf_len) {
941
                session->write_buf_p += write_len;
942
                return 1;
943
        }
944
945
        g_free(session->write_buf);
946
        session->write_buf = NULL;
947
        session->write_buf_p = NULL;
948
        session->write_buf_len = 0;
949
950
        return 0;
951
}
952
953
static gint session_write_data(Session *session)
954
{
955
        gchar buf[SESSION_BUFFSIZE];
956
        gint write_len;
957
        gint to_write_len;
958
959
        g_return_val_if_fail(session->write_data_fp != NULL, -1);
960
        g_return_val_if_fail(session->write_data_pos >= 0, -1);
961
        g_return_val_if_fail(session->write_data_len > 0, -1);
962
963
        to_write_len = session->write_data_len - session->write_data_pos;
964
        to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
965
        if (fread(buf, to_write_len, 1, session->write_data_fp) < 1) {
966
                g_warning("session_write_data: reading data from file failed\n");
967
                session->state = SESSION_ERROR;
968
                return -1;
969
        }
970
971
        write_len = sock_write(session->sock, buf, to_write_len);
972
973
        if (write_len < 0) {
974
                switch (errno) {
975
                case EAGAIN:
976
                        write_len = 0;
977
                        break;
978
                default:
979
                        g_warning("sock_write: %s\n", g_strerror(errno));
980
                        session->state = SESSION_ERROR;
981
                        return -1;
982
                }
983
        }
984
985
        /* incomplete write */
986
        if (session->write_data_pos + write_len < session->write_data_len) {
987
                session->write_data_pos += write_len;
988
                if (write_len < to_write_len) {
989
                        if (fseek(session->write_data_fp,
990
                                  session->write_data_pos, SEEK_SET) < 0) {
991
                                g_warning("session_write_data: file seek failed\n");
992
                                session->state = SESSION_ERROR;
993
                                return -1;
994
                        }
995
                }
996
                return 1;
997
        }
998
999
        session->write_data_fp = NULL;
1000
        session->write_data_pos = 0;
1001
        session->write_data_len = 0;
1002
1003
        return 0;
1004
}
1005
1006
static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
1007
                                     gpointer data)
1008
{
1009
        Session *session = SESSION(data);
1010
        gint ret;
1011
1012
        g_return_val_if_fail(condition == G_IO_OUT, FALSE);
1013
        g_return_val_if_fail(session->write_buf != NULL, FALSE);
1014
        g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
1015
        g_return_val_if_fail(session->write_buf_len > 0, FALSE);
1016
1017
        ret = session_write_buf(session);
1018
1019
        if (ret < 0) {
1020
                session->state = SESSION_ERROR;
1021
                return FALSE;
1022
        } else if (ret > 0)
1023
                return TRUE;
1024
1025
        if (session->io_tag > 0) {
1026
                g_source_remove(session->io_tag);
1027
                session->io_tag = 0;
1028
        }
1029
1030
        session_recv_msg(session);
1031
1032
        return FALSE;
1033
}
1034
1035
static gboolean session_write_data_cb(SockInfo *source,
1036
                                      GIOCondition condition, gpointer data)
1037
{
1038
        Session *session = SESSION(data);
1039
        guint write_data_len;
1040
        gint ret;
1041
1042
        g_return_val_if_fail(condition == G_IO_OUT, FALSE);
1043
        g_return_val_if_fail(session->write_data_fp != NULL, FALSE);
1044
        g_return_val_if_fail(session->write_data_pos >= 0, FALSE);
1045
        g_return_val_if_fail(session->write_data_len > 0, FALSE);
1046
1047
        write_data_len = session->write_data_len;
1048
1049
        ret = session_write_data(session);
1050
1051
        if (ret < 0) {
1052
                session->state = SESSION_ERROR;
1053
                return FALSE;
1054
        } else if (ret > 0) {
1055
                GTimeVal tv_cur;
1056
1057
                g_get_current_time(&tv_cur);
1058
                if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
1059
                    tv_cur.tv_usec - session->tv_prev.tv_usec >
1060
                    UI_REFRESH_INTERVAL) {
1061
                        session_set_timeout(session, session->timeout_interval);
1062
                        session->send_data_progressive_notify
1063
                                (session,
1064
                                 session->write_data_pos, write_data_len,
1065
                                 session->send_data_progressive_notify_data);
1066
                        g_get_current_time(&session->tv_prev);
1067
                }
1068
                return TRUE;
1069
        }
1070
1071
        if (session->io_tag > 0) {
1072
                g_source_remove(session->io_tag);
1073
                session->io_tag = 0;
1074
        }
1075
1076
        /* callback */
1077
        ret = session->send_data_finished(session, write_data_len);
1078
        session->send_data_notify(session, write_data_len,
1079
                                  session->send_data_notify_data);
1080
1081
        return FALSE;
1082
}