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