Bump to cups 2.3.3
[platform/upstream/cups.git] / scheduler / colorman.c
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
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
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
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
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
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
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
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
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
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
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
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
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 */
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
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
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
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__ */