Add default context if no context exists
[platform/upstream/connman.git] / plugins / ofono.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
28 #include <gdbus.h>
29 #include <string.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/element.h>
34 #include <connman/device.h>
35 #include <connman/network.h>
36 #include <connman/dbus.h>
37 #include <connman/inet.h>
38 #include <connman/log.h>
39
40 #define OFONO_SERVICE                   "org.ofono"
41
42 #define OFONO_MANAGER_INTERFACE         OFONO_SERVICE ".Manager"
43 #define OFONO_MODEM_INTERFACE           OFONO_SERVICE ".Modem"
44 #define OFONO_GPRS_INTERFACE            OFONO_SERVICE ".DataConnectionManager"
45 #define OFONO_SIM_INTERFACE             OFONO_SERVICE ".SimManager"
46 #define OFONO_PRI_CONTEXT_INTERFACE     OFONO_SERVICE ".PrimaryDataContext"
47
48 #define PROPERTY_CHANGED                "PropertyChanged"
49 #define GET_PROPERTIES                  "GetProperties"
50 #define SET_PROPERTY                    "SetProperty"
51 #define CREATE_CONTEXT                  "CreateContext"
52
53 #define TIMEOUT 5000
54
55 #define CONTEXT_NAME "3G Connection"
56 #define CONTEXT_TYPE "internet"
57
58 static DBusConnection *connection;
59
60 static GHashTable *modem_hash = NULL;
61
62 struct modem_data {
63         char *path;
64         struct connman_device *device;
65         gboolean available;
66 };
67
68 static int modem_probe(struct connman_device *device)
69 {
70         DBG("device %p", device);
71
72         return 0;
73 }
74
75 static void modem_remove(struct connman_device *device)
76 {
77         DBG("device %p", device);
78 }
79
80 static void powered_reply(DBusPendingCall *call, void *user_data)
81 {
82         DBusMessage *reply;
83         DBusError error;
84
85         DBG("");
86
87         dbus_error_init(&error);
88
89         reply = dbus_pending_call_steal_reply(call);
90
91         if (dbus_set_error_from_message(&error, reply)) {
92                 connman_error("%s", error.message);
93                 dbus_error_free(&error);
94         }
95
96         dbus_message_unref(reply);
97
98         dbus_pending_call_unref(call);
99 }
100
101 static int gprs_change_powered(const char *path, dbus_bool_t powered)
102 {
103         DBusMessage *message;
104         DBusMessageIter iter;
105         DBusPendingCall *call;
106
107         DBG("path %s powered %d", path, powered);
108
109         if (path == NULL)
110                 return -EINVAL;
111
112         message = dbus_message_new_method_call(OFONO_SERVICE, path,
113                                         OFONO_GPRS_INTERFACE, SET_PROPERTY);
114         if (message == NULL)
115                 return -ENOMEM;
116
117         dbus_message_set_auto_start(message, FALSE);
118
119         dbus_message_iter_init_append(message, &iter);
120         connman_dbus_property_append_basic(&iter, "Powered",
121                                                 DBUS_TYPE_BOOLEAN, &powered);
122
123         if (dbus_connection_send_with_reply(connection, message,
124                                                 &call, TIMEOUT) == FALSE) {
125                 connman_error("Failed to change powered property");
126                 dbus_message_unref(message);
127                 return -EINVAL;
128         }
129
130         if (call == NULL) {
131                 connman_error("D-Bus connection not available");
132                 dbus_message_unref(message);
133                 return -EINVAL;
134         }
135
136         dbus_pending_call_set_notify(call, powered_reply, (void *)path, NULL);
137
138         dbus_message_unref(message);
139
140         return -EINPROGRESS;
141 }
142
143 static int modem_enable(struct connman_device *device)
144 {
145         const char *path = connman_device_get_string(device, "Path");
146
147         DBG("device %p, path, %s", device, path);
148
149         return gprs_change_powered(path, TRUE);
150 }
151
152 static int modem_disable(struct connman_device *device)
153 {
154         const char *path = connman_device_get_string(device, "Path");
155
156         DBG("device %p, path %s", device, path);
157
158         return gprs_change_powered(path, FALSE);
159 }
160
161 static struct connman_device_driver modem_driver = {
162         .name           = "modem",
163         .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
164         .probe          = modem_probe,
165         .remove         = modem_remove,
166         .enable         = modem_enable,
167         .disable        = modem_disable,
168 };
169
170 static char *get_ident(const char *path)
171 {
172         char *ident, *pos;
173
174         if (*path != '/')
175                 return NULL;
176
177         ident = g_strdup(path + 1);
178
179         pos = ident;
180
181         while ((pos = strchr(pos, '/')) != NULL)
182                 *pos = '_';
183
184         return ident;
185 }
186
187 static void config_network_reply(DBusPendingCall *call, void *user_data)
188 {
189         struct connman_network *network = user_data;
190         DBusMessage *reply;
191         DBusMessageIter array, dict;
192         gboolean internet_type = FALSE;
193
194         DBG("network %p", network);
195
196         reply = dbus_pending_call_steal_reply(call);
197
198         if (dbus_message_iter_init(reply, &array) == FALSE)
199                 goto done;
200
201         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
202                 goto done;
203
204         dbus_message_iter_recurse(&array, &dict);
205
206         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
207                 DBusMessageIter entry, value;
208                 const char *key;
209
210                 dbus_message_iter_recurse(&dict, &entry);
211                 dbus_message_iter_get_basic(&entry, &key);
212
213                 dbus_message_iter_next(&entry);
214                 dbus_message_iter_recurse(&entry, &value);
215
216                 if (g_str_equal(key, "Name") == TRUE) {
217                         const char *name;
218
219                         dbus_message_iter_get_basic(&value, &name);
220                         connman_network_set_name(network, name);
221                 } else if (g_str_equal(key, "Type") == TRUE) {
222                         const char *type;
223
224                         dbus_message_iter_get_basic(&value, &type);
225                         if (g_strcmp0(type, "internet") == 0) {
226                                 internet_type = TRUE;
227
228                                 connman_network_set_protocol(network,
229                                                 CONNMAN_NETWORK_PROTOCOL_IP);
230                         } else {
231                                 internet_type = FALSE;
232
233                                 connman_network_set_protocol(network,
234                                         CONNMAN_NETWORK_PROTOCOL_UNKNOWN);
235                         }
236                 }
237
238                 dbus_message_iter_next(&dict);
239         }
240
241         if (internet_type == TRUE) {
242                 const char *path;
243                 char *group;
244
245                 path = connman_network_get_string(network, "Path");
246
247                 group = get_ident(path);
248
249                 connman_network_set_group(network, group);
250
251                 g_free(group);
252         }
253
254 done:
255         dbus_message_unref(reply);
256
257         dbus_pending_call_unref(call);
258 }
259
260 static void config_network(struct connman_network *network, const char *path)
261 {
262         DBusMessage *message;
263         DBusPendingCall *call;
264
265         DBG("path %s", path);
266
267         message = dbus_message_new_method_call(OFONO_SERVICE, path,
268                                 OFONO_PRI_CONTEXT_INTERFACE, GET_PROPERTIES);
269         if (message == NULL)
270                 return;
271
272         dbus_message_set_auto_start(message, FALSE);
273
274         if (dbus_connection_send_with_reply(connection, message,
275                                                 &call, TIMEOUT) == FALSE) {
276                 connman_error("Failed to get Primary Context");
277                 goto done;
278         }
279
280         if (call == NULL) {
281                 connman_error("D-Bus connection not available");
282                 goto done;
283         }
284
285         dbus_pending_call_set_notify(call, config_network_reply,
286                                                 (void *)network, NULL);
287
288 done:
289         dbus_message_unref(message);
290 }
291
292 static int network_probe(struct connman_network *network)
293 {
294         const char *path;
295
296         path = connman_network_get_string(network, "Path");
297
298         DBG("network %p path %s", network, path);
299
300         config_network(network, path);
301
302         return 0;
303 }
304
305 static struct connman_network *pending_network;
306
307 static gboolean pending_network_is_available(
308                 struct connman_network *pending_network)
309 {
310         struct connman_device *device;
311         struct connman_network *network;
312         const char *identifier;
313         char *ident;
314
315         /* Modem may be removed during waiting for active reply */
316         device  = connman_network_get_device(pending_network);
317         if (device == NULL)
318                 return FALSE;
319
320         identifier = connman_network_get_identifier(pending_network);
321
322         ident = g_strdup(identifier);
323
324         connman_network_unref(pending_network);
325
326         /* network may be removed during waiting for active reply */
327         network = connman_device_get_network(device, ident);
328
329         g_free(ident);
330
331         if (network == NULL)
332                 return FALSE;
333
334         return TRUE;
335 }
336
337 static void set_active_reply(DBusPendingCall *call, void *user_data)
338 {
339         DBusMessage *reply;
340         DBusError error;
341         struct connman_network *network = user_data;
342
343         DBG("network %p", network);
344
345         reply = dbus_pending_call_steal_reply(call);
346
347         if (pending_network_is_available(network) == FALSE)
348                 goto done;
349
350         dbus_error_init(&error);
351         if (dbus_set_error_from_message(&error, reply)) {
352                 if (connman_network_get_index(network) < 0)
353                         connman_network_set_error(network,
354                                 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
355
356                 pending_network = NULL;
357
358                 connman_error("%s", error.message);
359
360                 dbus_error_free(&error);
361         } else
362                 pending_network = network;
363
364 done:
365         dbus_message_unref(reply);
366
367         dbus_pending_call_unref(call);
368 }
369
370 static int set_network_active(struct connman_network *network,
371                                                 dbus_bool_t active)
372 {
373         DBusMessage *message;
374         DBusPendingCall *call;
375         DBusMessageIter iter;
376
377         const char *path = connman_network_get_string(network, "Path");
378
379         DBG("network %p, path %s, active %d", network, path, active);
380
381         if (path == NULL)
382                 return -EINVAL;
383
384         message = dbus_message_new_method_call(OFONO_SERVICE, path,
385                                 OFONO_PRI_CONTEXT_INTERFACE, SET_PROPERTY);
386         if (message == NULL)
387                 return -ENOMEM;
388
389         dbus_message_set_auto_start(message, FALSE);
390
391         dbus_message_iter_init_append(message, &iter);
392         connman_dbus_property_append_basic(&iter, "Active",
393                                                 DBUS_TYPE_BOOLEAN, &active);
394
395         if (dbus_connection_send_with_reply(connection, message,
396                                         &call, TIMEOUT * 10) == FALSE) {
397                 connman_error("Failed to connect service");
398                 dbus_message_unref(message);
399                 return -EINVAL;
400         }
401
402         if (call == NULL) {
403                 connman_error("D-Bus connection not available");
404                 dbus_message_unref(message);
405                 return -EINVAL;
406         }
407
408         connman_network_ref(network);
409
410         dbus_pending_call_set_notify(call, set_active_reply, network, NULL);
411
412         dbus_message_unref(message);
413
414         if (active == TRUE)
415                 return -EINPROGRESS;
416
417         return 0;
418 }
419
420 static int network_connect(struct connman_network *network)
421 {
422         if (connman_network_get_index(network) >= 0)
423                 return -EISCONN;
424
425         return set_network_active(network, TRUE);
426 }
427
428 static int network_disconnect(struct connman_network *network)
429 {
430         if (connman_network_get_index(network) < 0)
431                 return -ENOTCONN;
432
433         return set_network_active(network, FALSE);
434 }
435
436 static void network_remove(struct connman_network *network)
437 {
438         DBG("network %p", network);
439 }
440
441 static struct connman_network_driver network_driver = {
442         .name           = "network",
443         .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
444         .probe          = network_probe,
445         .remove         = network_remove,
446         .connect        = network_connect,
447         .disconnect     = network_disconnect,
448 };
449
450 static void add_network(struct connman_device *device, const char *path)
451 {
452         struct connman_network *network;
453         char *ident;
454
455         DBG("device %p path %s", device, path);
456
457         network = connman_device_get_network(device, path);
458         if (network != NULL)
459                 return;
460
461         ident = get_ident(path);
462
463         network = connman_network_create(ident,
464                                         CONNMAN_NETWORK_TYPE_CELLULAR);
465         if (network == NULL)
466                 return;
467
468         g_free(ident);
469
470         connman_network_set_string(network, "Path", path);
471         connman_network_set_available(network, TRUE);
472         connman_network_set_index(network, -1);
473         connman_device_add_network(device, network);
474 }
475
476 static void add_networks(struct connman_device *device, DBusMessageIter *array)
477 {
478         DBusMessageIter entry;
479
480         DBG("");
481
482         dbus_message_iter_recurse(array, &entry);
483
484         while (dbus_message_iter_get_arg_type(&entry) ==
485                                         DBUS_TYPE_OBJECT_PATH) {
486                 const char *path;
487
488                 dbus_message_iter_get_basic(&entry, &path);
489
490                 add_network(device, path);
491
492                 dbus_message_iter_next(&entry);
493         }
494 }
495
496 static void create_context_reply(DBusPendingCall *call, void *user_data)
497 {
498         DBusMessage *reply;
499         DBusError error;
500
501         DBG("");
502
503         dbus_error_init(&error);
504
505         reply = dbus_pending_call_steal_reply(call);
506
507         if (dbus_set_error_from_message(&error, reply)) {
508                 connman_error("%s", error.message);
509                 dbus_error_free(&error);
510         }
511
512         dbus_message_unref(reply);
513
514         dbus_pending_call_unref(call);
515 }
516
517 static void add_default_context(DBusMessageIter *array,
518                 const char *path, const char *name, const char *type)
519 {
520         DBusMessageIter entry;
521         DBusMessage *message;
522         DBusPendingCall *call;
523
524         if (path == NULL)
525                 return;
526
527         DBG("");
528
529         dbus_message_iter_recurse(array, &entry);
530
531         if (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_OBJECT_PATH)
532                 return;
533
534         DBG("path %s, name %s, type %s", path, name, type);
535
536         message = dbus_message_new_method_call(OFONO_SERVICE, path,
537                                         OFONO_GPRS_INTERFACE, CREATE_CONTEXT);
538         if (message == NULL)
539                 return;
540
541         dbus_message_set_auto_start(message, FALSE);
542
543         dbus_message_append_args(message, DBUS_TYPE_STRING,
544                                         &name, DBUS_TYPE_STRING,
545                                                 &type, DBUS_TYPE_INVALID);
546
547         if (dbus_connection_send_with_reply(connection, message,
548                                                 &call, TIMEOUT) == FALSE) {
549                 connman_error("Failed to create default context");
550                 dbus_message_unref(message);
551                 return;
552         }
553
554         if (call == NULL) {
555                 connman_error("D-Bus connection not available");
556                 dbus_message_unref(message);
557                 return;
558         }
559
560         dbus_pending_call_set_notify(call, create_context_reply, NULL, NULL);
561
562         dbus_message_unref(message);
563 }
564
565 static void check_networks_reply(DBusPendingCall *call, void *user_data)
566 {
567         struct connman_device *device = user_data;
568         DBusMessage *reply;
569         DBusMessageIter array, dict, contexts;
570         dbus_bool_t attached;
571
572         DBG("device %p", device);
573
574         reply = dbus_pending_call_steal_reply(call);
575
576         if (dbus_message_iter_init(reply, &array) == FALSE)
577                 goto done;
578
579         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
580                 goto done;
581
582         dbus_message_iter_recurse(&array, &dict);
583
584         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
585                 DBusMessageIter entry, value;
586                 const char *key;
587
588                 dbus_message_iter_recurse(&dict, &entry);
589                 dbus_message_iter_get_basic(&entry, &key);
590
591                 dbus_message_iter_next(&entry);
592                 dbus_message_iter_recurse(&entry, &value);
593
594                 DBG("key %s", key);
595
596                 if (g_str_equal(key, "Attached") == TRUE) {
597                         dbus_message_iter_get_basic(&value, &attached);
598                         DBG("Attached %d", attached);
599                 } else if (g_str_equal(key, "PrimaryContexts") == TRUE) {
600                         const char *path;
601
602                         path = connman_device_get_string(device, "Path");
603                         contexts = value;
604                         add_default_context(&contexts, path,
605                                         CONTEXT_NAME, CONTEXT_TYPE);
606                 } else if (g_str_equal(key, "Status") == TRUE) {
607                         const char *status;
608
609                         dbus_message_iter_get_basic(&value, &status);
610                         /* FIXME: add roaming support */
611                 } else if (g_str_equal(key, "Powered") == TRUE) {
612                         dbus_bool_t powered;
613
614                         dbus_message_iter_get_basic(&value, &powered);
615
616                         connman_device_set_powered(device, powered);
617                 }
618
619                 dbus_message_iter_next(&dict);
620         }
621
622         if (attached == TRUE)
623                 add_networks(device, &contexts);
624
625 done:
626         dbus_message_unref(reply);
627
628         dbus_pending_call_unref(call);
629 }
630
631 static void check_networks(struct modem_data *modem)
632 {
633         DBusMessage *message;
634         DBusPendingCall *call;
635         struct connman_device *device;
636
637         DBG("modem %p", modem);
638
639         if (modem == NULL)
640                 return;
641
642         device = modem->device;
643         if (device == NULL)
644                 return;
645
646         message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
647                                         OFONO_GPRS_INTERFACE, GET_PROPERTIES);
648         if (message == NULL)
649                 return;
650
651         dbus_message_set_auto_start(message, FALSE);
652
653         if (dbus_connection_send_with_reply(connection, message,
654                                                 &call, TIMEOUT) == FALSE) {
655                 connman_error("Failed to get ofono GPRS");
656                 goto done;
657         }
658
659         if (call == NULL) {
660                 connman_error("D-Bus connection not available");
661                 goto done;
662         }
663
664         dbus_pending_call_set_notify(call, check_networks_reply,
665                                                 (void *)device, NULL);
666
667 done:
668         dbus_message_unref(message);
669 }
670
671 static void add_device(const char *path, const char *imsi)
672 {
673         struct modem_data *modem;
674         struct connman_device *device;
675
676         DBG("path %s imsi %s", path, imsi);
677
678         if (path == NULL)
679                 return;
680
681         if (imsi == NULL)
682                 return;
683
684         modem = g_hash_table_lookup(modem_hash, path);
685         if (modem == NULL)
686                 return;
687
688         device = connman_device_create(imsi, CONNMAN_DEVICE_TYPE_CELLULAR);
689         if (device == NULL)
690                 return;
691
692         connman_device_set_ident(device, imsi);
693
694         connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE);
695
696         connman_device_set_string(device, "Path", path);
697
698         if (connman_device_register(device) < 0) {
699                 connman_device_unref(device);
700                 return;
701         }
702
703         modem->device = device;
704
705         check_networks(modem);
706 }
707
708 static void sim_properties_reply(DBusPendingCall *call, void *user_data)
709 {
710         const char *path = user_data;
711         DBusMessage *reply;
712         DBusMessageIter array, dict;
713
714         DBG("path %s", path);
715
716         reply = dbus_pending_call_steal_reply(call);
717
718         if (dbus_message_iter_init(reply, &array) == FALSE)
719                 goto done;
720
721         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
722                 goto done;
723
724         dbus_message_iter_recurse(&array, &dict);
725
726         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
727                 DBusMessageIter entry, value;
728                 const char *key, *imsi;
729
730                 dbus_message_iter_recurse(&dict, &entry);
731                 dbus_message_iter_get_basic(&entry, &key);
732
733                 dbus_message_iter_next(&entry);
734                 dbus_message_iter_recurse(&entry, &value);
735
736                 if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
737                         dbus_message_iter_get_basic(&value, &imsi);
738
739                         add_device(path, imsi);
740                 }
741
742                 dbus_message_iter_next(&dict);
743         }
744
745 done:
746         dbus_message_unref(reply);
747
748         dbus_pending_call_unref(call);
749 }
750
751 static void get_imsi(const char *path)
752 {
753         DBusMessage *message;
754         DBusPendingCall *call;
755
756         DBG("path %s", path);
757
758         message = dbus_message_new_method_call(OFONO_SERVICE, path,
759                                 OFONO_SIM_INTERFACE, GET_PROPERTIES);
760         if (message == NULL)
761                 return;
762
763         dbus_message_set_auto_start(message, FALSE);
764
765         if (dbus_connection_send_with_reply(connection, message,
766                                                 &call, TIMEOUT) == FALSE) {
767                 connman_error("Failed to get ofono modem sim");
768                 goto done;
769         }
770
771         if (call == NULL) {
772                 connman_error("D-Bus connection not available");
773                 goto done;
774         }
775
776         dbus_pending_call_set_notify(call, sim_properties_reply,
777                                                 (void *)path, NULL);
778
779 done:
780         dbus_message_unref(message);
781 }
782
783 static int modem_change_powered(const char *path, dbus_bool_t powered)
784 {
785         DBusMessage *message;
786         DBusMessageIter iter;
787         DBusPendingCall *call;
788
789         DBG("path %s powered %d", path, powered);
790
791         if (path == NULL)
792                 return -EINVAL;
793
794         message = dbus_message_new_method_call(OFONO_SERVICE, path,
795                                         OFONO_MODEM_INTERFACE, SET_PROPERTY);
796         if (message == NULL)
797                 return -ENOMEM;
798
799         dbus_message_set_auto_start(message, FALSE);
800
801         dbus_message_iter_init_append(message, &iter);
802         connman_dbus_property_append_basic(&iter, "Powered",
803                                                 DBUS_TYPE_BOOLEAN, &powered);
804
805         if (dbus_connection_send_with_reply(connection, message,
806                                                 &call, TIMEOUT) == FALSE) {
807                 connman_error("Failed to change powered property");
808                 dbus_message_unref(message);
809                 return -EINVAL;
810         }
811
812         if (call == NULL) {
813                 connman_error("D-Bus connection not available");
814                 dbus_message_unref(message);
815                 return -EINVAL;
816         }
817
818         dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
819
820         dbus_message_unref(message);
821
822         return -EINPROGRESS;
823 }
824
825 static struct modem_data *add_modem(const char *path)
826 {
827         struct modem_data *modem;
828
829         if (path == NULL)
830                 return NULL;
831
832         modem = g_hash_table_lookup(modem_hash, path);
833         if (modem != NULL) {
834                 modem->available = TRUE;
835
836                 return modem;
837         }
838
839         modem = g_try_new0(struct modem_data, 1);
840         if (modem == NULL)
841                 return NULL;
842
843         modem->path = g_strdup(path);
844         modem->device = NULL;
845         modem->available = TRUE;
846
847         g_hash_table_insert(modem_hash, g_strdup(path), modem);
848
849         return modem;
850 }
851
852 static gboolean modem_has_gprs(DBusMessageIter *array)
853 {
854         DBusMessageIter entry;
855
856         dbus_message_iter_recurse(array, &entry);
857
858         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
859                 const char *interface;
860
861                 dbus_message_iter_get_basic(&entry, &interface);
862
863                 if (g_strcmp0(OFONO_GPRS_INTERFACE, interface) == 0)
864                         return TRUE;
865
866                 dbus_message_iter_next(&entry);
867         }
868
869         return FALSE;
870 }
871
872 static void modem_properties_reply(DBusPendingCall *call, void *user_data)
873 {
874         DBusMessage *reply;
875         DBusMessageIter array, dict;
876         const char *path = user_data;
877
878         DBG("path %s", path);
879
880         reply = dbus_pending_call_steal_reply(call);
881
882         if (dbus_message_iter_init(reply, &array) == FALSE)
883                 goto done;
884
885         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
886                 goto done;
887
888         dbus_message_iter_recurse(&array, &dict);
889
890         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
891                 DBusMessageIter entry, value;
892                 const char *key;
893                 dbus_bool_t powered;
894
895                 dbus_message_iter_recurse(&dict, &entry);
896                 dbus_message_iter_get_basic(&entry, &key);
897
898                 dbus_message_iter_next(&entry);
899                 dbus_message_iter_recurse(&entry, &value);
900
901                 if (g_str_equal(key, "Powered") == TRUE) {
902                         dbus_message_iter_get_basic(&value, &powered);
903
904                         if (powered == FALSE) {
905                                 modem_change_powered(path, TRUE);
906                                 break;
907                         }
908                 } else if (g_str_equal(key, "Interface") == TRUE) {
909                         if (modem_has_gprs(&value) == TRUE)
910                                 get_imsi(path);
911                 }
912
913                 dbus_message_iter_next(&dict);
914         }
915
916 done:
917         dbus_message_unref(reply);
918
919         dbus_pending_call_unref(call);
920 }
921
922 static void get_modem_properties(struct modem_data *modem)
923 {
924         DBusMessage *message;
925         DBusPendingCall *call;
926
927         DBG("path %s", modem->path);
928
929         if (modem->path == NULL)
930                 return;
931
932         message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
933                                 OFONO_MODEM_INTERFACE, GET_PROPERTIES);
934         if (message == NULL)
935                 return;
936
937         dbus_message_set_auto_start(message, FALSE);
938
939         if (dbus_connection_send_with_reply(connection, message,
940                                                 &call, TIMEOUT) == FALSE) {
941                 connman_error("Failed to get ofono modem");
942                 goto done;
943         }
944
945         if (call == NULL) {
946                 connman_error("D-Bus connection not available");
947                 goto done;
948         }
949
950         dbus_pending_call_set_notify(call, modem_properties_reply,
951                                                 (void *)modem->path, NULL);
952
953 done:
954         dbus_message_unref(message);
955 }
956
957 static void mask_unavailable(gpointer key, gpointer value, gpointer user_data)
958 {
959         struct modem_data *modem = value;
960
961         modem->available = FALSE;
962 }
963
964 static void modems_set_unavailable()
965 {
966         g_hash_table_foreach(modem_hash, mask_unavailable, NULL);
967 }
968
969 static void cleanup_modem(gpointer key, gpointer value, gpointer user_data)
970 {
971         struct modem_data *modem = value;
972
973         if (modem->available == FALSE)
974                 g_hash_table_remove(modem_hash, key);
975 }
976
977 static void cleanup_modems()
978 {
979         g_hash_table_foreach(modem_hash, cleanup_modem, NULL);
980 }
981
982 static void update_modems(DBusMessageIter *array)
983 {
984         DBusMessageIter entry;
985
986         dbus_message_iter_recurse(array, &entry);
987
988         modems_set_unavailable();
989
990         while (dbus_message_iter_get_arg_type(&entry) ==
991                                         DBUS_TYPE_OBJECT_PATH) {
992                 const char *path;
993                 struct modem_data *modem;
994
995                 dbus_message_iter_get_basic(&entry, &path);
996
997                 modem = add_modem(path);
998                 if (modem != NULL)
999                         get_modem_properties(modem);
1000
1001                 dbus_message_iter_next(&entry);
1002         }
1003
1004         cleanup_modems();
1005 }
1006
1007 static void manager_properties_reply(DBusPendingCall *call, void *user_data)
1008 {
1009         DBusMessage *reply;
1010         DBusMessageIter array, dict;
1011
1012         DBG("");
1013
1014         reply = dbus_pending_call_steal_reply(call);
1015
1016         if (dbus_message_iter_init(reply, &array) == FALSE)
1017                 goto done;
1018
1019         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
1020                 goto done;
1021
1022         dbus_message_iter_recurse(&array, &dict);
1023
1024         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1025                 DBusMessageIter entry, value;
1026                 const char *key;
1027
1028                 dbus_message_iter_recurse(&dict, &entry);
1029                 dbus_message_iter_get_basic(&entry, &key);
1030
1031                 dbus_message_iter_next(&entry);
1032                 dbus_message_iter_recurse(&entry, &value);
1033
1034                 if (g_str_equal(key, "Modems") == TRUE) {
1035                         update_modems(&value);
1036                         break;
1037                 }
1038
1039                 dbus_message_iter_next(&dict);
1040         }
1041
1042 done:
1043         dbus_message_unref(reply);
1044
1045         dbus_pending_call_unref(call);
1046 }
1047
1048 static void modem_remove_device(struct modem_data *modem)
1049 {
1050         if (modem->device == NULL)
1051                 return;
1052
1053         connman_device_unregister(modem->device);
1054         connman_device_unref(modem->device);
1055
1056         modem->device = NULL;
1057 }
1058
1059 static void remove_modem(gpointer data)
1060 {
1061         struct modem_data *modem = data;
1062
1063         g_free(modem->path);
1064
1065         modem_remove_device(modem);
1066
1067         g_free(modem);
1068 }
1069
1070 static void ofono_connect(DBusConnection *connection, void *user_data)
1071 {
1072         DBusMessage *message;
1073         DBusPendingCall *call;
1074
1075         DBG("connection %p", connection);
1076
1077         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1078                                                 g_free, remove_modem);
1079
1080         message = dbus_message_new_method_call(OFONO_SERVICE, "/",
1081                                 OFONO_MANAGER_INTERFACE, GET_PROPERTIES);
1082         if (message == NULL)
1083                 return;
1084
1085         dbus_message_set_auto_start(message, FALSE);
1086
1087         if (dbus_connection_send_with_reply(connection, message,
1088                                                 &call, TIMEOUT) == FALSE) {
1089                 connman_error("Failed to get ofono modems");
1090                 goto done;
1091         }
1092
1093         if (call == NULL) {
1094                 connman_error("D-Bus connection not available");
1095                 goto done;
1096         }
1097
1098         dbus_pending_call_set_notify(call, manager_properties_reply,
1099                                                                 NULL, NULL);
1100
1101 done:
1102         dbus_message_unref(message);
1103
1104 }
1105
1106 static void ofono_disconnect(DBusConnection *connection, void *user_data)
1107 {
1108         DBG("connection %p", connection);
1109
1110         if (modem_hash == NULL)
1111                 return;
1112
1113         g_hash_table_destroy(modem_hash);
1114
1115         modem_hash = NULL;
1116 }
1117
1118 static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
1119                                 void *user_data)
1120 {
1121         const char *path = dbus_message_get_path(message);
1122         struct modem_data *modem;
1123         DBusMessageIter iter, value;
1124         const char *key;
1125
1126         DBG("path %s", path);
1127
1128         modem = g_hash_table_lookup(modem_hash, path);
1129         if (modem == NULL)
1130                 return TRUE;
1131
1132         if (dbus_message_iter_init(message, &iter) == FALSE)
1133                 return TRUE;
1134
1135         dbus_message_iter_get_basic(&iter, &key);
1136
1137         dbus_message_iter_next(&iter);
1138         dbus_message_iter_recurse(&iter, &value);
1139
1140         if (g_str_equal(key, "Powered") == TRUE) {
1141                 dbus_bool_t powered;
1142
1143                 dbus_message_iter_get_basic(&value, &powered);
1144                 if (powered == TRUE)
1145                         return TRUE;
1146
1147                 modem_remove_device(modem);
1148         } else if (g_str_equal(key, "Interfaces") == TRUE) {
1149                 if (modem_has_gprs(&value) == TRUE) {
1150                         if (modem->device == NULL)
1151                                 get_imsi(modem->path);
1152                 } else if (modem->device != NULL)
1153                         modem_remove_device(modem);
1154         }
1155
1156         return TRUE;
1157 }
1158
1159 static gboolean gprs_changed(DBusConnection *connection, DBusMessage *message,
1160                                 void *user_data)
1161 {
1162         const char *path = dbus_message_get_path(message);
1163         struct modem_data *modem;
1164         DBusMessageIter iter, value;
1165         const char *key;
1166
1167         DBG("path %s", path);
1168
1169         modem = g_hash_table_lookup(modem_hash, path);
1170         if (modem == NULL)
1171                 return TRUE;
1172
1173         if (dbus_message_iter_init(message, &iter) == FALSE)
1174                 return TRUE;
1175
1176         dbus_message_iter_get_basic(&iter, &key);
1177
1178         dbus_message_iter_next(&iter);
1179         dbus_message_iter_recurse(&iter, &value);
1180
1181         if (g_str_equal(key, "Attached") == TRUE) {
1182                 dbus_bool_t attached;
1183
1184                 dbus_message_iter_get_basic(&value, &attached);
1185
1186                 DBG("Attached %d", attached);
1187
1188                 if (attached == TRUE)
1189                         check_networks(modem);
1190                 else if (modem->device != NULL)
1191                         connman_device_remove_all_networks(modem->device);
1192
1193         } else if (g_str_equal(key, "Status") == TRUE) {
1194                 const char *status;
1195                 dbus_message_iter_get_basic(&value, &status);
1196
1197                 DBG("status %s", status);
1198
1199                 /* FIXME: add roaming support */
1200         } else if (g_str_equal(key, "PrimaryContexts") == TRUE) {
1201                 check_networks(modem);
1202         } else if (g_str_equal(key, "Powered") == TRUE) {
1203                 dbus_bool_t powered;
1204
1205                 if (modem->device == NULL)
1206                         return TRUE;
1207
1208                 dbus_message_iter_get_basic(&value, &powered);
1209                 connman_device_set_powered(modem->device, powered);
1210         }
1211
1212         return TRUE;
1213 }
1214
1215 static gboolean manager_changed(DBusConnection *connection,
1216                                 DBusMessage *message, void *user_data)
1217 {
1218         const char *path = dbus_message_get_path(message);
1219         DBusMessageIter iter, value;
1220         const char *key;
1221
1222         DBG("path %s", path);
1223
1224         if (dbus_message_iter_init(message, &iter) == FALSE)
1225                 return TRUE;
1226
1227         dbus_message_iter_get_basic(&iter, &key);
1228
1229         dbus_message_iter_next(&iter);
1230         dbus_message_iter_recurse(&iter, &value);
1231
1232         if (g_str_equal(key, "Modems") == TRUE)
1233                 update_modems(&value);
1234
1235         return TRUE;
1236 }
1237
1238 static void get_dns(DBusMessageIter *array, struct connman_element *parent)
1239 {
1240         DBusMessageIter entry;
1241         gchar *nameserver = NULL, *nameserver_old = NULL;
1242
1243         DBG("");
1244
1245         dbus_message_iter_recurse(array, &entry);
1246
1247         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
1248                 const char *dns;
1249
1250                 dbus_message_iter_get_basic(&entry, &dns);
1251
1252                 DBG("dns %s", dns);
1253
1254                 if (nameserver == NULL) {
1255
1256                         nameserver = g_strdup(dns);
1257                 } else {
1258
1259                         nameserver_old = nameserver;
1260                         nameserver = g_strdup_printf("%s %s",
1261                                                 nameserver_old, dns);
1262                         g_free(nameserver_old);
1263                 }
1264
1265                 dbus_message_iter_next(&entry);
1266         }
1267
1268         parent->ipv4.nameserver = nameserver;
1269 }
1270
1271 static void update_settings(DBusMessageIter *array,
1272                         struct connman_element *parent)
1273 {
1274         DBusMessageIter dict;
1275         const char *interface = NULL;
1276
1277         DBG("");
1278
1279         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
1280                 return;
1281
1282         dbus_message_iter_recurse(array, &dict);
1283
1284         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1285                 DBusMessageIter entry, value;
1286                 const char *key;
1287
1288                 dbus_message_iter_recurse(&dict, &entry);
1289                 dbus_message_iter_get_basic(&entry, &key);
1290
1291                 dbus_message_iter_next(&entry);
1292                 dbus_message_iter_recurse(&entry, &value);
1293
1294                 if (g_str_equal(key, "Interface") == TRUE) {
1295                         int index;
1296
1297                         dbus_message_iter_get_basic(&value, &interface);
1298
1299                         DBG("interface %s", interface);
1300
1301                         index = connman_inet_ifindex(interface);
1302                         if (index >= 0) {
1303                                 connman_network_set_index(
1304                                         pending_network, index);
1305                         } else {
1306                                 connman_error("Can not find interface %s",
1307                                                                 interface);
1308                                 break;
1309                         }
1310                 } else if (g_str_equal(key, "Method") == TRUE) {
1311                         const char *method;
1312
1313                         dbus_message_iter_get_basic(&value, &method);
1314                         if (g_strcmp0(method, "static") == 0) {
1315
1316                                 parent->ipv4.method =
1317                                         CONNMAN_IPCONFIG_METHOD_FIXED;
1318                         } else if (g_strcmp0(method, "dhcp") == 0) {
1319
1320                                 parent->ipv4.method =
1321                                         CONNMAN_IPCONFIG_METHOD_DHCP;
1322                                 break;
1323                         }
1324                 } else if (g_str_equal(key, "Address") == TRUE) {
1325                         const char *address;
1326
1327                         dbus_message_iter_get_basic(&value, &address);
1328
1329                         DBG("address %s", address);
1330
1331                         parent->ipv4.address = g_strdup(address);
1332                 } else if (g_str_equal(key, "Netmask") == TRUE) {
1333                         const char *netmask;
1334
1335                         dbus_message_iter_get_basic(&value, &netmask);
1336
1337                         DBG("netmask %s", netmask);
1338
1339                         parent->ipv4.netmask = g_strdup(netmask);
1340                 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
1341
1342                         get_dns(&value, parent);
1343                 } else if (g_str_equal(key, "Gateway") == TRUE) {
1344                         const char *gateway;
1345
1346                         dbus_message_iter_get_basic(&value, &gateway);
1347
1348                         DBG("gateway %s", gateway);
1349
1350                         parent->ipv4.gateway = g_strdup(gateway);
1351                 }
1352
1353                 dbus_message_iter_next(&dict);
1354         }
1355
1356         /* deactive, oFono send NULL inteface before deactive signal */
1357         if (interface == NULL)
1358                 connman_network_set_index(pending_network, -1);
1359 }
1360
1361 static void cleanup_ipconfig(struct connman_element *parent)
1362 {
1363         g_free(parent->ipv4.address);
1364         parent->ipv4.address = NULL;
1365
1366         g_free(parent->ipv4.netmask);
1367         parent->ipv4.netmask = NULL;
1368
1369         g_free(parent->ipv4.nameserver);
1370         parent->ipv4.nameserver = NULL;
1371
1372         g_free(parent->ipv4.gateway);
1373         parent->ipv4.gateway = NULL;
1374
1375         parent->ipv4.method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
1376 }
1377
1378 static int static_network_set_connected(
1379                 struct connman_network *pending_network,
1380                                 struct connman_element *parent,
1381                                         connman_bool_t connected)
1382 {
1383         if (connected == TRUE) {
1384                 struct connman_element *element;
1385
1386                 if (parent->ipv4.address == NULL)
1387                         goto failed;
1388
1389                 if (parent->ipv4.netmask == NULL)
1390                         goto failed;
1391
1392                 element = connman_element_create(NULL);
1393                 if (element == NULL) {
1394                         connman_error("Can not create connman_element");
1395                         return -ENOMEM;
1396                 }
1397
1398                 element->type = CONNMAN_ELEMENT_TYPE_IPV4;
1399                 element->index = parent->index;
1400
1401                 if (connman_element_register(element, parent) < 0) {
1402                         connman_element_unref(element);
1403                         goto failed;
1404                 }
1405         } else
1406                 cleanup_ipconfig(parent);
1407
1408         connman_network_set_connected(pending_network, connected);
1409
1410         return 0;
1411
1412 failed:
1413         connman_network_set_error(pending_network,
1414                 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
1415
1416         cleanup_ipconfig(parent);
1417
1418         return -EINVAL;
1419 }
1420
1421 static gboolean pri_context_changed(DBusConnection *connection,
1422                                         DBusMessage *message, void *user_data)
1423 {
1424         const char *path = dbus_message_get_path(message);
1425         struct connman_element *parent;
1426         const char *pending_path;
1427         DBusMessageIter iter, value;
1428         const char *key;
1429
1430         DBG("pending_network %p, path %s", pending_network, path);
1431
1432         if (pending_network == NULL)
1433                 return TRUE;
1434
1435         pending_path = connman_network_get_string(pending_network, "Path");
1436         if (g_strcmp0(pending_path, path) != 0)
1437                 return TRUE;
1438
1439         parent = connman_network_get_element(pending_network);
1440
1441         if (dbus_message_iter_init(message, &iter) == FALSE)
1442                 return TRUE;
1443
1444         dbus_message_iter_get_basic(&iter, &key);
1445
1446         dbus_message_iter_next(&iter);
1447         dbus_message_iter_recurse(&iter, &value);
1448
1449         if (g_str_equal(key, "Settings") == TRUE) {
1450
1451                 update_settings(&value, parent);
1452         } else if (g_str_equal(key, "Active") == TRUE) {
1453                 dbus_bool_t active;
1454
1455                 dbus_message_iter_get_basic(&value, &active);
1456
1457                 switch (parent->ipv4.method) {
1458                 case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
1459                 case CONNMAN_IPCONFIG_METHOD_OFF:
1460                 case CONNMAN_IPCONFIG_METHOD_MANUAL:
1461                         break;
1462                 case CONNMAN_IPCONFIG_METHOD_FIXED:
1463                         if (static_network_set_connected(
1464                                         pending_network, parent, active) < 0)
1465                                 set_network_active(pending_network, FALSE);
1466                         break;
1467                 case CONNMAN_IPCONFIG_METHOD_DHCP:
1468                         connman_network_set_method(pending_network,
1469                                                 CONNMAN_IPCONFIG_METHOD_DHCP);
1470                         connman_network_set_connected(pending_network, active);
1471                         break;
1472                 }
1473
1474                 pending_network = NULL;
1475         }
1476
1477         return TRUE;
1478 }
1479
1480 static guint watch;
1481 static guint gprs_watch;
1482 static guint modem_watch;
1483 static guint manager_watch;
1484 static guint context_watch;
1485
1486 static int ofono_init(void)
1487 {
1488         int err;
1489
1490         connection = connman_dbus_get_connection();
1491         if (connection == NULL)
1492                 return -EIO;
1493
1494         watch = g_dbus_add_service_watch(connection, OFONO_SERVICE,
1495                         ofono_connect, ofono_disconnect, NULL, NULL);
1496
1497         gprs_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1498                                                 OFONO_GPRS_INTERFACE,
1499                                                 PROPERTY_CHANGED,
1500                                                 gprs_changed,
1501                                                 NULL, NULL);
1502
1503         modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1504                                                 OFONO_MODEM_INTERFACE,
1505                                                 PROPERTY_CHANGED,
1506                                                 modem_changed,
1507                                                 NULL, NULL);
1508
1509         manager_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1510                                                 OFONO_MANAGER_INTERFACE,
1511                                                 PROPERTY_CHANGED,
1512                                                 manager_changed,
1513                                                 NULL, NULL);
1514
1515         context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1516                                                 OFONO_PRI_CONTEXT_INTERFACE,
1517                                                 PROPERTY_CHANGED,
1518                                                 pri_context_changed,
1519                                                 NULL, NULL);
1520
1521         if (watch == 0 || gprs_watch == 0 || modem_watch == 0 ||
1522                         manager_watch == 0 || context_watch == 0) {
1523                 err = -EIO;
1524                 goto remove;
1525         }
1526
1527         err = connman_network_driver_register(&network_driver);
1528         if (err < 0)
1529                 goto remove;
1530
1531         err = connman_device_driver_register(&modem_driver);
1532         if (err < 0) {
1533                 connman_network_driver_unregister(&network_driver);
1534                 goto remove;
1535         }
1536
1537         return 0;
1538
1539 remove:
1540         g_dbus_remove_watch(connection, watch);
1541         g_dbus_remove_watch(connection, gprs_watch);
1542         g_dbus_remove_watch(connection, modem_watch);
1543         g_dbus_remove_watch(connection, manager_watch);
1544         g_dbus_remove_watch(connection, context_watch);
1545
1546         dbus_connection_unref(connection);
1547
1548         return err;
1549 }
1550
1551 static void ofono_exit(void)
1552 {
1553         g_dbus_remove_watch(connection, watch);
1554         g_dbus_remove_watch(connection, gprs_watch);
1555         g_dbus_remove_watch(connection, modem_watch);
1556         g_dbus_remove_watch(connection, manager_watch);
1557         g_dbus_remove_watch(connection, context_watch);
1558
1559         ofono_disconnect(connection, NULL);
1560
1561         connman_device_driver_unregister(&modem_driver);
1562         connman_network_driver_unregister(&network_driver);
1563
1564         dbus_connection_unref(connection);
1565 }
1566
1567 CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
1568                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)