pacrunner: Add pacrunner proxy driver.
[framework/connectivity/connman.git] / plugins / pacrunner.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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, &path,
71                                                 DBUS_TYPE_INVALID) == FALSE)
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(DBusMessageIter *iter, void *user_data)
82 {
83         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, user_data);
84 }
85
86 static void append_string_list(DBusMessageIter *iter, void *user_data)
87 {
88         char **list = user_data;
89         int i;
90
91         for (i = 0; list[i] != NULL; i++)
92                 dbus_message_iter_append_basic(iter,
93                                         DBUS_TYPE_STRING, &list[i]);
94 }
95
96 static void create_proxy_configuration(void)
97 {
98         DBusMessage *msg;
99         DBusMessageIter iter, dict;
100         DBusPendingCall *call;
101         dbus_bool_t result;
102         char *interface;
103         const char *method;
104         const char *str;
105         char **str_list;
106
107         if (default_service == NULL)
108                 return;
109
110         DBG("");
111
112         msg = dbus_message_new_method_call(PACRUNNER_SERVICE, PACRUNNER_PATH,
113                         PACRUNNER_INTERFACE, "CreateProxyConfiguration");
114         if (msg == NULL)
115                 return;
116
117         dbus_message_set_auto_start(msg, FALSE);
118
119         dbus_message_iter_init_append(msg, &iter);
120         connman_dbus_dict_open(&iter, &dict);
121
122         switch(connman_service_get_proxy_method(default_service)) {
123         case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
124                 goto done;
125         case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
126                 method= "direct";
127                 break;
128         case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
129                 method = "manual";
130
131                 str_list = connman_service_get_proxy_servers(default_service);
132                 if (str_list == NULL)
133                         goto done;
134
135                 connman_dbus_dict_append_array(&dict, "Servers",
136                                         DBUS_TYPE_STRING, append_string_list,
137                                         str_list);
138                 g_strfreev(str_list);
139
140                 str_list = connman_service_get_proxy_excludes(default_service);
141                 if (str_list == NULL)
142                         break;
143
144                 connman_dbus_dict_append_array(&dict, "Excludes",
145                                         DBUS_TYPE_STRING, append_string_list,
146                                         str_list);
147                 g_strfreev(str_list);
148
149                 break;
150         case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
151                 method = "auto";
152
153                 str = connman_service_get_proxy_url(default_service);
154                 if (str == NULL) {
155                         str = connman_service_get_proxy_autoconfig(
156                                                         default_service);
157                         if (str == NULL)
158                                 goto done;
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 != NULL) {
171                 connman_dbus_dict_append_basic(&dict, "Interface",
172                                                 DBUS_TYPE_STRING, &interface);
173                 g_free(interface);
174         }
175
176         str = connman_service_get_domainname(default_service);
177         if (str != NULL)
178                 connman_dbus_dict_append_array(&dict, "Domains",
179                                         DBUS_TYPE_STRING, append_string, &str);
180
181         str = connman_service_get_nameserver(default_service);
182         if (str != NULL)
183                 connman_dbus_dict_append_array(&dict, "Nameservers",
184                                         DBUS_TYPE_STRING, append_string, &str);
185
186         connman_dbus_dict_close(&iter, &dict);
187
188         result = dbus_connection_send_with_reply(connection, msg,
189                                                         &call, DBUS_TIMEOUT);
190
191         if (result == FALSE || call == NULL)
192                 goto done;
193
194         dbus_pending_call_set_notify(call, create_config_reply, NULL, NULL);
195
196         dbus_pending_call_unref(call);
197
198 done:
199         dbus_message_unref(msg);
200 }
201
202 static void destroy_config_reply(DBusPendingCall *call, void *user_data)
203 {
204         DBusMessage *reply = dbus_pending_call_steal_reply(call);
205
206         DBG("");
207
208         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
209                 connman_error("Failed to destoy proxy configuration");
210
211         dbus_message_unref(reply);
212 }
213
214 static void destroy_proxy_configuration(void)
215 {
216         DBusMessage *msg;
217         DBusPendingCall *call;
218         dbus_bool_t result;
219
220         if (current_config == NULL)
221                 return;
222
223         DBG("");
224
225         msg = dbus_message_new_method_call(PACRUNNER_SERVICE, PACRUNNER_PATH,
226                         PACRUNNER_INTERFACE, "DestroyProxyConfiguration");
227         if (msg == NULL)
228                 return;
229
230         dbus_message_set_auto_start(msg, FALSE);
231
232         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &current_config,
233                                                         DBUS_TYPE_INVALID);
234
235         result = dbus_connection_send_with_reply(connection, msg,
236                                                         &call, DBUS_TIMEOUT);
237
238         dbus_message_unref(msg);
239
240         if (result == FALSE || call == NULL)
241                 return;
242
243         dbus_pending_call_set_notify(call, destroy_config_reply, NULL, NULL);
244
245         dbus_pending_call_unref(call);
246
247         g_free(current_config);
248         current_config = NULL;
249 }
250
251 static void default_service_changed(struct connman_service *service)
252 {
253         DBG("service %p", service);
254
255         if (service == default_service)
256                 return;
257
258         default_service = service;
259
260         if (daemon_running == FALSE)
261                 return;
262
263         destroy_proxy_configuration();
264
265         create_proxy_configuration();
266 }
267
268 static void proxy_changed(struct connman_service *service)
269 {
270         DBG("service %p", service);
271
272         if (service != default_service)
273                 return;
274
275         if (daemon_running == FALSE)
276                 return;
277
278         destroy_proxy_configuration();
279
280         create_proxy_configuration();
281 }
282
283 static struct connman_notifier pacrunner_notifier = {
284         .name                   = "pacrunner",
285         .default_changed        = default_service_changed,
286         .proxy_changed          = proxy_changed,
287 };
288
289 static void pacrunner_connect(DBusConnection *conn, void *user_data)
290 {
291         DBG("");
292
293         daemon_running = TRUE;
294
295         create_proxy_configuration();
296 }
297
298 static void pacrunner_disconnect(DBusConnection *conn, void *user_data)
299 {
300         DBG("");
301
302         daemon_running = FALSE;
303
304         g_free(current_config);
305         current_config = NULL;
306 }
307
308 static char * parse_url(const char *url)
309 {
310         char *scheme, *host, *path, *host_ret;
311
312         scheme = g_strdup(url);
313         if (scheme == NULL)
314                 return NULL;
315
316         if (host_ret == NULL)
317                 return NULL;
318
319         host = strstr(scheme, "://");
320         if (host != NULL) {
321                 *host = '\0';
322                 host += 3;
323         } else
324                 host = scheme;
325
326         path = strchr(host, '/');
327         if (path != NULL)
328                 *(path++) = '\0';
329
330         host_ret = g_strdup(host);
331
332         g_free(scheme);
333
334         return host_ret;
335 }
336
337 static void request_lookup_reply(DBusPendingCall *call, void *user_data)
338 {
339         DBusMessage *reply = dbus_pending_call_steal_reply(call);
340         struct proxy_data *data = user_data;
341         const char *proxy;
342
343         DBG("");
344
345         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
346                 connman_error("Failed to find URL:%s", data->url);
347                 proxy = NULL;
348                 goto done;
349         }
350
351         if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &proxy,
352                                                 DBUS_TYPE_INVALID) == FALSE)
353                 proxy = NULL;
354
355 done:
356         connman_proxy_driver_lookup_notify(data->service, data->url, proxy);
357
358         connman_service_unref(data->service);
359
360         g_free(data->url);
361         g_free(data);
362
363         dbus_message_unref(reply);
364 }
365
366 static int request_lookup(struct connman_service *service, const char *url)
367 {
368         DBusMessage *msg;
369         DBusPendingCall *call;
370         dbus_bool_t result;
371         char *host;
372         struct proxy_data *data;
373
374         DBG("");
375
376         if (daemon_running == FALSE)
377                 return -EINVAL;
378
379         msg = dbus_message_new_method_call(PACRUNNER_SERVICE,
380                                                 PACRUNNER_CLIENT_PATH,
381                                                 PACRUNNER_CLIENT_INTERFACE,
382                                                 "FindProxyForURL");
383         if (msg == NULL)
384                 return -1;
385
386         host = parse_url(url);
387         if (host == NULL) {
388                 dbus_message_unref(msg);
389                 return -EINVAL;
390         }
391
392         data = g_try_new0(struct proxy_data, 1);
393         if (data == NULL) {
394                 dbus_message_unref(msg);
395                 g_free(host);
396                 return -ENOMEM;
397         }
398
399         data->url = g_strdup(url);
400         data->service = connman_service_ref(service);
401
402         dbus_message_set_auto_start(msg, FALSE);
403
404         dbus_message_append_args(msg, DBUS_TYPE_STRING, &url,
405                                         DBUS_TYPE_STRING, &host,
406                                         DBUS_TYPE_INVALID);
407
408         result = dbus_connection_send_with_reply(connection, msg,
409                                                         &call, DBUS_TIMEOUT);
410
411         dbus_message_unref(msg);
412
413         if (result == FALSE || call == NULL) {
414                 g_free(host);
415                 g_free(data->url);
416                 g_free(data);
417                 return -EINVAL;
418         }
419
420         dbus_pending_call_set_notify(call, request_lookup_reply,
421                                                         data, NULL);
422
423         dbus_pending_call_unref(call);
424         g_free(host);
425
426         return 0;
427 }
428
429 static void cancel_lookup(struct connman_service *service, const char *url)
430 {
431         DBG("");
432 }
433
434 static struct connman_proxy_driver pacrunner_proxy = {
435         .name           = "pacrunnerproxy",
436         .priority       = CONNMAN_PROXY_PRIORITY_HIGH,
437         .request_lookup = request_lookup,
438         .cancel_lookup  = cancel_lookup,
439 };
440
441 static guint pacrunner_watch;
442
443 static int pacrunner_init(void)
444 {
445         connection = connman_dbus_get_connection();
446         if (connection == NULL)
447                 return -EIO;
448
449         pacrunner_watch = g_dbus_add_service_watch(connection,
450                                         PACRUNNER_SERVICE, pacrunner_connect,
451                                         pacrunner_disconnect, NULL, NULL);
452         if (pacrunner_watch == 0) {
453                 dbus_connection_unref(connection);
454                 return -EIO;
455         }
456
457         connman_notifier_register(&pacrunner_notifier);
458
459         connman_proxy_driver_register(&pacrunner_proxy);
460
461         return 0;
462 }
463
464 static void pacrunner_exit(void)
465 {
466         connman_proxy_driver_unregister(&pacrunner_proxy);
467
468         connman_notifier_unregister(&pacrunner_notifier);
469
470         g_dbus_remove_watch(connection, pacrunner_watch);
471
472         destroy_proxy_configuration();
473
474         dbus_connection_unref(connection);
475 }
476
477 CONNMAN_PLUGIN_DEFINE(pacrunner, "PAC runner proxy plugin", VERSION,
478                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, pacrunner_init, pacrunner_exit)