1 /*
2  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
3  *
4  * This library is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authors: Jeffrey Stedfast <fejj@ximian.com>
17  *          Veerapuram Varadhan <vvaradhan@novell.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include <string.h>
23 #include <stdlib.h>
24 
25 #ifdef _WIN32
26 #include <winsock2.h>
27 #include <ws2tcpip.h>
28 #ifndef IN6_ARE_ADDR_EQUAL
29 #define IN6_ARE_ADDR_EQUAL(a, b) \
30     (memcmp ((gpointer)(a), (gpointer)(b), sizeof (struct in6_addr)) == 0)
31 #endif
32 #else
33 #include <netinet/in.h>
34 #include <sys/socket.h>
35 #endif
36 
37 #include <libsoup/soup-address.h>
38 #include <libsoup/soup-uri.h>
39 #include "e-proxy.h"
40 
41 /* Debug */
42 #define d(x)
43 
44 enum ProxyType {
45 	PROXY_TYPE_SYSTEM = 0,
46 	PROXY_TYPE_NO_PROXY,
47 	PROXY_TYPE_MANUAL,
48 	PROXY_TYPE_AUTO_URL /* no auto-proxy at the moment */
49 };
50 
51 typedef enum {
52 	E_PROXY_KEY_MODE,
53 	E_PROXY_KEY_USE_HTTP_PROXY,
54 	E_PROXY_KEY_HTTP_HOST,
55 	E_PROXY_KEY_HTTP_PORT,
56 	E_PROXY_KEY_HTTP_USE_AUTH,
57 	E_PROXY_KEY_HTTP_AUTH_USER,
58 	E_PROXY_KEY_HTTP_AUTH_PWD,
59 	E_PROXY_KEY_HTTP_IGNORE_HOSTS,
60 	E_PROXY_KEY_HTTPS_HOST,
61 	E_PROXY_KEY_HTTPS_PORT,
62 	E_PROXY_KEY_SOCKS_HOST,
63 	E_PROXY_KEY_SOCKS_PORT,
64 	E_PROXY_KEY_AUTOCONFIG_URL
65 } EProxyKey;
66 
67 struct _EProxyPrivate {
68 	SoupURI *uri_http, *uri_https, *uri_socks;
69 	GSList * ign_hosts;	/* List of hostnames. (Strings)		*/
70 	GSList * ign_addrs;	/* List of hostaddrs. (ProxyHostAddrs)	*/
71 	gboolean use_proxy;	/* Is our-proxy enabled? */
72 	enum ProxyType type;
73 	GSettings *evolution_proxy_settings;
74 	GSettings *proxy_settings;
75 	GSettings *proxy_http_settings;
76 	GSettings *proxy_https_settings;
77 	GSettings *proxy_socks_settings;
78 };
79 
80 /* Enum definition is copied from gnome-vfs/modules/http-proxy.c */
81 typedef enum {
82 	PROXY_IPV4 = 4,
83 	PROXY_IPV6 = 6
84 } ProxyAddrType;
85 
86 typedef struct {
87 	ProxyAddrType type;	/* Specifies whether IPV4 or IPV6 */
88 	gpointer  addr;		/* Either in_addr * or in6_addr * */
89 	gpointer  mask;		/* Either in_addr * or in6_addr * */
90 } ProxyHostAddr;
91 
92 /* Signals.  */
93 enum {
94 	CHANGED,
95 	LAST_SIGNAL
96 };
97 
98 G_DEFINE_TYPE_WITH_PRIVATE (EProxy, e_proxy, G_TYPE_OBJECT)
99 
100 static guint signals[LAST_SIGNAL] = { 0 };
101 
102 /* Forward declarations.  */
103 
104 static void	ipv6_network_addr	(const struct in6_addr *addr,
105 					 const struct in6_addr *mask,
106 					 struct in6_addr *res);
107 
108 static void
ep_free_proxy_host_addr(ProxyHostAddr * host)109 ep_free_proxy_host_addr (ProxyHostAddr *host)
110 {
111 	if (host) {
112 		g_clear_pointer (&host->addr, g_free);
113 		g_clear_pointer (&host->mask, g_free);
114 		g_slice_free (ProxyHostAddr, host);
115 	}
116 }
117 
118 static gboolean
ep_read_key_boolean(EProxy * proxy,EProxyKey key)119 ep_read_key_boolean (EProxy *proxy,
120                      EProxyKey key)
121 {
122 	gboolean res = FALSE;
123 
124 	g_return_val_if_fail (E_IS_PROXY (proxy), FALSE);
125 
126 	switch (key) {
127 	case E_PROXY_KEY_USE_HTTP_PROXY:
128 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
129 			/* it's not used in the UI, thus behave like always set to TRUE */
130 			res = TRUE; /* g_settings_get_boolean (proxy->priv->proxy_http_settings, "enabled"); */
131 		else
132 			res = g_settings_get_boolean (proxy->priv->evolution_proxy_settings, "use-http-proxy");
133 		break;
134 	case E_PROXY_KEY_HTTP_USE_AUTH:
135 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
136 			res = g_settings_get_boolean (proxy->priv->proxy_http_settings, "use-authentication");
137 		else
138 			res = g_settings_get_boolean (proxy->priv->evolution_proxy_settings, "use-authentication");
139 		break;
140 	default:
141 		g_warn_if_reached ();
142 		break;
143 	}
144 
145 	return res;
146 }
147 
148 static gint
ep_read_key_int(EProxy * proxy,EProxyKey key)149 ep_read_key_int (EProxy *proxy,
150                  EProxyKey key)
151 {
152 	gint res = 0;
153 
154 	g_return_val_if_fail (E_IS_PROXY (proxy), 0);
155 
156 	switch (key) {
157 	case E_PROXY_KEY_HTTP_PORT:
158 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
159 			res = g_settings_get_int (proxy->priv->proxy_http_settings, "port");
160 		else
161 			res = g_settings_get_int (proxy->priv->evolution_proxy_settings, "http-port");
162 		break;
163 	case E_PROXY_KEY_HTTPS_PORT:
164 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
165 			res = g_settings_get_int (proxy->priv->proxy_https_settings, "port");
166 		else
167 			res = g_settings_get_int (proxy->priv->evolution_proxy_settings, "secure-port");
168 		break;
169 	case E_PROXY_KEY_SOCKS_PORT:
170 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
171 			res = g_settings_get_int (proxy->priv->proxy_socks_settings, "port");
172 		else
173 			res = g_settings_get_int (proxy->priv->evolution_proxy_settings, "socks-port");
174 		break;
175 	default:
176 		g_warn_if_reached ();
177 		break;
178 	}
179 
180 	return res;
181 }
182 
183 /* free returned pointer with g_free() */
184 static gchar *
ep_read_key_string(EProxy * proxy,EProxyKey key)185 ep_read_key_string (EProxy *proxy,
186                     EProxyKey key)
187 {
188 	gchar *res = NULL;
189 
190 	g_return_val_if_fail (E_IS_PROXY (proxy), NULL);
191 
192 	switch (key) {
193 	case E_PROXY_KEY_MODE:
194 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
195 			res = g_settings_get_string (proxy->priv->proxy_settings, "mode");
196 		else
197 			g_warn_if_reached ();
198 		break;
199 	case E_PROXY_KEY_HTTP_HOST:
200 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
201 			res = g_settings_get_string (proxy->priv->proxy_http_settings, "host");
202 		else
203 			res = g_settings_get_string (proxy->priv->evolution_proxy_settings, "http-host");
204 		break;
205 	case E_PROXY_KEY_HTTPS_HOST:
206 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
207 			res = g_settings_get_string (proxy->priv->proxy_https_settings, "host");
208 		else
209 			res = g_settings_get_string (proxy->priv->evolution_proxy_settings, "secure-host");
210 		break;
211 	case E_PROXY_KEY_SOCKS_HOST:
212 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
213 			res = g_settings_get_string (proxy->priv->proxy_socks_settings, "host");
214 		else
215 			res = g_settings_get_string (proxy->priv->evolution_proxy_settings, "socks-host");
216 		break;
217 	case E_PROXY_KEY_HTTP_AUTH_USER:
218 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
219 			res = g_settings_get_string (proxy->priv->proxy_http_settings, "authentication-user");
220 		else
221 			res = g_settings_get_string (proxy->priv->evolution_proxy_settings, "authentication-user");
222 		break;
223 	case E_PROXY_KEY_HTTP_AUTH_PWD:
224 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
225 			res = g_settings_get_string (proxy->priv->proxy_http_settings, "authentication-password");
226 		else
227 			res = g_settings_get_string (proxy->priv->evolution_proxy_settings, "authentication-password");
228 		break;
229 	case E_PROXY_KEY_AUTOCONFIG_URL:
230 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
231 			res = g_settings_get_string (proxy->priv->proxy_settings, "autoconfig-url");
232 		else
233 			res = g_settings_get_string (proxy->priv->evolution_proxy_settings, "autoconfig-url");
234 		break;
235 	default:
236 		g_warn_if_reached ();
237 		break;
238 	}
239 
240 	return res;
241 }
242 
243 /* list of newly allocated strings, use g_free() for each member and free list itself too */
244 static GSList *
ep_read_key_list(EProxy * proxy,EProxyKey key)245 ep_read_key_list (EProxy *proxy,
246                   EProxyKey key)
247 {
248 	GSList *res = NULL;
249 	gchar **strv = NULL;
250 
251 	g_return_val_if_fail (E_IS_PROXY (proxy), NULL);
252 
253 	switch (key) {
254 	case E_PROXY_KEY_HTTP_IGNORE_HOSTS:
255 		if (proxy->priv->type == PROXY_TYPE_SYSTEM)
256 			strv = g_settings_get_strv (proxy->priv->proxy_settings, "ignore-hosts");
257 		else
258 			strv = g_settings_get_strv (proxy->priv->evolution_proxy_settings, "ignore-hosts");
259 		break;
260 	default:
261 		g_warn_if_reached ();
262 		break;
263 	}
264 
265 	if (strv) {
266 		gint ii;
267 
268 		for (ii = 0; strv && strv[ii]; ii++) {
269 			res = g_slist_prepend (res, g_strdup (strv[ii]));
270 		}
271 
272 		g_strfreev (strv);
273 
274 		res = g_slist_reverse (res);
275 	}
276 
277 	return res;
278 }
279 
280 static gboolean
ep_is_in_ignored(EProxy * proxy,const gchar * host)281 ep_is_in_ignored (EProxy *proxy,
282                   const gchar *host)
283 {
284 	EProxyPrivate *priv;
285 	GSList * l;
286 	gchar *hn;
287 
288 	g_return_val_if_fail (proxy != NULL, FALSE);
289 	g_return_val_if_fail (host != NULL, FALSE);
290 
291 	priv = proxy->priv;
292 	if (!priv->ign_hosts)
293 		return FALSE;
294 
295 	hn = g_ascii_strdown (host, -1);
296 
297 	for (l = priv->ign_hosts; l; l = l->next) {
298 		if (*((gchar *) l->data) == '*') {
299 			if (g_str_has_suffix (hn, ((gchar *) l->data) + 1)) {
300 				g_free (hn);
301 				return TRUE;
302 			}
303 		} else if (strcmp (hn, l->data) == 0) {
304 				g_free (hn);
305 				return TRUE;
306 		}
307 	}
308 	g_free (hn);
309 
310 	return FALSE;
311 }
312 
313 static gboolean
ep_need_proxy_http(EProxy * proxy,const gchar * host)314 ep_need_proxy_http (EProxy *proxy,
315                     const gchar *host)
316 {
317 	SoupAddress *addr = NULL;
318 	EProxyPrivate *priv = proxy->priv;
319 	ProxyHostAddr *p_addr = NULL;
320 	GSList *l;
321 	guint status;
322 
323 	/* check for ignored first */
324 	if (ep_is_in_ignored (proxy, host))
325 		return FALSE;
326 
327 	addr = soup_address_new (host, 0);
328 	status = soup_address_resolve_sync (addr, NULL);
329 	if (status == SOUP_STATUS_OK) {
330 		gint addr_len;
331 		struct sockaddr * so_addr = NULL;
332 
333 		so_addr = soup_address_get_sockaddr (addr, &addr_len);
334 
335 		/* This will never happen, since we have already called
336 		 * soup_address_resolve_sync ().
337 		*/
338 		if (!so_addr) {
339 			g_object_unref (addr);
340 			return TRUE;
341 		}
342 
343 		if (so_addr->sa_family == AF_INET) {
344 			struct in_addr in, *mask, *addr_in;
345 
346 			in = ((struct sockaddr_in *) so_addr)->sin_addr;
347 			for (l = priv->ign_addrs; l; l = l->next) {
348 				p_addr = (ProxyHostAddr *) l->data;
349 				if (p_addr->type == PROXY_IPV4) {
350 					addr_in = ((struct in_addr *) p_addr->addr);
351 					mask = ((struct in_addr *) p_addr->mask);
352 					if ((in.s_addr & mask->s_addr) == addr_in->s_addr) {
353 						d (g_print ("Host [%s] doesn't require proxy\n", host));
354 						g_object_unref (addr);
355 						return FALSE;
356 					}
357 				}
358 			}
359 		} else {
360 			struct in6_addr in6, net6;
361 			struct in_addr *addr_in, *mask;
362 
363 			in6 = ((struct sockaddr_in6 *) so_addr)->sin6_addr;
364 			for (l = priv->ign_addrs; l; l = l->next) {
365 				p_addr = (ProxyHostAddr *) l->data;
366 				ipv6_network_addr (&in6, (struct in6_addr *) p_addr->mask, &net6);
367 				if (p_addr->type == PROXY_IPV6) {
368 					if (IN6_ARE_ADDR_EQUAL (&net6, (struct in6_addr *) p_addr->addr)) {
369 						d (g_print ("Host [%s] doesn't require proxy\n", host));
370 						g_object_unref (addr);
371 						return FALSE;
372 					}
373 				} else if (p_addr->type == PROXY_IPV6 &&
374 					   IN6_IS_ADDR_V4MAPPED (&net6)) {
375 					guint32 v4addr;
376 
377 					addr_in = ((struct in_addr *) p_addr->addr);
378 					mask = ((struct in_addr *) p_addr->mask);
379 
380 					v4addr = net6.s6_addr[12] << 24
381 						| net6.s6_addr[13] << 16
382 						| net6.s6_addr[14] << 8
383 						| net6.s6_addr[15];
384 					if ((v4addr & mask->s_addr) != addr_in->s_addr) {
385 						d (g_print ("Host [%s] doesn't require proxy\n", host));
386 						g_object_unref (addr);
387 						return FALSE;
388 					}
389 				}
390 			}
391 		}
392 	}
393 
394 	d (g_print ("%s needs a proxy to connect to internet\n", host));
395 	g_object_unref (addr);
396 
397 	return TRUE;
398 }
399 
400 static gboolean
ep_need_proxy_https(EProxy * proxy,const gchar * host)401 ep_need_proxy_https (EProxy *proxy,
402                      const gchar *host)
403 {
404 	/* Can we share ignore list from HTTP at all? */
405 	return !ep_is_in_ignored (proxy, host);
406 }
407 
408 static gboolean
ep_need_proxy_socks(EProxy * proxy,const gchar * host)409 ep_need_proxy_socks (EProxy *proxy,
410                      const gchar *host)
411 {
412 	/* Can we share ignore list from HTTP at all? */
413 	return !ep_is_in_ignored (proxy, host);
414 }
415 
416 /* Apply a prefix-notation @netmask to the given @addr_in, as described in
417  * http://tools.ietf.org/html/rfc4632#section-3.1 */
418 static gboolean
ep_manipulate_ipv4(ProxyHostAddr * host_addr,struct in_addr * addr_in,gchar * netmask)419 ep_manipulate_ipv4 (ProxyHostAddr *host_addr,
420                     struct in_addr *addr_in,
421                     gchar *netmask)
422 {
423 	gboolean has_error = FALSE;
424 	struct in_addr *addr, *mask;
425 
426 	if (!addr_in)
427 		return has_error;
428 
429 	host_addr->type = PROXY_IPV4;
430 	addr = g_new0 (struct in_addr, 1);
431 	memcpy (addr, addr_in, sizeof (struct in_addr));
432 	mask = g_new0 (struct in_addr, 1);
433 
434 	if (netmask) {
435 		gchar *endptr;
436 		gint width = strtol (netmask, &endptr, 10);
437 
438 		if (*endptr != '\0' || width < 0 || width > 32) {
439 			has_error = TRUE;
440 			mask->s_addr = 0xFFFFFFFF;
441 		} else if (width == 32) {
442 			mask->s_addr = 0;
443 			addr->s_addr = 0;
444 		} else {
445 			mask->s_addr = htonl (~0U << width);
446 			addr->s_addr &= mask->s_addr;
447 		}
448 	} else {
449 		mask->s_addr = 0xFFFFFFFF;
450 	}
451 
452 	host_addr->addr = addr;
453 	host_addr->mask = mask;
454 
455 	return has_error;
456 }
457 
458 static void
ipv6_network_addr(const struct in6_addr * addr,const struct in6_addr * mask,struct in6_addr * res)459 ipv6_network_addr (const struct in6_addr *addr,
460                    const struct in6_addr *mask,
461                    struct in6_addr *res)
462 {
463 	gint i;
464 
465 	for (i = 0; i < 16; ++i) {
466 		res->s6_addr[i] = addr->s6_addr[i] & mask->s6_addr[i];
467 	}
468 }
469 
470 static gboolean
ep_manipulate_ipv6(ProxyHostAddr * host_addr,struct in6_addr * addr_in6,gchar * netmask)471 ep_manipulate_ipv6 (ProxyHostAddr *host_addr,
472                     struct in6_addr *addr_in6,
473                     gchar *netmask)
474 {
475 	gboolean has_error = FALSE;
476 	struct in6_addr *addr, *mask;
477 	gint i;
478 
479 	if (!addr_in6)
480 		return has_error;
481 
482 	host_addr->type = PROXY_IPV6;
483 
484 	addr = g_new0 (struct in6_addr, 1);
485 	mask = g_new0 (struct in6_addr, 1);
486 
487 	for (i = 0; i < 16; ++i) {
488 		addr->s6_addr[i] = addr_in6->s6_addr[i];
489 	}
490 	if (netmask) {
491 		gchar *endptr;
492 		gint width = strtol (netmask, &endptr, 10);
493 
494 		if (*endptr != '\0' || width < 0 || width > 128) {
495 			has_error = TRUE;
496 		}
497 		for (i = 0; i < 16; ++i) {
498 			mask->s6_addr[i] = 0;
499 		}
500 		for (i = 0; i < width / 8; i++) {
501 			mask->s6_addr[i] = 0xff;
502 		}
503 		mask->s6_addr[i] = (0xff << (8 - width % 8)) & 0xff;
504 		ipv6_network_addr (addr, mask, addr);
505 	} else {
506 		for (i = 0; i < 16; ++i) {
507 			mask->s6_addr[i] = 0xff;
508 		}
509 	}
510 
511 	host_addr->addr = addr;
512 	host_addr->mask = mask;
513 
514 	return has_error;
515 }
516 
517 static void
ep_parse_ignore_host(gpointer data,gpointer user_data)518 ep_parse_ignore_host (gpointer data,
519                       gpointer user_data)
520 {
521 	EProxy * proxy = (EProxy *) user_data;
522 	EProxyPrivate * priv = NULL;
523 	SoupAddress *addr;
524 	guint status;
525 	gchar *input, *netmask, *hostname;
526 	gboolean has_error = FALSE;
527 
528 	if (!proxy || !proxy->priv)
529 		return;
530 
531 	priv = proxy->priv;
532 	input = (gchar *) data;
533 
534 	if ((netmask = strrchr (input, '/')) != NULL) {
535 		hostname = g_strndup (input, netmask - input);
536 		++netmask;
537 	} else {
538 		hostname = g_ascii_strdown (input, -1);
539 	}
540 
541 	addr = soup_address_new (hostname, 0);
542 	status = soup_address_resolve_sync (addr, NULL);
543 	if (status == SOUP_STATUS_OK) {
544 		ProxyHostAddr *host_addr;
545 		gint addr_len;
546 		struct sockaddr * so_addr = NULL;
547 
548 		host_addr = g_slice_new0 (ProxyHostAddr);
549 
550 		so_addr = soup_address_get_sockaddr (addr, &addr_len);
551 
552 		/* This will never happen, since we have already called
553 		 * soup_address_resolve_sync ().
554 		*/
555 		if (!so_addr) {
556 			ep_free_proxy_host_addr (host_addr);
557 			goto error;
558 		}
559 
560 		if (so_addr->sa_family == AF_INET)
561 			has_error = ep_manipulate_ipv4 (
562 				host_addr,
563 				&((struct sockaddr_in *) so_addr)->sin_addr,
564 				netmask);
565 		else
566 			has_error = ep_manipulate_ipv6 (
567 				host_addr,
568 				&((struct sockaddr_in6 *) so_addr)->sin6_addr,
569 				netmask);
570 
571 		if (!has_error) {
572 			priv->ign_addrs = g_slist_append (
573 				priv->ign_addrs, host_addr);
574 			priv->ign_hosts = g_slist_append (
575 				priv->ign_hosts, hostname);
576 		} else {
577 			ep_free_proxy_host_addr (host_addr);
578 			g_free (hostname);
579 		}
580 	} else {
581 		d (g_print ("Unable to resolve %s\n", hostname));
582 		priv->ign_hosts = g_slist_append (priv->ign_hosts, hostname);
583 	}
584  error:
585 	g_object_unref (addr);
586 }
587 
588 static gboolean
ep_change_uri(SoupURI ** soup_uri,const gchar * uri)589 ep_change_uri (SoupURI **soup_uri,
590                const gchar *uri)
591 {
592 	gboolean changed = FALSE;
593 
594 	g_return_val_if_fail (soup_uri != NULL, FALSE);
595 
596 	if (!uri || !*uri) {
597 		if (*soup_uri) {
598 			soup_uri_free (*soup_uri);
599 			*soup_uri = NULL;
600 			changed = TRUE;
601 		}
602 	} else if (*soup_uri) {
603 		gchar *old = soup_uri_to_string (*soup_uri, FALSE);
604 
605 		if (old && *old) {
606 			gint len = strlen (old);
607 
608 			/* remove ending slash, if there */
609 			if (old[len - 1] == '/')
610 				old[len - 1] = 0;
611 		}
612 
613 		changed = old && uri && g_ascii_strcasecmp (old, uri) != 0;
614 		if (changed) {
615 			soup_uri_free (*soup_uri);
616 			*soup_uri = soup_uri_new (uri);
617 		}
618 
619 		g_free (old);
620 	} else {
621 		*soup_uri = soup_uri_new (uri);
622 		changed = TRUE;
623 	}
624 
625 	return changed;
626 }
627 
628 static gchar *
update_proxy_uri(const gchar * uri,const gchar * proxy_user,const gchar * proxy_pw)629 update_proxy_uri (const gchar *uri,
630                   const gchar *proxy_user,
631                   const gchar *proxy_pw)
632 {
633 	gchar *res, *user = NULL, *pw = NULL;
634 	gboolean is_https;
635 
636 	g_return_val_if_fail (uri != NULL, NULL);
637 
638 	if (proxy_user && *proxy_user) {
639 		user = soup_uri_encode (proxy_user, ":/;#@?\\");
640 		if (proxy_pw)
641 			pw = soup_uri_encode (proxy_pw, ":/;#@?\\");
642 	}
643 
644 	if (!user)
645 		return g_strdup (uri);
646 
647 	/*  here can be only http or https and nothing else */
648 	is_https = g_str_has_prefix (uri, "https://");
649 
650 	res = g_strdup_printf (
651 		"%s://%s%s%s@%s",
652 		is_https ? "https" : "http",
653 		user,
654 		pw ? ":" : "",
655 		pw ? pw : "",
656 		uri + strlen ("http://") + (is_https ? 1 : 0));
657 
658 	g_free (user);
659 	g_free (pw);
660 
661 	return res;
662 }
663 
664 static void
ep_set_proxy(EProxy * proxy,gboolean regen_ign_host_list)665 ep_set_proxy (EProxy *proxy,
666               gboolean regen_ign_host_list)
667 {
668 	gchar *proxy_server, *uri_http = NULL, *uri_https = NULL, *uri_socks = NULL;
669 	gint proxy_port, old_type;
670 	EProxyPrivate * priv = proxy->priv;
671 	GSList *ignore;
672 	gboolean changed = FALSE, sys_manual = TRUE;
673 
674 	old_type = priv->type;
675 	priv->type = g_settings_get_int (priv->evolution_proxy_settings, "proxy-type");
676 	if (priv->type > PROXY_TYPE_AUTO_URL)
677 		priv->type = PROXY_TYPE_SYSTEM;
678 	changed = priv->type != old_type;
679 
680 	if (priv->type == PROXY_TYPE_SYSTEM) {
681 		gchar *mode = ep_read_key_string (proxy, E_PROXY_KEY_MODE);
682 
683 		/* supporting only manual system proxy setting */
684 		sys_manual = mode && g_str_equal (mode, "manual");
685 
686 		g_free (mode);
687 	}
688 
689 	priv->use_proxy = ep_read_key_boolean (proxy, E_PROXY_KEY_USE_HTTP_PROXY);
690 	if (!priv->use_proxy || priv->type == PROXY_TYPE_NO_PROXY || !sys_manual) {
691 		changed = ep_change_uri (&priv->uri_http, NULL) || changed;
692 		changed = ep_change_uri (&priv->uri_https, NULL) || changed;
693 		changed = ep_change_uri (&priv->uri_socks, NULL) || changed;
694 		goto emit_signal;
695 	}
696 
697 	proxy_server = ep_read_key_string (proxy, E_PROXY_KEY_HTTP_HOST);
698 	proxy_port = ep_read_key_int (proxy, E_PROXY_KEY_HTTP_PORT);
699 	if (proxy_server != NULL && *proxy_server && !g_ascii_isspace (*proxy_server)) {
700 		if (proxy_port > 0)
701 			uri_http = g_strdup_printf ("http://%s:%d", proxy_server, proxy_port);
702 		else
703 			uri_http = g_strdup_printf ("http://%s", proxy_server);
704 	} else
705 		uri_http = NULL;
706 	g_free (proxy_server);
707 	d (g_print ("ep_set_proxy: uri_http: %s\n", uri_http));
708 
709 	proxy_server = ep_read_key_string (proxy, E_PROXY_KEY_HTTPS_HOST);
710 	proxy_port = ep_read_key_int (proxy, E_PROXY_KEY_HTTPS_PORT);
711 	if (proxy_server != NULL && *proxy_server && !g_ascii_isspace (*proxy_server)) {
712 		if (proxy_port > 0)
713 			uri_https = g_strdup_printf ("https://%s:%d", proxy_server, proxy_port);
714 		else
715 			uri_https = g_strdup_printf ("https://%s", proxy_server);
716 	} else
717 		uri_https = NULL;
718 	g_free (proxy_server);
719 	d (g_print ("ep_set_proxy: uri_https: %s\n", uri_https));
720 
721 	proxy_server = ep_read_key_string (proxy, E_PROXY_KEY_SOCKS_HOST);
722 	proxy_port = ep_read_key_int (proxy, E_PROXY_KEY_SOCKS_PORT);
723 	if (proxy_server != NULL && *proxy_server && !g_ascii_isspace (*proxy_server)) {
724 		if (proxy_port > 0)
725 			uri_socks = g_strdup_printf ("socks://%s:%d", proxy_server, proxy_port);
726 		else
727 			uri_socks = g_strdup_printf ("socks://%s", proxy_server);
728 	} else
729 		uri_socks = NULL;
730 	g_free (proxy_server);
731 	d (g_print ("ep_set_proxy: uri_socks: %s\n", uri_socks));
732 
733 	if (regen_ign_host_list) {
734 		if (priv->ign_hosts) {
735 			g_slist_foreach (priv->ign_hosts, (GFunc) g_free, NULL);
736 			g_slist_free (priv->ign_hosts);
737 			priv->ign_hosts = NULL;
738 		}
739 
740 		if (priv->ign_addrs) {
741 			g_slist_foreach (priv->ign_addrs, (GFunc) ep_free_proxy_host_addr, NULL);
742 			g_slist_free (priv->ign_addrs);
743 			priv->ign_addrs = NULL;
744 		}
745 
746 		ignore = ep_read_key_list (proxy, E_PROXY_KEY_HTTP_IGNORE_HOSTS);
747 		if (ignore) {
748 			g_slist_foreach (ignore, (GFunc) ep_parse_ignore_host, proxy);
749 			g_slist_foreach (ignore, (GFunc) g_free, NULL);
750 			g_slist_free (ignore);
751 		}
752 	}
753 
754 	if (ep_read_key_boolean (proxy, E_PROXY_KEY_HTTP_USE_AUTH)) {
755 		gchar *proxy_user, *proxy_pw, *tmp = NULL, *tmps = NULL;
756 
757 		proxy_user = ep_read_key_string (proxy, E_PROXY_KEY_HTTP_AUTH_USER);
758 		proxy_pw = ep_read_key_string (proxy, E_PROXY_KEY_HTTP_AUTH_PWD);
759 
760 		if (uri_http && proxy_user && *proxy_user) {
761 			tmp = uri_http;
762 			uri_http = update_proxy_uri (uri_http, proxy_user, proxy_pw);
763 		}
764 
765 		if (uri_https && proxy_user && *proxy_user) {
766 			tmps = uri_https;
767 			uri_https = update_proxy_uri (uri_https, proxy_user, proxy_pw);
768 		}
769 
770 		g_free (proxy_user);
771 		g_free (proxy_pw);
772 		g_free (tmp);
773 		g_free (tmps);
774 	}
775 
776 	changed = ep_change_uri (&priv->uri_http, uri_http) || changed;
777 	changed = ep_change_uri (&priv->uri_https, uri_https) || changed;
778 	changed = ep_change_uri (&priv->uri_socks, uri_socks) || changed;
779 
780  emit_signal:
781 	d (g_print (
782 		"%s: changed:%d "
783 		"uri_http: %s; "
784 		"uri_https: %s; "
785 		"uri_socks: %s\n",
786 		G_STRFUNC, changed ? 1 : 0,
787 		uri_http ? uri_http : "[null]",
788 		uri_https ? uri_https : "[null]",
789 		uri_socks ? uri_socks : "[null]"));
790 	if (changed)
791 		g_signal_emit (proxy, signals[CHANGED], 0);
792 
793 	g_free (uri_http);
794 	g_free (uri_https);
795 	g_free (uri_socks);
796 }
797 
798 static void
ep_evo_proxy_changed_cb(GSettings * settings,const gchar * key,EProxy * proxy)799 ep_evo_proxy_changed_cb (GSettings *settings,
800                          const gchar *key,
801                          EProxy *proxy)
802 {
803 	EProxyPrivate *priv;
804 
805 	g_return_if_fail (E_IS_PROXY (proxy));
806 
807 	priv = proxy->priv;
808 
809 	d (g_print ("%s: proxy settings changed, key '%s'\n", G_STRFUNC, key ? key : "NULL"));
810 	if (g_strcmp0 (key, "proxy-type") == 0) {
811 		ep_set_proxy (proxy, TRUE);
812 	} else if (priv->type == PROXY_TYPE_SYSTEM) {
813 		return;
814 	}
815 
816 	ep_set_proxy (proxy, g_strcmp0 (key, "ignore-hosts") == 0);
817 }
818 
819 static void
ep_sys_proxy_changed_cb(GSettings * settings,const gchar * key,EProxy * proxy)820 ep_sys_proxy_changed_cb (GSettings *settings,
821                          const gchar *key,
822                          EProxy *proxy)
823 {
824 	g_return_if_fail (proxy != NULL);
825 
826 	if (proxy->priv->type != PROXY_TYPE_SYSTEM)
827 		return;
828 
829 	ep_set_proxy (proxy, g_strcmp0 (key, "ignore-hosts") == 0);
830 }
831 
832 static void
ep_sys_proxy_http_changed_cb(GSettings * settings,const gchar * key,EProxy * proxy)833 ep_sys_proxy_http_changed_cb (GSettings *settings,
834                               const gchar *key,
835                               EProxy *proxy)
836 {
837 	g_return_if_fail (proxy != NULL);
838 
839 	if (proxy->priv->type != PROXY_TYPE_SYSTEM)
840 		return;
841 
842 	ep_set_proxy (proxy, FALSE);
843 }
844 
845 static void
ep_sys_proxy_https_changed_cb(GSettings * settings,const gchar * key,EProxy * proxy)846 ep_sys_proxy_https_changed_cb (GSettings *settings,
847                                const gchar *key,
848                                EProxy *proxy)
849 {
850 	g_return_if_fail (proxy != NULL);
851 
852 	if (proxy->priv->type != PROXY_TYPE_SYSTEM)
853 		return;
854 
855 	ep_set_proxy (proxy, FALSE);
856 }
857 
858 static void
ep_sys_proxy_socks_changed_cb(GSettings * settings,const gchar * key,EProxy * proxy)859 ep_sys_proxy_socks_changed_cb (GSettings *settings,
860                                const gchar *key,
861                                EProxy *proxy)
862 {
863 	g_return_if_fail (proxy != NULL);
864 
865 	if (proxy->priv->type != PROXY_TYPE_SYSTEM)
866 		return;
867 
868 	ep_set_proxy (proxy, FALSE);
869 }
870 
871 static void
e_proxy_dispose(GObject * object)872 e_proxy_dispose (GObject *object)
873 {
874 	EProxy *proxy;
875 	EProxyPrivate *priv;
876 
877 	proxy = E_PROXY (object);
878 	priv = proxy->priv;
879 
880 	if (priv->evolution_proxy_settings) {
881 		g_signal_handlers_disconnect_by_func (priv->evolution_proxy_settings, ep_evo_proxy_changed_cb, proxy);
882 		g_object_unref (priv->evolution_proxy_settings);
883 		priv->evolution_proxy_settings = NULL;
884 	}
885 
886 	if (priv->proxy_settings) {
887 		g_signal_handlers_disconnect_by_func (priv->proxy_settings, ep_sys_proxy_changed_cb, proxy);
888 		g_object_unref (priv->proxy_settings);
889 		priv->proxy_settings = NULL;
890 	}
891 
892 	if (priv->proxy_http_settings) {
893 		g_signal_handlers_disconnect_by_func (priv->proxy_http_settings, ep_sys_proxy_http_changed_cb, proxy);
894 		g_object_unref (priv->proxy_http_settings);
895 		priv->proxy_http_settings = NULL;
896 	}
897 
898 	if (priv->proxy_https_settings) {
899 		g_signal_handlers_disconnect_by_func (priv->proxy_https_settings, ep_sys_proxy_https_changed_cb, proxy);
900 		g_object_unref (priv->proxy_https_settings);
901 		priv->proxy_https_settings = NULL;
902 	}
903 
904 	if (priv->proxy_socks_settings) {
905 		g_signal_handlers_disconnect_by_func (priv->proxy_socks_settings, ep_sys_proxy_socks_changed_cb, proxy);
906 		g_object_unref (priv->proxy_socks_settings);
907 		priv->proxy_socks_settings = NULL;
908 	}
909 
910 	if (priv->uri_http)
911 		soup_uri_free (priv->uri_http);
912 
913 	if (priv->uri_https)
914 		soup_uri_free (priv->uri_https);
915 
916 	if (priv->uri_socks)
917 		soup_uri_free (priv->uri_socks);
918 
919 	g_slist_foreach (priv->ign_hosts, (GFunc) g_free, NULL);
920 	g_slist_free (priv->ign_hosts);
921 
922 	g_slist_foreach (priv->ign_addrs, (GFunc) ep_free_proxy_host_addr, NULL);
923 	g_slist_free (priv->ign_addrs);
924 
925 	/* Chain up to parent's dispose() method. */
926 	G_OBJECT_CLASS (e_proxy_parent_class)->dispose (object);
927 }
928 
929 static void
e_proxy_class_init(EProxyClass * class)930 e_proxy_class_init (EProxyClass *class)
931 {
932 	GObjectClass *object_class;
933 
934 	object_class = G_OBJECT_CLASS (class);
935 	object_class->dispose = e_proxy_dispose;
936 
937 	/**
938 	 * EProxy::changed:
939 	 * @proxy: the #EProxy which emitted the signal
940 	 *
941 	 * Emitted when proxy settings changes.
942 	 **/
943 	signals[CHANGED] = g_signal_new (
944 		"changed",
945 		G_OBJECT_CLASS_TYPE (object_class),
946 		G_SIGNAL_RUN_FIRST,
947 		G_STRUCT_OFFSET (EProxyClass, changed),
948 		NULL, NULL, NULL,
949 		G_TYPE_NONE, 0);
950 
951 }
952 
953 static void
e_proxy_init(EProxy * proxy)954 e_proxy_init (EProxy *proxy)
955 {
956 	proxy->priv = e_proxy_get_instance_private (proxy);
957 
958 	proxy->priv->type = PROXY_TYPE_SYSTEM;
959 
960 	proxy->priv->evolution_proxy_settings = g_settings_new ("org.gnome.evolution.shell.network-config");
961 	proxy->priv->proxy_settings = g_settings_new ("org.gnome.system.proxy");
962 	proxy->priv->proxy_http_settings = g_settings_get_child (proxy->priv->proxy_settings, "http");
963 	proxy->priv->proxy_https_settings = g_settings_get_child (proxy->priv->proxy_settings, "https");
964 	proxy->priv->proxy_socks_settings = g_settings_get_child (proxy->priv->proxy_settings, "socks");
965 
966 	g_signal_connect (proxy->priv->evolution_proxy_settings, "changed", G_CALLBACK (ep_evo_proxy_changed_cb), proxy);
967 	g_signal_connect (proxy->priv->proxy_settings, "changed", G_CALLBACK (ep_sys_proxy_changed_cb), proxy);
968 	g_signal_connect (proxy->priv->proxy_http_settings, "changed", G_CALLBACK (ep_sys_proxy_http_changed_cb), proxy);
969 	g_signal_connect (proxy->priv->proxy_https_settings, "changed", G_CALLBACK (ep_sys_proxy_https_changed_cb), proxy);
970 	g_signal_connect (proxy->priv->proxy_socks_settings, "changed", G_CALLBACK (ep_sys_proxy_socks_changed_cb), proxy);
971 }
972 
973 /**
974  * e_proxy_new:
975  *
976  * Returns: (transfer full): a new instance of an #EProxy
977  *
978  * Since: 2.24
979  **/
980 EProxy *
e_proxy_new(void)981 e_proxy_new (void)
982 {
983 	return g_object_new (E_TYPE_PROXY, NULL);
984 }
985 
986 /**
987  * e_proxy_setup_proxy:
988  * @proxy: an #EProxy
989  *
990  * Sets up internal structure members and reads the proxy settings.
991  *
992  * Since: 2.24
993  **/
994 void
e_proxy_setup_proxy(EProxy * proxy)995 e_proxy_setup_proxy (EProxy *proxy)
996 {
997 	g_return_if_fail (E_IS_PROXY (proxy));
998 
999 	/* We get the evolution-shell proxy keys here
1000 	 * set soup up to use the proxy,
1001 	 * and listen to any changes */
1002 
1003 	/* XXX Why can't we do this automatically in constructed() ? */
1004 
1005 	ep_set_proxy (proxy, TRUE);
1006 }
1007 
1008 /**
1009  * e_proxy_peek_uri_for:
1010  * @proxy: an #EProxy
1011  * @uri: a URI
1012  *
1013  * Returns: (transfer none): A proxy URI (as a #SoupURI) which the given @uri
1014  *   may use, based on its scheme
1015  *
1016  * Since: 2.26
1017  **/
1018 SoupURI *
e_proxy_peek_uri_for(EProxy * proxy,const gchar * uri)1019 e_proxy_peek_uri_for (EProxy *proxy,
1020                       const gchar *uri)
1021 {
1022 	SoupURI *res = NULL;
1023 	SoupURI *soup_uri;
1024 
1025 	g_return_val_if_fail (E_IS_PROXY (proxy), NULL);
1026 	g_return_val_if_fail (uri != NULL, NULL);
1027 
1028 	soup_uri = soup_uri_new (uri);
1029 	if (soup_uri == NULL)
1030 		return NULL;
1031 
1032 	if (soup_uri->scheme == SOUP_URI_SCHEME_HTTP)
1033 		res = proxy->priv->uri_http;
1034 	else if (soup_uri->scheme == SOUP_URI_SCHEME_HTTPS)
1035 		res = proxy->priv->uri_https;
1036 	else if (soup_uri->scheme && g_ascii_strcasecmp (soup_uri->scheme, "socks") == 0)
1037 		res = proxy->priv->uri_socks;
1038 
1039 	soup_uri_free (soup_uri);
1040 
1041 	return res;
1042 }
1043 
1044 /**
1045  * e_proxy_require_proxy_for_uri:
1046  * @proxy: an #EProxy
1047  * @uri: a URI
1048  *
1049  * Returns: Whether the @uri requires proxy to connect to it
1050  *
1051  * Since: 2.24
1052  **/
1053 gboolean
e_proxy_require_proxy_for_uri(EProxy * proxy,const gchar * uri)1054 e_proxy_require_proxy_for_uri (EProxy *proxy,
1055                                const gchar *uri)
1056 {
1057 	SoupURI *soup_uri = NULL;
1058 	gboolean need_proxy = FALSE;
1059 
1060 	g_return_val_if_fail (E_IS_PROXY (proxy), FALSE);
1061 	g_return_val_if_fail (uri != NULL, FALSE);
1062 
1063 	if (!proxy->priv->use_proxy || proxy->priv->type == PROXY_TYPE_NO_PROXY) {
1064 		d (g_print ("[%s] don't need a proxy to connect to internet\n", uri));
1065 		return FALSE;
1066 	}
1067 
1068 	soup_uri = soup_uri_new (uri);
1069 	if (soup_uri == NULL)
1070 		return FALSE;
1071 
1072 	if (soup_uri->scheme == SOUP_URI_SCHEME_HTTP)
1073 		need_proxy = ep_need_proxy_http (proxy, soup_uri->host);
1074 	else if (soup_uri->scheme == SOUP_URI_SCHEME_HTTPS)
1075 		need_proxy = ep_need_proxy_https (proxy, soup_uri->host);
1076 	else if (soup_uri->scheme && g_ascii_strcasecmp (soup_uri->scheme, "socks") == 0)
1077 		need_proxy = ep_need_proxy_socks (proxy, soup_uri->host);
1078 
1079 	soup_uri_free (soup_uri);
1080 
1081 	return need_proxy;
1082 }
1083