Imported Upstream version 1.35
[platform/upstream/connman.git] / plugins / pacrunner.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <string.h>
28
29 #include <gdbus.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/notifier.h>
34 #include <connman/dbus.h>
35 #include <connman/log.h>
36 #include <connman/proxy.h>
37
38 #define PACRUNNER_SERVICE       "org.pacrunner"
39 #define PACRUNNER_INTERFACE     "org.pacrunner.Manager"
40 #define PACRUNNER_PATH          "/org/pacrunner/manager"
41
42 #define PACRUNNER_CLIENT_INTERFACE      "org.pacrunner.Client"
43 #define PACRUNNER_CLIENT_PATH           "/org/pacrunner/client"
44
45 #define DBUS_TIMEOUT    5000
46
47 struct proxy_data {
48         struct connman_service *service;
49         char *url;
50 };
51
52 static DBusConnection *connection;
53 static dbus_bool_t daemon_running = FALSE;
54
55 static struct connman_service *default_service = NULL;
56 static char *current_config = NULL;
57
58 static void create_config_reply(DBusPendingCall *call, void *user_data)
59 {
60         DBusMessage *reply = dbus_pending_call_steal_reply(call);
61         const char *path;
62
63         DBG("");
64
65         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
66                 connman_error("Failed to create proxy configuration");
67                 goto done;
68         }
69
70         if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH,
71                                         &path, DBUS_TYPE_INVALID))
72                 goto done;
73
74         g_free(current_config);
75         current_config = g_strdup(path);
76
77 done:
78         dbus_message_unref(reply);
79 }
80
81 static void append_string_list(DBusMessageIter *iter, void *user_data)
82 {
83         char **list = user_data;
84         int i;
85
86         for (i = 0; list[i]; i++)
87                 dbus_message_iter_append_basic(iter,
88                                         DBUS_TYPE_STRING, &list[i]);
89 }
90
91 static void create_proxy_configuration(void)
92 {
93         DBusMessage *msg;
94         DBusMessageIter iter, dict;
95         DBusPendingCall *call;
96         dbus_bool_t result;
97         char *interface;
98         const char *method;
99         const char *str;
100         char **str_list;
101
102         if (!default_service)
103                 return;
104
105         DBG("");
106
107         msg = dbus_message_new_method_call(PACRUNNER_SERVICE, PACRUNNER_PATH,
108                         PACRUNNER_INTERFACE, "CreateProxyConfiguration");
109         if (!msg)
110                 return;
111
112         dbus_message_set_auto_start(msg, FALSE);
113
114         dbus_message_iter_init_append(msg, &iter);
115         connman_dbus_dict_open(&iter, &dict);
116
117         switch(connman_service_get_proxy_method(default_service)) {
118         case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
119                 connman_dbus_dict_close(&iter, &dict);
120                 goto done;
121         case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
122                 method= "direct";
123                 break;
124         case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
125                 method = "manual";
126
127                 str_list = connman_service_get_proxy_servers(default_service);
128                 if (!str_list) {
129                         connman_dbus_dict_close(&iter, &dict);
130                         goto done;
131                 }
132
133                 connman_dbus_dict_append_array(&dict, "Servers",
134                                         DBUS_TYPE_STRING, append_string_list,
135                                         str_list);
136                 g_strfreev(str_list);
137
138                 str_list = connman_service_get_proxy_excludes(default_service);
139                 if (!str_list)
140                         break;
141
142                 connman_dbus_dict_append_array(&dict, "Excludes",
143                                         DBUS_TYPE_STRING, append_string_list,
144                                         str_list);
145                 g_strfreev(str_list);
146
147                 break;
148         case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
149                 method = "auto";
150
151                 str = connman_service_get_proxy_url(default_service);
152                 if (!str) {
153                         str = connman_service_get_proxy_autoconfig(
154                                                         default_service);
155                         if (!str) {
156                                 connman_dbus_dict_close(&iter, &dict);
157                                 goto done;
158                         }
159                 }
160
161                 connman_dbus_dict_append_basic(&dict, "URL",
162                                         DBUS_TYPE_STRING, &str);
163                 break;
164         }
165
166         connman_dbus_dict_append_basic(&dict, "Method",
167                                 DBUS_TYPE_STRING, &method);
168
169         interface = connman_service_get_interface(default_service);
170         if (interface) {
171                 connman_dbus_dict_append_basic(&dict, "Interface",
172                                                 DBUS_TYPE_STRING, &interface);
173                 g_free(interface);
174         }
175
176         str_list = connman_service_get_nameservers(default_service);
177         if (str_list)
178                 connman_dbus_dict_append_array(&dict, "Nameservers",
179                                         DBUS_TYPE_STRING, append_string_list,
180                                         str_list);
181         g_strfreev(str_list);
182
183         connman_dbus_dict_close(&iter, &dict);
184
185         result = dbus_connection_send_with_reply(connection, msg,
186                                                         &call, DBUS_TIMEOUT);
187
188         if (!result || !call)
189                 goto done;
190
191         dbus_pending_call_set_notify(call, create_config_reply, NULL, NULL);
192
193         dbus_pending_call_unref(call);
194
195 done:
196         dbus_message_unref(msg);
197 }
198
199 static void destroy_config_reply(DBusPendingCall *call, void *user_data)
200 {
201         DBusMessage *reply = dbus_pending_call_steal_reply(call);
202
203         DBG("");
204
205         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
206                 connman_error("Failed to destoy proxy configuration");
207
208         dbus_message_unref(reply);
209 }
210
211 static void destroy_proxy_configuration(void)
212 {
213         DBusMessage *msg;
214         DBusPendingCall *call;
215         dbus_bool_t result;
216
217         if (!current_config)
218                 return;
219
220         DBG("");
221
222         msg = dbus_message_new_method_call(PACRUNNER_SERVICE, PACRUNNER_PATH,
223                         PACRUNNER_INTERFACE, "DestroyProxyConfiguration");
224         if (!msg)
225                 return;
226
227         dbus_message_set_auto_start(msg, FALSE);
228
229         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &current_config,
230                                                         DBUS_TYPE_INVALID);
231
232         result = dbus_connection_send_with_reply(connection, msg,
233                                                         &call, DBUS_TIMEOUT);
234
235         dbus_message_unref(msg);
236
237         if (!result || !call)
238                 return;
239
240         dbus_pending_call_set_notify(call, destroy_config_reply, NULL, NULL);
241
242         dbus_pending_call_unref(call);
243
244         g_free(current_config);
245         current_config = NULL;
246 }
247
248 static void default_service_changed(struct connman_service *service)
249 {
250         DBG("service %p", service);
251
252         if (service == default_service)
253                 return;
254
255         default_service = service;
256
257         if (!daemon_running)
258                 return;
259
260         destroy_proxy_configuration();
261
262         create_proxy_configuration();
263 }
264
265 static void proxy_changed(struct connman_service *service)
266 {
267         DBG("service %p", service);
268
269         if (service != default_service)
270                 return;
271
272         if (!daemon_running)
273                 return;
274
275         destroy_proxy_configuration();
276
277         create_proxy_configuration();
278 }
279
280 static struct connman_notifier pacrunner_notifier = {
281         .name                   = "pacrunner",
282         .default_changed        = default_service_changed,
283         .proxy_changed          = proxy_changed,
284 };
285
286 static void pacrunner_connect(DBusConnection *conn, void *user_data)
287 {
288         DBG("");
289
290         daemon_running = TRUE;
291
292         create_proxy_configuration();
293 }
294
295 static void pacrunner_disconnect(DBusConnection *conn, void *user_data)
296 {
297         DBG("");
298
299         daemon_running = FALSE;
300
301         g_free(current_config);
302         current_config = NULL;
303 }
304
305 static char * parse_url(const char *url)
306 {
307         char *scheme, *host, *path, *host_ret;
308
309         scheme = g_strdup(url);
310         if (!scheme)
311                 return NULL;
312
313         host = strstr(scheme, "://");
314         if (host) {
315                 *host = '\0';
316                 host += 3;
317         } else
318                 host = scheme;
319
320         path = strchr(host, '/');
321         if (path)
322                 *(path++) = '\0';
323
324         host_ret = g_strdup(host);
325
326         g_free(scheme);
327
328         return host_ret;
329 }
330
331 static void request_lookup_reply(DBusPendingCall *call, void *user_data)
332 {
333         DBusMessage *reply = dbus_pending_call_steal_reply(call);
334         struct proxy_data *data = user_data;
335         const char *proxy;
336
337         DBG("");
338
339         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
340                 connman_error("Failed to find URL:%s", data->url);
341                 proxy = NULL;
342                 goto done;
343         }
344
345         if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING,
346                                         &proxy, DBUS_TYPE_INVALID))
347                 proxy = NULL;
348
349 done:
350         connman_proxy_driver_lookup_notify(data->service, data->url, proxy);
351
352         connman_service_unref(data->service);
353
354         g_free(data->url);
355         g_free(data);
356
357         dbus_message_unref(reply);
358 }
359
360 static int request_lookup(struct connman_service *service, const char *url)
361 {
362         DBusMessage *msg;
363         DBusPendingCall *call;
364         dbus_bool_t result;
365         char *host;
366         struct proxy_data *data;
367
368         DBG("");
369
370         msg = dbus_message_new_method_call(PACRUNNER_SERVICE,
371                                                 PACRUNNER_CLIENT_PATH,
372                                                 PACRUNNER_CLIENT_INTERFACE,
373                                                 "FindProxyForURL");
374         if (!msg)
375                 return -1;
376
377         host = parse_url(url);
378         if (!host) {
379                 dbus_message_unref(msg);
380                 return -EINVAL;
381         }
382
383         data = g_try_new0(struct proxy_data, 1);
384         if (!data) {
385                 dbus_message_unref(msg);
386                 g_free(host);
387                 return -ENOMEM;
388         }
389
390         data->url = g_strdup(url);
391         data->service = connman_service_ref(service);
392
393         dbus_message_append_args(msg, DBUS_TYPE_STRING, &url,
394                                         DBUS_TYPE_STRING, &host,
395                                         DBUS_TYPE_INVALID);
396
397         result = dbus_connection_send_with_reply(connection, msg,
398                                                         &call, DBUS_TIMEOUT);
399
400         dbus_message_unref(msg);
401
402         if (!result || !call) {
403                 g_free(host);
404                 g_free(data->url);
405                 g_free(data);
406                 return -EINVAL;
407         }
408
409         dbus_pending_call_set_notify(call, request_lookup_reply,
410                                                         data, NULL);
411
412         dbus_pending_call_unref(call);
413         g_free(host);
414
415         return 0;
416 }
417
418 static void cancel_lookup(struct connman_service *service, const char *url)
419 {
420         DBG("");
421 }
422
423 static struct connman_proxy_driver pacrunner_proxy = {
424         .name           = "pacrunnerproxy",
425         .priority       = CONNMAN_PROXY_PRIORITY_HIGH,
426         .request_lookup = request_lookup,
427         .cancel_lookup  = cancel_lookup,
428 };
429
430 static guint pacrunner_watch;
431
432 static int pacrunner_init(void)
433 {
434         connection = connman_dbus_get_connection();
435         if (!connection)
436                 return -EIO;
437
438         pacrunner_watch = g_dbus_add_service_watch(connection,
439                                         PACRUNNER_SERVICE, pacrunner_connect,
440                                         pacrunner_disconnect, NULL, NULL);
441         if (pacrunner_watch == 0) {
442                 dbus_connection_unref(connection);
443                 return -EIO;
444         }
445
446         connman_notifier_register(&pacrunner_notifier);
447
448         connman_proxy_driver_register(&pacrunner_proxy);
449
450         return 0;
451 }
452
453 static void pacrunner_exit(void)
454 {
455         connman_proxy_driver_unregister(&pacrunner_proxy);
456
457         connman_notifier_unregister(&pacrunner_notifier);
458
459         g_dbus_remove_watch(connection, pacrunner_watch);
460
461         destroy_proxy_configuration();
462
463         dbus_connection_unref(connection);
464 }
465
466 CONNMAN_PLUGIN_DEFINE(pacrunner, "PAC runner proxy plugin", VERSION,
467                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, pacrunner_init, pacrunner_exit)