]> www.fi.muni.cz Git - evince.git/blob - shell/ev-daemon.c
[shell] Plug refcount leaks
[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 static GList *ev_daemon_docs = NULL;
41 static guint kill_timer_id;
42
43 typedef struct {
44         gchar *dbus_name;
45         gchar *uri;
46         guint watch_id;
47 } EvDoc;
48
49 static void
50 ev_doc_free (EvDoc *doc)
51 {
52         if (!doc)
53                 return;
54
55         g_free (doc->dbus_name);
56         g_free (doc->uri);
57
58         g_bus_unwatch_name (doc->watch_id);
59
60         g_free (doc);
61 }
62
63 static EvDoc *
64 ev_daemon_find_doc (const gchar *uri)
65 {
66         GList *l;
67
68         for (l = ev_daemon_docs; l != NULL; l = l->next) {
69                 EvDoc *doc = (EvDoc *)l->data;
70
71                 if (strcmp (doc->uri, uri) == 0)
72                         return doc;
73         }
74
75         return NULL;
76 }
77
78 static void
79 ev_daemon_stop_killtimer (void)
80 {
81         if (kill_timer_id != 0)
82                 g_source_remove (kill_timer_id);
83         kill_timer_id = 0;
84 }
85
86 static gboolean
87 ev_daemon_shutdown (gpointer user_data)
88 {
89         GMainLoop *loop = (GMainLoop *) user_data;
90
91         if (g_main_loop_is_running (loop))
92                 g_main_loop_quit (loop);
93
94         return FALSE;
95 }
96
97 static void
98 ev_daemon_maybe_start_killtimer (gpointer data)
99 {
100         ev_daemon_stop_killtimer ();
101         if (ev_daemon_docs != NULL)
102                 return;
103
104         kill_timer_id = g_timeout_add_seconds (DAEMON_TIMEOUT,
105                                                (GSourceFunc) ev_daemon_shutdown,
106                                                data);
107 }
108
109 static gboolean
110 convert_metadata (const gchar *metadata)
111 {
112         GFile   *file;
113         char    *argv[3];
114         gint     exit_status;
115         GFileAttributeInfoList *namespaces;
116         gboolean supported = FALSE;
117         GError  *error = NULL;
118         gboolean retval;
119
120         /* If metadata is not supported for a local file
121          * is likely because and old gvfs version is running.
122          */
123         file = g_file_new_for_path (metadata);
124         namespaces = g_file_query_writable_namespaces (file, NULL, NULL);
125         if (namespaces) {
126                 gint i;
127
128                 for (i = 0; i < namespaces->n_infos; i++) {
129                         if (strcmp (namespaces->infos[i].name, "metadata") == 0) {
130                                 supported = TRUE;
131                                 break;
132                         }
133                 }
134                 g_file_attribute_info_list_unref (namespaces);
135         }
136         if (!supported) {
137                 g_warning ("GVFS metadata not supported. "
138                            "Evince will run without metadata support.\n");
139                 g_object_unref (file);
140                 return FALSE;
141         }
142         g_object_unref (file);
143
144         argv[0] = g_build_filename (LIBEXECDIR, "evince-convert-metadata", NULL);
145         argv[1] = (char *) metadata;
146         argv[2] = NULL;
147
148         retval = g_spawn_sync (NULL /* wd */, argv, NULL /* env */,
149                                0, NULL, NULL, NULL, NULL,
150                                &exit_status, &error);
151         g_free (argv[0]);
152
153         if (!retval) {
154                 g_printerr ("Error migrating metadata: %s\n", error->message);
155                 g_error_free (error);
156         }
157
158         return retval && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0;
159 }
160
161 static void
162 ev_migrate_metadata (void)
163 {
164         gchar       *updated;
165         gchar       *metadata;
166         gchar       *dot_dir;
167         const gchar *userdir;
168
169         userdir = g_getenv ("GNOME22_USER_DIR");
170         if (userdir) {
171                 dot_dir = g_build_filename (userdir, "evince", NULL);
172         } else {
173                 dot_dir = g_build_filename (g_get_home_dir (),
174                                             ".gnome2",
175                                             "evince",
176                                             NULL);
177         }
178
179         updated = g_build_filename (dot_dir, "migrated-to-gvfs", NULL);
180         if (g_file_test (updated, G_FILE_TEST_EXISTS)) {
181                 /* Already migrated */
182                 g_free (updated);
183                 g_free (dot_dir);
184                 return;
185         }
186
187         metadata = g_build_filename (dot_dir, "ev-metadata.xml", NULL);
188         if (g_file_test (metadata, G_FILE_TEST_EXISTS)) {
189                 if (convert_metadata (metadata)) {
190                         gint fd;
191
192                         fd = g_creat (updated, 0600);
193                         if (fd != -1) {
194                                 close (fd);
195                         }
196                 }
197         }
198
199         g_free (dot_dir);
200         g_free (updated);
201         g_free (metadata);
202 }
203
204 static void
205 name_appeared_cb (GDBusConnection *connection,
206                   const gchar     *name,
207                   const gchar     *name_owner,
208                   gpointer         user_data)
209 {
210 }
211
212 static void
213 name_vanished_cb (GDBusConnection *connection,
214                   const gchar     *name,
215                   gpointer         user_data)
216 {
217         GList *l;
218
219         for (l = ev_daemon_docs; l != NULL; l = l->next) {
220                 EvDoc *doc = (EvDoc *) l->data;
221
222                 if (strcmp (doc->dbus_name, name) != 0)
223                         continue;
224
225                 ev_daemon_docs = g_list_delete_link (ev_daemon_docs, l);
226                 ev_doc_free (doc);
227                 
228                 ev_daemon_maybe_start_killtimer (user_data);
229                 return;
230         }
231 }
232
233 static void
234 method_call_cb (GDBusConnection       *connection,
235                 const gchar           *sender,
236                 const gchar           *object_path,
237                 const gchar           *interface_name,
238                 const gchar           *method_name,
239                 GVariant              *parameters,
240                 GDBusMethodInvocation *invocation,
241                 gpointer               user_data)
242 {
243         if (g_strcmp0 (interface_name, EV_DBUS_DAEMON_INTERFACE_NAME) != 0)
244                 return;
245
246         if (g_strcmp0 (method_name, "RegisterDocument") == 0) {
247                 EvDoc       *doc;
248                 const gchar *uri;
249                 const gchar *owner = NULL;
250
251                 g_variant_get (parameters, "(&s)", &uri);
252
253                 doc = ev_daemon_find_doc (uri);
254                 if (doc) {
255                         /* Already registered */
256                         owner = doc->dbus_name;
257                 } else {
258                         ev_daemon_stop_killtimer ();
259
260                         doc = g_new (EvDoc, 1);
261                         doc->dbus_name = g_strdup (sender);
262                         doc->uri = g_strdup (uri);
263
264                         doc->watch_id = g_bus_watch_name (G_BUS_TYPE_STARTER,
265                                                           sender,
266                                                           G_BUS_NAME_WATCHER_FLAGS_NONE,
267                                                           name_appeared_cb,
268                                                           name_vanished_cb,
269                                                           user_data, NULL);
270
271                         ev_daemon_docs = g_list_prepend (ev_daemon_docs, doc);
272                 }
273
274                 g_dbus_method_invocation_return_value (invocation,
275                                                        g_variant_new_string (owner));
276                 return;
277
278         } else if (g_strcmp0 (method_name, "UnregisterDocument") == 0) {
279                 EvDoc *doc;
280                 const gchar *uri;
281
282                 g_variant_get (parameters, "(&s)", &uri);
283
284                 doc = ev_daemon_find_doc (uri);
285                 if (doc == NULL) {
286                         g_dbus_method_invocation_return_error_literal (invocation,
287                                                                        G_DBUS_ERROR,
288                                                                        G_DBUS_ERROR_INVALID_ARGS,
289                                                                        "URI not registered");
290                         return;
291                 }
292
293                 if (strcmp (doc->dbus_name, sender) != 0) {
294                         g_dbus_method_invocation_return_error_literal (invocation,
295                                                                        G_DBUS_ERROR,
296                                                                        G_DBUS_ERROR_BAD_ADDRESS,
297                                                                        "Only owner can call this method");
298                         return;
299                 }
300
301                 ev_daemon_docs = g_list_remove (ev_daemon_docs, doc);
302                 ev_doc_free (doc);
303                 ev_daemon_maybe_start_killtimer (user_data);
304
305                 g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
306                 return;
307         }
308 }
309
310 static void
311 name_acquired_cb (GDBusConnection *connection,
312                   const gchar     *name,
313                   gpointer         user_data)
314 {
315         ev_migrate_metadata ();
316
317         ev_daemon_maybe_start_killtimer (user_data);
318 }
319
320 static void
321 name_lost_cb (GDBusConnection *connection,
322               const gchar     *name,
323               gpointer         user_data)
324 {
325           GMainLoop *loop = (GMainLoop *) user_data;
326
327           /* Failed to acquire the name; exit daemon */
328           if (g_main_loop_is_running (loop))
329                   g_main_loop_quit (loop);
330 }
331
332 static const char introspection_xml[] =
333   "<node>"
334     "<interface name='org.gnome.evince.Daemon'>"
335       "<method name='RegisterDocument'>"
336         "<arg type='s' name='uri' direction='in'/>"
337         "<arg type='s' name='owner' direction='out'/>"
338       "</method>"
339       "<method name='UnregisterDocument'>"
340         "<arg type='s' name='uri' direction='in'/>"
341       "</method>"
342     "</interface>"
343   "</node>";
344
345 static const GDBusInterfaceVTable interface_vtable = {
346   method_call_cb,
347   NULL,
348   NULL
349 };
350
351 gint
352 main (gint argc, gchar **argv)
353 {
354         GDBusConnection *connection;
355         GMainLoop *loop;
356         GError *error = NULL;
357         guint registration_id, owner_id;
358         GDBusNodeInfo *introspection_data;
359
360         g_type_init ();
361
362         connection = g_bus_get_sync (G_BUS_TYPE_STARTER, NULL, &error);
363         if (connection == NULL) {
364                 g_printerr ("Failed to get bus connection: %s\n", error->message);
365                 g_error_free (error);
366                 return 1;
367         }
368
369         introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
370         g_assert (introspection_data != NULL);
371
372         loop = g_main_loop_new (NULL, FALSE);
373
374         registration_id = g_dbus_connection_register_object (connection,
375                                                              EV_DBUS_DAEMON_OBJECT_PATH,
376                                                              EV_DBUS_DAEMON_NAME,
377                                                              introspection_data->interfaces[0],
378                                                              &interface_vtable,
379                                                              g_main_loop_ref (loop),
380                                                              (GDestroyNotify) g_main_loop_unref,
381                                                              &error);
382         if (registration_id == 0) {
383                 g_printerr ("Failed to register object: %s\n", error->message);
384                 g_error_free (error);
385                 g_object_unref (connection);
386                 return 1;
387         }
388
389         owner_id = g_bus_own_name_on_connection (connection,
390                                                  EV_DBUS_DAEMON_NAME,
391                                                  G_BUS_NAME_OWNER_FLAGS_NONE,
392                                                  name_acquired_cb,
393                                                  name_lost_cb,
394                                                  g_main_loop_ref (loop),
395                                                  (GDestroyNotify) g_main_loop_unref);
396
397         g_main_loop_run (loop);
398
399         g_bus_unown_name (owner_id);
400
401         g_main_loop_unref (loop);
402         g_dbus_node_info_unref (introspection_data);
403         g_list_foreach (ev_daemon_docs, (GFunc)ev_doc_free, NULL);
404         g_list_free (ev_daemon_docs);
405         g_object_unref (connection);
406
407         return 0;
408 }