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