pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Replace PurpleNamedValue with PurpleKeyValuePair in GTK-Doc comments
2019-11-27, qarkai
5311cf2ae115
Replace PurpleNamedValue with PurpleKeyValuePair in GTK-Doc comments
/* purple
*
* 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
* source distribution.
*
* 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
"internal.h"
#include
"debug.h"
#include
"notify.h"
#include
"prefs.h"
#include
"proxy.h"
#include
"purple-gio.h"
#include
"util.h"
#include
<gio/gio.h>
#include
<libsoup/soup.h>
struct
_PurpleProxyInfo
{
PurpleProxyType
type
;
/* The proxy type. */
char
*
host
;
/* The host. */
int
port
;
/* The port number. */
char
*
username
;
/* The username. */
char
*
password
;
/* The password. */
};
struct
_PurpleProxyConnectData
{
void
*
handle
;
PurpleProxyConnectFunction
connect_cb
;
gpointer
data
;
gchar
*
host
;
int
port
;
int
fd
;
PurpleProxyInfo
*
gpi
;
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)
/**************************************************************************
* Proxy structure API
**************************************************************************/
PurpleProxyInfo
*
purple_proxy_info_new
(
void
)
{
return
g_new0
(
PurpleProxyInfo
,
1
);
}
static
PurpleProxyInfo
*
purple_proxy_info_copy
(
PurpleProxyInfo
*
info
)
{
PurpleProxyInfo
*
copy
;
g_return_val_if_fail
(
info
!=
NULL
,
NULL
);
copy
=
purple_proxy_info_new
();
copy
->
type
=
info
->
type
;
copy
->
host
=
g_strdup
(
info
->
host
);
copy
->
port
=
info
->
port
;
copy
->
username
=
g_strdup
(
info
->
username
);
copy
->
password
=
g_strdup
(
info
->
password
);
return
copy
;
}
void
purple_proxy_info_destroy
(
PurpleProxyInfo
*
info
)
{
g_return_if_fail
(
info
!=
NULL
);
g_free
(
info
->
host
);
g_free
(
info
->
username
);
g_free
(
info
->
password
);
g_free
(
info
);
}
void
purple_proxy_info_set_proxy_type
(
PurpleProxyInfo
*
info
,
PurpleProxyType
type
)
{
g_return_if_fail
(
info
!=
NULL
);
info
->
type
=
type
;
}
void
purple_proxy_info_set_host
(
PurpleProxyInfo
*
info
,
const
char
*
host
)
{
g_return_if_fail
(
info
!=
NULL
);
g_free
(
info
->
host
);
info
->
host
=
g_strdup
(
host
);
}
void
purple_proxy_info_set_port
(
PurpleProxyInfo
*
info
,
int
port
)
{
g_return_if_fail
(
info
!=
NULL
);
info
->
port
=
port
;
}
void
purple_proxy_info_set_username
(
PurpleProxyInfo
*
info
,
const
char
*
username
)
{
g_return_if_fail
(
info
!=
NULL
);
g_free
(
info
->
username
);
info
->
username
=
g_strdup
(
username
);
}
void
purple_proxy_info_set_password
(
PurpleProxyInfo
*
info
,
const
char
*
password
)
{
g_return_if_fail
(
info
!=
NULL
);
g_free
(
info
->
password
);
info
->
password
=
g_strdup
(
password
);
}
PurpleProxyType
purple_proxy_info_get_proxy_type
(
const
PurpleProxyInfo
*
info
)
{
g_return_val_if_fail
(
info
!=
NULL
,
PURPLE_PROXY_NONE
);
return
info
->
type
;
}
const
char
*
purple_proxy_info_get_host
(
const
PurpleProxyInfo
*
info
)
{
g_return_val_if_fail
(
info
!=
NULL
,
NULL
);
return
info
->
host
;
}
int
purple_proxy_info_get_port
(
const
PurpleProxyInfo
*
info
)
{
g_return_val_if_fail
(
info
!=
NULL
,
0
);
return
info
->
port
;
}
const
char
*
purple_proxy_info_get_username
(
const
PurpleProxyInfo
*
info
)
{
g_return_val_if_fail
(
info
!=
NULL
,
NULL
);
return
info
->
username
;
}
const
char
*
purple_proxy_info_get_password
(
const
PurpleProxyInfo
*
info
)
{
g_return_val_if_fail
(
info
!=
NULL
,
NULL
);
return
info
->
password
;
}
G_DEFINE_BOXED_TYPE
(
PurpleProxyInfo
,
purple_proxy_info
,
purple_proxy_info_copy
,
purple_proxy_info_destroy
);
/**************************************************************************
* Global Proxy API
**************************************************************************/
PurpleProxyInfo
*
purple_global_proxy_get_info
(
void
)
{
return
global_proxy_info
;
}
void
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
#define GNOME2_CMDS 0
#define GNOME3_CMDS 1
/* 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
* GNOME 2/3 environment.
*
* Returns: The value of requested proxy parameter
*/
static
char
*
purple_gnome_proxy_get_parameter
(
guint8
parameter
,
guint8
gnome_version
)
{
gchar
*
param
,
*
err
;
size_t
param_len
;
if
(
parameter
>
GNOME_PROXY_HTTP_PASS
)
return
NULL
;
if
(
gnome_version
>
GNOME3_CMDS
)
return
NULL
;
if
(
!
g_spawn_command_line_sync
(
gproxycmds
[
parameter
][
gnome_version
],
&
param
,
&
err
,
NULL
,
NULL
))
return
NULL
;
g_free
(
err
);
g_strstrip
(
param
);
if
(
param
[
0
]
==
'\''
||
param
[
0
]
==
'\"'
)
{
param_len
=
strlen
(
param
);
memmove
(
param
,
param
+
1
,
param_len
);
/* copy last \0 too */
--
param_len
;
if
(
param_len
>
0
&&
(
param
[
param_len
-
1
]
==
'\''
||
param
[
param_len
-
1
]
==
'\"'
))
param
[
param_len
-
1
]
=
'\0'
;
g_strstrip
(
param
);
}
return
param
;
}
static
PurpleProxyInfo
*
purple_gnome_proxy_get_info
(
void
)
{
static
PurpleProxyInfo
info
=
{
0
,
NULL
,
0
,
NULL
,
NULL
};
gboolean
use_same_proxy
=
FALSE
;
gchar
*
tmp
;
guint8
gnome_version
=
GNOME3_CMDS
;
tmp
=
g_find_program_in_path
(
"gsettings"
);
if
(
tmp
==
NULL
)
{
tmp
=
g_find_program_in_path
(
"gconftool-2"
);
gnome_version
=
GNOME2_CMDS
;
}
if
(
tmp
==
NULL
)
return
purple_global_proxy_get_info
();
g_free
(
tmp
);
/* Check whether to use a proxy. */
tmp
=
purple_gnome_proxy_get_parameter
(
GNOME_PROXY_MODE
,
gnome_version
);
if
(
!
tmp
)
return
purple_global_proxy_get_info
();
if
(
purple_strequal
(
tmp
,
"none"
))
{
info
.
type
=
PURPLE_PROXY_NONE
;
g_free
(
tmp
);
return
&
info
;
}
if
(
!
purple_strequal
(
tmp
,
"manual"
))
{
/* Unknown setting. Fallback to using our global proxy settings. */
g_free
(
tmp
);
return
purple_global_proxy_get_info
();
}
g_free
(
tmp
);
/* Free the old fields */
g_free
(
info
.
host
);
info
.
host
=
NULL
;
g_free
(
info
.
username
);
info
.
username
=
NULL
;
g_free
(
info
.
password
);
info
.
password
=
NULL
;
tmp
=
purple_gnome_proxy_get_parameter
(
GNOME_PROXY_USE_SAME_PROXY
,
gnome_version
);
if
(
!
tmp
)
return
purple_global_proxy_get_info
();
if
(
purple_strequal
(
tmp
,
"true"
))
use_same_proxy
=
TRUE
;
g_free
(
tmp
);
if
(
!
use_same_proxy
)
{
info
.
host
=
purple_gnome_proxy_get_parameter
(
GNOME_PROXY_SOCKS_HOST
,
gnome_version
);
if
(
!
info
.
host
)
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
);
if
(
!
tmp
)
{
g_free
(
info
.
host
);
info
.
host
=
NULL
;
return
purple_global_proxy_get_info
();
}
info
.
port
=
atoi
(
tmp
);
g_free
(
tmp
);
}
else
{
g_free
(
info
.
host
);
info
.
host
=
purple_gnome_proxy_get_parameter
(
GNOME_PROXY_HTTP_HOST
,
gnome_version
);
if
(
!
info
.
host
)
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
;
if
(
*
info
.
host
==
'\0'
)
{
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
"
);
g_free
(
info
.
host
);
info
.
host
=
NULL
;
return
purple_global_proxy_get_info
();
}
info
.
username
=
purple_gnome_proxy_get_parameter
(
GNOME_PROXY_HTTP_USER
,
gnome_version
);
if
(
!
info
.
username
)
{
g_free
(
info
.
host
);
info
.
host
=
NULL
;
return
purple_global_proxy_get_info
();
}
info
.
password
=
purple_gnome_proxy_get_parameter
(
GNOME_PROXY_HTTP_PASS
,
gnome_version
);
if
(
!
info
.
password
)
{
g_free
(
info
.
host
);
info
.
host
=
NULL
;
g_free
(
info
.
username
);
info
.
username
=
NULL
;
return
purple_global_proxy_get_info
();
}
tmp
=
purple_gnome_proxy_get_parameter
(
GNOME_PROXY_HTTP_PORT
,
gnome_version
);
if
(
!
tmp
)
{
g_free
(
info
.
host
);
info
.
host
=
NULL
;
g_free
(
info
.
username
);
info
.
username
=
NULL
;
g_free
(
info
.
password
);
info
.
password
=
NULL
;
return
purple_global_proxy_get_info
();
}
info
.
port
=
atoi
(
tmp
);
g_free
(
tmp
);
}
return
&
info
;
}
#ifdef _WIN32
typedef
BOOL
(
CALLBACK
*
LPFNWINHTTPGETIEPROXYCONFIG
)(
/*IN OUT*/
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
*
pProxyConfig
);
/* This modifies "host" in-place evilly */
static
void
_proxy_fill_hostinfo
(
PurpleProxyInfo
*
info
,
char
*
host
,
int
default_port
)
{
int
port
=
default_port
;
char
*
d
;
d
=
g_strrstr
(
host
,
":"
);
if
(
d
)
{
*
d
=
'\0'
;
d
++
;
if
(
*
d
)
sscanf
(
d
,
"%d"
,
&
port
);
if
(
port
==
0
)
port
=
default_port
;
}
purple_proxy_info_set_host
(
info
,
host
);
purple_proxy_info_set_port
(
info
,
port
);
}
static
PurpleProxyInfo
*
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
;
if
(
!
loaded
)
{
loaded
=
TRUE
;
MyWinHttpGetIEProxyConfig
=
(
LPFNWINHTTPGETIEPROXYCONFIG
)
wpurple_find_and_loadproc
(
"winhttp.dll"
,
"WinHttpGetIEProxyConfigForCurrentUser"
);
if
(
!
MyWinHttpGetIEProxyConfig
)
purple_debug_warning
(
"proxy"
,
"Unable to read Windows Proxy Settings.
\n
"
);
}
if
(
!
MyWinHttpGetIEProxyConfig
)
return
NULL
;
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
());
return
NULL
;
}
/* 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)*/
return
NULL
;
}
else
if
(
ie_proxy_config
.
lpszProxy
)
{
gchar
*
proxy_list
=
g_utf16_to_utf8
(
ie_proxy_config
.
lpszProxy
,
-1
,
NULL
,
NULL
,
NULL
);
/* 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
)
{
if
(
specific
)
{
specific
+=
strlen
(
"http="
);
tmp
=
strchr
(
specific
,
';'
);
if
(
tmp
)
*
tmp
=
'\0'
;
/* specific now points the proxy server (and port) */
}
else
specific
=
proxy_list
;
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
,
';'
);
if
(
tmp
)
*
tmp
=
'\0'
;
/* 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
));
}
else
{
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 */
g_free
(
proxy_list
);
}
else
{
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
);
return
&
info
;
}
#endif
/**************************************************************************
* Proxy API
**************************************************************************/
/*
* Whoever calls this needs to have called
* purple_proxy_connect_data_disconnect() beforehand.
*/
static
void
purple_proxy_connect_data_destroy
(
PurpleProxyConnectData
*
connect_data
)
{
if
(
!
PURPLE_PROXY_CONNECT_DATA_IS_VALID
(
connect_data
))
return
;
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
);
g_free
(
connect_data
);
}
/*
* 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
* to another IP address.
*
* 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.
*/
static
void
purple_proxy_connect_data_disconnect
(
PurpleProxyConnectData
*
connect_data
,
const
gchar
*
error_message
)
{
if
(
connect_data
->
fd
>=
0
)
{
close
(
connect_data
->
fd
);
connect_data
->
fd
=
-1
;
}
if
(
error_message
!=
NULL
)
{
purple_debug_error
(
"proxy"
,
"Connection attempt failed: %s
\n
"
,
error_message
);
/* 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
);
}
}
static
void
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.
*/
connect_data
->
fd
=
-1
;
purple_proxy_connect_data_disconnect
(
connect_data
,
NULL
);
purple_proxy_connect_data_destroy
(
connect_data
);
}
PurpleProxyInfo
*
purple_proxy_get_setup
(
PurpleAccount
*
account
)
{
PurpleProxyInfo
*
gpi
=
NULL
;
const
gchar
*
tmp
;
/* 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
)
gpi
=
NULL
;
}
if
(
gpi
==
NULL
)
{
if
(
purple_running_gnome
())
gpi
=
purple_gnome_proxy_get_info
();
else
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
)
{
SoupURI
*
url
;
/* http_proxy-format:
* export http_proxy="http://user:passwd@your.proxy.server:port/"
*/
url
=
soup_uri_new
(
tmp
);
if
(
!
SOUP_URI_VALID_FOR_HTTP
(
url
))
{
purple_debug_warning
(
"proxy"
,
"Couldn't parse URL: %s"
,
tmp
);
return
gpi
;
}
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
);
soup_uri_free
(
url
);
/* 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
));
}
else
{
#ifdef _WIN32
PurpleProxyInfo
*
wgpi
;
if
((
wgpi
=
purple_win32_proxy_get_info
())
!=
NULL
)
return
wgpi
;
#endif
/* 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
;
}
}
return
gpi
;
}
/* Grabbed duplicate_fd() from GLib's testcases (gio/tests/socket.c).
* Can be dropped once this API has been converted to Gio.
*/
static
int
duplicate_fd
(
int
fd
)
{
#ifdef G_OS_WIN32
HANDLE
newfd
;
if
(
!
DuplicateHandle
(
GetCurrentProcess
(),
(
HANDLE
)
fd
,
GetCurrentProcess
(),
&
newfd
,
0
,
FALSE
,
DUPLICATE_SAME_ACCESS
))
{
return
-1
;
}
return
(
int
)
newfd
;
#else
return
dup
(
fd
);
#endif
}
/* End function grabbed from GLib */
static
void
connect_to_host_cb
(
GObject
*
source
,
GAsyncResult
*
res
,
gpointer
user_data
)
{
PurpleProxyConnectData
*
connect_data
=
user_data
;
GSocketConnection
*
conn
;
GError
*
error
=
NULL
;
GSocket
*
socket
;
conn
=
g_socket_client_connect_to_host_finish
(
G_SOCKET_CLIENT
(
source
),
res
,
&
error
);
if
(
conn
==
NULL
)
{
/* Ignore cancelled error as that signifies connect_data has
* been freed
*/
if
(
!
g_error_matches
(
error
,
G_IO_ERROR
,
G_IO_ERROR_CANCELLED
))
{
purple_debug_error
(
"proxy"
,
"Unable to connect to "
"destination host: %s
\n
"
,
error
->
message
);
purple_proxy_connect_data_disconnect
(
connect_data
,
"Unable to connect to destination "
"host.
\n
"
);
}
g_clear_error
(
&
error
);
return
;
}
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
));
g_object_unref
(
conn
);
purple_proxy_connect_data_connected
(
connect_data
);
}
PurpleProxyConnectData
*
purple_proxy_connect
(
void
*
handle
,
PurpleAccount
*
account
,
const
char
*
host
,
int
port
,
PurpleProxyConnectFunction
connect_cb
,
gpointer
data
)
{
PurpleProxyConnectData
*
connect_data
;
GSocketClient
*
client
;
GError
*
error
=
NULL
;
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
);
if
(
client
==
NULL
)
{
/* Assume it's a proxy error */
purple_notify_error
(
NULL
,
NULL
,
_
(
"Invalid proxy settings"
),
error
->
message
,
purple_request_cpar_from_account
(
account
));
g_clear_error
(
&
error
);
return
NULL
;
}
connect_data
=
g_new0
(
PurpleProxyConnectData
,
1
);
connect_data
->
fd
=
-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
"
,
host
,
port
);
g_socket_client_connect_to_host_async
(
client
,
host
,
port
,
connect_data
->
cancellable
,
connect_to_host_cb
,
connect_data
);
g_object_unref
(
client
);
handles
=
g_slist_prepend
(
handles
,
connect_data
);
return
connect_data
;
}
static
void
socks5_proxy_connect_cb
(
GObject
*
source
,
GAsyncResult
*
res
,
gpointer
user_data
)
{
PurpleProxyConnectData
*
connect_data
=
user_data
;
GIOStream
*
stream
;
GError
*
error
=
NULL
;
GSocket
*
socket
;
stream
=
g_proxy_connect_finish
(
G_PROXY
(
source
),
res
,
&
error
);
if
(
stream
==
NULL
)
{
/* Ignore cancelled error as that signifies connect_data has
* been freed
*/
if
(
!
g_error_matches
(
error
,
G_IO_ERROR
,
G_IO_ERROR_CANCELLED
))
{
purple_debug_error
(
"proxy"
,
"Unable to connect to "
"destination host: %s
\n
"
,
error
->
message
);
purple_proxy_connect_data_disconnect
(
connect_data
,
"Unable to connect to destination "
"host.
\n
"
);
}
g_clear_error
(
&
error
);
return
;
}
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
"
);
g_object_unref
(
stream
);
return
;
}
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
));
g_object_unref
(
stream
);
purple_proxy_connect_data_connected
(
connect_data
);
}
/* This is called when we connect to the SOCKS5 proxy server (through any
* relevant account proxy)
*/
static
void
socks5_connect_to_host_cb
(
GObject
*
source
,
GAsyncResult
*
res
,
gpointer
user_data
)
{
PurpleProxyConnectData
*
connect_data
=
user_data
;
GSocketConnection
*
conn
;
GError
*
error
=
NULL
;
GProxy
*
proxy
;
PurpleProxyInfo
*
info
;
GSocketAddress
*
addr
;
GInetSocketAddress
*
inet_addr
;
GSocketAddress
*
proxy_addr
;
conn
=
g_socket_client_connect_to_host_finish
(
G_SOCKET_CLIENT
(
source
),
res
,
&
error
);
if
(
conn
==
NULL
)
{
/* Ignore cancelled error as that signifies connect_data has
* been freed
*/
if
(
!
g_error_matches
(
error
,
G_IO_ERROR
,
G_IO_ERROR_CANCELLED
))
{
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
"
);
}
g_clear_error
(
&
error
);
return
;
}
proxy
=
g_proxy_get_default_for_protocol
(
"socks5"
);
if
(
proxy
==
NULL
)
{
purple_debug_error
(
"proxy"
,
"SOCKS5 proxy backend missing.
\n
"
);
purple_proxy_connect_data_disconnect
(
connect_data
,
"SOCKS5 proxy backend missing.
\n
"
);
g_object_unref
(
conn
);
return
;
}
info
=
connect_data
->
gpi
;
addr
=
g_socket_connection_get_remote_address
(
conn
,
&
error
);
if
(
addr
==
NULL
)
{
purple_debug_error
(
"proxy"
,
"Unable to retrieve SOCKS5 host "
"address from connection: %s
\n
"
,
error
->
message
);
purple_proxy_connect_data_disconnect
(
connect_data
,
"Unable to retrieve SOCKS5 host address from "
"connection"
);
g_object_unref
(
conn
);
g_object_unref
(
proxy
);
g_clear_error
(
&
error
);
return
;
}
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
);
g_object_unref
(
conn
);
g_object_unref
(
proxy
);
}
/*
* Combine some of this code with purple_proxy_connect()
*/
PurpleProxyConnectData
*
purple_proxy_connect_socks5_account
(
void
*
handle
,
PurpleAccount
*
account
,
PurpleProxyInfo
*
gpi
,
const
char
*
host
,
int
port
,
PurpleProxyConnectFunction
connect_cb
,
gpointer
data
)
{
PurpleProxyConnectData
*
connect_data
;
GSocketClient
*
client
;
GError
*
error
=
NULL
;
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
);
if
(
client
==
NULL
)
{
/* Assume it's a proxy error */
purple_notify_error
(
NULL
,
NULL
,
_
(
"Invalid proxy settings"
),
error
->
message
,
purple_request_cpar_from_account
(
account
));
g_clear_error
(
&
error
);
return
NULL
;
}
connect_data
=
g_new0
(
PurpleProxyConnectData
,
1
);
connect_data
->
fd
=
-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
=
gpi
;
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
,
connect_data
);
g_object_unref
(
client
);
handles
=
g_slist_prepend
(
handles
,
connect_data
);
return
connect_data
;
}
void
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
);
}
void
purple_proxy_connect_cancel_with_handle
(
void
*
handle
)
{
GSList
*
l
,
*
l_next
;
for
(
l
=
handles
;
l
!=
NULL
;
l
=
l_next
)
{
PurpleProxyConnectData
*
connect_data
=
l
->
data
;
l_next
=
l
->
next
;
if
(
connect_data
->
handle
==
handle
)
purple_proxy_connect_cancel
(
connect_data
);
}
}
GProxyResolver
*
purple_proxy_get_proxy_resolver
(
PurpleAccount
*
account
,
GError
**
error
)
{
PurpleProxyInfo
*
info
=
purple_proxy_get_setup
(
account
);
const
gchar
*
protocol
;
const
gchar
*
username
;
const
gchar
*
password
;
gchar
*
auth
;
gchar
*
proxy
;
GProxyResolver
*
resolver
;
if
(
purple_proxy_info_get_proxy_type
(
info
)
==
PURPLE_PROXY_NONE
)
{
/* Return an empty simple resolver, which will resolve on direct
* connection. */
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_HTTP
:
protocol
=
"http"
;
break
;
case
PURPLE_PROXY_SOCKS4
:
protocol
=
"socks4"
;
break
;
case
PURPLE_PROXY_SOCKS5
:
/* Intentional passthrough */
case
PURPLE_PROXY_TOR
:
protocol
=
"socks5"
;
break
;
default
:
g_set_error
(
error
,
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
_
(
"Invalid Proxy type (%d) specified"
),
purple_proxy_info_get_proxy_type
(
info
));
return
NULL
;
}
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 "
"invalid."
));
return
NULL
;
}
/* 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
);
}
else
{
auth
=
NULL
;
}
proxy
=
g_strdup_printf
(
"%s://%s%s:%i"
,
protocol
,
auth
!=
NULL
?
auth
:
""
,
purple_proxy_info_get_host
(
info
),
purple_proxy_info_get_port
(
info
));
g_free
(
auth
);
resolver
=
g_simple_proxy_resolver_new
(
proxy
,
NULL
);
g_free
(
proxy
);
return
resolver
;
}
static
void
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"
))
{
int
proxytype
;
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
;
else
proxytype
=
-1
;
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
);
}
void
*
purple_proxy_get_handle
()
{
static
int
handle
;
return
&
handle
;
}
void
purple_proxy_init
(
void
)
{
void
*
handle
;
/* Initialize a default proxy info struct. */
global_proxy_info
=
purple_proxy_info_new
();
/* Proxy */
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
,
NULL
);
purple_prefs_connect_callback
(
handle
,
"/purple/proxy/host"
,
proxy_pref_cb
,
NULL
);
purple_prefs_connect_callback
(
handle
,
"/purple/proxy/port"
,
proxy_pref_cb
,
NULL
);
purple_prefs_connect_callback
(
handle
,
"/purple/proxy/username"
,
proxy_pref_cb
,
NULL
);
purple_prefs_connect_callback
(
handle
,
"/purple/proxy/password"
,
proxy_pref_cb
,
NULL
);
/* 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"
);
}
void
purple_proxy_uninit
(
void
)
{
while
(
handles
!=
NULL
)
{
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
;
}