Initial commit
[atlantis.git] / atlantis-server.c
blobcabfa2f9d6fe36df7d1f2c411f56fbeb2e820e00
1 /*
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.
5 */
7 #include <dbus/dbus.h>
8 #include <dbus/dbus-glib.h>
9 #include <netdb.h>
10 #include <string.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;
21 GData **services;
24 typedef struct _AtlantisService AtlantisService;
26 struct _AtlantisService
28 gchar *bus_name;
29 gchar *object_path;
32 /* FIXME: Why should we do this? */
33 extern void
34 close (int);
36 extern DBusConnection *
37 dbus_g_connection_get_connection (DBusGConnection *gconnection);
38 /* end of FIXME */
40 void
41 web_respond (int cfd, gchar *resp)
43 send (cfd, resp, strlen (resp), 0);
46 gchar *
47 get_response_from_service (AtlantisServer *server,
48 gchar *url)
50 GRegex *regex = g_regex_new ("^/-/(?P<service>[^/]+)(?P<path>/.*)$",
51 0, 0, NULL);
52 GMatchInfo *match_info;
53 gchar *service_id;
54 GError *error;
55 DBusGProxy *proxy;
56 AtlantisService *service;
57 gchar *content;
58 gchar *path;
60 if (!g_regex_match (regex, url, 0, &match_info))
62 g_regex_unref (regex);
63 g_match_info_free (match_info);
64 return NULL;
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);
74 if (service == NULL)
75 return NULL;
77 proxy = dbus_g_proxy_new_for_name (server->private->dbus_connection,
78 service->bus_name,
79 service->object_path,
80 "nl.momple.Atlantis.Service");
82 error = NULL;
83 if (!dbus_g_proxy_call (proxy, "GetContent", &error,
84 G_TYPE_STRING, service_id,
85 G_TYPE_STRING, path,
86 G_TYPE_INVALID,
87 G_TYPE_STRING, &content,
88 G_TYPE_INVALID))
90 g_message ("D-Bus error: %s", error->message);
91 return NULL;
94 g_free (service_id);
96 return content;
99 void
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));
108 void
109 add_service_script (GQuark key_id, gpointer data, gpointer user_data)
111 struct DataStruct
113 int cfd;
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",
119 NULL));
122 void
123 send_web_response (AtlantisServer *self, char *url, int cfd)
125 GIOChannel *file;
126 gchar *content;
127 gsize content_length;
128 GError **error = NULL;
130 if (!strcmp (url, "/"))
132 web_respond (cfd,
133 "HTTP/1.1 200 OK\n"
134 "Content-Type: text/html; charset=UTF-8\n"
135 "\n"
136 "<!doctype html>\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"
144 "</div>\n"
145 "<div id=message><p></div>\n"
146 "<div id=content contentEditable>\n"
147 "<h1>Test</h1>\n"
148 "<p>Hello, world!\n"
149 "<hr class=doc-char>\n"
150 "<p>Bye, world!\n"
151 "</div>"
154 else if (!strcmp (url, "/interface.js"))
156 web_respond (cfd,
157 "HTTP/1.1 200 OK\n"
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);
165 g_free (content);
166 g_io_channel_shutdown (file, FALSE, error);
168 else if (!strcmp (url, "/interface.css"))
170 web_respond (cfd,
171 "HTTP/1.1 200 OK\n"
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);
179 g_free (content);
180 g_io_channel_shutdown (file, FALSE, error);
182 else if (!strcmp (url, "/services.js"))
184 struct
186 int cfd;
187 AtlantisServer *server;
188 } data;
189 data.cfd = cfd;
190 data.server = self;
192 web_respond (cfd,
193 "HTTP/1.1 200 OK\n"
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);
205 else
207 web_respond (cfd, "HTTP/1.1 404 Not Found\n\n404, not found.");
211 gboolean
212 incoming_cb (GIOChannel *source, GIOCondition condition, gpointer data)
214 int cfd;
215 struct sockaddr_in sockaddr;
216 unsigned int socklen = sizeof (sockaddr);
217 char buf[256];
218 char url[256];
219 int i, j;
220 int fd = g_io_channel_unix_get_fd (source);
221 if (condition != G_IO_IN)
222 return TRUE;
224 if ((cfd = accept (fd, (struct sockaddr *)&sockaddr, &socklen)) < 0)
225 return TRUE;
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++)
232 url[j] = buf[i];
233 url[j] = '\0';
235 send_web_response ((AtlantisServer *)data, url, cfd);
237 close (cfd);
239 return TRUE;
242 void
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);
250 void
251 atlantis_server_instance_init (GTypeInstance *instance,
252 gpointer g_class)
254 AtlantisServer *self = (AtlantisServer *)instance;
255 GError *error = NULL;
256 DBusError *dbus_error = NULL;
257 struct sockaddr_in sockaddr;
258 int fd;
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",
278 G_OBJECT (self));
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.");
294 listen (fd, 1);
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);
301 AtlantisServer *
302 atlantis_server_new (void)
304 return g_object_new (ATLANTIS_TYPE_SERVER, NULL);
307 gboolean
308 atlantis_server_register_service (AtlantisServer *self,
309 gchar *bus_name,
310 gchar *object_path,
311 gchar *service_id,
312 GError **error)
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);
322 return TRUE;
325 GType
326 atlantis_server_get_type (void)
328 static GType type = 0;
329 if (!type)
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),
340 0, /* n_preallocs */
341 &atlantis_server_instance_init
344 type = g_type_register_static (G_TYPE_OBJECT,
345 "AtlantisServerType",
346 &info, 0);
348 return type;