Fix crashes when filenames end up being NULL in some prpls.
[pidgin-git.git] / libpurple / proxy.c
blob03d798e631eddf8e8c503b9e6886155bdbcf6e3b
1 /**
2 * @file proxy.c Proxy API
3 * @ingroup core
4 */
6 /* purple
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 */
33 #include "internal.h"
34 #include "cipher.h"
35 #include "debug.h"
36 #include "dnsquery.h"
37 #include "notify.h"
38 #include "ntlm.h"
39 #include "prefs.h"
40 #include "proxy.h"
41 #include "util.h"
43 struct _PurpleProxyConnectData {
44 void *handle;
45 PurpleProxyConnectFunction connect_cb;
46 gpointer data;
47 gchar *host;
48 int port;
49 int fd;
50 guint inpa;
51 PurpleProxyInfo *gpi;
52 PurpleDnsQueryData *query_data;
54 /**
55 * This contains alternating length/char* values. The char*
56 * values need to be freed when removed from the linked list.
58 GSList *hosts;
61 * All of the following variables are used when establishing a
62 * connection through a proxy.
64 guchar *write_buffer;
65 gsize write_buf_len;
66 gsize written_len;
67 PurpleInputFunction read_cb;
68 guchar *read_buffer;
69 gsize read_buf_len;
70 gsize read_len;
73 static const char * const socks5errors[] = {
74 "succeeded\n",
75 "general SOCKS server failure\n",
76 "connection not allowed by ruleset\n",
77 "Network unreachable\n",
78 "Host unreachable\n",
79 "Connection refused\n",
80 "TTL expired\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 /**************************************************************************
98 * Proxy structure API
99 **************************************************************************/
100 PurpleProxyInfo *
101 purple_proxy_info_new(void)
103 return g_new0(PurpleProxyInfo, 1);
106 void
107 purple_proxy_info_destroy(PurpleProxyInfo *info)
109 g_return_if_fail(info != NULL);
111 g_free(info->host);
112 g_free(info->username);
113 g_free(info->password);
115 g_free(info);
118 void
119 purple_proxy_info_set_type(PurpleProxyInfo *info, PurpleProxyType type)
121 g_return_if_fail(info != NULL);
123 info->type = type;
126 void
127 purple_proxy_info_set_host(PurpleProxyInfo *info, const char *host)
129 g_return_if_fail(info != NULL);
131 g_free(info->host);
132 info->host = g_strdup(host);
135 void
136 purple_proxy_info_set_port(PurpleProxyInfo *info, int port)
138 g_return_if_fail(info != NULL);
140 info->port = port;
143 void
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);
152 void
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);
161 PurpleProxyType
162 purple_proxy_info_get_type(const PurpleProxyInfo *info)
164 g_return_val_if_fail(info != NULL, PURPLE_PROXY_NONE);
166 return info->type;
169 const char *
170 purple_proxy_info_get_host(const PurpleProxyInfo *info)
172 g_return_val_if_fail(info != NULL, NULL);
174 return info->host;
178 purple_proxy_info_get_port(const PurpleProxyInfo *info)
180 g_return_val_if_fail(info != NULL, 0);
182 return info->port;
185 const char *
186 purple_proxy_info_get_username(const PurpleProxyInfo *info)
188 g_return_val_if_fail(info != NULL, NULL);
190 return info->username;
193 const char *
194 purple_proxy_info_get_password(const PurpleProxyInfo *info)
196 g_return_val_if_fail(info != NULL, NULL);
198 return info->password;
201 /**************************************************************************
202 * Global Proxy API
203 **************************************************************************/
204 PurpleProxyInfo *
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");
218 if (tmp == NULL)
219 return purple_global_proxy_get_info();
221 g_free(tmp);
222 tmp = NULL;
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();
228 g_free(err);
229 err = NULL;
231 if (!strcmp(tmp, "none\n")) {
232 info.type = PURPLE_PROXY_NONE;
233 g_free(tmp);
234 return &info;
237 if (strcmp(tmp, "manual\n")) {
238 /* Unknown setting. Fallback to using our global proxy settings. */
239 g_free(tmp);
240 return purple_global_proxy_get_info();
243 g_free(tmp);
244 tmp = NULL;
246 /* Free the old fields */
247 if (info.host) {
248 g_free(info.host);
249 info.host = NULL;
251 if (info.username) {
252 g_free(info.username);
253 info.username = NULL;
255 if (info.password) {
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();
263 g_free(err);
264 err = NULL;
266 if (!strcmp(tmp, "true\n"))
267 use_same_proxy = TRUE;
268 g_free(tmp);
269 tmp = NULL;
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();
275 g_free(err);
276 err = NULL;
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))
287 g_free(info.host);
288 info.host = NULL;
289 return purple_global_proxy_get_info();
291 g_free(err);
292 info.port = atoi(tmp);
293 g_free(tmp);
294 } else {
295 g_free(info.host);
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();
299 g_free(err);
300 err = NULL;
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");
311 g_free(info.host);
312 info.host = NULL;
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))
319 g_free(info.host);
320 info.host = NULL;
321 return purple_global_proxy_get_info();
323 g_free(err);
324 err = NULL;
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))
330 g_free(info.host);
331 info.host = NULL;
332 g_free(info.username);
333 info.username = NULL;
334 return purple_global_proxy_get_info();
336 g_free(err);
337 err = NULL;
338 g_strchomp(info.password);
340 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/port",
341 &tmp, &err, NULL, NULL))
343 g_free(info.host);
344 info.host = 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();
351 g_free(err);
352 info.port = atoi(tmp);
353 g_free(tmp);
356 return &info;
359 #ifdef _WIN32
361 typedef BOOL (CALLBACK* LPFNWINHTTPGETIEPROXYCONFIG)(/*IN OUT*/ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* pProxyConfig);
363 /* This modifies "host" in-place evilly */
364 static void
365 _proxy_fill_hostinfo(PurpleProxyInfo *info, char *host, int default_port)
367 int port = default_port;
368 char *d;
370 d = g_strrstr(host, ":");
371 if (d)
372 *d = '\0';
373 d++;
374 if (*d)
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;
390 if (!loaded) {
391 loaded = TRUE;
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)
399 return NULL;
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());
404 return NULL;
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)*/
413 return NULL;
415 } else if (ie_proxy_config.lpszProxy) {
416 gchar *proxy_list = g_utf16_to_utf8(ie_proxy_config.lpszProxy, -1,
417 NULL, NULL, NULL);
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) {
429 if (specific) {
430 specific += strlen("http=");
431 tmp = strchr(specific, ';');
432 if (tmp)
433 *tmp = '\0';
434 /* specific now points the proxy server (and port) */
435 } else
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, ';');
452 if (tmp)
453 *tmp = '\0';
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));
466 } else {
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 */
477 g_free(proxy_list);
478 } else {
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);
490 return &info;
492 #endif
495 /**************************************************************************
496 * Proxy API
497 **************************************************************************/
500 * Whoever calls this needs to have called
501 * purple_proxy_connect_data_disconnect() beforehand.
503 static void
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.
541 static void
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",
565 error_message);
566 if (connect_data->hosts != NULL)
567 try_connect(connect_data);
568 else
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.
581 static void
582 purple_proxy_connect_data_disconnect_formatted(PurpleProxyConnectData *connect_data, const char *format, ...)
584 va_list args;
585 gchar *tmp;
587 va_start(args, format);
588 tmp = g_strdup_vprintf(format, args);
589 va_end(args);
591 purple_proxy_connect_data_disconnect(connect_data, tmp);
592 g_free(tmp);
595 static void
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);
611 static void
612 socket_ready_cb(gpointer data, gint source, PurpleInputCondition cond)
614 PurpleProxyConnectData *connect_data = data;
615 int error = 0;
616 int ret;
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))
622 return;
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");
644 return;
647 if (ret != 0 || error != 0) {
648 if (ret != 0)
649 error = errno;
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));
654 return;
657 purple_proxy_connect_data_connected(connect_data);
660 static gboolean
661 clean_connect(gpointer data)
663 purple_proxy_connect_data_connected(data);
665 return FALSE;
668 static void
669 proxy_connect_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
671 int flags;
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));
681 return;
684 flags = fcntl(connect_data->fd, F_GETFL);
685 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
686 #ifndef _WIN32
687 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
688 #endif
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);
698 else
700 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
703 else
706 * The connection happened IMMEDIATELY... strange, but whatever.
708 int error = ETIMEDOUT;
709 int ret;
711 purple_debug_info("proxy", "Connected immediately.\n");
713 ret = purple_input_get_error(connect_data->fd, &error);
714 if ((ret != 0) || (error != 0))
716 if (ret != 0)
717 error = errno;
718 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
719 return;
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.
736 static void
737 proxy_do_write(gpointer data, gint source, PurpleInputCondition cond)
739 PurpleProxyConnectData *connect_data;
740 const guchar *request;
741 gsize request_len;
742 int ret;
744 connect_data = data;
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);
749 if (ret <= 0)
751 if (errno == EAGAIN)
752 /* No worries */
753 return;
755 /* Error! */
756 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
757 return;
759 if (ret < request_len) {
760 connect_data->written_len += ret;
761 return;
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.
779 static void
780 http_canread(gpointer data, gint source, PurpleInputCondition cond)
782 int len, headers_len, status = 0;
783 gboolean error;
784 PurpleProxyConnectData *connect_data = data;
785 char *p;
786 gsize max_read;
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);
799 if (len == 0) {
800 purple_proxy_connect_data_disconnect(connect_data,
801 _("Server closed the connection."));
802 return;
805 if (len < 0) {
806 if (errno == EAGAIN)
807 /* No worries */
808 return;
810 /* Error! */
811 purple_proxy_connect_data_disconnect_formatted(connect_data,
812 _("Lost connection with server:\n%s"), g_strerror(errno));
813 return;
816 connect_data->read_len += len;
817 p[len] = '\0';
819 p = g_strstr_len((const gchar *)connect_data->read_buffer,
820 connect_data->read_len, "\r\n\r\n");
821 if (p != NULL) {
822 *p = '\0';
823 headers_len = (p - (char *)connect_data->read_buffer) + 4;
824 } else if(len == max_read)
825 headers_len = len;
826 else
827 return;
829 error = strncmp((const char *)connect_data->read_buffer, "HTTP/", 5) != 0;
830 if (!error) {
831 int major;
832 p = (char *)connect_data->read_buffer + 5;
833 major = strtol(p, &p, 10);
834 error = (major == 0) || (*p != '.');
835 if(!error) {
836 int minor;
837 p++;
838 minor = strtol(p, &p, 10);
839 error = (*p != ' ');
840 if(!error) {
841 p++;
842 status = strtol(p, &p, 10);
843 error = (*p != ' ');
848 /* Read the contents */
849 p = g_strrstr((const gchar *)connect_data->read_buffer, "Content-Length: ");
850 if (p != NULL) {
851 gchar *tmp;
852 int len = 0;
853 char tmpc;
854 p += strlen("Content-Length: ");
855 tmp = strchr(p, '\r');
856 if(tmp)
857 *tmp = '\0';
858 len = atoi(p);
859 if(tmp)
860 *tmp = '\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 */
866 while (len--) {
867 /* TODO: deal with EAGAIN (and other errors) better */
868 if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN)
869 break;
873 if (error) {
874 purple_proxy_connect_data_disconnect_formatted(connect_data,
875 _("Unable to parse response from HTTP proxy: %s\n"),
876 connect_data->read_buffer);
877 return;
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 */) {
885 const char *header;
886 gchar *request;
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];
894 gchar *response;
895 int ret;
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");
904 if (domain != NULL)
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);
909 return;
911 *username = '\0';
913 /* Is there a message? */
914 if (*header_end == ' ') {
915 /* Check for Type-2 */
916 char *tmp = (char*) header;
917 guint8 *nonce;
919 header_end++;
920 username++;
921 while(*tmp != '\r' && *tmp != '\0') tmp++;
922 *tmp = '\0';
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),
926 hostname,
927 domain, nonce, NULL);
928 username--;
929 } else /* Empty message */
930 response = purple_ntlm_gen_type1(hostname, domain);
932 *username = '\\';
934 request = g_strdup_printf(
935 "CONNECT %s:%d HTTP/1.1\r\n"
936 "Host: %s:%d\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,
941 response);
943 g_free(response);
945 } else if((header = g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: Basic"))) {
946 gchar *t1, *t2;
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));
956 g_free(t1);
958 request = g_strdup_printf(
959 "CONNECT %s:%d HTTP/1.1\r\n"
960 "Host: %s:%d\r\n"
961 "Proxy-Authorization: Basic %s\r\n",
962 connect_data->host, connect_data->port,
963 connect_data->host, connect_data->port,
964 t2);
966 g_free(t2);
968 } else {
969 purple_proxy_connect_data_disconnect_formatted(connect_data,
970 _("HTTP proxy connection error %d"), status);
971 return;
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);
989 return;
992 if (status == 403) {
993 /* Forbidden */
994 purple_proxy_connect_data_disconnect_formatted(connect_data,
995 _("Access denied: HTTP proxy server forbids port %d tunneling."),
996 connect_data->port);
997 } else {
998 purple_proxy_connect_data_disconnect_formatted(connect_data,
999 _("HTTP proxy connection error %d"), status);
1001 } else {
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);
1008 return;
1012 static void
1013 http_start_connect_tunneling(PurpleProxyConnectData *connect_data) {
1014 GString *request;
1015 int ret;
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;
1029 char hostname[256];
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));
1043 g_free(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",
1051 t2, ntlm_type1);
1052 g_free(ntlm_type1);
1053 g_free(t2);
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);
1068 static void
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) {
1083 if (ret != 0)
1084 error = errno;
1085 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
1086 return;
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);
1100 } else {
1101 http_start_connect_tunneling(connect_data);
1106 static void
1107 proxy_connect_http(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
1109 int flags;
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));
1122 return;
1125 flags = fcntl(connect_data->fd, F_GETFL);
1126 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
1127 #ifndef _WIN32
1128 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
1129 #endif
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);
1137 } else
1138 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1139 } else {
1140 purple_debug_info("proxy", "Connected immediately.\n");
1142 http_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1146 static void
1147 s4_canread(gpointer data, gint source, PurpleInputCondition cond)
1149 PurpleProxyConnectData *connect_data = data;
1150 guchar *buf;
1151 int len, max_read;
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))
1168 return;
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);
1172 return;
1176 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1179 static void
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);
1190 return;
1193 if (hosts == NULL) {
1194 purple_proxy_connect_data_disconnect_formatted(connect_data,
1195 _("Error resolving %s"), connect_data->host);
1196 return;
1199 /* Discard the length... */
1200 hosts = g_slist_delete_link(hosts, hosts);
1201 addr = hosts->data;
1202 hosts = g_slist_delete_link(hosts, hosts);
1204 packet[0] = 0x04;
1205 packet[1] = 0x01;
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);
1209 packet[8] = 0x00;
1211 g_free(addr);
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);
1232 static void
1233 s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
1235 PurpleProxyConnectData *connect_data = data;
1236 int error = ETIMEDOUT;
1237 int ret;
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)) {
1248 if (ret != 0)
1249 error = errno;
1250 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
1251 return;
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];
1262 int len;
1264 purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
1266 packet[0] = 0x04;
1267 packet[1] = 0x01;
1268 packet[2] = connect_data->port >> 8;
1269 packet[3] = connect_data->port & 0xff;
1270 packet[4] = 0x00;
1271 packet[5] = 0x00;
1272 packet[6] = 0x00;
1273 packet[7] = 0x01;
1274 packet[8] = 0x00;
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);
1288 } else {
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);
1299 static void
1300 proxy_connect_socks4(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
1302 int flags;
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));
1315 return;
1318 flags = fcntl(connect_data->fd, F_GETFL);
1319 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
1320 #ifndef _WIN32
1321 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
1322 #endif
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);
1332 else
1334 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1337 else
1339 purple_debug_info("proxy", "Connected immediately.\n");
1341 s4_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1345 static gboolean
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);
1356 return FALSE;
1359 return TRUE;
1362 static void
1363 s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
1365 guchar *dest, *buf;
1366 PurpleProxyConnectData *connect_data = data;
1367 int len;
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));
1380 if (len == 0)
1382 purple_proxy_connect_data_disconnect(connect_data,
1383 _("Server closed the connection."));
1384 return;
1387 if (len < 0)
1389 if (errno == EAGAIN)
1390 /* No worries */
1391 return;
1393 /* Error! */
1394 purple_proxy_connect_data_disconnect_formatted(connect_data,
1395 _("Lost connection with server:\n%s"), g_strerror(errno));
1396 return;
1399 connect_data->read_len += len;
1401 if(connect_data->read_len < 4)
1402 return;
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]]);
1409 } else {
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."));
1414 return;
1417 /* Skip past BND.ADDR */
1418 switch(buf[3]) {
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))
1421 return;
1422 buf += 4 + 4;
1423 break;
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))
1428 return;
1429 buf += 4;
1430 if(!s5_ensure_buffer_length(connect_data, 4 + 1 + buf[0]))
1431 return;
1432 buf += buf[0] + 1;
1433 break;
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))
1436 return;
1437 buf += 4 + 16;
1438 break;
1439 default:
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."));
1443 return;
1446 /* Skip past BND.PORT */
1447 if(!s5_ensure_buffer_length(connect_data, (buf - connect_data->read_buffer) + 2))
1448 return;
1450 purple_proxy_connect_data_connected(connect_data);
1453 static void
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);
1477 static void
1478 s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
1480 PurpleProxyConnectData *connect_data = data;
1481 int len;
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);
1494 if (len == 0)
1496 purple_proxy_connect_data_disconnect(connect_data,
1497 _("Server closed the connection."));
1498 return;
1501 if (len < 0)
1503 if (errno == EAGAIN)
1504 /* No worries */
1505 return;
1507 /* Error! */
1508 purple_proxy_connect_data_disconnect_formatted(connect_data,
1509 _("Lost connection with server:\n%s"), g_strerror(errno));
1510 return;
1513 connect_data->read_len += len;
1514 if (connect_data->read_len < 2)
1515 return;
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."));
1523 return;
1526 g_free(connect_data->read_buffer);
1527 connect_data->read_buffer = NULL;
1529 s5_sendconnect(connect_data, connect_data->fd);
1532 static void
1533 hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response)
1535 PurpleCipher *cipher;
1536 PurpleCipherContext *ctx;
1537 int i;
1538 unsigned char Kxoripad[65];
1539 unsigned char Kxoropad[65];
1540 size_t pwlen;
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);
1549 if (pwlen>64) {
1550 purple_cipher_context_append(ctx, (const guchar *)passwd, strlen(passwd));
1551 purple_cipher_context_digest(ctx, sizeof(Kxoripad), Kxoripad, NULL);
1552 pwlen=16;
1553 } else {
1554 memcpy(Kxoripad, passwd, pwlen);
1556 memcpy(Kxoropad,Kxoripad,pwlen);
1558 for (i=0;i<64;i++) {
1559 Kxoripad[i]^=0x36;
1560 Kxoropad[i]^=0x5c;
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);
1576 static void
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
1583 static gssize
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."));
1594 return -1;
1596 cmdbuf++;
1598 navas = *cmdbuf;
1600 purple_debug_misc("socks5 proxy", "Expecting %d attribute(s).\n", navas);
1602 cmdbuf++;
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);
1619 return -1;
1622 buf = cmdbuf + 2;
1624 if (cmdbuf[1] == 0) {
1625 purple_debug_error("socks5 proxy", "Attribute %x Value length of 0; ignoring.\n", cmdbuf[0]);
1626 cmdbuf = buf;
1627 continue;
1630 switch (cmdbuf[0]) {
1631 case 0x00:
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;
1639 /* Success */
1640 s5_sendconnect(connect_data, connect_data->fd);
1641 } else {
1642 /* Failure */
1643 purple_debug_warning("proxy",
1644 "socks5 CHAP authentication "
1645 "failed. Disconnecting...");
1646 purple_proxy_connect_data_disconnect(connect_data,
1647 _("Authentication failed"));
1649 return -1;
1650 case 0x01:
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);
1653 break;
1654 case 0x03:
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);
1681 return -1;
1682 case 0x11:
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."));
1695 return -1;
1697 break;
1698 default:
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);
1707 static void
1708 s5_readchap(gpointer data, gint source, PurpleInputCondition cond)
1710 gssize msg_ret;
1711 PurpleProxyConnectData *connect_data = data;
1712 int len;
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);
1731 if (len == 0) {
1732 purple_proxy_connect_data_disconnect(connect_data,
1733 _("Server closed the connection."));
1734 return;
1737 if (len < 0) {
1738 if (errno == EAGAIN)
1739 /* No worries */
1740 return;
1742 /* Error! */
1743 purple_proxy_connect_data_disconnect_formatted(connect_data,
1744 _("Lost connection with server:\n%s"), g_strerror(errno));
1745 return;
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 */
1751 while (1) {
1753 /* We need more to be able to read this message */
1754 if (connect_data->read_len < 2)
1755 return;
1757 msg_ret = s5_parse_chap_msg(connect_data);
1759 if (msg_ret < 0)
1760 return;
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 */
1771 continue;
1774 break;
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;
1788 static void
1789 s5_canread(gpointer data, gint source, PurpleInputCondition cond)
1791 PurpleProxyConnectData *connect_data = data;
1792 int len;
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);
1805 if (len == 0)
1807 purple_proxy_connect_data_disconnect(connect_data,
1808 _("Server closed the connection."));
1809 return;
1812 if (len < 0)
1814 if (errno == EAGAIN)
1815 /* No worries */
1816 return;
1818 /* Error! */
1819 purple_proxy_connect_data_disconnect_formatted(connect_data,
1820 _("Lost connection with server:\n%s"), g_strerror(errno));
1821 return;
1824 connect_data->read_len += len;
1825 if (connect_data->read_len < 2)
1826 return;
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."));
1834 return;
1837 if (connect_data->read_buffer[1] == 0x02) {
1838 size_t i, j;
1839 const char *u, *p;
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;
1853 if (u != NULL)
1854 memcpy(connect_data->write_buffer + 2, u, i);
1855 connect_data->write_buffer[2 + i] = j;
1856 if (p != NULL)
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);
1869 return;
1870 } else if (connect_data->read_buffer[1] == 0x03) {
1871 size_t userlen;
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);
1898 return;
1899 } else {
1900 g_free(connect_data->read_buffer);
1901 connect_data->read_buffer = NULL;
1903 s5_sendconnect(connect_data, connect_data->fd);
1907 static void
1908 s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
1910 unsigned char buf[5];
1911 int i;
1912 PurpleProxyConnectData *connect_data = data;
1913 int error = ETIMEDOUT;
1914 int ret;
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))
1927 if (ret != 0)
1928 error = errno;
1929 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
1930 return;
1933 i = 0;
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 */
1941 i = 5;
1943 else {
1944 buf[1] = 0x01;
1945 buf[2] = 0x00;
1946 i = 3;
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);
1959 static void
1960 proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
1962 int flags;
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));
1975 return;
1978 flags = fcntl(connect_data->fd, F_GETFL);
1979 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
1980 #ifndef _WIN32
1981 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
1982 #endif
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);
1992 else
1994 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1997 else
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
2014 #endif
2016 static void try_connect(PurpleProxyConnectData *connect_data)
2018 socklen_t addrlen;
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));
2029 #else
2030 memcpy(ipaddr, inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
2031 sizeof(ipaddr));
2032 #endif
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);
2038 break;
2040 case PURPLE_PROXY_HTTP:
2041 proxy_connect_http(connect_data, addr, addrlen);
2042 break;
2044 case PURPLE_PROXY_SOCKS4:
2045 proxy_connect_socks4(connect_data, addr, addrlen);
2046 break;
2048 case PURPLE_PROXY_SOCKS5:
2049 proxy_connect_socks5(connect_data, addr, addrlen);
2050 break;
2052 case PURPLE_PROXY_USE_ENVVAR:
2053 proxy_connect_http(connect_data, addr, addrlen);
2054 break;
2056 default:
2057 break;
2060 g_free(addr);
2063 static void
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);
2075 return;
2078 if (hosts == NULL)
2080 purple_proxy_connect_data_disconnect(connect_data, _("Could not resolve host name"));
2081 return;
2084 connect_data->hosts = hosts;
2086 try_connect(connect_data);
2089 PurpleProxyInfo *
2090 purple_proxy_get_setup(PurpleAccount *account)
2092 PurpleProxyInfo *gpi = NULL;
2093 const gchar *tmp;
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)
2105 gpi = NULL;
2107 if (gpi == NULL) {
2108 if (purple_running_gnome())
2109 gpi = purple_gnome_proxy_get_info();
2110 else
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;
2119 int proxyport;
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);
2126 g_free(proxyhost);
2128 purple_proxy_info_set_username(gpi, proxyuser);
2129 g_free(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);
2155 } else {
2156 #ifdef _WIN32
2157 PurpleProxyInfo *wgpi;
2158 if ((wgpi = purple_win32_proxy_get_info()) != NULL)
2159 return wgpi;
2160 #endif
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;
2168 return gpi;
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);
2199 return NULL;
2202 switch (purple_proxy_info_get_type(connect_data->gpi))
2204 case PURPLE_PROXY_NONE:
2205 break;
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);
2213 break;
2215 default:
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);
2219 return NULL;
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);
2228 return NULL;
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,
2243 gpointer data)
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);
2267 return NULL;
2270 handles = g_slist_prepend(handles, connect_data);
2272 return connect_data;
2275 void
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);
2282 void
2283 purple_proxy_connect_cancel_with_handle(void *handle)
2285 GSList *l, *l_next;
2287 for (l = handles; l != NULL; l = l_next) {
2288 PurpleProxyConnectData *connect_data = l->data;
2290 l_next = l->next;
2292 if (connect_data->handle == handle)
2293 purple_proxy_connect_cancel(connect_data);
2297 static void
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")) {
2304 int proxytype;
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;
2317 else
2318 proxytype = -1;
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);
2331 void *
2332 purple_proxy_get_handle()
2334 static int handle;
2336 return &handle;
2339 void
2340 purple_proxy_init(void)
2342 void *handle;
2344 /* Initialize a default proxy info struct. */
2345 global_proxy_info = purple_proxy_info_new();
2347 /* Proxy */
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,
2359 NULL);
2360 purple_prefs_connect_callback(handle, "/purple/proxy/host", proxy_pref_cb,
2361 NULL);
2362 purple_prefs_connect_callback(handle, "/purple/proxy/port", proxy_pref_cb,
2363 NULL);
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");
2377 void
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);