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