2 * atlantis-server.c - The Atlantis server
3 * (c) 2008 Sander Dijkhuis <sander.dijkhuis@gmail.com>
4 * Still need to decide about licensing. Ask if interested.
8 #include <dbus/dbus-glib.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include "atlantis-server.h"
14 #include "atlantis-server-glue.h"
16 #define ATLANTIS_SERVER_PORT 8000
18 struct _AtlantisServerPrivate
20 DBusGConnection
*dbus_connection
;
24 typedef struct _AtlantisService AtlantisService
;
26 struct _AtlantisService
32 /* FIXME: Why should we do this? */
36 extern DBusConnection
*
37 dbus_g_connection_get_connection (DBusGConnection
*gconnection
);
41 web_respond (int cfd
, gchar
*resp
)
43 send (cfd
, resp
, strlen (resp
), 0);
47 get_response_from_service (AtlantisServer
*server
,
50 GRegex
*regex
= g_regex_new ("^/-/(?P<service>[^/]+)(?P<path>/.*)$",
52 GMatchInfo
*match_info
;
56 AtlantisService
*service
;
60 if (!g_regex_match (regex
, url
, 0, &match_info
))
62 g_regex_unref (regex
);
63 g_match_info_free (match_info
);
67 service_id
= g_match_info_fetch_named (match_info
, "service");
68 path
= g_match_info_fetch_named (match_info
, "path");
70 g_regex_unref (regex
);
71 g_match_info_free (match_info
);
73 service
= g_datalist_get_data (server
->private->services
, service_id
);
77 proxy
= dbus_g_proxy_new_for_name (server
->private->dbus_connection
,
80 "nl.momple.Atlantis.Service");
83 if (!dbus_g_proxy_call (proxy
, "GetContent", &error
,
84 G_TYPE_STRING
, service_id
,
87 G_TYPE_STRING
, &content
,
90 g_message ("D-Bus error: %s", error
->message
);
100 add_service_init (GQuark key_id
, gpointer data
, gpointer user_data
)
102 int *cfd
= (int *)user_data
;
103 gchar
*service_id
= (gchar
*)(g_quark_to_string (key_id
));
104 web_respond (*cfd
, g_strconcat ("<script src='-/", service_id
,
105 "/main.js'></script>\n", NULL
));
109 add_service_script (GQuark key_id
, gpointer data
, gpointer user_data
)
114 AtlantisServer
*server
;
116 struct DataStruct
*ds
= (struct DataStruct
*)user_data
;
117 gchar
*service_id
= (gchar
*)(g_quark_to_string (key_id
));
118 web_respond (ds
->cfd
, g_strconcat (" loadService('", service_id
, "');\n",
123 send_web_response (AtlantisServer
*self
, char *url
, int cfd
)
127 gsize content_length
;
128 GError
**error
= NULL
;
130 if (!strcmp (url
, "/"))
134 "Content-Type: text/html; charset=UTF-8\n"
137 "<title>Atlantis</title>\n"
138 "<script src=interface.js></script>\n"
139 "<script src=services.js></script>\n"
140 "<link rel=stylesheet href=interface.css>\n"
141 "<div id=command-block>\n"
142 " <p id=command-string></p><br>\n"
143 " <ul id=command-alternatives></ul>\n"
145 "<div id=message><p></div>\n"
146 "<div id=content contentEditable>\n"
149 "<hr class=doc-char>\n"
154 else if (!strcmp (url
, "/interface.js"))
158 "Content-Type: text/javascript\n\n");
160 file
= g_io_channel_new_file ("interface.js", "r", error
);
161 g_io_channel_read_to_end (file
, &content
, &content_length
, error
);
163 web_respond (cfd
, content
);
166 g_io_channel_shutdown (file
, FALSE
, error
);
168 else if (!strcmp (url
, "/interface.css"))
172 "Content-Type: text/css\n\n");
174 file
= g_io_channel_new_file ("interface.css", "r", error
);
175 g_io_channel_read_to_end (file
, &content
, &content_length
, error
);
177 web_respond (cfd
, content
);
180 g_io_channel_shutdown (file
, FALSE
, error
);
182 else if (!strcmp (url
, "/services.js"))
187 AtlantisServer
*server
;
194 "Content-Type: text/javascript\n\n");
196 web_respond (cfd
, "with (Atlantis) {\n"
197 " addInitFunction(function() {\n");
198 g_datalist_foreach (self
->private->services
, add_service_script
, &data
);
199 web_respond (cfd
, " });\n}");
201 else if ((content
= get_response_from_service (self
, url
)) != NULL
)
203 web_respond (cfd
, content
);
207 web_respond (cfd
, "HTTP/1.1 404 Not Found\n\n404, not found.");
212 incoming_cb (GIOChannel
*source
, GIOCondition condition
, gpointer data
)
215 struct sockaddr_in sockaddr
;
216 unsigned int socklen
= sizeof (sockaddr
);
220 int fd
= g_io_channel_unix_get_fd (source
);
221 if (condition
!= G_IO_IN
)
224 if ((cfd
= accept (fd
, (struct sockaddr
*)&sockaddr
, &socklen
)) < 0)
227 recv (cfd
, buf
, sizeof (buf
), 0);
229 /* Put the requested URL in url. */
230 for (i
= 0; i
< 256 && buf
[i
] != ' '; i
++);
231 for (i
++, j
= 0; i
< 256 && buf
[i
] != ' '; i
++, j
++)
235 send_web_response ((AtlantisServer
*)data
, url
, cfd
);
243 atlantis_server_class_init (gpointer g_class
,
244 gpointer g_class_data
)
246 dbus_g_object_type_install_info (ATLANTIS_TYPE_SERVER
,
247 &dbus_glib_atlantis_server_object_info
);
251 atlantis_server_instance_init (GTypeInstance
*instance
,
254 AtlantisServer
*self
= (AtlantisServer
*)instance
;
255 GError
*error
= NULL
;
256 DBusError
*dbus_error
= NULL
;
257 struct sockaddr_in sockaddr
;
260 self
->private = g_new0 (AtlantisServerPrivate
, 1);
262 self
->private->services
= g_malloc (sizeof (GData
*));
263 *(self
->private->services
) = NULL
;
264 g_datalist_init (self
->private->services
);
266 if ((self
->private->dbus_connection
=
267 dbus_g_bus_get (DBUS_BUS_SESSION
, &error
)) == NULL
)
269 g_error ("Failed to open D-Bus connection: %s\n", error
->message
);
272 dbus_bus_request_name (dbus_g_connection_get_connection
273 (self
->private->dbus_connection
),
274 "nl.momple.Atlantis.Server", 0, dbus_error
);
276 dbus_g_connection_register_g_object (self
->private->dbus_connection
,
277 "/nl/momple/Atlantis/Server",
280 if ((fd
= socket (PF_INET
, SOCK_STREAM
, 0)) < 0)
282 g_error ("Couldn't open the server socket.");
285 sockaddr
.sin_family
= AF_INET
;
286 sockaddr
.sin_addr
.s_addr
= INADDR_ANY
;
287 sockaddr
.sin_port
= htons (ATLANTIS_SERVER_PORT
);
289 if ((bind (fd
, (struct sockaddr
*)&sockaddr
, sizeof (sockaddr
))) < 0)
291 g_error ("Couldn't bind to the server socket.");
295 g_io_add_watch (g_io_channel_unix_new (fd
), G_IO_IN
, incoming_cb
, self
);
297 g_message ("Started the server at http://localhost:%d/",
298 ATLANTIS_SERVER_PORT
);
302 atlantis_server_new (void)
304 return g_object_new (ATLANTIS_TYPE_SERVER
, NULL
);
308 atlantis_server_register_service (AtlantisServer
*self
,
314 AtlantisService
*service
= g_malloc (sizeof (AtlantisService
));
316 service
->bus_name
= g_strdup (bus_name
);
317 service
->object_path
= g_strdup (object_path
);
318 g_datalist_set_data (self
->private->services
, service_id
, service
);
320 g_message ("Service %s registered", service_id
);
326 atlantis_server_get_type (void)
328 static GType type
= 0;
331 static const GTypeInfo info
=
333 sizeof (AtlantisServerClass
),
334 NULL
, /* base_init */
335 NULL
, /* base_finalize */
336 &atlantis_server_class_init
,
337 NULL
, /* class_finalize */
338 NULL
, /* class_data */
339 sizeof (AtlantisServer
),
341 &atlantis_server_instance_init
344 type
= g_type_register_static (G_TYPE_OBJECT
,
345 "AtlantisServerType",