1/*
2 * Copyright (C) 2012 Intel Corporation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SoupProxyResolverEfl.h"
28
29#include <libsoup/soup.h>
30#include <string.h>
31#include <wtf/Vector.h>
32#include <wtf/text/CString.h>
33#include <wtf/text/WTFString.h>
34
35static const char defaultNoProxyValue[] = "localhost,127.0.0.1";
36
37typedef struct {
38 SoupURI* proxyURI;
39 CString noProxy;
40 Vector<String> proxyExceptions;
41} SoupProxyResolverEflPrivate;
42
43#define SOUP_PROXY_RESOLVER_EFL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SOUP_TYPE_PROXY_RESOLVER_EFL, SoupProxyResolverEflPrivate))
44
45static void soup_proxy_resolver_efl_interface_init(SoupProxyURIResolverInterface* proxyResolverInterface);
46
47G_DEFINE_TYPE_EXTENDED(SoupProxyResolverEfl, soup_proxy_resolver_efl, G_TYPE_OBJECT, 0,
48 G_IMPLEMENT_INTERFACE(SOUP_TYPE_SESSION_FEATURE, 0)
49 G_IMPLEMENT_INTERFACE(SOUP_TYPE_PROXY_URI_RESOLVER, soup_proxy_resolver_efl_interface_init))
50
51enum {
52 PROP_0,
53 PROP_PROXY_URI,
54 PROP_NO_PROXY,
55 LAST_PROP
56};
57
58static void soup_proxy_resolver_efl_init(SoupProxyResolverEfl* resolverEfl)
59{
60}
61
62static void soupProxyResolverEflFinalize(GObject* object)
63{
64 SoupProxyResolverEflPrivate* priv = SOUP_PROXY_RESOLVER_EFL_GET_PRIVATE(object);
65
66 g_clear_pointer(&priv->proxyURI, soup_uri_free);
67
68 G_OBJECT_CLASS(soup_proxy_resolver_efl_parent_class)->finalize(object);
69}
70
71static void soupProxyResolverEflSetProperty(GObject* object, uint propID, const GValue* value, GParamSpec* pspec)
72{
73 SoupProxyResolverEflPrivate* priv = SOUP_PROXY_RESOLVER_EFL_GET_PRIVATE(object);
74 SoupURI* uri;
75
76 switch (propID) {
77 case PROP_PROXY_URI:
78 uri = static_cast<SoupURI*>(g_value_get_boxed(value));
79 if (priv->proxyURI)
80 soup_uri_free(priv->proxyURI);
81
82 priv->proxyURI = uri ? soup_uri_copy(uri) : 0;
83 break;
84 case PROP_NO_PROXY:
85 priv->noProxy = g_value_get_string(value);
86 priv->proxyExceptions.clear();
87 String::fromUTF8(priv->noProxy.data()).split(',', priv->proxyExceptions);
88 break;
89 default:
90 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
91 break;
92 }
93}
94
95static void soupProxyResolverEflGetProperty(GObject* object, uint propID, GValue* value, GParamSpec* pspec)
96{
97 SoupProxyResolverEflPrivate* priv = SOUP_PROXY_RESOLVER_EFL_GET_PRIVATE(object);
98
99 switch (propID) {
100 case PROP_PROXY_URI:
101 g_value_set_boxed(value, priv->proxyURI);
102 break;
103 case PROP_NO_PROXY:
104 g_value_set_string(value, priv->noProxy.data());
105 break;
106 default:
107 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
108 break;
109 }
110}
111
112static bool shouldBypassProxy(SoupProxyResolverEflPrivate* priv, SoupURI* uri)
113{
114 const size_t exceptionCount = priv->proxyExceptions.size();
115 for (size_t i = 0; i < exceptionCount; ++i) {
116 if (String::fromUTF8(uri->host).endsWith(priv->proxyExceptions[i], false))
117 return true;
118 }
119
120 return false;
121}
122
123typedef struct {
124 SoupProxyURIResolver* proxyResolver;
125 SoupURI* uri;
126 SoupProxyURIResolverCallback callback;
127 void* userData;
128} SoupEflAsyncData;
129
130static gboolean idle_return_proxy_uri(void* data)
131{
132 SoupEflAsyncData* ssad = static_cast<SoupEflAsyncData*>(data);
133 SoupProxyResolverEflPrivate* priv = SOUP_PROXY_RESOLVER_EFL_GET_PRIVATE(ssad->proxyResolver);
134
135 SoupURI* proxyURI = 0;
136 if (!shouldBypassProxy(priv, ssad->uri))
137 proxyURI = priv->proxyURI;
138
139 ssad->callback(ssad->proxyResolver, SOUP_STATUS_OK, proxyURI, ssad->userData);
140 g_object_unref(ssad->proxyResolver);
141 soup_uri_free(ssad->uri);
142 g_slice_free(SoupEflAsyncData, ssad);
143
144 return false;
145}
146
147static void soupProxyResolverEflGetProxyURIAsync(SoupProxyURIResolver* proxyResolver, SoupURI* uri, GMainContext* asyncContext, GCancellable* cancellable, SoupProxyURIResolverCallback callback, void* userData)
148{
149 SoupEflAsyncData* ssad;
150
151 ssad = g_slice_new0(SoupEflAsyncData);
152 ssad->proxyResolver = SOUP_PROXY_URI_RESOLVER(g_object_ref(proxyResolver));
153 ssad->uri = soup_uri_copy(uri);
154 ssad->callback = callback;
155 ssad->userData = userData;
156 soup_add_completion(asyncContext, idle_return_proxy_uri, ssad);
157}
158
159static uint soupProxyResolverEflGetProxyURISync(SoupProxyURIResolver* proxyResolver, SoupURI* uri, GCancellable* cancellable, SoupURI** proxyURI)
160{
161 SoupProxyResolverEflPrivate* priv = SOUP_PROXY_RESOLVER_EFL_GET_PRIVATE(proxyResolver);
162
163 if (!shouldBypassProxy(priv, uri))
164 *proxyURI = soup_uri_copy(priv->proxyURI);
165
166 return SOUP_STATUS_OK;
167}
168
169static void soup_proxy_resolver_efl_class_init(SoupProxyResolverEflClass* eflClass)
170{
171 GObjectClass* object_class = G_OBJECT_CLASS(eflClass);
172
173 g_type_class_add_private(eflClass, sizeof(SoupProxyResolverEflPrivate));
174
175 object_class->set_property = soupProxyResolverEflSetProperty;
176 object_class->get_property = soupProxyResolverEflGetProperty;
177 object_class->finalize = soupProxyResolverEflFinalize;
178
179 g_object_class_install_property(object_class, PROP_PROXY_URI,
180 g_param_spec_boxed(SOUP_PROXY_RESOLVER_EFL_PROXY_URI,
181 "Proxy URI",
182 "The HTTP Proxy to use",
183 SOUP_TYPE_URI,
184 static_cast<GParamFlags>(G_PARAM_READWRITE)));
185
186 g_object_class_install_property(object_class, PROP_NO_PROXY,
187 g_param_spec_string(SOUP_PROXY_RESOLVER_EFL_NO_PROXY,
188 "Proxy exceptions",
189 "Comma-separated proxy exceptions",
190 defaultNoProxyValue,
191 static_cast<GParamFlags>(G_PARAM_READWRITE)));
192}
193
194static void soup_proxy_resolver_efl_interface_init(SoupProxyURIResolverInterface* proxy_uri_resolver_interface)
195{
196 proxy_uri_resolver_interface->get_proxy_uri_async = soupProxyResolverEflGetProxyURIAsync;
197 proxy_uri_resolver_interface->get_proxy_uri_sync = soupProxyResolverEflGetProxyURISync;
198}
199
200SoupProxyURIResolver* soupProxyResolverEflNew(const char* httpProxy, const char* noProxy)
201{
202 SoupURI* proxyURI = soup_uri_new(httpProxy);
203 SoupProxyURIResolver* resolver = SOUP_PROXY_URI_RESOLVER(g_object_new(SOUP_TYPE_PROXY_RESOLVER_EFL,
204 SOUP_PROXY_RESOLVER_EFL_PROXY_URI, proxyURI,
205 SOUP_PROXY_RESOLVER_EFL_NO_PROXY, noProxy ? noProxy : defaultNoProxyValue,
206 0));
207 soup_uri_free(proxyURI);
208
209 return resolver;
210}