3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 /* this is a little piece of code to handle proxy connection */
24 /* it is intended to : 1st handle http proxy, using the CONNECT command
25 , 2nd provide an easy way to add socks support
26 , 3rd draw women to it like flies to honey */
27 #define _PURPLE_PROXY_C_
35 #include "purple-gio.h"
40 struct _PurpleProxyInfo
42 PurpleProxyType type
; /* The proxy type. */
44 char *host
; /* The host. */
45 int port
; /* The port number. */
46 char *username
; /* The username. */
47 char *password
; /* The password. */
50 struct _PurpleProxyConnectData
{
52 PurpleProxyConnectFunction connect_cb
;
59 GCancellable
*cancellable
;
62 static PurpleProxyInfo
*global_proxy_info
= NULL
;
64 static GSList
*handles
= NULL
;
67 * TODO: Eventually (GObjectification) this bad boy will be removed, because it is
68 * a gross fix for a crashy problem.
70 #define PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data) g_slist_find(handles, connect_data)
72 /**************************************************************************
74 **************************************************************************/
76 purple_proxy_info_new(void)
78 return g_new0(PurpleProxyInfo
, 1);
81 static PurpleProxyInfo
*
82 purple_proxy_info_copy(PurpleProxyInfo
*info
)
84 PurpleProxyInfo
*copy
;
86 g_return_val_if_fail(info
!= NULL
, NULL
);
88 copy
= purple_proxy_info_new();
89 copy
->type
= info
->type
;
90 copy
->host
= g_strdup(info
->host
);
91 copy
->port
= info
->port
;
92 copy
->username
= g_strdup(info
->username
);
93 copy
->password
= g_strdup(info
->password
);
99 purple_proxy_info_destroy(PurpleProxyInfo
*info
)
101 g_return_if_fail(info
!= NULL
);
104 g_free(info
->username
);
105 g_free(info
->password
);
111 purple_proxy_info_set_proxy_type(PurpleProxyInfo
*info
, PurpleProxyType type
)
113 g_return_if_fail(info
!= NULL
);
119 purple_proxy_info_set_host(PurpleProxyInfo
*info
, const char *host
)
121 g_return_if_fail(info
!= NULL
);
124 info
->host
= g_strdup(host
);
128 purple_proxy_info_set_port(PurpleProxyInfo
*info
, int port
)
130 g_return_if_fail(info
!= NULL
);
136 purple_proxy_info_set_username(PurpleProxyInfo
*info
, const char *username
)
138 g_return_if_fail(info
!= NULL
);
140 g_free(info
->username
);
141 info
->username
= g_strdup(username
);
145 purple_proxy_info_set_password(PurpleProxyInfo
*info
, const char *password
)
147 g_return_if_fail(info
!= NULL
);
149 g_free(info
->password
);
150 info
->password
= g_strdup(password
);
154 purple_proxy_info_get_proxy_type(const PurpleProxyInfo
*info
)
156 g_return_val_if_fail(info
!= NULL
, PURPLE_PROXY_NONE
);
162 purple_proxy_info_get_host(const PurpleProxyInfo
*info
)
164 g_return_val_if_fail(info
!= NULL
, NULL
);
170 purple_proxy_info_get_port(const PurpleProxyInfo
*info
)
172 g_return_val_if_fail(info
!= NULL
, 0);
178 purple_proxy_info_get_username(const PurpleProxyInfo
*info
)
180 g_return_val_if_fail(info
!= NULL
, NULL
);
182 return info
->username
;
186 purple_proxy_info_get_password(const PurpleProxyInfo
*info
)
188 g_return_val_if_fail(info
!= NULL
, NULL
);
190 return info
->password
;
193 G_DEFINE_BOXED_TYPE(PurpleProxyInfo
, purple_proxy_info
,
194 purple_proxy_info_copy
, purple_proxy_info_destroy
);
196 /**************************************************************************
198 **************************************************************************/
200 purple_global_proxy_get_info(void)
202 return global_proxy_info
;
206 purple_global_proxy_set_info(PurpleProxyInfo
*info
)
208 g_return_if_fail(info
!= NULL
);
210 purple_proxy_info_destroy(global_proxy_info
);
212 global_proxy_info
= info
;
216 /* index in gproxycmds below, keep them in sync */
217 #define GNOME_PROXY_MODE 0
218 #define GNOME_PROXY_USE_SAME_PROXY 1
219 #define GNOME_PROXY_SOCKS_HOST 2
220 #define GNOME_PROXY_SOCKS_PORT 3
221 #define GNOME_PROXY_HTTP_HOST 4
222 #define GNOME_PROXY_HTTP_PORT 5
223 #define GNOME_PROXY_HTTP_USER 6
224 #define GNOME_PROXY_HTTP_PASS 7
225 #define GNOME2_CMDS 0
226 #define GNOME3_CMDS 1
228 /* detect proxy settings for gnome2/gnome3 */
229 static const char* gproxycmds
[][2] = {
230 { "gconftool-2 -g /system/proxy/mode" , "gsettings get org.gnome.system.proxy mode" },
231 { "gconftool-2 -g /system/http_proxy/use_same_proxy", "gsettings get org.gnome.system.proxy use-same-proxy" },
232 { "gconftool-2 -g /system/proxy/socks_host", "gsettings get org.gnome.system.proxy.socks host" },
233 { "gconftool-2 -g /system/proxy/socks_port", "gsettings get org.gnome.system.proxy.socks port" },
234 { "gconftool-2 -g /system/http_proxy/host", "gsettings get org.gnome.system.proxy.http host" },
235 { "gconftool-2 -g /system/http_proxy/port", "gsettings get org.gnome.system.proxy.http port"},
236 { "gconftool-2 -g /system/http_proxy/authentication_user", "gsettings get org.gnome.system.proxy.http authentication-user" },
237 { "gconftool-2 -g /system/http_proxy/authentication_password", "gsettings get org.gnome.system.proxy.http authentication-password" },
241 * purple_gnome_proxy_get_parameter:
242 * @parameter: One of the GNOME_PROXY_x constants defined above
243 * @gnome_version: GNOME2_CMDS or GNOME3_CMDS
245 * This is a utility function used to retrieve proxy parameter values from
246 * GNOME 2/3 environment.
248 * Returns: The value of requested proxy parameter
251 purple_gnome_proxy_get_parameter(guint8 parameter
, guint8 gnome_version
)
256 if (parameter
> GNOME_PROXY_HTTP_PASS
)
258 if (gnome_version
> GNOME3_CMDS
)
261 if (!g_spawn_command_line_sync(gproxycmds
[parameter
][gnome_version
],
262 ¶m
, &err
, NULL
, NULL
))
267 if (param
[0] == '\'' || param
[0] == '\"') {
268 param_len
= strlen(param
);
269 memmove(param
, param
+ 1, param_len
); /* copy last \0 too */
271 if (param_len
> 0 && (param
[param_len
- 1] == '\'' || param
[param_len
- 1] == '\"'))
272 param
[param_len
- 1] = '\0';
279 static PurpleProxyInfo
*
280 purple_gnome_proxy_get_info(void)
282 static PurpleProxyInfo info
= {0, NULL
, 0, NULL
, NULL
};
283 gboolean use_same_proxy
= FALSE
;
285 guint8 gnome_version
= GNOME3_CMDS
;
287 tmp
= g_find_program_in_path("gsettings");
289 tmp
= g_find_program_in_path("gconftool-2");
290 gnome_version
= GNOME2_CMDS
;
293 return purple_global_proxy_get_info();
297 /* Check whether to use a proxy. */
298 tmp
= purple_gnome_proxy_get_parameter(GNOME_PROXY_MODE
, gnome_version
);
300 return purple_global_proxy_get_info();
302 if (purple_strequal(tmp
, "none")) {
303 info
.type
= PURPLE_PROXY_NONE
;
308 if (!purple_strequal(tmp
, "manual")) {
309 /* Unknown setting. Fallback to using our global proxy settings. */
311 return purple_global_proxy_get_info();
316 /* Free the old fields */
319 g_free(info
.username
);
320 info
.username
= NULL
;
321 g_free(info
.password
);
322 info
.password
= NULL
;
324 tmp
= purple_gnome_proxy_get_parameter(GNOME_PROXY_USE_SAME_PROXY
, gnome_version
);
326 return purple_global_proxy_get_info();
328 if (purple_strequal(tmp
, "true"))
329 use_same_proxy
= TRUE
;
333 if (!use_same_proxy
) {
334 info
.host
= purple_gnome_proxy_get_parameter(GNOME_PROXY_SOCKS_HOST
, gnome_version
);
336 return purple_global_proxy_get_info();
339 if (!use_same_proxy
&& (info
.host
!= NULL
) && (*info
.host
!= '\0')) {
340 info
.type
= PURPLE_PROXY_SOCKS5
;
341 tmp
= purple_gnome_proxy_get_parameter(GNOME_PROXY_SOCKS_PORT
, gnome_version
);
345 return purple_global_proxy_get_info();
347 info
.port
= atoi(tmp
);
351 info
.host
= purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_HOST
, gnome_version
);
353 return purple_global_proxy_get_info();
355 /* If we get this far then we know we're using an HTTP proxy */
356 info
.type
= PURPLE_PROXY_HTTP
;
358 if (*info
.host
== '\0')
360 purple_debug_info("proxy", "Gnome proxy settings are set to "
361 "'manual' but no suitable proxy server is specified. Using "
362 "Pidgin's proxy settings instead.\n");
365 return purple_global_proxy_get_info();
368 info
.username
= purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_USER
, gnome_version
);
373 return purple_global_proxy_get_info();
376 info
.password
= purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_PASS
, gnome_version
);
381 g_free(info
.username
);
382 info
.username
= NULL
;
383 return purple_global_proxy_get_info();
386 tmp
= purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_PORT
, gnome_version
);
391 g_free(info
.username
);
392 info
.username
= NULL
;
393 g_free(info
.password
);
394 info
.password
= NULL
;
395 return purple_global_proxy_get_info();
397 info
.port
= atoi(tmp
);
406 typedef BOOL (CALLBACK
* LPFNWINHTTPGETIEPROXYCONFIG
)(/*IN OUT*/ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
* pProxyConfig
);
408 /* This modifies "host" in-place evilly */
410 _proxy_fill_hostinfo(PurpleProxyInfo
*info
, char *host
, int default_port
)
412 int port
= default_port
;
415 d
= g_strrstr(host
, ":");
421 sscanf(d
, "%d", &port
);
427 purple_proxy_info_set_host(info
, host
);
428 purple_proxy_info_set_port(info
, port
);
431 static PurpleProxyInfo
*
432 purple_win32_proxy_get_info(void)
434 static LPFNWINHTTPGETIEPROXYCONFIG MyWinHttpGetIEProxyConfig
= NULL
;
435 static gboolean loaded
= FALSE
;
436 static PurpleProxyInfo info
= {0, NULL
, 0, NULL
, NULL
};
438 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config
;
442 MyWinHttpGetIEProxyConfig
= (LPFNWINHTTPGETIEPROXYCONFIG
)
443 wpurple_find_and_loadproc("winhttp.dll", "WinHttpGetIEProxyConfigForCurrentUser");
444 if (!MyWinHttpGetIEProxyConfig
)
445 purple_debug_warning("proxy", "Unable to read Windows Proxy Settings.\n");
448 if (!MyWinHttpGetIEProxyConfig
)
451 ZeroMemory(&ie_proxy_config
, sizeof(ie_proxy_config
));
452 if (!MyWinHttpGetIEProxyConfig(&ie_proxy_config
)) {
453 purple_debug_error("proxy", "Error reading Windows Proxy Settings(%lu).\n", GetLastError());
457 /* We can't do much if it is autodetect*/
458 if (ie_proxy_config
.fAutoDetect
) {
459 purple_debug_error("proxy", "Windows Proxy Settings set to autodetect (not supported).\n");
461 /* TODO: For 3.0.0 we'll revisit this (maybe)*/
465 } else if (ie_proxy_config
.lpszProxy
) {
466 gchar
*proxy_list
= g_utf16_to_utf8(ie_proxy_config
.lpszProxy
, -1,
469 /* We can't do anything about the bypass list, as we don't have the url */
470 /* TODO: For 3.0.0 we'll revisit this*/
472 /* There are proxy settings for several protocols */
473 if (proxy_list
&& *proxy_list
) {
474 char *specific
= NULL
, *tmp
;
476 /* If there is only a global proxy, which means "HTTP" */
477 if (!strchr(proxy_list
, ';') || (specific
= g_strstr_len(proxy_list
, -1, "http=")) != NULL
) {
480 specific
+= strlen("http=");
481 tmp
= strchr(specific
, ';');
484 /* specific now points the proxy server (and port) */
486 specific
= proxy_list
;
488 purple_proxy_info_set_proxy_type(&info
, PURPLE_PROXY_HTTP
);
489 _proxy_fill_hostinfo(&info
, specific
, 80);
490 /* TODO: is there a way to set the username/password? */
491 purple_proxy_info_set_username(&info
, NULL
);
492 purple_proxy_info_set_password(&info
, NULL
);
494 purple_debug_info("proxy", "Windows Proxy Settings: HTTP proxy: '%s:%d'.\n",
495 purple_proxy_info_get_host(&info
),
496 purple_proxy_info_get_port(&info
));
498 } else if ((specific
= g_strstr_len(proxy_list
, -1, "socks=")) != NULL
) {
500 specific
+= strlen("socks=");
501 tmp
= strchr(specific
, ';');
504 /* specific now points the proxy server (and port) */
506 purple_proxy_info_set_proxy_type(&info
, PURPLE_PROXY_SOCKS5
);
507 _proxy_fill_hostinfo(&info
, specific
, 1080);
508 /* TODO: is there a way to set the username/password? */
509 purple_proxy_info_set_username(&info
, NULL
);
510 purple_proxy_info_set_password(&info
, NULL
);
512 purple_debug_info("proxy", "Windows Proxy Settings: SOCKS5 proxy: '%s:%d'.\n",
513 purple_proxy_info_get_host(&info
),
514 purple_proxy_info_get_port(&info
));
518 purple_debug_info("proxy", "Windows Proxy Settings: No supported proxy specified.\n");
520 purple_proxy_info_set_proxy_type(&info
, PURPLE_PROXY_NONE
);
525 /* TODO: Fix API to be able look at proxy bypass settings */
529 purple_debug_info("proxy", "No Windows proxy set.\n");
530 purple_proxy_info_set_proxy_type(&info
, PURPLE_PROXY_NONE
);
533 if (ie_proxy_config
.lpszAutoConfigUrl
)
534 GlobalFree(ie_proxy_config
.lpszAutoConfigUrl
);
535 if (ie_proxy_config
.lpszProxy
)
536 GlobalFree(ie_proxy_config
.lpszProxy
);
537 if (ie_proxy_config
.lpszProxyBypass
)
538 GlobalFree(ie_proxy_config
.lpszProxyBypass
);
545 /**************************************************************************
547 **************************************************************************/
550 * Whoever calls this needs to have called
551 * purple_proxy_connect_data_disconnect() beforehand.
554 purple_proxy_connect_data_destroy(PurpleProxyConnectData
*connect_data
)
556 if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data
))
559 handles
= g_slist_remove(handles
, connect_data
);
561 if(G_IS_CANCELLABLE(connect_data
->cancellable
)) {
562 g_cancellable_cancel(connect_data
->cancellable
);
564 g_object_unref(G_OBJECT(connect_data
->cancellable
));
566 connect_data
->cancellable
= NULL
;
569 g_free(connect_data
->host
);
570 g_free(connect_data
);
574 * purple_proxy_connect_data_disconnect:
575 * @error_message: An error message explaining why the connection
576 * failed. This will be passed to the callback function
577 * specified in the call to purple_proxy_connect(). If the
578 * connection was successful then pass in null.
580 * Free all information dealing with a connection attempt and
581 * reset the connect_data to prepare for it to try to connect
582 * to another IP address.
584 * If an error message is passed in, then we know the connection
585 * attempt failed. If so, we call the callback with the given
586 * error message, then destroy the connect_data.
589 purple_proxy_connect_data_disconnect(PurpleProxyConnectData
*connect_data
, const gchar
*error_message
)
591 if (connect_data
->fd
>= 0)
593 close(connect_data
->fd
);
594 connect_data
->fd
= -1;
597 if (error_message
!= NULL
)
599 purple_debug_error("proxy", "Connection attempt failed: %s\n",
602 /* Everything failed! Tell the originator of the request. */
603 connect_data
->connect_cb(connect_data
->data
, -1, error_message
);
604 purple_proxy_connect_data_destroy(connect_data
);
609 purple_proxy_connect_data_connected(PurpleProxyConnectData
*connect_data
)
611 purple_debug_info("proxy", "Connected to %s:%d.\n",
612 connect_data
->host
, connect_data
->port
);
614 connect_data
->connect_cb(connect_data
->data
, connect_data
->fd
, NULL
);
617 * We've passed the file descriptor to the protocol, so it's no longer
618 * our responsibility, and we should be careful not to free it when
619 * we destroy the connect_data.
621 connect_data
->fd
= -1;
623 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
624 purple_proxy_connect_data_destroy(connect_data
);
628 purple_proxy_get_setup(PurpleAccount
*account
)
630 PurpleProxyInfo
*gpi
= NULL
;
633 /* This is used as a fallback so we don't overwrite the selected proxy type */
634 static PurpleProxyInfo
*tmp_none_proxy_info
= NULL
;
635 if (!tmp_none_proxy_info
) {
636 tmp_none_proxy_info
= purple_proxy_info_new();
637 purple_proxy_info_set_proxy_type(tmp_none_proxy_info
, PURPLE_PROXY_NONE
);
640 if (account
&& purple_account_get_proxy_info(account
) != NULL
) {
641 gpi
= purple_account_get_proxy_info(account
);
642 if (purple_proxy_info_get_proxy_type(gpi
) == PURPLE_PROXY_USE_GLOBAL
)
646 if (purple_running_gnome())
647 gpi
= purple_gnome_proxy_get_info();
649 gpi
= purple_global_proxy_get_info();
652 if (purple_proxy_info_get_proxy_type(gpi
) == PURPLE_PROXY_USE_ENVVAR
) {
653 if ((tmp
= g_getenv("HTTP_PROXY")) != NULL
||
654 (tmp
= g_getenv("http_proxy")) != NULL
||
655 (tmp
= g_getenv("HTTPPROXY")) != NULL
)
659 /* http_proxy-format:
660 * export http_proxy="http://user:passwd@your.proxy.server:port/"
662 url
= purple_http_url_parse(tmp
);
664 purple_debug_warning("proxy", "Couldn't parse URL\n");
668 purple_proxy_info_set_host(gpi
, purple_http_url_get_host(url
));
669 purple_proxy_info_set_username(gpi
, purple_http_url_get_username(url
));
670 purple_proxy_info_set_password(gpi
, purple_http_url_get_password(url
));
671 purple_proxy_info_set_port(gpi
, purple_http_url_get_port(url
));
673 /* XXX: Do we want to skip this step if user/password/port were part of url? */
674 if ((tmp
= g_getenv("HTTP_PROXY_USER")) != NULL
||
675 (tmp
= g_getenv("http_proxy_user")) != NULL
||
676 (tmp
= g_getenv("HTTPPROXYUSER")) != NULL
)
677 purple_proxy_info_set_username(gpi
, tmp
);
679 if ((tmp
= g_getenv("HTTP_PROXY_PASS")) != NULL
||
680 (tmp
= g_getenv("http_proxy_pass")) != NULL
||
681 (tmp
= g_getenv("HTTPPROXYPASS")) != NULL
)
682 purple_proxy_info_set_password(gpi
, tmp
);
684 if ((tmp
= g_getenv("HTTP_PROXY_PORT")) != NULL
||
685 (tmp
= g_getenv("http_proxy_port")) != NULL
||
686 (tmp
= g_getenv("HTTPPROXYPORT")) != NULL
)
687 purple_proxy_info_set_port(gpi
, atoi(tmp
));
690 PurpleProxyInfo
*wgpi
;
691 if ((wgpi
= purple_win32_proxy_get_info()) != NULL
)
694 /* no proxy environment variable found, don't use a proxy */
695 purple_debug_info("proxy", "No environment settings found, not using a proxy\n");
696 gpi
= tmp_none_proxy_info
;
704 /* Grabbed duplicate_fd() from GLib's testcases (gio/tests/socket.c).
705 * Can be dropped once this API has been converted to Gio.
708 duplicate_fd (int fd
)
713 if (!DuplicateHandle (GetCurrentProcess (),
715 GetCurrentProcess (),
719 DUPLICATE_SAME_ACCESS
))
729 /* End function grabbed from GLib */
732 connect_to_host_cb(GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
734 PurpleProxyConnectData
*connect_data
= user_data
;
735 GSocketConnection
*conn
;
736 GError
*error
= NULL
;
739 conn
= g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source
),
742 /* Ignore cancelled error as that signifies connect_data has
745 if (!g_error_matches(error
, G_IO_ERROR
,
746 G_IO_ERROR_CANCELLED
)) {
747 purple_debug_error("proxy", "Unable to connect to "
748 "destination host: %s\n",
750 purple_proxy_connect_data_disconnect(connect_data
,
751 "Unable to connect to destination "
755 g_clear_error(&error
);
759 socket
= g_socket_connection_get_socket(conn
);
760 g_assert(socket
!= NULL
);
762 /* Duplicate the file descriptor, and then free the connection.
763 * libpurple's proxy code doesn't keep an object around for the
764 * lifetime of the connection. Therefore, in order to not leak
765 * memory, the GSocketConnection must be freed here. In order
766 * to avoid the double close/free of the file descriptor, the
767 * file descriptor is duplicated.
769 connect_data
->fd
= duplicate_fd(g_socket_get_fd(socket
));
770 g_object_unref(conn
);
772 purple_proxy_connect_data_connected(connect_data
);
775 PurpleProxyConnectData
*
776 purple_proxy_connect(void *handle
, PurpleAccount
*account
,
777 const char *host
, int port
,
778 PurpleProxyConnectFunction connect_cb
, gpointer data
)
780 PurpleProxyConnectData
*connect_data
;
781 GSocketClient
*client
;
782 GError
*error
= NULL
;
784 g_return_val_if_fail(host
!= NULL
, NULL
);
785 g_return_val_if_fail(port
> 0, NULL
);
786 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
788 client
= purple_gio_socket_client_new(account
, &error
);
790 if (client
== NULL
) {
791 /* Assume it's a proxy error */
792 purple_notify_error(NULL
, NULL
, _("Invalid proxy settings"),
794 purple_request_cpar_from_account(account
));
795 g_clear_error(&error
);
799 connect_data
= g_new0(PurpleProxyConnectData
, 1);
800 connect_data
->fd
= -1;
801 connect_data
->handle
= handle
;
802 connect_data
->connect_cb
= connect_cb
;
803 connect_data
->data
= data
;
804 connect_data
->host
= g_strdup(host
);
805 connect_data
->port
= port
;
806 connect_data
->gpi
= purple_proxy_get_setup(account
);
807 connect_data
->cancellable
= g_cancellable_new();
809 purple_debug_info("proxy", "Attempting connection to %s:%u\n",
812 g_socket_client_connect_to_host_async(client
, host
, port
,
813 connect_data
->cancellable
, connect_to_host_cb
,
815 g_object_unref(client
);
817 handles
= g_slist_prepend(handles
, connect_data
);
823 socks5_proxy_connect_cb(GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
825 PurpleProxyConnectData
*connect_data
= user_data
;
827 GError
*error
= NULL
;
830 stream
= g_proxy_connect_finish(G_PROXY(source
), res
, &error
);
832 if (stream
== NULL
) {
833 /* Ignore cancelled error as that signifies connect_data has
836 if (!g_error_matches(error
, G_IO_ERROR
,
837 G_IO_ERROR_CANCELLED
)) {
838 purple_debug_error("proxy", "Unable to connect to "
839 "destination host: %s\n",
841 purple_proxy_connect_data_disconnect(connect_data
,
842 "Unable to connect to destination "
846 g_clear_error(&error
);
850 if (!G_IS_SOCKET_CONNECTION(stream
)) {
851 purple_debug_error("proxy",
852 "GProxy didn't return a GSocketConnection.\n");
853 purple_proxy_connect_data_disconnect(connect_data
,
854 "GProxy didn't return a GSocketConnection.\n");
855 g_object_unref(stream
);
859 socket
= g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream
));
861 /* Duplicate the file descriptor, and then free the connection.
862 * libpurple's proxy code doesn't keep an object around for the
863 * lifetime of the connection. Therefore, in order to not leak
864 * memory, the GSocketConnection (aka GIOStream here) must be
865 * freed here. In order to avoid the double close/free of the
866 * file descriptor, the file descriptor is duplicated.
868 connect_data
->fd
= duplicate_fd(g_socket_get_fd(socket
));
869 g_object_unref(stream
);
871 purple_proxy_connect_data_connected(connect_data
);
874 /* This is called when we connect to the SOCKS5 proxy server (through any
875 * relevant account proxy)
878 socks5_connect_to_host_cb(GObject
*source
, GAsyncResult
*res
,
881 PurpleProxyConnectData
*connect_data
= user_data
;
882 GSocketConnection
*conn
;
883 GError
*error
= NULL
;
885 PurpleProxyInfo
*info
;
886 GSocketAddress
*addr
;
887 GInetSocketAddress
*inet_addr
;
888 GSocketAddress
*proxy_addr
;
890 conn
= g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source
),
893 /* Ignore cancelled error as that signifies connect_data has
896 if (!g_error_matches(error
, G_IO_ERROR
,
897 G_IO_ERROR_CANCELLED
)) {
898 purple_debug_error("proxy", "Unable to connect to "
899 "SOCKS5 host: %s\n", error
->message
);
900 purple_proxy_connect_data_disconnect(connect_data
,
901 "Unable to connect to SOCKS5 host.\n");
904 g_clear_error(&error
);
908 proxy
= g_proxy_get_default_for_protocol("socks5");
910 purple_debug_error("proxy", "SOCKS5 proxy backend missing.\n");
911 purple_proxy_connect_data_disconnect(connect_data
,
912 "SOCKS5 proxy backend missing.\n");
913 g_object_unref(conn
);
917 info
= connect_data
->gpi
;
919 addr
= g_socket_connection_get_remote_address(conn
, &error
);
921 purple_debug_error("proxy", "Unable to retrieve SOCKS5 host "
922 "address from connection: %s\n",
924 purple_proxy_connect_data_disconnect(connect_data
,
925 "Unable to retrieve SOCKS5 host address from "
927 g_object_unref(conn
);
928 g_object_unref(proxy
);
929 g_clear_error(&error
);
933 inet_addr
= G_INET_SOCKET_ADDRESS(addr
);
935 proxy_addr
= g_proxy_address_new(
936 g_inet_socket_address_get_address(inet_addr
),
937 g_inet_socket_address_get_port(inet_addr
),
938 "socks5", connect_data
->host
, connect_data
->port
,
939 purple_proxy_info_get_username(info
),
940 purple_proxy_info_get_password(info
));
941 g_object_unref(inet_addr
);
943 purple_debug_info("proxy", "Initiating SOCKS5 negotiation.\n");
945 purple_debug_info("proxy",
946 "Connecting to %s:%d via %s:%d using SOCKS5\n",
947 connect_data
->host
, connect_data
->port
,
948 purple_proxy_info_get_host(connect_data
->gpi
),
949 purple_proxy_info_get_port(connect_data
->gpi
));
951 g_proxy_connect_async(proxy
, G_IO_STREAM(conn
),
952 G_PROXY_ADDRESS(proxy_addr
),
953 connect_data
->cancellable
,
954 socks5_proxy_connect_cb
, connect_data
);
955 g_object_unref(proxy_addr
);
956 g_object_unref(conn
);
957 g_object_unref(proxy
);
961 * Combine some of this code with purple_proxy_connect()
963 PurpleProxyConnectData
*
964 purple_proxy_connect_socks5_account(void *handle
, PurpleAccount
*account
,
965 PurpleProxyInfo
*gpi
,
966 const char *host
, int port
,
967 PurpleProxyConnectFunction connect_cb
,
970 PurpleProxyConnectData
*connect_data
;
971 GSocketClient
*client
;
972 GError
*error
= NULL
;
974 g_return_val_if_fail(host
!= NULL
, NULL
);
975 g_return_val_if_fail(port
>= 0, NULL
);
976 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
978 client
= purple_gio_socket_client_new(account
, &error
);
980 if (client
== NULL
) {
981 /* Assume it's a proxy error */
982 purple_notify_error(NULL
, NULL
, _("Invalid proxy settings"),
984 purple_request_cpar_from_account(account
));
985 g_clear_error(&error
);
989 connect_data
= g_new0(PurpleProxyConnectData
, 1);
990 connect_data
->fd
= -1;
991 connect_data
->handle
= handle
;
992 connect_data
->connect_cb
= connect_cb
;
993 connect_data
->data
= data
;
994 connect_data
->host
= g_strdup(host
);
995 connect_data
->port
= port
;
996 connect_data
->gpi
= gpi
;
997 connect_data
->cancellable
= g_cancellable_new();
999 purple_debug_info("proxy",
1000 "Connecting to %s:%d via %s:%d using SOCKS5\n",
1001 connect_data
->host
, connect_data
->port
,
1002 purple_proxy_info_get_host(connect_data
->gpi
),
1003 purple_proxy_info_get_port(connect_data
->gpi
));
1005 g_socket_client_connect_to_host_async(client
,
1006 purple_proxy_info_get_host(connect_data
->gpi
),
1007 purple_proxy_info_get_port(connect_data
->gpi
),
1008 connect_data
->cancellable
, socks5_connect_to_host_cb
,
1010 g_object_unref(client
);
1012 handles
= g_slist_prepend(handles
, connect_data
);
1014 return connect_data
;
1018 purple_proxy_connect_cancel(PurpleProxyConnectData
*connect_data
)
1020 g_return_if_fail(connect_data
!= NULL
);
1022 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
1023 purple_proxy_connect_data_destroy(connect_data
);
1027 purple_proxy_connect_cancel_with_handle(void *handle
)
1031 for (l
= handles
; l
!= NULL
; l
= l_next
) {
1032 PurpleProxyConnectData
*connect_data
= l
->data
;
1036 if (connect_data
->handle
== handle
)
1037 purple_proxy_connect_cancel(connect_data
);
1042 purple_proxy_get_proxy_resolver(PurpleAccount
*account
, GError
**error
)
1044 PurpleProxyInfo
*info
= purple_proxy_get_setup(account
);
1045 const gchar
*protocol
;
1046 const gchar
*username
;
1047 const gchar
*password
;
1050 GProxyResolver
*resolver
;
1052 if (purple_proxy_info_get_proxy_type(info
) == PURPLE_PROXY_NONE
) {
1053 /* Return an empty simple resolver, which will resolve on direct
1055 return g_simple_proxy_resolver_new(NULL
, NULL
);
1058 switch (purple_proxy_info_get_proxy_type(info
))
1060 /* PURPLE_PROXY_NONE already handled above */
1062 case PURPLE_PROXY_USE_ENVVAR
:
1063 /* Intentional passthrough */
1064 case PURPLE_PROXY_HTTP
:
1067 case PURPLE_PROXY_SOCKS4
:
1068 protocol
= "socks4";
1070 case PURPLE_PROXY_SOCKS5
:
1071 /* Intentional passthrough */
1072 case PURPLE_PROXY_TOR
:
1073 protocol
= "socks5";
1077 g_set_error(error
, PURPLE_CONNECTION_ERROR
,
1078 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
1079 _("Invalid Proxy type (%d) specified"),
1080 purple_proxy_info_get_proxy_type(info
));
1085 if (purple_proxy_info_get_host(info
) == NULL
||
1086 purple_proxy_info_get_port(info
) <= 0) {
1087 g_set_error_literal(error
, PURPLE_CONNECTION_ERROR
,
1088 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
1089 _("Either the host name or port number "
1090 "specified for your given proxy type is "
1095 /* Everything checks out. Create and return the GProxyResolver */
1097 username
= purple_proxy_info_get_username(info
);
1098 password
= purple_proxy_info_get_password(info
);
1100 /* Username and password are optional */
1101 if (username
!= NULL
&& password
!= NULL
) {
1102 auth
= g_strdup_printf("%s:%s@", username
, password
);
1103 } else if (username
!= NULL
) {
1104 auth
= g_strdup_printf("%s@", username
);
1109 proxy
= g_strdup_printf("%s://%s%s:%i", protocol
,
1110 auth
!= NULL
? auth
: "",
1111 purple_proxy_info_get_host(info
),
1112 purple_proxy_info_get_port(info
));
1115 resolver
= g_simple_proxy_resolver_new(proxy
, NULL
);
1122 proxy_pref_cb(const char *name
, PurplePrefType type
,
1123 gconstpointer value
, gpointer data
)
1125 PurpleProxyInfo
*info
= purple_global_proxy_get_info();
1127 if (purple_strequal(name
, "/purple/proxy/type")) {
1129 const char *type
= value
;
1131 if (purple_strequal(type
, "none"))
1132 proxytype
= PURPLE_PROXY_NONE
;
1133 else if (purple_strequal(type
, "http"))
1134 proxytype
= PURPLE_PROXY_HTTP
;
1135 else if (purple_strequal(type
, "socks4"))
1136 proxytype
= PURPLE_PROXY_SOCKS4
;
1137 else if (purple_strequal(type
, "socks5"))
1138 proxytype
= PURPLE_PROXY_SOCKS5
;
1139 else if (purple_strequal(type
, "tor"))
1140 proxytype
= PURPLE_PROXY_TOR
;
1141 else if (purple_strequal(type
, "envvar"))
1142 proxytype
= PURPLE_PROXY_USE_ENVVAR
;
1146 purple_proxy_info_set_proxy_type(info
, proxytype
);
1147 } else if (purple_strequal(name
, "/purple/proxy/host"))
1148 purple_proxy_info_set_host(info
, value
);
1149 else if (purple_strequal(name
, "/purple/proxy/port"))
1150 purple_proxy_info_set_port(info
, GPOINTER_TO_INT(value
));
1151 else if (purple_strequal(name
, "/purple/proxy/username"))
1152 purple_proxy_info_set_username(info
, value
);
1153 else if (purple_strequal(name
, "/purple/proxy/password"))
1154 purple_proxy_info_set_password(info
, value
);
1158 purple_proxy_get_handle()
1166 purple_proxy_init(void)
1170 /* Initialize a default proxy info struct. */
1171 global_proxy_info
= purple_proxy_info_new();
1174 purple_prefs_add_none("/purple/proxy");
1175 purple_prefs_add_string("/purple/proxy/type", "none");
1176 purple_prefs_add_string("/purple/proxy/host", "");
1177 purple_prefs_add_int("/purple/proxy/port", 0);
1178 purple_prefs_add_string("/purple/proxy/username", "");
1179 purple_prefs_add_string("/purple/proxy/password", "");
1180 purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE
);
1182 /* Setup callbacks for the preferences. */
1183 handle
= purple_proxy_get_handle();
1184 purple_prefs_connect_callback(handle
, "/purple/proxy/type", proxy_pref_cb
,
1186 purple_prefs_connect_callback(handle
, "/purple/proxy/host", proxy_pref_cb
,
1188 purple_prefs_connect_callback(handle
, "/purple/proxy/port", proxy_pref_cb
,
1190 purple_prefs_connect_callback(handle
, "/purple/proxy/username",
1191 proxy_pref_cb
, NULL
);
1192 purple_prefs_connect_callback(handle
, "/purple/proxy/password",
1193 proxy_pref_cb
, NULL
);
1195 /* Load the initial proxy settings */
1196 purple_prefs_trigger_callback("/purple/proxy/type");
1197 purple_prefs_trigger_callback("/purple/proxy/host");
1198 purple_prefs_trigger_callback("/purple/proxy/port");
1199 purple_prefs_trigger_callback("/purple/proxy/username");
1200 purple_prefs_trigger_callback("/purple/proxy/password");
1204 purple_proxy_uninit(void)
1206 while (handles
!= NULL
)
1208 purple_proxy_connect_data_disconnect(handles
->data
, NULL
);
1209 purple_proxy_connect_data_destroy(handles
->data
);
1212 purple_prefs_disconnect_by_handle(purple_proxy_get_handle());
1214 purple_proxy_info_destroy(global_proxy_info
);
1215 global_proxy_info
= NULL
;