/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2004 Imendio AB
* Copyright (C) 2004 Josh Beam <josh@3ddrome.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*/
#include <config.h>
#include <glib.h>
#include <string.h>
#ifndef G_OS_WIN32
#include <unistd.h>
#include <sys/socket.h>
#else /* G_OS_WIN32 */
#include <winsock2.h>
#endif /* G_OS_WIN32 */
#include "lm-internals.h"
#include "lm-proxy.h"
#include "lm-debug.h"
#include "lm-utils.h"
struct _LmProxy {
LmProxyType type;
gchar *server;
guint port;
gchar *username;
gchar *password;
guint io_watch;
gint ref_count;
};
static void proxy_free (LmProxy *proxy);
static gboolean proxy_http_negotiate (LmProxy *proxy,
gint fd,
const gchar *server,
guint port);
static gboolean proxy_negotiate (LmProxy *proxy,
gint fd,
const gchar *server,
guint port);
static gboolean proxy_http_read_cb (GIOChannel *source,
GIOCondition condition,
gpointer data);
static gboolean proxy_read_cb (GIOChannel *source,
GIOCondition condition,
gpointer data);
static void
proxy_free (LmProxy *proxy)
{
g_free (proxy->server);
g_free (proxy->username);
g_free (proxy->password);
g_free (proxy);
}
static gboolean
proxy_http_negotiate (LmProxy *proxy, gint fd, const gchar *server, guint port)
{
gchar *str;
if (proxy->username && proxy->password) {
gchar *tmp1;
gchar *tmp2;
tmp1 = g_strdup_printf ("%s:%s",
proxy->username,
proxy->password);
tmp2 = _lm_utils_base64_encode (tmp1);
g_free (tmp1);
str = g_strdup_printf ("CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nProxy-Authorization: Basic %s\r\n\r\n",
server, port,
server, port,
tmp2);
g_free (tmp2);
} else {
str = g_strdup_printf ("CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\n\r\n",
server, port,
server, port);
}
send (fd, str, strlen (str), 0);
g_free (str);
return TRUE;
}
/* returns TRUE when connected through proxy */
static gboolean
proxy_http_read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
{
gchar buf[512];
gsize bytes_read;
GError *error = NULL;
g_io_channel_read_chars (source, buf, 512, &bytes_read, &error);
if (bytes_read < 16) {
return FALSE;
}
if (strncmp (buf, "HTTP/1.1 200", 12) != 0 &&
strncmp (buf, "HTTP/1.0 200", 12) != 0) {
return FALSE;
}
if (strncmp (buf + (bytes_read - 4), "\r\n\r\n", 4) != 0) {
return FALSE;
}
UNUSED_FORMAL_PARAM(condition);
UNUSED_FORMAL_PARAM(data);
return TRUE;
}
static gboolean
proxy_read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
{
LmConnectData *connect_data;
LmConnection *connection;
LmProxy *proxy;
gboolean retval = FALSE;
connect_data = (LmConnectData *) data;
connection = connect_data->connection;
proxy = lm_connection_get_proxy (connection);
g_return_val_if_fail (proxy != NULL, FALSE);
if (lm_connection_is_open (connection)) {
return FALSE;
}
switch (lm_proxy_get_type (proxy)) {
default:
case LM_PROXY_TYPE_NONE:
g_assert_not_reached ();
break;
case LM_PROXY_TYPE_HTTP:
retval = proxy_http_read_cb (source, condition, data);
break;
}
if (retval == TRUE) {
g_source_remove (proxy->io_watch);
_lm_socket_succeeded ((LmConnectData *) data);
}
//retval is false
//route call to connect_cb
else
{
_lm_socket_failed_with_error(connect_data, _LM_SOCK_EINVAL);
}
return FALSE;
}
gboolean
proxy_negotiate (LmProxy *proxy, gint fd, const gchar *server, guint port)
{
g_return_val_if_fail (proxy != NULL, FALSE);
switch (proxy->type) {
case LM_PROXY_TYPE_NONE:
return TRUE;
case LM_PROXY_TYPE_HTTP:
return proxy_http_negotiate (proxy, fd, server, port);
default:
g_assert_not_reached ();
}
return FALSE;
}
gboolean
_lm_proxy_connect_cb (GIOChannel *source, GIOCondition condition, gpointer data)
{
LmConnection *connection;
LmConnectData *connect_data;
LmProxy *proxy;
int error;
socklen_t len;
connect_data = (LmConnectData *) data;
connection = connect_data->connection;
proxy = lm_connection_get_proxy (connection);
g_return_val_if_fail (proxy != NULL, FALSE);
if (condition == G_IO_ERR) {
len = sizeof (error);
_lm_sock_get_error (connect_data->fd, &error, &len);
_lm_socket_failed_with_error (connect_data, error);
return FALSE;
} else if (condition == G_IO_OUT) {
if (!proxy_negotiate (lm_connection_get_proxy (connection), connect_data->fd, lm_connection_get_server (connection), lm_connection_get_port (connection))) {
_lm_socket_failed (connect_data);
return FALSE;
}
proxy->io_watch = g_io_add_watch (connect_data->io_channel,
G_IO_IN|G_IO_ERR,
(GIOFunc) proxy_read_cb,
connect_data);
} else {
g_assert_not_reached ();
}
UNUSED_FORMAL_PARAM(source);
return FALSE;
}
/**
* lm_proxy_new
* @type: the type of the new proxy
*
* Creates a new Proxy. Used #lm_connection_set_proxy to make a connection
* user this proxy.
*
* Return value: a newly create proxy
**/
EXPORT_C LmProxy *
lm_proxy_new (LmProxyType type)
{
LmProxy *proxy;
proxy = g_new0 (LmProxy, 1);
proxy->ref_count = 1;
proxy->type = type;
switch (proxy->type) {
case LM_PROXY_TYPE_HTTP:
proxy->port = 8000;
break;
default:
proxy->port = 0;
}
return proxy;
}
/**
* lm_proxy_new_with_server
* @type: the type of the new proxy
* @server: the proxy server
* @port: the proxy server port
*
* Creates a new Proxy. Use #lm_connection_set_proxy to make a connection
* user this proxy.
*
* Return value: a newly create proxy
**/
EXPORT_C LmProxy *
lm_proxy_new_with_server (LmProxyType type,
const gchar *server,
guint port)
{
LmProxy *proxy;
proxy = lm_proxy_new (type);
lm_proxy_set_server (proxy, server);
lm_proxy_set_port (proxy, port);
return proxy;
}
/**
* lm_proxy_get_type
* @proxy: an #LmProxy
*
* Fetches the proxy type
*
* Return value: the type
**/
EXPORT_C LmProxyType
lm_proxy_get_type (LmProxy *proxy)
{
g_return_val_if_fail (proxy != NULL, LM_PROXY_TYPE_NONE);
return proxy->type;
}
/**
* lm_proxy_set_type
* @proxy: an #LmProxy
* @type: an LmProxyType
*
* Sets the proxy type for @proxy to @type.
**/
EXPORT_C void
lm_proxy_set_type (LmProxy *proxy, LmProxyType type)
{
g_return_if_fail (proxy != NULL);
proxy->type = type;
}
/**
* lm_proxy_get_server:
* @proxy: an #LmProxy
*
* Fetches the server address that @proxy is using.
*
* Return value: the proxy server address
**/
EXPORT_C const gchar *
lm_proxy_get_server (LmProxy *proxy)
{
g_return_val_if_fail (proxy != NULL, NULL);
return proxy->server;
}
/**
* lm_proxy_set_server:
* @proxy: an #LmProxy
* @server: Address of the proxy server
*
* Sets the server address for @proxy to @server.
**/
EXPORT_C void
lm_proxy_set_server (LmProxy *proxy, const gchar *server)
{
g_return_if_fail (proxy != NULL);
g_return_if_fail (server != NULL);
g_free (proxy->server);
proxy->server = _lm_utils_hostname_to_punycode (server);
}
/**
* lm_proxy_get_port:
* @proxy: an #LmProxy
*
* Fetches the port that @proxy is using.
*
* Return value: The port
**/
EXPORT_C guint
lm_proxy_get_port (LmProxy *proxy)
{
g_return_val_if_fail (proxy != NULL, 0);
return proxy->port;
}
/**
* lm_proxy_set_port:
* @proxy: an #LmProxy
* @port: proxy server port
*
* Sets the server port that @proxy will be using.
**/
EXPORT_C void
lm_proxy_set_port (LmProxy *proxy, guint port)
{
g_return_if_fail (proxy != NULL);
proxy->port = port;
}
/**
* lm_proxy_get_username:
* @proxy: an #LmProxy
*
* Fetches the username that @proxy is using.
*
* Return value: the username
**/
EXPORT_C const gchar *
lm_proxy_get_username (LmProxy *proxy)
{
g_return_val_if_fail (proxy != NULL, NULL);
return proxy->username;
}
/**
* lm_proxy_set_username:
* @proxy: an #LmProxy
* @username: Username
*
* Sets the username for @proxy to @username or %NULL to unset.
**/
EXPORT_C void
lm_proxy_set_username (LmProxy *proxy, const gchar *username)
{
g_return_if_fail (proxy != NULL);
g_free (proxy->username);
if (username) {
proxy->username = g_strdup (username);
} else {
proxy->username = NULL;
}
}
/**
* lm_proxy_get_password:
* @proxy: an #LmProxy
*
* Fetches the password that @proxy is using.
*
* Return value: the proxy password
**/
EXPORT_C const gchar *
lm_proxy_get_password (LmProxy *proxy)
{
g_return_val_if_fail (proxy != NULL, NULL);
return proxy->password;
}
/**
* lm_proxy_set_password:
* @proxy: an #LmProxy
* @password: Password
*
* Sets the password for @proxy to @password or %NULL to unset.
**/
EXPORT_C void
lm_proxy_set_password (LmProxy *proxy, const gchar *password)
{
g_return_if_fail (proxy != NULL);
g_free (proxy->password);
if (password) {
proxy->password = g_strdup (password);
} else {
proxy->password = NULL;
}
}
/**
* lm_proxy_ref:
* @proxy: an #LmProxy
*
* Adds a reference to @proxy.
*
* Return value: the proxy
**/
EXPORT_C LmProxy *
lm_proxy_ref (LmProxy *proxy)
{
g_return_val_if_fail (proxy != NULL, NULL);
proxy->ref_count++;
return proxy;
}
/**
* lm_proxy_unref
* @proxy: an #LmProxy
*
* Removes a reference from @proxy. When no more references are present
* @proxy is freed.
**/
EXPORT_C void
lm_proxy_unref (LmProxy *proxy)
{
g_return_if_fail (proxy != NULL);
proxy->ref_count--;
if (proxy->ref_count == 0) {
proxy_free (proxy);
}
}