[Goodies-commits] r5232 - xfce4-mailwatch-plugin/trunk/libmailwatch-core

Brian Tarricone kelnos at xfce.org
Sat Aug 16 10:47:36 CEST 2008


Author: kelnos
Date: 2008-08-16 08:47:36 +0000 (Sat, 16 Aug 2008)
New Revision: 5232

Added:
   xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.c
   xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.h
Modified:
   xfce4-mailwatch-plugin/trunk/libmailwatch-core/Makefile.am
Log:
XfceMailwatchNetConn: a buffered possibly-encrypted net connection handler

not really tested aside from making sure it compiles.

Modified: xfce4-mailwatch-plugin/trunk/libmailwatch-core/Makefile.am
===================================================================
--- xfce4-mailwatch-plugin/trunk/libmailwatch-core/Makefile.am	2008-08-16 08:47:21 UTC (rev 5231)
+++ xfce4-mailwatch-plugin/trunk/libmailwatch-core/Makefile.am	2008-08-16 08:47:36 UTC (rev 5232)
@@ -13,6 +13,8 @@
 	mailwatch-mailbox-mh.c \
 	mailwatch-mailbox-pop3.c \
 	mailwatch-mailbox.h \
+	mailwatch-net-conn.c \
+	mailwatch-net-conn.h \
 	mailwatch-utils.c \
 	mailwatch-utils.h \
 	mailwatch.c \

Added: xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.c
===================================================================
--- xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.c	                        (rev 0)
+++ xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.c	2008-08-16 08:47:36 UTC (rev 5232)
@@ -0,0 +1,881 @@
+/*
+ *  xfce4-mailwatch-plugin - a mail notification applet for the xfce4 panel
+ *  Copyright (c) 2008 Brian Tarricone <bjt23 at cornell.edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License ONLY.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <libxfce4util/libxfce4util.h>
+
+#ifdef HAVE_SSL_SUPPORT
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+#endif
+
+#include "mailwatch-net-conn.h"
+#include "mailwatch-common.h"
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
+#define SHOULD_CONTINUE(nc)  ( !(nc)->should_continue \
+                               || ( (nc)->should_continue \
+                                    && (nc)->should_continue((nc), \
+                                                             (nc)->should_continue_user_data) \
+                                  ) \
+                             )
+
+struct _XfceMailwatchNetConn
+{
+    gchar *hostname;
+    gchar *service;
+    guint port;
+    const gchar *line_terminator;
+
+    gint fd;
+
+    guchar *buffer;
+    gsize buffer_len;
+    
+    gboolean is_secure;
+#ifdef HAVE_SSL_SUPPORT
+    gnutls_session_t gt_session;
+    gnutls_certificate_credentials_t gt_creds;
+#endif
+
+    XMNCShouldContinueFunc should_continue;
+    gpointer should_continue_user_data;
+};
+
+
+#ifdef HAVE_SSL_SUPPORT
+
+/* missing from 1.2.0? */
+#ifndef _GCRY_PTH_SOCKADDR
+#define _GCRY_PTH_SOCKADDR  struct sockaddr
+#endif
+#ifndef _GCRY_PTH_SOCKLEN_T
+#define _GCRY_PTH_SOCKLEN_T socklen_t
+#endif
+
+#define GNUTLS_CA_FILE           "ca.pem"
+    
+/* stuff to support 'gthreads' with gcrypt */
+static int my_g_mutex_init(void **priv);
+static int my_g_mutex_destroy(void **priv);
+static int my_g_mutex_lock(void **priv);
+static int my_g_mutex_unlock(void **priv);
+static struct gcry_thread_cbs gcry_threads_gthread = {
+    GCRY_THREAD_OPTION_USER,
+    NULL,
+    my_g_mutex_init,
+    my_g_mutex_destroy,
+    my_g_mutex_lock,
+    my_g_mutex_unlock,
+    read,
+    write,
+    (ssize_t (*)(int, fd_set *, fd_set *, fd_set *, struct timeval *))select,
+    (ssize_t (*)(pid_t, int *, int))waitpid,
+    accept,
+    (int (*)(int, _GCRY_PTH_SOCKADDR *, _GCRY_PTH_SOCKLEN_T))connect,
+    (int (*)(int, const struct msghdr *, int))sendmsg,
+    (int (*)(int, struct msghdr *, int))recvmsg
+};
+
+/*
+ * gthread -> gcrypt support wrappers
+ */
+static int
+my_g_mutex_init(void **priv)
+{
+    GMutex **gmx = (GMutex **)priv;
+    
+    *gmx = g_mutex_new();
+    if(!*gmx)
+        return -1;
+    return 0;
+}
+
+static int
+my_g_mutex_destroy(void **priv)
+{
+    GMutex **gmx = (GMutex **)priv;
+    
+    g_mutex_free(*gmx);
+    return 0;
+}
+
+static int
+my_g_mutex_lock(void **priv)
+{
+    GMutex **gmx = (GMutex **)priv;
+    
+    g_mutex_lock(*gmx);
+    return 0;
+}
+
+static int
+my_g_mutex_unlock(void **priv)
+{
+    GMutex **gmx = (GMutex **)priv;
+    
+    g_mutex_unlock(*gmx);
+    return 0;
+}
+
+#endif  /* defined(HAVE_SSL_SUPPORT) */
+
+
+void
+xfce_mailwatch_net_conn_init()
+{
+#ifdef HAVE_SSL_SUPPORT
+    gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_gthread);
+    gnutls_global_init();
+#endif
+}
+
+XfceMailwatchNetConn *
+xfce_mailwatch_net_conn_new(const gchar *hostname,
+                            const gchar *service)
+{
+    XfceMailwatchNetConn *net_conn;
+    
+    g_return_val_if_fail(hostname && *hostname, NULL);
+
+    net_conn = g_new0(XfceMailwatchNetConn, 1);
+    net_conn->hostname = g_strdup(hostname);
+    net_conn->service = service ? g_strdup(service) : NULL;
+    net_conn->line_terminator = g_intern_string("\r\n");
+    net_conn->fd = -1;
+
+    return net_conn;
+}
+
+void
+xfce_mailwatch_net_conn_set_should_continue_func(XfceMailwatchNetConn *net_conn,
+                                                 XMNCShouldContinueFunc func,
+                                                 gpointer user_data)
+{
+    g_return_if_fail(net_conn);
+    net_conn->should_continue = func;
+    net_conn->should_continue_user_data = user_data;
+}
+
+void
+xfce_mailwatch_net_conn_set_port(XfceMailwatchNetConn *net_conn,
+                                 guint port)
+{
+    g_return_if_fail(net_conn && net_conn->fd == -1);
+    net_conn->port = port;
+}
+
+guint
+xfce_mailwatch_net_conn_get_port(XfceMailwatchNetConn *net_conn)
+{
+    g_return_val_if_fail(net_conn, 0);
+    return net_conn->port;
+}
+
+void
+xfce_mailwatch_net_conn_set_line_terminator(XfceMailwatchNetConn *net_conn,
+                                            const gchar *line_term)
+{
+    g_return_if_fail(net_conn && line_term && *line_term);
+    net_conn->line_terminator = g_intern_string(line_term);
+}
+
+const gchar *
+xfce_mailwatch_net_conn_get_line_terminator(XfceMailwatchNetConn *net_conn)
+{
+    g_return_val_if_fail(net_conn, NULL);
+    return net_conn->line_terminator;
+}
+
+gboolean
+xfce_mailwatch_net_conn_is_secure(XfceMailwatchNetConn *net_conn)
+{
+    g_return_val_if_fail(net_conn, FALSE);
+    return net_conn->is_secure;
+}
+
+static gboolean
+xfce_mailwatch_net_conn_get_addrinfo(XfceMailwatchNetConn *net_conn,
+                                     struct addrinfo **addresses,
+                                     GError **error)
+{
+    struct addrinfo hints;
+    gint ret;
+    gchar real_service[128];
+
+    g_return_val_if_fail(net_conn && addresses && !*addresses
+                         && (!error || !*error), FALSE);
+
+    memset(&hints, 0, sizeof(hints));
+#ifdef ENABLE_IPV6_SUPPORT
+    hints.ai_family = AF_UNSPEC;
+#else
+    hints.ai_family = AF_INET;
+#endif
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_ADDRCONFIG;
+
+    /* allow setting nonstandard port */
+    if(net_conn->port > 0)
+        g_snprintf(real_service, sizeof(real_service), "%d", net_conn->port);
+    else
+        g_strlcpy(real_service, net_conn->service, sizeof(real_service));
+    
+    /* according to getaddrinfo(3), this should be reentrant.  however, calling
+     * it from several threads often causes a crash.  backtraces show that we're
+     * indeed inside getaddrinfo() in more than one thread, and I can't figure
+     * out any other explanation. */
+    
+    xfce_mailwatch_threads_enter();
+    ret = getaddrinfo(net_conn->hostname, real_service, &hints, addresses);
+    xfce_mailwatch_threads_leave();
+    if(ret) {
+        if(error) {
+            g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                        _("Could not find host \"%s\": %s"),
+                        net_conn->hostname, gai_strerror(ret));
+        }
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+gboolean
+xfce_mailwatch_net_conn_connect(XfceMailwatchNetConn *net_conn,
+                                GError **error)
+{
+    gboolean connect_succeeded = FALSE;
+    struct addrinfo *addresses = NULL, *ai;
+    
+    g_return_val_if_fail(net_conn && (!error || !*error), FALSE);
+    g_return_val_if_fail(net_conn->fd == -1, TRUE);
+
+    if(!xfce_mailwatch_net_conn_get_addrinfo(net_conn, &addresses, error)) {
+        DBG("failed to get sockaddr");
+        return FALSE;
+    }
+    
+    for(ai = addresses; ai; ai = ai->ai_next) {
+        gint ret;
+
+        net_conn->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+        if(net_conn->fd < 0)
+            continue;
+        
+        if(fcntl(net_conn->fd, F_SETFL,
+                 fcntl(net_conn->fd, F_GETFL) | O_NONBLOCK))
+        {
+            g_warning(_("Unable to set socket to non-blocking mode.  If the connect attempt hangs, the panel may hang on close."));
+        }
+        
+        do {
+            ret = connect(net_conn->fd, ai->ai_addr, ai->ai_addrlen);
+
+            if(ret < 0 && !SHOULD_CONTINUE(net_conn)) {
+                if(error) {
+                    g_set_error(error, XFCE_MAILWATCH_ERROR,
+                                XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+                    goto out_err;
+                }
+            }
+        } while(ret < 0 && (EINTR == errno || EAGAIN == errno));
+
+        if(ret < 0 && EINPROGRESS == errno) {
+            gint iters_left;
+
+            for(iters_left = 45; iters_left >= 0; iters_left--) {
+                fd_set wfd;
+                struct timeval tv = { 1, 0 };
+                int sock_err = 0;
+                socklen_t sock_err_len = sizeof(int);
+                
+                FD_ZERO(&wfd);
+                FD_SET(net_conn->fd, &wfd);
+                
+                DBG("checking for a connection...");
+
+                /* check the main thread to see if we're supposed to quit */
+                if(!SHOULD_CONTINUE(net_conn)) {
+                    if(error) {
+                        g_set_error(error, XFCE_MAILWATCH_ERROR,
+                                    XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+                    }
+                    goto out_err;
+                }
+                
+                /* wait until the connect attempt finishes */
+                if(select(FD_SETSIZE, NULL, &wfd, NULL, &tv) < 0) {
+                    if(EINTR == errno || EAGAIN == errno)
+                        continue;
+                    else
+                        break;
+                }
+                
+                /* check to see if it finished, and, if so, if there was an
+                 * error, or if it completed successfully */
+                if(FD_ISSET(net_conn->fd, &wfd)) {
+                    if(!getsockopt(net_conn->fd, SOL_SOCKET, SO_ERROR,
+                                   &sock_err, &sock_err_len)
+                       && !sock_err)
+                    {
+                        DBG("    connection succeeded");
+                        connect_succeeded = TRUE;
+                        errno = 0;
+                    } else {
+                        DBG("    connection failed: sock_err is (%d) %s",
+                            sock_err, strerror(sock_err));
+                        errno = sock_err;
+                    }
+                    break;
+                } else
+                    errno = ETIMEDOUT;
+            }
+        }
+        
+        if(connect_succeeded) {
+            if(fcntl(net_conn->fd, F_SETFL,
+                     fcntl(net_conn->fd, F_GETFL) & ~(O_NONBLOCK)))
+            {
+                g_warning(_("Unable to return socket to blocking mode.  Data may not be retreived correctly."));
+            }
+            break;
+        } else {
+            DBG("failed to connect");
+            if(net_conn->fd != -1) {
+                close(net_conn->fd);
+                net_conn->fd = -1;
+            }
+        }
+        
+        if(!SHOULD_CONTINUE(net_conn)) {
+            if(error) {
+                g_set_error(error, XFCE_MAILWATCH_ERROR,
+                            XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+            }
+            goto out_err;
+        }
+    }
+    
+    if(!connect_succeeded) {
+        if(error) {
+            g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                        _("Failed to connect to server \"%s\": %s"),
+                        net_conn->hostname, strerror(errno));
+        }
+    }
+
+out_err:
+    
+    if(net_conn->fd != -1) {  /* needed for the gotos */
+        shutdown(net_conn->fd, SHUT_RDWR);
+        close(net_conn->fd);
+        net_conn->fd = -1;
+    }
+
+    if(addresses)
+        freeaddrinfo(addresses);
+
+    return connect_succeeded;
+}
+
+gboolean
+xfce_mailwatch_net_conn_make_secure(XfceMailwatchNetConn *net_conn,
+                                    GError **error)
+{
+#ifdef HAVE_SSL_SUPPORT
+    gint gt_ret;
+    const int cert_type_prio[2] = { GNUTLS_CRT_X509, 0 };
+#endif
+
+    g_return_val_if_fail(net_conn && (!error || !*error), FALSE);
+    g_return_val_if_fail(net_conn->fd != -1, FALSE);
+    g_return_val_if_fail(!net_conn->is_secure, TRUE);
+
+#ifdef HAVE_SSL_SUPPORT
+    /* init the x509 cert */
+    gnutls_certificate_allocate_credentials(&net_conn->gt_creds);
+    gnutls_certificate_set_x509_trust_file(net_conn->gt_creds,
+                                           GNUTLS_CA_FILE,
+                                           GNUTLS_X509_FMT_PEM);
+    
+    /* init the session and set it up */
+    gnutls_init(&net_conn->gt_session, GNUTLS_CLIENT);
+    gnutls_set_default_priority(net_conn->gt_session);
+    gnutls_certificate_type_set_priority(net_conn->gt_session, cert_type_prio);
+    gnutls_credentials_set(net_conn->gt_session, GNUTLS_CRD_CERTIFICATE,
+                           net_conn->gt_creds);
+    gnutls_transport_set_ptr(net_conn->gt_session,
+                             (gnutls_transport_ptr_t)net_conn->fd);
+    
+    do {
+        gt_ret = gnutls_handshake(net_conn->gt_session);
+
+        if(gt_ret != GNUTLS_E_SUCCESS && !SHOULD_CONTINUE(net_conn)) {
+            if(error) {
+                g_set_error(error, XFCE_MAILWATCH_ERROR,
+                            XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+            }
+            goto out_err;
+        }
+    } while(gt_ret == GNUTLS_E_AGAIN || gt_ret == GNUTLS_E_INTERRUPTED);
+
+    if(gt_ret != GNUTLS_E_SUCCESS) {
+        if(error) {
+            g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                        gnutls_strerror(gt_ret));
+        }
+        g_critical(_("XfceMailwatch: TLS handshake failed: %s"), gnutls_strerror(gt_ret));
+        goto out_err;
+    }
+
+    DBG("TLS handshake succeeded");
+    net_conn->is_secure = TRUE;
+
+    return TRUE;
+
+out_err:
+    gnutls_bye(net_conn->gt_session, GNUTLS_SHUT_RDWR);
+    gnutls_deinit(net_conn->gt_session);
+    gnutls_certificate_free_credentials(net_conn->gt_creds);
+
+    return FALSE;
+#else
+    if(error) {
+        g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                    _("Not compiled with SSL/TLS support"));
+    }
+    g_critical(_("XfceMailwatch: TLS handshake failed: not compiled with SSL support."));
+    
+    return FALSE;
+#endif
+}
+
+gint
+xfce_mailwatch_net_conn_send_data(XfceMailwatchNetConn *net_conn,
+                                  const guchar *buf,
+                                  gsize buf_len,
+                                  GError **error)
+{
+    gint bout = 0;
+
+    g_return_val_if_fail(net_conn && (!error || !*error), -1);
+    g_return_val_if_fail(net_conn->fd != -1, -1);
+
+#ifdef HAVE_SSL_SUPPORT
+    if(net_conn->is_secure) {
+        gint ret = 0, totallen = buf_len;
+        gint bytesleft = totallen;
+        
+        while(bytesleft > 0) {
+            do {
+                ret = gnutls_record_send(net_conn->gt_session,
+                                         buf + totallen - bytesleft,
+                                         bytesleft);
+
+                if(GNUTLS_E_SUCCESS != ret && !SHOULD_CONTINUE(net_conn)) {
+                    if(error) {
+                        g_set_error(error, XFCE_MAILWATCH_ERROR,
+                                    XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+                    }
+                    return -1;
+                }
+            } while(GNUTLS_E_INTERRUPTED == ret || GNUTLS_E_AGAIN == ret);
+            
+            if(GNUTLS_E_REHANDSHAKE == ret) {
+                /* server has requested a new handshake */
+                do {
+                    ret = gnutls_handshake(net_conn->gt_session);
+
+                    if(GNUTLS_E_SUCCESS != ret && !SHOULD_CONTINUE(net_conn)) {
+                        if(error) {
+                            g_set_error(error, XFCE_MAILWATCH_ERROR,
+                                        XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+                        }
+                        return -1;
+                    }
+                } while(GNUTLS_E_AGAIN == ret || GNUTLS_E_INTERRUPTED == ret);
+                
+                if(GNUTLS_E_SUCCESS != ret) {
+                    if(error) {
+                        g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                                    _("TLS handshake failed (%d): %s"), ret,
+                                    gnutls_strerror(ret));
+                    }
+                    return -1;
+                }
+            } else if(ret < 0) {
+                if(error) {
+                    g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                                _("Failed to send encrypted data (%d): %s"),
+                                ret, gnutls_strerror(ret));
+                }
+                DBG("gnutls_record_send() failed (%d): %s", ret,
+                    gnutls_strerror(ret));
+                return -1;
+            } else if(ret > 0) {
+                bout += ret;
+                bytesleft -= ret;
+            }
+        }
+    } else
+#endif
+    {
+        do {
+            bout = send(net_conn->fd, buf, buf_len, MSG_NOSIGNAL);
+
+            if(bout < 0 && !SHOULD_CONTINUE(net_conn)) {
+                if(error) {
+                    g_set_error(error, XFCE_MAILWATCH_ERROR,
+                                XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+                }
+                return -1;
+            }
+        } while(bout < 0 && (EINTR == errno || EAGAIN == errno));
+    }
+
+    if(bout < 0) {
+        if(error) {
+            g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                        _("Failed to send data: %s"), strerror(errno));
+        }
+    }
+
+    return bout;
+}
+
+static gint
+xfce_mailwatch_net_conn_recv_internal(XfceMailwatchNetConn *net_conn,
+                                      guchar *buf,
+                                      gsize buf_len,
+                                      gboolean block,
+                                      GError **error)
+{
+    gint bin = 0;
+    gint tries_left;
+    gboolean data_ready = FALSE;
+
+    for(tries_left = 45; tries_left >= 0; --tries_left) {
+        fd_set rfd;
+        struct timeval tv;
+
+        if(!SHOULD_CONTINUE(net_conn)) {
+            if(error) {
+                g_set_error(error, XFCE_MAILWATCH_ERROR,
+                            XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+            }
+            return -1;
+        }
+
+        FD_ZERO(&rfd);
+        FD_SET(net_conn->fd, &rfd);
+        if(block)
+            tv.tv_sec = 1;
+        else
+            tv.tv_sec = 0;
+        tv.tv_usec = 0;
+
+        if(select(FD_SETSIZE, &rfd, NULL, NULL, &tv) < 0)
+            continue;
+
+        if(FD_ISSET(net_conn->fd, &rfd)) {
+            data_ready = TRUE;
+            break;
+        } else if(!block)
+            return 0;
+    }
+
+    if(!data_ready)
+        return 0;
+
+#ifdef HAVE_SSL_SUPPORT
+    if(net_conn->is_secure) {
+        gint ret;
+
+        do {
+            ret = gnutls_record_recv(net_conn->gt_session, buf, buf_len);
+
+            if(GNUTLS_E_REHANDSHAKE == ret) {
+                do {
+                    ret = gnutls_handshake(net_conn->gt_session);
+
+                    if(GNUTLS_E_SUCCESS != ret && !SHOULD_CONTINUE(net_conn)) {
+                        if(error) {
+                            g_set_error(error, XFCE_MAILWATCH_ERROR,
+                                        XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+                        }
+                        return -1;
+                    }
+                } while(GNUTLS_E_AGAIN == ret || GNUTLS_E_INTERRUPTED == ret);
+
+                if(ret != GNUTLS_E_SUCCESS) {
+                    if(error) {
+                        g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                                    _("TLS handshake failed (%d): %s"), ret,
+                                    gnutls_strerror(ret));
+                    }
+                    return -1;
+                }
+
+                ret = GNUTLS_E_AGAIN;
+            }
+        } while(GNUTLS_E_INTERRUPTED == ret || GNUTLS_E_AGAIN == ret);
+        
+        if(ret < 0) {
+            if(error) {
+                g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                            _("Failed to receive encrypted data (%d): %s"),
+                            ret, gnutls_strerror(ret));
+            }
+            return -1;
+        } else
+            bin = ret;
+    } else
+#endif
+    {
+        gint ret;
+
+        do {
+            ret = recv(net_conn->fd, buf, buf_len, MSG_NOSIGNAL);
+
+            if(ret < 0 && !SHOULD_CONTINUE(net_conn)) {
+                if(error) {
+                    g_set_error(error, XFCE_MAILWATCH_ERROR,
+                                XFCE_MAILWATCH_ERROR_ABORTED, NULL);
+                }
+                return -1;
+            }
+        } while(ret < 0 && (EINTR == errno || EAGAIN == errno));
+
+        if(ret > 0)
+            bin = ret;
+    }
+
+    if(bin < 0) {
+        if(error) {
+            g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                        _("Failed to receive data: %s"), strerror(errno));
+        }
+    }
+
+    return bin;
+}
+
+gint
+xfce_mailwatch_net_conn_recv_data(XfceMailwatchNetConn *net_conn,
+                                  guchar *buf,
+                                  gsize buf_len,
+                                  GError **error)
+{
+    gint bin = 0, ret;
+
+    g_return_val_if_fail(net_conn && (!error || !*error), -1);
+    g_return_val_if_fail(net_conn->fd != -1, -1);
+
+    if(net_conn->buffer_len) {
+        if(net_conn->buffer_len <= buf_len) {
+            bin = net_conn->buffer_len;
+            memcpy(buf, net_conn->buffer, bin);
+            g_free(net_conn->buffer);
+            net_conn->buffer = NULL;
+            net_conn->buffer_len = 0;
+
+            if(bin == buf_len)
+                return bin;
+            else {
+                buf += bin;
+                buf_len -= bin;
+            }
+        } else {
+            bin = buf_len;
+            net_conn->buffer_len -= bin;
+            memcpy(buf, net_conn->buffer, bin);
+            memmove(net_conn->buffer, net_conn->buffer + bin,
+                    net_conn->buffer_len);
+            net_conn->buffer = g_realloc(net_conn->buffer,
+                                         net_conn->buffer_len + 1);
+            net_conn->buffer[net_conn->buffer_len] = 0;
+
+            return bin;
+        }
+    }
+
+    ret = xfce_mailwatch_net_conn_recv_internal(net_conn, buf, buf_len,
+                                                bin > 0 ? FALSE : TRUE,
+                                                error);
+    if(ret > 0)
+        bin += ret;
+
+    return bin;
+}
+
+gint
+xfce_mailwatch_net_conn_recv_line(XfceMailwatchNetConn *net_conn,
+                                  guchar *buf,
+                                  gsize buf_len,
+                                  GError **error)
+{
+#define BUFSTEP  1024
+    gint bin;
+    gchar *p = NULL;
+
+    g_return_val_if_fail(net_conn && (!error || !*error), -1);
+    g_return_val_if_fail(net_conn->fd != -1, -1);
+
+    do {
+        p = NULL;
+        if(net_conn->buffer_len > 0)
+            p = strstr((char *)net_conn->buffer, net_conn->line_terminator);
+
+        if(!p) {
+            net_conn->buffer = g_realloc(net_conn->buffer,
+                                         net_conn->buffer_len + BUFSTEP + 1);
+            bin = xfce_mailwatch_net_conn_recv_internal(net_conn,
+                                                        net_conn->buffer
+                                                        + net_conn->buffer_len,
+                                                        BUFSTEP, TRUE, error);
+            if(bin <= 0) {
+                net_conn->buffer = g_realloc(net_conn->buffer,
+                                             net_conn->buffer_len + 1);
+                net_conn->buffer[net_conn->buffer_len] = 0;
+                return bin;
+            }
+
+            net_conn->buffer_len += bin;
+            net_conn->buffer[net_conn->buffer_len] = 0;
+        }
+
+        /* XXX: keep this from going too crazy */
+        if(net_conn->buffer_len > (512 * 1024)) {
+            if(error) {
+                g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                            _("Canceling read: read too many bytes without a newline"));
+            }
+            return -1;
+        }
+    } while(!p);
+
+    if(buf_len < p - (gchar *)net_conn->buffer) {
+        if(error) {
+            g_set_error(error, XFCE_MAILWATCH_ERROR, 0,
+                        _("Buffer is not large enough to hold a full line (%d < %d)"),
+                        buf_len, p - (gchar *)net_conn->buffer);
+        }
+        return -1;
+    }
+
+    bin = p - (gchar *)net_conn->buffer;
+    memcpy(buf, net_conn->buffer, bin);
+
+    net_conn->buffer_len -= bin + strlen(net_conn->line_terminator);
+    memmove(net_conn->buffer, p + strlen(net_conn->line_terminator),
+            net_conn->buffer_len);
+    net_conn->buffer = g_realloc(net_conn->buffer,
+                                 net_conn->buffer_len + 1);
+    net_conn->buffer[net_conn->buffer_len] = 0;
+
+    return bin;
+}
+
+void
+xfce_mailwatch_net_conn_disconnect(XfceMailwatchNetConn *net_conn)
+{
+    g_return_if_fail(net_conn);
+    g_return_if_fail(net_conn->fd != -1);
+
+#ifdef HAVE_SSL_SUPPORT
+    if(net_conn->is_secure) {
+        gnutls_bye(net_conn->gt_session, GNUTLS_SHUT_RDWR);
+        gnutls_deinit(net_conn->gt_session);
+        gnutls_certificate_free_credentials(net_conn->gt_creds);
+        net_conn->is_secure = FALSE;
+    }
+#endif
+
+    g_free(net_conn->buffer);
+    net_conn->buffer = NULL;
+    net_conn->buffer_len = 0;
+
+    shutdown(net_conn->fd, SHUT_RDWR);
+    close(net_conn->fd);
+    net_conn->fd = -1;
+}
+
+void
+xfce_mailwatch_net_conn_destroy(XfceMailwatchNetConn *net_conn)
+{
+    g_return_if_fail(net_conn);
+
+    if(net_conn->fd != -1)
+        xfce_mailwatch_net_conn_disconnect(net_conn);
+
+    g_free(net_conn->hostname);
+    g_free(net_conn->service);
+    g_free(net_conn->buffer);  /* shouldn't need this */
+
+    g_free(net_conn);
+}
+

Added: xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.h
===================================================================
--- xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.h	                        (rev 0)
+++ xfce4-mailwatch-plugin/trunk/libmailwatch-core/mailwatch-net-conn.h	2008-08-16 08:47:36 UTC (rev 5232)
@@ -0,0 +1,77 @@
+/*
+ *  xfce4-mailwatch-plugin - a mail notification applet for the xfce4 panel
+ *  Copyright (c) 2008 Brian Tarricone <bjt23 at cornell.edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License ONLY.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAILWATCH_NET_H__
+#define __MAILWATCH_NET_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _XfceMailwatchNetConn  XfceMailwatchNetConn;
+
+typedef gboolean (*XMNCShouldContinueFunc)(XfceMailwatchNetConn *net_conn,
+                                           gpointer user_data);
+
+
+void xfce_mailwatch_net_conn_init();
+
+XfceMailwatchNetConn *xfce_mailwatch_net_conn_new(const gchar *hostname,
+                                                  const gchar *service);
+
+void xfce_mailwatch_net_conn_set_should_continue_func(XfceMailwatchNetConn *net_conn,
+                                                      XMNCShouldContinueFunc func,
+                                                      gpointer user_data);
+
+void xfce_mailwatch_net_conn_set_port(XfceMailwatchNetConn *net_conn,
+                                      guint port);
+guint xfce_mailwatch_net_conn_get_port(XfceMailwatchNetConn *net_conn);
+
+void xfce_mailwatch_net_conn_set_line_terminator(XfceMailwatchNetConn *net_conn,
+                                                 const gchar *line_term);
+const gchar *xfce_mailwatch_net_conn_get_line_terminator(XfceMailwatchNetConn *net_conn);
+
+gboolean xfce_mailwatch_net_conn_is_secure(XfceMailwatchNetConn *net_conn);
+
+gboolean xfce_mailwatch_net_conn_connect(XfceMailwatchNetConn *net_conn,
+                                         GError **error);
+
+gboolean xfce_mailwatch_net_conn_make_secure(XfceMailwatchNetConn *net_conn,
+                                             GError **error);
+
+gint xfce_mailwatch_net_conn_send_data(XfceMailwatchNetConn *net_conn,
+                                       const guchar *buf,
+                                       gsize buf_len,
+                                       GError **error);
+
+gint xfce_mailwatch_net_conn_recv_data(XfceMailwatchNetConn *net_conn,
+                                       guchar *buf,
+                                       gsize buf_len,
+                                       GError **error);
+gint xfce_mailwatch_net_conn_recv_line(XfceMailwatchNetConn *net_conn,
+                                       guchar *buf,
+                                       gsize buf_len,
+                                       GError **error);
+
+void xfce_mailwatch_net_conn_disconnect(XfceMailwatchNetConn *net_conn);
+void xfce_mailwatch_net_conn_destroy(XfceMailwatchNetConn *net_conn);
+
+G_END_DECLS
+
+#endif  /* __MAILWATCH_NET_H__ */
+




More information about the Goodies-commits mailing list