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