• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Color management routines for the CUPS scheduler.
3  *
4  * Copyright 2007-2014 by Apple Inc.
5  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
8  *
9  * Original DBUS/colord code is Copyright 2011 Red Hat, Inc.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  *   Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  *
18  *   Redistributions in binary form must reproduce the above copyright
19  *   notice, this list of conditions and the following disclaimer in the
20  *   documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /*
37  * Include necessary headers...
38  */
39 
40 #include "cupsd.h"
41 #include <cups/ppd-private.h>
42 
43 #ifdef __APPLE__
44 #  include <ApplicationServices/ApplicationServices.h>
45 extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
46 #  include <CoreFoundation/CoreFoundation.h>
47 #elif defined(HAVE_DBUS)
48 #  include <dbus/dbus.h>
49 
50 /*
51  * Defines used by colord. See the reference docs for further details:
52  *
53  *   http://colord.hughsie.com/api/ref-dbus.html
54  */
55 
56 #  define COLORD_SCOPE_NORMAL	"normal"
57 					/* System scope */
58 #  define COLORD_SCOPE_TEMP	"temp"	/* Process scope */
59 #  define COLORD_SCOPE_DISK	"disk"	/* Lives forever, as stored in DB */
60 
61 #  define COLORD_RELATION_SOFT	"soft"	/* Mapping is not default */
62 #  define COLORD_RELATION_HARD	"hard"	/* Explicitly mapped profile */
63 
64 #  define COLORD_SPACE_RGB	"rgb"	/* RGB colorspace */
65 #  define COLORD_SPACE_CMYK	"cmyk"	/* CMYK colorspace */
66 #  define COLORD_SPACE_GRAY	"gray"	/* Gray colorspace */
67 #  define COLORD_SPACE_UNKNOWN	"unknown"
68 					/* Unknown colorspace */
69 
70 #  define COLORD_MODE_PHYSICAL	"physical"
71 					/* Actual device */
72 #  define COLORD_MODE_VIRTUAL	"virtual"
73 					/* Virtual device with no hardware */
74 
75 #  define COLORD_KIND_PRINTER	"printer"
76 					/* printing output device */
77 
78 #  define COLORD_DBUS_SERVICE		"org.freedesktop.ColorManager"
79 #  define COLORD_DBUS_INTERFACE 	"org.freedesktop.ColorManager"
80 #  define COLORD_DBUS_INTERFACE_DEVICE	"org.freedesktop.ColorManager.Device"
81 #  define COLORD_DBUS_PATH		"/org/freedesktop/ColorManager"
82 					/* Path for color management system */
83 #  define COLORD_DBUS_TIMEOUT	5000	/* Timeout for connecting to colord in ms */
84 #endif /* __APPLE__ */
85 
86 
87 /*
88  * Local globals...
89  */
90 
91 #if !defined(__APPLE__) && defined(HAVE_DBUS)
92 static DBusConnection *colord_con = NULL;
93 					/* DBUS connection for colord */
94 #endif /* !__APPLE__ && HAVE_DBUS */
95 
96 
97 /*
98  * Local functions...
99  */
100 
101 #ifdef __APPLE__
102 static void	apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
103                                    CFMutableDictionaryRef profile,
104 				   unsigned id, const char *name,
105 				   const char *text, const char *iccfile);
106 static void	apple_register_profiles(cupsd_printer_t *p);
107 static void	apple_unregister_profiles(cupsd_printer_t *p);
108 
109 #elif defined(HAVE_DBUS)
110 static void	colord_create_device(cupsd_printer_t *p, ppd_file_t *ppd,
111 				     cups_array_t *profiles,
112 				     const char *colorspace, char **format,
113 				     const char *relation, const char *scope);
114 static void	colord_create_profile(cups_array_t *profiles,
115 				      const char *printer_name,
116 				      const char *qualifier,
117 				      const char *colorspace,
118 				      char **format, const char *iccfile,
119 				      const char *scope);
120 static void	colord_delete_device(const char *device_id);
121 static void	colord_device_add_profile(const char *device_path,
122 					  const char *profile_path,
123 					  const char *relation);
124 static void	colord_dict_add_strings(DBusMessageIter *dict,
125 					const char *key, const char *value);
126 static char	*colord_find_device(const char *device_id);
127 static void	colord_get_qualifier_format(ppd_file_t *ppd, char *format[3]);
128 static void	colord_register_printer(cupsd_printer_t *p);
129 static void	colord_unregister_printer(cupsd_printer_t *p);
130 #endif /* __APPLE__ */
131 
132 
133 /*
134  * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
135  */
136 
137 void
cupsdRegisterColor(cupsd_printer_t * p)138 cupsdRegisterColor(cupsd_printer_t *p)	/* I - Printer */
139 {
140 #ifdef __APPLE__
141   if (!RunUser)
142   {
143     apple_unregister_profiles(p);
144     apple_register_profiles(p);
145   }
146 
147 #elif defined(HAVE_DBUS)
148   if (!RunUser)
149   {
150     colord_unregister_printer(p);
151     colord_register_printer(p);
152   }
153 #endif /* __APPLE__ */
154 }
155 
156 
157 /*
158  * 'cupsdStartColor()' - Initialize color management.
159  */
160 
161 void
cupsdStartColor(void)162 cupsdStartColor(void)
163 {
164 #if !defined(__APPLE__) && defined(HAVE_DBUS)
165   cupsd_printer_t	*p;		/* Current printer */
166 
167 
168   colord_con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
169 
170   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
171        p;
172        p = (cupsd_printer_t *)cupsArrayNext(Printers))
173     cupsdRegisterColor(p);
174 #endif /* !__APPLE__ && HAVE_DBUS */
175 }
176 
177 
178 /*
179  * 'cupsdStopColor()' - Shutdown color management.
180  */
181 
182 void
cupsdStopColor(void)183 cupsdStopColor(void)
184 {
185 #if !defined(__APPLE__) && defined(HAVE_DBUS)
186   if (colord_con)
187     dbus_connection_unref(colord_con);
188   colord_con = NULL;
189 #endif /* !__APPLE__ && HAVE_DBUS */
190 }
191 
192 
193 /*
194  * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
195  */
196 
197 void
cupsdUnregisterColor(cupsd_printer_t * p)198 cupsdUnregisterColor(cupsd_printer_t *p)/* I - Printer */
199 {
200 #ifdef __APPLE__
201   if (!RunUser)
202     apple_unregister_profiles(p);
203 
204 #elif defined(HAVE_DBUS)
205   if (!RunUser)
206     colord_unregister_printer(p);
207 #endif /* __APPLE__ */
208 }
209 
210 
211 #ifdef __APPLE__
212 /*
213  * 'apple_init_profile()' - Initialize a color profile.
214  */
215 
216 static void
apple_init_profile(ppd_file_t * ppd,cups_array_t * languages,CFMutableDictionaryRef profile,unsigned id,const char * name,const char * text,const char * iccfile)217 apple_init_profile(
218     ppd_file_t             *ppd,	/* I - PPD file */
219     cups_array_t	   *languages,	/* I - Languages in the PPD file */
220     CFMutableDictionaryRef profile,	/* I - Profile dictionary */
221     unsigned               id,		/* I - Profile ID */
222     const char             *name,	/* I - Profile name */
223     const char             *text,	/* I - Profile UI text */
224     const char             *iccfile)	/* I - ICC filename */
225 {
226   CFURLRef		url;		/* URL for profile filename */
227   CFMutableDictionaryRef dict;		/* Dictionary for name */
228   char			*language;	/* Current language */
229   ppd_attr_t		*attr;		/* Profile attribute */
230   CFStringRef		cflang,		/* Language string */
231 			cftext;		/* Localized text */
232 
233 
234   (void)id;
235 
236  /*
237   * Build the profile name dictionary...
238   */
239 
240   dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
241 				   &kCFTypeDictionaryKeyCallBacks,
242 				   &kCFTypeDictionaryValueCallBacks);
243   if (!dict)
244   {
245     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
246                     iccfile);
247     return;
248   }
249 
250   cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
251 				     kCFStringEncodingUTF8);
252 
253   if (cftext)
254   {
255     CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
256     CFRelease(cftext);
257   }
258 
259   if (languages)
260   {
261    /*
262     * Find localized names for the color profiles...
263     */
264 
265     cupsArraySave(ppd->sorted_attrs);
266 
267     for (language = (char *)cupsArrayFirst(languages);
268 	 language;
269 	 language = (char *)cupsArrayNext(languages))
270     {
271       if (iccfile)
272       {
273         if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
274 	                              language)) == NULL)
275 	  attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
276       }
277       else
278         attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
279 
280       if (attr && attr->text[0])
281       {
282 	cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
283 					   kCFStringEncodingUTF8);
284 	cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
285 					   kCFStringEncodingUTF8);
286 
287         if (cflang && cftext)
288 	  CFDictionarySetValue(dict, cflang, cftext);
289 
290         if (cflang)
291 	  CFRelease(cflang);
292 
293         if (cftext)
294 	  CFRelease(cftext);
295       }
296     }
297 
298     cupsArrayRestore(ppd->sorted_attrs);
299   }
300 
301  /*
302   * Fill in the profile data...
303   */
304 
305  if (iccfile && *iccfile)
306  {
307     url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)iccfile, (CFIndex)strlen(iccfile), false);
308 
309     if (url)
310     {
311       CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
312       CFRelease(url);
313     }
314   }
315 
316   CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
317   CFRelease(dict);
318 }
319 
320 
321 /*
322  * 'apple_register_profiles()' - Register color profiles for a printer.
323  */
324 
325 static void
apple_register_profiles(cupsd_printer_t * p)326 apple_register_profiles(
327     cupsd_printer_t *p)			/* I - Printer */
328 {
329   int			i;		/* Looping var */
330   char			ppdfile[1024],	/* PPD filename */
331 			iccfile[1024],	/* ICC filename */
332 			selector[PPD_MAX_NAME];
333 					/* Profile selection string */
334   ppd_file_t		*ppd;		/* PPD file */
335   ppd_attr_t		*attr,		/* Profile attributes */
336 			*profileid_attr,/* cupsProfileID attribute */
337 			*q1_attr,	/* ColorModel (or other) qualifier */
338 			*q2_attr,	/* MediaType (or other) qualifier */
339 			*q3_attr;	/* Resolution (or other) qualifier */
340   char			q_keyword[PPD_MAX_NAME];
341 					/* Qualifier keyword */
342   const char		*q1_choice,	/* ColorModel (or other) choice */
343 			*q2_choice,	/* MediaType (or other) choice */
344 			*q3_choice;	/* Resolution (or other) choice */
345   ppd_option_t		*cm_option;	/* Color model option */
346   ppd_choice_t		*cm_choice;	/* Color model choice */
347   int			num_profiles;	/* Number of profiles */
348   OSStatus		error = 0;	/* Last error */
349   unsigned		device_id,	/* Printer device ID */
350 			profile_id = 0,	/* Profile ID */
351 			default_profile_id = 0;
352 					/* Default profile ID */
353   CFMutableDictionaryRef device_name;	/* Printer device name dictionary */
354   CFStringRef		printer_name;	/* Printer name string */
355   cups_array_t		*languages;	/* Languages array */
356   CFMutableDictionaryRef profiles,	/* Dictionary of profiles */
357 			profile;	/* Current profile info dictionary */
358   CFStringRef		dict_key;	/* Key in factory profile dictionary */
359 
360 
361  /*
362   * Make sure ColorSync is available...
363   */
364 
365   if (&ColorSyncRegisterDevice == NULL)
366     return;
367 
368  /*
369   * Try opening the PPD file for this printer...
370   */
371 
372   snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
373   if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
374     return;
375 
376  /*
377   * See if we have any profiles...
378   */
379 
380   for (num_profiles = 0, attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
381        attr;
382        attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
383     if (attr->spec[0] && attr->value && attr->value[0])
384     {
385       if (attr->value[0] != '/')
386 	snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
387 		 attr->value);
388       else
389 	strlcpy(iccfile, attr->value, sizeof(iccfile));
390 
391       if (access(iccfile, 0))
392       {
393         cupsdLogMessage(CUPSD_LOG_ERROR,
394                         "%s: ICC Profile \"%s\" does not exist.", p->name,
395                         iccfile);
396         cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
397 	continue;
398       }
399 
400       num_profiles ++;
401     }
402 
403  /*
404   * Create a dictionary for the factory profiles...
405   */
406 
407   profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
408 				       &kCFTypeDictionaryKeyCallBacks,
409 				       &kCFTypeDictionaryValueCallBacks);
410   if (!profiles)
411   {
412     cupsdLogMessage(CUPSD_LOG_ERROR,
413 		    "Unable to allocate memory for factory profiles.");
414     ppdClose(ppd);
415     return;
416   }
417 
418  /*
419   * If we have profiles, add them...
420   */
421 
422   if (num_profiles > 0)
423   {
424    /*
425     * For CUPS PPDs, figure out the default profile selector values...
426     */
427 
428     if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
429 	attr->value && attr->value[0])
430     {
431       snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
432       q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
433     }
434     else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
435       q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
436 
437     if (q1_attr && q1_attr->value && q1_attr->value[0])
438       q1_choice = q1_attr->value;
439     else
440       q1_choice = "";
441 
442     if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
443 	attr->value && attr->value[0])
444     {
445       snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
446       q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
447     }
448     else
449       q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
450 
451     if (q2_attr && q2_attr->value && q2_attr->value[0])
452       q2_choice = q2_attr->value;
453     else
454       q2_choice = NULL;
455 
456     if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
457 	attr->value && attr->value[0])
458     {
459       snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
460       q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
461     }
462     else
463       q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
464 
465     if (q3_attr && q3_attr->value && q3_attr->value[0])
466       q3_choice = q3_attr->value;
467     else
468       q3_choice = NULL;
469 
470    /*
471     * Loop through the profiles listed in the PPD...
472     */
473 
474     languages = _ppdGetLanguages(ppd);
475 
476     for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
477 	 attr;
478 	 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
479       if (attr->spec[0] && attr->value && attr->value[0])
480       {
481        /*
482         * Add this profile...
483 	*/
484 
485         if (attr->value[0] != '/')
486 	  snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
487 	           attr->value);
488         else
489 	  strlcpy(iccfile, attr->value, sizeof(iccfile));
490 
491         if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
492 	                   cupsdLogFCMessage, p))
493 	  iccfile[0] = '\0';
494 
495 	cupsArraySave(ppd->sorted_attrs);
496 
497 	if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
498 					  attr->spec)) != NULL &&
499 	    profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
500 	  profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
501 	else
502 	  profile_id = _ppdHashName(attr->spec);
503 
504 	cupsArrayRestore(ppd->sorted_attrs);
505 
506 	profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
507 					    &kCFTypeDictionaryKeyCallBacks,
508 					    &kCFTypeDictionaryValueCallBacks);
509 	if (!profile)
510 	{
511 	  cupsdLogMessage(CUPSD_LOG_ERROR,
512 	                  "Unable to allocate memory for color profile.");
513 	  CFRelease(profiles);
514 	  ppdClose(ppd);
515 	  return;
516 	}
517 
518 	apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
519 	                   attr->text[0] ? attr->text : attr->spec, iccfile);
520 
521 	dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
522 	                                    CFSTR("%u"), profile_id);
523 	if (dict_key)
524 	{
525 	  CFDictionarySetValue(profiles, dict_key, profile);
526 	  CFRelease(dict_key);
527 	}
528 
529 	CFRelease(profile);
530 
531        /*
532         * See if this is the default profile...
533 	*/
534 
535         if (!default_profile_id && q1_choice && q2_choice && q3_choice)
536 	{
537 	  snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
538 	           q3_choice);
539 	  if (!strcmp(selector, attr->spec))
540 	    default_profile_id = profile_id;
541 	}
542 
543         if (!default_profile_id && q1_choice && q2_choice)
544 	{
545 	  snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
546 	  if (!strcmp(selector, attr->spec))
547 	    default_profile_id = profile_id;
548 	}
549 
550         if (!default_profile_id && q1_choice && q3_choice)
551 	{
552 	  snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
553 	  if (!strcmp(selector, attr->spec))
554 	    default_profile_id = profile_id;
555 	}
556 
557         if (!default_profile_id && q1_choice)
558 	{
559 	  snprintf(selector, sizeof(selector), "%s..", q1_choice);
560 	  if (!strcmp(selector, attr->spec))
561 	    default_profile_id = profile_id;
562 	}
563 
564         if (!default_profile_id && q2_choice && q3_choice)
565 	{
566 	  snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
567 	  if (!strcmp(selector, attr->spec))
568 	    default_profile_id = profile_id;
569 	}
570 
571         if (!default_profile_id && q2_choice)
572 	{
573 	  snprintf(selector, sizeof(selector), ".%s.", q2_choice);
574 	  if (!strcmp(selector, attr->spec))
575 	    default_profile_id = profile_id;
576 	}
577 
578         if (!default_profile_id && q3_choice)
579 	{
580 	  snprintf(selector, sizeof(selector), "..%s", q3_choice);
581 	  if (!strcmp(selector, attr->spec))
582 	    default_profile_id = profile_id;
583 	}
584       }
585 
586     _ppdFreeLanguages(languages);
587   }
588   else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
589   {
590    /*
591     * Extract profiles from ColorModel option...
592     */
593 
594     const char *profile_name;		/* Name of generic profile */
595 
596 
597     num_profiles = cm_option->num_choices;
598 
599     for (i = cm_option->num_choices, cm_choice = cm_option->choices;
600          i > 0;
601 	 i --, cm_choice ++)
602     {
603       if (!strcmp(cm_choice->choice, "Gray") ||
604           !strcmp(cm_choice->choice, "Black"))
605         profile_name = "Gray";
606       else if (!strcmp(cm_choice->choice, "RGB") ||
607                !strcmp(cm_choice->choice, "CMY"))
608         profile_name = "RGB";
609       else if (!strcmp(cm_choice->choice, "CMYK") ||
610                !strcmp(cm_choice->choice, "KCMY"))
611         profile_name = "CMYK";
612       else
613         profile_name = "DeviceN";
614 
615       snprintf(selector, sizeof(selector), "%s..", profile_name);
616       profile_id = _ppdHashName(selector);
617 
618       profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
619 					  &kCFTypeDictionaryKeyCallBacks,
620 					  &kCFTypeDictionaryValueCallBacks);
621       if (!profile)
622       {
623 	cupsdLogMessage(CUPSD_LOG_ERROR,
624 			"Unable to allocate memory for color profile.");
625 	CFRelease(profiles);
626 	ppdClose(ppd);
627 	return;
628       }
629 
630       apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
631                          cm_choice->text, NULL);
632 
633       dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
634                                           CFSTR("%u"), profile_id);
635       if (dict_key)
636       {
637 	CFDictionarySetValue(profiles, dict_key, profile);
638 	CFRelease(dict_key);
639       }
640 
641       CFRelease(profile);
642 
643       if (cm_choice->marked)
644         default_profile_id = profile_id;
645     }
646   }
647   else
648   {
649    /*
650     * Use the default colorspace...
651     */
652 
653     attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
654 
655     num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
656 
657    /*
658     * Add the grayscale profile first.  We always have a grayscale profile.
659     */
660 
661     profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
662 				        &kCFTypeDictionaryKeyCallBacks,
663 				        &kCFTypeDictionaryValueCallBacks);
664 
665     if (!profile)
666     {
667       cupsdLogMessage(CUPSD_LOG_ERROR,
668                       "Unable to allocate memory for color profile.");
669       CFRelease(profiles);
670       ppdClose(ppd);
671       return;
672     }
673 
674     profile_id = _ppdHashName("Gray..");
675     apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
676 
677     dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
678                                         profile_id);
679     if (dict_key)
680     {
681       CFDictionarySetValue(profiles, dict_key, profile);
682       CFRelease(dict_key);
683     }
684 
685     CFRelease(profile);
686 
687    /*
688     * Then add the RGB/CMYK/DeviceN color profile...
689     */
690 
691     profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
692 				        &kCFTypeDictionaryKeyCallBacks,
693 				        &kCFTypeDictionaryValueCallBacks);
694 
695     if (!profile)
696     {
697       cupsdLogMessage(CUPSD_LOG_ERROR,
698                       "Unable to allocate memory for color profile.");
699       CFRelease(profiles);
700       ppdClose(ppd);
701       return;
702     }
703 
704     switch (ppd->colorspace)
705     {
706       default :
707       case PPD_CS_RGB :
708       case PPD_CS_CMY :
709           profile_id = _ppdHashName("RGB..");
710           apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
711 	                     NULL);
712           break;
713 
714       case PPD_CS_RGBK :
715       case PPD_CS_CMYK :
716           profile_id = _ppdHashName("CMYK..");
717           apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
718 	                     NULL);
719           break;
720 
721       case PPD_CS_GRAY :
722           if (attr)
723             break;
724 
725       case PPD_CS_N :
726           profile_id = _ppdHashName("DeviceN..");
727           apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
728 	                     "DeviceN", NULL);
729           break;
730     }
731 
732     if (CFDictionaryGetCount(profile) > 0)
733     {
734       dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
735                                           CFSTR("%u"), profile_id);
736       if (dict_key)
737       {
738         CFDictionarySetValue(profiles, dict_key, profile);
739         CFRelease(dict_key);
740       }
741     }
742 
743     CFRelease(profile);
744   }
745 
746   if (num_profiles > 0)
747   {
748    /*
749     * Make sure we have a default profile ID...
750     */
751 
752     if (!default_profile_id)
753       default_profile_id = profile_id;	/* Last profile */
754 
755     dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
756                                         default_profile_id);
757     if (dict_key)
758     {
759       CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
760                            dict_key);
761       CFRelease(dict_key);
762     }
763 
764    /*
765     * Get the device ID hash and pathelogical name dictionary.
766     */
767 
768     cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
769 		    p->name);
770 
771     device_id    = _ppdHashName(p->name);
772     device_name  = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
773 					     &kCFTypeDictionaryKeyCallBacks,
774 					     &kCFTypeDictionaryValueCallBacks);
775     printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
776                                              p->name, kCFStringEncodingUTF8);
777 
778     if (device_name && printer_name)
779     {
780      /*
781       * Register the device with ColorSync...
782       */
783 
784       CFTypeRef		deviceDictKeys[] =
785       {					/* Device keys */
786         kColorSyncDeviceDescriptions,
787 	kColorSyncFactoryProfiles,
788 	kColorSyncDeviceUserScope,
789 	kColorSyncDeviceHostScope
790       };
791       CFTypeRef 	deviceDictVals[] =
792       {					/* Device values */
793         device_name,
794 	profiles,
795 	kCFPreferencesAnyUser,
796 	kCFPreferencesCurrentHost
797       };
798       CFDictionaryRef	deviceDict;	/* Device dictionary */
799       CFUUIDRef		deviceUUID;	/* Device UUID */
800 
801       CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
802 
803       deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
804 				      (const void **)deviceDictKeys,
805 				      (const void **)deviceDictVals,
806 				      sizeof(deviceDictKeys) /
807 				          sizeof(deviceDictKeys[0]),
808 				      &kCFTypeDictionaryKeyCallBacks,
809 				      &kCFTypeDictionaryValueCallBacks);
810       deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
811 
812       if (!deviceDict || !deviceUUID ||
813 	  !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
814 				   deviceDict))
815 	error = 1001;
816 
817       if (deviceUUID)
818         CFRelease(deviceUUID);
819 
820       if (deviceDict)
821         CFRelease(deviceDict);
822     }
823     else
824       error = 1000;
825 
826    /*
827     * Clean up...
828     */
829 
830     if (error != noErr)
831       cupsdLogMessage(CUPSD_LOG_ERROR,
832 		      "Unable to register ICC color profiles for \"%s\": %d",
833 		      p->name, (int)error);
834 
835     if (printer_name)
836       CFRelease(printer_name);
837 
838     if (device_name)
839       CFRelease(device_name);
840   }
841 
842  /*
843   * Free any memory we used...
844   */
845 
846   CFRelease(profiles);
847 
848   ppdClose(ppd);
849 }
850 
851 
852 /*
853  * 'apple_unregister_profiles()' - Remove color profiles for the specified
854  *                                 printer.
855  */
856 
857 static void
apple_unregister_profiles(cupsd_printer_t * p)858 apple_unregister_profiles(
859     cupsd_printer_t *p)			/* I - Printer */
860 {
861  /*
862   * Make sure ColorSync is available...
863   */
864 
865   if (&ColorSyncUnregisterDevice != NULL)
866   {
867     CFUUIDRef deviceUUID;		/* Device UUID */
868 
869     deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
870     if (deviceUUID)
871     {
872       ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
873       CFRelease(deviceUUID);
874     }
875   }
876 }
877 
878 
879 #elif defined(HAVE_DBUS)
880 /*
881  * 'colord_create_device()' - Create a device and register profiles.
882  */
883 
884 static void
colord_create_device(cupsd_printer_t * p,ppd_file_t * ppd,cups_array_t * profiles,const char * colorspace,char ** format,const char * relation,const char * scope)885 colord_create_device(
886     cupsd_printer_t *p,			/* I - Printer */
887     ppd_file_t      *ppd,		/* I - PPD file */
888     cups_array_t    *profiles,		/* I - Profiles array */
889     const char      *colorspace,	/* I - Device colorspace, e.g. 'rgb' */
890     char            **format,		/* I - Device qualifier format */
891     const char      *relation,		/* I - Profile relation, either 'soft'
892 					       or 'hard' */
893     const char      *scope)		/* I - The scope of the device, e.g.
894 					       'normal', 'temp' or 'disk' */
895 {
896   DBusMessage	*message = NULL;	/* D-Bus request */
897   DBusMessage	*reply = NULL;		/* D-Bus reply */
898   DBusMessageIter args;			/* D-Bus method arguments */
899   DBusMessageIter dict;			/* D-Bus method arguments */
900   DBusError	error;			/* D-Bus error */
901   const char	*device_path;		/* Device object path */
902   const char	*profile_path;		/* Profile path */
903   char		*default_profile_path = NULL;
904 					/* Default profile path */
905   char		device_id[1024];	/* Device ID as understood by colord */
906   char		format_str[1024];	/* Qualifier format as a string */
907 
908 
909  /*
910   * Create the device...
911   */
912 
913   snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
914   device_path = device_id;
915 
916   message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
917                                          COLORD_DBUS_PATH,
918                                          COLORD_DBUS_INTERFACE,
919                                          "CreateDevice");
920 
921   dbus_message_iter_init_append(message, &args);
922   dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_path);
923   dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
924 
925   snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
926            format[2]);
927 
928   dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
929   colord_dict_add_strings(&dict, "Colorspace", colorspace);
930   colord_dict_add_strings(&dict, "Mode", COLORD_MODE_PHYSICAL);
931   if (ppd->manufacturer)
932     colord_dict_add_strings(&dict, "Vendor", ppd->manufacturer);
933   if (ppd->modelname)
934     colord_dict_add_strings(&dict, "Model", ppd->modelname);
935   if (p->sanitized_device_uri)
936     colord_dict_add_strings(&dict, "Serial", p->sanitized_device_uri);
937   colord_dict_add_strings(&dict, "Format", format_str);
938   colord_dict_add_strings(&dict, "Kind", COLORD_KIND_PRINTER);
939   dbus_message_iter_close_container(&args, &dict);
940 
941  /*
942   * Send the CreateDevice request synchronously...
943   */
944 
945   dbus_error_init(&error);
946   cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateDevice(%s,%s)", device_id,
947                   scope);
948   reply = dbus_connection_send_with_reply_and_block(colord_con, message,
949                                                     COLORD_DBUS_TIMEOUT,
950                                                     &error);
951   if (!reply)
952   {
953     cupsdLogMessage(CUPSD_LOG_WARN, "CreateDevice failed: %s:%s", error.name,
954                     error.message);
955     dbus_error_free(&error);
956     goto out;
957   }
958 
959  /*
960   * Get reply data...
961   */
962 
963   dbus_message_iter_init(reply, &args);
964   if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
965   {
966     cupsdLogMessage(CUPSD_LOG_WARN,
967                     "CreateDevice failed: Incorrect reply type.");
968     goto out;
969   }
970 
971   dbus_message_iter_get_basic(&args, &device_path);
972   cupsdLogMessage(CUPSD_LOG_DEBUG, "Created device \"%s\".", device_path);
973 
974  /*
975   * Add profiles...
976   */
977 
978   for (profile_path = cupsArrayFirst(profiles);
979        profile_path;
980        profile_path = cupsArrayNext(profiles))
981   {
982     colord_device_add_profile(device_path, profile_path, relation);
983   }
984 
985 out:
986 
987   if (default_profile_path)
988     free(default_profile_path);
989 
990   if (message)
991     dbus_message_unref(message);
992 
993   if (reply)
994     dbus_message_unref(reply);
995 }
996 
997 
998 /*
999  * 'colord_create_profile()' - Create a color profile for a printer.
1000  */
1001 
1002 static void
colord_create_profile(cups_array_t * profiles,const char * printer_name,const char * qualifier,const char * colorspace,char ** format,const char * iccfile,const char * scope)1003 colord_create_profile(
1004     cups_array_t *profiles,		/* I - Profiles array */
1005     const char   *printer_name,		/* I - Printer name */
1006     const char   *qualifier,		/* I - Profile qualifier */
1007     const char   *colorspace,		/* I - Profile colorspace */
1008     char         **format,		/* I - Profile qualifier format */
1009     const char   *iccfile,		/* I - ICC filename */
1010     const char   *scope)		/* I - The scope of the profile, e.g.
1011 				               'normal', 'temp' or 'disk' */
1012 {
1013   DBusMessage	*message = NULL;        /* D-Bus request */
1014   DBusMessage	*reply = NULL;          /* D-Bus reply */
1015   DBusMessageIter args;			/* D-Bus method arguments */
1016   DBusMessageIter dict;			/* D-Bus method arguments */
1017   DBusError	error;			/* D-Bus error */
1018   char		*idstr;			/* Profile ID string */
1019   size_t	idstrlen;		/* Profile ID allocated length */
1020   const char	*profile_path;		/* Device object path */
1021   char		format_str[1024];	/* Qualifier format as a string */
1022 
1023 
1024  /*
1025   * Create the profile...
1026   */
1027 
1028   message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1029                                          COLORD_DBUS_PATH,
1030                                          COLORD_DBUS_INTERFACE,
1031                                          "CreateProfile");
1032 
1033   idstrlen = strlen(printer_name) + 1 + strlen(qualifier) + 1;
1034   if ((idstr = malloc(idstrlen)) == NULL)
1035     goto out;
1036   snprintf(idstr, idstrlen, "%s-%s", printer_name, qualifier);
1037   cupsdLogMessage(CUPSD_LOG_DEBUG, "Using profile ID \"%s\".", idstr);
1038 
1039   dbus_message_iter_init_append(message, &args);
1040   dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &idstr);
1041   dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
1042 
1043   snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
1044            format[2]);
1045 
1046   dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
1047   colord_dict_add_strings(&dict, "Qualifier", qualifier);
1048   colord_dict_add_strings(&dict, "Format", format_str);
1049   colord_dict_add_strings(&dict, "Colorspace", colorspace);
1050   if (iccfile)
1051     colord_dict_add_strings(&dict, "Filename", iccfile);
1052   dbus_message_iter_close_container(&args, &dict);
1053 
1054  /*
1055   * Send the CreateProfile request synchronously...
1056   */
1057 
1058   dbus_error_init(&error);
1059   cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateProfile(%s,%s)", idstr,
1060                   scope);
1061   reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1062                                                     COLORD_DBUS_TIMEOUT,
1063                                                     &error);
1064   if (!reply)
1065   {
1066     cupsdLogMessage(CUPSD_LOG_WARN, "CreateProfile failed: %s:%s", error.name,
1067                     error.message);
1068     dbus_error_free(&error);
1069     goto out;
1070   }
1071 
1072  /*
1073   * Get reply data...
1074   */
1075 
1076   dbus_message_iter_init(reply, &args);
1077   if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
1078   {
1079     cupsdLogMessage(CUPSD_LOG_WARN,
1080                     "CreateProfile failed: Incorrect reply type.");
1081     goto out;
1082   }
1083 
1084   dbus_message_iter_get_basic(&args, &profile_path);
1085   cupsdLogMessage(CUPSD_LOG_DEBUG, "Created profile \"%s\".", profile_path);
1086   cupsArrayAdd(profiles, strdup(profile_path));
1087 
1088 out:
1089 
1090   if (message)
1091     dbus_message_unref(message);
1092 
1093   if (reply)
1094     dbus_message_unref(reply);
1095 
1096   if (idstr)
1097     free(idstr);
1098 }
1099 
1100 
1101 /*
1102  * 'colord_delete_device()' - Delete a device
1103  */
1104 
1105 static void
colord_delete_device(const char * device_id)1106 colord_delete_device(
1107     const char *device_id)		/* I - Device ID string */
1108 {
1109   DBusMessage	*message = NULL;	/* D-Bus request */
1110   DBusMessage	*reply = NULL;		/* D-Bus reply */
1111   DBusMessageIter args;			/* D-Bus method arguments */
1112   DBusError	error;			/* D-Bus error */
1113   char		*device_path;		/* Device object path */
1114 
1115 
1116  /*
1117   * Find the device...
1118   */
1119 
1120   if ((device_path = colord_find_device(device_id)) == NULL)
1121     goto out;
1122 
1123  /*
1124   * Delete the device...
1125   */
1126 
1127   message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1128                                          COLORD_DBUS_PATH,
1129                                          COLORD_DBUS_INTERFACE,
1130                                          "DeleteDevice");
1131 
1132   dbus_message_iter_init_append(message, &args);
1133   dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &device_path);
1134 
1135  /*
1136   * Send the DeleteDevice request synchronously...
1137   */
1138 
1139   dbus_error_init(&error);
1140   cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling DeleteDevice(%s)", device_path);
1141   reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1142                                                     COLORD_DBUS_TIMEOUT,
1143                                                     &error);
1144   if (!reply)
1145   {
1146     cupsdLogMessage(CUPSD_LOG_DEBUG, "DeleteDevice failed: %s:%s", error.name,
1147                     error.message);
1148     dbus_error_free(&error);
1149     goto out;
1150   }
1151 
1152 out:
1153 
1154   if (device_path)
1155     free(device_path);
1156 
1157   if (message)
1158     dbus_message_unref(message);
1159 
1160   if (reply)
1161     dbus_message_unref(reply);
1162 }
1163 
1164 
1165 /*
1166  * 'colord_device_add_profile()' - Assign a profile to a device.
1167  */
1168 
1169 static void
colord_device_add_profile(const char * device_path,const char * profile_path,const char * relation)1170 colord_device_add_profile(
1171     const char *device_path,		/* I - Device object path */
1172     const char *profile_path,		/* I - Profile object path */
1173     const char *relation)		/* I - Device relation, either
1174 					       'soft' or 'hard' */
1175 {
1176   DBusMessage	*message = NULL;	/* D-Bus request */
1177   DBusMessage	*reply = NULL;		/* D-Bus reply */
1178   DBusMessageIter args;			/* D-Bus method arguments */
1179   DBusError	error;			/* D-Bus error */
1180 
1181 
1182   message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1183                                          device_path,
1184                                          COLORD_DBUS_INTERFACE_DEVICE,
1185                                          "AddProfile");
1186 
1187   dbus_message_iter_init_append(message, &args);
1188   dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &relation);
1189   dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &profile_path);
1190   cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling %s:AddProfile(%s) [%s]",
1191                   device_path, profile_path, relation);
1192 
1193  /*
1194   * Send the AddProfile request synchronously...
1195   */
1196 
1197   dbus_error_init(&error);
1198   reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1199                                                     COLORD_DBUS_TIMEOUT,
1200                                                     &error);
1201   if (!reply)
1202   {
1203     cupsdLogMessage(CUPSD_LOG_WARN, "AddProfile failed: %s:%s", error.name,
1204                     error.message);
1205     dbus_error_free(&error);
1206     goto out;
1207   }
1208 
1209 out:
1210 
1211   if (message)
1212     dbus_message_unref(message);
1213 
1214   if (reply)
1215     dbus_message_unref(reply);
1216 }
1217 
1218 
1219 /*
1220  * 'colord_dict_add_strings()' - Add two strings to a dictionary.
1221  */
1222 
1223 static void
colord_dict_add_strings(DBusMessageIter * dict,const char * key,const char * value)1224 colord_dict_add_strings(
1225     DBusMessageIter *dict,		/* I - Dictionary */
1226     const char      *key,		/* I - Key string */
1227     const char      *value)		/* I - Value string */
1228 {
1229   DBusMessageIter	entry;		/* Entry to add */
1230 
1231 
1232   dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
1233   dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
1234   dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
1235   dbus_message_iter_close_container(dict, &entry);
1236 }
1237 
1238 
1239 /*
1240  * 'colord_find_device()' - Finds a device
1241  */
1242 
1243 static char *				/* O - Device path or NULL */
colord_find_device(const char * device_id)1244 colord_find_device(
1245     const char *device_id)		/* I - Device ID string */
1246 {
1247   DBusMessage	*message = NULL;	/* D-Bus request */
1248   DBusMessage	*reply = NULL;		/* D-Bus reply */
1249   DBusMessageIter args;			/* D-Bus method arguments */
1250   DBusError	error;			/* D-Bus error */
1251   const char	*device_path_tmp;	/* Device object path */
1252   char		*device_path = NULL;	/* Device object path */
1253 
1254 
1255   message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1256                                          COLORD_DBUS_PATH,
1257                                          COLORD_DBUS_INTERFACE,
1258                                          "FindDeviceById");
1259 
1260   dbus_message_iter_init_append(message, &args);
1261   dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id);
1262 
1263  /*
1264   * Send the FindDeviceById request synchronously...
1265   */
1266 
1267   dbus_error_init(&error);
1268   cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling FindDeviceById(%s)", device_id);
1269   reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1270                                                     COLORD_DBUS_TIMEOUT,
1271                                                     &error);
1272   if (!reply)
1273   {
1274     cupsdLogMessage(CUPSD_LOG_DEBUG, "FindDeviceById failed: %s:%s",
1275 		    error.name, error.message);
1276     dbus_error_free(&error);
1277     goto out;
1278   }
1279 
1280  /*
1281   * Get reply data...
1282   */
1283 
1284   dbus_message_iter_init(reply, &args);
1285   if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
1286   {
1287     cupsdLogMessage(CUPSD_LOG_WARN,
1288                     "FindDeviceById failed: Incorrect reply type.");
1289     goto out;
1290   }
1291 
1292   dbus_message_iter_get_basic(&args, &device_path_tmp);
1293   if (device_path_tmp)
1294     device_path = strdup(device_path_tmp);
1295 
1296 out:
1297 
1298   if (message)
1299     dbus_message_unref(message);
1300 
1301   if (reply)
1302     dbus_message_unref(reply);
1303 
1304   return (device_path);
1305 }
1306 
1307 
1308 /*
1309  * 'colord_get_qualifier_format()' - Get the qualifier format.
1310  *
1311  * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
1312  */
1313 
1314 static void
colord_get_qualifier_format(ppd_file_t * ppd,char * format[3])1315 colord_get_qualifier_format(
1316     ppd_file_t *ppd,			/* I - PPD file data */
1317     char       *format[3])		/* I - Format tuple */
1318 {
1319   const char	*tmp;			/* Temporary string */
1320   ppd_attr_t	*attr;			/* Profile attributes */
1321 
1322 
1323  /*
1324   * Get 1st section...
1325   */
1326 
1327   if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL)
1328     tmp = attr->value;
1329   else if (ppdFindAttr(ppd, "DefaultColorModel", NULL))
1330     tmp = "ColorModel";
1331   else if (ppdFindAttr(ppd, "DefaultColorSpace", NULL))
1332     tmp = "ColorSpace";
1333   else
1334     tmp = "";
1335 
1336   format[0] = strdup(tmp);
1337 
1338  /*
1339   * Get 2nd section...
1340   */
1341 
1342   if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL)
1343     tmp = attr->value;
1344   else
1345     tmp = "MediaType";
1346 
1347   format[1] = strdup(tmp);
1348 
1349  /*
1350   * Get 3rd section...
1351   */
1352 
1353   if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL)
1354     tmp = attr->value;
1355   else
1356     tmp = "Resolution";
1357 
1358   format[2] = strdup(tmp);
1359 }
1360 
1361 
1362 /*
1363  * 'colord_register_printer()' - Register profiles for a printer.
1364  */
1365 
1366 static void
colord_register_printer(cupsd_printer_t * p)1367 colord_register_printer(
1368     cupsd_printer_t *p)			/* I - printer */
1369 {
1370   char		ppdfile[1024],		/* PPD filename */
1371 		iccfile[1024];		/* ICC filename */
1372   ppd_file_t	*ppd;			/* PPD file */
1373   cups_array_t	*profiles;		/* Profile paths array */
1374   ppd_attr_t	*attr;			/* Profile attributes */
1375   const char	*device_colorspace;	/* Device colorspace */
1376   char		*format[3];		/* Qualifier format tuple */
1377 
1378 
1379  /*
1380   * Ensure we have a D-Bus connection...
1381   */
1382 
1383   if (!colord_con)
1384     return;
1385 
1386  /*
1387   * Try opening the PPD file for this printer...
1388   */
1389 
1390   snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
1391   if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
1392     return;
1393 
1394  /*
1395   * Find out the qualifier format
1396   */
1397 
1398   colord_get_qualifier_format(ppd, format);
1399 
1400  /*
1401   * See if we have any embedded profiles...
1402   */
1403 
1404   profiles = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup,
1405 			   (cups_afree_func_t)free);
1406   for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
1407        attr;
1408        attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
1409     if (attr->spec[0] && attr->value && attr->value[0])
1410     {
1411       if (attr->value[0] != '/')
1412         snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
1413                  attr->value);
1414       else
1415         strlcpy(iccfile, attr->value, sizeof(iccfile));
1416 
1417       if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
1418 			 cupsdLogFCMessage, p))
1419 	continue;
1420 
1421       colord_create_profile(profiles, p->name, attr->spec, COLORD_SPACE_UNKNOWN,
1422 			    format, iccfile, COLORD_SCOPE_TEMP);
1423     }
1424 
1425  /*
1426   * Add the grayscale profile first.  We always have a grayscale profile.
1427   */
1428 
1429   colord_create_profile(profiles, p->name, "Gray..", COLORD_SPACE_GRAY,
1430                         format, NULL, COLORD_SCOPE_TEMP);
1431 
1432  /*
1433   * Then add the RGB/CMYK/DeviceN color profile...
1434   */
1435 
1436   device_colorspace = "unknown";
1437   switch (ppd->colorspace)
1438   {
1439     case PPD_CS_RGB :
1440     case PPD_CS_CMY :
1441         device_colorspace = COLORD_SPACE_RGB;
1442         colord_create_profile(profiles, p->name, "RGB..", COLORD_SPACE_RGB,
1443 			      format, NULL, COLORD_SCOPE_TEMP);
1444         break;
1445 
1446     case PPD_CS_RGBK :
1447     case PPD_CS_CMYK :
1448         device_colorspace = COLORD_SPACE_CMYK;
1449         colord_create_profile(profiles, p->name, "CMYK..", COLORD_SPACE_CMYK,
1450                               format, NULL, COLORD_SCOPE_TEMP);
1451         break;
1452 
1453     case PPD_CS_GRAY :
1454         device_colorspace = COLORD_SPACE_GRAY;
1455         break;
1456 
1457     case PPD_CS_N :
1458         colord_create_profile(profiles, p->name, "DeviceN..",
1459                               COLORD_SPACE_UNKNOWN, format, NULL,
1460 			      COLORD_SCOPE_TEMP);
1461         break;
1462   }
1463 
1464  /*
1465   * Register the device with colord.
1466   */
1467 
1468   cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\".",
1469                   p->name);
1470   colord_create_device(p, ppd, profiles, device_colorspace, format,
1471 		       COLORD_RELATION_SOFT, COLORD_SCOPE_TEMP);
1472 
1473  /*
1474   * Free any memory we used...
1475   */
1476 
1477   cupsArrayDelete(profiles);
1478 
1479   free(format[0]);
1480   free(format[1]);
1481   free(format[2]);
1482 
1483   ppdClose(ppd);
1484 }
1485 
1486 
1487 /*
1488  * 'colord_unregister_printer()' - Unregister profiles for a printer.
1489  */
1490 
1491 static void
colord_unregister_printer(cupsd_printer_t * p)1492 colord_unregister_printer(
1493     cupsd_printer_t *p)			/* I - printer */
1494 {
1495   char	device_id[1024];		/* Device ID as understood by colord */
1496 
1497 
1498  /*
1499   * Ensure we have a D-Bus connection...
1500   */
1501 
1502   if (!colord_con)
1503     return;
1504 
1505  /*
1506   * Just delete the device itself, and leave the profiles registered
1507   */
1508 
1509   snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
1510   colord_delete_device(device_id);
1511 }
1512 #endif /* __APPLE__ */
1513