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