Cut a release to delay phonestack initialization till it is needed
[profile/ivi/sockdrawer.git] / main.c
1 #include <Ecore.h>
2 #include <E_DBus.h>
3 #include <Efreet.h>
4 #include <Eina.h>
5 #include <getopt.h>
6 #include <jansson.h>
7 #include <libwebsockets.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <sys/inotify.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13
14 enum sockdrawer_protocols {
15     PROTOCOL_HTTP = 0,
16     PROTOCOL_APP_LIST,
17     PROTOCOL_DIALER,
18     SOCKDRAWER_PROTOCOL_COUNT
19 };
20
21 #ifdef INSTALL_DATADIR
22 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/sockdrawer"
23 #else
24 #define LOCAL_RESOURCE_PATH "."
25 #endif
26
27 #define DEFAULTCONTENT LOCAL_RESOURCE_PATH"/index.html"
28 #define DEFAULTICON LOCAL_RESOURCE_PATH"/default.png"
29
30 #define APPDIR "/usr/share/applications"
31
32 struct libwebsocket_context *context;
33
34 E_DBus_Connection *bus;
35
36 json_t *app_list = NULL;
37 char *apps_directory = NULL;
38
39 char *content_directory = NULL;
40
41 json_t *event_queue = NULL;
42
43 char *voicecallmanager_path = NULL;
44
45 #define MAX_POLL_ELEMENTS 100
46 struct pollfd pollfds[100];
47 int count_pollfds = 0;
48
49 void handle_command(json_t *o);
50 static int callback_http(struct libwebsocket_context *context,
51                          struct libwebsocket *wsi,
52                          enum libwebsocket_callback_reasons reason, void *user,
53                          void *in, size_t len);
54 void send_app_list(struct libwebsocket *socket);
55 static int callback_app_list(struct libwebsocket_context *context,
56                              struct libwebsocket *wsi,
57                              enum libwebsocket_callback_reasons reason,
58                              void *user, void *in, size_t len);
59 static Eina_Bool get_websocket_event(void *data);
60 static int callback_dialer(struct libwebsocket_context *context,
61                            struct libwebsocket *wsi,
62                            enum libwebsocket_callback_reasons reason,
63                            void *user, void *in, size_t len);
64
65 void set_powered_cb(void *data, DBusMessage *reply, DBusError *error);
66 static Eina_Bool power_on_modem(void *data);
67 void setup_new_modem(DBusMessageIter entry);
68
69 void initialize_ofono(void);
70
71 /*
72  * Per instance data
73  */
74 struct per_session_data__app_list {
75     int placeholder;
76 };
77 struct per_session_data__dialer {
78     int placeholder;
79 };
80
81 static struct libwebsocket_protocols protocols[] = {
82     {
83         "http-only",
84         callback_http,
85         0
86     },
87    {
88         "app-list-protocol",
89         callback_app_list,
90         sizeof(struct per_session_data__app_list),
91     },
92    {
93         "dialer-protocol",
94         callback_dialer,
95         sizeof(struct per_session_data__dialer),
96     },
97     {
98         NULL, NULL, 0
99     }
100 };
101
102 /* libwebsocket callback for handling normal http request. We use this */
103 /* to implementa a rudamentary webserver. */
104 static int
105 callback_http(struct libwebsocket_context *context,
106               struct libwebsocket *wsi,
107               enum libwebsocket_callback_reasons reason, void *user,
108               void *in, size_t len)
109 {
110     char client_name[128];
111     char client_ip[128];
112     char filename[256];
113     int n;
114     struct stat info;
115
116     switch (reason) {
117     case LWS_CALLBACK_HTTP:
118         if (in && ((char *)in)[0] == '/' && !strstr(in, "..")) {
119             if (strstr(in, "/icon/") == in) {
120                 /* virtual directory used for serving up system icons */
121
122                 char *icon = (char *)(in + 6);
123                 const char *path = efreet_icon_path_find(getenv("E_ICON_THEME"), 
124                                                          (const char *)icon, 128);
125                 if (!path) {
126                     path = efreet_icon_path_find("default", icon, 128);
127                     if (!path) {
128                         path = efreet_icon_path_find("hicolor", icon, 128);
129                         if (!path) {
130                             if (icon[0] == '/' && !stat(icon, &info) && S_ISREG(info.st_mode))
131                                 path = icon;
132                             else {
133                                 fprintf(stderr, "Can not find icon: %s\n", icon);
134                                 path = DEFAULTICON;
135                             }
136                         }
137                     }
138                 }
139
140                 libwebsockets_serve_http_file(wsi, path, 
141                                               (const char *)efreet_mime_type_get((char *)path));
142             } else {
143                 snprintf(filename, 256, "%s/%s", content_directory, (char *)in + 1);
144                 if (!stat(filename, &info) && S_ISDIR(info.st_mode))
145                     /* try serving up index.html */
146                     snprintf(filename, 256, "%s/%s/index.html", 
147                              content_directory, (char *)in + 1);
148
149                 if (!stat(filename, &info) && S_ISREG(info.st_mode)) {
150                     libwebsockets_serve_http_file(wsi, filename, 
151                                                   (const char *)efreet_mime_type_get(filename));
152                 } else {
153                     /* TODO: Serve up error html page */
154                     fprintf(stderr, "Unable to find %s\n", filename);
155                 }
156             }
157         } else {
158             fprintf(stderr, "Invalid request \'%s\'\n", (char *)in);
159         }
160         break;
161
162     case LWS_CALLBACK_ADD_POLL_FD:
163         pollfds[count_pollfds].fd = (int)(long)user;
164         pollfds[count_pollfds].events = (int)len;
165         pollfds[count_pollfds++].revents = 0;
166         break;
167
168     case LWS_CALLBACK_DEL_POLL_FD:
169         for (n = 0; n < count_pollfds; n++)
170             if (pollfds[n].fd == (int)(long)user)
171                 while (n < count_pollfds) {
172                     pollfds[n] = pollfds[n + 1];
173                     n++;
174                 }
175         count_pollfds--;
176         break;
177
178     case LWS_CALLBACK_SET_MODE_POLL_FD:
179         for (n = 0; n < count_pollfds; n++)
180             if (pollfds[n].fd == (int)(long)user)
181                 pollfds[n].events |= (int)(long)len;
182         break;
183
184     case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
185         for (n = 0; n < count_pollfds; n++)
186             if (pollfds[n].fd == (int)(long)user)
187                 pollfds[n].events &= ~(int)(long)len;
188         break;
189
190     default:
191         break;
192     }
193     
194     return 0;
195 }
196
197 void
198 dial_reply(void *data, DBusMessage *reply, DBusError *error)
199 {
200     if (dbus_error_is_set(error)) {
201         fprintf(stderr, "Error: %s - %s\n", error->name, error->message);
202         return;
203     }
204 }
205
206 /* libwebsocket callback for the 'protocol-dialer' protocol */
207 static int
208 callback_dialer(struct libwebsocket_context *context,
209                 struct libwebsocket *wsi,
210                 enum libwebsocket_callback_reasons reason,
211                 void *user, void *in, size_t len)
212 {
213     json_t *o = NULL;
214     int i;
215
216     switch (reason) {
217     case LWS_CALLBACK_BROADCAST:
218         /*
219          * This is triggered on all open websocket connections
220          * when libwebsocket_broadcast is called. For this
221          * protocol we ignore the 'in' paramter and send out
222          * the pending event queue
223          */
224         if (in) {
225             unsigned char *b = malloc(LWS_SEND_BUFFER_PRE_PADDING +
226                                       strlen((char *)in) +
227                                       LWS_SEND_BUFFER_POST_PADDING);
228             unsigned char *p = b + LWS_SEND_BUFFER_PRE_PADDING;
229             int n = sprintf((char *)p, "%s", (char *)in);
230
231             libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
232
233             free(b);
234         }
235         break;
236
237     case LWS_CALLBACK_ESTABLISHED:
238         initialize_ofono();
239         if (voicecallmanager_path) {
240             /* inform any connections about the addition of the modem */
241             json_t *o = json_object();
242
243             json_object_set(o, "type", json_string("ModemAdded"));
244             json_object_set(o, "id", json_string(voicecallmanager_path));
245             char *dump = json_dumps(o, 0);
246
247             unsigned char *b = malloc(LWS_SEND_BUFFER_PRE_PADDING +
248                                       strlen(dump) +
249                                       LWS_SEND_BUFFER_POST_PADDING);
250             unsigned char *p = b + LWS_SEND_BUFFER_PRE_PADDING;
251             int n = sprintf((char *)p, "%s", dump);
252
253             libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
254             free(b);
255             free(dump);
256         }
257
258     case LWS_CALLBACK_RECEIVE:
259         /*
260          * The other end of the websocket is sending us text
261          * so attempt to parse that text as a JSON object providing
262          * either a single command object or an array of command
263          * objects.
264          */
265         if (in && (o = json_loads((char *)in, 0, NULL))) {
266             if (json_is_array(o)) {
267                 for (i = 0; i < json_array_size(o); i++)
268                     handle_command(json_array_get(o, i));
269                 json_array_clear(o);
270             } else {
271                 handle_command(o);
272                 json_object_clear(o);
273             }
274         }
275         break;
276
277     default:
278         break;
279     }
280
281     return 0;
282 }
283
284 /* Write a serialized JSON object containing */
285 /* a list of objects each describing an application */
286 void send_app_list(struct libwebsocket *socket) 
287 {
288     /* 
289      * WARNING: libwebsockets requires prefixing and post
290      *          fixing the send buffer with enough space
291      *          for the library to insert required websocket
292      *          protocol information.
293      *          Ignoring this will result in segfaults.
294      */
295     char *dump = json_dumps(app_list, 0);
296     unsigned char *b = malloc(LWS_SEND_BUFFER_PRE_PADDING +
297                               strlen(dump) +
298                               LWS_SEND_BUFFER_POST_PADDING);
299     unsigned char *p = b + LWS_SEND_BUFFER_PRE_PADDING;
300     int n = sprintf((char *)p, "%s", dump);
301
302     libwebsocket_write(socket, p, n, LWS_WRITE_TEXT);
303
304     free(dump);
305     free(b);
306 }
307
308 /* command object parser */
309 void handle_command(json_t *o)
310 {
311     const char *cmd = json_string_value(json_object_get(o, "cmd"));
312     if (!cmd)
313         return;
314     
315     json_t *tmp;
316     if (strncmp(cmd, "launch", 6) == 0 && (tmp = json_object_get(o, "index"))) {
317         int index = json_integer_value(tmp);
318         json_t *app = json_array_get(app_list, index);
319         if (!app)
320             return;
321
322         json_t *exec = json_object_get(app, "exec");
323         if (!exec)
324             return;
325
326         ecore_exe_run(json_string_value(exec), NULL);
327     } else if (strncmp(cmd, "makeCall", 8) == 0 &&
328                (tmp = json_object_get(o, "remotePartyId"))) {
329         const char *remotePartyId = json_string_value(tmp);
330         const char *hideCallerId = "";
331
332         DBusMessage *msg;
333         msg = dbus_message_new_method_call(
334                                            "org.ofono",
335                                            voicecallmanager_path,
336                                            "org.ofono.VoiceCallManager",
337                                            "Dial"
338                                            );
339         dbus_message_append_args(msg,
340                                  DBUS_TYPE_STRING, &remotePartyId,
341                                  DBUS_TYPE_INVALID);
342         dbus_message_append_args(msg,
343                                  DBUS_TYPE_STRING, &hideCallerId,
344                                  DBUS_TYPE_INVALID);
345         e_dbus_message_send(bus, msg, dial_reply, -1, NULL);
346     } else if (strncmp(cmd, "end", 3) == 0) {
347         const char *path = json_string_value(json_object_get(o, "path"));
348
349         DBusMessage *msg;
350         msg = dbus_message_new_method_call(
351                                            "org.ofono",
352                                            path,
353                                            "org.ofono.VoiceCall",
354                                            "Hangup"
355                                            );
356         e_dbus_message_send(bus, msg, dial_reply, -1, NULL);
357
358     } else if (strncmp(cmd, "accept", 6) == 0) {
359         const char *path = json_string_value(json_object_get(o, "path"));
360         
361         DBusMessage *msg;
362         msg = dbus_message_new_method_call(
363                                            "org.ofono",
364                                            path,
365                                            "org.ofono.VoiceCall",
366                                            "Answer"
367                                            );
368         e_dbus_message_send(bus, msg, dial_reply, -1, NULL);        
369     }
370 }
371
372 /* libwebsocket callback for 'protocol-app-list' based websockets */
373 static int
374 callback_app_list(struct libwebsocket_context *context,
375                   struct libwebsocket *wsi,
376                   enum libwebsocket_callback_reasons reason,
377                   void *user, void *in, size_t len)
378 {
379     json_t *o = NULL;
380     int i;
381
382     switch (reason) {
383     case LWS_CALLBACK_BROADCAST:
384         /*
385          * This is triggered on all open websocket connections
386          * when libwebsocket_broadcast is called. For this
387          * protocol we only do one possible thing on broadcast
388          * so just ignore the incoming 'in' string and just
389          * send a new list of applications.
390          */
391     case LWS_CALLBACK_ESTABLISHED:
392         send_app_list(wsi);
393         break;
394
395     case LWS_CALLBACK_RECEIVE:
396         /*
397          * The other end of the websocket is sending us text
398          * so attempt to parse that text as a JSON object providing
399          * either a single command object or an array of command
400          * objects.
401          */
402         if (in && (o = json_loads((char *)in, 0, NULL))) {
403             if (json_is_array(o)) {
404                 for (i = 0; i < json_array_size(o); i++)
405                     handle_command(json_array_get(o, i));
406                 json_array_clear(o);
407             } else {
408                 handle_command(o);
409                 json_object_clear(o);
410             }
411         }
412         break;
413
414     default:
415         break;
416     }
417
418     return 0;
419 }
420
421 void build_app_list(char *dir) {
422     Eina_Iterator *it;
423     const char *filename;
424     const Eina_File_Direct_Info *info;
425
426     json_array_clear(app_list);
427
428     int index = 0;
429     it = eina_file_ls(dir);
430     EINA_ITERATOR_FOREACH(it, filename)
431         {
432             if (!eina_str_has_suffix(filename, ".desktop"))
433                 continue;
434
435             Efreet_Desktop *desktop = efreet_desktop_get(filename);
436             if (!desktop || !desktop->name || !desktop->icon || !desktop->exec)
437                 continue;
438
439             json_t *o = json_object();
440             json_object_set(o, "id", json_integer(index++));
441             json_object_set(o, "version", json_string("0.1"));
442             json_object_set(o, "show", json_boolean(1));
443             json_object_set(o, "name", json_string(desktop->name));
444             json_object_set(o, "iconPath", json_string(desktop->icon));
445             json_object_set(o, "exec", json_string(desktop->exec));
446             json_array_append(app_list, o);
447         }
448     eina_iterator_free(it);
449 }
450
451 #define BUFF_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*1024)
452 static Eina_Bool poll_descriptors(void *data)
453 {
454     int n;
455     ssize_t len, i = 0;
456     char buff[BUFF_SIZE] = {0};
457
458     n = poll(pollfds, count_pollfds, 100);
459     if (n > -1) {
460         if (pollfds[0].revents) {
461             /* The first descriptor what inotify returns*/
462             len = read(pollfds[0].fd, buff, BUFF_SIZE);
463             while (i < len) {
464                 struct inotify_event *pevent = (struct inotify_event *)&buff[i];
465                 if (pevent->name && eina_str_has_suffix(pevent->name, ".desktop")) {
466                     build_app_list(apps_directory);
467
468                     /* Trigger the broadcast callback for all open connectinos */
469                     libwebsockets_broadcast(&protocols[PROTOCOL_APP_LIST], "x", 2);
470                     break;
471                 }
472
473                 i += sizeof(struct inotify_event) + pevent->len;
474             }
475         }
476         if (count_pollfds > 1) {
477             for (n = 1; n < count_pollfds; n++)
478                 if (pollfds[n].revents)
479                     libwebsocket_service_fd(context, &pollfds[n]);
480         }
481     }
482
483     return ECORE_CALLBACK_RENEW;
484 }
485
486 void call_properties_changed_cb(void *data, DBusMessage *msg)
487 {
488     DBusMessageIter iter, value;
489     const char *property, *v;
490     char *dump;
491     int n;
492     json_t *object;
493
494     dbus_message_iter_init(msg, &iter);
495     dbus_message_iter_get_basic(&iter, &property);
496
497     dbus_message_iter_next(&iter);
498     dbus_message_iter_recurse(&iter, &value);
499
500     dbus_message_iter_get_basic(&value, &v);
501
502     object = json_object();
503     json_object_set(object, "type", json_string("PropertyChanged"));
504     json_object_set(object, "id", json_string(dbus_message_get_path(msg)));
505     json_object_set(object, property, json_string(v));
506     dump = json_dumps(object, 0);
507     libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
508     free(dump);
509 }
510
511 void modem_properties_changed_cb(void *data, DBusMessage *msg)
512 {
513     DBusMessageIter iter, values, entry;
514     const char *property, *v;
515     char *dump;
516     int n;
517     json_t *object;
518
519     dbus_message_iter_init(msg, &iter);
520     dbus_message_iter_get_basic(&iter, &property);
521
522     if (!strcasecmp(property, "Interfaces")) {
523         dbus_message_iter_next(&iter);
524         dbus_message_iter_recurse(&iter, &values);
525
526         while (dbus_message_iter_get_arg_type(&values) == DBUS_TYPE_ARRAY) {
527             DBusMessageIter variant;
528             dbus_message_iter_recurse(&iter, &entry);
529             dbus_message_iter_recurse(&entry, &variant);
530             if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_INVALID) {
531                 dbus_message_iter_get_basic(&variant, &v);
532                 if (!strcasecmp(v, "org.ofono.VoiceCallManager")) {
533                     object = json_object();
534                     json_object_set(object, "type", json_string("ModemAdded"));
535                     json_object_set(object, "id", json_string(dbus_message_get_path(msg)));
536                     dump = json_dumps(object, 0);
537                     libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
538                     free(dump);
539                 }
540             } else {
541                 const char *path = strdup(dbus_message_get_path(msg));
542                 
543                 // If the modem has removed all interfaces then its no longer
544                 // available for making calls
545                 object = json_object();
546                 json_object_set(object, "type", json_string("ModemRemoved"));
547                 json_object_set(object, "id", json_string(path));
548                 dump = json_dumps(object, 0);
549                 libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
550                 free(dump);
551
552                 // Start polling to re-enable the modem
553                 power_on_modem((void *)path);
554             }
555             dbus_message_iter_next(&values);
556         }
557     }
558 }
559
560 void call_added_cb(void *data, DBusMessage *msg)
561 {
562     DBusMessageIter iter, properties;
563     const char *path;
564
565     dbus_message_iter_init(msg, &iter);
566     dbus_message_iter_get_basic(&iter, &path);
567
568     dbus_message_iter_next(&iter);
569     dbus_message_iter_recurse(&iter, &properties);
570
571     json_t *object = json_object();
572     json_object_set(object, "id", json_string(path));
573     json_object_set(object, "type", json_string("CallAdded"));
574
575     while (dbus_message_iter_get_arg_type(&properties) == DBUS_TYPE_DICT_ENTRY) {
576         DBusMessageIter entry, value;
577         const char *key, *v;
578
579         dbus_message_iter_recurse(&properties, &entry);
580         dbus_message_iter_get_basic(&entry, &key);
581
582         dbus_message_iter_next(&entry);
583         dbus_message_iter_recurse(&entry, &value);
584
585         dbus_message_iter_get_basic(&value, &v);
586
587         json_object_set(object, key, json_string(v));
588
589         dbus_message_iter_next(&properties);
590     }
591
592     e_dbus_signal_handler_add(bus,
593                               "org.ofono",
594                               path,
595                               "org.ofono.VoiceCall",
596                               "PropertyChanged",
597                               call_properties_changed_cb,
598                               NULL);
599
600     char *dump = json_dumps(object, 0);
601     libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
602     free(dump);
603 }
604
605 void call_removed_cb(void *data, DBusMessage *msg)
606 {
607     DBusMessageIter iter;
608     const char *path;
609
610     dbus_message_iter_init(msg, &iter);
611     dbus_message_iter_get_basic(&iter, &path);
612
613     json_t *object = json_object();
614     json_object_set(object, "id", json_string(path));
615     json_object_set(object, "type", json_string("CallRemoved"));
616     char *dump = json_dumps(object, 0);
617     libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
618     free(dump);
619 }
620
621 void modem_added_cb(void *data, DBusMessage *msg)
622 {
623     /*
624      * TODO: Verify the modem is powered on before sending a ModemAdded
625      *       event, and if the modem is not powered then go ahead and
626      *       ask ofono to power on the modem
627      */
628     DBusMessageIter iter;
629     const char *path;
630
631     dbus_message_iter_init(msg, &iter);
632     dbus_message_iter_get_basic(&iter, &path);
633
634     json_t *object = json_object();
635     json_object_set(object, "type", json_string("ModemAdded"));
636     json_object_set(object, "id", json_string(path));
637     char *dump = json_dumps(object, 0);
638     libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
639     free(dump);
640 }
641
642 void modem_removed_cb(void *data, DBusMessage *msg)
643 {
644     DBusMessageIter entry;
645     dbus_message_iter_init(msg, &entry);
646     setup_new_modem(entry);
647 }
648
649
650 static Eina_Bool
651 power_on_modem(void *data)
652 {
653     const char *path = (const char *)data;
654     DBusMessage *msg;
655     DBusMessageIter iter, sub;
656     msg = dbus_message_new_method_call(
657                                        "org.ofono",
658                                        path,
659                                        "org.ofono.Modem",
660                                        "SetProperty"
661                                        );
662     const char *property = "Powered";
663     int p = 1;
664     char sig[2];
665     sig[0] = DBUS_TYPE_BOOLEAN;
666     sig[1] = 0;
667
668     dbus_message_append_args(msg,
669                              DBUS_TYPE_STRING, &property,
670                              DBUS_TYPE_INVALID);
671
672     dbus_message_iter_init_append(msg, &iter);            
673     if (dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &sub)) {
674         dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &p);
675         dbus_message_iter_close_container(&iter, &sub);
676
677         e_dbus_message_send(bus, msg, set_powered_cb, -1, (void *)path);
678     }
679
680     return ECORE_CALLBACK_DONE;
681 }
682
683 void
684 set_powered_cb(void *data, DBusMessage *reply, DBusError *error)
685 {
686     if (dbus_error_is_set(error)) {
687         /* 
688          * Attempting to power on a modem when the associated phone
689          * is out of range will result in this call failing.  Since we
690          * expect a users phone to automatically be enabled when the
691          * user comes within range then we start a periodic attempt
692          * to enable.
693          */
694         const char *path = (const char *)data;
695         ecore_poller_add(ECORE_POLLER_CORE, 4, power_on_modem, (void *)path);
696         return;
697     }
698 }
699
700 void setup_new_modem(DBusMessageIter entry)
701 {
702     while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) {
703         const char *path;
704         DBusMessageIter item, properties;
705
706         dbus_message_iter_recurse(&entry, &item);
707         dbus_message_iter_get_basic(&item, &path);
708
709         printf("registering %s\n", path);
710         e_dbus_signal_handler_add(bus, "org.ofono",
711                                   path, "org.ofono.Modem", "PropertyChanged",
712                                   modem_properties_changed_cb, (void *)strdup(path));
713         e_dbus_signal_handler_add(bus, "org.ofono",
714                                   path, "org.ofono.VoiceCallManager", "CallAdded", 
715                                   call_added_cb, NULL);
716         e_dbus_signal_handler_add(bus, "org.ofono",
717                                   path, "org.ofono.VoiceCallManager",
718                                   "CallRemoved",
719                                   call_removed_cb, NULL);
720
721         dbus_message_iter_next(&item);
722         dbus_message_iter_recurse(&item, &properties);
723
724         
725         int powered = 0;
726         while (dbus_message_iter_get_arg_type(&properties) == DBUS_TYPE_DICT_ENTRY) {
727             const char *key;
728             DBusMessageIter interfaces, value, entry;
729
730             dbus_message_iter_recurse(&properties, &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 (!strcasecmp(key, "Interfaces")) {
737                 dbus_message_iter_recurse(&value, &interfaces);
738                 while (dbus_message_iter_get_arg_type(&interfaces) ==
739                        DBUS_TYPE_STRING) {
740                     const char *iface;
741
742                     dbus_message_iter_get_basic(&interfaces, &iface);
743
744                     if (!strcasecmp(iface, "org.ofono.VoiceCallManager")) {
745                         powered = 1;
746                         if (voicecallmanager_path)
747                             free(voicecallmanager_path);
748                         voicecallmanager_path = strdup(path);
749                                                 
750                         /* inform any connections about the addition of the modem */
751                         json_t *o = json_object();
752                         json_object_set(o, "type", json_string("ModemAdded"));
753                         json_object_set(o, "id", json_string(path));
754                         char *dump = json_dumps(o, 0);
755                         libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
756                         free(dump);
757                     }
758
759                     dbus_message_iter_next(&interfaces);
760                 }
761             }
762             
763             dbus_message_iter_next(&properties);
764         }
765
766         if (powered == 0) {
767             power_on_modem(strdup(path));
768         }
769
770         dbus_message_iter_next(&entry);
771     }
772 }
773
774 void
775 get_modems_reply(void *data, DBusMessage *reply, DBusError *error)
776 {
777     DBusMessageIter iter, entry;
778     if (dbus_error_is_set(error)) {
779         fprintf(stderr, "Error: %s - %s\n", error->name, error->message);
780         return;
781     }
782
783     dbus_message_iter_init(reply, &iter);
784     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
785         fprintf(stderr, "Unexpeced signature from ofono GetModems call\n");
786         return;
787     }
788
789     dbus_message_iter_recurse(&iter, &entry);
790
791     setup_new_modem(entry);
792 }
793
794 void initialize_ofono(void)
795 {
796     static int ofono_initialized = 0;
797
798     if (ofono_initialized > 0)
799         return;
800     ofono_initialized++;
801
802     fprintf(stderr, "Initializing phone stack\n");
803
804     /* Start listening for incoming phone calls */
805     DBusMessage *msg;
806     msg = dbus_message_new_method_call(
807                                        "org.ofono",
808                                        "/",
809                                        "org.ofono.Manager",
810                                        "GetModems"
811                                        );
812     e_dbus_message_send(bus, msg, get_modems_reply, -1, NULL);
813
814     e_dbus_signal_handler_add(bus, "org.ofono", "/", "org.ofono.Manager", "ModemAdded", 
815                               modem_added_cb, NULL);
816     e_dbus_signal_handler_add(bus, "org.ofono", "/", "org.ofono.Manager", "ModemRemoved", 
817                               modem_removed_cb, NULL);
818 }
819
820 static struct option options[] = {
821     { "help",       no_argument,       NULL, 'h' },
822     { "port",       required_argument, NULL, 'p' },
823     { "appsdir",    required_argument, NULL, 'a' },
824     { "contentdir", required_argument, NULL, 'c' },
825     { NULL, 0, 0, 0 }
826 };
827
828 int main(int argc, char **argv)
829 {
830     int fd;
831     int n = 0;
832     int port = 7681;
833     int opts = 0;
834
835     eina_init();
836     ecore_init();
837     e_dbus_init();
838     efreet_init();
839     efreet_mime_init();
840     ecore_app_args_set(argc, (const char **)argv);
841
842     /* parse command-line options */
843     while (n >= 0) {
844         n = getopt_long(argc, argv, "hp:d:", options, NULL);
845         if (n < 0)
846             continue;
847         switch (n) {
848         case 'a':
849             apps_directory = strdup(optarg);
850             break;
851         case 'c':
852             content_directory = strdup(optarg);
853             break;
854         case 'p':
855             port = atoi(optarg);
856             break;
857         case 'h':
858         default:
859             fprintf(stderr, "Usage: %s [--port=<p>] [--appsdir=<a>] [--contentdir=<c>]\n", argv[0]);
860             exit(-1);
861         }
862     }
863     if (!apps_directory)
864         apps_directory = APPDIR;
865     if (!content_directory)
866         content_directory = LOCAL_RESOURCE_PATH;
867
868     /* Initialize and build up the application list */
869     app_list = json_array();
870     build_app_list(apps_directory);
871
872     bus = e_dbus_bus_get(DBUS_BUS_SYSTEM);
873
874     /* Listen for updates on application desktop files*/
875     fd = inotify_init();
876     inotify_add_watch(fd, APPDIR, IN_CREATE | IN_DELETE | IN_MODIFY);
877     pollfds[0].fd = fd;
878     pollfds[0].events = POLLIN;
879     pollfds[0].revents = 0;
880     count_pollfds = 1;
881
882     /* Start listening for incoming websocket connections */
883     context = libwebsocket_create_context(port, NULL, protocols,
884                                           libwebsocket_internal_extensions,
885                                           NULL, NULL, -1, -1, opts);
886     if (context == NULL) {
887         fprintf(stderr, "libwebsocket init failed\n");
888         return -1;
889     }
890
891     ecore_idler_add(poll_descriptors, NULL);
892
893     /* configure poller for periodically attempting */
894     /* to enabled modems */
895     ecore_poller_poll_interval_set(ECORE_POLLER_CORE, 1.0);
896
897     ecore_main_loop_begin();
898
899     /* cleanup */
900     libwebsocket_context_destroy(context);
901     ecore_shutdown();
902     return 0;
903 }
904
905 /* Local Variables:      */
906 /* mode:c                */
907 /* c-basic-offset:4      */
908 /* indent-tabs-mode: nil */
909 /* End:                  */