2006-08-11 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / metadata / rb-metadata-dbus-service.c
blob67bec092b1048c00c973e4460817d13660909797
1 /*
2 * Copyright (C) 2006 Jonathan Matthew <jonathan@kaolin.hn.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Rhythmbox out-of-process metadata reader.
24 #include <config.h>
25 #include <time.h>
26 #include <string.h>
27 #include <unistd.h>
29 #include <glib/gi18n.h>
30 #include <libgnomeui/gnome-authentication-manager.h>
31 #include <libgnomevfs/gnome-vfs.h>
32 #include <gst/gst.h>
34 #include <dbus/dbus.h>
35 #include <dbus/dbus-glib.h>
36 #include <dbus/dbus-glib-lowlevel.h>
38 #include "rb-metadata.h"
39 #include "rb-metadata-dbus.h"
40 #include "rb-debug.h"
41 #include "rb-util.h"
43 /* number of seconds to hang around doing nothing */
44 #define ATTENTION_SPAN 30
46 typedef struct {
47 DBusServer *server;
48 DBusConnection *connection;
49 GMainLoop *loop;
50 time_t last_active;
51 RBMetaData *metadata;
52 gboolean external;
53 } ServiceData;
55 static DBusHandlerResult
56 _send_error (DBusConnection *connection,
57 DBusMessage *request,
58 gboolean include_flag,
59 gint error_type,
60 const char *message)
62 DBusMessage *reply = dbus_message_new_method_return (request);
64 if (!message) {
65 message = "";
66 rb_debug ("attempting to return error with no message");
67 } else {
68 rb_debug ("attempting to return error: %s", message);
71 if (include_flag) {
72 gboolean ok = FALSE;
73 if (!dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ok, DBUS_TYPE_INVALID)) {
74 rb_debug ("couldn't append error flag");
75 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
79 if (!dbus_message_append_args (reply,
80 DBUS_TYPE_UINT32, &error_type,
81 DBUS_TYPE_STRING, &message,
82 DBUS_TYPE_INVALID)) {
83 rb_debug ("couldn't append error data");
84 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
87 dbus_connection_send (connection, reply, NULL);
88 dbus_message_unref (reply);
89 return DBUS_HANDLER_RESULT_HANDLED;
92 static DBusHandlerResult
93 rb_metadata_dbus_load (DBusConnection *connection,
94 DBusMessage *message,
95 ServiceData *svc)
97 char *uri;
98 DBusMessageIter iter;
99 DBusMessage *reply;
100 GError *error = NULL;
101 gboolean ok = TRUE;
102 const char *mimetype = NULL;
104 if (!dbus_message_iter_init (message, &iter)) {
105 return DBUS_HANDLER_RESULT_NEED_MEMORY;
108 if (!rb_metadata_dbus_get_string (&iter, &uri)) {
109 /* make translatable? */
110 return _send_error (connection, message, TRUE,
111 RB_METADATA_ERROR_INTERNAL,
112 "Unable to read URI from request");
115 rb_debug ("loading metadata from %s", uri);
117 rb_metadata_load (svc->metadata, uri, &error);
118 g_free (uri);
119 if (error != NULL) {
120 DBusHandlerResult r;
121 rb_debug ("metadata error: %s", error->message);
123 r = _send_error (connection, message, TRUE, error->code, error->message);
124 g_clear_error (&error);
125 return r;
127 rb_debug ("metadata load finished; mimetype = %s", rb_metadata_get_mime (svc->metadata));
129 /* construct reply */
130 reply = dbus_message_new_method_return (message);
131 if (!reply) {
132 rb_debug ("out of memory creating return message");
133 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
136 mimetype = rb_metadata_get_mime (svc->metadata);
137 dbus_message_iter_init_append (reply, &iter);
139 if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &ok) ||
140 !dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mimetype)) {
141 rb_debug ("out of memory adding data to return message");
142 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
145 if (!rb_metadata_dbus_add_to_message (svc->metadata, &iter)) {
146 /* make translatable? */
147 return _send_error (connection, message, TRUE,
148 RB_METADATA_ERROR_INTERNAL,
149 "Unable to add metadata to return message");
152 if (!dbus_connection_send (connection, reply, NULL)) {
153 rb_debug ("failed to send return message");
154 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
157 dbus_message_unref (reply);
159 return DBUS_HANDLER_RESULT_HANDLED;
162 static DBusHandlerResult
163 rb_metadata_dbus_can_save (DBusConnection *connection,
164 DBusMessage *message,
165 ServiceData *svc)
167 char *mimetype;
168 DBusMessageIter iter;
169 DBusMessage *reply;
170 gboolean can_save;
172 if (!dbus_message_iter_init (message, &iter)) {
173 return DBUS_HANDLER_RESULT_NEED_MEMORY;
176 if (!rb_metadata_dbus_get_string (&iter, &mimetype)) {
177 /* make translatable? */
178 return _send_error (connection, message, TRUE,
179 RB_METADATA_ERROR_INTERNAL,
180 "Unable to read MIME type from request");
183 can_save = rb_metadata_can_save (svc->metadata, mimetype);
184 g_free (mimetype);
186 /* construct reply */
187 reply = dbus_message_new_method_return (message);
188 if (!reply) {
189 rb_debug ("out of memory creating return message");
190 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
193 dbus_message_iter_init_append (reply, &iter);
194 if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &can_save)) {
195 rb_debug ("out of memory adding data to return message");
196 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
199 if (!dbus_connection_send (connection, reply, NULL)) {
200 rb_debug ("failed to send return message");
201 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
204 dbus_message_unref (reply);
206 return DBUS_HANDLER_RESULT_HANDLED;
209 static gboolean
210 _set_metadata (gpointer key, GValue *data, RBMetaData *metadata)
212 RBMetaDataField field = GPOINTER_TO_INT (key);
213 rb_metadata_set (metadata, field, data);
214 return TRUE;
217 static DBusHandlerResult
218 rb_metadata_dbus_save (DBusConnection *connection,
219 DBusMessage *message,
220 ServiceData *svc)
222 DBusMessageIter iter;
223 DBusMessage *reply;
224 GHashTable *data;
225 GError *error = NULL;
227 /* get metadata from message */
228 if (!dbus_message_iter_init (message, &iter)) {
229 return DBUS_HANDLER_RESULT_NEED_MEMORY;
231 data = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rb_value_free);
232 if (!rb_metadata_dbus_read_from_message (svc->metadata,
233 data,
234 &iter)) {
235 /* make translatable? */
236 return _send_error (connection, message, FALSE,
237 RB_METADATA_ERROR_INTERNAL,
238 "Unable to read metadata from message");
241 /* pass to real metadata instance, and save it */
242 g_hash_table_foreach_remove (data, (GHRFunc) _set_metadata, svc->metadata);
243 g_hash_table_destroy (data);
245 rb_metadata_save (svc->metadata, &error);
247 if (error) {
248 DBusHandlerResult r;
249 rb_debug ("metadata error: %s", error->message);
251 r = _send_error (connection, message, FALSE, error->code, error->message);
252 g_clear_error (&error);
253 return r;
256 reply = dbus_message_new_method_return (message);
257 if (!reply) {
258 rb_debug ("out of memory creating return message");
259 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
262 if (!dbus_connection_send (connection, reply, NULL)) {
263 rb_debug ("failed to send return message");
264 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
267 dbus_message_unref (reply);
269 return DBUS_HANDLER_RESULT_HANDLED;
272 static DBusHandlerResult
273 rb_metadata_dbus_ping (DBusConnection *connection,
274 DBusMessage *message,
275 ServiceData *svc)
277 gboolean ok = TRUE;
279 rb_debug ("ping");
281 DBusMessage *reply = dbus_message_new_method_return (message);
282 if (!message)
283 return DBUS_HANDLER_RESULT_NEED_MEMORY;
285 if (!dbus_message_append_args (reply,
286 DBUS_TYPE_BOOLEAN, &ok,
287 DBUS_TYPE_INVALID)) {
288 return DBUS_HANDLER_RESULT_NEED_MEMORY;
291 dbus_connection_send (connection, reply, NULL);
292 dbus_message_unref (reply);
293 return DBUS_HANDLER_RESULT_HANDLED;
296 static gboolean
297 electromagnetic_shotgun (gpointer data)
299 ServiceData *c = (ServiceData *)data;
300 GTime now = time(NULL);
301 int idle = now - c->last_active;
303 /* quit if we haven't done anything for a while */
304 if (idle > ATTENTION_SPAN) {
305 rb_debug ("shutting down (%ds idle)", idle);
306 g_main_loop_quit (c->loop);
309 return TRUE;
312 static void
313 _unregister_handler (DBusConnection *connection, void *data)
315 /* nothing? */
318 static DBusHandlerResult
319 _handle_message (DBusConnection *connection, DBusMessage *message, void *data)
321 ServiceData *svc = (ServiceData *)data;
322 rb_debug ("handling metadata service message");
324 svc->last_active = time (NULL);
325 if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "load")) {
326 return rb_metadata_dbus_load (connection, message, svc);
327 } else if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "canSave")) {
328 return rb_metadata_dbus_can_save (connection, message, svc);
329 } else if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "save")) {
330 return rb_metadata_dbus_save (connection, message, svc);
331 } else if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "ping")) {
332 return rb_metadata_dbus_ping (connection, message, svc);
333 } else {
334 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
338 static void
339 _new_connection (DBusServer *server,
340 DBusConnection *connection,
341 void *data)
343 ServiceData *svc = (ServiceData *)data;
344 DBusObjectPathVTable vt = {
345 _unregister_handler,
346 _handle_message,
347 NULL, NULL, NULL, NULL
350 rb_debug ("new connection to metadata service");
352 /* don't allow more than one connection at a time */
353 if (svc->connection) {
354 rb_debug ("metadata service already has a client. go away.");
355 return;
358 dbus_connection_register_object_path (connection,
359 RB_METADATA_DBUS_OBJECT_PATH,
360 &vt,
361 svc);
362 dbus_connection_ref (connection);
363 dbus_connection_setup_with_g_main (connection,
364 g_main_loop_get_context (svc->loop));
365 if (!svc->external)
366 dbus_connection_set_exit_on_disconnect (connection, TRUE);
369 static int
370 test_can_save (const char *mimetype)
372 RBMetaData *md;
373 gboolean can_save;
375 md = rb_metadata_new ();
376 can_save = rb_metadata_can_save (md, mimetype);
377 g_print ("%s save %s\n", can_save ? "Can" : "Can't", mimetype);
378 g_object_unref (G_OBJECT (md));
379 return 0;
382 static int
383 test_load (const char *uri)
385 RBMetaData *md;
386 GError *error = NULL;
387 int rv = 0;
389 md = rb_metadata_new ();
390 rb_metadata_load (md, uri, &error);
391 if (error) {
392 if (error->code == RB_METADATA_ERROR_NOT_AUDIO_IGNORE) {
393 g_print ("%s is not an audio stream (ignored)\n", uri);
394 } else if (error->code == RB_METADATA_ERROR_NOT_AUDIO) {
395 g_print ("%s is not an audio stream\n", uri);
396 } else {
397 g_print ("Error loading metadata from %s: %s\n", uri, error->message);
399 g_clear_error (&error);
400 rv = -1;
401 } else {
402 int i;
403 g_print ("mimetype: %s\n", rb_metadata_get_mime (md));
404 for (i=0; i<RB_METADATA_FIELD_LAST; i++) {
405 GValue v = {0,};
406 GValue sv = {0,};
407 if (rb_metadata_get (md, i, &v)) {
408 g_value_init (&sv, G_TYPE_STRING);
409 g_value_transform (&v, &sv);
411 g_print ("%s: %s\n", rb_metadata_get_field_name (i), g_value_get_string (&sv));
413 g_value_unset (&v);
414 g_value_unset (&sv);
418 g_object_unref (G_OBJECT (md));
419 return rv;
423 main (int argc, char **argv)
425 ServiceData svc = {0,};
426 DBusError dbus_error = {0,};
427 gboolean debug = FALSE;
428 const char *address = NULL;
430 #ifdef ENABLE_NLS
431 /* initialize i18n */
432 bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
433 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
434 textdomain (GETTEXT_PACKAGE);
435 #endif
436 g_type_init ();
437 gnome_vfs_init ();
438 gnome_authentication_manager_init ();
439 #ifdef HAVE_GSTREAMER
440 gst_init (NULL, NULL);
441 g_set_prgname ("rhythmbox-metadata");
442 #endif
444 if (argv[1] != NULL && strcmp(argv[1], "--debug") == 0) {
445 argv++;
446 debug = TRUE;
448 rb_debug_init (debug);
450 /* bug report modes */
451 if (argv[1] != NULL && strcmp(argv[1], "--load") == 0) {
452 return test_load (argv[2]);
454 if (argv[1] != NULL && strcmp(argv[1], "--can-save") == 0) {
455 return test_can_save (argv[2]);
458 if (argv[1] != NULL && strcmp (argv[1], "--external") == 0) {
459 argv++;
460 svc.external = TRUE;
462 if (argv[1] == NULL) {
463 address = "unix:tmpdir=/tmp";
464 } else {
465 address = argv[1];
468 rb_debug ("initializing metadata service; pid = %d; address = %s", getpid (), address);
469 svc.metadata = rb_metadata_new ();
471 /* set up D-BUS server */
472 svc.server = dbus_server_listen (address, &dbus_error);
473 if (!svc.server) {
474 rb_debug ("D-BUS server init failed: %s", dbus_error.message);
475 return -1;
478 dbus_server_set_new_connection_function (svc.server,
479 _new_connection,
480 (gpointer) &svc,
481 NULL);
483 /* write the server address back to the parent process */
485 char *addr;
486 addr = dbus_server_get_address (svc.server);
487 rb_debug ("D-BUS server listening on address %s", addr);
488 printf ("%s\n", addr);
489 fflush (stdout);
490 free (addr);
493 /* run main loop until we get bored */
494 svc.loop = g_main_loop_new (NULL, TRUE);
495 dbus_server_setup_with_g_main (svc.server,
496 g_main_loop_get_context (svc.loop));
498 if (!svc.external)
499 g_timeout_add (ATTENTION_SPAN * 500, (GSourceFunc) electromagnetic_shotgun, &svc);
501 g_main_loop_run (svc.loop);
503 if (svc.connection) {
504 #ifdef WITH_OLD_DBUS
505 dbus_connection_disconnect (svc.connection);
506 #else
507 dbus_connection_close (svc.connection);
508 #endif
509 dbus_connection_unref (svc.connection);
512 dbus_server_disconnect (svc.server);
513 dbus_server_unref (svc.server);
514 #ifdef HAVE_GSTREAMER_0_10
515 gst_deinit ();
516 #endif
517 return 0;