1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2009-2013 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU Lesser General Public License Version 2.1
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * SECTION:cd-icc-store
24 * @short_description: An object to monitor a directory full of ICC profiles
25 */
26
27 #include "config.h"
28
29 #include <glib-object.h>
30 #include <gio/gio.h>
31
32 #include "cd-icc-store.h"
33
34 static void cd_icc_store_finalize (GObject *object);
35
36 #define GET_PRIVATE(o) (cd_icc_store_get_instance_private (o))
37
38 typedef struct
39 {
40 CdIccLoadFlags load_flags;
41 GPtrArray *directory_array;
42 GPtrArray *icc_array;
43 GResource *cache;
44 } CdIccStorePrivate;
45
46 enum {
47 SIGNAL_ADDED,
48 SIGNAL_REMOVED,
49 SIGNAL_LAST
50 };
51
52 static guint signals[SIGNAL_LAST] = { 0 };
53
54 G_DEFINE_TYPE_WITH_PRIVATE (CdIccStore, cd_icc_store, G_TYPE_OBJECT)
55
56 #define CD_ICC_STORE_MAX_RECURSION_LEVELS 2
57
58 static gboolean
59 cd_icc_store_search_path (CdIccStore *store,
60 const gchar *path,
61 guint depth,
62 GCancellable *cancellable,
63 GError **error);
64 static gboolean
65 cd_icc_store_search_path_child (CdIccStore *store,
66 const gchar *path,
67 GFileInfo *info,
68 guint depth,
69 GCancellable *cancellable,
70 GError **error);
71
72 typedef struct {
73 gchar *path;
74 GFileMonitor *monitor;
75 } CdIccStoreDirHelper;
76
77 /**
78 * cd_icc_store_helper_free:
79 **/
80 static void
cd_icc_store_helper_free(CdIccStoreDirHelper * helper)81 cd_icc_store_helper_free (CdIccStoreDirHelper *helper)
82 {
83 g_free (helper->path);
84 if (helper->monitor != NULL)
85 g_object_unref (helper->monitor);
86 g_free (helper);
87 }
88
89 /**
90 * cd_icc_store_find_by_filename:
91 * @store: a #CdIccStore instance.
92 * @filename: a fully qualified filename
93 *
94 * Finds a ICC object in the store by filename.
95 *
96 * Return value: (transfer full): an ICC profile object or %NULL
97 *
98 * Since: 1.0.2
99 **/
100 CdIcc *
cd_icc_store_find_by_filename(CdIccStore * store,const gchar * filename)101 cd_icc_store_find_by_filename (CdIccStore *store, const gchar *filename)
102 {
103 CdIccStorePrivate *priv = GET_PRIVATE (store);
104 CdIcc *tmp;
105 guint i;
106 GPtrArray *array = priv->icc_array;
107
108 g_return_val_if_fail (CD_IS_ICC_STORE (store), NULL);
109 g_return_val_if_fail (filename != NULL, NULL);
110
111 for (i = 0; i < array->len; i++) {
112 tmp = g_ptr_array_index (array, i);
113 if (g_strcmp0 (filename, cd_icc_get_filename (tmp)) == 0)
114 return g_object_ref (tmp);
115 }
116 return NULL;
117 }
118
119 /**
120 * cd_icc_store_find_by_checksum:
121 * @store: a #CdIccStore instance.
122 * @checksum: a checksum value
123 *
124 * Finds a ICC object in the store by checksum.
125 *
126 * Return value: (transfer full): an ICC profile object or %NULL
127 *
128 * Since: 1.0.2
129 **/
130 CdIcc *
cd_icc_store_find_by_checksum(CdIccStore * store,const gchar * checksum)131 cd_icc_store_find_by_checksum (CdIccStore *store, const gchar *checksum)
132 {
133 CdIccStorePrivate *priv = GET_PRIVATE (store);
134 CdIcc *tmp;
135 guint i;
136 GPtrArray *array = priv->icc_array;
137
138 g_return_val_if_fail (CD_IS_ICC_STORE (store), NULL);
139 g_return_val_if_fail (checksum != NULL, NULL);
140
141 for (i = 0; i < array->len; i++) {
142 tmp = g_ptr_array_index (array, i);
143 if (g_strcmp0 (checksum, cd_icc_get_checksum (tmp)) == 0)
144 return g_object_ref (tmp);
145 }
146 return NULL;
147 }
148
149 /**
150 * cd_icc_store_find_by_directory:
151 **/
152 static CdIccStoreDirHelper *
cd_icc_store_find_by_directory(CdIccStore * store,const gchar * path)153 cd_icc_store_find_by_directory (CdIccStore *store, const gchar *path)
154 {
155 CdIccStorePrivate *priv = GET_PRIVATE (store);
156 CdIccStoreDirHelper *tmp;
157 guint i;
158 GPtrArray *array = priv->directory_array;
159
160 for (i = 0; i < array->len; i++) {
161 tmp = g_ptr_array_index (array, i);
162 if (g_strcmp0 (path, tmp->path) == 0)
163 return tmp;
164 }
165 return NULL;
166 }
167
168 /**
169 * cd_icc_store_remove_icc:
170 **/
171 static gboolean
cd_icc_store_remove_icc(CdIccStore * store,const gchar * filename)172 cd_icc_store_remove_icc (CdIccStore *store, const gchar *filename)
173 {
174 CdIccStorePrivate *priv = GET_PRIVATE (store);
175 g_autoptr(CdIcc) icc = NULL;
176
177 /* find exact pointer */
178 icc = cd_icc_store_find_by_filename (store, filename);
179 if (icc == NULL)
180 return FALSE;
181
182 /* we have a ref so we can emit the signal */
183 if (!g_ptr_array_remove (priv->icc_array, icc)) {
184 g_warning ("failed to remove %s", filename);
185 return FALSE;
186 }
187
188 /* emit a signal */
189 g_signal_emit (store, signals[SIGNAL_REMOVED], 0, icc);
190 return TRUE;
191 }
192
193 /**
194 * cd_icc_store_add_icc:
195 **/
196 static gboolean
cd_icc_store_add_icc(CdIccStore * store,GFile * file,GError ** error)197 cd_icc_store_add_icc (CdIccStore *store, GFile *file, GError **error)
198 {
199 CdIccStorePrivate *priv = GET_PRIVATE (store);
200 g_autoptr(GBytes) data = NULL;
201 g_autofree gchar *filename = NULL;
202 g_autoptr(CdIcc) icc = NULL;
203 g_autoptr(CdIcc) icc_tmp = NULL;
204
205 /* use the GResource cache if available */
206 icc = cd_icc_new ();
207 filename = g_file_get_path (file);
208 if (priv->cache != NULL) {
209 if (g_str_has_prefix (filename, "/usr/local/share/color/icc/colord/")) {
210 g_autofree gchar *cache_key = NULL;
211 cache_key = g_build_filename ("/org/freedesktop/colord",
212 "profiles",
213 filename + 28,
214 NULL);
215 data = g_resource_lookup_data (priv->cache,
216 cache_key,
217 G_RESOURCE_LOOKUP_FLAGS_NONE,
218 NULL);
219 }
220 }
221
222 /* parse new icc object */
223 if (data != NULL) {
224 g_autofree gchar *basename = NULL;
225 basename = g_path_get_basename (filename);
226 g_debug ("Using built-in %s", basename);
227 cd_icc_set_filename (icc, filename);
228 if (!cd_icc_load_data (icc,
229 g_bytes_get_data (data, NULL),
230 g_bytes_get_size (data),
231 CD_ICC_LOAD_FLAGS_METADATA,
232 error)) {
233 return FALSE;
234 }
235 } else {
236 if (!cd_icc_load_file (icc,
237 file,
238 priv->load_flags,
239 NULL,
240 error)) {
241 return FALSE;
242 }
243 }
244
245 /* check it's not a duplicate */
246 icc_tmp = cd_icc_store_find_by_checksum (store, cd_icc_get_checksum (icc));
247 if (icc_tmp != NULL) {
248 g_debug ("CdIccStore: Failed to add %s as profile %s "
249 "already exists with the same checksum of %s",
250 filename,
251 cd_icc_get_filename (icc_tmp),
252 cd_icc_get_checksum (icc_tmp));
253 return TRUE;
254 }
255
256 /* add to list */
257 g_ptr_array_add (priv->icc_array, g_object_ref (icc));
258
259 /* emit a signal */
260 g_signal_emit (store, signals[SIGNAL_ADDED], 0, icc);
261 return TRUE;
262 }
263
264 /**
265 * cd_icc_store_created_query_info_cb:
266 **/
267 static void
cd_icc_store_created_query_info_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)268 cd_icc_store_created_query_info_cb (GObject *source_object,
269 GAsyncResult *res,
270 gpointer user_data)
271 {
272 CdIccStore *store = CD_ICC_STORE (user_data);
273 GFile *file = G_FILE (source_object);
274 gboolean ret;
275 g_autoptr(GError) error = NULL;
276 g_autofree gchar *path = NULL;
277 g_autoptr(GFileInfo) info = NULL;
278 g_autoptr(GFile) parent = NULL;
279
280 info = g_file_query_info_finish (file, res, NULL);
281 if (info == NULL)
282 return;
283 parent = g_file_get_parent (file);
284 path = g_file_get_path (parent);
285 ret = cd_icc_store_search_path_child (store, path, info,
286 0, NULL, &error);
287 if (!ret)
288 g_warning ("failed to search file: %s", error->message);
289 }
290
291 /**
292 * cd_icc_store_remove_from_prefix:
293 **/
294 static void
cd_icc_store_remove_from_prefix(CdIccStore * store,const gchar * prefix)295 cd_icc_store_remove_from_prefix (CdIccStore *store, const gchar *prefix)
296 {
297 CdIccStorePrivate *priv = GET_PRIVATE (store);
298 CdIcc *tmp;
299 const gchar *filename;
300 guint i;
301
302 for (i = 0; i < priv->icc_array->len; i++) {
303 tmp = g_ptr_array_index (priv->icc_array, i);
304 filename = cd_icc_get_filename (tmp);
305 if (g_str_has_prefix (filename, prefix)) {
306 g_debug ("auto-removed %s as path removed", prefix);
307 cd_icc_store_remove_icc (store, filename);
308 }
309 }
310 }
311
312 /**
313 * cd_icc_store_file_monitor_changed_cb:
314 **/
315 static void
cd_icc_store_file_monitor_changed_cb(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,CdIccStore * store)316 cd_icc_store_file_monitor_changed_cb (GFileMonitor *monitor,
317 GFile *file,
318 GFile *other_file,
319 GFileMonitorEvent event_type,
320 CdIccStore *store)
321 {
322 CdIccStorePrivate *priv = GET_PRIVATE (store);
323 CdIcc *tmp;
324 CdIccStoreDirHelper *helper;
325 g_autofree gchar *path = NULL;
326
327 /* icc was deleted */
328 if (event_type == G_FILE_MONITOR_EVENT_DELETED) {
329
330 /* we can either have two things here, a directory or a
331 * file. We can't call g_file_query_info_async() as the
332 * inode doesn't exist anymore */
333 path = g_file_get_path (file);
334 tmp = cd_icc_store_find_by_filename (store, path);
335 if (tmp != NULL) {
336 /* is a file */
337 cd_icc_store_remove_icc (store, path);
338 return;
339 }
340
341 /* is a directory, urgh. Remove all ICCs there. */
342 cd_icc_store_remove_from_prefix (store, path);
343 helper = cd_icc_store_find_by_directory (store, path);
344 if (helper != NULL) {
345 g_ptr_array_remove (priv->directory_array,
346 helper);
347 }
348 return;
349 }
350
351 /* ignore temp files */
352 path = g_file_get_path (file);
353 if (g_strrstr (path, ".goutputstream") != NULL) {
354 g_debug ("ignoring gvfs temporary file");
355 return;
356 }
357
358 /* only care about created objects */
359 if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
360 g_file_query_info_async (file,
361 G_FILE_ATTRIBUTE_STANDARD_NAME ","
362 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
363 G_FILE_ATTRIBUTE_STANDARD_TYPE,
364 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
365 G_PRIORITY_LOW,
366 NULL,
367 cd_icc_store_created_query_info_cb,
368 store);
369 return;
370 }
371 }
372
373 /**
374 * cd_icc_store_search_path_child:
375 **/
376 static gboolean
cd_icc_store_search_path_child(CdIccStore * store,const gchar * path,GFileInfo * info,guint depth,GCancellable * cancellable,GError ** error)377 cd_icc_store_search_path_child (CdIccStore *store,
378 const gchar *path,
379 GFileInfo *info,
380 guint depth,
381 GCancellable *cancellable,
382 GError **error)
383 {
384 const gchar *name;
385 const gchar *type;
386 g_autofree gchar *full_path = NULL;
387 g_autoptr(GFile) file = NULL;
388
389 /* further down the worm-hole */
390 name = g_file_info_get_name (info);
391 full_path = g_build_filename (path, name, NULL);
392 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
393 return cd_icc_store_search_path (store,
394 full_path,
395 depth + 1,
396 cancellable,
397 error);
398 }
399
400 /* ignore temp files */
401 if (g_strrstr (full_path, ".goutputstream") != NULL) {
402 g_debug ("ignoring gvfs temporary file");
403 return TRUE;
404 }
405
406 /* check type */
407 type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
408 if (g_strcmp0 (type, "application/vnd.iccprofile") != 0) {
409 g_debug ("Incorrect content type for %s, got %s", full_path, type);
410 return TRUE;
411 }
412
413 /* is a file */
414 file = g_file_new_for_path (full_path);
415 return cd_icc_store_add_icc (store, file, error);
416 }
417
418 /**
419 * cd_icc_store_search_path:
420 **/
421 static gboolean
cd_icc_store_search_path(CdIccStore * store,const gchar * path,guint depth,GCancellable * cancellable,GError ** error)422 cd_icc_store_search_path (CdIccStore *store,
423 const gchar *path,
424 guint depth,
425 GCancellable *cancellable,
426 GError **error)
427 {
428 CdIccStorePrivate *priv = GET_PRIVATE (store);
429 CdIccStoreDirHelper *helper;
430 GError *error_local = NULL;
431 gboolean ret = TRUE;
432 g_autoptr(GFileEnumerator) enumerator = NULL;
433 g_autoptr(GFile) file = NULL;
434 g_autoptr(GFileInfo) info = NULL;
435
436 /* check sanity */
437 if (depth > CD_ICC_STORE_MAX_RECURSION_LEVELS) {
438 ret = FALSE;
439 g_set_error (error,
440 CD_ICC_ERROR,
441 CD_ICC_ERROR_FAILED_TO_OPEN,
442 "cannot recurse more than %i levels deep",
443 CD_ICC_STORE_MAX_RECURSION_LEVELS);
444 goto out;
445 }
446
447 /* add an inotify watch if not already added */
448 file = g_file_new_for_path (path);
449 helper = cd_icc_store_find_by_directory (store, path);
450 if (helper == NULL) {
451 helper = g_new0 (CdIccStoreDirHelper, 1);
452 helper->path = g_strdup (path);
453 helper->monitor = g_file_monitor_directory (file,
454 G_FILE_MONITOR_NONE,
455 NULL,
456 error);
457 if (helper->monitor == NULL) {
458 ret = FALSE;
459 cd_icc_store_helper_free (helper);
460 goto out;
461 }
462 g_signal_connect (helper->monitor, "changed",
463 G_CALLBACK(cd_icc_store_file_monitor_changed_cb),
464 store);
465 g_ptr_array_add (priv->directory_array, helper);
466 }
467
468 /* get contents of directory */
469 enumerator = g_file_enumerate_children (file,
470 G_FILE_ATTRIBUTE_STANDARD_NAME ","
471 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
472 G_FILE_ATTRIBUTE_STANDARD_TYPE,
473 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
474 cancellable,
475 error);
476 if (enumerator == NULL) {
477 ret = FALSE;
478 helper = cd_icc_store_find_by_directory (store, path);
479 if (helper != NULL)
480 g_ptr_array_remove (priv->directory_array, helper);
481 goto out;
482 }
483
484 /* get all the files */
485 while (TRUE) {
486 info = g_file_enumerator_next_file (enumerator,
487 cancellable,
488 &error_local);
489 if (info == NULL && error_local != NULL) {
490 ret = FALSE;
491 g_propagate_error (error, error_local);
492 goto out;
493 }
494
495 /* special value, meaning "no more files to process" */
496 if (info == NULL)
497 break;
498
499 /* process this child */
500 ret = cd_icc_store_search_path_child (store,
501 path,
502 info,
503 depth,
504 cancellable,
505 error);
506 g_object_unref (info);
507 if (!ret)
508 goto out;
509 }
510 out:
511 return ret;
512 }
513
514 /**
515 * cd_icc_store_set_load_flags:
516 * @store: a #CdIccStore instance.
517 * @load_flags: #CdIccLoadFlags, e.g. %CD_ICC_LOAD_FLAGS_TRANSLATIONS
518 *
519 * Sets the load flags to use when loading newly added profiles
520 *
521 * Since: 1.0.2
522 **/
523 void
cd_icc_store_set_load_flags(CdIccStore * store,CdIccLoadFlags load_flags)524 cd_icc_store_set_load_flags (CdIccStore *store, CdIccLoadFlags load_flags)
525 {
526 CdIccStorePrivate *priv = GET_PRIVATE (store);
527 g_return_if_fail (CD_IS_ICC_STORE (store));
528 priv->load_flags = load_flags | CD_ICC_LOAD_FLAGS_FALLBACK_MD5;
529 }
530
531 /**
532 * cd_icc_store_get_load_flags:
533 * @store: a #CdIccStore instance.
534 *
535 * Gets the load flags to use when loading newly added profiles
536 *
537 * Return value: the load flags to use
538 *
539 * Since: 1.0.2
540 **/
541 CdIccLoadFlags
cd_icc_store_get_load_flags(CdIccStore * store)542 cd_icc_store_get_load_flags (CdIccStore *store)
543 {
544 CdIccStorePrivate *priv = GET_PRIVATE (store);
545 g_return_val_if_fail (CD_IS_ICC_STORE (store), 0);
546 return priv->load_flags;
547 }
548
549 /**
550 * cd_icc_store_set_cache:
551 * @store: a #CdIccStore instance.
552 * @cache: a #GResource
553 *
554 * Sets an optional cache to use when reading profiles. This is probably
555 * only useful to the colord daemon. This function can only be called once.
556 *
557 * Since: 1.0.2
558 **/
559 void
cd_icc_store_set_cache(CdIccStore * store,GResource * cache)560 cd_icc_store_set_cache (CdIccStore *store, GResource *cache)
561 {
562 CdIccStorePrivate *priv = GET_PRIVATE (store);
563 g_return_if_fail (CD_IS_ICC_STORE (store));
564 g_return_if_fail (priv->cache == NULL);
565 priv->cache = g_resource_ref (cache);
566 }
567
568 /**
569 * cd_icc_store_get_all:
570 * @store: a #CdIccStore instance.
571 *
572 * Gets the list of #CdIcc objects in the store
573 *
574 * Return value: (transfer container) (element-type CdIcc): ICC profile objects
575 *
576 * Since: 1.0.2
577 **/
578 GPtrArray *
cd_icc_store_get_all(CdIccStore * store)579 cd_icc_store_get_all (CdIccStore *store)
580 {
581 CdIccStorePrivate *priv = GET_PRIVATE (store);
582 g_return_val_if_fail (CD_IS_ICC_STORE (store), NULL);
583 return g_ptr_array_ref (priv->icc_array);
584 }
585
586 /**
587 * cd_icc_store_search_kind:
588 * @store: a #CdIccStore instance.
589 * @search_kind: a #CdIccStoreSearchKind, e.g. %CD_ICC_STORE_SEARCH_KIND_USER
590 * @search_flags: a #CdIccStoreSearchFlags, e.g. %CD_ICC_STORE_SEARCH_FLAGS_CREATE_LOCATION
591 * @cancellable: A #GCancellable or %NULL
592 * @error: A #GError or %NULL
593 *
594 * Adds a location to be watched for ICC profiles
595 *
596 * Return value: %TRUE for success
597 *
598 * Since: 1.0.2
599 **/
600 gboolean
cd_icc_store_search_kind(CdIccStore * store,CdIccStoreSearchKind search_kind,CdIccStoreSearchFlags search_flags,GCancellable * cancellable,GError ** error)601 cd_icc_store_search_kind (CdIccStore *store,
602 CdIccStoreSearchKind search_kind,
603 CdIccStoreSearchFlags search_flags,
604 GCancellable *cancellable,
605 GError **error)
606 {
607 gchar *tmp;
608 guint i;
609 g_autoptr(GPtrArray) locations = NULL;
610
611 g_return_val_if_fail (CD_IS_ICC_STORE (store), FALSE);
612 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
613
614 /* get the locations for each kind */
615 locations = g_ptr_array_new_with_free_func (g_free);
616 switch (search_kind) {
617 case CD_ICC_STORE_SEARCH_KIND_USER:
618 tmp = g_build_filename (g_get_user_data_dir (), "icc", NULL);
619 g_ptr_array_add (locations, tmp);
620 tmp = g_build_filename (g_get_home_dir (), ".color", "icc", NULL);
621 g_ptr_array_add (locations, tmp);
622 break;
623 case CD_ICC_STORE_SEARCH_KIND_MACHINE:
624 g_ptr_array_add (locations, g_strdup (CD_SYSTEM_PROFILES_DIR));
625 g_ptr_array_add (locations, g_strdup ("/var/db/color/icc"));
626 break;
627 case CD_ICC_STORE_SEARCH_KIND_SYSTEM:
628 g_ptr_array_add (locations, g_strdup ("/usr/local/share/color/icc"));
629 g_ptr_array_add (locations, g_strdup ("/usr/local/share/color/icc"));
630 g_ptr_array_add (locations, g_strdup ("/Library/ColorSync/Profiles/Displays"));
631 break;
632 default:
633 break;
634 }
635
636 /* add any found locations */
637 for (i = 0; i < locations->len; i++) {
638 tmp = g_ptr_array_index (locations, i);
639 if (!cd_icc_store_search_location (store,
640 tmp,
641 search_flags,
642 cancellable,
643 error))
644 return FALSE;
645
646 /* only create the first location */
647 search_flags &= ~CD_ICC_STORE_SEARCH_FLAGS_CREATE_LOCATION;
648 }
649 return TRUE;
650 }
651
652 /**
653 * cd_icc_store_search_location:
654 * @store: a #CdIccStore instance.
655 * @location: a fully qualified path
656 * @search_flags: #CdIccStoreSearchFlags, e.g. %CD_ICC_STORE_SEARCH_FLAGS_CREATE_LOCATION
657 * @cancellable: A #GCancellable or %NULL
658 * @error: A #GError or %NULL
659 *
660 * Adds a location to be watched for ICC profiles
661 *
662 * Return value: %TRUE for success
663 *
664 * Since: 1.0.2
665 **/
666 gboolean
cd_icc_store_search_location(CdIccStore * store,const gchar * location,CdIccStoreSearchFlags search_flags,GCancellable * cancellable,GError ** error)667 cd_icc_store_search_location (CdIccStore *store,
668 const gchar *location,
669 CdIccStoreSearchFlags search_flags,
670 GCancellable *cancellable,
671 GError **error)
672 {
673 g_autoptr(GFile) file = NULL;
674
675 g_return_val_if_fail (CD_IS_ICC_STORE (store), FALSE);
676 g_return_val_if_fail (location != NULL, FALSE);
677 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
678
679 /* does folder exist? */
680 file = g_file_new_for_path (location);
681 if (!g_file_query_exists (file, cancellable)) {
682 if ((search_flags & CD_ICC_STORE_SEARCH_FLAGS_CREATE_LOCATION) > 0) {
683 if (!g_file_make_directory_with_parents (file, cancellable, error))
684 return FALSE;
685 } else {
686 /* the directory does not exist */
687 return TRUE;
688 }
689 }
690
691 /* search all */
692 return cd_icc_store_search_path (store, location, 0, cancellable, error);
693 }
694
695 /**
696 * cd_icc_store_class_init:
697 **/
698 static void
cd_icc_store_class_init(CdIccStoreClass * klass)699 cd_icc_store_class_init (CdIccStoreClass *klass)
700 {
701 GObjectClass *object_class = G_OBJECT_CLASS (klass);
702 object_class->finalize = cd_icc_store_finalize;
703
704 /**
705 * CdIccStore::added:
706 * @profile: the #CdIccStore instance that emitted the signal
707 * @icc: the #CdIcc that was added
708 *
709 * The ::added signal is emitted when an ICC profile has been added.
710 *
711 * Since: 1.0.2
712 **/
713 signals[SIGNAL_ADDED] =
714 g_signal_new ("added",
715 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
716 G_STRUCT_OFFSET (CdIccStoreClass, added),
717 NULL, NULL, g_cclosure_marshal_generic,
718 G_TYPE_NONE, 1, CD_TYPE_ICC);
719 /**
720 * CdIccStore::removed:
721 * @profile: the #CdIccStore instance that emitted the signal
722 * @icc: the #CdIcc that was removed
723 *
724 * The ::removed signal is emitted when an ICC profile has been removed.
725 *
726 * Since: 1.0.2
727 **/
728 signals[SIGNAL_REMOVED] =
729 g_signal_new ("removed",
730 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
731 G_STRUCT_OFFSET (CdIccStoreClass, removed),
732 NULL, NULL, g_cclosure_marshal_generic,
733 G_TYPE_NONE, 1, CD_TYPE_ICC);
734 }
735
736 /**
737 * cd_icc_store_init:
738 **/
739 static void
cd_icc_store_init(CdIccStore * store)740 cd_icc_store_init (CdIccStore *store)
741 {
742 CdIccStorePrivate *priv = GET_PRIVATE (store);
743 priv->load_flags = CD_ICC_LOAD_FLAGS_FALLBACK_MD5;
744 priv->icc_array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
745 priv->directory_array = g_ptr_array_new_with_free_func ((GDestroyNotify) cd_icc_store_helper_free);
746 }
747
748 /**
749 * cd_icc_store_finalize:
750 **/
751 static void
cd_icc_store_finalize(GObject * object)752 cd_icc_store_finalize (GObject *object)
753 {
754 CdIccStore *store = CD_ICC_STORE (object);
755 CdIccStorePrivate *priv = GET_PRIVATE (store);
756
757 g_ptr_array_unref (priv->icc_array);
758 g_ptr_array_unref (priv->directory_array);
759 if (priv->cache != NULL)
760 g_resource_unref (priv->cache);
761
762 G_OBJECT_CLASS (cd_icc_store_parent_class)->finalize (object);
763 }
764
765 /**
766 * cd_icc_store_new:
767 *
768 * Creates a new #CdIccStore object.
769 *
770 * Return value: a new CdIccStore object.
771 *
772 * Since: 1.0.2
773 **/
774 CdIccStore *
cd_icc_store_new(void)775 cd_icc_store_new (void)
776 {
777 CdIccStore *store;
778 store = g_object_new (CD_TYPE_ICC_STORE, NULL);
779 return CD_ICC_STORE (store);
780 }
781