Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-daemon.c
Go to the documentation of this file.
1 /* ev-daemon.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, 2012 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 #define G_LOG_DOMAIN "EvinceDaemon"
25 #include <glib.h>
26 #include <glib/gstdio.h>
27 #include <gio/gio.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 
34 #include "ev-daemon-gdbus-generated.h"
35 
36 #define EV_DBUS_DAEMON_NAME "org.gnome.evince.Daemon"
37 #define EV_DBUS_DAEMON_INTERFACE_NAME "org.gnome.evince.Daemon"
38 #define EV_DBUS_DAEMON_OBJECT_PATH "/org/gnome/evince/Daemon"
39 
40 #define EV_DBUS_WINDOW_INTERFACE_NAME "org.gnome.evince.Window"
41 
42 #define DAEMON_TIMEOUT (30) /* seconds */
43 
44 #define LOG g_debug
45 
46 #define EV_TYPE_DAEMON_APPLICATION (ev_daemon_application_get_type ())
47 #define EV_DAEMON_APPLICATION(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), EV_TYPE_DAEMON_APPLICATION, EvDaemonApplication))
48 #define EV_DAEMON_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_DAEMON_APPLICATION, EvDaemonApplicationClass))
49 #define EV_IS_DAEMON_APPLICATION(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), EV_TYPE_DAEMON_APPLICATION))
50 #define EV_IS_DAEMON_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_DAEMON_APPLICATION))
51 #define EV_DAEMON_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_DAEMON_APPLICATION, EvDaemonApplicationClass))
52 
55 
57  GApplicationClass parent_class;
58 };
59 
61 {
62  GApplication parent_instance;
63 
64  EvDaemon *daemon;
65  GHashTable *pending_invocations;
66  GList *docs;
67 };
68 
69 static GType ev_daemon_application_get_type (void);
70 G_DEFINE_TYPE (EvDaemonApplication, ev_daemon_application, G_TYPE_APPLICATION)
71 
72 typedef struct {
73  gchar *dbus_name;
74  gchar *uri;
75  guint watch_id;
76  guint loaded_id;
77 } EvDoc;
78 
79 static void
81 {
82  if (!doc)
83  return;
84 
85  g_free (doc->dbus_name);
86  g_free (doc->uri);
87 
88  g_bus_unwatch_name (doc->watch_id);
89 
90  g_free (doc);
91 }
92 
93 static EvDoc *
95  const gchar *uri)
96 {
97  GList *l;
98 
99  for (l = application->docs; l != NULL; l = l->next) {
100  EvDoc *doc = (EvDoc *)l->data;
101 
102  if (strcmp (doc->uri, uri) == 0)
103  return doc;
104  }
105 
106  return NULL;
107 }
108 
109 static gboolean
110 spawn_evince (const gchar *uri)
111 {
112  gchar *argv[3];
113  gboolean retval;
114  GError *error = NULL;
115 
116  /* TODO Check that the uri exists */
117  argv[0] = g_build_filename (BINDIR, "evince", NULL);
118  argv[1] = (gchar *) uri;
119  argv[2] = NULL;
120 
121  retval = g_spawn_async (NULL /* wd */, argv, NULL /* env */,
122  0, NULL, NULL, NULL, &error);
123  if (!retval) {
124  g_printerr ("Error spawning evince for uri %s: %s\n", uri, error->message);
125  g_error_free (error);
126  }
127  g_free (argv[0]);
128 
129  return retval;
130 }
131 
132 static void
133 name_appeared_cb (GDBusConnection *connection,
134  const gchar *name,
135  const gchar *name_owner,
136  gpointer user_data)
137 {
138  LOG ("Watch name'%s' appeared with owner '%s'", name, name_owner);
139 }
140 
141 static void
142 name_vanished_cb (GDBusConnection *connection,
143  const gchar *name,
144  gpointer user_data)
145 {
146  EvDaemonApplication *application = EV_DAEMON_APPLICATION (user_data);
147  GList *l;
148 
149  LOG ("Watch name'%s' disappeared", name);
150 
151  for (l = application->docs; l != NULL; l = l->next) {
152  EvDoc *doc = (EvDoc *) l->data;
153 
154  if (strcmp (doc->dbus_name, name) != 0)
155  continue;
156 
157  LOG ("Watch found URI '%s' for name; removing", doc->uri);
158 
159  application->docs = g_list_delete_link (application->docs, l);
160  ev_doc_free (doc);
161 
162  g_application_release (G_APPLICATION (application));
163 
164  return;
165  }
166 }
167 
168 static void
170  const gchar *uri,
171  const gchar *dbus_name)
172 {
173  GList *l;
174  GList *uri_invocations;
175 
176  LOG ("RegisterDocument process pending invocations for URI %s", uri);
177  uri_invocations = g_hash_table_lookup (application->pending_invocations, uri);
178 
179  for (l = uri_invocations; l != NULL; l = l->next) {
180  GDBusMethodInvocation *invocation;
181 
182  invocation = (GDBusMethodInvocation *)l->data;
183  g_dbus_method_invocation_return_value (invocation,
184  g_variant_new ("(s)", dbus_name));
185  }
186 
187  g_list_free (uri_invocations);
188  g_hash_table_remove (application->pending_invocations, uri);
189 }
190 
191 static void
192 document_loaded_cb (GDBusConnection *connection,
193  const gchar *sender_name,
194  const gchar *object_path,
195  const gchar *interface_name,
196  const gchar *signal_name,
197  GVariant *parameters,
198  gpointer user_data)
199 {
200  EvDaemonApplication *application = EV_DAEMON_APPLICATION (user_data);
201  const gchar *uri;
202  EvDoc *doc;
203 
204  g_variant_get (parameters, "(&s)", &uri);
205  doc = ev_daemon_application_find_doc (application, uri);
206 
207  if (doc == NULL)
208  return;
209  if (strcmp (uri, doc->uri) == 0) {
210  process_pending_invocations (application, uri, sender_name);
211  }
212 
213  g_dbus_connection_signal_unsubscribe (connection, doc->loaded_id);
214  doc->loaded_id = 0;
215 }
216 
217 static gboolean
218 handle_register_document_cb (EvDaemon *object,
219  GDBusMethodInvocation *invocation,
220  const gchar *uri,
221  EvDaemonApplication *application)
222 {
223  GDBusConnection *connection;
224  const char *sender;
225  EvDoc *doc;
226 
227  doc = ev_daemon_application_find_doc (application, uri);
228  if (doc != NULL) {
229  LOG ("RegisterDocument found owner '%s' for URI '%s'", doc->dbus_name, uri);
230  ev_daemon_complete_register_document (object, invocation, doc->dbus_name);
231 
232  return TRUE;
233  }
234 
235  sender = g_dbus_method_invocation_get_sender (invocation);
236  connection = g_dbus_method_invocation_get_connection (invocation);
237 
238  LOG ("RegisterDocument registered owner '%s' for URI '%s'", sender, uri);
239 
240  doc = g_new (EvDoc, 1);
241  doc->dbus_name = g_strdup (sender);
242  doc->uri = g_strdup (uri);
243 
244  application->docs = g_list_prepend (application->docs, doc);
245 
246  doc->loaded_id = g_dbus_connection_signal_subscribe (connection,
247  doc->dbus_name,
249  "DocumentLoaded",
250  NULL,
251  NULL,
252  0,
254  application, NULL);
255  doc->watch_id = g_bus_watch_name_on_connection (connection,
256  sender,
257  G_BUS_NAME_WATCHER_FLAGS_NONE,
260  application, NULL);
261 
262  ev_daemon_complete_register_document (object, invocation, "");
263 
264  g_application_hold (G_APPLICATION (application));
265 
266  return TRUE;
267 }
268 
269 static gboolean
271  GDBusMethodInvocation *invocation,
272  const gchar *uri,
273  EvDaemonApplication *application)
274 {
275  EvDoc *doc;
276  const char *sender;
277 
278  LOG ("UnregisterDocument URI '%s'", uri);
279 
280  doc = ev_daemon_application_find_doc (application, uri);
281  if (doc == NULL) {
282  LOG ("UnregisterDocument URI was not registered!");
283  g_dbus_method_invocation_return_error_literal (invocation,
284  G_DBUS_ERROR,
285  G_DBUS_ERROR_INVALID_ARGS,
286  "URI not registered");
287  return TRUE;
288  }
289 
290  sender = g_dbus_method_invocation_get_sender (invocation);
291  if (strcmp (doc->dbus_name, sender) != 0) {
292  LOG ("UnregisterDocument called by non-owner (owner '%s' sender '%s')",
293  doc->dbus_name, sender);
294 
295  g_dbus_method_invocation_return_error_literal (invocation,
296  G_DBUS_ERROR,
297  G_DBUS_ERROR_BAD_ADDRESS,
298  "Only owner can call this method");
299  return TRUE;
300  }
301 
302  application->docs = g_list_remove (application->docs, doc);
303 
304  if (doc->loaded_id != 0) {
305  g_dbus_connection_signal_unsubscribe (g_dbus_method_invocation_get_connection (invocation),
306  doc->loaded_id);
307  doc->loaded_id = 0;
308  }
309 
310  ev_doc_free (doc);
311 
312  ev_daemon_complete_unregister_document (object, invocation);
313 
314  g_application_release (G_APPLICATION (application));
315 
316  return TRUE;
317 }
318 
319 static gboolean
320 handle_find_document_cb (EvDaemon *object,
321  GDBusMethodInvocation *invocation,
322  const gchar *uri,
323  gboolean spawn,
324  EvDaemonApplication *application)
325 {
326  EvDoc *doc;
327 
328  LOG ("FindDocument URI '%s'", uri);
329 
330  doc = ev_daemon_application_find_doc (application, uri);
331  if (doc != NULL) {
332  ev_daemon_complete_find_document (object, invocation, doc->dbus_name);
333 
334  return TRUE;
335  }
336 
337  if (spawn) {
338  GList *uri_invocations;
339  gboolean ret_val = TRUE;
340 
341  uri_invocations = g_hash_table_lookup (application->pending_invocations, uri);
342 
343  if (uri_invocations == NULL) {
344  /* Only spawn once. */
345  ret_val = spawn_evince (uri);
346  }
347 
348  if (ret_val) {
349  /* Only defer DBUS answer if evince was succesfully spawned */
350  uri_invocations = g_list_prepend (uri_invocations, invocation);
351  g_hash_table_insert (application->pending_invocations,
352  g_strdup (uri),
353  uri_invocations);
354  return TRUE;
355  }
356  }
357 
358  LOG ("FindDocument URI '%s' was not registered!", uri);
359  // FIXME: shouldn't this return an error then?
360  ev_daemon_complete_find_document (object, invocation, "");
361 
362  return TRUE;
363 }
364 
365 /* ------------------------------------------------------------------------- */
366 
367 static gboolean
368 ev_daemon_application_dbus_register (GApplication *gapplication,
369  GDBusConnection *connection,
370  const gchar *object_path,
371  GError **error)
372 {
373  EvDaemonApplication *application = EV_DAEMON_APPLICATION (gapplication);
374  EvDaemon *skeleton;
375 
376  if (!G_APPLICATION_CLASS (ev_daemon_application_parent_class)->dbus_register (gapplication,
377  connection,
378  object_path,
379  error))
380  return FALSE;
381 
382  skeleton = ev_daemon_skeleton_new ();
383  if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
384  connection,
386  error)) {
387  g_object_unref (skeleton);
388  return FALSE;
389  }
390 
391  application->daemon = skeleton;
392  g_signal_connect (skeleton, "handle-register-document",
393  G_CALLBACK (handle_register_document_cb), application);
394  g_signal_connect (skeleton, "handle-unregister-document",
395  G_CALLBACK (handle_unregister_document_cb), application);
396  g_signal_connect (skeleton, "handle-find-document",
397  G_CALLBACK (handle_find_document_cb), application);
398  return TRUE;
399 }
400 
401 static void
402 ev_daemon_application_dbus_unregister (GApplication *gapplication,
403  GDBusConnection *connection,
404  const gchar *object_path)
405 {
406  EvDaemonApplication *application = EV_DAEMON_APPLICATION (gapplication);
407 
408  if (application->daemon) {
409  g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (application->daemon));
410  g_object_unref (application->daemon);
411  application->daemon = NULL;
412  }
413 
414  G_APPLICATION_CLASS (ev_daemon_application_parent_class)->dbus_unregister (gapplication,
415  connection,
416  object_path);
417 }
418 
419 static void
421 {
422  application->pending_invocations = g_hash_table_new_full (g_str_hash,
423  g_str_equal,
424  (GDestroyNotify) g_free,
425  NULL);
426 }
427 
428 static void
430 {
431  EvDaemonApplication *application = EV_DAEMON_APPLICATION (object);
432 
433  g_warn_if_fail (g_hash_table_size (application->pending_invocations) == 0);
434  g_hash_table_destroy (application->pending_invocations);
435 
436  g_list_free_full (application->docs, (GDestroyNotify) ev_doc_free);
437 
438  G_OBJECT_CLASS (ev_daemon_application_parent_class)->finalize (object);
439 }
440 
441 static void
443 {
444  GObjectClass *object_class = G_OBJECT_CLASS (klass);
445  GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
446 
447  object_class->finalize = ev_daemon_application_finalize;
448 
449  g_application_class->dbus_register = ev_daemon_application_dbus_register;
450  g_application_class->dbus_unregister = ev_daemon_application_dbus_unregister;
451 }
452 
453 /* ------------------------------------------------------------------------- */
454 
455 gint
456 main (gint argc, gchar **argv)
457 {
458  GApplication *application;
459  const GApplicationFlags flags = G_APPLICATION_IS_SERVICE;
460  GError *error = NULL;
461  int status;
462 
463  g_set_prgname ("evince-daemon");
464 
465  application = g_object_new (EV_TYPE_DAEMON_APPLICATION,
466  "application-id", EV_DBUS_DAEMON_NAME,
467  "flags", flags,
468  NULL);
469  g_application_set_inactivity_timeout (application, DAEMON_TIMEOUT);
470 
471  if (!g_application_register (application, NULL, &error)) {
472  g_printerr ("Failed to register: %s\n", error->message);
473  g_error_free (error);
474  g_object_unref (application);
475 
476  return 1;
477  }
478 
479  status = g_application_run (application, 0, NULL);
480  g_object_unref (application);
481 
482  return status;
483 }