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