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