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