* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* 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, 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
/* this is a little piece of code to handle proxy connection */
/* it is intended to : 1st handle http proxy, using the CONNECT command
, 2nd provide an easy way to add socks support
, 3rd draw women to it like flies to honey */
#include <libsoup/soup.h>
PurpleProxyType type; /* The proxy type. */
char *host; /* The host. */
int port; /* The port number. */
char *username; /* The username. */
char *password; /* The password. */
struct _PurpleProxyConnectData {
PurpleProxyConnectFunction connect_cb;
GCancellable *cancellable;
static PurpleProxyInfo *global_proxy_info = NULL;
static GSList *handles = NULL;
* TODO: Eventually (GObjectification) this bad boy will be removed, because it is
* a gross fix for a crashy problem.
#define PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data) g_slist_find(handles, connect_data)
/**************************************************************************
**************************************************************************/
purple_proxy_info_new(void)
return g_new0(PurpleProxyInfo, 1);
purple_proxy_info_copy(PurpleProxyInfo *info)
g_return_val_if_fail(info != NULL, NULL);
copy = purple_proxy_info_new();
copy->host = g_strdup(info->host);
copy->username = g_strdup(info->username);
copy->password = g_strdup(info->password);
purple_proxy_info_destroy(PurpleProxyInfo *info)
g_return_if_fail(info != NULL);
purple_proxy_info_set_proxy_type(PurpleProxyInfo *info, PurpleProxyType type)
g_return_if_fail(info != NULL);
purple_proxy_info_set_host(PurpleProxyInfo *info, const char *host)
g_return_if_fail(info != NULL);
info->host = g_strdup(host);
purple_proxy_info_set_port(PurpleProxyInfo *info, int port)
g_return_if_fail(info != NULL);
purple_proxy_info_set_username(PurpleProxyInfo *info, const char *username)
g_return_if_fail(info != NULL);
info->username = g_strdup(username);
purple_proxy_info_set_password(PurpleProxyInfo *info, const char *password)
g_return_if_fail(info != NULL);
info->password = g_strdup(password);
purple_proxy_info_get_proxy_type(const PurpleProxyInfo *info)
g_return_val_if_fail(info != NULL, PURPLE_PROXY_NONE);
purple_proxy_info_get_host(const PurpleProxyInfo *info)
g_return_val_if_fail(info != NULL, NULL);
purple_proxy_info_get_port(const PurpleProxyInfo *info)
g_return_val_if_fail(info != NULL, 0);
purple_proxy_info_get_username(const PurpleProxyInfo *info)
g_return_val_if_fail(info != NULL, NULL);
purple_proxy_info_get_password(const PurpleProxyInfo *info)
g_return_val_if_fail(info != NULL, NULL);
G_DEFINE_BOXED_TYPE(PurpleProxyInfo, purple_proxy_info,
purple_proxy_info_copy, purple_proxy_info_destroy);
/**************************************************************************
**************************************************************************/
purple_global_proxy_get_info(void)
return global_proxy_info;
purple_global_proxy_set_info(PurpleProxyInfo *info)
g_return_if_fail(info != NULL);
purple_proxy_info_destroy(global_proxy_info);
global_proxy_info = info;
/* index in gproxycmds below, keep them in sync */
#define GNOME_PROXY_MODE 0
#define GNOME_PROXY_USE_SAME_PROXY 1
#define GNOME_PROXY_SOCKS_HOST 2
#define GNOME_PROXY_SOCKS_PORT 3
#define GNOME_PROXY_HTTP_HOST 4
#define GNOME_PROXY_HTTP_PORT 5
#define GNOME_PROXY_HTTP_USER 6
#define GNOME_PROXY_HTTP_PASS 7
/* detect proxy settings for gnome2/gnome3 */
static const char* gproxycmds[][2] = {
{ "gconftool-2 -g /system/proxy/mode" , "gsettings get org.gnome.system.proxy mode" },
{ "gconftool-2 -g /system/http_proxy/use_same_proxy", "gsettings get org.gnome.system.proxy use-same-proxy" },
{ "gconftool-2 -g /system/proxy/socks_host", "gsettings get org.gnome.system.proxy.socks host" },
{ "gconftool-2 -g /system/proxy/socks_port", "gsettings get org.gnome.system.proxy.socks port" },
{ "gconftool-2 -g /system/http_proxy/host", "gsettings get org.gnome.system.proxy.http host" },
{ "gconftool-2 -g /system/http_proxy/port", "gsettings get org.gnome.system.proxy.http port"},
{ "gconftool-2 -g /system/http_proxy/authentication_user", "gsettings get org.gnome.system.proxy.http authentication-user" },
{ "gconftool-2 -g /system/http_proxy/authentication_password", "gsettings get org.gnome.system.proxy.http authentication-password" },
* purple_gnome_proxy_get_parameter:
* @parameter: One of the GNOME_PROXY_x constants defined above
* @gnome_version: GNOME2_CMDS or GNOME3_CMDS
* This is a utility function used to retrieve proxy parameter values from
* Returns: The value of requested proxy parameter
purple_gnome_proxy_get_parameter(guint8 parameter, guint8 gnome_version)
if (parameter > GNOME_PROXY_HTTP_PASS)
if (gnome_version > GNOME3_CMDS)
if (!g_spawn_command_line_sync(gproxycmds[parameter][gnome_version],
¶m, &err, NULL, NULL))
if (param[0] == '\'' || param[0] == '\"') {
param_len = strlen(param);
memmove(param, param + 1, param_len); /* copy last \0 too */
if (param_len > 0 && (param[param_len - 1] == '\'' || param[param_len - 1] == '\"'))
param[param_len - 1] = '\0';
purple_gnome_proxy_get_info(void)
static PurpleProxyInfo info = {0, NULL, 0, NULL, NULL};
gboolean use_same_proxy = FALSE;
guint8 gnome_version = GNOME3_CMDS;
tmp = g_find_program_in_path("gsettings");
tmp = g_find_program_in_path("gconftool-2");
gnome_version = GNOME2_CMDS;
return purple_global_proxy_get_info();
/* Check whether to use a proxy. */
tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_MODE, gnome_version);
return purple_global_proxy_get_info();
if (purple_strequal(tmp, "none")) {
info.type = PURPLE_PROXY_NONE;
if (!purple_strequal(tmp, "manual")) {
/* Unknown setting. Fallback to using our global proxy settings. */
return purple_global_proxy_get_info();
/* Free the old fields */
tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_USE_SAME_PROXY, gnome_version);
return purple_global_proxy_get_info();
if (purple_strequal(tmp, "true"))
info.host = purple_gnome_proxy_get_parameter(GNOME_PROXY_SOCKS_HOST, gnome_version);
return purple_global_proxy_get_info();
if (!use_same_proxy && (info.host != NULL) && (*info.host != '\0')) {
info.type = PURPLE_PROXY_SOCKS5;
tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_SOCKS_PORT, gnome_version);
return purple_global_proxy_get_info();
info.host = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_HOST, gnome_version);
return purple_global_proxy_get_info();
/* If we get this far then we know we're using an HTTP proxy */
info.type = PURPLE_PROXY_HTTP;
purple_debug_info("proxy", "Gnome proxy settings are set to "
"'manual' but no suitable proxy server is specified. Using "
"Pidgin's proxy settings instead.\n");
return purple_global_proxy_get_info();
info.username = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_USER, gnome_version);
return purple_global_proxy_get_info();
info.password = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_PASS, gnome_version);
return purple_global_proxy_get_info();
tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_PORT, gnome_version);
return purple_global_proxy_get_info();
typedef BOOL (CALLBACK* LPFNWINHTTPGETIEPROXYCONFIG)(/*IN OUT*/ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* pProxyConfig);
/* This modifies "host" in-place evilly */
_proxy_fill_hostinfo(PurpleProxyInfo *info, char *host, int default_port)
d = g_strrstr(host, ":");
purple_proxy_info_set_host(info, host);
purple_proxy_info_set_port(info, port);
purple_win32_proxy_get_info(void)
static LPFNWINHTTPGETIEPROXYCONFIG MyWinHttpGetIEProxyConfig = NULL;
static gboolean loaded = FALSE;
static PurpleProxyInfo info = {0, NULL, 0, NULL, NULL};
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config;
MyWinHttpGetIEProxyConfig = (LPFNWINHTTPGETIEPROXYCONFIG)
wpurple_find_and_loadproc("winhttp.dll", "WinHttpGetIEProxyConfigForCurrentUser");
if (!MyWinHttpGetIEProxyConfig)
purple_debug_warning("proxy", "Unable to read Windows Proxy Settings.\n");
if (!MyWinHttpGetIEProxyConfig)
ZeroMemory(&ie_proxy_config, sizeof(ie_proxy_config));
if (!MyWinHttpGetIEProxyConfig(&ie_proxy_config)) {
purple_debug_error("proxy", "Error reading Windows Proxy Settings(%lu).\n", GetLastError());
/* We can't do much if it is autodetect*/
if (ie_proxy_config.fAutoDetect) {
purple_debug_error("proxy", "Windows Proxy Settings set to autodetect (not supported).\n");
/* TODO: For 3.0.0 we'll revisit this (maybe)*/
} else if (ie_proxy_config.lpszProxy) {
gchar *proxy_list = g_utf16_to_utf8(ie_proxy_config.lpszProxy, -1,
/* We can't do anything about the bypass list, as we don't have the url */
/* TODO: For 3.0.0 we'll revisit this*/
/* There are proxy settings for several protocols */
if (proxy_list && *proxy_list) {
char *specific = NULL, *tmp;
/* If there is only a global proxy, which means "HTTP" */
if (!strchr(proxy_list, ';') || (specific = g_strstr_len(proxy_list, -1, "http=")) != NULL) {
specific += strlen("http=");
tmp = strchr(specific, ';');
/* specific now points the proxy server (and port) */
purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_HTTP);
_proxy_fill_hostinfo(&info, specific, 80);
/* TODO: is there a way to set the username/password? */
purple_proxy_info_set_username(&info, NULL);
purple_proxy_info_set_password(&info, NULL);
purple_debug_info("proxy", "Windows Proxy Settings: HTTP proxy: '%s:%d'.\n",
purple_proxy_info_get_host(&info),
purple_proxy_info_get_port(&info));
} else if ((specific = g_strstr_len(proxy_list, -1, "socks=")) != NULL) {
specific += strlen("socks=");
tmp = strchr(specific, ';');
/* specific now points the proxy server (and port) */
purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_SOCKS5);
_proxy_fill_hostinfo(&info, specific, 1080);
/* TODO: is there a way to set the username/password? */
purple_proxy_info_set_username(&info, NULL);
purple_proxy_info_set_password(&info, NULL);
purple_debug_info("proxy", "Windows Proxy Settings: SOCKS5 proxy: '%s:%d'.\n",
purple_proxy_info_get_host(&info),
purple_proxy_info_get_port(&info));
purple_debug_info("proxy", "Windows Proxy Settings: No supported proxy specified.\n");
purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_NONE);
/* TODO: Fix API to be able look at proxy bypass settings */
purple_debug_info("proxy", "No Windows proxy set.\n");
purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_NONE);
if (ie_proxy_config.lpszAutoConfigUrl)
GlobalFree(ie_proxy_config.lpszAutoConfigUrl);
if (ie_proxy_config.lpszProxy)
GlobalFree(ie_proxy_config.lpszProxy);
if (ie_proxy_config.lpszProxyBypass)
GlobalFree(ie_proxy_config.lpszProxyBypass);
/**************************************************************************
**************************************************************************/
* Whoever calls this needs to have called
* purple_proxy_connect_data_disconnect() beforehand.
purple_proxy_connect_data_destroy(PurpleProxyConnectData *connect_data)
if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data))
handles = g_slist_remove(handles, connect_data);
if(G_IS_CANCELLABLE(connect_data->cancellable)) {
g_cancellable_cancel(connect_data->cancellable);
g_object_unref(G_OBJECT(connect_data->cancellable));
connect_data->cancellable = NULL;
g_free(connect_data->host);
* purple_proxy_connect_data_disconnect:
* @error_message: An error message explaining why the connection
* failed. This will be passed to the callback function
* specified in the call to purple_proxy_connect(). If the
* connection was successful then pass in null.
* Free all information dealing with a connection attempt and
* reset the connect_data to prepare for it to try to connect
* If an error message is passed in, then we know the connection
* attempt failed. If so, we call the callback with the given
* error message, then destroy the connect_data.
purple_proxy_connect_data_disconnect(PurpleProxyConnectData *connect_data, const gchar *error_message)
if (connect_data->fd >= 0)
if (error_message != NULL)
purple_debug_error("proxy", "Connection attempt failed: %s\n",
/* Everything failed! Tell the originator of the request. */
connect_data->connect_cb(connect_data->data, -1, error_message);
purple_proxy_connect_data_destroy(connect_data);
purple_proxy_connect_data_connected(PurpleProxyConnectData *connect_data)
purple_debug_info("proxy", "Connected to %s:%d.\n",
connect_data->host, connect_data->port);
connect_data->connect_cb(connect_data->data, connect_data->fd, NULL);
* We've passed the file descriptor to the protocol, so it's no longer
* our responsibility, and we should be careful not to free it when
* we destroy the connect_data.
purple_proxy_connect_data_disconnect(connect_data, NULL);
purple_proxy_connect_data_destroy(connect_data);
purple_proxy_get_setup(PurpleAccount *account)
PurpleProxyInfo *gpi = NULL;
/* This is used as a fallback so we don't overwrite the selected proxy type */
static PurpleProxyInfo *tmp_none_proxy_info = NULL;
if (!tmp_none_proxy_info) {
tmp_none_proxy_info = purple_proxy_info_new();
purple_proxy_info_set_proxy_type(tmp_none_proxy_info, PURPLE_PROXY_NONE);
if (account && purple_account_get_proxy_info(account) != NULL) {
gpi = purple_account_get_proxy_info(account);
if (purple_proxy_info_get_proxy_type(gpi) == PURPLE_PROXY_USE_GLOBAL)
if (purple_running_gnome())
gpi = purple_gnome_proxy_get_info();
gpi = purple_global_proxy_get_info();
if (purple_proxy_info_get_proxy_type(gpi) == PURPLE_PROXY_USE_ENVVAR) {
if ((tmp = g_getenv("HTTP_PROXY")) != NULL ||
(tmp = g_getenv("http_proxy")) != NULL ||
(tmp = g_getenv("HTTPPROXY")) != NULL)
* export http_proxy="http://user:passwd@your.proxy.server:port/"
if (!SOUP_URI_VALID_FOR_HTTP(url)) {
purple_debug_warning("proxy", "Couldn't parse URL: %s", tmp);
purple_proxy_info_set_host(gpi, url->host);
purple_proxy_info_set_username(gpi, url->user);
purple_proxy_info_set_password(gpi, url->password);
purple_proxy_info_set_port(gpi, url->port);
/* XXX: Do we want to skip this step if user/password/port were part of url? */
if ((tmp = g_getenv("HTTP_PROXY_USER")) != NULL ||
(tmp = g_getenv("http_proxy_user")) != NULL ||
(tmp = g_getenv("HTTPPROXYUSER")) != NULL)
purple_proxy_info_set_username(gpi, tmp);
if ((tmp = g_getenv("HTTP_PROXY_PASS")) != NULL ||
(tmp = g_getenv("http_proxy_pass")) != NULL ||
(tmp = g_getenv("HTTPPROXYPASS")) != NULL)
purple_proxy_info_set_password(gpi, tmp);
if ((tmp = g_getenv("HTTP_PROXY_PORT")) != NULL ||
(tmp = g_getenv("http_proxy_port")) != NULL ||
(tmp = g_getenv("HTTPPROXYPORT")) != NULL)
purple_proxy_info_set_port(gpi, atoi(tmp));
if ((wgpi = purple_win32_proxy_get_info()) != NULL)
/* no proxy environment variable found, don't use a proxy */
purple_debug_info("proxy", "No environment settings found, not using a proxy\n");
gpi = tmp_none_proxy_info;
/* Grabbed duplicate_fd() from GLib's testcases (gio/tests/socket.c).
* Can be dropped once this API has been converted to Gio.
if (!DuplicateHandle (GetCurrentProcess (),
/* End function grabbed from GLib */
connect_to_host_cb(GObject *source, GAsyncResult *res, gpointer user_data)
PurpleProxyConnectData *connect_data = user_data;
conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
/* Ignore cancelled error as that signifies connect_data has
if (!g_error_matches(error, G_IO_ERROR,
purple_debug_error("proxy", "Unable to connect to "
"destination host: %s\n",
purple_proxy_connect_data_disconnect(connect_data,
"Unable to connect to destination "
socket = g_socket_connection_get_socket(conn);
g_assert(socket != NULL);
/* Duplicate the file descriptor, and then free the connection.
* libpurple's proxy code doesn't keep an object around for the
* lifetime of the connection. Therefore, in order to not leak
* memory, the GSocketConnection must be freed here. In order
* to avoid the double close/free of the file descriptor, the
* file descriptor is duplicated.
connect_data->fd = duplicate_fd(g_socket_get_fd(socket));
purple_proxy_connect_data_connected(connect_data);
purple_proxy_connect(void *handle, PurpleAccount *account,
const char *host, int port,
PurpleProxyConnectFunction connect_cb, gpointer data)
PurpleProxyConnectData *connect_data;
g_return_val_if_fail(host != NULL, NULL);
g_return_val_if_fail(port > 0, NULL);
g_return_val_if_fail(connect_cb != NULL, NULL);
client = purple_gio_socket_client_new(account, &error);
/* Assume it's a proxy error */
purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
purple_request_cpar_from_account(account));
connect_data = g_new0(PurpleProxyConnectData, 1);
connect_data->handle = handle;
connect_data->connect_cb = connect_cb;
connect_data->data = data;
connect_data->host = g_strdup(host);
connect_data->port = port;
connect_data->gpi = purple_proxy_get_setup(account);
connect_data->cancellable = g_cancellable_new();
purple_debug_info("proxy", "Attempting connection to %s:%u\n",
g_socket_client_connect_to_host_async(client, host, port,
connect_data->cancellable, connect_to_host_cb,
handles = g_slist_prepend(handles, connect_data);
socks5_proxy_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data)
PurpleProxyConnectData *connect_data = user_data;
stream = g_proxy_connect_finish(G_PROXY(source), res, &error);
/* Ignore cancelled error as that signifies connect_data has
if (!g_error_matches(error, G_IO_ERROR,
purple_debug_error("proxy", "Unable to connect to "
"destination host: %s\n",
purple_proxy_connect_data_disconnect(connect_data,
"Unable to connect to destination "
if (!G_IS_SOCKET_CONNECTION(stream)) {
purple_debug_error("proxy",
"GProxy didn't return a GSocketConnection.\n");
purple_proxy_connect_data_disconnect(connect_data,
"GProxy didn't return a GSocketConnection.\n");
socket = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
/* Duplicate the file descriptor, and then free the connection.
* libpurple's proxy code doesn't keep an object around for the
* lifetime of the connection. Therefore, in order to not leak
* memory, the GSocketConnection (aka GIOStream here) must be
* freed here. In order to avoid the double close/free of the
* file descriptor, the file descriptor is duplicated.
connect_data->fd = duplicate_fd(g_socket_get_fd(socket));
purple_proxy_connect_data_connected(connect_data);
/* This is called when we connect to the SOCKS5 proxy server (through any
* relevant account proxy)
socks5_connect_to_host_cb(GObject *source, GAsyncResult *res,
PurpleProxyConnectData *connect_data = user_data;
GInetSocketAddress *inet_addr;
GSocketAddress *proxy_addr;
conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
/* Ignore cancelled error as that signifies connect_data has
if (!g_error_matches(error, G_IO_ERROR,
purple_debug_error("proxy", "Unable to connect to "
"SOCKS5 host: %s\n", error->message);
purple_proxy_connect_data_disconnect(connect_data,
"Unable to connect to SOCKS5 host.\n");
proxy = g_proxy_get_default_for_protocol("socks5");
purple_debug_error("proxy", "SOCKS5 proxy backend missing.\n");
purple_proxy_connect_data_disconnect(connect_data,
"SOCKS5 proxy backend missing.\n");
info = connect_data->gpi;
addr = g_socket_connection_get_remote_address(conn, &error);
purple_debug_error("proxy", "Unable to retrieve SOCKS5 host "
"address from connection: %s\n",
purple_proxy_connect_data_disconnect(connect_data,
"Unable to retrieve SOCKS5 host address from "
inet_addr = G_INET_SOCKET_ADDRESS(addr);
proxy_addr = g_proxy_address_new(
g_inet_socket_address_get_address(inet_addr),
g_inet_socket_address_get_port(inet_addr),
"socks5", connect_data->host, connect_data->port,
purple_proxy_info_get_username(info),
purple_proxy_info_get_password(info));
g_object_unref(inet_addr);
purple_debug_info("proxy", "Initiating SOCKS5 negotiation.\n");
purple_debug_info("proxy",
"Connecting to %s:%d via %s:%d using SOCKS5\n",
connect_data->host, connect_data->port,
purple_proxy_info_get_host(connect_data->gpi),
purple_proxy_info_get_port(connect_data->gpi));
g_proxy_connect_async(proxy, G_IO_STREAM(conn),
G_PROXY_ADDRESS(proxy_addr),
connect_data->cancellable,
socks5_proxy_connect_cb, connect_data);
g_object_unref(proxy_addr);
* Combine some of this code with purple_proxy_connect()
purple_proxy_connect_socks5_account(void *handle, PurpleAccount *account,
const char *host, int port,
PurpleProxyConnectFunction connect_cb,
PurpleProxyConnectData *connect_data;
g_return_val_if_fail(host != NULL, NULL);
g_return_val_if_fail(port >= 0, NULL);
g_return_val_if_fail(connect_cb != NULL, NULL);
client = purple_gio_socket_client_new(account, &error);
/* Assume it's a proxy error */
purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
purple_request_cpar_from_account(account));
connect_data = g_new0(PurpleProxyConnectData, 1);
connect_data->handle = handle;
connect_data->connect_cb = connect_cb;
connect_data->data = data;
connect_data->host = g_strdup(host);
connect_data->port = port;
connect_data->cancellable = g_cancellable_new();
purple_debug_info("proxy",
"Connecting to %s:%d via %s:%d using SOCKS5\n",
connect_data->host, connect_data->port,
purple_proxy_info_get_host(connect_data->gpi),
purple_proxy_info_get_port(connect_data->gpi));
g_socket_client_connect_to_host_async(client,
purple_proxy_info_get_host(connect_data->gpi),
purple_proxy_info_get_port(connect_data->gpi),
connect_data->cancellable, socks5_connect_to_host_cb,
handles = g_slist_prepend(handles, connect_data);
purple_proxy_connect_cancel(PurpleProxyConnectData *connect_data)
g_return_if_fail(connect_data != NULL);
purple_proxy_connect_data_disconnect(connect_data, NULL);
purple_proxy_connect_data_destroy(connect_data);
purple_proxy_connect_cancel_with_handle(void *handle)
for (l = handles; l != NULL; l = l_next) {
PurpleProxyConnectData *connect_data = l->data;
if (connect_data->handle == handle)
purple_proxy_connect_cancel(connect_data);
purple_proxy_get_proxy_resolver(PurpleAccount *account, GError **error)
PurpleProxyInfo *info = purple_proxy_get_setup(account);
GProxyResolver *resolver;
if (purple_proxy_info_get_proxy_type(info) == PURPLE_PROXY_NONE) {
/* Return an empty simple resolver, which will resolve on direct
return g_simple_proxy_resolver_new(NULL, NULL);
switch (purple_proxy_info_get_proxy_type(info))
/* PURPLE_PROXY_NONE already handled above */
case PURPLE_PROXY_USE_ENVVAR:
/* Intentional passthrough */
case PURPLE_PROXY_SOCKS4:
case PURPLE_PROXY_SOCKS5:
/* Intentional passthrough */
g_set_error(error, PURPLE_CONNECTION_ERROR,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid Proxy type (%d) specified"),
purple_proxy_info_get_proxy_type(info));
if (purple_proxy_info_get_host(info) == NULL ||
purple_proxy_info_get_port(info) <= 0) {
g_set_error_literal(error, PURPLE_CONNECTION_ERROR,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Either the host name or port number "
"specified for your given proxy type is "
/* Everything checks out. Create and return the GProxyResolver */
username = purple_proxy_info_get_username(info);
password = purple_proxy_info_get_password(info);
/* Username and password are optional */
if (username != NULL && password != NULL) {
auth = g_strdup_printf("%s:%s@", username, password);
} else if (username != NULL) {
auth = g_strdup_printf("%s@", username);
proxy = g_strdup_printf("%s://%s%s:%i", protocol,
auth != NULL ? auth : "",
purple_proxy_info_get_host(info),
purple_proxy_info_get_port(info));
resolver = g_simple_proxy_resolver_new(proxy, NULL);
proxy_pref_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
PurpleProxyInfo *info = purple_global_proxy_get_info();
if (purple_strequal(name, "/purple/proxy/type")) {
const char *type = value;
if (purple_strequal(type, "none"))
proxytype = PURPLE_PROXY_NONE;
else if (purple_strequal(type, "http"))
proxytype = PURPLE_PROXY_HTTP;
else if (purple_strequal(type, "socks4"))
proxytype = PURPLE_PROXY_SOCKS4;
else if (purple_strequal(type, "socks5"))
proxytype = PURPLE_PROXY_SOCKS5;
else if (purple_strequal(type, "tor"))
proxytype = PURPLE_PROXY_TOR;
else if (purple_strequal(type, "envvar"))
proxytype = PURPLE_PROXY_USE_ENVVAR;
purple_proxy_info_set_proxy_type(info, proxytype);
} else if (purple_strequal(name, "/purple/proxy/host"))
purple_proxy_info_set_host(info, value);
else if (purple_strequal(name, "/purple/proxy/port"))
purple_proxy_info_set_port(info, GPOINTER_TO_INT(value));
else if (purple_strequal(name, "/purple/proxy/username"))
purple_proxy_info_set_username(info, value);
else if (purple_strequal(name, "/purple/proxy/password"))
purple_proxy_info_set_password(info, value);
purple_proxy_get_handle()
/* Initialize a default proxy info struct. */
global_proxy_info = purple_proxy_info_new();
purple_prefs_add_none("/purple/proxy");
purple_prefs_add_string("/purple/proxy/type", "none");
purple_prefs_add_string("/purple/proxy/host", "");
purple_prefs_add_int("/purple/proxy/port", 0);
purple_prefs_add_string("/purple/proxy/username", "");
purple_prefs_add_string("/purple/proxy/password", "");
purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE);
/* Setup callbacks for the preferences. */
handle = purple_proxy_get_handle();
purple_prefs_connect_callback(handle, "/purple/proxy/type", proxy_pref_cb,
purple_prefs_connect_callback(handle, "/purple/proxy/host", proxy_pref_cb,
purple_prefs_connect_callback(handle, "/purple/proxy/port", proxy_pref_cb,
purple_prefs_connect_callback(handle, "/purple/proxy/username",
purple_prefs_connect_callback(handle, "/purple/proxy/password",
/* Load the initial proxy settings */
purple_prefs_trigger_callback("/purple/proxy/type");
purple_prefs_trigger_callback("/purple/proxy/host");
purple_prefs_trigger_callback("/purple/proxy/port");
purple_prefs_trigger_callback("/purple/proxy/username");
purple_prefs_trigger_callback("/purple/proxy/password");
purple_proxy_uninit(void)
purple_proxy_connect_data_disconnect(handles->data, NULL);
purple_proxy_connect_data_destroy(handles->data);
purple_prefs_disconnect_by_handle(purple_proxy_get_handle());
purple_proxy_info_destroy(global_proxy_info);
global_proxy_info = NULL;