]> www.fi.muni.cz Git - evince.git/blob - shell/ev-daemon.c
b136369dfe8142e07f359d5758097469ba11213a
[evince.git] / shell / ev-daemon.c
1 /* ev-metadata.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2009 Carlos Garcia Campos  <carlosgc@gnome.org>
5  * Copyright © 2010 Christian Persch
6  *
7  * Evince is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * Evince is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23
24 #include <glib.h>
25 #include <glib/gstdio.h>
26 #include <gio/gio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33
34 #define EV_DBUS_DAEMON_NAME             "org.gnome.evince.Daemon"
35 #define EV_DBUS_DAEMON_INTERFACE_NAME   "org.gnome.evince.Daemon"
36 #define EV_DBUS_DAEMON_OBJECT_PATH      "/org/gnome/evince/Daemon"
37
38 #define DAEMON_TIMEOUT (30) /* seconds */
39
40 #define LOG g_printerr
41
42 static GList *ev_daemon_docs = NULL;
43 static guint kill_timer_id;
44
45 typedef struct {
46         gchar *dbus_name;
47         gchar *uri;
48         guint watch_id;
49 } EvDoc;
50
51 static void
52 ev_doc_free (EvDoc *doc)
53 {
54         if (!doc)
55                 return;
56
57         g_free (doc->dbus_name);
58         g_free (doc->uri);
59
60         g_bus_unwatch_name (doc->watch_id);
61
62         g_free (doc);
63 }
64
65 static EvDoc *
66 ev_daemon_find_doc (const gchar *uri)
67 {
68         GList *l;
69
70         for (l = ev_daemon_docs; l != NULL; l = l->next) {
71                 EvDoc *doc = (EvDoc *)l->data;
72
73                 if (strcmp (doc->uri, uri) == 0)
74                         return doc;
75         }
76
77         return NULL;
78 }
79
80 static void
81 ev_daemon_stop_killtimer (void)
82 {
83         if (kill_timer_id != 0)
84                 g_source_remove (kill_timer_id);
85         kill_timer_id = 0;
86 }
87
88 static gboolean
89 ev_daemon_shutdown (gpointer user_data)
90 {
91         GMainLoop *loop = (GMainLoop *) user_data;
92
93         LOG ("Timeout; exiting daemon.\n");
94
95         if (g_main_loop_is_running (loop))
96                 g_main_loop_quit (loop);
97
98         return FALSE;
99 }
100
101 static void
102 ev_daemon_maybe_start_killtimer (gpointer data)
103 {
104         ev_daemon_stop_killtimer ();
105         if (ev_daemon_docs != NULL)
106                 return;
107
108         kill_timer_id = g_timeout_add_seconds (DAEMON_TIMEOUT,
109                                                (GSourceFunc) ev_daemon_shutdown,
110                                                data);
111 }
112
113 static gboolean
114 convert_metadata (const gchar *metadata)
115 {
116         GFile   *file;
117         char    *argv[3];
118         gint     exit_status;
119         GFileAttributeInfoList *namespaces;
120         gboolean supported = FALSE;
121         GError  *error = NULL;
122         gboolean retval;
123
124         /* If metadata is not supported for a local file
125          * is likely because and old gvfs version is running.
126          */
127         file = g_file_new_for_path (metadata);
128         namespaces = g_file_query_writable_namespaces (file, NULL, NULL);
129         if (namespaces) {
130                 gint i;
131
132                 for (i = 0; i < namespaces->n_infos; i++) {
133                         if (strcmp (namespaces->infos[i].name, "metadata") == 0) {
134                                 supported = TRUE;
135                                 break;
136                         }
137                 }
138                 g_file_attribute_info_list_unref (namespaces);
139         }
140         if (!supported) {
141                 g_warning ("GVFS metadata not supported. "
142                            "Evince will run without metadata support.\n");
143                 g_object_unref (file);
144                 return FALSE;
145         }
146         g_object_unref (file);
147
148         argv[0] = g_build_filename (LIBEXECDIR, "evince-convert-metadata", NULL);
149         argv[1] = (char *) metadata;
150         argv[2] = NULL;
151
152         retval = g_spawn_sync (NULL /* wd */, argv, NULL /* env */,
153                                0, NULL, NULL, NULL, NULL,
154                                &exit_status, &error);
155         g_free (argv[0]);
156
157         if (!retval) {
158                 g_printerr ("Error migrating metadata: %s\n", error->message);
159                 g_error_free (error);
160         }
161
162         return retval && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0;
163 }
164
165 static void
166 ev_migrate_metadata (void)
167 {
168         gchar       *updated;
169         gchar       *metadata;
170         gchar       *dot_dir;
171         const gchar *userdir;
172
173         userdir = g_getenv ("GNOME22_USER_DIR");
174         if (userdir) {
175                 dot_dir = g_build_filename (userdir, "evince", NULL);
176         } else {
177                 dot_dir = g_build_filename (g_get_home_dir (),
178                                             ".gnome2",
179                                             "evince",
180                                             NULL);
181         }
182
183         updated = g_build_filename (dot_dir, "migrated-to-gvfs", NULL);
184         if (g_file_test (updated, G_FILE_TEST_EXISTS)) {
185                 /* Already migrated */
186                 g_free (updated);
187                 g_free (dot_dir);
188                 return;
189         }
190
191         metadata = g_build_filename (dot_dir, "ev-metadata.xml", NULL);
192         if (g_file_test (metadata, G_FILE_TEST_EXISTS)) {
193                 if (convert_metadata (metadata)) {
194                         gint fd;
195
196                         fd = g_creat (updated, 0600);
197                         if (fd != -1) {
198                                 close (fd);
199                         }
200                 }
201         }
202
203         g_free (dot_dir);
204         g_free (updated);
205         g_free (metadata);
206 }
207
208 static void
209 name_appeared_cb (GDBusConnection *connection,
210                   const gchar     *name,
211                   const gchar     *name_owner,
212                   gpointer         user_data)
213 {
214         LOG ("Watch name'%s' appeared with owner '%s'\n", name, name_owner);
215 }
216
217 static void
218 name_vanished_cb (GDBusConnection *connection,
219                   const gchar     *name,
220                   gpointer         user_data)
221 {
222         GList *l;
223
224         LOG ("Watch name'%s' disappeared\n", name);
225
226         for (l = ev_daemon_docs; l != NULL; l = l->next) {
227                 EvDoc *doc = (EvDoc *) l->data;
228
229                 if (strcmp (doc->dbus_name, name) != 0)
230                         continue;
231
232                 LOG ("Watch found URI '%s' for name; removing\n", doc->uri);
233
234                 ev_daemon_docs = g_list_delete_link (ev_daemon_docs, l);
235                 ev_doc_free (doc);
236                 
237                 ev_daemon_maybe_start_killtimer (user_data);
238                 return;
239         }
240 }
241
242 static void
243 method_call_cb (GDBusConnection       *connection,
244                 const gchar           *sender,
245                 const gchar           *object_path,
246                 const gchar           *interface_name,
247                 const gchar           *method_name,
248                 GVariant              *parameters,
249                 GDBusMethodInvocation *invocation,
250                 gpointer               user_data)
251 {
252         if (g_strcmp0 (interface_name, EV_DBUS_DAEMON_INTERFACE_NAME) != 0)
253                 return;
254
255         if (g_strcmp0 (method_name, "RegisterDocument") == 0) {
256                 EvDoc       *doc;
257                 const gchar *uri;
258
259                 g_variant_get (parameters, "(&s)", &uri);
260
261                 doc = ev_daemon_find_doc (uri);
262                 if (doc != NULL) {
263                         LOG ("RegisterDocument found owner '%s' for URI '%s'\n", doc->dbus_name, uri);
264                         g_dbus_method_invocation_return_value (invocation,
265                                                                g_variant_new ("(s)", doc->dbus_name));
266                         return;
267                 }
268         
269                 ev_daemon_stop_killtimer ();
270
271                 doc = g_new (EvDoc, 1);
272                 doc->dbus_name = g_strdup (sender);
273                 doc->uri = g_strdup (uri);
274
275                 doc->watch_id = g_bus_watch_name_on_connection (connection,
276                                                                 sender,
277                                                                 G_BUS_NAME_WATCHER_FLAGS_NONE,
278                                                                 name_appeared_cb,
279                                                                 name_vanished_cb,
280                                                                 user_data, NULL);
281
282                 LOG ("RegisterDocument registered owner '%s' for URI '%s'\n", doc->dbus_name, uri);
283                 ev_daemon_docs = g_list_prepend (ev_daemon_docs, doc);
284
285                 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ""));
286         } else if (g_strcmp0 (method_name, "UnregisterDocument") == 0) {
287                 EvDoc *doc;
288                 const gchar *uri;
289
290                 g_variant_get (parameters, "(&s)", &uri);
291
292                 LOG ("UnregisterDocument URI '%s'\n", uri);
293
294                 doc = ev_daemon_find_doc (uri);
295                 if (doc == NULL) {
296                         LOG ("UnregisterDocument URI was not registered!\n");
297                         g_dbus_method_invocation_return_error_literal (invocation,
298                                                                        G_DBUS_ERROR,
299                                                                        G_DBUS_ERROR_INVALID_ARGS,
300                                                                        "URI not registered");
301                         return;
302                 }
303
304                 if (strcmp (doc->dbus_name, sender) != 0) {
305                         LOG ("UnregisterDocument called by non-owner (owner '%s' sender '%s')\n",
306                              doc->dbus_name, sender);
307
308                         g_dbus_method_invocation_return_error_literal (invocation,
309                                                                        G_DBUS_ERROR,
310                                                                        G_DBUS_ERROR_BAD_ADDRESS,
311                                                                        "Only owner can call this method");
312                         return;
313                 }
314
315                 ev_daemon_docs = g_list_remove (ev_daemon_docs, doc);
316                 ev_doc_free (doc);
317                 ev_daemon_maybe_start_killtimer (user_data);
318
319                 g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
320         }
321 }
322
323 static const char introspection_xml[] =
324   "<node>"
325     "<interface name='org.gnome.evince.Daemon'>"
326       "<method name='RegisterDocument'>"
327         "<arg type='s' name='uri' direction='in'/>"
328         "<arg type='s' name='owner' direction='out'/>"
329       "</method>"
330       "<method name='UnregisterDocument'>"
331         "<arg type='s' name='uri' direction='in'/>"
332       "</method>"
333     "</interface>"
334   "</node>";
335
336 static const GDBusInterfaceVTable interface_vtable = {
337   method_call_cb,
338   NULL,
339   NULL
340 };
341
342 static GDBusNodeInfo *introspection_data;
343
344 static void
345 bus_acquired_cb (GDBusConnection *connection,
346                  const gchar     *name,
347                  gpointer         user_data)
348 {
349         GMainLoop *loop = (GMainLoop *) user_data;
350         guint      registration_id;
351         GError    *error = NULL;
352
353         if (!introspection_data)
354                 introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
355
356         registration_id = g_dbus_connection_register_object (connection,
357                                                              EV_DBUS_DAEMON_OBJECT_PATH,
358                                                              introspection_data->interfaces[0],
359                                                              &interface_vtable,
360                                                              g_main_loop_ref (loop),
361                                                              (GDestroyNotify) g_main_loop_unref,
362                                                              &error);
363         if (registration_id == 0) {
364                 g_printerr ("Failed to register object: %s\n", error->message);
365                 g_error_free (error);
366
367                 if (g_main_loop_is_running (loop))
368                         g_main_loop_quit (loop);
369         }
370 }
371
372 static void
373 name_acquired_cb (GDBusConnection *connection,
374                   const gchar     *name,
375                   gpointer         user_data)
376 {
377         ev_migrate_metadata ();
378
379         ev_daemon_maybe_start_killtimer (user_data);
380 }
381
382 static void
383 name_lost_cb (GDBusConnection *connection,
384               const gchar     *name,
385               gpointer         user_data)
386 {
387           GMainLoop *loop = (GMainLoop *) user_data;
388
389           /* Failed to acquire the name; exit daemon */
390           if (g_main_loop_is_running (loop))
391                   g_main_loop_quit (loop);
392 }
393
394 gint
395 main (gint argc, gchar **argv)
396 {
397         GMainLoop *loop;
398         guint owner_id;
399
400         g_set_prgname ("evince-daemon");
401
402         g_type_init ();
403
404         loop = g_main_loop_new (NULL, FALSE);
405
406         owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
407                                    EV_DBUS_DAEMON_NAME,
408                                    G_BUS_NAME_OWNER_FLAGS_NONE,
409                                    bus_acquired_cb,
410                                    name_acquired_cb,
411                                    name_lost_cb,
412                                    g_main_loop_ref (loop),
413                                    (GDestroyNotify) g_main_loop_unref);
414
415         g_main_loop_run (loop);
416
417         g_bus_unown_name (owner_id);
418
419         g_main_loop_unref (loop);
420         if (introspection_data)
421                 g_dbus_node_info_unref (introspection_data);
422         g_list_foreach (ev_daemon_docs, (GFunc)ev_doc_free, NULL);
423         g_list_free (ev_daemon_docs);
424
425         return 0;
426 }