Enable GUI option for 'custom command' connection. Don't g_free strings in
[platform/upstream/evolution-data-server.git] / camel / camel-tcp-stream-raw.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2001-2003 Ximian, Inc. (www.ximian.com)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/time.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <errno.h>
36
37 #include "camel-tcp-stream-raw.h"
38 #include "camel-file-utils.h"
39 #include "camel-operation.h"
40
41 static CamelTcpStreamClass *parent_class = NULL;
42
43 /* Returns the class for a CamelTcpStreamRaw */
44 #define CTSR_CLASS(so) CAMEL_TCP_STREAM_RAW_CLASS (CAMEL_OBJECT_GET_CLASS (so))
45
46 static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
47 static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
48 static int stream_flush  (CamelStream *stream);
49 static int stream_close  (CamelStream *stream);
50
51 static int stream_connect (CamelTcpStream *stream, struct hostent *host, int port);
52 static int stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data);
53 static int stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data);
54 static CamelTcpAddress *stream_get_local_address (CamelTcpStream *stream);
55 static CamelTcpAddress *stream_get_remote_address (CamelTcpStream *stream);
56
57 static void
58 camel_tcp_stream_raw_class_init (CamelTcpStreamRawClass *camel_tcp_stream_raw_class)
59 {
60         CamelTcpStreamClass *camel_tcp_stream_class =
61                 CAMEL_TCP_STREAM_CLASS (camel_tcp_stream_raw_class);
62         CamelStreamClass *camel_stream_class =
63                 CAMEL_STREAM_CLASS (camel_tcp_stream_raw_class);
64         
65         parent_class = CAMEL_TCP_STREAM_CLASS (camel_type_get_global_classfuncs (camel_tcp_stream_get_type ()));
66         
67         /* virtual method overload */
68         camel_stream_class->read = stream_read;
69         camel_stream_class->write = stream_write;
70         camel_stream_class->flush = stream_flush;
71         camel_stream_class->close = stream_close;
72         
73         camel_tcp_stream_class->connect = stream_connect;
74         camel_tcp_stream_class->getsockopt = stream_getsockopt;
75         camel_tcp_stream_class->setsockopt  = stream_setsockopt;
76         camel_tcp_stream_class->get_local_address  = stream_get_local_address;
77         camel_tcp_stream_class->get_remote_address = stream_get_remote_address;
78 }
79
80 static void
81 camel_tcp_stream_raw_init (gpointer object, gpointer klass)
82 {
83         CamelTcpStreamRaw *stream = CAMEL_TCP_STREAM_RAW (object);
84         
85         stream->sockfd = -1;
86 }
87
88 static void
89 camel_tcp_stream_raw_finalize (CamelObject *object)
90 {
91         CamelTcpStreamRaw *stream = CAMEL_TCP_STREAM_RAW (object);
92         
93         if (stream->sockfd != -1)
94                 close (stream->sockfd);
95 }
96
97
98 CamelType
99 camel_tcp_stream_raw_get_type (void)
100 {
101         static CamelType type = CAMEL_INVALID_TYPE;
102         
103         if (type == CAMEL_INVALID_TYPE) {
104                 type = camel_type_register (camel_tcp_stream_get_type (),
105                                             "CamelTcpStreamRaw",
106                                             sizeof (CamelTcpStreamRaw),
107                                             sizeof (CamelTcpStreamRawClass),
108                                             (CamelObjectClassInitFunc) camel_tcp_stream_raw_class_init,
109                                             NULL,
110                                             (CamelObjectInitFunc) camel_tcp_stream_raw_init,
111                                             (CamelObjectFinalizeFunc) camel_tcp_stream_raw_finalize);
112         }
113         
114         return type;
115 }
116
117 #ifdef SIMULATE_FLAKY_NETWORK
118 static ssize_t
119 flaky_tcp_write (int fd, const char *buffer, size_t buflen)
120 {
121         size_t len = buflen;
122         ssize_t nwritten;
123         int val;
124         
125         if (buflen == 0)
126                 return 0;
127         
128         val = 1 + (int) (10.0 * rand () / (RAND_MAX + 1.0));
129         
130         switch (val) {
131         case 1:
132                 printf ("flaky_tcp_write (%d, ..., %d): (-1) EINTR\n", fd, buflen);
133                 errno = EINTR;
134                 return -1;
135         case 2:
136                 printf ("flaky_tcp_write (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
137                 errno = EAGAIN;
138                 return -1;
139         case 3:
140                 printf ("flaky_tcp_write (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
141                 errno = EWOULDBLOCK;
142                 return -1;
143         case 4:
144         case 5:
145         case 6:
146                 len = 1 + (size_t) (buflen * rand () / (RAND_MAX + 1.0));
147                 len = MIN (len, buflen);
148                 /* fall through... */
149         default:
150                 printf ("flaky_tcp_write (%d, ..., %d): (%d) '%.*s'", fd, buflen, len, (int) len, buffer);
151                 nwritten = write (fd, buffer, len);
152                 if (nwritten < 0)
153                         printf (" errno => %s\n", strerror (errno));
154                 else if (nwritten < len)
155                         printf (" only wrote %d bytes\n", nwritten);
156                 else
157                         printf ("\n");
158                 
159                 return nwritten;
160         }
161 }
162
163 #define write(fd, buffer, buflen) flaky_tcp_write (fd, buffer, buflen)
164
165 static ssize_t
166 flaky_tcp_read (int fd, char *buffer, size_t buflen)
167 {
168         size_t len = buflen;
169         ssize_t nread;
170         int val;
171         
172         if (buflen == 0)
173                 return 0;
174         
175         val = 1 + (int) (10.0 * rand () / (RAND_MAX + 1.0));
176         
177         switch (val) {
178         case 1:
179                 printf ("flaky_tcp_read (%d, ..., %d): (-1) EINTR\n", fd, buflen);
180                 errno = EINTR;
181                 return -1;
182         case 2:
183                 printf ("flaky_tcp_read (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
184                 errno = EAGAIN;
185                 return -1;
186         case 3:
187                 printf ("flaky_tcp_read (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
188                 errno = EWOULDBLOCK;
189                 return -1;
190         case 4:
191         case 5:
192         case 6:
193         case 7:
194         case 8:
195         case 9:
196         case 10:
197                 len = 1 + (size_t) (10.0 * rand () / (RAND_MAX + 1.0));
198                 len = MIN (len, buflen);
199                 /* fall through... */
200         default:
201                 printf ("flaky_tcp_read (%d, ..., %d): (%d)", fd, buflen, len);
202                 nread = read (fd, buffer, len);
203                 if (nread < 0)
204                         printf (" errno => %s\n", strerror (errno));
205                 else if (nread < len)
206                         printf (" only read %d bytes\n", nread);
207                 else
208                         printf ("\n");
209                 
210                 return nread;
211         }
212 }
213
214 #define read(fd, buffer, buflen) flaky_tcp_read (fd, buffer, buflen)
215
216 #endif /* SIMULATE_FLAKY_NETWORK */
217
218
219
220 /**
221  * camel_tcp_stream_raw_new:
222  *
223  * Return value: a tcp stream
224  **/
225 CamelStream *
226 camel_tcp_stream_raw_new ()
227 {
228         CamelTcpStreamRaw *stream;
229         
230         stream = CAMEL_TCP_STREAM_RAW (camel_object_new (camel_tcp_stream_raw_get_type ()));
231         
232         return CAMEL_STREAM (stream);
233 }
234
235 static ssize_t
236 stream_read (CamelStream *stream, char *buffer, size_t n)
237 {
238         CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
239         
240         return camel_read (raw->sockfd, buffer, n);
241 }
242
243 static ssize_t
244 stream_write (CamelStream *stream, const char *buffer, size_t n)
245 {
246         CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
247         
248         return camel_write (raw->sockfd, buffer, n);
249 }
250
251 static int
252 stream_flush (CamelStream *stream)
253 {
254         return 0;
255 }
256
257 static int
258 stream_close (CamelStream *stream)
259 {
260         if (close (((CamelTcpStreamRaw *)stream)->sockfd) == -1)
261                 return -1;
262         
263         ((CamelTcpStreamRaw *)stream)->sockfd = -1;
264         return 0;
265 }
266
267 /* this is a 'cancellable' connect, cancellable from camel_operation_cancel etc */
268 /* returns -1 & errno == EINTR if the connection was cancelled */
269 static int
270 socket_connect (struct hostent *h, int port)
271 {
272 #ifdef ENABLE_IPv6
273         struct sockaddr_in6 sin6;
274 #endif
275         struct sockaddr_in sin;
276         struct sockaddr *saddr;
277         struct timeval tv;
278         socklen_t len;
279         int cancel_fd;
280         int ret, fd;
281         
282         /* see if we're cancelled yet */
283         if (camel_operation_cancel_check (NULL)) {
284                 errno = EINTR;
285                 return -1;
286         }
287         
288         /* setup connect, we do it using a nonblocking socket so we can poll it */
289 #ifdef ENABLE_IPv6
290         if (h->h_addrtype == AF_INET6) {
291                 sin6.sin6_port = htons (port);
292                 sin6.sin6_family = h->h_addrtype;
293                 memcpy (&sin6.sin6_addr, h->h_addr, sizeof (sin6.sin6_addr));
294                 saddr = (struct sockaddr *) &sin6;
295                 len = sizeof (sin6);
296         } else {
297 #endif
298                 sin.sin_port = htons (port);
299                 sin.sin_family = h->h_addrtype;
300                 memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr));
301                 saddr = (struct sockaddr *) &sin;
302                 len = sizeof (sin);
303 #ifdef ENABLE_IPv6
304         }
305 #endif
306         
307         fd = socket (h->h_addrtype, SOCK_STREAM, 0);
308         
309         cancel_fd = camel_operation_cancel_fd (NULL);
310         if (cancel_fd == -1) {
311                 ret = connect (fd, saddr, len);
312                 if (ret == -1) {
313                         close (fd);
314                         return -1;
315                 }
316                 
317                 return fd;
318         } else {
319                 int flags, fdmax, status;
320                 fd_set rdset, wrset;
321                 
322                 flags = fcntl (fd, F_GETFL);
323                 fcntl (fd, F_SETFL, flags | O_NONBLOCK);
324                 
325                 ret = connect (fd, saddr, len);
326                 if (ret == 0) {
327                         fcntl (fd, F_SETFL, flags);
328                         return fd;
329                 }
330                 
331                 if (errno != EINPROGRESS) {
332                         close (fd);
333                         return -1;
334                 }
335                 
336                 do {
337                         FD_ZERO (&rdset);
338                         FD_ZERO (&wrset);
339                         FD_SET (fd, &wrset);
340                         FD_SET (cancel_fd, &rdset);
341                         fdmax = MAX (fd, cancel_fd) + 1;
342                         tv.tv_sec = 60 * 4;
343                         tv.tv_usec = 0;
344                         
345                         status = select (fdmax, &rdset, &wrset, 0, &tv);
346                 } while (status == -1 && errno == EINTR);
347                 
348                 if (status <= 0) {
349                         close (fd);
350                         errno = ETIMEDOUT;
351                         return -1;
352                 }
353                 
354                 if (cancel_fd != -1 && FD_ISSET (cancel_fd, &rdset)) {
355                         close (fd);
356                         errno = EINTR;
357                         return -1;
358                 } else {
359                         len = sizeof (int);
360                         
361                         if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &len) == -1) {
362                                 close (fd);
363                                 return -1;
364                         }
365                         
366                         if (ret != 0) {
367                                 close (fd);
368                                 errno = ret;
369                                 return -1;
370                         }
371                 }
372                 
373                 fcntl (fd, F_SETFL, flags);
374         }
375         
376         return fd;
377 }
378
379 static int
380 stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
381 {
382         CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
383         int fd;
384         
385         g_return_val_if_fail (host != NULL, -1);
386         
387         fd = socket_connect (host, port);
388         if (fd == -1)
389                 return -1;
390         
391         raw->sockfd = fd;
392         
393         return 0;
394 }
395
396
397 static int
398 get_sockopt_level (const CamelSockOptData *data)
399 {
400         switch (data->option) {
401         case CAMEL_SOCKOPT_MAXSEGMENT:
402         case CAMEL_SOCKOPT_NODELAY:
403                 return IPPROTO_TCP;
404         default:
405                 return SOL_SOCKET;
406         }
407 }
408
409 static int
410 get_sockopt_optname (const CamelSockOptData *data)
411 {
412         switch (data->option) {
413         case CAMEL_SOCKOPT_MAXSEGMENT:
414                 return TCP_MAXSEG;
415         case CAMEL_SOCKOPT_NODELAY:
416                 return TCP_NODELAY;
417         case CAMEL_SOCKOPT_BROADCAST:
418                 return SO_BROADCAST;
419         case CAMEL_SOCKOPT_KEEPALIVE:
420                 return SO_KEEPALIVE;
421         case CAMEL_SOCKOPT_LINGER:
422                 return SO_LINGER;
423         case CAMEL_SOCKOPT_RECVBUFFERSIZE:
424                 return SO_RCVBUF;
425         case CAMEL_SOCKOPT_SENDBUFFERSIZE:
426                 return SO_SNDBUF;
427         case CAMEL_SOCKOPT_REUSEADDR:
428                 return SO_REUSEADDR;
429         case CAMEL_SOCKOPT_IPTYPEOFSERVICE:
430                 return SO_TYPE;
431         default:
432                 return -1;
433         }
434 }
435
436 static int
437 stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data)
438 {
439         int optname, optlen;
440         
441         if ((optname = get_sockopt_optname (data)) == -1)
442                 return -1;
443         
444         if (data->option == CAMEL_SOCKOPT_NONBLOCKING) {
445                 int flags;
446                 
447                 flags = fcntl (((CamelTcpStreamRaw *)stream)->sockfd, F_GETFL);
448                 if (flags == -1)
449                         return -1;
450                 
451                 data->value.non_blocking = flags & O_NONBLOCK ? TRUE : FALSE;
452                 
453                 return 0;
454         }
455         
456         return getsockopt (((CamelTcpStreamRaw *)stream)->sockfd,
457                            get_sockopt_level (data),
458                            optname,
459                            (void *) &data->value,
460                            &optlen);
461 }
462
463 static int
464 stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data)
465 {
466         int optname;
467         
468         if ((optname = get_sockopt_optname (data)) == -1)
469                 return -1;
470         
471         if (data->option == CAMEL_SOCKOPT_NONBLOCKING) {
472                 int flags, set;
473                 
474                 flags = fcntl (((CamelTcpStreamRaw *)stream)->sockfd, F_GETFL);
475                 if (flags == -1)
476                         return -1;
477                 
478                 set = data->value.non_blocking ? O_NONBLOCK : 0;
479                 flags = (flags & ~O_NONBLOCK) | set;
480                 
481                 if (fcntl (((CamelTcpStreamRaw *)stream)->sockfd, F_SETFL, flags) == -1)
482                         return -1;
483                 
484                 return 0;
485         }
486         
487         return setsockopt (((CamelTcpStreamRaw *)stream)->sockfd,
488                            get_sockopt_level (data),
489                            optname,
490                            (void *) &data->value,
491                            sizeof (data->value));
492 }
493
494 #ifdef ENABLE_IPv6
495 #define MIN_SOCKADDR_BUFLEN  (sizeof (struct sockaddr_in6))
496 #else
497 #define MIN_SOCKADDR_BUFLEN  (sizeof (struct sockaddr_in))
498 #endif
499
500 static CamelTcpAddress *
501 stream_get_local_address (CamelTcpStream *stream)
502 {
503         unsigned char buf[MIN_SOCKADDR_BUFLEN];
504 #ifdef ENABLE_IPv6
505         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) buf;
506 #endif
507         struct sockaddr_in *sin = (struct sockaddr_in *) buf;
508         struct sockaddr *saddr = (struct sockaddr *) buf;
509         gpointer address;
510         socklen_t len;
511         int family;
512         
513         len = MIN_SOCKADDR_BUFLEN;
514         
515         if (getsockname (CAMEL_TCP_STREAM_RAW (stream)->sockfd, saddr, &len) == -1)
516                 return NULL;
517         
518         if (saddr->sa_family == AF_INET) {
519                 family = CAMEL_TCP_ADDRESS_IPv4;
520                 address = &sin->sin_addr;
521 #ifdef ENABLE_IPv6
522         } else if (saddr->sa_family == AF_INET6) {
523                 family = CAMEL_TCP_ADDRESS_IPv6;
524                 address = &sin6->sin6_addr;
525 #endif
526         } else
527                 return NULL;
528         
529         return camel_tcp_address_new (family, sin->sin_port, len, address);
530 }
531
532 static CamelTcpAddress *
533 stream_get_remote_address (CamelTcpStream *stream)
534 {
535         unsigned char buf[MIN_SOCKADDR_BUFLEN];
536 #ifdef ENABLE_IPv6
537         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) buf;
538 #endif
539         struct sockaddr_in *sin = (struct sockaddr_in *) buf;
540         struct sockaddr *saddr = (struct sockaddr *) buf;
541         gpointer address;
542         socklen_t len;
543         int family;
544         
545         len = MIN_SOCKADDR_BUFLEN;
546         
547         if (getpeername (CAMEL_TCP_STREAM_RAW (stream)->sockfd, saddr, &len) == -1)
548                 return NULL;
549         
550         if (saddr->sa_family == AF_INET) {
551                 family = CAMEL_TCP_ADDRESS_IPv4;
552                 address = &sin->sin_addr;
553 #ifdef ENABLE_IPv6
554         } else if (saddr->sa_family == AF_INET6) {
555                 family = CAMEL_TCP_ADDRESS_IPv6;
556                 address = &sin6->sin6_addr;
557 #endif
558         } else
559                 return NULL;
560         
561         return camel_tcp_address_new (family, sin->sin_port, len, address);
562 }