Statistics
| Revision:

root / src / session.c @ 1

History | View | Annotate | Download (17 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27

    
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <sys/types.h>
32
#include <unistd.h>
33
#include <sys/signal.h>
34
#include <sys/wait.h>
35
#include <sys/time.h>
36
#include <time.h>
37
#include <errno.h>
38

    
39
#include "session.h"
40
#include "utils.h"
41

    
42
static gint session_connect_cb                (SockInfo        *sock,
43
                                         gpointer         data);
44
static gint session_close                (Session        *session);
45

    
46
static gboolean session_timeout_cb        (gpointer         data);
47

    
48
static gboolean session_recv_msg_idle_cb        (gpointer         data);
49
static gboolean session_recv_data_idle_cb        (gpointer         data);
50

    
51
static gboolean session_read_msg_cb        (SockInfo        *source,
52
                                         GIOCondition         condition,
53
                                         gpointer         data);
54
static gboolean session_read_data_cb        (SockInfo        *source,
55
                                         GIOCondition         condition,
56
                                         gpointer         data);
57
static gboolean session_write_msg_cb        (SockInfo        *source,
58
                                         GIOCondition         condition,
59
                                         gpointer         data);
60
static gboolean session_write_data_cb        (SockInfo        *source,
61
                                         GIOCondition         condition,
62
                                         gpointer         data);
63

    
64

    
65
void session_init(Session *session)
66
{
67
        session->type = SESSION_UNKNOWN;
68
        session->sock = NULL;
69
        session->server = NULL;
70
        session->port = 0;
71
#if USE_SSL
72
        session->ssl_type = SSL_NONE;
73
#endif
74
        session->nonblocking = TRUE;
75
        session->state = SESSION_READY;
76
        session->last_access_time = time(NULL);
77

    
78
        gettimeofday(&session->tv_prev, NULL);
79

    
80
        session->conn_id = 0;
81

    
82
        session->io_tag = 0;
83

    
84
        session->read_buf_p = session->read_buf;
85
        session->read_buf_len = 0;
86

    
87
        session->read_msg_buf = g_string_sized_new(1024);
88
        session->read_data_buf = g_byte_array_new();
89

    
90
        session->write_buf = NULL;
91
        session->write_buf_p = NULL;
92
        session->write_buf_len = 0;
93

    
94
        session->timeout_tag = 0;
95
        session->timeout_interval = 0;
96

    
97
        session->data = NULL;
98
}
99

    
100
gint session_connect(Session *session, const gchar *server, gushort port)
101
{
102
        session->server = g_strdup(server);
103
        session->port = port;
104

    
105
        session->conn_id = sock_connect_async(server, port, session_connect_cb,
106
                                              session);
107
        if (session->conn_id < 0) {
108
                g_warning("can't connect to server.");
109
                session_close(session);
110
                return -1;
111
        }
112

    
113
        return 0;
114
}
115

    
116
static gint session_connect_cb(SockInfo *sock, gpointer data)
117
{
118
        Session *session = SESSION(data);
119

    
120
        session->conn_id = 0;
121

    
122
        if (!sock) {
123
                g_warning("can't connect to server.");
124
                session->state = SESSION_ERROR;
125
                return -1;
126
        }
127

    
128
        session->sock = sock;
129

    
130
#if USE_SSL
131
        if (session->ssl_type == SSL_TUNNEL) {
132
                sock_set_nonblocking_mode(sock, FALSE);
133
                if (!ssl_init_socket(sock)) {
134
                        g_warning("can't initialize SSL.");
135
                        session->state = SESSION_ERROR;
136
                        return -1;
137
                }
138
        }
139
#endif
140

    
141
        sock_set_nonblocking_mode(sock, session->nonblocking);
142

    
143
        debug_print("session (%p): connected\n", session);
144

    
145
        session->state = SESSION_RECV;
146
        session->io_tag = sock_add_watch(session->sock, G_IO_IN,
147
                                         session_read_msg_cb,
148
                                         session);
149

    
150
        return 0;
151
}
152

    
153
gint session_disconnect(Session *session)
154
{
155
        session_close(session);
156
        return 0;
157
}
158

    
159
void session_destroy(Session *session)
160
{
161
        g_return_if_fail(session != NULL);
162
        g_return_if_fail(session->destroy != NULL);
163

    
164
        session_close(session);
165
        session->destroy(session);
166
        g_free(session->server);
167
        g_string_free(session->read_msg_buf, TRUE);
168
        g_byte_array_free(session->read_data_buf, TRUE);
169
        g_free(session->read_data_terminator);
170
        g_free(session->write_buf);
171

    
172
        debug_print("session (%p): destroyed\n", session);
173

    
174
        g_free(session);
175
}
176

    
177
gboolean session_is_connected(Session *session)
178
{
179
        return (session->state == SESSION_READY ||
180
                session->state == SESSION_SEND ||
181
                session->state == SESSION_RECV);
182
}
183

    
184
void session_set_access_time(Session *session)
185
{
186
        session->last_access_time = time(NULL);
187
}
188

    
189
void session_set_timeout(Session *session, guint interval)
190
{
191
        if (session->timeout_tag > 0)
192
                g_source_remove(session->timeout_tag);
193

    
194
        session->timeout_interval = interval;
195
        if (interval > 0)
196
                session->timeout_tag =
197
                        g_timeout_add(interval, session_timeout_cb, session);
198
        else
199
                session->timeout_tag = 0;
200
}
201

    
202
static gboolean session_timeout_cb(gpointer data)
203
{
204
        Session *session = SESSION(data);
205

    
206
        g_warning("session timeout.\n");
207

    
208
        if (session->io_tag > 0) {
209
                g_source_remove(session->io_tag);
210
                session->io_tag = 0;
211
        }
212

    
213
        session->timeout_tag = 0;
214
        session->state = SESSION_TIMEOUT;
215

    
216
        return FALSE;
217
}
218

    
219
void session_set_recv_message_notify(Session *session,
220
                                     RecvMsgNotify notify_func, gpointer data)
221
{
222
        session->recv_msg_notify = notify_func;
223
        session->recv_msg_notify_data = data;
224
}
225

    
226
void session_set_recv_data_progressive_notify
227
                                        (Session *session,
228
                                         RecvDataProgressiveNotify notify_func,
229
                                         gpointer data)
230
{
231
        session->recv_data_progressive_notify = notify_func,
232
        session->recv_data_progressive_notify_data = data;
233
}
234

    
235
void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
236
                                  gpointer data)
237
{
238
        session->recv_data_notify = notify_func;
239
        session->recv_data_notify_data = data;
240
}
241

    
242
void session_set_send_data_progressive_notify
243
                                        (Session *session,
244
                                         SendDataProgressiveNotify notify_func,
245
                                         gpointer data)
246
{
247
        session->send_data_progressive_notify = notify_func;
248
        session->send_data_progressive_notify_data = data;
249
}
250

    
251
void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
252
                                  gpointer data)
253
{
254
        session->send_data_notify = notify_func;
255
        session->send_data_notify_data = data;
256
}
257

    
258
static gint session_close(Session *session)
259
{
260
        g_return_val_if_fail(session != NULL, -1);
261

    
262
        if (session->conn_id > 0) {
263
                sock_connect_async_cancel(session->conn_id);
264
                session->conn_id = 0;
265
                debug_print("session (%p): connection cancelled\n", session);
266
        }
267

    
268
        session_set_timeout(session, 0);
269

    
270
        if (session->io_tag > 0) {
271
                g_source_remove(session->io_tag);
272
                session->io_tag = 0;
273
        }
274

    
275
        if (session->sock) {
276
                sock_close(session->sock);
277
                session->sock = NULL;
278
                session->state = SESSION_DISCONNECTED;
279
                debug_print("session (%p): closed\n", session);
280
        }
281

    
282
        return 0;
283
}
284

    
285
#if USE_SSL
286
gint session_start_tls(Session *session)
287
{
288
        gboolean nb_mode;
289

    
290
        nb_mode = sock_is_nonblocking_mode(session->sock);
291

    
292
        if (nb_mode)
293
                sock_set_nonblocking_mode(session->sock, FALSE);
294

    
295
        if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
296
                g_warning("can't start TLS session.\n");
297
                if (nb_mode)
298
                        sock_set_nonblocking_mode(session->sock, TRUE);
299
                return -1;
300
        }
301

    
302
        if (nb_mode)
303
                sock_set_nonblocking_mode(session->sock, session->nonblocking);
304

    
305
        return 0;
306
}
307
#endif
308

    
309
gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
310
{
311
        gboolean ret;
312

    
313
        g_return_val_if_fail(session->write_buf == NULL, -1);
314
        g_return_val_if_fail(msg != NULL, -1);
315
        g_return_val_if_fail(msg[0] != '\0', -1);
316

    
317
        session->state = SESSION_SEND;
318
        session->write_buf = g_strconcat(msg, "\r\n", NULL);
319
        session->write_buf_p = session->write_buf;
320
        session->write_buf_len = strlen(msg) + 2;
321

    
322
        ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
323

    
324
        if (ret == TRUE)
325
                session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
326
                                                 session_write_msg_cb, session);
327
        else if (session->state == SESSION_ERROR)
328
                return -1;
329

    
330
        return 0;
331
}
332

    
333
gint session_recv_msg(Session *session)
334
{
335
        g_return_val_if_fail(session->read_msg_buf->len == 0, -1);
336

    
337
        session->state = SESSION_RECV;
338

    
339
        if (session->read_buf_len > 0)
340
                g_idle_add(session_recv_msg_idle_cb, session);
341
        else
342
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
343
                                                 session_read_msg_cb, session);
344

    
345
        return 0;
346
}
347

    
348
static gboolean session_recv_msg_idle_cb(gpointer data)
349
{
350
        Session *session = SESSION(data);
351
        gboolean ret;
352

    
353
        ret = session_read_msg_cb(session->sock, G_IO_IN, session);
354

    
355
        if (ret == TRUE)
356
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
357
                                                 session_read_msg_cb, session);
358

    
359
        return FALSE;
360
}
361

    
362
gint session_send_data(Session *session, const guchar *data, guint size)
363
{
364
        gboolean ret;
365

    
366
        g_return_val_if_fail(session->write_buf == NULL, -1);
367
        g_return_val_if_fail(data != NULL, -1);
368
        g_return_val_if_fail(size != 0, -1);
369

    
370
        session->state = SESSION_SEND;
371

    
372
        session->write_buf = g_malloc(size);
373
        session->write_buf_p = session->write_buf;
374
        memcpy(session->write_buf, data, size);
375
        session->write_buf_len = size;
376
        gettimeofday(&session->tv_prev, NULL);
377

    
378
        ret = session_write_data_cb(session->sock, G_IO_OUT, session);
379

    
380
        if (ret == TRUE)
381
                session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
382
                                                 session_write_data_cb,
383
                                                 session);
384
        else if (session->state == SESSION_ERROR)
385
                return -1;
386

    
387
        return 0;
388
}
389

    
390
gint session_recv_data(Session *session, guint size, const gchar *terminator)
391
{
392
        g_return_val_if_fail(session->read_data_buf->len == 0, -1);
393

    
394
        session->state = SESSION_RECV;
395

    
396
        g_free(session->read_data_terminator);
397
        session->read_data_terminator = g_strdup(terminator);
398
        gettimeofday(&session->tv_prev, NULL);
399

    
400
        if (session->read_buf_len > 0)
401
                g_idle_add(session_recv_data_idle_cb, session);
402
        else
403
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
404
                                                 session_read_data_cb, session);
405

    
406
        return 0;
407
}
408

    
409
static gboolean session_recv_data_idle_cb(gpointer data)
410
{
411
        Session *session = SESSION(data);
412
        gboolean ret;
413

    
414
        ret = session_read_data_cb(session->sock, G_IO_IN, session);
415

    
416
        if (ret == TRUE)
417
                session->io_tag = sock_add_watch(session->sock, G_IO_IN,
418
                                                 session_read_data_cb, session);
419

    
420
        return FALSE;
421
}
422

    
423
static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
424
                                    gpointer data)
425
{
426
        Session *session = SESSION(data);
427
        gchar buf[SESSION_BUFFSIZE];
428
        gint line_len;
429
        gchar *newline;
430
        gchar *msg;
431
        gint ret;
432

    
433
        g_return_val_if_fail(condition == G_IO_IN, FALSE);
434

    
435
        session_set_timeout(session, session->timeout_interval);
436

    
437
        if (session->read_buf_len == 0) {
438
                gint read_len;
439

    
440
                read_len = sock_read(session->sock, session->read_buf,
441
                                     SESSION_BUFFSIZE - 1);
442

    
443
                if (read_len == 0) {
444
                        g_warning("sock_read: received EOF\n");
445
                        session->state = SESSION_EOF;
446
                        return FALSE;
447
                }
448

    
449
                if (read_len < 0) {
450
                        switch (errno) {
451
                        case EAGAIN:
452
                                return TRUE;
453
                        default:
454
                                g_warning("sock_read: %s\n", g_strerror(errno));
455
                                session->state = SESSION_ERROR;
456
                                return FALSE;
457
                        }
458
                }
459

    
460
                session->read_buf_len = read_len;
461
        }
462

    
463
        if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
464
                != NULL)
465
                line_len = newline - session->read_buf_p + 1;
466
        else
467
                line_len = session->read_buf_len;
468

    
469
        if (line_len == 0)
470
                return TRUE;
471

    
472
        memcpy(buf, session->read_buf_p, line_len);
473
        buf[line_len] = '\0';
474

    
475
        g_string_append(session->read_msg_buf, buf);
476

    
477
        session->read_buf_len -= line_len;
478
        if (session->read_buf_len == 0)
479
                session->read_buf_p = session->read_buf;
480
        else
481
                session->read_buf_p += line_len;
482

    
483
        /* incomplete read */
484
        if (buf[line_len - 1] != '\n')
485
                return TRUE;
486

    
487
        /* complete */
488
        if (session->io_tag > 0) {
489
                g_source_remove(session->io_tag);
490
                session->io_tag = 0;
491
        }
492

    
493
        /* callback */
494
        msg = g_strdup(session->read_msg_buf->str);
495
        strretchomp(msg);
496
        g_string_truncate(session->read_msg_buf, 0);
497

    
498
        ret = session->recv_msg(session, msg);
499
        session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
500

    
501
        g_free(msg);
502

    
503
        if (ret < 0)
504
                session->state = SESSION_ERROR;
505

    
506
        return FALSE;
507
}
508

    
509
static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
510
                                     gpointer data)
511
{
512
        Session *session = SESSION(data);
513
        GByteArray *data_buf;
514
        gint terminator_len;
515
        gboolean complete = FALSE;
516
        guint data_len;
517
        gint ret;
518

    
519
        g_return_val_if_fail(condition == G_IO_IN, FALSE);
520

    
521
        session_set_timeout(session, session->timeout_interval);
522

    
523
        if (session->read_buf_len == 0) {
524
                gint read_len;
525

    
526
                read_len = sock_read(session->sock, session->read_buf,
527
                                     SESSION_BUFFSIZE);
528

    
529
                if (read_len == 0) {
530
                        g_warning("sock_read: received EOF\n");
531
                        session->state = SESSION_EOF;
532
                        return FALSE;
533
                }
534

    
535
                if (read_len < 0) {
536
                        switch (errno) {
537
                        case EAGAIN:
538
                                return TRUE;
539
                        default:
540
                                g_warning("sock_read: %s\n", g_strerror(errno));
541
                                session->state = SESSION_ERROR;
542
                                return FALSE;
543
                        }
544
                }
545

    
546
                session->read_buf_len = read_len;
547
        }
548

    
549
        data_buf = session->read_data_buf;
550
        terminator_len = strlen(session->read_data_terminator);
551

    
552
        if (session->read_buf_len == 0)
553
                return TRUE;
554

    
555
        g_byte_array_append(data_buf, session->read_buf_p,
556
                            session->read_buf_len);
557

    
558
        session->read_buf_len = 0;
559
        session->read_buf_p = session->read_buf;
560

    
561
        /* check if data is terminated */
562
        if (data_buf->len >= terminator_len) {
563
                if (memcmp(data_buf->data, session->read_data_terminator,
564
                           terminator_len) == 0)
565
                        complete = TRUE;
566
                else if (data_buf->len >= terminator_len + 2 &&
567
                         memcmp(data_buf->data + data_buf->len -
568
                                (terminator_len + 2), "\r\n", 2) == 0 &&
569
                         memcmp(data_buf->data + data_buf->len -
570
                                terminator_len, session->read_data_terminator,
571
                                terminator_len) == 0)
572
                        complete = TRUE;
573
        }
574

    
575
        /* incomplete read */
576
        if (!complete) {
577
                struct timeval tv_cur;
578

    
579
                gettimeofday(&tv_cur, NULL);
580
                if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
581
                    tv_cur.tv_usec - session->tv_prev.tv_usec >
582
                    UI_REFRESH_INTERVAL) {
583
                        session->recv_data_progressive_notify
584
                                (session, data_buf->len, 0,
585
                                 session->recv_data_progressive_notify_data);
586
                        gettimeofday(&session->tv_prev, NULL);
587
                }
588
                return TRUE;
589
        }
590

    
591
        /* complete */
592
        if (session->io_tag > 0) {
593
                g_source_remove(session->io_tag);
594
                session->io_tag = 0;
595
        }
596

    
597
        data_len = data_buf->len - terminator_len;
598

    
599
        /* callback */
600
        ret = session->recv_data_finished(session, (gchar *)data_buf->data,
601
                                          data_len);
602

    
603
        g_byte_array_set_size(data_buf, 0);
604

    
605
        session->recv_data_notify(session, data_len,
606
                                  session->recv_data_notify_data);
607

    
608
        if (ret < 0)
609
                session->state = SESSION_ERROR;
610

    
611
        return FALSE;
612
}
613

    
614
static gint session_write_buf(Session *session)
615
{
616
        gint write_len;
617
        gint to_write_len;
618

    
619
        g_return_val_if_fail(session->write_buf != NULL, -1);
620
        g_return_val_if_fail(session->write_buf_p != NULL, -1);
621
        g_return_val_if_fail(session->write_buf_len > 0, -1);
622

    
623
        to_write_len = session->write_buf_len -
624
                (session->write_buf_p - session->write_buf);
625
        to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
626

    
627
        write_len = sock_write(session->sock, session->write_buf_p,
628
                               to_write_len);
629

    
630
        if (write_len < 0) {
631
                switch (errno) {
632
                case EAGAIN:
633
                        write_len = 0;
634
                        break;
635
                default:
636
                        g_warning("sock_write: %s\n", g_strerror(errno));
637
                        session->state = SESSION_ERROR;
638
                        return -1;
639
                }
640
        }
641

    
642
        /* incomplete write */
643
        if (session->write_buf_p - session->write_buf + write_len <
644
            session->write_buf_len) {
645
                session->write_buf_p += write_len;
646
                return 1;
647
        }
648

    
649
        g_free(session->write_buf);
650
        session->write_buf = NULL;
651
        session->write_buf_p = NULL;
652
        session->write_buf_len = 0;
653

    
654
        return 0;
655
}
656

    
657
static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
658
                                     gpointer data)
659
{
660
        Session *session = SESSION(data);
661
        gint ret;
662

    
663
        g_return_val_if_fail(condition == G_IO_OUT, FALSE);
664
        g_return_val_if_fail(session->write_buf != NULL, FALSE);
665
        g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
666
        g_return_val_if_fail(session->write_buf_len > 0, FALSE);
667

    
668
        ret = session_write_buf(session);
669

    
670
        if (ret < 0) {
671
                session->state = SESSION_ERROR;
672
                return FALSE;
673
        } else if (ret > 0)
674
                return TRUE;
675

    
676
        if (session->io_tag > 0) {
677
                g_source_remove(session->io_tag);
678
                session->io_tag = 0;
679
        }
680

    
681
        session_recv_msg(session);
682

    
683
        return FALSE;
684
}
685

    
686
static gboolean session_write_data_cb(SockInfo *source,
687
                                      GIOCondition condition, gpointer data)
688
{
689
        Session *session = SESSION(data);
690
        guint write_buf_len;
691
        gint ret;
692

    
693
        g_return_val_if_fail(condition == G_IO_OUT, FALSE);
694
        g_return_val_if_fail(session->write_buf != NULL, FALSE);
695
        g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
696
        g_return_val_if_fail(session->write_buf_len > 0, FALSE);
697

    
698
        write_buf_len = session->write_buf_len;
699

    
700
        ret = session_write_buf(session);
701

    
702
        if (ret < 0) {
703
                session->state = SESSION_ERROR;
704
                return FALSE;
705
        } else if (ret > 0) {
706
                struct timeval tv_cur;
707

    
708
                gettimeofday(&tv_cur, NULL);
709
                if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
710
                    tv_cur.tv_usec - session->tv_prev.tv_usec >
711
                    UI_REFRESH_INTERVAL) {
712
                        session_set_timeout(session, session->timeout_interval);
713
                        session->send_data_progressive_notify
714
                                (session,
715
                                 session->write_buf_p - session->write_buf,
716
                                 write_buf_len,
717
                                 session->send_data_progressive_notify_data);
718
                        gettimeofday(&session->tv_prev, NULL);
719
                }
720
                return TRUE;
721
        }
722

    
723
        if (session->io_tag > 0) {
724
                g_source_remove(session->io_tag);
725
                session->io_tag = 0;
726
        }
727

    
728
        /* callback */
729
        ret = session->send_data_finished(session, write_buf_len);
730
        session->send_data_notify(session, write_buf_len,
731
                                  session->send_data_notify_data);
732

    
733
        return FALSE;
734
}