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