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