1 /*
2  * GNOME Online Miners - crawls through your online content
3  * Copyright (C) 2014 Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  *
20  * Author: Debarshi Ray <debarshir@gnome.org>
21  *
22  */
23 
24 #include "config.h"
25 
26 #include "gom-application.h"
27 #include "gom-dbus.h"
28 #include "gom-miner.h"
29 
30 #define AUTOQUIT_TIMEOUT 5 /* seconds */
31 
32 struct _GomApplication
33 {
34   GApplication parent;
35   GCancellable *cancellable;
36   GomDBus *skeleton;
37   GomMiner *miner;
38   GQueue *queue;
39   GType miner_type;
40   gboolean refreshing;
41 };
42 
43 struct _GomApplicationClass
44 {
45   GApplicationClass parent_class;
46 };
47 
48 enum
49 {
50   PROP_0,
51   PROP_MINER_TYPE
52 };
53 
54 G_DEFINE_TYPE (GomApplication, gom_application, G_TYPE_APPLICATION);
55 
56 static void gom_application_refresh_db_cb (GObject *source, GAsyncResult *res, gpointer user_data);
57 
58 static void
gom_application_insert_shared_content_cb(GObject * source,GAsyncResult * res,gpointer user_data)59 gom_application_insert_shared_content_cb (GObject *source,
60                                           GAsyncResult *res,
61                                           gpointer user_data)
62 {
63   GomApplication *self;
64   GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
65   GError *error;
66 
67   self = GOM_APPLICATION (g_application_get_default ());
68   g_application_release (G_APPLICATION (self));
69 
70   error = NULL;
71   if (!gom_miner_insert_shared_content_finish (GOM_MINER (source), res, &error))
72     {
73       g_printerr ("Failed to insert shared content: %s\n", error->message);
74       g_dbus_method_invocation_take_error (invocation, error);
75       goto out;
76     }
77 
78   gom_dbus_complete_insert_shared_content (self->skeleton, invocation);
79 
80  out:
81   g_object_unref (invocation);
82 }
83 
84 static gboolean
gom_application_insert_shared_content(GomApplication * self,GDBusMethodInvocation * invocation,const gchar * account_id,const gchar * shared_id,const gchar * shared_type,const gchar * source_urn)85 gom_application_insert_shared_content (GomApplication *self,
86                                        GDBusMethodInvocation *invocation,
87                                        const gchar *account_id,
88                                        const gchar *shared_id,
89                                        const gchar *shared_type,
90                                        const gchar *source_urn)
91 {
92   g_application_hold (G_APPLICATION (self));
93   gom_miner_insert_shared_content_async (self->miner,
94                                          account_id,
95                                          shared_id,
96                                          shared_type,
97                                          source_urn,
98                                          self->cancellable,
99                                          gom_application_insert_shared_content_cb,
100                                          g_object_ref (invocation));
101   return TRUE;
102 }
103 
104 static void
gom_application_process_queue(GomApplication * self)105 gom_application_process_queue (GomApplication *self)
106 {
107   GDBusMethodInvocation *invocation = NULL;
108   const gchar **index_types;
109 
110   if (self->refreshing)
111     goto out;
112 
113   if (g_queue_is_empty (self->queue))
114     goto out;
115 
116   invocation = G_DBUS_METHOD_INVOCATION (g_queue_pop_head (self->queue));
117   index_types = g_object_get_data (G_OBJECT (invocation), "index-types");
118   gom_miner_set_index_types (self->miner, index_types);
119 
120   self->refreshing = TRUE;
121   g_application_hold (G_APPLICATION (self));
122   gom_miner_refresh_db_async (self->miner,
123                               self->cancellable,
124                               gom_application_refresh_db_cb,
125                               g_object_ref (invocation));
126 
127  out:
128   g_clear_object (&invocation);
129 }
130 
131 static void
gom_application_refresh_db_cb(GObject * source,GAsyncResult * res,gpointer user_data)132 gom_application_refresh_db_cb (GObject *source,
133                                GAsyncResult *res,
134                                gpointer user_data)
135 {
136   GomApplication *self;
137   GDBusMethodInvocation *invocation = user_data;
138   GError *error = NULL;
139 
140   self = GOM_APPLICATION (g_application_get_default ());
141   g_application_release (G_APPLICATION (self));
142   self->refreshing = FALSE;
143 
144   gom_miner_refresh_db_finish (GOM_MINER (source), res, &error);
145   if (error != NULL)
146     {
147       g_printerr ("Failed to refresh the DB cache: %s\n", error->message);
148       g_dbus_method_invocation_take_error (invocation, error);
149       goto out;
150     }
151 
152   gom_dbus_complete_refresh_db (self->skeleton, invocation);
153 
154  out:
155   g_object_unref (invocation);
156   gom_application_process_queue (self);
157 }
158 
159 static gboolean
gom_application_refresh_db(GomApplication * self,GDBusMethodInvocation * invocation,const gchar * const * arg_index_types)160 gom_application_refresh_db (GomApplication *self,
161                             GDBusMethodInvocation *invocation,
162                             const gchar *const *arg_index_types)
163 {
164   gchar **index_types;
165 
166   index_types = g_strdupv ((gchar **) arg_index_types);
167   g_object_set_data_full (G_OBJECT (invocation), "index-types", index_types, (GDestroyNotify) g_strfreev);
168   g_queue_push_tail (self->queue, g_object_ref (invocation));
169   gom_application_process_queue (self);
170   return TRUE;
171 }
172 
173 static gboolean
gom_application_dbus_register(GApplication * application,GDBusConnection * connection,const gchar * object_path,GError ** error)174 gom_application_dbus_register (GApplication *application,
175                                GDBusConnection *connection,
176                                const gchar *object_path,
177                                GError **error)
178 {
179   GomApplication *self = GOM_APPLICATION (application);
180   gboolean retval = FALSE;
181 
182   if (!G_APPLICATION_CLASS (gom_application_parent_class)->dbus_register (application,
183                                                                           connection,
184                                                                           object_path,
185                                                                           error))
186     goto out;
187 
188   if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->skeleton),
189                                          connection,
190                                          object_path,
191                                          error))
192     goto out;
193 
194   retval = TRUE;
195 
196  out:
197   return retval;
198 }
199 
200 static void
gom_application_dbus_unregister(GApplication * application,GDBusConnection * connection,const gchar * object_path)201 gom_application_dbus_unregister (GApplication *application,
202                                  GDBusConnection *connection,
203                                  const gchar *object_path)
204 {
205   GomApplication *self = GOM_APPLICATION (application);
206 
207   if (self->skeleton != NULL)
208     {
209       if (g_dbus_interface_skeleton_has_connection (G_DBUS_INTERFACE_SKELETON (self->skeleton), connection))
210         g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (self->skeleton),
211                                                             connection);
212     }
213 
214   G_APPLICATION_CLASS (gom_application_parent_class)->dbus_unregister (application, connection, object_path);
215 }
216 
217 static void
gom_application_shutdown(GApplication * application)218 gom_application_shutdown (GApplication *application)
219 {
220   GomApplication *self = GOM_APPLICATION (application);
221 
222   g_cancellable_cancel (self->cancellable);
223 
224   G_APPLICATION_CLASS (gom_application_parent_class)->shutdown (application);
225 }
226 
227 static void
gom_application_constructed(GObject * object)228 gom_application_constructed (GObject *object)
229 {
230   GomApplication *self = GOM_APPLICATION (object);
231   const gchar *display_name;
232 
233   G_OBJECT_CLASS (gom_application_parent_class)->constructed (object);
234 
235   self->miner = g_object_new (self->miner_type, NULL);
236   display_name = gom_miner_get_display_name (self->miner);
237   gom_dbus_set_display_name (self->skeleton, display_name);
238 }
239 
240 static void
gom_application_dispose(GObject * object)241 gom_application_dispose (GObject *object)
242 {
243   GomApplication *self = GOM_APPLICATION (object);
244 
245   g_clear_object (&self->cancellable);
246   g_clear_object (&self->miner);
247   g_clear_object (&self->skeleton);
248 
249   if (self->queue != NULL)
250     {
251       g_queue_free_full (self->queue, g_object_unref);
252       self->queue = NULL;
253     }
254 
255   G_OBJECT_CLASS (gom_application_parent_class)->dispose (object);
256 }
257 
258 static void
gom_application_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)259 gom_application_set_property (GObject *object,
260                               guint prop_id,
261                               const GValue *value,
262                               GParamSpec *pspec)
263 {
264   GomApplication *self = GOM_APPLICATION (object);
265 
266   switch (prop_id)
267     {
268     case PROP_MINER_TYPE:
269       self->miner_type = g_value_get_gtype (value);
270       break;
271 
272     default:
273       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
274       break;
275     }
276 }
277 
278 static void
gom_application_init(GomApplication * self)279 gom_application_init (GomApplication *self)
280 {
281   self->cancellable = g_cancellable_new ();
282 
283   self->skeleton = gom_dbus_skeleton_new ();
284   g_signal_connect_swapped (self->skeleton,
285                             "handle-insert-shared-content",
286                             G_CALLBACK (gom_application_insert_shared_content),
287                             self);
288   g_signal_connect_swapped (self->skeleton, "handle-refresh-db", G_CALLBACK (gom_application_refresh_db), self);
289 
290   self->queue = g_queue_new ();
291 }
292 
293 static void
gom_application_class_init(GomApplicationClass * klass)294 gom_application_class_init (GomApplicationClass *klass)
295 {
296   GObjectClass *oclass = G_OBJECT_CLASS (klass);
297   GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
298 
299   oclass->constructed = gom_application_constructed;
300   oclass->dispose = gom_application_dispose;
301   oclass->set_property = gom_application_set_property;
302   application_class->dbus_register = gom_application_dbus_register;
303   application_class->dbus_unregister = gom_application_dbus_unregister;
304   application_class->shutdown = gom_application_shutdown;
305 
306   g_object_class_install_property (oclass,
307                                    PROP_MINER_TYPE,
308                                    g_param_spec_gtype ("miner-type",
309                                                        "Miner type",
310                                                        "A GType representing the miner class",
311                                                        GOM_TYPE_MINER,
312                                                        G_PARAM_CONSTRUCT_ONLY
313                                                        | G_PARAM_STATIC_STRINGS
314                                                        | G_PARAM_WRITABLE));
315 }
316 
317 GApplication *
gom_application_new(const gchar * application_id,GType miner_type)318 gom_application_new (const gchar *application_id,
319                      GType miner_type)
320 {
321   return g_object_new (GOM_TYPE_APPLICATION,
322                        "application-id", application_id,
323                        "flags", G_APPLICATION_IS_SERVICE,
324                        "inactivity-timeout", AUTOQUIT_TIMEOUT,
325                        "miner-type", miner_type,
326                        NULL);
327 }
328