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