2 * @file proxy.c Proxy API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 /* this is a little piece of code to handle proxy connection */
29 /* it is intended to : 1st handle http proxy, using the CONNECT command
30 , 2nd provide an easy way to add socks support
31 , 3rd draw women to it like flies to honey */
43 struct _PurpleProxyConnectData
{
45 PurpleProxyConnectFunction connect_cb
;
52 PurpleDnsQueryData
*query_data
;
55 * This contains alternating length/char* values. The char*
56 * values need to be freed when removed from the linked list.
61 * All of the following variables are used when establishing a
62 * connection through a proxy.
67 PurpleInputFunction read_cb
;
73 static const char * const socks5errors
[] = {
75 "general SOCKS server failure\n",
76 "connection not allowed by ruleset\n",
77 "Network unreachable\n",
79 "Connection refused\n",
81 "Command not supported\n",
82 "Address type not supported\n"
85 static PurpleProxyInfo
*global_proxy_info
= NULL
;
87 static GSList
*handles
= NULL
;
89 static void try_connect(PurpleProxyConnectData
*connect_data
);
92 * TODO: Eventually (GObjectification) this bad boy will be removed, because it is
93 * a gross fix for a crashy problem.
95 #define PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data) g_slist_find(handles, connect_data)
97 /**************************************************************************
99 **************************************************************************/
101 purple_proxy_info_new(void)
103 return g_new0(PurpleProxyInfo
, 1);
107 purple_proxy_info_destroy(PurpleProxyInfo
*info
)
109 g_return_if_fail(info
!= NULL
);
112 g_free(info
->username
);
113 g_free(info
->password
);
119 purple_proxy_info_set_type(PurpleProxyInfo
*info
, PurpleProxyType type
)
121 g_return_if_fail(info
!= NULL
);
127 purple_proxy_info_set_host(PurpleProxyInfo
*info
, const char *host
)
129 g_return_if_fail(info
!= NULL
);
132 info
->host
= g_strdup(host
);
136 purple_proxy_info_set_port(PurpleProxyInfo
*info
, int port
)
138 g_return_if_fail(info
!= NULL
);
144 purple_proxy_info_set_username(PurpleProxyInfo
*info
, const char *username
)
146 g_return_if_fail(info
!= NULL
);
148 g_free(info
->username
);
149 info
->username
= g_strdup(username
);
153 purple_proxy_info_set_password(PurpleProxyInfo
*info
, const char *password
)
155 g_return_if_fail(info
!= NULL
);
157 g_free(info
->password
);
158 info
->password
= g_strdup(password
);
162 purple_proxy_info_get_type(const PurpleProxyInfo
*info
)
164 g_return_val_if_fail(info
!= NULL
, PURPLE_PROXY_NONE
);
170 purple_proxy_info_get_host(const PurpleProxyInfo
*info
)
172 g_return_val_if_fail(info
!= NULL
, NULL
);
178 purple_proxy_info_get_port(const PurpleProxyInfo
*info
)
180 g_return_val_if_fail(info
!= NULL
, 0);
186 purple_proxy_info_get_username(const PurpleProxyInfo
*info
)
188 g_return_val_if_fail(info
!= NULL
, NULL
);
190 return info
->username
;
194 purple_proxy_info_get_password(const PurpleProxyInfo
*info
)
196 g_return_val_if_fail(info
!= NULL
, NULL
);
198 return info
->password
;
201 /**************************************************************************
203 **************************************************************************/
205 purple_global_proxy_get_info(void)
207 return global_proxy_info
;
210 static PurpleProxyInfo
*
211 purple_gnome_proxy_get_info(void)
213 static PurpleProxyInfo info
= {0, NULL
, 0, NULL
, NULL
};
214 gboolean use_same_proxy
= FALSE
;
215 gchar
*tmp
, *err
= NULL
;
217 tmp
= g_find_program_in_path("gconftool-2");
219 return purple_global_proxy_get_info();
224 /* Check whether to use a proxy. */
225 if (!g_spawn_command_line_sync("gconftool-2 -g /system/proxy/mode",
226 &tmp
, &err
, NULL
, NULL
))
227 return purple_global_proxy_get_info();
231 if (!strcmp(tmp
, "none\n")) {
232 info
.type
= PURPLE_PROXY_NONE
;
237 if (strcmp(tmp
, "manual\n")) {
238 /* Unknown setting. Fallback to using our global proxy settings. */
240 return purple_global_proxy_get_info();
246 /* Free the old fields */
252 g_free(info
.username
);
253 info
.username
= NULL
;
256 g_free(info
.password
);
257 info
.password
= NULL
;
260 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/use_same_proxy",
261 &tmp
, &err
, NULL
, NULL
))
262 return purple_global_proxy_get_info();
266 if (!strcmp(tmp
, "true\n"))
267 use_same_proxy
= TRUE
;
271 if (!use_same_proxy
) {
272 if (!g_spawn_command_line_sync("gconftool-2 -g /system/proxy/socks_host",
273 &info
.host
, &err
, NULL
, NULL
))
274 return purple_global_proxy_get_info();
279 if(info
.host
!= NULL
)
280 g_strchomp(info
.host
);
282 if (!use_same_proxy
&& (info
.host
!= NULL
) && (*info
.host
!= '\0')) {
283 info
.type
= PURPLE_PROXY_SOCKS5
;
284 if (!g_spawn_command_line_sync("gconftool-2 -g /system/proxy/socks_port",
285 &tmp
, &err
, NULL
, NULL
))
289 return purple_global_proxy_get_info();
292 info
.port
= atoi(tmp
);
296 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/host",
297 &info
.host
, &err
, NULL
, NULL
))
298 return purple_global_proxy_get_info();
302 /* If we get this far then we know we're using an HTTP proxy */
303 info
.type
= PURPLE_PROXY_HTTP
;
305 g_strchomp(info
.host
);
306 if (*info
.host
== '\0')
308 purple_debug_info("proxy", "Gnome proxy settings are set to "
309 "'manual' but no suitable proxy server is specified. Using "
310 "Pidgin's proxy settings instead.\n");
313 return purple_global_proxy_get_info();
316 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/authentication_user",
317 &info
.username
, &err
, NULL
, NULL
))
321 return purple_global_proxy_get_info();
325 g_strchomp(info
.username
);
327 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/authentication_password",
328 &info
.password
, &err
, NULL
, NULL
))
332 g_free(info
.username
);
333 info
.username
= NULL
;
334 return purple_global_proxy_get_info();
338 g_strchomp(info
.password
);
340 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/port",
341 &tmp
, &err
, NULL
, NULL
))
345 g_free(info
.username
);
346 info
.username
= NULL
;
347 g_free(info
.password
);
348 info
.password
= NULL
;
349 return purple_global_proxy_get_info();
352 info
.port
= atoi(tmp
);
361 typedef BOOL (CALLBACK
* LPFNWINHTTPGETIEPROXYCONFIG
)(/*IN OUT*/ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
* pProxyConfig
);
363 /* This modifies "host" in-place evilly */
365 _proxy_fill_hostinfo(PurpleProxyInfo
*info
, char *host
, int default_port
)
367 int port
= default_port
;
370 d
= g_strrstr(host
, ":");
375 sscanf(d
, "%d", &port
);
377 purple_proxy_info_set_host(info
, host
);
378 purple_proxy_info_set_port(info
, port
);
381 static PurpleProxyInfo
*
382 purple_win32_proxy_get_info(void)
384 static LPFNWINHTTPGETIEPROXYCONFIG MyWinHttpGetIEProxyConfig
= NULL
;
385 static gboolean loaded
= FALSE
;
386 static PurpleProxyInfo info
= {0, NULL
, 0, NULL
, NULL
};
388 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config
;
392 MyWinHttpGetIEProxyConfig
= (LPFNWINHTTPGETIEPROXYCONFIG
)
393 wpurple_find_and_loadproc("winhttp.dll", "WinHttpGetIEProxyConfigForCurrentUser");
394 if (!MyWinHttpGetIEProxyConfig
)
395 purple_debug_info("proxy", "Unable to read Windows Proxy Settings.\n");
398 if (!MyWinHttpGetIEProxyConfig
)
401 ZeroMemory(&ie_proxy_config
, sizeof(ie_proxy_config
));
402 if (!MyWinHttpGetIEProxyConfig(&ie_proxy_config
)) {
403 purple_debug_error("proxy", "Error reading Windows Proxy Settings(%lu).\n", GetLastError());
407 /* We can't do much if it is autodetect*/
408 if (ie_proxy_config
.fAutoDetect
) {
409 purple_debug_error("proxy", "Windows Proxy Settings set to autodetect (not supported).\n");
411 /* TODO: For 3.0.0 we'll revisit this (maybe)*/
415 } else if (ie_proxy_config
.lpszProxy
) {
416 gchar
*proxy_list
= g_utf16_to_utf8(ie_proxy_config
.lpszProxy
, -1,
419 /* We can't do anything about the bypass list, as we don't have the url */
420 /* TODO: For 3.0.0 we'll revisit this*/
422 /* There are proxy settings for several protocols */
423 if (proxy_list
&& *proxy_list
) {
424 char *specific
= NULL
, *tmp
;
426 /* If there is only a global proxy, which means "HTTP" */
427 if (!strchr(proxy_list
, ';') || (specific
= g_strstr_len(proxy_list
, -1, "http=")) != NULL
) {
430 specific
+= strlen("http=");
431 tmp
= strchr(specific
, ';');
434 /* specific now points the proxy server (and port) */
436 specific
= proxy_list
;
438 purple_proxy_info_set_type(&info
, PURPLE_PROXY_HTTP
);
439 _proxy_fill_hostinfo(&info
, specific
, 80);
440 /* TODO: is there a way to set the username/password? */
441 purple_proxy_info_set_username(&info
, NULL
);
442 purple_proxy_info_set_password(&info
, NULL
);
444 purple_debug_info("proxy", "Windows Proxy Settings: HTTP proxy: '%s:%d'.\n",
445 purple_proxy_info_get_host(&info
),
446 purple_proxy_info_get_port(&info
));
448 } else if ((specific
= g_strstr_len(proxy_list
, -1, "socks=")) != NULL
) {
450 specific
+= strlen("socks=");
451 tmp
= strchr(specific
, ';');
454 /* specific now points the proxy server (and port) */
456 purple_proxy_info_set_type(&info
, PURPLE_PROXY_SOCKS5
);
457 _proxy_fill_hostinfo(&info
, specific
, 1080);
458 /* TODO: is there a way to set the username/password? */
459 purple_proxy_info_set_username(&info
, NULL
);
460 purple_proxy_info_set_password(&info
, NULL
);
462 purple_debug_info("proxy", "Windows Proxy Settings: SOCKS5 proxy: '%s:%d'.\n",
463 purple_proxy_info_get_host(&info
),
464 purple_proxy_info_get_port(&info
));
468 purple_debug_info("proxy", "Windows Proxy Settings: No supported proxy specified.\n");
470 purple_proxy_info_set_type(&info
, PURPLE_PROXY_NONE
);
475 /* TODO: Fix API to be able look at proxy bypass settings */
479 purple_debug_info("proxy", "No Windows proxy set.\n");
480 purple_proxy_info_set_type(&info
, PURPLE_PROXY_NONE
);
483 if (ie_proxy_config
.lpszAutoConfigUrl
)
484 GlobalFree(ie_proxy_config
.lpszAutoConfigUrl
);
485 if (ie_proxy_config
.lpszProxy
)
486 GlobalFree(ie_proxy_config
.lpszProxy
);
487 if (ie_proxy_config
.lpszProxyBypass
)
488 GlobalFree(ie_proxy_config
.lpszProxyBypass
);
495 /**************************************************************************
497 **************************************************************************/
500 * Whoever calls this needs to have called
501 * purple_proxy_connect_data_disconnect() beforehand.
504 purple_proxy_connect_data_destroy(PurpleProxyConnectData
*connect_data
)
506 handles
= g_slist_remove(handles
, connect_data
);
508 if (connect_data
->query_data
!= NULL
)
509 purple_dnsquery_destroy(connect_data
->query_data
);
511 while (connect_data
->hosts
!= NULL
)
513 /* Discard the length... */
514 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
515 /* Free the address... */
516 g_free(connect_data
->hosts
->data
);
517 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
520 g_free(connect_data
->host
);
521 g_free(connect_data
);
525 * Free all information dealing with a connection attempt and
526 * reset the connect_data to prepare for it to try to connect
527 * to another IP address.
529 * If an error message is passed in, then we know the connection
530 * attempt failed. If the connection attempt failed and
531 * connect_data->hosts is not empty then we try the next IP address.
532 * If the connection attempt failed and we have no more hosts
533 * try try then we call the callback with the given error message,
534 * then destroy the connect_data.
536 * @param error_message An error message explaining why the connection
537 * failed. This will be passed to the callback function
538 * specified in the call to purple_proxy_connect(). If the
539 * connection was successful then pass in null.
542 purple_proxy_connect_data_disconnect(PurpleProxyConnectData
*connect_data
, const gchar
*error_message
)
544 if (connect_data
->inpa
> 0)
546 purple_input_remove(connect_data
->inpa
);
547 connect_data
->inpa
= 0;
550 if (connect_data
->fd
>= 0)
552 close(connect_data
->fd
);
553 connect_data
->fd
= -1;
556 g_free(connect_data
->write_buffer
);
557 connect_data
->write_buffer
= NULL
;
559 g_free(connect_data
->read_buffer
);
560 connect_data
->read_buffer
= NULL
;
562 if (error_message
!= NULL
)
564 purple_debug_info("proxy", "Connection attempt failed: %s\n",
566 if (connect_data
->hosts
!= NULL
)
567 try_connect(connect_data
);
570 /* Everything failed! Tell the originator of the request. */
571 connect_data
->connect_cb(connect_data
->data
, -1, error_message
);
572 purple_proxy_connect_data_destroy(connect_data
);
578 * This calls purple_proxy_connect_data_disconnect(), but it lets you
579 * specify the error_message using a printf()-like syntax.
582 purple_proxy_connect_data_disconnect_formatted(PurpleProxyConnectData
*connect_data
, const char *format
, ...)
587 va_start(args
, format
);
588 tmp
= g_strdup_vprintf(format
, args
);
591 purple_proxy_connect_data_disconnect(connect_data
, tmp
);
596 purple_proxy_connect_data_connected(PurpleProxyConnectData
*connect_data
)
598 connect_data
->connect_cb(connect_data
->data
, connect_data
->fd
, NULL
);
601 * We've passed the file descriptor to the protocol, so it's no longer
602 * our responsibility, and we should be careful not to free it when
603 * we destroy the connect_data.
605 connect_data
->fd
= -1;
607 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
608 purple_proxy_connect_data_destroy(connect_data
);
612 socket_ready_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
614 PurpleProxyConnectData
*connect_data
= data
;
618 /* If the socket-connected message had already been triggered when connect_data
619 * was destroyed via purple_proxy_connect_cancel(), we may get here with a freed connect_data.
621 if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data
))
624 purple_debug_info("proxy", "Connecting to %s:%d.\n",
625 connect_data
->host
, connect_data
->port
);
628 * purple_input_get_error after a non-blocking connect returns -1 if something is
629 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
630 * error holds what connect would have returned if it blocked until now.
631 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
632 * and anything else is a real error.
634 * (error == EINPROGRESS can happen after a select because the kernel can
635 * be overly optimistic sometimes. select is just a hint that you might be
636 * able to do something.)
638 ret
= purple_input_get_error(connect_data
->fd
, &error
);
640 if (ret
== 0 && error
== EINPROGRESS
) {
641 /* No worries - we'll be called again later */
642 /* TODO: Does this ever happen? */
643 purple_debug_info("proxy", "(ret == 0 && error == EINPROGRESS)\n");
647 if (ret
!= 0 || error
!= 0) {
650 purple_debug_info("proxy", "Error connecting to %s:%d (%s).\n",
651 connect_data
->host
, connect_data
->port
, g_strerror(error
));
653 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
657 purple_proxy_connect_data_connected(connect_data
);
661 clean_connect(gpointer data
)
663 purple_proxy_connect_data_connected(data
);
669 proxy_connect_none(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
673 purple_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
674 connect_data
->host
, connect_data
->port
);
676 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
677 if (connect_data
->fd
< 0)
679 purple_proxy_connect_data_disconnect_formatted(connect_data
,
680 _("Unable to create socket:\n%s"), g_strerror(errno
));
684 flags
= fcntl(connect_data
->fd
, F_GETFL
);
685 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
687 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
690 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
692 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
694 purple_debug_info("proxy", "Connection in progress\n");
695 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
696 PURPLE_INPUT_WRITE
, socket_ready_cb
, connect_data
);
700 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
706 * The connection happened IMMEDIATELY... strange, but whatever.
708 int error
= ETIMEDOUT
;
711 purple_debug_info("proxy", "Connected immediately.\n");
713 ret
= purple_input_get_error(connect_data
->fd
, &error
);
714 if ((ret
!= 0) || (error
!= 0))
718 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
723 * We want to call the "connected" callback eventually, but we
724 * don't want to call it before we return, just in case.
726 purple_timeout_add(10, clean_connect
, connect_data
);
731 * This is a utility function used by the HTTP, SOCKS4 and SOCKS5
732 * connect functions. It writes data from a buffer to a socket.
733 * When all the data is written it sets up a watcher to read a
734 * response and call a specified function.
737 proxy_do_write(gpointer data
, gint source
, PurpleInputCondition cond
)
739 PurpleProxyConnectData
*connect_data
;
740 const guchar
*request
;
745 request
= connect_data
->write_buffer
+ connect_data
->written_len
;
746 request_len
= connect_data
->write_buf_len
- connect_data
->written_len
;
748 ret
= write(connect_data
->fd
, request
, request_len
);
756 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
759 if (ret
< request_len
) {
760 connect_data
->written_len
+= ret
;
764 /* We're done writing data! Wait for a response. */
765 g_free(connect_data
->write_buffer
);
766 connect_data
->write_buffer
= NULL
;
767 purple_input_remove(connect_data
->inpa
);
768 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
769 PURPLE_INPUT_READ
, connect_data
->read_cb
, connect_data
);
772 #define HTTP_GOODSTRING "HTTP/1.0 200"
773 #define HTTP_GOODSTRING2 "HTTP/1.1 200"
776 * We're using an HTTP proxy for a non-port 80 tunnel. Read the
777 * response to the CONNECT request.
780 http_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
782 int len
, headers_len
, status
= 0;
784 PurpleProxyConnectData
*connect_data
= data
;
788 if (connect_data
->read_buffer
== NULL
) {
789 connect_data
->read_buf_len
= 8192;
790 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
791 connect_data
->read_len
= 0;
794 p
= (char *)connect_data
->read_buffer
+ connect_data
->read_len
;
795 max_read
= connect_data
->read_buf_len
- connect_data
->read_len
- 1;
797 len
= read(connect_data
->fd
, p
, max_read
);
800 purple_proxy_connect_data_disconnect(connect_data
,
801 _("Server closed the connection."));
811 purple_proxy_connect_data_disconnect_formatted(connect_data
,
812 _("Lost connection with server:\n%s"), g_strerror(errno
));
816 connect_data
->read_len
+= len
;
819 p
= g_strstr_len((const gchar
*)connect_data
->read_buffer
,
820 connect_data
->read_len
, "\r\n\r\n");
823 headers_len
= (p
- (char *)connect_data
->read_buffer
) + 4;
824 } else if(len
== max_read
)
829 error
= strncmp((const char *)connect_data
->read_buffer
, "HTTP/", 5) != 0;
832 p
= (char *)connect_data
->read_buffer
+ 5;
833 major
= strtol(p
, &p
, 10);
834 error
= (major
== 0) || (*p
!= '.');
838 minor
= strtol(p
, &p
, 10);
842 status
= strtol(p
, &p
, 10);
848 /* Read the contents */
849 p
= g_strrstr((const gchar
*)connect_data
->read_buffer
, "Content-Length: ");
854 p
+= strlen("Content-Length: ");
855 tmp
= strchr(p
, '\r');
862 /* Compensate for what has already been read */
863 len
-= connect_data
->read_len
- headers_len
;
864 /* I'm assuming that we're doing this to prevent the server from
865 complaining / breaking since we don't read the whole page */
867 /* TODO: deal with EAGAIN (and other errors) better */
868 if (read(connect_data
->fd
, &tmpc
, 1) < 0 && errno
!= EAGAIN
)
874 purple_proxy_connect_data_disconnect_formatted(connect_data
,
875 _("Unable to parse response from HTTP proxy: %s\n"),
876 connect_data
->read_buffer
);
879 else if (status
!= 200) {
880 purple_debug_error("proxy",
881 "Proxy server replied with:\n%s\n",
882 connect_data
->read_buffer
);
884 if (status
== 407 /* Proxy Auth */) {
888 header
= g_strrstr((const gchar
*)connect_data
->read_buffer
,
889 "Proxy-Authenticate: NTLM");
890 if (header
!= NULL
) {
891 const char *header_end
= header
+ strlen("Proxy-Authenticate: NTLM");
892 const char *domain
= purple_proxy_info_get_username(connect_data
->gpi
);
893 char *username
= NULL
, hostname
[256];
897 ret
= gethostname(hostname
, sizeof(hostname
));
898 hostname
[sizeof(hostname
) - 1] = '\0';
899 if (ret
< 0 || hostname
[0] == '\0') {
900 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
901 strcpy(hostname
, "localhost");
905 username
= (char*) strchr(domain
, '\\');
906 if (username
== NULL
) {
907 purple_proxy_connect_data_disconnect_formatted(connect_data
,
908 _("HTTP proxy connection error %d"), status
);
913 /* Is there a message? */
914 if (*header_end
== ' ') {
915 /* Check for Type-2 */
916 char *tmp
= (char*) header
;
921 while(*tmp
!= '\r' && *tmp
!= '\0') tmp
++;
923 nonce
= purple_ntlm_parse_type2(header_end
, NULL
);
924 response
= purple_ntlm_gen_type3(username
,
925 (gchar
*) purple_proxy_info_get_password(connect_data
->gpi
),
927 domain
, nonce
, NULL
);
929 } else /* Empty message */
930 response
= purple_ntlm_gen_type1(hostname
, domain
);
934 request
= g_strdup_printf(
935 "CONNECT %s:%d HTTP/1.1\r\n"
937 "Proxy-Authorization: NTLM %s\r\n"
938 "Proxy-Connection: Keep-Alive\r\n\r\n",
939 connect_data
->host
, connect_data
->port
,
940 connect_data
->host
, connect_data
->port
,
945 } else if((header
= g_strrstr((const char *)connect_data
->read_buffer
, "Proxy-Authenticate: Basic"))) {
947 const char *username
, *password
;
949 username
= purple_proxy_info_get_username(connect_data
->gpi
);
950 password
= purple_proxy_info_get_password(connect_data
->gpi
);
952 t1
= g_strdup_printf("%s:%s",
953 username
? username
: "",
954 password
? password
: "");
955 t2
= purple_base64_encode((guchar
*)t1
, strlen(t1
));
958 request
= g_strdup_printf(
959 "CONNECT %s:%d HTTP/1.1\r\n"
961 "Proxy-Authorization: Basic %s\r\n",
962 connect_data
->host
, connect_data
->port
,
963 connect_data
->host
, connect_data
->port
,
969 purple_proxy_connect_data_disconnect_formatted(connect_data
,
970 _("HTTP proxy connection error %d"), status
);
974 purple_input_remove(connect_data
->inpa
);
975 g_free(connect_data
->read_buffer
);
976 connect_data
->read_buffer
= NULL
;
978 connect_data
->write_buffer
= (guchar
*)request
;
979 connect_data
->write_buf_len
= strlen(request
);
980 connect_data
->written_len
= 0;
982 connect_data
->read_cb
= http_canread
;
984 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
985 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
987 proxy_do_write(connect_data
, connect_data
->fd
, cond
);
994 purple_proxy_connect_data_disconnect_formatted(connect_data
,
995 _("Access denied: HTTP proxy server forbids port %d tunneling."),
998 purple_proxy_connect_data_disconnect_formatted(connect_data
,
999 _("HTTP proxy connection error %d"), status
);
1002 purple_input_remove(connect_data
->inpa
);
1003 connect_data
->inpa
= 0;
1004 g_free(connect_data
->read_buffer
);
1005 connect_data
->read_buffer
= NULL
;
1006 purple_debug_info("proxy", "HTTP proxy connection established\n");
1007 purple_proxy_connect_data_connected(connect_data
);
1013 http_start_connect_tunneling(PurpleProxyConnectData
*connect_data
) {
1017 purple_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n",
1018 connect_data
->host
, connect_data
->port
);
1020 request
= g_string_sized_new(4096);
1021 g_string_append_printf(request
,
1022 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n",
1023 connect_data
->host
, connect_data
->port
,
1024 connect_data
->host
, connect_data
->port
);
1026 if (purple_proxy_info_get_username(connect_data
->gpi
) != NULL
)
1028 char *t1
, *t2
, *ntlm_type1
;
1031 ret
= gethostname(hostname
, sizeof(hostname
));
1032 hostname
[sizeof(hostname
) - 1] = '\0';
1033 if (ret
< 0 || hostname
[0] == '\0') {
1034 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
1035 strcpy(hostname
, "localhost");
1038 t1
= g_strdup_printf("%s:%s",
1039 purple_proxy_info_get_username(connect_data
->gpi
),
1040 purple_proxy_info_get_password(connect_data
->gpi
) ?
1041 purple_proxy_info_get_password(connect_data
->gpi
) : "");
1042 t2
= purple_base64_encode((const guchar
*)t1
, strlen(t1
));
1045 ntlm_type1
= purple_ntlm_gen_type1(hostname
, "");
1047 g_string_append_printf(request
,
1048 "Proxy-Authorization: Basic %s\r\n"
1049 "Proxy-Authorization: NTLM %s\r\n"
1050 "Proxy-Connection: Keep-Alive\r\n",
1056 g_string_append(request
, "\r\n");
1058 connect_data
->write_buf_len
= request
->len
;
1059 connect_data
->write_buffer
= (guchar
*)g_string_free(request
, FALSE
);
1060 connect_data
->written_len
= 0;
1061 connect_data
->read_cb
= http_canread
;
1063 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1064 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1065 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1069 http_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
) {
1070 PurpleProxyConnectData
*connect_data
= data
;
1071 int ret
, error
= ETIMEDOUT
;
1073 purple_debug_info("proxy", "Connected to %s:%d.\n",
1074 connect_data
->host
, connect_data
->port
);
1076 if (connect_data
->inpa
> 0) {
1077 purple_input_remove(connect_data
->inpa
);
1078 connect_data
->inpa
= 0;
1081 ret
= purple_input_get_error(connect_data
->fd
, &error
);
1082 if (ret
!= 0 || error
!= 0) {
1085 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
1089 if (connect_data
->port
== 80) {
1091 * If we're trying to connect to something running on
1092 * port 80 then we assume the traffic using this
1093 * connection is going to be HTTP traffic. If it's
1094 * not then this will fail (uglily). But it's good
1095 * to avoid using the CONNECT method because it's
1096 * not always allowed.
1098 purple_debug_info("proxy", "HTTP proxy connection established\n");
1099 purple_proxy_connect_data_connected(connect_data
);
1101 http_start_connect_tunneling(connect_data
);
1107 proxy_connect_http(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
1111 purple_debug_info("proxy",
1112 "Connecting to %s:%d via %s:%d using HTTP\n",
1113 connect_data
->host
, connect_data
->port
,
1114 (purple_proxy_info_get_host(connect_data
->gpi
) ? purple_proxy_info_get_host(connect_data
->gpi
) : "(null)"),
1115 purple_proxy_info_get_port(connect_data
->gpi
));
1117 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
1118 if (connect_data
->fd
< 0)
1120 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1121 _("Unable to create socket:\n%s"), g_strerror(errno
));
1125 flags
= fcntl(connect_data
->fd
, F_GETFL
);
1126 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
1128 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
1131 if (connect(connect_data
->fd
, addr
, addrlen
) != 0) {
1132 if (errno
== EINPROGRESS
|| errno
== EINTR
) {
1133 purple_debug_info("proxy", "Connection in progress\n");
1135 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1136 PURPLE_INPUT_WRITE
, http_canwrite
, connect_data
);
1138 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1140 purple_debug_info("proxy", "Connected immediately.\n");
1142 http_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1147 s4_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
1149 PurpleProxyConnectData
*connect_data
= data
;
1153 /* This is really not going to block under normal circumstances, but to
1154 * be correct, we deal with the unlikely scenario */
1156 if (connect_data
->read_buffer
== NULL
) {
1157 connect_data
->read_buf_len
= 12;
1158 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1159 connect_data
->read_len
= 0;
1162 buf
= connect_data
->read_buffer
+ connect_data
->read_len
;
1163 max_read
= connect_data
->read_buf_len
- connect_data
->read_len
;
1165 len
= read(connect_data
->fd
, buf
, max_read
);
1167 if ((len
< 0 && errno
== EAGAIN
) || (len
> 0 && len
+ connect_data
->read_len
< 4))
1169 else if (len
+ connect_data
->read_len
>= 4) {
1170 if (connect_data
->read_buffer
[1] == 90) {
1171 purple_proxy_connect_data_connected(connect_data
);
1176 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1180 s4_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
1182 PurpleProxyConnectData
*connect_data
= data
;
1183 unsigned char packet
[9];
1184 struct sockaddr
*addr
;
1186 connect_data
->query_data
= NULL
;
1188 if (error_message
!= NULL
) {
1189 purple_proxy_connect_data_disconnect(connect_data
, error_message
);
1193 if (hosts
== NULL
) {
1194 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1195 _("Error resolving %s"), connect_data
->host
);
1199 /* Discard the length... */
1200 hosts
= g_slist_delete_link(hosts
, hosts
);
1202 hosts
= g_slist_delete_link(hosts
, hosts
);
1206 packet
[2] = connect_data
->port
>> 8;
1207 packet
[3] = connect_data
->port
& 0xff;
1208 memcpy(packet
+ 4, &((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
, 4);
1213 /* We could try the other hosts, but hopefully that shouldn't be necessary */
1214 while (hosts
!= NULL
) {
1215 /* Discard the length... */
1216 hosts
= g_slist_delete_link(hosts
, hosts
);
1217 /* Free the address... */
1218 g_free(hosts
->data
);
1219 hosts
= g_slist_delete_link(hosts
, hosts
);
1222 connect_data
->write_buffer
= g_memdup(packet
, sizeof(packet
));
1223 connect_data
->write_buf_len
= sizeof(packet
);
1224 connect_data
->written_len
= 0;
1225 connect_data
->read_cb
= s4_canread
;
1227 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1229 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1233 s4_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
)
1235 PurpleProxyConnectData
*connect_data
= data
;
1236 int error
= ETIMEDOUT
;
1239 purple_debug_info("socks4 proxy", "Connected.\n");
1241 if (connect_data
->inpa
> 0) {
1242 purple_input_remove(connect_data
->inpa
);
1243 connect_data
->inpa
= 0;
1246 ret
= purple_input_get_error(connect_data
->fd
, &error
);
1247 if ((ret
!= 0) || (error
!= 0)) {
1250 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
1255 * The socks4 spec doesn't include support for doing host name lookups by
1256 * the proxy. Many socks4 servers do this via the "socks4a" extension to
1257 * the protocol. There doesn't appear to be a way to detect if a server
1258 * supports this, so we require that the user set a global option.
1260 if (purple_prefs_get_bool("/purple/proxy/socks4_remotedns")) {
1261 unsigned char packet
[9];
1264 purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
1268 packet
[2] = connect_data
->port
>> 8;
1269 packet
[3] = connect_data
->port
& 0xff;
1276 len
= sizeof(packet
) + strlen(connect_data
->host
) + 1;
1278 connect_data
->write_buffer
= g_malloc0(len
);
1279 memcpy(connect_data
->write_buffer
, packet
, sizeof(packet
));
1280 memcpy(connect_data
->write_buffer
+ sizeof(packet
), connect_data
->host
, strlen(connect_data
->host
));
1281 connect_data
->write_buf_len
= len
;
1282 connect_data
->written_len
= 0;
1283 connect_data
->read_cb
= s4_canread
;
1285 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1287 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1289 connect_data
->query_data
= purple_dnsquery_a(connect_data
->host
,
1290 connect_data
->port
, s4_host_resolved
, connect_data
);
1292 if (connect_data
->query_data
== NULL
) {
1293 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
1294 purple_proxy_connect_data_destroy(connect_data
);
1300 proxy_connect_socks4(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
1304 purple_debug_info("proxy",
1305 "Connecting to %s:%d via %s:%d using SOCKS4\n",
1306 connect_data
->host
, connect_data
->port
,
1307 purple_proxy_info_get_host(connect_data
->gpi
),
1308 purple_proxy_info_get_port(connect_data
->gpi
));
1310 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
1311 if (connect_data
->fd
< 0)
1313 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1314 _("Unable to create socket:\n%s"), g_strerror(errno
));
1318 flags
= fcntl(connect_data
->fd
, F_GETFL
);
1319 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
1321 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
1324 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
1326 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
1328 purple_debug_info("proxy", "Connection in progress.\n");
1329 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1330 PURPLE_INPUT_WRITE
, s4_canwrite
, connect_data
);
1334 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1339 purple_debug_info("proxy", "Connected immediately.\n");
1341 s4_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1346 s5_ensure_buffer_length(PurpleProxyConnectData
*connect_data
, int len
)
1348 if(connect_data
->read_len
< len
) {
1349 if(connect_data
->read_buf_len
< len
) {
1350 /* it's not just that we haven't read enough, it's that we haven't tried to read enough yet */
1351 purple_debug_info("s5", "reallocing from %" G_GSIZE_FORMAT
1352 " to %d\n", connect_data
->read_buf_len
, len
);
1353 connect_data
->read_buf_len
= len
;
1354 connect_data
->read_buffer
= g_realloc(connect_data
->read_buffer
, connect_data
->read_buf_len
);
1363 s5_canread_again(gpointer data
, gint source
, PurpleInputCondition cond
)
1366 PurpleProxyConnectData
*connect_data
= data
;
1369 if (connect_data
->read_buffer
== NULL
) {
1370 connect_data
->read_buf_len
= 5;
1371 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1372 connect_data
->read_len
= 0;
1375 dest
= connect_data
->read_buffer
+ connect_data
->read_len
;
1376 buf
= connect_data
->read_buffer
;
1378 len
= read(connect_data
->fd
, dest
, (connect_data
->read_buf_len
- connect_data
->read_len
));
1382 purple_proxy_connect_data_disconnect(connect_data
,
1383 _("Server closed the connection."));
1389 if (errno
== EAGAIN
)
1394 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1395 _("Lost connection with server:\n%s"), g_strerror(errno
));
1399 connect_data
->read_len
+= len
;
1401 if(connect_data
->read_len
< 4)
1404 if ((buf
[0] != 0x05) || (buf
[1] != 0x00)) {
1405 if ((buf
[0] == 0x05) && (buf
[1] < 0x09)) {
1406 purple_debug_error("socks5 proxy", "%s", socks5errors
[buf
[1]]);
1407 purple_proxy_connect_data_disconnect(connect_data
,
1408 socks5errors
[buf
[1]]);
1410 purple_debug_error("socks5 proxy", "Bad data.\n");
1411 purple_proxy_connect_data_disconnect(connect_data
,
1412 _("Received invalid data on connection with server."));
1417 /* Skip past BND.ADDR */
1419 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */
1420 if(!s5_ensure_buffer_length(connect_data
, 4 + 4))
1424 case 0x03: /* the address field contains a fully-qualified domain name. The first
1425 octet of the address field contains the number of octets of name that
1426 follow, there is no terminating NUL octet. */
1427 if(!s5_ensure_buffer_length(connect_data
, 4 + 1))
1430 if(!s5_ensure_buffer_length(connect_data
, 4 + 1 + buf
[0]))
1434 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */
1435 if(!s5_ensure_buffer_length(connect_data
, 4 + 16))
1440 purple_debug_error("socks5 proxy", "Invalid ATYP received (0x%X)\n", buf
[3]);
1441 purple_proxy_connect_data_disconnect(connect_data
,
1442 _("Received invalid data on connection with server."));
1446 /* Skip past BND.PORT */
1447 if(!s5_ensure_buffer_length(connect_data
, (buf
- connect_data
->read_buffer
) + 2))
1450 purple_proxy_connect_data_connected(connect_data
);
1454 s5_sendconnect(gpointer data
, int source
)
1456 PurpleProxyConnectData
*connect_data
= data
;
1457 size_t hlen
= strlen(connect_data
->host
);
1458 connect_data
->write_buf_len
= 5 + hlen
+ 2;
1459 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1460 connect_data
->written_len
= 0;
1462 connect_data
->write_buffer
[0] = 0x05;
1463 connect_data
->write_buffer
[1] = 0x01; /* CONNECT */
1464 connect_data
->write_buffer
[2] = 0x00; /* reserved */
1465 connect_data
->write_buffer
[3] = 0x03; /* address type -- host name */
1466 connect_data
->write_buffer
[4] = hlen
;
1467 memcpy(connect_data
->write_buffer
+ 5, connect_data
->host
, hlen
);
1468 connect_data
->write_buffer
[5 + hlen
] = connect_data
->port
>> 8;
1469 connect_data
->write_buffer
[5 + hlen
+ 1] = connect_data
->port
& 0xff;
1471 connect_data
->read_cb
= s5_canread_again
;
1473 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1474 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1478 s5_readauth(gpointer data
, gint source
, PurpleInputCondition cond
)
1480 PurpleProxyConnectData
*connect_data
= data
;
1483 if (connect_data
->read_buffer
== NULL
) {
1484 connect_data
->read_buf_len
= 2;
1485 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1486 connect_data
->read_len
= 0;
1489 purple_debug_info("socks5 proxy", "Got auth response.\n");
1491 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1492 connect_data
->read_buf_len
- connect_data
->read_len
);
1496 purple_proxy_connect_data_disconnect(connect_data
,
1497 _("Server closed the connection."));
1503 if (errno
== EAGAIN
)
1508 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1509 _("Lost connection with server:\n%s"), g_strerror(errno
));
1513 connect_data
->read_len
+= len
;
1514 if (connect_data
->read_len
< 2)
1517 purple_input_remove(connect_data
->inpa
);
1518 connect_data
->inpa
= 0;
1520 if ((connect_data
->read_buffer
[0] != 0x01) || (connect_data
->read_buffer
[1] != 0x00)) {
1521 purple_proxy_connect_data_disconnect(connect_data
,
1522 _("Received invalid data on connection with server."));
1526 g_free(connect_data
->read_buffer
);
1527 connect_data
->read_buffer
= NULL
;
1529 s5_sendconnect(connect_data
, connect_data
->fd
);
1533 hmacmd5_chap(const unsigned char * challenge
, int challen
, const char * passwd
, unsigned char * response
)
1535 PurpleCipher
*cipher
;
1536 PurpleCipherContext
*ctx
;
1538 unsigned char Kxoripad
[65];
1539 unsigned char Kxoropad
[65];
1542 cipher
= purple_ciphers_find_cipher("md5");
1543 ctx
= purple_cipher_context_new(cipher
, NULL
);
1545 memset(Kxoripad
,0,sizeof(Kxoripad
));
1546 memset(Kxoropad
,0,sizeof(Kxoropad
));
1548 pwlen
=strlen(passwd
);
1550 purple_cipher_context_append(ctx
, (const guchar
*)passwd
, strlen(passwd
));
1551 purple_cipher_context_digest(ctx
, sizeof(Kxoripad
), Kxoripad
, NULL
);
1554 memcpy(Kxoripad
, passwd
, pwlen
);
1556 memcpy(Kxoropad
,Kxoripad
,pwlen
);
1558 for (i
=0;i
<64;i
++) {
1563 purple_cipher_context_reset(ctx
, NULL
);
1564 purple_cipher_context_append(ctx
, Kxoripad
, 64);
1565 purple_cipher_context_append(ctx
, challenge
, challen
);
1566 purple_cipher_context_digest(ctx
, sizeof(Kxoripad
), Kxoripad
, NULL
);
1568 purple_cipher_context_reset(ctx
, NULL
);
1569 purple_cipher_context_append(ctx
, Kxoropad
, 64);
1570 purple_cipher_context_append(ctx
, Kxoripad
, 16);
1571 purple_cipher_context_digest(ctx
, 16, response
, NULL
);
1573 purple_cipher_context_destroy(ctx
);
1577 s5_readchap(gpointer data
, gint source
, PurpleInputCondition cond
);
1580 * Return how many bytes we processed
1581 * -1 means we've shouldn't keep reading from the buffer
1584 s5_parse_chap_msg(PurpleProxyConnectData
*connect_data
)
1586 guchar
*buf
, *cmdbuf
= connect_data
->read_buffer
;
1587 int len
, navas
, currentav
;
1589 purple_debug_misc("socks5 proxy", "Reading CHAP message: %x\n", *cmdbuf
);
1591 if (*cmdbuf
!= 0x01) {
1592 purple_proxy_connect_data_disconnect(connect_data
,
1593 _("Received invalid data on connection with server."));
1600 purple_debug_misc("socks5 proxy", "Expecting %d attribute(s).\n", navas
);
1604 for (currentav
= 0; currentav
< navas
; currentav
++) {
1606 len
= connect_data
->read_len
- (cmdbuf
- connect_data
->read_buffer
);
1607 /* We don't have enough data to even know how long the next attribute is,
1608 * or we don't have the full length of the next attribute. */
1609 if (len
< 2 || len
< (cmdbuf
[1] + 2)) {
1610 /* Clear out the attributes that have been read - decrease the attribute count */
1611 connect_data
->read_buffer
[1] = navas
- currentav
;
1612 /* Move the unprocessed data into the first attribute position */
1613 memmove((connect_data
->read_buffer
+ 2), cmdbuf
, len
);
1614 /* Decrease the read count accordingly */
1615 connect_data
->read_len
= len
+ 2;
1617 purple_debug_info("socks5 proxy", "Need more data to retrieve attribute %d.\n", currentav
);
1624 if (cmdbuf
[1] == 0) {
1625 purple_debug_error("socks5 proxy", "Attribute %x Value length of 0; ignoring.\n", cmdbuf
[0]);
1630 switch (cmdbuf
[0]) {
1632 purple_debug_info("socks5 proxy", "Received STATUS of %x\n", buf
[0]);
1633 /* Did auth work? */
1634 if (buf
[0] == 0x00) {
1635 purple_input_remove(connect_data
->inpa
);
1636 connect_data
->inpa
= 0;
1637 g_free(connect_data
->read_buffer
);
1638 connect_data
->read_buffer
= NULL
;
1640 s5_sendconnect(connect_data
, connect_data
->fd
);
1643 purple_debug_warning("proxy",
1644 "socks5 CHAP authentication "
1645 "failed. Disconnecting...");
1646 purple_proxy_connect_data_disconnect(connect_data
,
1647 _("Authentication failed"));
1651 /* We've already validated that cmdbuf[1] is sane. */
1652 purple_debug_info("socks5 proxy", "Received TEXT-MESSAGE of '%.*s'\n", (int) cmdbuf
[1], buf
);
1655 purple_debug_info("socks5 proxy", "Received CHALLENGE\n");
1656 /* Server wants our credentials */
1658 connect_data
->write_buf_len
= 16 + 4;
1659 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1660 connect_data
->written_len
= 0;
1662 hmacmd5_chap(buf
, cmdbuf
[1],
1663 purple_proxy_info_get_password(connect_data
->gpi
),
1664 connect_data
->write_buffer
+ 4);
1665 /* TODO: What about USER-IDENTITY? */
1666 connect_data
->write_buffer
[0] = 0x01;
1667 connect_data
->write_buffer
[1] = 0x01;
1668 connect_data
->write_buffer
[2] = 0x04;
1669 connect_data
->write_buffer
[3] = 0x10;
1671 purple_input_remove(connect_data
->inpa
);
1672 g_free(connect_data
->read_buffer
);
1673 connect_data
->read_buffer
= NULL
;
1675 connect_data
->read_cb
= s5_readchap
;
1677 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1678 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1680 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1683 purple_debug_info("socks5 proxy", "Received ALGORIGTHMS of %x\n", buf
[0]);
1684 /* Server wants to select an algorithm */
1685 if (buf
[0] != 0x85) {
1686 /* Only currently support HMAC-MD5 */
1687 purple_debug_warning("proxy",
1688 "Server tried to select an "
1689 "algorithm that we did not advertise "
1690 "as supporting. This is a violation "
1691 "of the socks5 CHAP specification. "
1692 "Disconnecting...");
1693 purple_proxy_connect_data_disconnect(connect_data
,
1694 _("Received invalid data on connection with server."));
1699 purple_debug_info("socks5 proxy", "Received unused command %x, length=%d\n", cmdbuf
[0], cmdbuf
[1]);
1701 cmdbuf
= buf
+ cmdbuf
[1];
1704 return (cmdbuf
- connect_data
->read_buffer
);
1708 s5_readchap(gpointer data
, gint source
, PurpleInputCondition cond
)
1711 PurpleProxyConnectData
*connect_data
= data
;
1714 purple_debug(PURPLE_DEBUG_INFO
, "socks5 proxy", "Got CHAP response.\n");
1716 if (connect_data
->read_buffer
== NULL
) {
1717 /* A big enough butfer to read the message header (2 bytes) and at least one complete attribute and value (1 + 1 + 255). */
1718 connect_data
->read_buf_len
= 259;
1719 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1720 connect_data
->read_len
= 0;
1723 if (connect_data
->read_buf_len
- connect_data
->read_len
== 0) {
1724 /*If the stuff below is right, this shouldn't be possible. */
1725 purple_debug_error("socks5 proxy", "This is about to suck because the read buffer is full (shouldn't happen).\n");
1728 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1729 connect_data
->read_buf_len
- connect_data
->read_len
);
1732 purple_proxy_connect_data_disconnect(connect_data
,
1733 _("Server closed the connection."));
1738 if (errno
== EAGAIN
)
1743 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1744 _("Lost connection with server:\n%s"), g_strerror(errno
));
1748 connect_data
->read_len
+= len
;
1750 /* We may have read more than one message into the buffer, we need to make sure to process them all */
1753 /* We need more to be able to read this message */
1754 if (connect_data
->read_len
< 2)
1757 msg_ret
= s5_parse_chap_msg(connect_data
);
1762 /* See if we have another message already in the buffer */
1763 if ((len
= connect_data
->read_len
- msg_ret
) > 0) {
1765 /* Move on to the next message */
1766 memmove(connect_data
->read_buffer
, connect_data
->read_buffer
+ msg_ret
, len
);
1767 /* Decrease the read count accordingly */
1768 connect_data
->read_len
= len
;
1770 /* Try to read the message that connect_data->read_buffer now points to */
1777 /* Fell through. We ran out of CHAP events to process, but haven't
1778 * succeeded or failed authentication - there may be more to come.
1779 * If this is the case, come straight back here. */
1781 purple_debug_info("socks5 proxy", "Waiting for another message from which to read CHAP info.\n");
1783 /* We've processed all the available attributes, so get ready for a whole new message */
1784 g_free(connect_data
->read_buffer
);
1785 connect_data
->read_buffer
= NULL
;
1789 s5_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
1791 PurpleProxyConnectData
*connect_data
= data
;
1794 if (connect_data
->read_buffer
== NULL
) {
1795 connect_data
->read_buf_len
= 2;
1796 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1797 connect_data
->read_len
= 0;
1800 purple_debug_info("socks5 proxy", "Able to read.\n");
1802 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1803 connect_data
->read_buf_len
- connect_data
->read_len
);
1807 purple_proxy_connect_data_disconnect(connect_data
,
1808 _("Server closed the connection."));
1814 if (errno
== EAGAIN
)
1819 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1820 _("Lost connection with server:\n%s"), g_strerror(errno
));
1824 connect_data
->read_len
+= len
;
1825 if (connect_data
->read_len
< 2)
1828 purple_input_remove(connect_data
->inpa
);
1829 connect_data
->inpa
= 0;
1831 if ((connect_data
->read_buffer
[0] != 0x05) || (connect_data
->read_buffer
[1] == 0xff)) {
1832 purple_proxy_connect_data_disconnect(connect_data
,
1833 _("Received invalid data on connection with server."));
1837 if (connect_data
->read_buffer
[1] == 0x02) {
1841 u
= purple_proxy_info_get_username(connect_data
->gpi
);
1842 p
= purple_proxy_info_get_password(connect_data
->gpi
);
1844 i
= (u
== NULL
) ? 0 : strlen(u
);
1845 j
= (p
== NULL
) ? 0 : strlen(p
);
1847 connect_data
->write_buf_len
= 1 + 1 + i
+ 1 + j
;
1848 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1849 connect_data
->written_len
= 0;
1851 connect_data
->write_buffer
[0] = 0x01; /* version 1 */
1852 connect_data
->write_buffer
[1] = i
;
1854 memcpy(connect_data
->write_buffer
+ 2, u
, i
);
1855 connect_data
->write_buffer
[2 + i
] = j
;
1857 memcpy(connect_data
->write_buffer
+ 2 + i
+ 1, p
, j
);
1859 g_free(connect_data
->read_buffer
);
1860 connect_data
->read_buffer
= NULL
;
1862 connect_data
->read_cb
= s5_readauth
;
1864 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
,
1865 proxy_do_write
, connect_data
);
1867 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1870 } else if (connect_data
->read_buffer
[1] == 0x03) {
1872 userlen
= strlen(purple_proxy_info_get_username(connect_data
->gpi
));
1874 connect_data
->write_buf_len
= 7 + userlen
;
1875 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1876 connect_data
->written_len
= 0;
1878 connect_data
->write_buffer
[0] = 0x01;
1879 connect_data
->write_buffer
[1] = 0x02;
1880 connect_data
->write_buffer
[2] = 0x11;
1881 connect_data
->write_buffer
[3] = 0x01;
1882 connect_data
->write_buffer
[4] = 0x85;
1883 connect_data
->write_buffer
[5] = 0x02;
1884 connect_data
->write_buffer
[6] = userlen
;
1885 memcpy(connect_data
->write_buffer
+ 7,
1886 purple_proxy_info_get_username(connect_data
->gpi
), userlen
);
1888 g_free(connect_data
->read_buffer
);
1889 connect_data
->read_buffer
= NULL
;
1891 connect_data
->read_cb
= s5_readchap
;
1893 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
,
1894 proxy_do_write
, connect_data
);
1896 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1900 g_free(connect_data
->read_buffer
);
1901 connect_data
->read_buffer
= NULL
;
1903 s5_sendconnect(connect_data
, connect_data
->fd
);
1908 s5_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
)
1910 unsigned char buf
[5];
1912 PurpleProxyConnectData
*connect_data
= data
;
1913 int error
= ETIMEDOUT
;
1916 purple_debug_info("socks5 proxy", "Connected.\n");
1918 if (connect_data
->inpa
> 0)
1920 purple_input_remove(connect_data
->inpa
);
1921 connect_data
->inpa
= 0;
1924 ret
= purple_input_get_error(connect_data
->fd
, &error
);
1925 if ((ret
!= 0) || (error
!= 0))
1929 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
1934 buf
[0] = 0x05; /* SOCKS version 5 */
1936 if (purple_proxy_info_get_username(connect_data
->gpi
) != NULL
) {
1937 buf
[1] = 0x03; /* three methods */
1938 buf
[2] = 0x00; /* no authentication */
1939 buf
[3] = 0x03; /* CHAP authentication */
1940 buf
[4] = 0x02; /* username/password authentication */
1949 connect_data
->write_buf_len
= i
;
1950 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1951 memcpy(connect_data
->write_buffer
, buf
, i
);
1953 connect_data
->read_cb
= s5_canread
;
1955 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1956 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1960 proxy_connect_socks5(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
1964 purple_debug_info("proxy",
1965 "Connecting to %s:%d via %s:%d using SOCKS5\n",
1966 connect_data
->host
, connect_data
->port
,
1967 purple_proxy_info_get_host(connect_data
->gpi
),
1968 purple_proxy_info_get_port(connect_data
->gpi
));
1970 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
1971 if (connect_data
->fd
< 0)
1973 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1974 _("Unable to create socket:\n%s"), g_strerror(errno
));
1978 flags
= fcntl(connect_data
->fd
, F_GETFL
);
1979 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
1981 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
1984 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
1986 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
1988 purple_debug_info("socks5 proxy", "Connection in progress\n");
1989 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1990 PURPLE_INPUT_WRITE
, s5_canwrite
, connect_data
);
1994 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1999 purple_debug_info("proxy", "Connected immediately.\n");
2001 s5_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
2006 * This function attempts to connect to the next IP address in the list
2007 * of IP addresses returned to us by purple_dnsquery_a() and attemps
2008 * to connect to each one. This is called after the hostname is
2009 * resolved, and each time a connection attempt fails (assuming there
2010 * is another IP address to try).
2012 #ifndef INET6_ADDRSTRLEN
2013 #define INET6_ADDRSTRLEN 46
2016 static void try_connect(PurpleProxyConnectData
*connect_data
)
2019 struct sockaddr
*addr
;
2020 char ipaddr
[INET6_ADDRSTRLEN
];
2022 addrlen
= GPOINTER_TO_INT(connect_data
->hosts
->data
);
2023 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
2024 addr
= connect_data
->hosts
->data
;
2025 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
2026 #ifdef HAVE_INET_NTOP
2027 inet_ntop(addr
->sa_family
, &((struct sockaddr_in
*)addr
)->sin_addr
,
2028 ipaddr
, sizeof(ipaddr
));
2030 memcpy(ipaddr
, inet_ntoa(((struct sockaddr_in
*)addr
)->sin_addr
),
2033 purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr
);
2035 switch (purple_proxy_info_get_type(connect_data
->gpi
)) {
2036 case PURPLE_PROXY_NONE
:
2037 proxy_connect_none(connect_data
, addr
, addrlen
);
2040 case PURPLE_PROXY_HTTP
:
2041 proxy_connect_http(connect_data
, addr
, addrlen
);
2044 case PURPLE_PROXY_SOCKS4
:
2045 proxy_connect_socks4(connect_data
, addr
, addrlen
);
2048 case PURPLE_PROXY_SOCKS5
:
2049 proxy_connect_socks5(connect_data
, addr
, addrlen
);
2052 case PURPLE_PROXY_USE_ENVVAR
:
2053 proxy_connect_http(connect_data
, addr
, addrlen
);
2064 connection_host_resolved(GSList
*hosts
, gpointer data
,
2065 const char *error_message
)
2067 PurpleProxyConnectData
*connect_data
;
2069 connect_data
= data
;
2070 connect_data
->query_data
= NULL
;
2072 if (error_message
!= NULL
)
2074 purple_proxy_connect_data_disconnect(connect_data
, error_message
);
2080 purple_proxy_connect_data_disconnect(connect_data
, _("Could not resolve host name"));
2084 connect_data
->hosts
= hosts
;
2086 try_connect(connect_data
);
2090 purple_proxy_get_setup(PurpleAccount
*account
)
2092 PurpleProxyInfo
*gpi
= NULL
;
2095 /* This is used as a fallback so we don't overwrite the selected proxy type */
2096 static PurpleProxyInfo
*tmp_none_proxy_info
= NULL
;
2097 if (!tmp_none_proxy_info
) {
2098 tmp_none_proxy_info
= purple_proxy_info_new();
2099 purple_proxy_info_set_type(tmp_none_proxy_info
, PURPLE_PROXY_NONE
);
2102 if (account
&& purple_account_get_proxy_info(account
) != NULL
) {
2103 gpi
= purple_account_get_proxy_info(account
);
2104 if (purple_proxy_info_get_type(gpi
) == PURPLE_PROXY_USE_GLOBAL
)
2108 if (purple_running_gnome())
2109 gpi
= purple_gnome_proxy_get_info();
2111 gpi
= purple_global_proxy_get_info();
2114 if (purple_proxy_info_get_type(gpi
) == PURPLE_PROXY_USE_ENVVAR
) {
2115 if ((tmp
= g_getenv("HTTP_PROXY")) != NULL
||
2116 (tmp
= g_getenv("http_proxy")) != NULL
||
2117 (tmp
= g_getenv("HTTPPROXY")) != NULL
) {
2118 char *proxyhost
, *proxyuser
, *proxypasswd
;
2121 /* http_proxy-format:
2122 * export http_proxy="http://user:passwd@your.proxy.server:port/"
2124 if(purple_url_parse(tmp
, &proxyhost
, &proxyport
, NULL
, &proxyuser
, &proxypasswd
)) {
2125 purple_proxy_info_set_host(gpi
, proxyhost
);
2128 purple_proxy_info_set_username(gpi
, proxyuser
);
2131 purple_proxy_info_set_password(gpi
, proxypasswd
);
2132 g_free(proxypasswd
);
2134 /* only for backward compatibility */
2135 if (proxyport
== 80 &&
2136 ((tmp
= g_getenv("HTTP_PROXY_PORT")) != NULL
||
2137 (tmp
= g_getenv("http_proxy_port")) != NULL
||
2138 (tmp
= g_getenv("HTTPPROXYPORT")) != NULL
))
2139 proxyport
= atoi(tmp
);
2141 purple_proxy_info_set_port(gpi
, proxyport
);
2143 /* XXX: Do we want to skip this step if user/password were part of url? */
2144 if ((tmp
= g_getenv("HTTP_PROXY_USER")) != NULL
||
2145 (tmp
= g_getenv("http_proxy_user")) != NULL
||
2146 (tmp
= g_getenv("HTTPPROXYUSER")) != NULL
)
2147 purple_proxy_info_set_username(gpi
, tmp
);
2149 if ((tmp
= g_getenv("HTTP_PROXY_PASS")) != NULL
||
2150 (tmp
= g_getenv("http_proxy_pass")) != NULL
||
2151 (tmp
= g_getenv("HTTPPROXYPASS")) != NULL
)
2152 purple_proxy_info_set_password(gpi
, tmp
);
2157 PurpleProxyInfo
*wgpi
;
2158 if ((wgpi
= purple_win32_proxy_get_info()) != NULL
)
2161 /* no proxy environment variable found, don't use a proxy */
2162 purple_debug_info("proxy", "No environment settings found, not using a proxy\n");
2163 gpi
= tmp_none_proxy_info
;
2171 PurpleProxyConnectData
*
2172 purple_proxy_connect(void *handle
, PurpleAccount
*account
,
2173 const char *host
, int port
,
2174 PurpleProxyConnectFunction connect_cb
, gpointer data
)
2176 const char *connecthost
= host
;
2177 int connectport
= port
;
2178 PurpleProxyConnectData
*connect_data
;
2180 g_return_val_if_fail(host
!= NULL
, NULL
);
2181 g_return_val_if_fail(port
> 0, NULL
);
2182 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2184 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2185 connect_data
->fd
= -1;
2186 connect_data
->handle
= handle
;
2187 connect_data
->connect_cb
= connect_cb
;
2188 connect_data
->data
= data
;
2189 connect_data
->host
= g_strdup(host
);
2190 connect_data
->port
= port
;
2191 connect_data
->gpi
= purple_proxy_get_setup(account
);
2193 if ((purple_proxy_info_get_type(connect_data
->gpi
) != PURPLE_PROXY_NONE
) &&
2194 (purple_proxy_info_get_host(connect_data
->gpi
) == NULL
||
2195 purple_proxy_info_get_port(connect_data
->gpi
) <= 0)) {
2197 purple_notify_error(NULL
, NULL
, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2198 purple_proxy_connect_data_destroy(connect_data
);
2202 switch (purple_proxy_info_get_type(connect_data
->gpi
))
2204 case PURPLE_PROXY_NONE
:
2207 case PURPLE_PROXY_HTTP
:
2208 case PURPLE_PROXY_SOCKS4
:
2209 case PURPLE_PROXY_SOCKS5
:
2210 case PURPLE_PROXY_USE_ENVVAR
:
2211 connecthost
= purple_proxy_info_get_host(connect_data
->gpi
);
2212 connectport
= purple_proxy_info_get_port(connect_data
->gpi
);
2216 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2217 purple_proxy_info_get_type(connect_data
->gpi
));
2218 purple_proxy_connect_data_destroy(connect_data
);
2222 connect_data
->query_data
= purple_dnsquery_a(connecthost
,
2223 connectport
, connection_host_resolved
, connect_data
);
2224 if (connect_data
->query_data
== NULL
)
2226 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
2227 purple_proxy_connect_data_destroy(connect_data
);
2231 handles
= g_slist_prepend(handles
, connect_data
);
2233 return connect_data
;
2237 * Combine some of this code with purple_proxy_connect()
2239 PurpleProxyConnectData
*
2240 purple_proxy_connect_socks5(void *handle
, PurpleProxyInfo
*gpi
,
2241 const char *host
, int port
,
2242 PurpleProxyConnectFunction connect_cb
,
2245 PurpleProxyConnectData
*connect_data
;
2247 g_return_val_if_fail(host
!= NULL
, NULL
);
2248 g_return_val_if_fail(port
>= 0, NULL
);
2249 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2251 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2252 connect_data
->fd
= -1;
2253 connect_data
->handle
= handle
;
2254 connect_data
->connect_cb
= connect_cb
;
2255 connect_data
->data
= data
;
2256 connect_data
->host
= g_strdup(host
);
2257 connect_data
->port
= port
;
2258 connect_data
->gpi
= gpi
;
2260 connect_data
->query_data
=
2261 purple_dnsquery_a(purple_proxy_info_get_host(gpi
),
2262 purple_proxy_info_get_port(gpi
),
2263 connection_host_resolved
, connect_data
);
2264 if (connect_data
->query_data
== NULL
)
2266 purple_proxy_connect_data_destroy(connect_data
);
2270 handles
= g_slist_prepend(handles
, connect_data
);
2272 return connect_data
;
2276 purple_proxy_connect_cancel(PurpleProxyConnectData
*connect_data
)
2278 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
2279 purple_proxy_connect_data_destroy(connect_data
);
2283 purple_proxy_connect_cancel_with_handle(void *handle
)
2287 for (l
= handles
; l
!= NULL
; l
= l_next
) {
2288 PurpleProxyConnectData
*connect_data
= l
->data
;
2292 if (connect_data
->handle
== handle
)
2293 purple_proxy_connect_cancel(connect_data
);
2298 proxy_pref_cb(const char *name
, PurplePrefType type
,
2299 gconstpointer value
, gpointer data
)
2301 PurpleProxyInfo
*info
= purple_global_proxy_get_info();
2303 if (!strcmp(name
, "/purple/proxy/type")) {
2305 const char *type
= value
;
2307 if (!strcmp(type
, "none"))
2308 proxytype
= PURPLE_PROXY_NONE
;
2309 else if (!strcmp(type
, "http"))
2310 proxytype
= PURPLE_PROXY_HTTP
;
2311 else if (!strcmp(type
, "socks4"))
2312 proxytype
= PURPLE_PROXY_SOCKS4
;
2313 else if (!strcmp(type
, "socks5"))
2314 proxytype
= PURPLE_PROXY_SOCKS5
;
2315 else if (!strcmp(type
, "envvar"))
2316 proxytype
= PURPLE_PROXY_USE_ENVVAR
;
2320 purple_proxy_info_set_type(info
, proxytype
);
2321 } else if (!strcmp(name
, "/purple/proxy/host"))
2322 purple_proxy_info_set_host(info
, value
);
2323 else if (!strcmp(name
, "/purple/proxy/port"))
2324 purple_proxy_info_set_port(info
, GPOINTER_TO_INT(value
));
2325 else if (!strcmp(name
, "/purple/proxy/username"))
2326 purple_proxy_info_set_username(info
, value
);
2327 else if (!strcmp(name
, "/purple/proxy/password"))
2328 purple_proxy_info_set_password(info
, value
);
2332 purple_proxy_get_handle()
2340 purple_proxy_init(void)
2344 /* Initialize a default proxy info struct. */
2345 global_proxy_info
= purple_proxy_info_new();
2348 purple_prefs_add_none("/purple/proxy");
2349 purple_prefs_add_string("/purple/proxy/type", "none");
2350 purple_prefs_add_string("/purple/proxy/host", "");
2351 purple_prefs_add_int("/purple/proxy/port", 0);
2352 purple_prefs_add_string("/purple/proxy/username", "");
2353 purple_prefs_add_string("/purple/proxy/password", "");
2354 purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE
);
2356 /* Setup callbacks for the preferences. */
2357 handle
= purple_proxy_get_handle();
2358 purple_prefs_connect_callback(handle
, "/purple/proxy/type", proxy_pref_cb
,
2360 purple_prefs_connect_callback(handle
, "/purple/proxy/host", proxy_pref_cb
,
2362 purple_prefs_connect_callback(handle
, "/purple/proxy/port", proxy_pref_cb
,
2364 purple_prefs_connect_callback(handle
, "/purple/proxy/username",
2365 proxy_pref_cb
, NULL
);
2366 purple_prefs_connect_callback(handle
, "/purple/proxy/password",
2367 proxy_pref_cb
, NULL
);
2369 /* Load the initial proxy settings */
2370 purple_prefs_trigger_callback("/purple/proxy/type");
2371 purple_prefs_trigger_callback("/purple/proxy/host");
2372 purple_prefs_trigger_callback("/purple/proxy/port");
2373 purple_prefs_trigger_callback("/purple/proxy/username");
2374 purple_prefs_trigger_callback("/purple/proxy/password");
2378 purple_proxy_uninit(void)
2380 while (handles
!= NULL
)
2382 purple_proxy_connect_data_disconnect(handles
->data
, NULL
);
2383 purple_proxy_connect_data_destroy(handles
->data
);