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