7 #include <libwebsockets.h>
10 #include <sys/inotify.h>
11 #include <sys/types.h>
14 enum sockdrawer_protocols {
18 SOCKDRAWER_PROTOCOL_COUNT
21 #ifdef INSTALL_DATADIR
22 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/sockdrawer"
24 #define LOCAL_RESOURCE_PATH "."
27 #define DEFAULTCONTENT LOCAL_RESOURCE_PATH"/index.html"
28 #define DEFAULTICON LOCAL_RESOURCE_PATH"/default.png"
30 #define APPDIR "/usr/share/applications"
32 struct libwebsocket_context *context;
34 E_DBus_Connection *bus;
36 json_t *app_list = NULL;
37 char *apps_directory = NULL;
39 json_t *event_queue = NULL;
41 #define MAX_POLL_ELEMENTS 100
42 struct pollfd pollfds[100];
43 int count_pollfds = 0;
45 void handle_command(json_t *o);
46 static int callback_http(struct libwebsocket_context *context,
47 struct libwebsocket *wsi,
48 enum libwebsocket_callback_reasons reason, void *user,
49 void *in, size_t len);
50 void send_app_list(struct libwebsocket *socket);
51 static int callback_app_list(struct libwebsocket_context *context,
52 struct libwebsocket *wsi,
53 enum libwebsocket_callback_reasons reason,
54 void *user, void *in, size_t len);
55 static Eina_Bool get_websocket_event(void *data);
56 static int callback_dialer(struct libwebsocket_context *context,
57 struct libwebsocket *wsi,
58 enum libwebsocket_callback_reasons reason,
59 void *user, void *in, size_t len);
64 struct per_session_data__app_list {
67 struct per_session_data__dialer {
71 static struct libwebsocket_protocols protocols[] = {
80 sizeof(struct per_session_data__app_list),
85 sizeof(struct per_session_data__dialer),
92 /* libwebsocket callback for handling normal http request. We use this */
93 /* to implementa a rudamentary webserver. */
95 callback_http(struct libwebsocket_context *context,
96 struct libwebsocket *wsi,
97 enum libwebsocket_callback_reasons reason, void *user,
100 char client_name[128];
107 case LWS_CALLBACK_HTTP:
108 if (in && ((char *)in)[0] == '/' && !strstr(in, "..")) {
109 if (strstr(in, "/icon/") == in) {
110 /* virtual directory used for serving up system icons */
112 char *icon = (char *)(in + 6);
113 const char *path = efreet_icon_path_find(getenv("E_ICON_THEME"),
114 (const char *)icon, 128);
116 path = efreet_icon_path_find("default", icon, 128);
118 path = efreet_icon_path_find("hicolor", icon, 128);
120 if (icon[0] == '/' && !stat(icon, &info) && S_ISREG(info.st_mode))
123 fprintf(stderr, "Can not find icon: %s\n", icon);
130 libwebsockets_serve_http_file(wsi, path,
131 (const char *)efreet_mime_type_get((char *)path));
133 snprintf(filename, 256, "%s/%s", LOCAL_RESOURCE_PATH, (char *)in + 1);
134 if (strlen((char *)in) == 1)
135 /* serve up default content */
136 libwebsockets_serve_http_file(wsi, DEFAULTCONTENT, "text/html");
137 else if (!stat(filename, &info) && S_ISREG(info.st_mode))
138 libwebsockets_serve_http_file(wsi, filename,
139 (const char *)efreet_mime_type_get((char *)in));
141 fprintf(stderr, "Failed to find content: %s\n", (char *)in);
144 fprintf(stderr, "Invalid request \'%s\'\n", (char *)in);
148 case LWS_CALLBACK_ADD_POLL_FD:
149 pollfds[count_pollfds].fd = (int)(long)user;
150 pollfds[count_pollfds].events = (int)len;
151 pollfds[count_pollfds++].revents = 0;
154 case LWS_CALLBACK_DEL_POLL_FD:
155 for (n = 0; n < count_pollfds; n++)
156 if (pollfds[n].fd == (int)(long)user)
157 while (n < count_pollfds) {
158 pollfds[n] = pollfds[n + 1];
164 case LWS_CALLBACK_SET_MODE_POLL_FD:
165 for (n = 0; n < count_pollfds; n++)
166 if (pollfds[n].fd == (int)(long)user)
167 pollfds[n].events |= (int)(long)len;
170 case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
171 for (n = 0; n < count_pollfds; n++)
172 if (pollfds[n].fd == (int)(long)user)
173 pollfds[n].events &= ~(int)(long)len;
183 /* libwebsocket callback for 'protocol-app-list' based websockets */
185 callback_dialer(struct libwebsocket_context *context,
186 struct libwebsocket *wsi,
187 enum libwebsocket_callback_reasons reason,
188 void *user, void *in, size_t len)
194 case LWS_CALLBACK_BROADCAST:
196 * This is triggered on all open websocket connections
197 * when libwebsocket_broadcast is called. For this
198 * protocol we ignore the 'in' paramter and send out
199 * the pending event queue
201 case LWS_CALLBACK_ESTABLISHED:
203 printf("About to broadcast:\n%s\n", (char *) in);
204 unsigned char *b = malloc(LWS_SEND_BUFFER_PRE_PADDING +
206 LWS_SEND_BUFFER_POST_PADDING);
207 unsigned char *p = b + LWS_SEND_BUFFER_PRE_PADDING;
208 int n = sprintf((char *)p, "%s", (char *)in);
210 libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
216 case LWS_CALLBACK_RECEIVE:
218 * The other end of the websocket is sending us text
219 * so attempt to parse that text as a JSON object providing
220 * either a single command object or an array of command
223 if (in && (o = json_loads((char *)in, 0, NULL))) {
224 if (json_is_array(o)) {
225 for (i = 0; i < json_array_size(o); i++)
226 handle_command(json_array_get(o, i));
230 json_object_clear(o);
242 /* Write a serialized JSON object containing */
243 /* a list of objects each describing an application */
244 void send_app_list(struct libwebsocket *socket)
247 * WARNING: libwebsockets requires prefixing and post
248 * fixing the send buffer with enough space
249 * for the library to insert required websocket
250 * protocol information.
251 * Ignoring this will result in segfaults.
253 char *dump = json_dumps(app_list, 0);
254 unsigned char *b = malloc(LWS_SEND_BUFFER_PRE_PADDING +
256 LWS_SEND_BUFFER_POST_PADDING);
257 unsigned char *p = b + LWS_SEND_BUFFER_PRE_PADDING;
258 int n = sprintf((char *)p, "%s", dump);
260 libwebsocket_write(socket, p, n, LWS_WRITE_TEXT);
266 /* command object parser */
267 void handle_command(json_t *o)
269 const char *cmd = json_string_value(json_object_get(o, "cmd"));
274 if (strncmp(cmd, "launch", 6) == 0 && (tmp = json_object_get(o, "index"))) {
275 int index = json_integer_value(tmp);
276 json_t *app = json_array_get(app_list, index);
280 json_t *exec = json_object_get(app, "exec");
284 ecore_exe_run(json_string_value(exec), NULL);
288 /* libwebsocket callback for 'protocol-app-list' based websockets */
290 callback_app_list(struct libwebsocket_context *context,
291 struct libwebsocket *wsi,
292 enum libwebsocket_callback_reasons reason,
293 void *user, void *in, size_t len)
299 case LWS_CALLBACK_BROADCAST:
301 * This is triggered on all open websocket connections
302 * when libwebsocket_broadcast is called. For this
303 * protocol we only do one possible thing on broadcast
304 * so just ignore the incoming 'in' string and just
305 * send a new list of applications.
307 case LWS_CALLBACK_ESTABLISHED:
311 case LWS_CALLBACK_RECEIVE:
313 * The other end of the websocket is sending us text
314 * so attempt to parse that text as a JSON object providing
315 * either a single command object or an array of command
318 if (in && (o = json_loads((char *)in, 0, NULL))) {
319 if (json_is_array(o)) {
320 for (i = 0; i < json_array_size(o); i++)
321 handle_command(json_array_get(o, i));
325 json_object_clear(o);
337 void build_app_list(char *dir) {
339 const char *filename;
340 const Eina_File_Direct_Info *info;
342 json_array_clear(app_list);
345 it = eina_file_ls(dir);
346 EINA_ITERATOR_FOREACH(it, filename)
348 if (!eina_str_has_suffix(filename, ".desktop"))
351 Efreet_Desktop *desktop = efreet_desktop_get(filename);
352 if (!desktop || !desktop->name || !desktop->icon || !desktop->exec)
355 json_t *o = json_object();
356 json_object_set(o, "id", json_integer(index++));
357 json_object_set(o, "version", json_string("0.1"));
358 json_object_set(o, "show", json_boolean(1));
359 json_object_set(o, "name", json_string(desktop->name));
360 json_object_set(o, "iconPath", json_string(desktop->icon));
361 json_object_set(o, "exec", json_string(desktop->exec));
362 json_array_append(app_list, o);
364 eina_iterator_free(it);
367 #define BUFF_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*1024)
368 static Eina_Bool poll_descriptors(void *data)
372 char buff[BUFF_SIZE] = {0};
374 n = poll(pollfds, count_pollfds, 100);
376 if (pollfds[0].revents) {
377 /* The first descriptor what inotify returns*/
378 len = read(pollfds[0].fd, buff, BUFF_SIZE);
380 struct inotify_event *pevent = (struct inotify_event *)&buff[i];
381 if (pevent->name && eina_str_has_suffix(pevent->name, ".desktop")) {
382 build_app_list(apps_directory);
384 /* Trigger the broadcast callback for all open connectinos */
385 libwebsockets_broadcast(&protocols[PROTOCOL_APP_LIST], "x", 2);
389 i += sizeof(struct inotify_event) + pevent->len;
392 if (count_pollfds > 1) {
393 for (n = 1; n < count_pollfds; n++)
394 if (pollfds[n].revents)
395 libwebsocket_service_fd(context, &pollfds[n]);
399 return ECORE_CALLBACK_RENEW;
402 void call_properties_changed_cb(void *data, DBusMessage *msg)
404 DBusMessageIter iter, value;
405 const char *property, *v;
410 dbus_message_iter_init(msg, &iter);
411 dbus_message_iter_get_basic(&iter, &property);
413 dbus_message_iter_next(&iter);
414 dbus_message_iter_recurse(&iter, &value);
416 dbus_message_iter_get_basic(&value, &v);
418 printf("Call changed: %s=%s\n", property, v);
420 object = json_object();
421 json_object_set(object, property, json_string(v));
422 dump = json_dumps(object, 0);
423 libwebsockets_broadcast(&protocols[PROTOCOL_DIALER], dump, strlen(dump));
427 void call_added_cb(void *data, DBusMessage *msg)
429 DBusMessageIter iter, properties;
432 dbus_message_iter_init(msg, &iter);
433 dbus_message_iter_get_basic(&iter, &path);
435 dbus_message_iter_next(&iter);
436 dbus_message_iter_recurse(&iter, &properties);
438 printf("%s :", path);
439 while (dbus_message_iter_get_arg_type(&properties) == DBUS_TYPE_DICT_ENTRY) {
440 DBusMessageIter entry, value;
443 dbus_message_iter_recurse(&properties, &entry);
444 dbus_message_iter_get_basic(&entry, &key);
446 dbus_message_iter_next(&entry);
447 dbus_message_iter_recurse(&entry, &value);
449 dbus_message_iter_get_basic(&value, &v);
451 printf(" %s=%s ", key, v);
453 dbus_message_iter_next(&properties);
457 e_dbus_signal_handler_add(bus,
460 "org.ofono.VoiceCall",
462 call_properties_changed_cb,
466 void call_removed_cb(void *data, DBusMessage *msg)
468 printf("Call removed\n");
472 get_modems_reply(void *data, DBusMessage *reply, DBusError *error)
474 DBusMessageIter iter, entry;
475 if (dbus_error_is_set(error)) {
476 printf("Error: %s - %s\n", error->name, error->message);
480 dbus_message_iter_init(reply, &iter);
481 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
482 fprintf(stderr, "Unexpeced signature from ofono GetModems call\n");
486 dbus_message_iter_recurse(&iter, &entry);
488 while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) {
490 DBusMessageIter item, properties;
492 dbus_message_iter_recurse(&entry, &item);
493 dbus_message_iter_get_basic(&item, &path);
495 dbus_message_iter_next(&item);
496 dbus_message_iter_recurse(&item, &properties);
498 while (dbus_message_iter_get_arg_type(&properties) == DBUS_TYPE_DICT_ENTRY) {
500 DBusMessageIter interfaces, value, entry;
502 dbus_message_iter_recurse(&properties, &entry);
503 dbus_message_iter_get_basic(&entry, &key);
505 dbus_message_iter_next(&entry);
506 dbus_message_iter_recurse(&entry, &value);
508 if (!strcasecmp(key, "Interfaces")) {
509 dbus_message_iter_recurse(&value, &interfaces);
510 while (dbus_message_iter_get_arg_type(&interfaces) ==
514 dbus_message_iter_get_basic(&interfaces, &iface);
516 if (!strcasecmp(iface, "org.ofono.VoiceCallManager")) {
517 printf("Listening for calls on %s\n", path);
518 e_dbus_signal_handler_add(bus, "org.ofono",
519 path, iface, "CallAdded",
520 call_added_cb, NULL);
521 e_dbus_signal_handler_add(bus, "org.ofono",
524 call_removed_cb, NULL);
527 dbus_message_iter_next(&interfaces);
531 dbus_message_iter_next(&properties);
534 dbus_message_iter_next(&entry);
539 static struct option options[] = {
540 { "help", no_argument, NULL, 'h' },
541 { "port", required_argument, NULL, 'p' },
542 { "directory", required_argument, NULL, 'd' },
546 int main(int argc, char **argv)
558 ecore_app_args_set(argc, (const char **)argv);
560 /* parse command-line options */
562 n = getopt_long(argc, argv, "hp:d:", options, NULL);
567 apps_directory = strdup(optarg);
573 fprintf(stderr, "Usage: %s [--port=<p>] [--directory=<d>]\n", argv[0]);
578 apps_directory = APPDIR;
580 /* Initialize and build up the application list */
581 app_list = json_array();
582 build_app_list(apps_directory);
584 bus = e_dbus_bus_get(DBUS_BUS_SYSTEM);
586 /* Start listening for incoming phone calls */
588 msg = dbus_message_new_method_call(
594 e_dbus_message_send(bus, msg, get_modems_reply, -1, NULL);
597 /* Listen for updates on application desktop files*/
599 inotify_add_watch(fd, APPDIR, IN_CREATE | IN_DELETE | IN_MODIFY);
601 pollfds[0].events = POLLIN;
602 pollfds[0].revents = 0;
605 /* Start listening for incoming websocket connections */
606 context = libwebsocket_create_context(port, NULL, protocols,
607 libwebsocket_internal_extensions,
608 NULL, NULL, -1, -1, opts);
609 if (context == NULL) {
610 fprintf(stderr, "libwebsocket init failed\n");
614 ecore_idler_add(poll_descriptors, NULL);
616 ecore_main_loop_begin();
619 libwebsocket_context_destroy(context);
624 /* Local Variables: */
626 /* c-basic-offset:4 */
627 /* indent-tabs-mode: nil */