config: Use config file name and entry when deciding removal
[framework/connectivity/connman.git] / gweb / giognutls.c
index e55a8fd..47ff8c4 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Web service library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -25,7 +25,7 @@
 
 #include <stdio.h>
 #include <errno.h>
-#include <ctype.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 #include <gnutls/gnutls.h>
@@ -40,10 +40,11 @@ typedef struct _GIOGnuTLSWatch GIOGnuTLSWatch;
 
 struct _GIOGnuTLSChannel {
        GIOChannel channel;
-       GIOChannel *transport;
+       gint fd;
        gnutls_certificate_credentials_t cred;
-       gnutls_session session;
+       gnutls_session_t session;
        gboolean established;
+       gboolean again;
 };
 
 struct _GIOGnuTLSWatch {
@@ -53,27 +54,44 @@ struct _GIOGnuTLSWatch {
        GIOCondition condition;
 };
 
-static volatile gint global_init_done = 0;
+static volatile int global_init_done = 0;
 
 static inline void g_io_gnutls_global_init(void)
 {
-       if (g_atomic_int_compare_and_exchange(&global_init_done, 0, 1) == TRUE)
+       if (__sync_bool_compare_and_swap(&global_init_done, 0, 1) == TRUE)
                gnutls_global_init();
 }
 
-static GIOStatus check_handshake(GIOChannel *channel, GError **error)
+static GIOStatus check_handshake(GIOChannel *channel, GError **err)
 {
        GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
-       int err;
+       int result;
 
        DBG("channel %p", channel);
 
        if (gnutls_channel->established == TRUE)
                return G_IO_STATUS_NORMAL;
 
-        err = gnutls_handshake(gnutls_channel->session);
-       if (err < 0)
-               return G_IO_STATUS_AGAIN;
+again:
+       result = gnutls_handshake(gnutls_channel->session);
+
+       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+               GIOFlags flags = g_io_channel_get_flags(channel);
+
+               if (gnutls_channel->again == TRUE)
+                       return G_IO_STATUS_AGAIN;
+
+               if (flags & G_IO_FLAG_NONBLOCK)
+                       return G_IO_STATUS_AGAIN;
+
+               goto again;
+       }
+
+       if (result < 0) {
+               g_set_error(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Handshake failed");
+               return G_IO_STATUS_ERROR;
+       }
 
        gnutls_channel->established = TRUE;
 
@@ -107,8 +125,17 @@ again:
                goto again;
        }
 
-       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
+       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+               GIOFlags flags = g_io_channel_get_flags(channel);
+
+               if (gnutls_channel->again == TRUE)
+                       return G_IO_STATUS_AGAIN;
+
+               if (flags & G_IO_FLAG_NONBLOCK)
+                       return G_IO_STATUS_AGAIN;
+
                goto again;
+       }
 
        if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
                return G_IO_STATUS_EOF;
@@ -149,8 +176,17 @@ again:
                goto again;
        }
 
-       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
+       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+               GIOFlags flags = g_io_channel_get_flags(channel);
+
+               if (gnutls_channel->again == TRUE)
+                       return G_IO_STATUS_AGAIN;
+
+               if (flags & G_IO_FLAG_NONBLOCK)
+                       return G_IO_STATUS_AGAIN;
+
                goto again;
+       }
 
        if (result < 0) {
                g_set_error(err, G_IO_CHANNEL_ERROR,
@@ -166,25 +202,29 @@ again:
 static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
                                                GSeekType type, GError **err)
 {
-       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
-       GIOChannel *transport = gnutls_channel->transport;
-
        DBG("channel %p", channel);
 
-       return transport->funcs->io_seek(transport, offset, type, err);
+       g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Not supported");
+       return G_IO_STATUS_ERROR;
 }
 
 static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
 {
        GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
-       GIOChannel *transport = gnutls_channel->transport;
 
        DBG("channel %p", channel);
 
        if (gnutls_channel->established == TRUE)
                gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
 
-       return transport->funcs->io_close(transport, err);
+       if (close(gnutls_channel->fd) < 0) {
+               g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Closing failed");
+               return G_IO_STATUS_ERROR;
+       }
+
+       return G_IO_STATUS_NORMAL;
 }
 
 static void g_io_gnutls_free(GIOChannel *channel)
@@ -193,8 +233,6 @@ static void g_io_gnutls_free(GIOChannel *channel)
 
        DBG("channel %p", channel);
 
-       g_io_channel_unref(gnutls_channel->transport);
-
        gnutls_deinit(gnutls_channel->session);
 
        gnutls_certificate_free_credentials(gnutls_channel->cred);
@@ -206,21 +244,38 @@ static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
                                                GIOFlags flags, GError **err)
 {
        GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
-       GIOChannel *transport = gnutls_channel->transport;
+       glong fcntl_flags = 0;
 
        DBG("channel %p flags %u", channel, flags);
 
-       return transport->funcs->io_set_flags(transport, flags, err);
+       if (flags & G_IO_FLAG_NONBLOCK)
+               fcntl_flags |= O_NONBLOCK;
+
+       if (fcntl(gnutls_channel->fd, F_SETFL, fcntl_flags) < 0) {
+               g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+                       G_IO_CHANNEL_ERROR_FAILED, "Setting flags failed");
+               return G_IO_STATUS_ERROR;
+       }
+
+       return G_IO_STATUS_NORMAL;
 }
 
 static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
 {
        GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
-       GIOChannel *transport = gnutls_channel->transport;
+       GIOFlags flags = 0;
+       glong fcntl_flags;
 
        DBG("channel %p", channel);
 
-       return transport->funcs->io_get_flags(transport);
+       fcntl_flags = fcntl(gnutls_channel->fd, F_GETFL);
+       if (fcntl_flags < 0)
+               return 0;
+
+       if (fcntl_flags & O_NONBLOCK)
+               flags |= G_IO_FLAG_NONBLOCK;
+
+       return flags;
 }
 
 static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
@@ -294,7 +349,7 @@ static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
 
        watch->condition = condition;
 
-       watch->pollfd.fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+       watch->pollfd.fd = gnutls_channel->fd;
        watch->pollfd.events = condition;
 
        g_source_add_poll(source, &watch->pollfd);
@@ -318,13 +373,15 @@ static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
 {
        GIOGnuTLSChannel *gnutls_channel = transport_data;
        ssize_t result;
-       int fd;
 
-       DBG("transport %p count %zu", gnutls_channel->transport, count);
+       DBG("count %zu", count);
 
-       fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+       result = write(gnutls_channel->fd, buf, count);
 
-       result = write(fd, buf, count);
+       if (result < 0 && errno == EAGAIN)
+               gnutls_channel->again = TRUE;
+       else
+               gnutls_channel->again = FALSE;
 
        DBG("result %zd", result);
 
@@ -336,19 +393,26 @@ static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
 {
        GIOGnuTLSChannel *gnutls_channel = transport_data;
        ssize_t result;
-       int fd;
 
-       DBG("transport %p count %zu", gnutls_channel->transport, count);
+       DBG("count %zu", count);
 
-       fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+       result = read(gnutls_channel->fd, buf, count);
 
-       result = read(fd, buf, count);
+       if (result < 0 && errno == EAGAIN)
+               gnutls_channel->again = TRUE;
+       else
+               gnutls_channel->again = FALSE;
 
        DBG("result %zd", result);
 
        return result;
 }
 
+gboolean g_io_channel_supports_tls(void)
+{
+       return TRUE;
+}
+
 GIOChannel *g_io_channel_gnutls_new(int fd)
 {
        GIOGnuTLSChannel *gnutls_channel;
@@ -364,10 +428,7 @@ GIOChannel *g_io_channel_gnutls_new(int fd)
        g_io_channel_init(channel);
        channel->funcs = &gnutls_channel_funcs;
 
-       gnutls_channel->transport = g_io_channel_unix_new(fd);
-
-       g_io_channel_set_encoding(gnutls_channel->transport, NULL, NULL);
-       g_io_channel_set_buffered(gnutls_channel->transport, FALSE);
+       gnutls_channel->fd = fd;
 
        channel->is_seekable = FALSE;
        channel->is_readable = TRUE;
@@ -388,16 +449,21 @@ GIOChannel *g_io_channel_gnutls_new(int fd)
                                                g_io_gnutls_push_func);
         gnutls_transport_set_pull_function(gnutls_channel->session,
                                                g_io_gnutls_pull_func);
+#if GNUTLS_VERSION_NUMBER < 0x020c00
        gnutls_transport_set_lowat(gnutls_channel->session, 0);
 
        gnutls_priority_set_direct(gnutls_channel->session,
-                               "NORMAL:!VERS-TLS1.1:!VERS-TLS1.0", NULL);
+                                               "NORMAL:%COMPAT", NULL);
+#else
+       gnutls_priority_set_direct(gnutls_channel->session,
+               "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL);
+#endif
 
        gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
        gnutls_credentials_set(gnutls_channel->session,
                                GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
 
-       DBG("channel %p transport %p", channel, gnutls_channel->transport);
+       DBG("channel %p", channel);
 
        return channel;
 }