1 /*
2  * Photos - access, organize and share your photos on GNOME
3  * Copyright © 2012 – 2019 Red Hat, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (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, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /* Based on code from:
20  *   + Documents
21  */
22 
23 
24 #include "config.h"
25 
26 #include <dazzle.h>
27 
28 #include "photos-filterable.h"
29 #include "photos-query.h"
30 #include "photos-source.h"
31 #include "photos-utils.h"
32 
33 
34 struct _PhotosSource
35 {
36   GObject parent_instance;
37   GIcon *icon;
38   GIcon *symbolic_icon;
39   GMount *mount;
40   GoaObject *object;
41   gboolean builtin;
42   gchar *id;
43   gchar *name;
44 };
45 
46 enum
47 {
48   PROP_0,
49   PROP_BUILTIN,
50   PROP_ID,
51   PROP_MOUNT,
52   PROP_NAME,
53   PROP_OBJECT
54 };
55 
56 static void photos_source_filterable_iface_init (PhotosFilterableInterface *iface);
57 
58 
59 G_DEFINE_TYPE_WITH_CODE (PhotosSource, photos_source, G_TYPE_OBJECT,
60                          G_IMPLEMENT_INTERFACE (PHOTOS_TYPE_FILTERABLE,
61                                                 photos_source_filterable_iface_init));
62 DZL_DEFINE_COUNTER (instances, "PhotosSource", "Instances", "Number of PhotosSource instances")
63 
64 
65 static const gchar *TRACKER_SCHEMA = "org.freedesktop.Tracker3.Miner.Files";
66 static const gchar *TRACKER_KEY_RECURSIVE_DIRECTORIES = "index-recursive-directories";
67 
68 
69 static gchar *
photos_source_build_filter_local(void)70 photos_source_build_filter_local (void)
71 {
72   g_autoptr (GSettings) settings = NULL;
73   g_autoptr (GString) tracker_filter = NULL;
74   g_auto (GStrv) tracker_dirs = NULL;
75   g_autofree gchar *desktop_uri = NULL;
76   g_autofree gchar *download_uri = NULL;
77   g_autofree gchar *export_path = NULL;
78   g_autofree gchar *export_uri = NULL;
79   gchar *filter;
80   const gchar *path;
81   g_autofree gchar *pictures_uri = NULL;
82   guint i;
83 
84   settings = g_settings_new (TRACKER_SCHEMA);
85   tracker_dirs = g_settings_get_strv (settings, TRACKER_KEY_RECURSIVE_DIRECTORIES);
86   tracker_filter = g_string_new ("");
87 
88   for (i = 0; tracker_dirs[i] != NULL; i++)
89     {
90       g_autofree gchar *tracker_uri = NULL;
91 
92       /* ignore special XDG placeholders, since we handle those internally */
93       if (tracker_dirs[i][0] == '&' || tracker_dirs[i][0] == '$')
94         continue;
95 
96       tracker_uri = photos_utils_convert_path_to_uri (tracker_dirs[i]);
97       g_string_append_printf (tracker_filter, " || fn:contains (nie:isStoredAs (?urn), '%s')", tracker_uri);
98     }
99 
100   path = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
101   desktop_uri = photos_utils_convert_path_to_uri (path);
102 
103   path = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
104   download_uri = photos_utils_convert_path_to_uri (path);
105 
106   path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
107   pictures_uri = photos_utils_convert_path_to_uri (path);
108 
109   export_path = g_build_filename (path, PHOTOS_EXPORT_SUBPATH, NULL);
110   export_uri = photos_utils_convert_path_to_uri (export_path);
111 
112   filter = g_strdup_printf ("(((fn:contains (nie:isStoredAs (?urn), '%s')"
113                             "   || fn:contains (nie:isStoredAs (?urn), '%s')"
114                             "   || fn:contains (nie:isStoredAs (?urn), '%s')"
115                             "   %s)"
116                             "  && !fn:contains (nie:isStoredAs (?urn), '%s'))"
117                             " || fn:starts-with (nao:identifier (?urn), '%s')"
118                             " || (?urn = nfo:image-category-screenshot))",
119                             desktop_uri,
120                             download_uri,
121                             pictures_uri,
122                             tracker_filter->str,
123                             export_uri,
124                             PHOTOS_QUERY_LOCAL_COLLECTIONS_IDENTIFIER);
125 
126   return filter;
127 }
128 
129 
130 static gchar *
photos_source_build_filter_resource(PhotosSource * self)131 photos_source_build_filter_resource (PhotosSource *self)
132 {
133   g_autofree gchar *filter = NULL;
134   gchar *ret_val = NULL;
135 
136   g_return_val_if_fail (!self->builtin, NULL);
137 
138   if (self->object != NULL)
139     {
140       filter = g_strdup_printf ("(nie:dataSource (?urn) = '%s')", self->id);
141     }
142   else if (self->mount != NULL)
143     {
144       g_autoptr (GFile) root = NULL;
145       g_autofree gchar *uri = NULL;
146 
147       root = g_mount_get_root (self->mount);
148       uri = g_file_get_uri (root);
149       filter = g_strdup_printf ("(fn:starts-with (nie:isStoredAs (?urn), '%s'))", uri);
150     }
151   else
152     {
153       g_return_val_if_reached (NULL);
154     }
155 
156   ret_val = g_steal_pointer (&filter);
157   return ret_val;
158 }
159 
160 
161 static gboolean
photos_source_get_builtin(PhotosFilterable * iface)162 photos_source_get_builtin (PhotosFilterable *iface)
163 {
164   PhotosSource *self = PHOTOS_SOURCE (iface);
165   return self->builtin;
166 }
167 
168 
169 static gchar *
photos_source_get_filter(PhotosFilterable * iface)170 photos_source_get_filter (PhotosFilterable *iface)
171 {
172   PhotosSource *self = PHOTOS_SOURCE (iface);
173 
174   g_assert_cmpstr (self->id, !=, PHOTOS_SOURCE_STOCK_ALL);
175 
176   if (g_strcmp0 (self->id, PHOTOS_SOURCE_STOCK_LOCAL) == 0)
177     return photos_source_build_filter_local ();
178 
179   return photos_source_build_filter_resource (self);
180 }
181 
182 
183 static const gchar *
photos_source_get_id(PhotosFilterable * filterable)184 photos_source_get_id (PhotosFilterable *filterable)
185 {
186   PhotosSource *self = PHOTOS_SOURCE (filterable);
187   return self->id;
188 }
189 
190 
191 static gboolean
photos_source_is_search_criterion(PhotosFilterable * filterable)192 photos_source_is_search_criterion (PhotosFilterable *filterable)
193 {
194   PhotosSource *self = PHOTOS_SOURCE (filterable);
195   gboolean ret_val;
196 
197   ret_val = self->mount == NULL;
198   return ret_val;
199 }
200 
201 
202 static void
photos_source_dispose(GObject * object)203 photos_source_dispose (GObject *object)
204 {
205   PhotosSource *self = PHOTOS_SOURCE (object);
206 
207   g_clear_object (&self->icon);
208   g_clear_object (&self->mount);
209   g_clear_object (&self->object);
210   g_clear_object (&self->symbolic_icon);
211 
212   G_OBJECT_CLASS (photos_source_parent_class)->dispose (object);
213 }
214 
215 
216 static void
photos_source_finalize(GObject * object)217 photos_source_finalize (GObject *object)
218 {
219   PhotosSource *self = PHOTOS_SOURCE (object);
220 
221   g_free (self->id);
222   g_free (self->name);
223 
224   G_OBJECT_CLASS (photos_source_parent_class)->finalize (object);
225 
226   DZL_COUNTER_DEC (instances);
227 }
228 
229 
230 static void
photos_source_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)231 photos_source_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
232 {
233   PhotosSource *self = PHOTOS_SOURCE (object);
234 
235   switch (prop_id)
236     {
237     case PROP_BUILTIN:
238       g_value_set_boolean (value, self->builtin);
239       break;
240 
241     case PROP_ID:
242       g_value_set_string (value, self->id);
243       break;
244 
245     case PROP_MOUNT:
246       g_value_set_object (value, self->mount);
247       break;
248 
249     case PROP_NAME:
250       g_value_set_string (value, self->name);
251       break;
252 
253     case PROP_OBJECT:
254       g_value_set_object (value, (gpointer) self->object);
255       break;
256 
257     default:
258       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
259       break;
260     }
261 }
262 
263 
264 static void
photos_source_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)265 photos_source_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
266 {
267   PhotosSource *self = PHOTOS_SOURCE (object);
268 
269   switch (prop_id)
270     {
271     case PROP_BUILTIN:
272       self->builtin = g_value_get_boolean (value);
273       break;
274 
275     case PROP_ID:
276       {
277         const gchar *id;
278 
279         id = g_value_get_string (value);
280         g_return_if_fail (self->id == NULL || id == NULL);
281 
282         if (self->id == NULL && id != NULL)
283           self->id = g_strdup (id);
284 
285         break;
286       }
287 
288     case PROP_MOUNT:
289       {
290         g_autoptr (GFile) root = NULL;
291         const gchar *type_name;
292         g_autofree gchar *uri = NULL;
293 
294         self->mount = G_MOUNT (g_value_dup_object (value));
295         if (self->mount == NULL)
296           break;
297 
298         type_name = G_OBJECT_TYPE_NAME (self->mount);
299         root = g_mount_get_root (self->mount);
300         uri = g_file_get_uri (root);
301         self->id = g_strdup_printf ("gd:g-mount:%s:%s", type_name, uri);
302 
303         self->icon = g_mount_get_icon (self->mount);
304         self->symbolic_icon = g_mount_get_symbolic_icon (self->mount);
305         self->name = g_mount_get_name (self->mount);
306         break;
307       }
308 
309     case PROP_NAME:
310       {
311         const gchar *name;
312 
313         name = g_value_get_string (value);
314         g_return_if_fail (self->name == NULL || name == NULL);
315 
316         if (self->name == NULL && name != NULL)
317           self->name = g_strdup (name);
318 
319         break;
320       }
321 
322     case PROP_OBJECT:
323       {
324         GoaAccount *account;
325         const gchar *provider_icon;
326         const gchar *provider_name;
327         const gchar *id;
328 
329         self->object = GOA_OBJECT (g_value_dup_object (value));
330         if (self->object == NULL)
331           break;
332 
333         g_return_if_fail (self->id == NULL);
334         g_return_if_fail (self->name == NULL);
335 
336         account = goa_object_peek_account (self->object);
337 
338         id = goa_account_get_id (account);
339         self->id = g_strdup_printf ("gd:goa-account:%s", id);
340 
341         provider_icon = goa_account_get_provider_icon (account);
342 
343           {
344             g_autoptr (GError) error = NULL;
345 
346             self->icon = g_icon_new_for_string (provider_icon, &error);
347             if (error != NULL)
348               g_critical ("Unable to generate a GIcon for %s: %s", id, error->message);
349           }
350 
351         provider_name = goa_account_get_provider_name (account);
352         self->name = g_strdup (provider_name);
353         break;
354       }
355 
356     default:
357       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358       break;
359     }
360 }
361 
362 
363 static void
photos_source_init(PhotosSource * self)364 photos_source_init (PhotosSource *self)
365 {
366   DZL_COUNTER_INC (instances);
367 }
368 
369 
370 static void
photos_source_class_init(PhotosSourceClass * class)371 photos_source_class_init (PhotosSourceClass *class)
372 {
373   GObjectClass *object_class = G_OBJECT_CLASS (class);
374 
375   object_class->dispose = photos_source_dispose;
376   object_class->finalize = photos_source_finalize;
377   object_class->get_property = photos_source_get_property;
378   object_class->set_property = photos_source_set_property;
379 
380   g_object_class_install_property (object_class,
381                                    PROP_BUILTIN,
382                                    g_param_spec_boolean ("builtin",
383                                                          "",
384                                                          "",
385                                                          FALSE,
386                                                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
387 
388   g_object_class_install_property (object_class,
389                                    PROP_ID,
390                                    g_param_spec_string ("id",
391                                                         "",
392                                                         "",
393                                                         NULL,
394                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
395 
396   g_object_class_install_property (object_class,
397                                    PROP_MOUNT,
398                                    g_param_spec_object ("mount",
399                                                         "GMount instance",
400                                                         "A mount point representing a removable device (eg., camera)",
401                                                         G_TYPE_MOUNT,
402                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
403 
404   g_object_class_install_property (object_class,
405                                    PROP_NAME,
406                                    g_param_spec_string ("name",
407                                                         "",
408                                                         "",
409                                                         NULL,
410                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
411 
412   g_object_class_install_property (object_class,
413                                    PROP_OBJECT,
414                                    g_param_spec_object ("object",
415                                                         "GoaObject instance",
416                                                         "A GOA configured account from which the source was created",
417                                                         GOA_TYPE_OBJECT,
418                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
419 }
420 
421 
422 static void
photos_source_filterable_iface_init(PhotosFilterableInterface * iface)423 photos_source_filterable_iface_init (PhotosFilterableInterface *iface)
424 {
425   iface->get_builtin = photos_source_get_builtin;
426   iface->get_filter = photos_source_get_filter;
427   iface->get_id = photos_source_get_id;
428   iface->is_search_criterion = photos_source_is_search_criterion;
429 }
430 
431 
432 PhotosSource *
photos_source_new(const gchar * id,const gchar * name,gboolean builtin)433 photos_source_new (const gchar *id, const gchar *name, gboolean builtin)
434 {
435   return g_object_new (PHOTOS_TYPE_SOURCE, "id", id, "name", name, "builtin", builtin, NULL);
436 }
437 
438 
439 PhotosSource *
photos_source_new_from_goa_object(GoaObject * object)440 photos_source_new_from_goa_object (GoaObject *object)
441 {
442   g_return_val_if_fail (GOA_IS_OBJECT (object), NULL);
443   return g_object_new (PHOTOS_TYPE_SOURCE, "object", object, NULL);
444 }
445 
446 
447 PhotosSource *
photos_source_new_from_mount(GMount * mount)448 photos_source_new_from_mount (GMount *mount)
449 {
450   g_return_val_if_fail (G_IS_MOUNT (mount), NULL);
451   return g_object_new (PHOTOS_TYPE_SOURCE, "mount", mount, NULL);
452 }
453 
454 
455 const gchar *
photos_source_get_name(PhotosSource * self)456 photos_source_get_name (PhotosSource *self)
457 {
458   return self->name;
459 }
460 
461 
462 GoaObject *
photos_source_get_goa_object(PhotosSource * self)463 photos_source_get_goa_object (PhotosSource *self)
464 {
465   return self->object;
466 }
467 
468 
469 GIcon *
photos_source_get_icon(PhotosSource * self)470 photos_source_get_icon (PhotosSource *self)
471 {
472   return self->icon;
473 }
474 
475 
476 GMount *
photos_source_get_mount(PhotosSource * self)477 photos_source_get_mount (PhotosSource *self)
478 {
479   return self->mount;
480 }
481 
482 
483 GIcon *
photos_source_get_symbolic_icon(PhotosSource * self)484 photos_source_get_symbolic_icon (PhotosSource *self)
485 {
486   return self->symbolic_icon;
487 }
488