1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2010-2014 Richard Hughes <richard@hughsie.com>
4  *
5  * Licensed under the GNU General Public License Version 2
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program 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
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include "config.h"
23 
24 #include <gio/gio.h>
25 #include <glib-object.h>
26 #include <lcms2.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #ifdef HAVE_PWD_H
30 #include <pwd.h>
31 #endif
32 #include <math.h>
33 
34 #include "cd-common.h"
35 #include "cd-profile.h"
36 #include "cd-profile-db.h"
37 #include "cd-resources.h"
38 
39 static void	cd_profile_finalize	(GObject	*object);
40 static void	cd_profile_set_filename	(CdProfile	*profile,
41 					 const gchar	*filename);
42 
43 #define GET_PRIVATE(o) (cd_profile_get_instance_private (o))
44 
45 /**
46  * CdProfilePrivate:
47  *
48  * Private #CdProfile data
49  **/
50 typedef struct
51 {
52 	CdObjectScope			 object_scope;
53 	gchar				*filename;
54 	gchar				*id;
55 	gchar				*object_path;
56 	gchar				*qualifier;
57 	gchar				*format;
58 	gchar				*checksum;
59 	gchar				*title;
60 	GDBusConnection			*connection;
61 	guint				 registration_id;
62 	guint				 watcher_id;
63 	CdProfileKind			 kind;
64 	CdColorspace			 colorspace;
65 	GHashTable			*metadata;
66 	gboolean			 has_vcgt;
67 	gboolean			 is_system_wide;
68 	gint64				 created;
69 	guint				 owner;
70 	gchar				**warnings;
71 	GMappedFile			*mapped_file;
72 	guint				 score;
73 	CdProfileDb			*db;
74 } CdProfilePrivate;
75 
76 enum {
77 	SIGNAL_INVALIDATE,
78 	SIGNAL_LAST
79 };
80 
81 enum {
82 	PROP_0,
83 	PROP_OBJECT_PATH,
84 	PROP_ID,
85 	PROP_QUALIFIER,
86 	PROP_TITLE,
87 	PROP_FILENAME,
88 	PROP_LAST
89 };
90 
91 static guint signals[SIGNAL_LAST] = { 0 };
G_DEFINE_TYPE_WITH_PRIVATE(CdProfile,cd_profile,G_TYPE_OBJECT)92 G_DEFINE_TYPE_WITH_PRIVATE (CdProfile, cd_profile, G_TYPE_OBJECT)
93 
94 /**
95  * cd_profile_error_quark:
96  **/
97 GQuark
98 cd_profile_error_quark (void)
99 {
100 	guint i;
101 	static GQuark quark = 0;
102 	if (!quark) {
103 		quark = g_quark_from_static_string ("CdProfile");
104 		for (i = 0; i < CD_PROFILE_ERROR_LAST; i++) {
105 			g_dbus_error_register_error (quark,
106 						     i,
107 						     cd_profile_error_to_string (i));
108 		}
109 	}
110 	return quark;
111 }
112 
113 /**
114  * cd_profile_get_scope:
115  **/
116 CdObjectScope
cd_profile_get_scope(CdProfile * profile)117 cd_profile_get_scope (CdProfile *profile)
118 {
119 	CdProfilePrivate *priv = GET_PRIVATE (profile);
120 	g_return_val_if_fail (CD_IS_PROFILE (profile), 0);
121 	return priv->object_scope;
122 }
123 
124 /**
125  * cd_profile_set_scope:
126  **/
127 void
cd_profile_set_scope(CdProfile * profile,CdObjectScope object_scope)128 cd_profile_set_scope (CdProfile *profile, CdObjectScope object_scope)
129 {
130 	CdProfilePrivate *priv = GET_PRIVATE (profile);
131 	g_return_if_fail (CD_IS_PROFILE (profile));
132 	priv->object_scope = object_scope;
133 }
134 
135 /**
136  * cd_profile_get_owner:
137  **/
138 guint
cd_profile_get_owner(CdProfile * profile)139 cd_profile_get_owner (CdProfile *profile)
140 {
141 	CdProfilePrivate *priv = GET_PRIVATE (profile);
142 	g_return_val_if_fail (CD_IS_PROFILE (profile), G_MAXUINT);
143 	return priv->owner;
144 }
145 
146 /**
147  * cd_profile_set_owner:
148  **/
149 void
cd_profile_set_owner(CdProfile * profile,guint owner)150 cd_profile_set_owner (CdProfile *profile, guint owner)
151 {
152 	CdProfilePrivate *priv = GET_PRIVATE (profile);
153 	g_return_if_fail (CD_IS_PROFILE (profile));
154 	priv->owner = owner;
155 }
156 
157 /**
158  * cd_profile_set_is_system_wide:
159  **/
160 void
cd_profile_set_is_system_wide(CdProfile * profile,gboolean is_system_wide)161 cd_profile_set_is_system_wide (CdProfile *profile, gboolean is_system_wide)
162 {
163 	CdProfilePrivate *priv = GET_PRIVATE (profile);
164 	g_return_if_fail (CD_IS_PROFILE (profile));
165 	priv->is_system_wide = is_system_wide;
166 
167 	/* by default, prefer systemwide profiles over user profiles */
168 	priv->score += 1;
169 }
170 
171 /**
172  * cd_profile_get_is_system_wide:
173  **/
174 gboolean
cd_profile_get_is_system_wide(CdProfile * profile)175 cd_profile_get_is_system_wide (CdProfile *profile)
176 {
177 	CdProfilePrivate *priv = GET_PRIVATE (profile);
178 	g_return_val_if_fail (CD_IS_PROFILE (profile), FALSE);
179 	return priv->is_system_wide;
180 }
181 
182 /**
183  * cd_profile_get_object_path:
184  **/
185 const gchar *
cd_profile_get_object_path(CdProfile * profile)186 cd_profile_get_object_path (CdProfile *profile)
187 {
188 	CdProfilePrivate *priv = GET_PRIVATE (profile);
189 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
190 	return priv->object_path;
191 }
192 
193 /**
194  * cd_profile_get_id:
195  **/
196 const gchar *
cd_profile_get_id(CdProfile * profile)197 cd_profile_get_id (CdProfile *profile)
198 {
199 	CdProfilePrivate *priv = GET_PRIVATE (profile);
200 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
201 	return priv->id;
202 }
203 
204 /**
205  * cd_profile_set_object_path:
206  **/
207 static void
cd_profile_set_object_path(CdProfile * profile)208 cd_profile_set_object_path (CdProfile *profile)
209 {
210 	CdProfilePrivate *priv = GET_PRIVATE (profile);
211 #ifdef HAVE_PWD_H
212 	struct passwd *pw;
213 #endif
214 	g_autofree gchar *path_tmp = NULL;
215 	g_autofree gchar *path_owner = NULL;
216 
217 	/* append the uid to the object path */
218 #ifdef HAVE_PWD_H
219 	pw = getpwuid (priv->owner);
220 	if (priv->owner == 0 ||
221 	    g_strcmp0 (pw->pw_name, DAEMON_USER) == 0) {
222 		path_tmp = g_strdup (priv->id);
223 	} else {
224 		path_tmp = g_strdup_printf ("%s_%s_%u",
225 					    priv->id,
226 					    pw->pw_name,
227 					    priv->owner);
228 	}
229 #else
230 	if (priv->owner == 0) {
231 		path_tmp = g_strdup (priv->id);
232 	} else {
233 		path_tmp = g_strdup_printf ("%s_%d",
234 					    priv->id,
235 					    priv->owner);
236 	}
237 #endif
238 	/* make sure object path is sane */
239 	path_owner = cd_main_ensure_dbus_path (path_tmp);
240 
241 	priv->object_path = g_build_filename (COLORD_DBUS_PATH,
242 						       "profiles",
243 						       path_owner,
244 						       NULL);
245 }
246 
247 /**
248  * cd_profile_set_metadata:
249  **/
250 static void
cd_profile_set_metadata(CdProfile * profile,const gchar * property,const gchar * value)251 cd_profile_set_metadata (CdProfile *profile,
252 			 const gchar *property,
253 			 const gchar *value)
254 {
255 	CdProfilePrivate *priv = GET_PRIVATE (profile);
256 	/* i1Profiler sets this */
257 	if (g_strcmp0 (property, "CreatorApp") == 0)
258 		property = CD_PROFILE_METADATA_CMF_PRODUCT;
259 	g_hash_table_insert (priv->metadata,
260 			     g_strdup (property),
261 			     g_strdup (value));
262 }
263 
264 /**
265  * cd_profile_set_id:
266  **/
267 void
cd_profile_set_id(CdProfile * profile,const gchar * id)268 cd_profile_set_id (CdProfile *profile, const gchar *id)
269 {
270 	CdProfilePrivate *priv = GET_PRIVATE (profile);
271 
272 	CdStandardSpace standard_space = CD_STANDARD_SPACE_UNKNOWN;
273 
274 	g_return_if_fail (CD_IS_PROFILE (profile));
275 
276 	g_free (priv->id);
277 	priv->id = g_strdup (id);
278 
279 	/* all profiles have a score initially */
280 	priv->score = 1;
281 
282 	/* http://www.color.org/srgbprofiles.xalter */
283 	if (g_strcmp0 (id, "icc-34562abf994ccd066d2c5721d0d68c5d") == 0) {
284 		/* sRGB_v4_ICC_preference */
285 		standard_space = CD_STANDARD_SPACE_SRGB;
286 		priv->score = 10;
287 	}
288 	if (g_strcmp0 (id, "icc-fc66337837e2886bfd72e9838228f1b8") == 0) {
289 		/* sRGB_v4_ICC_preference_displayclass */
290 		standard_space = CD_STANDARD_SPACE_SRGB;
291 		priv->score = 8;
292 	}
293 	if (g_strcmp0 (id, "icc-29f83ddeaff255ae7842fae4ca83390d") == 0) {
294 		/* sRGB_IEC61966-2-1_black_scaled */
295 		standard_space = CD_STANDARD_SPACE_SRGB;
296 		priv->score = 6;
297 	}
298 	if (g_strcmp0 (id, "icc-c95bd637e95d8a3b0df38f99c1320389") == 0) {
299 		/* sRGB_IEC61966-2-1_no_black_scaling */
300 		standard_space = CD_STANDARD_SPACE_SRGB;
301 		priv->score = 4;
302 	}
303 
304 	/* from http://download.adobe.com/pub/adobe/iccprofiles/ */
305 	if (g_strcmp0 (id, "icc-dea88382d899d5f6e573b432473ae138") == 0) {
306 		/* AdobeRGB1998 */
307 		standard_space = CD_STANDARD_SPACE_ADOBE_RGB;
308 		priv->score = 10;
309 	}
310 	if (g_strcmp0 (id, "icc-91cf26c58e07eda724fdbf3eadce4505") == 0) {
311 		/* ColorMatchRGB */
312 		standard_space = CD_STANDARD_SPACE_LAST;
313 	}
314 	if (g_strcmp0 (id, "icc-2e54d10b392cac47226469ba2ea95bd8") == 0) {
315 		/* AppleRGB */
316 		standard_space = CD_STANDARD_SPACE_LAST;
317 	}
318 
319 	/* add additional metadata to fix the GUIs */
320 	if (standard_space != CD_STANDARD_SPACE_UNKNOWN) {
321 		cd_profile_set_metadata (profile,
322 					 CD_PROFILE_METADATA_STANDARD_SPACE,
323 					 cd_standard_space_to_string (standard_space));
324 		cd_profile_set_metadata (profile,
325 					 CD_PROFILE_METADATA_DATA_SOURCE,
326 					 CD_PROFILE_METADATA_DATA_SOURCE_STANDARD);
327 	}
328 
329 	/* now calculate this again */
330 	cd_profile_set_object_path (profile);
331 }
332 
333 /**
334  * cd_profile_get_filename:
335  **/
336 const gchar *
cd_profile_get_filename(CdProfile * profile)337 cd_profile_get_filename (CdProfile *profile)
338 {
339 	CdProfilePrivate *priv = GET_PRIVATE (profile);
340 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
341 	return priv->filename;
342 }
343 
344 /**
345  * cd_profile_dbus_emit_property_changed:
346  **/
347 static void
cd_profile_dbus_emit_property_changed(CdProfile * profile,const gchar * property_name,GVariant * property_value)348 cd_profile_dbus_emit_property_changed (CdProfile *profile,
349 				       const gchar *property_name,
350 				       GVariant *property_value)
351 {
352 	CdProfilePrivate *priv = GET_PRIVATE (profile);
353 	GVariantBuilder builder;
354 	GVariantBuilder invalidated_builder;
355 
356 	/* not yet connected */
357 	if (priv->connection == NULL)
358 		return;
359 
360 	/* build the dict */
361 	g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
362 	g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
363 	g_variant_builder_add (&builder,
364 			       "{sv}",
365 			       property_name,
366 			       property_value);
367 	g_dbus_connection_emit_signal (priv->connection,
368 				       NULL,
369 				       priv->object_path,
370 				       "org.freedesktop.DBus.Properties",
371 				       "PropertiesChanged",
372 				       g_variant_new ("(sa{sv}as)",
373 				       COLORD_DBUS_INTERFACE_PROFILE,
374 				       &builder,
375 				       &invalidated_builder),
376 				       NULL);
377 	g_variant_builder_clear (&builder);
378 	g_variant_builder_clear (&invalidated_builder);
379 }
380 
381 /**
382  * cd_profile_dbus_emit_profile_changed:
383  **/
384 static void
cd_profile_dbus_emit_profile_changed(CdProfile * profile)385 cd_profile_dbus_emit_profile_changed (CdProfile *profile)
386 {
387 	CdProfilePrivate *priv = GET_PRIVATE (profile);
388 
389 	/* not yet connected */
390 	if (priv->connection == NULL)
391 		return;
392 
393 	/* emit signal */
394 	g_debug ("CdProfile: emit Changed on %s",
395 		 cd_profile_get_object_path (profile));
396 	g_dbus_connection_emit_signal (priv->connection,
397 				       NULL,
398 				       cd_profile_get_object_path (profile),
399 				       COLORD_DBUS_INTERFACE_PROFILE,
400 				       "Changed",
401 				       NULL,
402 				       NULL);
403 
404 	/* emit signal */
405 	g_debug ("CdProfile: emit Changed");
406 	g_dbus_connection_emit_signal (priv->connection,
407 				       NULL,
408 				       COLORD_DBUS_PATH,
409 				       COLORD_DBUS_INTERFACE,
410 				       "ProfileChanged",
411 				       g_variant_new ("(o)",
412 						      cd_profile_get_object_path (profile)),
413 				       NULL);
414 }
415 
416 /**
417  * cd_profile_install_system_wide:
418  **/
419 static gboolean
cd_profile_install_system_wide(CdProfile * profile,GError ** error)420 cd_profile_install_system_wide (CdProfile *profile, GError **error)
421 {
422 	CdProfilePrivate *priv = GET_PRIVATE (profile);
423 	gboolean ret = TRUE;
424 	g_autoptr(GError) error_local = NULL;
425 	g_autofree gchar *basename = NULL;
426 	g_autofree gchar *filename = NULL;
427 	g_autoptr(GFile) file_dest = NULL;
428 	g_autoptr(GFile) file = NULL;
429 
430 	/* is icc filename set? */
431 	if (priv->filename == NULL) {
432 		g_set_error (error,
433 			     CD_PROFILE_ERROR,
434 			     CD_PROFILE_ERROR_INTERNAL,
435 			     "icc filename not set");
436 		return FALSE;
437 	}
438 
439 	/* is profile already installed in /var/db/color */
440 	if (g_str_has_prefix (priv->filename,
441 			      CD_SYSTEM_PROFILES_DIR)) {
442 		g_set_error (error,
443 			     CD_PROFILE_ERROR,
444 			     CD_PROFILE_ERROR_ALREADY_INSTALLED,
445 			     "file %s already installed in /var",
446 			     priv->filename);
447 		return FALSE;
448 	}
449 
450 	/* is profile already installed in /usr/local/share/color */
451 	if (g_str_has_prefix (priv->filename,
452 			      DATADIR "/color")) {
453 		g_set_error (error,
454 			     CD_PROFILE_ERROR,
455 			     CD_PROFILE_ERROR_ALREADY_INSTALLED,
456 			     "file %s already installed in /usr",
457 			     priv->filename);
458 		return FALSE;
459 	}
460 
461 	/* copy */
462 	basename = g_path_get_basename (priv->filename);
463 	filename = g_build_filename (CD_SYSTEM_PROFILES_DIR,
464 				     basename, NULL);
465 	file_dest = g_file_new_for_path (filename);
466 
467 	/* try to write a mapped file first, else copy the file */
468 	if (priv->mapped_file != NULL) {
469 		g_debug ("writing mapped file to %s", filename);
470 		ret = g_file_replace_contents (file_dest,
471 					       g_mapped_file_get_contents (priv->mapped_file),
472 					       g_mapped_file_get_length (priv->mapped_file),
473 					       NULL,
474 					       FALSE,
475 					       G_FILE_CREATE_REPLACE_DESTINATION,
476 					       NULL,
477 					       NULL, /* cancellable */
478 					       &error_local);
479 		if (!ret) {
480 			g_set_error (error,
481 				     CD_PROFILE_ERROR,
482 				     CD_PROFILE_ERROR_FAILED_TO_WRITE,
483 				     "failed to write mapped file %s: %s",
484 				     priv->filename,
485 				     error_local->message);
486 			return FALSE;
487 		}
488 	} else {
489 		file = g_file_new_for_path (priv->filename);
490 		ret = g_file_copy (file, file_dest, G_FILE_COPY_OVERWRITE,
491 				   NULL, NULL, NULL, &error_local);
492 		if (!ret) {
493 			g_set_error (error,
494 				     CD_PROFILE_ERROR,
495 				     CD_PROFILE_ERROR_FAILED_TO_WRITE,
496 				     "failed to copy %s: %s",
497 				     priv->filename,
498 				     error_local->message);
499 			return FALSE;
500 		}
501 	}
502 	return TRUE;
503 }
504 
505 /**
506  * cd_profile_get_metadata_as_variant:
507  **/
508 static GVariant *
cd_profile_get_metadata_as_variant(CdProfile * profile)509 cd_profile_get_metadata_as_variant (CdProfile *profile)
510 {
511 	CdProfilePrivate *priv = GET_PRIVATE (profile);
512 	GList *l;
513 	GVariantBuilder builder;
514 	g_autoptr(GList) list = NULL;
515 
516 	/* do not try to build an empty array */
517 	if (g_hash_table_size (priv->metadata) == 0)
518 		return g_variant_new_array (G_VARIANT_TYPE ("{ss}"), NULL, 0);
519 
520 	/* add all the keys in the dictionary to the variant builder */
521 	list = g_hash_table_get_keys (priv->metadata);
522 	g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
523 	for (l = list; l != NULL; l = l->next) {
524 		g_variant_builder_add (&builder,
525 				       "{ss}",
526 				       l->data,
527 				       g_hash_table_lookup (priv->metadata,
528 							    l->data));
529 	}
530 	return g_variant_builder_end (&builder);
531 }
532 
533 /**
534  * cd_profile_get_nullable_for_string:
535  **/
536 static GVariant *
cd_profile_get_nullable_for_string(const gchar * value)537 cd_profile_get_nullable_for_string (const gchar *value)
538 {
539 	if (value == NULL)
540 		return g_variant_new_string ("");
541 	return g_variant_new_string (value);
542 }
543 
544 /**
545  * cd_profile_set_title:
546  **/
547 static gboolean
cd_profile_set_title(CdProfile * profile,const gchar * value,guint sender_uid,GError ** error)548 cd_profile_set_title (CdProfile *profile,
549 		      const gchar *value,
550 		      guint sender_uid,
551 		      GError **error)
552 {
553 	CdProfilePrivate *priv = GET_PRIVATE (profile);
554 
555 	/* check title is suitable */
556 	if (value == NULL || strlen (value) < 3 ||
557 	    !g_utf8_validate (value, -1, NULL)) {
558 		g_set_error (error,
559 			     CD_CLIENT_ERROR,
560 			     CD_CLIENT_ERROR_INPUT_INVALID,
561 			     "'Title' value input invalid: %s", value);
562 		return FALSE;
563 	}
564 
565 	/* save in database */
566 	return cd_profile_db_set_property (priv->db, priv->id,
567 					   CD_PROFILE_PROPERTY_TITLE, sender_uid,
568 					   value, error);
569 }
570 
571 /**
572  * cd_profile_set_property_internal:
573  **/
574 gboolean
cd_profile_set_property_internal(CdProfile * profile,const gchar * property,const gchar * value,guint sender_uid,GError ** error)575 cd_profile_set_property_internal (CdProfile *profile,
576 				  const gchar *property,
577 				  const gchar *value,
578 				  guint sender_uid,
579 				  GError **error)
580 {
581 	CdProfilePrivate *priv = GET_PRIVATE (profile);
582 
583 	/* sanity check the length of the key and value */
584 	if (strlen (property) > CD_DBUS_METADATA_KEY_LEN_MAX) {
585 		g_set_error_literal (error,
586 				     CD_CLIENT_ERROR,
587 				     CD_CLIENT_ERROR_INPUT_INVALID,
588 				     "metadata key length invalid");
589 		return FALSE;
590 	}
591 	if (value != NULL && strlen (value) > CD_DBUS_METADATA_VALUE_LEN_MAX) {
592 		g_set_error_literal (error,
593 				     CD_CLIENT_ERROR,
594 				     CD_CLIENT_ERROR_INPUT_INVALID,
595 				     "metadata value length invalid");
596 		return FALSE;
597 	}
598 
599 	if (g_strcmp0 (property, CD_PROFILE_PROPERTY_FILENAME) == 0) {
600 		cd_profile_set_filename (profile, value);
601 		cd_profile_dbus_emit_property_changed (profile,
602 						       property,
603 						       g_variant_new_string (value));
604 	} else if (g_strcmp0 (property, CD_PROFILE_PROPERTY_QUALIFIER) == 0) {
605 		cd_profile_set_qualifier (profile, value);
606 		cd_profile_dbus_emit_property_changed (profile,
607 						       property,
608 						       g_variant_new_string (value));
609 	} else if (g_strcmp0 (property, CD_PROFILE_PROPERTY_FORMAT) == 0) {
610 		cd_profile_set_format (profile, value);
611 		cd_profile_dbus_emit_property_changed (profile,
612 						       property,
613 						       g_variant_new_string (value));
614 	} else if (g_strcmp0 (property, CD_PROFILE_PROPERTY_COLORSPACE) == 0) {
615 		priv->colorspace = cd_colorspace_from_string (value);
616 		cd_profile_dbus_emit_property_changed (profile,
617 						       property,
618 						       g_variant_new_string (value));
619 	} else if (g_strcmp0 (property, CD_PROFILE_PROPERTY_TITLE) == 0) {
620 		if (!cd_profile_set_title (profile, value, sender_uid, error))
621 			return FALSE;
622 		cd_profile_dbus_emit_property_changed (profile, property,
623 						       g_variant_new_string (value));
624 	} else {
625 		/* add to metadata */
626 		cd_profile_set_metadata (profile, property, value);
627 		cd_profile_dbus_emit_property_changed (profile,
628 						       CD_PROFILE_PROPERTY_METADATA,
629 						       cd_profile_get_metadata_as_variant (profile));
630 		return TRUE;
631 	}
632 
633 	/* emit global signal */
634 	cd_profile_dbus_emit_profile_changed (profile);
635 	return TRUE;
636 }
637 
638 /**
639  * cd_profile_dbus_method_call:
640  **/
641 static void
cd_profile_dbus_method_call(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation,gpointer user_data)642 cd_profile_dbus_method_call (GDBusConnection *connection, const gchar *sender,
643 			    const gchar *object_path, const gchar *interface_name,
644 			    const gchar *method_name, GVariant *parameters,
645 			    GDBusMethodInvocation *invocation, gpointer user_data)
646 {
647 	CdProfile *profile = CD_PROFILE (user_data);
648 	CdProfilePrivate *priv = GET_PRIVATE (profile);
649 	gboolean ret;
650 	guint uid;
651 	const gchar *property_name = NULL;
652 	const gchar *property_value = NULL;
653 	g_autoptr(GError) error = NULL;
654 
655 	/* return '' */
656 	if (g_strcmp0 (method_name, "SetProperty") == 0) {
657 
658 		/* require auth */
659 		ret = cd_main_sender_authenticated (connection,
660 						    sender,
661 						    "org.freedesktop.color-manager.modify-profile",
662 						    &error);
663 		if (!ret) {
664 			g_dbus_method_invocation_return_error (invocation,
665 							       CD_PROFILE_ERROR,
666 							       CD_PROFILE_ERROR_FAILED_TO_AUTHENTICATE,
667 							       "%s", error->message);
668 			return;
669 		}
670 
671 		/* get UID */
672 		uid = cd_main_get_sender_uid (connection, sender, &error);
673 		if (uid == G_MAXUINT) {
674 			g_dbus_method_invocation_return_error (invocation,
675 							       CD_PROFILE_ERROR,
676 							       CD_PROFILE_ERROR_FAILED_TO_GET_UID,
677 							       "%s", error->message);
678 			return;
679 		}
680 
681 		/* set, and parse */
682 		g_variant_get (parameters, "(&s&s)",
683 			       &property_name,
684 			       &property_value);
685 		g_debug ("CdProfile %s:SetProperty(%s,%s)",
686 			 sender, property_name, property_value);
687 		if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_FILENAME) == 0) {
688 			g_dbus_method_invocation_return_error (invocation,
689 							       CD_PROFILE_ERROR,
690 							       CD_PROFILE_ERROR_PROPERTY_INVALID,
691 							       "Setting the %s property after "
692 							       "profile creation is no longer supported",
693 							       property_name);
694 			return;
695 		}
696 		ret = cd_profile_set_property_internal (profile,
697 							property_name,
698 							property_value,
699 							uid,
700 							&error);
701 		if (!ret) {
702 			g_dbus_method_invocation_return_gerror (invocation, error);
703 			return;
704 		}
705 		g_dbus_method_invocation_return_value (invocation, NULL);
706 		return;
707 	}
708 
709 	/* return '' */
710 	if (g_strcmp0 (method_name, "InstallSystemWide") == 0) {
711 
712 		/* require auth */
713 		g_debug ("CdProfile %s:InstallSystemWide() on %s",
714 			 sender, priv->object_path);
715 		ret = cd_main_sender_authenticated (connection,
716 						    sender,
717 						    "org.freedesktop.color-manager.install-system-wide",
718 						    &error);
719 		if (!ret) {
720 			g_dbus_method_invocation_return_error (invocation,
721 							       CD_PROFILE_ERROR,
722 							       CD_PROFILE_ERROR_FAILED_TO_AUTHENTICATE,
723 							       "%s", error->message);
724 			return;
725 		}
726 
727 		/* copy systemwide */
728 		ret = cd_profile_install_system_wide (profile, &error);
729 		if (!ret) {
730 			g_dbus_method_invocation_return_gerror (invocation, error);
731 			return;
732 		}
733 
734 		g_dbus_method_invocation_return_value (invocation, NULL);
735 		return;
736 	}
737 
738 
739 	/* we suck */
740 	g_critical ("failed to process method %s", method_name);
741 }
742 
743 /**
744  * cd_profile_dbus_get_property:
745  **/
746 static GVariant *
cd_profile_dbus_get_property(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GError ** error,gpointer user_data)747 cd_profile_dbus_get_property (GDBusConnection *connection, const gchar *sender,
748 			     const gchar *object_path, const gchar *interface_name,
749 			     const gchar *property_name, GError **error,
750 			     gpointer user_data)
751 {
752 	CdProfile *profile = CD_PROFILE (user_data);
753 	CdProfilePrivate *priv = GET_PRIVATE (profile);
754 	gboolean ret;
755 
756 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_TITLE) == 0) {
757 		guint uid;
758 		g_autofree gchar *title_db = NULL;
759 
760 		uid = cd_main_get_sender_uid (connection, sender, error);
761 		if (uid == G_MAXUINT)
762 			return NULL;
763 		ret = cd_profile_db_get_property (priv->db, priv->id,
764 						  property_name, uid,
765 						  &title_db, error);
766 		if (!ret)
767 			return NULL;
768 		if (title_db != NULL)
769 			return cd_profile_get_nullable_for_string (title_db);
770 		return cd_profile_get_nullable_for_string (priv->title);
771 	}
772 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_ID) == 0)
773 		return cd_profile_get_nullable_for_string (priv->id);
774 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_QUALIFIER) == 0)
775 		return cd_profile_get_nullable_for_string (priv->qualifier);
776 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_FORMAT) == 0)
777 		return cd_profile_get_nullable_for_string (priv->format);
778 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_FILENAME) == 0)
779 		return cd_profile_get_nullable_for_string (priv->filename);
780 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_KIND) == 0)
781 		return g_variant_new_string (cd_profile_kind_to_string (priv->kind));
782 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_COLORSPACE) == 0)
783 		return g_variant_new_string (cd_colorspace_to_string (priv->colorspace));
784 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_HAS_VCGT) == 0)
785 		return g_variant_new_boolean (priv->has_vcgt);
786 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_IS_SYSTEM_WIDE) == 0)
787 		return g_variant_new_boolean (priv->is_system_wide);
788 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_METADATA) == 0)
789 		return cd_profile_get_metadata_as_variant (profile);
790 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_CREATED) == 0)
791 		return g_variant_new_int64 (priv->created);
792 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_SCOPE) == 0)
793 		return g_variant_new_string (cd_object_scope_to_string (priv->object_scope));
794 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_OWNER) == 0)
795 		return g_variant_new_uint32 (priv->owner);
796 	if (g_strcmp0 (property_name, CD_PROFILE_PROPERTY_WARNINGS) == 0) {
797 		if (priv->warnings == NULL) {
798 			const gchar *tmp[] = { NULL };
799 			return g_variant_new_strv (tmp, -1);
800 		}
801 		return g_variant_new_strv ((const gchar * const *) priv->warnings, -1);
802 	}
803 
804 	/* return an error */
805 	g_set_error (error,
806 		     CD_PROFILE_ERROR,
807 		     CD_PROFILE_ERROR_INTERNAL,
808 		     "failed to get profile property %s",
809 		     property_name);
810 	return NULL;
811 }
812 
813 /**
814  * cd_profile_register_object:
815  **/
816 gboolean
cd_profile_register_object(CdProfile * profile,GDBusConnection * connection,GDBusInterfaceInfo * info,GError ** error)817 cd_profile_register_object (CdProfile *profile,
818 			    GDBusConnection *connection,
819 			    GDBusInterfaceInfo *info,
820 			    GError **error)
821 {
822 	CdProfilePrivate *priv = GET_PRIVATE (profile);
823 	g_autoptr(GError) error_local = NULL;
824 
825 	static const GDBusInterfaceVTable interface_vtable = {
826 		cd_profile_dbus_method_call,
827 		cd_profile_dbus_get_property,
828 		NULL
829 	};
830 
831 	priv->connection = connection;
832 	priv->registration_id = g_dbus_connection_register_object (
833 		connection,
834 		priv->object_path,
835 		info,
836 		&interface_vtable,
837 		profile,  /* user_data */
838 		NULL,  /* user_data_free_func */
839 		&error_local); /* GError** */
840 	if (priv->registration_id == 0) {
841 		g_set_error (error,
842 			     CD_PROFILE_ERROR,
843 			     CD_PROFILE_ERROR_INTERNAL,
844 			     "failed to register object: %s",
845 			     error_local->message);
846 		return FALSE;
847 	}
848 	return TRUE;
849 }
850 
851 /**
852  * cd_profile_fixup_title:
853  **/
854 static gchar *
cd_profile_fixup_title(const gchar * text)855 cd_profile_fixup_title (const gchar *text)
856 {
857 	gchar *title;
858 	gchar *tmp;
859 	guint len;
860 
861 	/* nothing set */
862 	if (text == NULL)
863 		return NULL;
864 
865 	/* remove the hardcoded confusing title */
866 	if (g_str_has_prefix (text, "Default, "))
867 		text += 9;
868 	title = g_strdup (text);
869 
870 	/* hack to make old profiles look nice */
871 	tmp = g_strstr_len (title, -1, " (201");
872 	if (tmp != NULL)
873 		*tmp = '\0';
874 
875 	/* make underscores into spaces */
876 	g_strdelimit (title, "_", ' ');
877 
878 	/* remove any shitty suffix */
879 	if (g_str_has_suffix (title, ".icc") ||
880 	    g_str_has_suffix (title, ".ICC") ||
881 	    g_str_has_suffix (title, ".icm") ||
882 	    g_str_has_suffix (title, ".ICM")) {
883 		len = strlen (title);
884 		if (len > 4)
885 			title[len - 4] = '\0';
886 	}
887 	return title;
888 }
889 
890 /**
891  * cd_profile_set_from_profile:
892  **/
893 static gboolean
cd_profile_set_from_profile(CdProfile * profile,CdIcc * icc,GError ** error)894 cd_profile_set_from_profile (CdProfile *profile, CdIcc *icc, GError **error)
895 {
896 	CdProfilePrivate *priv = GET_PRIVATE (profile);
897 	CdProfileWarning warning;
898 	GList *l;
899 	cmsHPROFILE lcms_profile;
900 	const gchar *key;
901 	const gchar *value;
902 	gboolean ret = FALSE;
903 	guint i;
904 	struct tm created;
905 	g_autoptr(GArray) flags = NULL;
906 	g_autoptr(GHashTable) metadata = NULL;
907 	g_autoptr(GList) keys = NULL;
908 
909 	/* get the description as the title */
910 	value = cd_icc_get_description (icc, NULL, error);
911 	if (value == NULL)
912 		return FALSE;
913 	priv->title =  cd_profile_fixup_title (value);
914 
915 	/* get the profile kind */
916 	priv->kind = cd_icc_get_kind (icc);
917 	priv->colorspace = cd_icc_get_colorspace (icc);
918 
919 	/* get metadata */
920 	metadata = cd_icc_get_metadata (icc);
921 	keys = g_hash_table_get_keys (metadata);
922 	for (l = keys; l != NULL; l = l->next) {
923 		key = l->data;
924 		value = g_hash_table_lookup (metadata, key);
925 		g_debug ("Adding metadata %s=%s", key, value);
926 		g_hash_table_insert (priv->metadata,
927 				     g_strdup (key),
928 				     g_strdup (value));
929 	}
930 
931 	/* set the format from the metadata */
932 	value = g_hash_table_lookup (priv->metadata,
933 				     CD_PROFILE_METADATA_MAPPING_FORMAT);
934 	if (value != NULL)
935 		cd_profile_set_format (profile, value);
936 
937 	/* set the qualifier from the metadata */
938 	value = g_hash_table_lookup (priv->metadata,
939 				     CD_PROFILE_METADATA_MAPPING_QUALIFIER);
940 	if (value != NULL)
941 		cd_profile_set_qualifier (profile, value);
942 
943 	/* set a generic qualifier if there was nothing set before */
944 	if (priv->colorspace == CD_COLORSPACE_RGB &&
945 	    priv->qualifier == NULL) {
946 		cd_profile_set_format (profile, "ColorSpace..");
947 		cd_profile_set_qualifier (profile, "RGB..");
948 	}
949 
950 	/* get the profile created time and date */
951 	lcms_profile = cd_icc_get_handle (icc);
952 	ret = cmsGetHeaderCreationDateTime (lcms_profile, &created);
953 	if (ret) {
954 		created.tm_isdst = -1;
955 		priv->created = mktime (&created);
956 	} else {
957 		g_warning ("failed to get created time");
958 		priv->created = 0;
959 	}
960 
961 	/* do we have vcgt */
962 	priv->has_vcgt = cmsIsTag (lcms_profile, cmsSigVcgtTag);
963 
964 	/* get the checksum for the profile if we can */
965 	priv->checksum = g_strdup (cd_icc_get_checksum (icc));
966 
967 	/* get any warnings for the profile */
968 	flags = cd_icc_get_warnings (icc);
969 	priv->warnings = g_new0 (gchar *, flags->len + 1);
970 	if (flags->len > 0) {
971 		for (i = 0; i < flags->len; i++) {
972 			warning = g_array_index (flags, CdProfileWarning, i);
973 			priv->warnings[i] = g_strdup (cd_profile_warning_to_string (warning));
974 		}
975 	}
976 	return TRUE;
977 }
978 
979 /**
980  * cd_profile_get_warnings:
981  **/
982 const gchar **
cd_profile_get_warnings(CdProfile * profile)983 cd_profile_get_warnings (CdProfile *profile)
984 {
985 	CdProfilePrivate *priv = GET_PRIVATE (profile);
986 	return (const gchar **) priv->warnings;
987 }
988 
989 /**
990  * cd_profile_emit_parsed_property_changed:
991  **/
992 static void
cd_profile_emit_parsed_property_changed(CdProfile * profile)993 cd_profile_emit_parsed_property_changed (CdProfile *profile)
994 {
995 	CdProfilePrivate *priv = GET_PRIVATE (profile);
996 
997 	cd_profile_dbus_emit_property_changed (profile,
998 					       CD_PROFILE_PROPERTY_FILENAME,
999 					       cd_profile_get_nullable_for_string (priv->filename));
1000 	cd_profile_dbus_emit_property_changed (profile,
1001 					       CD_PROFILE_PROPERTY_TITLE,
1002 					       cd_profile_get_nullable_for_string (priv->title));
1003 	cd_profile_dbus_emit_property_changed (profile,
1004 					       CD_PROFILE_PROPERTY_KIND,
1005 					       g_variant_new_string (cd_profile_kind_to_string (priv->kind)));
1006 	cd_profile_dbus_emit_property_changed (profile,
1007 					       CD_PROFILE_PROPERTY_COLORSPACE,
1008 					       g_variant_new_string (cd_colorspace_to_string (priv->colorspace)));
1009 	cd_profile_dbus_emit_property_changed (profile,
1010 					       CD_PROFILE_PROPERTY_HAS_VCGT,
1011 					       g_variant_new_boolean (priv->has_vcgt));
1012 	cd_profile_dbus_emit_property_changed (profile,
1013 					       CD_PROFILE_PROPERTY_METADATA,
1014 					       cd_profile_get_metadata_as_variant (profile));
1015 	cd_profile_dbus_emit_property_changed (profile,
1016 					       CD_PROFILE_PROPERTY_QUALIFIER,
1017 					       cd_profile_get_nullable_for_string (priv->qualifier));
1018 	cd_profile_dbus_emit_property_changed (profile,
1019 					       CD_PROFILE_PROPERTY_FORMAT,
1020 					       cd_profile_get_nullable_for_string (priv->format));
1021 	cd_profile_dbus_emit_property_changed (profile,
1022 					       CD_PROFILE_PROPERTY_CREATED,
1023 					       g_variant_new_int64 (priv->created));
1024 }
1025 
1026 /**
1027  * cd_profile_load_from_icc:
1028  **/
1029 gboolean
cd_profile_load_from_icc(CdProfile * profile,CdIcc * icc,GError ** error)1030 cd_profile_load_from_icc (CdProfile *profile, CdIcc *icc, GError **error)
1031 {
1032 	g_return_val_if_fail (CD_IS_PROFILE (profile), FALSE);
1033 
1034 	/* save filename */
1035 	cd_profile_set_filename (profile, cd_icc_get_filename (icc));
1036 
1037 	/* set the virtual profile from the lcms profile */
1038 	if (!cd_profile_set_from_profile (profile, icc, error))
1039 		return FALSE;
1040 
1041 	/* emit all the things that could have changed */
1042 	cd_profile_emit_parsed_property_changed (profile);
1043 	return TRUE;
1044 }
1045 
1046 /**
1047  * cd_profile_load_from_fd:
1048  **/
1049 gboolean
cd_profile_load_from_fd(CdProfile * profile,gint fd,GError ** error)1050 cd_profile_load_from_fd (CdProfile *profile,
1051 			 gint fd,
1052 			 GError **error)
1053 {
1054 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1055 	gboolean ret;
1056 	g_autoptr(GError) error_local = NULL;
1057 	g_autoptr(CdIcc) icc = NULL;
1058 
1059 	g_return_val_if_fail (CD_IS_PROFILE (profile), FALSE);
1060 
1061 	/* check we're not already set */
1062 	if (priv->kind != CD_PROFILE_KIND_UNKNOWN) {
1063 		g_set_error (error,
1064 			     CD_PROFILE_ERROR,
1065 			     CD_PROFILE_ERROR_INTERNAL,
1066 			     "profile '%s' already set",
1067 			     priv->object_path);
1068 		return FALSE;
1069 	}
1070 
1071 	/* open fd and parse the file */
1072 	icc = cd_icc_new ();
1073 	ret = cd_icc_load_fd (icc, fd,
1074 			      CD_ICC_LOAD_FLAGS_METADATA,
1075 			      &error_local);
1076 	if (!ret) {
1077 		g_set_error_literal (error,
1078 				     CD_PROFILE_ERROR,
1079 				     CD_PROFILE_ERROR_FAILED_TO_READ,
1080 				     error_local->message);
1081 		return FALSE;
1082 	}
1083 
1084 	/* create a mapped file */
1085 	priv->mapped_file = g_mapped_file_new_from_fd (fd, FALSE, error);
1086 	if (priv->mapped_file == NULL) {
1087 		g_set_error (error,
1088 			     CD_PROFILE_ERROR,
1089 			     CD_PROFILE_ERROR_FAILED_TO_READ,
1090 			     "failed to create mapped file from fd %i",
1091 			     fd);
1092 		return FALSE;
1093 	}
1094 
1095 	/* set the virtual profile from the lcms profile */
1096 	if (!cd_profile_set_from_profile (profile, icc, error))
1097 		return FALSE;
1098 
1099 	/* emit all the things that could have changed */
1100 	cd_profile_emit_parsed_property_changed (profile);
1101 	return TRUE;
1102 }
1103 
1104 /**
1105  * cd_profile_load_from_filename:
1106  **/
1107 gboolean
cd_profile_load_from_filename(CdProfile * profile,const gchar * filename,GError ** error)1108 cd_profile_load_from_filename (CdProfile *profile, const gchar *filename, GError **error)
1109 {
1110 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1111 	gboolean ret = FALSE;
1112 	g_autoptr(GError) error_local = NULL;
1113 	g_autoptr(CdIcc) icc = NULL;
1114 	g_autoptr(GFile) file = NULL;
1115 
1116 	g_return_val_if_fail (CD_IS_PROFILE (profile), FALSE);
1117 
1118 	/* check we're not already set */
1119 	if (priv->kind != CD_PROFILE_KIND_UNKNOWN) {
1120 		g_set_error (error,
1121 			     CD_PROFILE_ERROR,
1122 			     CD_PROFILE_ERROR_INTERNAL,
1123 			     "profile '%s' already set",
1124 			     priv->object_path);
1125 		return FALSE;
1126 	}
1127 
1128 	/* open fd and parse the file */
1129 	icc = cd_icc_new ();
1130 	file = g_file_new_for_path (filename);
1131 	ret = cd_icc_load_file (icc, file,
1132 				CD_ICC_LOAD_FLAGS_METADATA,
1133 				NULL,
1134 				&error_local);
1135 	if (!ret) {
1136 		g_set_error_literal (error,
1137 				     CD_PROFILE_ERROR,
1138 				     CD_PROFILE_ERROR_FAILED_TO_READ,
1139 				     error_local->message);
1140 		return FALSE;
1141 	}
1142 
1143 	/* create a mapped file */
1144 	priv->mapped_file = g_mapped_file_new (filename, FALSE, error);
1145 	if (priv->mapped_file == NULL) {
1146 		g_set_error (error,
1147 			     CD_PROFILE_ERROR,
1148 			     CD_PROFILE_ERROR_FAILED_TO_READ,
1149 			     "failed to create mapped file from filname %s",
1150 			     filename);
1151 		return FALSE;
1152 	}
1153 
1154 	/* set the virtual profile from the lcms profile */
1155 	if (!cd_profile_set_from_profile (profile, icc, error))
1156 		return FALSE;
1157 
1158 	/* emit all the things that could have changed */
1159 	cd_profile_emit_parsed_property_changed (profile);
1160 	return TRUE;
1161 }
1162 
1163 /**
1164  * cd_profile_get_qualifier:
1165  **/
1166 const gchar *
cd_profile_get_qualifier(CdProfile * profile)1167 cd_profile_get_qualifier (CdProfile *profile)
1168 {
1169 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1170 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
1171 	return priv->qualifier;
1172 }
1173 
1174 /**
1175  * cd_profile_set_qualifier:
1176  **/
1177 void
cd_profile_set_qualifier(CdProfile * profile,const gchar * qualifier)1178 cd_profile_set_qualifier (CdProfile *profile, const gchar *qualifier)
1179 {
1180 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1181 	g_return_if_fail (CD_IS_PROFILE (profile));
1182 	g_free (priv->qualifier);
1183 	priv->qualifier = g_strdup (qualifier);
1184 }
1185 
1186 /**
1187  * cd_profile_set_format:
1188  **/
1189 void
cd_profile_set_format(CdProfile * profile,const gchar * format)1190 cd_profile_set_format (CdProfile *profile, const gchar *format)
1191 {
1192 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1193 	g_return_if_fail (CD_IS_PROFILE (profile));
1194 	g_free (priv->format);
1195 	priv->format = g_strdup (format);
1196 }
1197 
1198 /**
1199  * cd_profile_set_filename:
1200  **/
1201 static void
cd_profile_set_filename(CdProfile * profile,const gchar * filename)1202 cd_profile_set_filename (CdProfile *profile, const gchar *filename)
1203 {
1204 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1205 	g_return_if_fail (CD_IS_PROFILE (profile));
1206 	g_free (priv->filename);
1207 	priv->filename = g_strdup (filename);
1208 }
1209 
1210 /**
1211  * cd_profile_get_title:
1212  **/
1213 const gchar *
cd_profile_get_title(CdProfile * profile)1214 cd_profile_get_title (CdProfile *profile)
1215 {
1216 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1217 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
1218 	return priv->title;
1219 }
1220 
1221 /**
1222  * cd_profile_get_metadata:
1223  **/
1224 GHashTable *
cd_profile_get_metadata(CdProfile * profile)1225 cd_profile_get_metadata (CdProfile *profile)
1226 {
1227 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1228 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
1229 	return priv->metadata;
1230 }
1231 
1232 /**
1233  * cd_profile_get_metadata_item:
1234  **/
1235 const gchar *
cd_profile_get_metadata_item(CdProfile * profile,const gchar * key)1236 cd_profile_get_metadata_item (CdProfile *profile, const gchar *key)
1237 {
1238 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1239 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
1240 	return g_hash_table_lookup (priv->metadata, key);
1241 }
1242 
1243 /**
1244  * cd_profile_get_score:
1245  *
1246  * Profiles from official vendors such as http://www.color.org/ should be
1247  * more important than profiles generated from source data.
1248  *
1249  * Return value: A number which corresponds to the importance of the profile,
1250  * where larger numbers have more importance than lower numbers.
1251  **/
1252 guint
cd_profile_get_score(CdProfile * profile)1253 cd_profile_get_score (CdProfile *profile)
1254 {
1255 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1256 	g_return_val_if_fail (CD_IS_PROFILE (profile), 0);
1257 	return priv->score;
1258 }
1259 
1260 /**
1261  * cd_profile_get_kind:
1262  **/
1263 CdProfileKind
cd_profile_get_kind(CdProfile * profile)1264 cd_profile_get_kind (CdProfile *profile)
1265 {
1266 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1267 	g_return_val_if_fail (CD_IS_PROFILE (profile), 0);
1268 	return priv->kind;
1269 }
1270 
1271 /**
1272  * cd_profile_get_colorspace:
1273  **/
1274 CdColorspace
cd_profile_get_colorspace(CdProfile * profile)1275 cd_profile_get_colorspace (CdProfile *profile)
1276 {
1277 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1278 	g_return_val_if_fail (CD_IS_PROFILE (profile), 0);
1279 	return priv->colorspace;
1280 }
1281 
1282 /**
1283  * cd_profile_get_has_vcgt:
1284  **/
1285 gboolean
cd_profile_get_has_vcgt(CdProfile * profile)1286 cd_profile_get_has_vcgt (CdProfile *profile)
1287 {
1288 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1289 	g_return_val_if_fail (CD_IS_PROFILE (profile), FALSE);
1290 	return priv->has_vcgt;
1291 }
1292 
1293 /**
1294  * cd_profile_get_checksum:
1295  **/
1296 const gchar *
cd_profile_get_checksum(CdProfile * profile)1297 cd_profile_get_checksum (CdProfile *profile)
1298 {
1299 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1300 	g_return_val_if_fail (CD_IS_PROFILE (profile), NULL);
1301 	return priv->checksum;
1302 }
1303 
1304 /**
1305  * cd_profile_name_vanished_cb:
1306  **/
1307 static void
cd_profile_name_vanished_cb(GDBusConnection * connection,const gchar * name,gpointer user_data)1308 cd_profile_name_vanished_cb (GDBusConnection *connection,
1309 			     const gchar *name,
1310 			     gpointer user_data)
1311 {
1312 	CdProfile *profile = CD_PROFILE (user_data);
1313 	g_debug ("CdProfile: emit 'invalidate' as %s vanished", name);
1314 	g_signal_emit (profile, signals[SIGNAL_INVALIDATE], 0);
1315 }
1316 
1317 /**
1318  * cd_profile_watch_sender:
1319  **/
1320 void
cd_profile_watch_sender(CdProfile * profile,const gchar * sender)1321 cd_profile_watch_sender (CdProfile *profile, const gchar *sender)
1322 {
1323 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1324 	g_return_if_fail (CD_IS_PROFILE (profile));
1325 	priv->watcher_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
1326 						      sender,
1327 						      G_BUS_NAME_WATCHER_FLAGS_NONE,
1328 						      NULL,
1329 						      cd_profile_name_vanished_cb,
1330 						      profile,
1331 						      NULL);
1332 }
1333 
1334 /**
1335  * cd_profile_get_property:
1336  **/
1337 static void
cd_profile_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1338 cd_profile_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1339 {
1340 	CdProfile *profile = CD_PROFILE (object);
1341 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1342 
1343 	switch (prop_id) {
1344 	case PROP_OBJECT_PATH:
1345 		g_value_set_string (value, priv->object_path);
1346 		break;
1347 	case PROP_ID:
1348 		g_value_set_string (value, priv->id);
1349 		break;
1350 	default:
1351 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1352 		break;
1353 	}
1354 }
1355 
1356 /**
1357  * cd_profile_set_property:
1358  **/
1359 static void
cd_profile_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1360 cd_profile_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1361 {
1362 	CdProfile *profile = CD_PROFILE (object);
1363 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1364 
1365 	switch (prop_id) {
1366 	case PROP_OBJECT_PATH:
1367 		g_free (priv->object_path);
1368 		priv->object_path = g_strdup (g_value_get_string (value));
1369 		break;
1370 	case PROP_ID:
1371 		g_free (priv->id);
1372 		priv->id = g_strdup (g_value_get_string (value));
1373 		break;
1374 	default:
1375 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1376 		break;
1377 	}
1378 }
1379 
1380 /**
1381  * cd_profile_class_init:
1382  **/
1383 static void
cd_profile_class_init(CdProfileClass * klass)1384 cd_profile_class_init (CdProfileClass *klass)
1385 {
1386 	GParamSpec *pspec;
1387 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
1388 	object_class->finalize = cd_profile_finalize;
1389 	object_class->get_property = cd_profile_get_property;
1390 	object_class->set_property = cd_profile_set_property;
1391 
1392 	/**
1393 	 * CdProfile:object-path:
1394 	 */
1395 	pspec = g_param_spec_string ("object-path", NULL, NULL,
1396 				     NULL,
1397 				     G_PARAM_READWRITE);
1398 	g_object_class_install_property (object_class, PROP_OBJECT_PATH, pspec);
1399 
1400 	/**
1401 	 * CdProfile:id:
1402 	 */
1403 	pspec = g_param_spec_string ("id", NULL, NULL,
1404 				     NULL,
1405 				     G_PARAM_READWRITE);
1406 	g_object_class_install_property (object_class, PROP_ID, pspec);
1407 
1408 	/**
1409 	 * CdProfile::invalidate:
1410 	 **/
1411 	signals[SIGNAL_INVALIDATE] =
1412 		g_signal_new ("invalidate",
1413 			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1414 			      G_STRUCT_OFFSET (CdProfileClass, invalidate),
1415 			      NULL, NULL, g_cclosure_marshal_VOID__VOID,
1416 			      G_TYPE_NONE, 0);
1417 }
1418 
1419 /**
1420  * cd_profile_init:
1421  **/
1422 static void
cd_profile_init(CdProfile * profile)1423 cd_profile_init (CdProfile *profile)
1424 {
1425 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1426 	priv->db = cd_profile_db_new ();
1427 	priv->metadata = g_hash_table_new_full (g_str_hash,
1428 							 g_str_equal,
1429 							 g_free,
1430 							 g_free);
1431 }
1432 
1433 /**
1434  * cd_profile_finalize:
1435  **/
1436 static void
cd_profile_finalize(GObject * object)1437 cd_profile_finalize (GObject *object)
1438 {
1439 	CdProfile *profile = CD_PROFILE (object);
1440 	CdProfilePrivate *priv = GET_PRIVATE (profile);
1441 
1442 	if (priv->watcher_id > 0)
1443 		g_bus_unwatch_name (priv->watcher_id);
1444 	if (priv->registration_id > 0) {
1445 		g_dbus_connection_unregister_object (priv->connection,
1446 						     priv->registration_id);
1447 	}
1448 	if (priv->mapped_file != NULL)
1449 		g_mapped_file_unref (priv->mapped_file);
1450 	g_free (priv->filename);
1451 	g_free (priv->qualifier);
1452 	g_free (priv->format);
1453 	g_free (priv->title);
1454 	g_free (priv->id);
1455 	g_free (priv->checksum);
1456 	g_free (priv->object_path);
1457 	g_object_unref (priv->db);
1458 	g_strfreev (priv->warnings);
1459 	g_hash_table_unref (priv->metadata);
1460 
1461 	G_OBJECT_CLASS (cd_profile_parent_class)->finalize (object);
1462 }
1463 
1464 /**
1465  * cd_profile_new:
1466  **/
1467 CdProfile *
cd_profile_new(void)1468 cd_profile_new (void)
1469 {
1470 	CdProfile *profile;
1471 	profile = g_object_new (CD_TYPE_PROFILE, NULL);
1472 	return CD_PROFILE (profile);
1473 }
1474 
1475