xdgmime: plug a small leak
[platform/upstream/glib.git] / gio / gproxyaddressenumerator.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2010 Collabora, Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
21  */
22
23 #include "config.h"
24 #include "gproxyaddressenumerator.h"
25
26 #include <string.h>
27
28 #include "gasyncresult.h"
29 #include "ginetaddress.h"
30 #include "glibintl.h"
31 #include "gnetworkaddress.h"
32 #include "gnetworkingprivate.h"
33 #include "gproxy.h"
34 #include "gproxyaddress.h"
35 #include "gproxyresolver.h"
36 #include "gsimpleasyncresult.h"
37 #include "gresolver.h"
38 #include "gsocketaddress.h"
39 #include "gsocketaddressenumerator.h"
40 #include "gsocketconnectable.h"
41
42 G_DEFINE_TYPE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR);
43
44 #define GET_PRIVATE(o) (G_PROXY_ADDRESS_ENUMERATOR (o)->priv)
45
46 enum
47 {
48   PROP_0,
49   PROP_URI,
50   PROP_CONNECTABLE
51 };
52
53 struct _GProxyAddressEnumeratorPrivate
54 {
55   /* Destination address */
56   GSocketConnectable *connectable;
57   gchar              *dest_uri;
58   gchar              *dest_hostname;
59   guint16             dest_port;
60   GList              *dest_ips;
61
62   /* Proxy enumeration */
63   gchar                   **proxies;
64   gchar                   **next_proxy;
65   GSocketAddressEnumerator *addr_enum;
66   GSocketAddress           *proxy_address;
67   const gchar              *proxy_uri;
68   gchar                    *proxy_type;
69   gchar                    *proxy_username;
70   gchar                    *proxy_password;
71   gboolean                  supports_hostname;
72   GList                    *next_dest_ip;
73   GError                   *last_error;
74
75   /* Async attributes */
76   GSimpleAsyncResult *simple;
77   GCancellable       *cancellable;
78 };
79
80 static void
81 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
82                const gchar *proxy)
83 {
84   gchar *userinfo;
85
86   if (priv->proxy_username)
87     {
88       g_free (priv->proxy_username);
89       priv->proxy_username = NULL;
90     }
91
92   if (priv->proxy_password)
93     {
94       g_free (priv->proxy_password);
95       priv->proxy_password = NULL;
96     }
97   
98   if (_g_uri_parse_authority (proxy, NULL, NULL, &userinfo))
99     {
100       if (userinfo)
101         {
102           gchar **split = g_strsplit (userinfo, ":", 2);
103
104           if (split[0] != NULL)
105             {
106               priv->proxy_username = g_uri_unescape_string (split[0], NULL);
107               if (split[1] != NULL)
108                 priv->proxy_password = g_uri_unescape_string (split[1], NULL);
109             }
110
111           g_strfreev (split);
112           g_free (userinfo);
113         }
114     }
115 }
116
117 static void
118 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
119 {
120   if (priv->proxy_address)
121     return;
122
123   while (priv->addr_enum == NULL && *priv->next_proxy)
124     {
125       GSocketConnectable *connectable = NULL;
126       GProxy *proxy;
127
128       priv->proxy_uri = *priv->next_proxy++;
129       g_free (priv->proxy_type);
130       priv->proxy_type = g_uri_parse_scheme (priv->proxy_uri);
131
132       if (priv->proxy_type == NULL)
133         continue;
134
135       /* Assumes hostnames are supported for unknown protocols */
136       priv->supports_hostname = TRUE;
137       proxy = g_proxy_get_default_for_protocol (priv->proxy_type);
138       if (proxy)
139         {
140           priv->supports_hostname = g_proxy_supports_hostname (proxy);
141           g_object_unref (proxy);
142         }
143
144       if (strcmp ("direct", priv->proxy_type) == 0)
145         {
146           if (priv->connectable)
147             connectable = g_object_ref (priv->connectable);
148           else
149             connectable = g_network_address_new (priv->dest_hostname,
150                                                  priv->dest_port);
151         }
152       else
153         {
154           GError *error = NULL;
155
156           connectable = g_network_address_parse_uri (priv->proxy_uri, 0, &error);
157
158           if (error)
159             {
160               g_warning ("Invalid proxy URI '%s': %s",
161                          priv->proxy_uri, error->message);
162               g_error_free (error);
163             }
164
165           save_userinfo (priv, priv->proxy_uri);
166         }
167
168       if (connectable)
169         {
170           priv->addr_enum = g_socket_connectable_enumerate (connectable);
171           g_object_unref (connectable);
172         }
173     }
174 }
175
176 static GSocketAddress *
177 g_proxy_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
178                                  GCancellable              *cancellable,
179                                  GError                   **error)
180 {
181   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
182   GSocketAddress *result = NULL;
183   GError *first_error = NULL;
184
185   if (priv->proxies == NULL)
186     {
187       GProxyResolver *resolver = g_proxy_resolver_get_default ();
188       priv->proxies = g_proxy_resolver_lookup (resolver,
189                                                priv->dest_uri,
190                                                cancellable,
191                                                error);
192       priv->next_proxy = priv->proxies;
193
194       if (priv->proxies == NULL)
195         return NULL;
196     }
197
198   while (result == NULL && (*priv->next_proxy || priv->addr_enum))
199     {
200       gchar *dest_hostname;
201       gchar *dest_protocol;
202       GInetSocketAddress *inetsaddr;
203       GInetAddress *inetaddr;
204       guint16 port;
205
206       next_enumerator (priv);
207
208       if (!priv->addr_enum)
209         continue;
210
211       if (priv->proxy_address == NULL)
212         {
213           priv->proxy_address = g_socket_address_enumerator_next (
214                                     priv->addr_enum,
215                                     cancellable,
216                                     first_error ? NULL : &first_error);
217         }
218
219       if (priv->proxy_address == NULL)
220         {
221           g_object_unref (priv->addr_enum);
222           priv->addr_enum = NULL;
223
224           if (priv->dest_ips)
225             {
226               g_resolver_free_addresses (priv->dest_ips);
227               priv->dest_ips = NULL;
228             }
229
230           continue;
231         }
232
233       if (strcmp ("direct", priv->proxy_type) == 0)
234         {
235           result = priv->proxy_address;
236           priv->proxy_address = NULL;
237           continue;
238         }
239
240       if (!priv->supports_hostname)
241         {
242           GInetAddress *dest_ip;
243
244           if (!priv->dest_ips)
245             {
246               GResolver *resolver;
247
248               resolver = g_resolver_get_default();
249               priv->dest_ips = g_resolver_lookup_by_name (resolver,
250                                                           priv->dest_hostname,
251                                                           cancellable,
252                                                           first_error ? NULL : &first_error);
253               g_object_unref (resolver);
254
255               if (!priv->dest_ips)
256                 {
257                   g_object_unref (priv->proxy_address);
258                   priv->proxy_address = NULL;
259                   continue;
260                 }
261             }
262
263           if (!priv->next_dest_ip)
264             priv->next_dest_ip = priv->dest_ips;
265         
266           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
267           dest_hostname = g_inet_address_to_string (dest_ip);
268
269           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
270         }
271       else
272         {
273           dest_hostname = g_strdup (priv->dest_hostname);
274         }
275       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
276                                   
277       g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address),
278                             NULL);
279
280       inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
281       inetaddr = g_inet_socket_address_get_address (inetsaddr);
282       port = g_inet_socket_address_get_port (inetsaddr);
283
284       result = g_object_new (G_TYPE_PROXY_ADDRESS,
285                              "address", inetaddr,
286                              "port", port,
287                              "protocol", priv->proxy_type,
288                              "destination-protocol", dest_protocol,
289                              "destination-hostname", dest_hostname,
290                              "destination-port", priv->dest_port,
291                              "username", priv->proxy_username,
292                              "password", priv->proxy_password,
293                              "uri", priv->proxy_uri,
294                              NULL);
295       g_free (dest_hostname);
296       g_free (dest_protocol);
297
298       if (priv->supports_hostname || priv->next_dest_ip == NULL)
299         {
300           g_object_unref (priv->proxy_address);
301           priv->proxy_address = NULL;
302         }
303     }
304
305   if (result == NULL && first_error)
306     g_propagate_error (error, first_error);
307   else if (first_error)
308     g_error_free (first_error);
309
310   return result;
311 }
312
313
314
315 static void
316 complete_async (GProxyAddressEnumeratorPrivate *priv)
317 {
318   GSimpleAsyncResult *simple = priv->simple;
319
320   if (priv->cancellable)
321     {
322       g_object_unref (priv->cancellable);
323       priv->cancellable = NULL;
324     }
325
326   priv->simple = NULL;
327
328   if (priv->last_error)
329     {
330       g_simple_async_result_take_error (simple, priv->last_error);
331       priv->last_error = NULL;
332     }
333
334   g_simple_async_result_complete (simple);
335   g_object_unref (simple);
336 }
337
338 static void
339 save_result (GProxyAddressEnumeratorPrivate *priv)
340 {
341   GSocketAddress *result;
342
343   if (strcmp ("direct", priv->proxy_type) == 0)
344     {
345       result = priv->proxy_address;
346       priv->proxy_address = NULL;
347     }
348   else
349     {
350       gchar *dest_hostname, *dest_protocol;
351       GInetSocketAddress *inetsaddr;
352       GInetAddress *inetaddr;
353       guint16 port;
354
355       if (!priv->supports_hostname)
356         {
357           GInetAddress *dest_ip;
358
359           if (!priv->next_dest_ip)
360             priv->next_dest_ip = priv->dest_ips;
361
362           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
363           dest_hostname = g_inet_address_to_string (dest_ip);
364
365           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
366         }
367       else
368         {
369           dest_hostname = g_strdup (priv->dest_hostname);
370         }
371       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
372
373       g_return_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address));
374
375       inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
376       inetaddr = g_inet_socket_address_get_address (inetsaddr);
377       port = g_inet_socket_address_get_port (inetsaddr);
378
379       result = g_object_new (G_TYPE_PROXY_ADDRESS,
380                              "address", inetaddr,
381                              "port", port,
382                              "protocol", priv->proxy_type,
383                              "destination-protocol", dest_protocol,
384                              "destination-hostname", dest_hostname,
385                              "destination-port", priv->dest_port,
386                              "username", priv->proxy_username,
387                              "password", priv->proxy_password,
388                              "uri", priv->proxy_uri,
389                              NULL);
390       g_free (dest_hostname);
391       g_free (dest_protocol);
392
393       if (priv->supports_hostname || priv->next_dest_ip == NULL)
394         {
395           g_object_unref (priv->proxy_address);
396           priv->proxy_address = NULL;
397         }
398     }
399
400   g_simple_async_result_set_op_res_gpointer (priv->simple,
401                                              result,
402                                              g_object_unref);
403 }
404
405 static void address_enumerate_cb (GObject      *object,
406                                   GAsyncResult *result,
407                                   gpointer      user_data);
408
409 static void
410 next_proxy (GProxyAddressEnumeratorPrivate *priv)
411 {
412   if (*priv->next_proxy)
413     {
414       g_object_unref (priv->addr_enum);
415       priv->addr_enum = NULL;
416
417       if (priv->dest_ips)
418         {
419           g_resolver_free_addresses (priv->dest_ips);
420           priv->dest_ips = NULL;
421         }
422
423       next_enumerator (priv);
424
425       if (priv->addr_enum)
426         {
427           g_socket_address_enumerator_next_async (priv->addr_enum,
428                                                   priv->cancellable,
429                                                   address_enumerate_cb,
430                                                   priv);
431           return;
432         }
433     }
434
435   complete_async (priv);
436 }
437
438 static void
439 dest_hostname_lookup_cb (GObject           *object,
440                          GAsyncResult      *result,
441                          gpointer           user_data)
442 {
443   GProxyAddressEnumeratorPrivate *priv = user_data;
444
445   g_clear_error (&priv->last_error);
446   priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
447                                                      result,
448                                                      &priv->last_error);
449   if (priv->dest_ips)
450     {
451       save_result (priv);
452       complete_async (priv);
453     }
454   else
455     {
456       g_clear_object (&priv->proxy_address);
457       next_proxy (priv);
458     }
459 }
460
461 static void
462 address_enumerate_cb (GObject      *object,
463                       GAsyncResult *result,
464                       gpointer      user_data)
465 {
466   GProxyAddressEnumeratorPrivate *priv = user_data;
467
468   g_clear_error (&priv->last_error);
469   priv->proxy_address =
470     g_socket_address_enumerator_next_finish (priv->addr_enum,
471                                              result,
472                                              &priv->last_error);
473   if (priv->proxy_address)
474     {
475       if (!priv->supports_hostname && !priv->dest_ips)
476         {
477           GResolver *resolver;
478           resolver = g_resolver_get_default();
479           g_resolver_lookup_by_name_async (resolver,
480                                            priv->dest_hostname,
481                                            priv->cancellable,
482                                            dest_hostname_lookup_cb,
483                                            priv);
484           g_object_unref (resolver);
485           return;
486         }
487
488       save_result (priv);
489       complete_async (priv);
490     }
491   else
492     next_proxy (priv);
493 }
494
495 static void
496 proxy_lookup_cb (GObject      *object,
497                  GAsyncResult *result,
498                  gpointer      user_data)
499 {
500   GError *error = NULL;
501   GProxyAddressEnumeratorPrivate *priv = user_data;
502   GSimpleAsyncResult *simple = priv->simple;
503
504   priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
505                                                   result,
506                                                   &error);
507   priv->next_proxy = priv->proxies;
508
509   if (error)
510     {
511       g_simple_async_result_take_error (simple, error);
512     }
513   else
514     {
515       next_enumerator (priv);
516       if (priv->addr_enum)
517         {
518           g_socket_address_enumerator_next_async (priv->addr_enum,
519                                                   priv->cancellable,
520                                                   address_enumerate_cb,
521                                                   priv);
522           return;
523         }
524     }
525
526   complete_async (priv); 
527 }
528
529 static void
530 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
531                                        GCancellable             *cancellable,
532                                        GAsyncReadyCallback       callback,
533                                        gpointer                  user_data)
534 {
535   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
536
537   g_return_if_fail (priv->simple == NULL);
538   g_return_if_fail (priv->cancellable == NULL);
539
540   priv->simple = g_simple_async_result_new (G_OBJECT (enumerator),
541                                             callback, user_data,
542                                             g_proxy_address_enumerator_next_async);
543
544   priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
545
546   if (priv->proxies == NULL)
547     {
548       GProxyResolver *resolver = g_proxy_resolver_get_default ();
549       g_proxy_resolver_lookup_async (resolver,
550                                      priv->dest_uri,
551                                      cancellable,
552                                      proxy_lookup_cb,
553                                      priv);
554       return;
555     }
556
557   if (priv->addr_enum)
558     {
559       if (priv->proxy_address)
560         {
561           save_result (priv);
562         }
563       else
564         {
565           g_socket_address_enumerator_next_async (priv->addr_enum,
566                                                   cancellable,
567                                                   address_enumerate_cb,
568                                                   priv);
569           return;
570         }
571     }
572
573   g_simple_async_result_complete_in_idle (priv->simple);
574
575   g_object_unref (priv->simple);
576   priv->simple = NULL;
577
578   if (priv->cancellable)
579     {
580       g_object_unref (priv->cancellable);
581       priv->cancellable = NULL;
582     }
583 }
584
585 static GSocketAddress *
586 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
587                                         GAsyncResult              *result,
588                                         GError                   **error)
589 {
590   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
591   GSocketAddress *address;
592
593   if (g_simple_async_result_propagate_error (simple, error))
594     return NULL;
595
596   address = g_simple_async_result_get_op_res_gpointer (simple);
597   if (address)
598     g_object_ref (address);
599
600   return address;
601 }
602
603 static void
604 g_proxy_address_enumerator_get_property (GObject        *object,
605                                          guint           property_id,
606                                          GValue         *value,
607                                          GParamSpec     *pspec)
608 {
609   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
610   switch (property_id)
611     {
612       case PROP_URI:
613         g_value_set_string (value, priv->dest_uri);
614         break;
615
616       case PROP_CONNECTABLE:
617         g_value_set_object (value, priv->connectable);
618         break;
619
620       default:
621         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
622     }
623 }
624
625 static void
626 g_proxy_address_enumerator_set_property (GObject        *object,
627                                          guint           property_id,
628                                          const GValue   *value,
629                                          GParamSpec     *pspec)
630 {
631   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
632   switch (property_id)
633     {
634       case PROP_URI:
635           {
636             const gchar *uri;
637
638             g_free (priv->dest_hostname);
639             priv->dest_hostname = NULL;
640             priv->dest_port = 0;
641
642             g_free (priv->dest_uri);
643             priv->dest_uri = NULL;
644
645             uri = g_value_get_string (value);
646
647             if (uri)
648               {
649                 GSocketConnectable *conn;
650
651                 conn = g_network_address_parse_uri (uri, 0, NULL);
652                 if (conn)
653                   {
654                     guint port;
655
656                     priv->dest_uri = g_strdup (uri);
657                     
658                     g_object_get (conn,
659                                   "hostname", &priv->dest_hostname,
660                                   "port", &port,
661                                   NULL);
662
663                     priv->dest_port = port;
664                     g_object_unref (conn);
665                   }
666                 else
667                   g_warning ("Invalid URI '%s'", uri);
668               }
669
670             break;
671           }
672
673       case PROP_CONNECTABLE:
674           priv->connectable = g_value_dup_object (value);
675           break;
676
677       default:
678         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
679     }
680 }
681
682 static void
683 g_proxy_address_enumerator_finalize (GObject *object)
684 {
685   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
686
687   if (priv->connectable)
688     g_object_unref (priv->connectable);
689
690   g_free (priv->dest_uri);
691   g_free (priv->dest_hostname);
692
693   if (priv->dest_ips)
694     g_resolver_free_addresses (priv->dest_ips);
695
696   g_strfreev (priv->proxies);
697
698   if (priv->addr_enum)
699     g_object_unref (priv->addr_enum);
700
701   g_free (priv->proxy_type);
702   g_free (priv->proxy_username);
703   g_free (priv->proxy_password);
704
705   if (priv->cancellable)
706     g_object_unref (priv->cancellable);
707
708   g_clear_error (&priv->last_error);
709
710   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
711 }
712
713 static void
714 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
715 {
716   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
717                                             G_TYPE_PROXY_ADDRESS_ENUMERATOR,
718                                             GProxyAddressEnumeratorPrivate);
719 }
720
721 static void
722 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
723 {
724   GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
725   GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
726
727   g_type_class_add_private (enumerator_class,
728                             sizeof (GProxyAddressEnumeratorPrivate));
729
730   object_class->set_property = g_proxy_address_enumerator_set_property;
731   object_class->get_property = g_proxy_address_enumerator_get_property;
732   object_class->finalize = g_proxy_address_enumerator_finalize;
733
734   enumerator_class->next = g_proxy_address_enumerator_next;
735   enumerator_class->next_async = g_proxy_address_enumerator_next_async;
736   enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
737
738   g_object_class_install_property (object_class,
739                                    PROP_URI,
740                                    g_param_spec_string ("uri",
741                                                         P_("URI"),
742                                                         P_("The destination URI, use none:// for generic socket"),
743                                                         NULL,
744                                                         G_PARAM_READWRITE |
745                                                         G_PARAM_CONSTRUCT_ONLY |
746                                                         G_PARAM_STATIC_STRINGS));
747
748   g_object_class_install_property (object_class,
749                                    PROP_CONNECTABLE,
750                                    g_param_spec_object ("connectable",
751                                                         P_("Connectable"),
752                                                         P_("The connectable being enumerated."),
753                                                         G_TYPE_SOCKET_CONNECTABLE,
754                                                         G_PARAM_READWRITE |
755                                                         G_PARAM_CONSTRUCT_ONLY |
756                                                         G_PARAM_STATIC_STRINGS));
757 }