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