* src/folderview.c
[claws.git] / src / socket.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 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 <glib.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <netdb.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <signal.h>
39 #include <setjmp.h>
40 #if HAVE_SYS_SELECT_H
41 #  include <sys/select.h>
42 #endif
43
44 #include "socket.h"
45 #include "utils.h"
46 #if USE_SSL
47 #  include "ssl.h"
48 #endif
49
50 #if USE_GIO
51 #error USE_GIO is currently not supported
52 #endif
53
54 #define BUFFSIZE        8192
55 #define IO_TIMEOUT      60
56
57 static gint sock_connect_with_timeout   (gint                    sock,
58                                          const struct sockaddr  *serv_addr,
59                                          gint                    addrlen,
60                                          guint                   timeout_secs);
61
62 #ifndef INET6
63 static gint sock_connect_by_hostname    (gint            sock,
64                                          const gchar    *hostname,
65                                          gushort         port);
66 #else
67 static gint sock_connect_by_getaddrinfo (const gchar    *hostname,
68                                          gushort         port);
69 #endif
70
71 static SockInfo *sockinfo_from_fd(const gchar *hostname,
72                                   gushort port,
73                                   gint sock);
74
75 gint fd_connect_unix(const gchar *path)
76 {
77         gint sock;
78         struct sockaddr_un addr;
79
80         sock = socket(PF_UNIX, SOCK_STREAM, 0);
81         if (sock < 0) {
82                 perror("sock_connect_unix(): socket");
83                 return -1;
84         }
85
86         memset(&addr, 0, sizeof(addr));
87         addr.sun_family = AF_UNIX;
88         strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
89
90         if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
91                 close(sock);
92                 return -1;
93         }
94
95         return sock;
96 }
97
98 gint fd_open_unix(const gchar *path)
99 {
100         gint sock;
101         struct sockaddr_un addr;
102
103         sock = socket(PF_UNIX, SOCK_STREAM, 0);
104
105         if (sock < 0) {
106                 perror("sock_open_unix(): socket");
107                 return -1;
108         }
109
110         memset(&addr, 0, sizeof(addr));
111         addr.sun_family = AF_UNIX;
112         strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
113
114         if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
115                 perror("bind");
116                 close(sock);
117                 return -1;
118         }
119
120         if (listen(sock, 1) < 0) {
121                 perror("listen");
122                 close(sock);
123                 return -1;              
124         }
125
126         return sock;
127 }
128
129 gint fd_accept(gint sock)
130 {
131         struct sockaddr_in caddr;
132         gint caddr_len;
133
134         caddr_len = sizeof(caddr);
135         return accept(sock, (struct sockaddr *)&caddr, &caddr_len);
136 }
137
138
139 static gint set_nonblocking_mode(gint fd, gboolean nonblock)
140 {
141         gint flags;
142
143         flags = fcntl(fd, F_GETFL, 0);
144         if (flags < 0) {
145                 perror("fcntl");
146                 return -1;
147         }
148
149         if (nonblock)
150                 flags |= O_NONBLOCK;
151         else
152                 flags &= ~O_NONBLOCK;
153
154         return fcntl(fd, F_SETFL, flags);
155 }
156
157 gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock)
158 {
159         g_return_val_if_fail(sock != NULL, -1);
160
161         return set_nonblocking_mode(sock->sock, nonblock);
162 }
163
164
165 static gboolean is_nonblocking_mode(gint fd)
166 {
167         gint flags;
168
169         flags = fcntl(fd, F_GETFL, 0);
170         if (flags < 0) {
171                 perror("fcntl");
172                 return FALSE;
173         }
174
175         return ((flags & O_NONBLOCK) != 0);
176 }
177
178 gboolean sock_is_nonblocking_mode(SockInfo *sock)
179 {
180         g_return_val_if_fail(sock != NULL, FALSE);
181
182         return is_nonblocking_mode(sock->sock);
183 }
184
185 static gint fd_check_io(gint fd, GIOCondition cond)
186 {
187         struct timeval timeout;
188         fd_set fds;
189
190         timeout.tv_sec  = IO_TIMEOUT;
191         timeout.tv_usec = 0;
192
193         FD_ZERO(&fds);
194         FD_SET(fd, &fds);
195
196         if (cond == G_IO_IN) {
197                 select(fd + 1, &fds, NULL, NULL, &timeout);
198         } else {
199                 select(fd + 1, NULL, &fds, NULL, &timeout);
200         }
201
202         if (FD_ISSET(fd, &fds)) {
203                 return 0;
204         } else {
205                 g_warning("Socket IO timeout\n");
206                 return -1;
207         }
208 }
209
210 static sigjmp_buf jmpenv;
211
212 static void timeout_handler(gint sig)
213 {
214         siglongjmp(jmpenv, 1);
215 }
216
217 static gint sock_connect_with_timeout(gint sock,
218                                       const struct sockaddr *serv_addr,
219                                       gint addrlen,
220                                       guint timeout_secs)
221 {
222         gint ret;
223         void (*prev_handler)(gint);
224
225         alarm(0);
226         prev_handler = signal(SIGALRM, timeout_handler);
227         if (sigsetjmp(jmpenv, 1)) {
228                 alarm(0);
229                 signal(SIGALRM, prev_handler);
230                 errno = ETIMEDOUT;
231                 return -1;
232         }
233         alarm(timeout_secs);
234
235         ret = connect(sock, serv_addr, addrlen);
236
237         alarm(0);
238         signal(SIGALRM, prev_handler);
239
240         return ret;
241 }
242
243 struct hostent *my_gethostbyname(const gchar *hostname)
244 {
245         struct hostent *hp;
246         void (*prev_handler)(gint);
247         guint timeout_secs = IO_TIMEOUT;
248
249         alarm(0);
250         prev_handler = signal(SIGALRM, timeout_handler);
251         if (sigsetjmp(jmpenv, 1)) {
252                 alarm(0);
253                 signal(SIGALRM, prev_handler);
254                 fprintf(stderr, "%s: host lookup timed out.\n", hostname);
255                 errno = 0;
256                 return NULL;
257         }
258         alarm(timeout_secs);
259
260         if ((hp = gethostbyname(hostname)) == NULL) {
261                 alarm(0);
262                 signal(SIGALRM, prev_handler);
263                 fprintf(stderr, "%s: unknown host.\n", hostname);
264                 errno = 0;
265                 return NULL;
266         }
267
268         alarm(0);
269         signal(SIGALRM, prev_handler);
270
271         return hp;
272 }
273
274 #ifndef INET6
275 static gint my_inet_aton(const gchar *hostname, struct in_addr *inp)
276 {
277 #if HAVE_INET_ATON
278         return inet_aton(hostname, inp);
279 #else
280 #if HAVE_INET_ADDR
281         guint32 inaddr;
282
283         inaddr = inet_addr(hostname);
284         if (inaddr != -1) {
285                 memcpy(inp, &inaddr, sizeof(inaddr));
286                 return 1;
287         } else
288                 return 0;
289 #else
290         return 0;
291 #endif
292 #endif /* HAVE_INET_ATON */
293 }
294
295 static gint sock_connect_by_hostname(gint sock, const gchar *hostname,
296                                      gushort port)
297 {
298         struct hostent *hp;
299         struct sockaddr_in ad;
300         guint timeout_secs = IO_TIMEOUT;
301
302         memset(&ad, 0, sizeof(ad));
303         ad.sin_family = AF_INET;
304         ad.sin_port = htons(port);
305
306         if (!my_inet_aton(hostname, &ad.sin_addr)) {
307                 if ((hp = my_gethostbyname(hostname)) == NULL) {
308                         fprintf(stderr, "%s: unknown host.\n", hostname);
309                         errno = 0;
310                         return -1;
311                 }
312
313                 if (hp->h_length != 4 && hp->h_length != 8) {
314                         fprintf(stderr, "illegal address length received for host %s\n", hostname);
315                         errno = 0;
316                         return -1;
317                 }
318
319                 memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
320         }
321
322         return sock_connect_with_timeout(sock, (struct sockaddr *)&ad,
323                                          sizeof(ad), timeout_secs);
324 }
325
326 #else /* INET6 */
327 static gint sock_connect_by_getaddrinfo(const gchar *hostname, gushort  port)
328 {
329         gint sock = -1, gai_error;
330         struct addrinfo hints, *res, *ai;
331         guint timeout_secs = IO_TIMEOUT;
332         gchar port_str[6];
333
334         memset(&hints, 0, sizeof(hints));
335         /* hints.ai_flags = AI_CANONNAME; */
336         hints.ai_family = AF_UNSPEC;
337         hints.ai_socktype = SOCK_STREAM;
338         hints.ai_protocol = IPPROTO_TCP;
339
340         /* convert port from integer to string. */
341         g_snprintf(port_str, sizeof(port_str), "%d", port);
342
343         if ((gai_error = getaddrinfo(hostname, port_str, &hints, &res)) != 0) {
344                 fprintf(stderr, "getaddrinfo for %s:%s failed: %s\n",
345                         hostname, port_str, gai_strerror(gai_error));
346                 return -1;
347         }
348
349         for (ai = res; ai != NULL; ai = ai->ai_next) {
350                 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
351                 if (sock < 0)
352                         continue;
353
354                 if (sock_connect_with_timeout
355                         (sock, ai->ai_addr, ai->ai_addrlen, timeout_secs) == 0)
356                         break;
357
358                 close(sock);
359         }
360
361         if (res != NULL)
362                 freeaddrinfo(res);
363
364         if (ai == NULL)
365                 return -1;
366
367         return sock;
368 }
369 #endif /* !INET6 */
370
371
372 /* Open a connection using an external program.  May be useful when
373  * you need to tunnel through a SOCKS or other firewall, or to
374  * establish an IMAP-over-SSH connection. */
375 /* TODO: Recreate this for sock_connect_thread() */
376 SockInfo *sock_connect_cmd(const gchar *hostname, const gchar *tunnelcmd)
377 {
378         gint fd[2];
379         int r;
380                      
381         if ((r = socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) == -1) {
382                 perror("socketpair");
383                 return NULL;
384         }
385         log_message("launching tunnel command \"%s\"\n", tunnelcmd);
386         if (fork() == 0) {
387                 close(fd[0]);
388                 close(0);
389                 close(1);
390                 dup(fd[1]);     /* set onto stdin */
391                 dup(fd[1]);
392                 execlp("/bin/sh", "/bin/sh", "-c", tunnelcmd, NULL);
393         }
394
395         close(fd[1]);
396         return sockinfo_from_fd(hostname, 0, fd[0]);
397 }
398
399
400 SockInfo *sock_connect(const gchar *hostname, gushort port)
401 {
402         gint sock;
403
404 #ifdef INET6
405         if ((sock = sock_connect_by_getaddrinfo(hostname, port)) < 0)
406                 return NULL;
407 #else
408         if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
409                 perror("socket");
410                 return NULL;
411         }
412
413         if (sock_connect_by_hostname(sock, hostname, port) < 0) {
414                 if (errno != 0) perror("connect");
415                 close(sock);
416                 return NULL;
417         }
418 #endif /* INET6 */
419
420         return sockinfo_from_fd(hostname, port, sock);
421 }
422
423
424 static SockInfo *sockinfo_from_fd(const gchar *hostname,
425                                   gushort port,
426                                   gint sock)
427 {
428         SockInfo *sockinfo;
429         
430         sockinfo = g_new0(SockInfo, 1);
431         sockinfo->sock = sock;
432         sockinfo->hostname = g_strdup(hostname);
433         sockinfo->port = port;
434         sockinfo->state = CONN_ESTABLISHED;
435
436         usleep(100000);
437
438         return sockinfo;
439 }
440
441 gint sock_printf(SockInfo *sock, const gchar *format, ...)
442 {
443         va_list args;
444         gchar buf[BUFFSIZE];
445
446         va_start(args, format);
447         g_vsnprintf(buf, sizeof(buf), format, args);
448         va_end(args);
449
450         return sock_write(sock, buf, strlen(buf));
451 }
452
453 gint sock_read(SockInfo *sock, gchar *buf, gint len)
454 {
455         g_return_val_if_fail(sock != NULL, -1);
456
457 #if USE_SSL
458         if (sock->ssl)
459                 return ssl_read(sock->ssl, buf, len);
460 #endif
461         return fd_read(sock->sock, buf, len);
462 }
463
464 gint fd_read(gint fd, gchar *buf, gint len)
465 {
466         if (fd_check_io(fd, G_IO_IN) < 0)
467                 return -1;
468
469         return read(fd, buf, len);
470 }
471
472 #if USE_SSL
473 gint ssl_read(SSL *ssl, gchar *buf, gint len)
474 {
475         return SSL_read(ssl, buf, len);
476 }
477 #endif
478
479 gint sock_write(SockInfo *sock, const gchar *buf, gint len)
480 {
481         g_return_val_if_fail(sock != NULL, -1);
482
483 #if USE_SSL
484         if (sock->ssl)
485                 return ssl_write(sock->ssl, buf, len);
486 #endif
487         return fd_write(sock->sock, buf, len);
488 }
489
490 gint fd_write(gint fd, const gchar *buf, gint len)
491 {
492         gint n, wrlen = 0;
493
494         while (len) {
495                 if (fd_check_io(fd, G_IO_OUT) < 0)
496                         return -1;
497                 signal(SIGPIPE, SIG_IGN);
498                 n = write(fd, buf, len);
499                 if (n <= 0) {
500                         log_error("write on fd%d: %s\n", fd, strerror(errno));
501                         return -1;
502                 }
503                 len -= n;
504                 wrlen += n;
505                 buf += n;
506         }
507
508         return wrlen;
509 }
510
511 #if USE_SSL
512 gint ssl_write(SSL *ssl, const gchar *buf, gint len)
513 {
514         gint n, wrlen = 0;
515
516         while (len) {
517                 n = SSL_write(ssl, buf, len);
518                 if (n <= 0)
519                         return -1;
520                 len -= n;
521                 wrlen += n;
522                 buf += n;
523         }
524
525         return wrlen;
526 }
527 #endif
528
529 gint fd_recv(gint fd, gchar *buf, gint len, gint flags)
530 {
531         if (fd_check_io(fd, G_IO_IN) < 0)
532                 return -1;
533
534         return recv(fd, buf, len, flags);
535 }
536
537 gint fd_gets(gint fd, gchar *buf, gint len)
538 {
539         gchar *newline, *bp = buf;
540         gint n;
541
542         if (--len < 1)
543                 return -1;
544         do {
545                 if ((n = fd_recv(fd, bp, len, MSG_PEEK)) <= 0)
546                         return -1;
547                 if ((newline = memchr(bp, '\n', n)) != NULL)
548                         n = newline - bp + 1;
549                 if ((n = fd_read(fd, bp, n)) < 0)
550                         return -1;
551                 bp += n;
552                 len -= n;
553         } while (!newline && len);
554
555         *bp = '\0';
556         return bp - buf;
557 }
558
559 #if USE_SSL
560 gint ssl_gets(SSL *ssl, gchar *buf, gint len)
561 {
562         gchar *newline, *bp = buf;
563         gint n;
564
565         if (--len < 1)
566                 return -1;
567         do {
568                 if ((n = SSL_peek(ssl, bp, len)) <= 0)
569                         return -1;
570                 if ((newline = memchr(bp, '\n', n)) != NULL)
571                         n = newline - bp + 1;
572                 if ((n = SSL_read(ssl, bp, n)) < 0)
573                         return -1;
574                 bp += n;
575                 len -= n;
576         } while (!newline && len);
577
578         *bp = '\0';
579         return bp - buf;
580 }
581 #endif
582
583 gint sock_gets(SockInfo *sock, gchar *buf, gint len)
584 {
585         g_return_val_if_fail(sock != NULL, -1);
586
587 #if USE_SSL
588         if (sock->ssl)
589                 return ssl_gets(sock->ssl, buf, len);
590 #endif
591         return fd_gets(sock->sock, buf, len);
592 }
593
594 gchar *fd_getline(gint fd)
595 {
596         gchar buf[BUFFSIZE];
597         gchar *str = NULL;
598         gint len;
599         gulong size = 1;
600
601         while ((len = fd_gets(fd, buf, sizeof(buf))) > 0) {
602                 size += len;
603                 if (!str)
604                         str = g_strdup(buf);
605                 else {
606                         str = g_realloc(str, size);
607                         strcat(str, buf);
608                 }
609                 if (buf[len - 1] == '\n')
610                         break;
611         }
612         if (len == -1) {
613                 log_error("Read from socket fd%d failed: %s\n",
614                           fd, strerror(errno));
615                 if (str)
616                         g_free(str);
617                 return NULL;
618         }
619
620         return str;
621 }
622
623 #if USE_SSL
624 gchar *ssl_getline(SSL *ssl)
625 {
626         gchar buf[BUFFSIZE];
627         gchar *str = NULL;
628         gint len;
629         gulong size = 1;
630
631         while ((len = ssl_gets(ssl, buf, sizeof(buf))) > 0) {
632                 size += len;
633                 if (!str)
634                         str = g_strdup(buf);
635                 else {
636                         str = g_realloc(str, size);
637                         strcat(str, buf);
638                 }
639                 if (buf[len - 1] == '\n')
640                         break;
641         }
642
643         return str;
644 }
645 #endif
646
647 gchar *sock_getline(SockInfo *sock)
648 {
649         g_return_val_if_fail(sock != NULL, NULL);
650
651 #if USE_SSL
652         if (sock->ssl)
653                 return ssl_getline(sock->ssl);
654 #endif
655         return fd_getline(sock->sock);
656 }
657
658 gint sock_puts(SockInfo *sock, const gchar *buf)
659 {
660         gint ret;
661
662         if ((ret = sock_write(sock, buf, strlen(buf))) < 0)
663                 return ret;
664         return sock_write(sock, "\r\n", 2);
665 }
666
667 /* peek at the next socket character without actually reading it */
668 gint sock_peek(SockInfo *sock)
669 {
670         gint n;
671         gchar ch;
672
673         g_return_val_if_fail(sock != NULL, -1);
674
675 #if USE_SSL
676         if (sock->ssl) {
677                 if ((n = SSL_peek(sock->ssl, &ch, 1)) < 0)
678                         return -1;
679                 else
680                         return ch;
681         }
682 #endif
683         if ((n = recv(sock->sock, &ch, 1, MSG_PEEK)) < 0)
684                 return -1;
685         else
686                 return ch;
687 }
688
689 gint sock_close(SockInfo *sock)
690 {
691         gint ret;
692
693         if (!sock)
694                 return 0;
695
696 #if USE_SSL
697         if (sock->ssl)
698                 ssl_done_socket(sock);
699 #endif
700         ret = fd_close(sock->sock); 
701         g_free(sock->hostname);
702         g_free(sock);
703
704         return ret;
705 }
706
707 gint fd_close(gint fd)
708 {
709         return close(fd);
710 }
711
712 gint sock_gdk_input_add(SockInfo *sock,
713                         GdkInputCondition condition,
714                         GdkInputFunction function,
715                         gpointer data)
716 {
717         g_return_val_if_fail(sock != NULL, -1);
718
719         /* :WK: We have to change some things here becuse most likey
720            function() does take SockInfo * and not an gint */
721         return gdk_input_add(sock->sock, condition, function, data);
722 }