#include "cupsd.h"
#include <cups/ppd-private.h>
#ifdef __APPLE__
# include <ApplicationServices/ApplicationServices.h>
extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
# include <CoreFoundation/CoreFoundation.h>
#elif defined(HAVE_DBUS)
# include <dbus/dbus.h>
# define COLORD_SCOPE_NORMAL "normal"
# define COLORD_SCOPE_TEMP "temp"
# define COLORD_SCOPE_DISK "disk"
# define COLORD_RELATION_SOFT "soft"
# define COLORD_RELATION_HARD "hard"
# define COLORD_SPACE_RGB "rgb"
# define COLORD_SPACE_CMYK "cmyk"
# define COLORD_SPACE_GRAY "gray"
# define COLORD_SPACE_UNKNOWN "unknown"
# define COLORD_MODE_PHYSICAL "physical"
# define COLORD_MODE_VIRTUAL "virtual"
# define COLORD_KIND_PRINTER "printer"
# define COLORD_DBUS_MSG(p,m) dbus_message_new_method_call(\
"org.freedesktop.ColorManager", (p),\
"org.freedesktop.ColorManager", (m))
# define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
# define COLORD_DBUS_TIMEOUT 5000
#endif
#if !defined(__APPLE__) && defined(HAVE_DBUS)
static DBusConnection *colord_con = NULL;
#endif
#ifdef __APPLE__
static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
CFMutableDictionaryRef profile,
unsigned id, const char *name,
const char *text, const char *iccfile);
static void apple_register_profiles(cupsd_printer_t *p);
static void apple_unregister_profiles(cupsd_printer_t *p);
#elif defined(HAVE_DBUS)
static void colord_create_device(cupsd_printer_t *p, ppd_file_t *ppd,
cups_array_t *profiles,
const char *colorspace, char **format,
const char *relation, const char *scope);
static void colord_create_profile(cups_array_t *profiles,
const char *printer_name,
const char *qualifier,
const char *colorspace,
char **format, const char *iccfile,
const char *scope);
static void colord_delete_device(const char *device_id);
static void colord_device_add_profile(const char *device_path,
const char *profile_path,
const char *relation);
static void colord_dict_add_strings(DBusMessageIter *dict,
const char *key, const char *value);
static char *colord_find_device(const char *device_id);
static void colord_get_qualifier_format(ppd_file_t *ppd, char *format[3]);
static void colord_register_printer(cupsd_printer_t *p);
static void colord_unregister_printer(cupsd_printer_t *p);
#endif
void
cupsdRegisterColor(cupsd_printer_t *p)
{
#ifdef __APPLE__
if (!RunUser)
{
apple_unregister_profiles(p);
apple_register_profiles(p);
}
#elif defined(HAVE_DBUS)
colord_unregister_printer(p);
colord_register_printer(p);
#endif
}
void
cupsdStartColor(void)
{
#if !defined(__APPLE__) && defined(HAVE_DBUS)
cupsd_printer_t *p;
colord_con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
p;
p = (cupsd_printer_t *)cupsArrayNext(Printers))
cupsdRegisterColor(p);
#endif
}
void
cupsdStopColor(void)
{
#if !defined(__APPLE__) && defined(HAVE_DBUS)
dbus_connection_unref(colord_con);
colord_con = NULL;
#endif
}
void
cupsdUnregisterColor(cupsd_printer_t *p)
{
#ifdef __APPLE__
if (!RunUser)
apple_unregister_profiles(p);
#elif defined(HAVE_DBUS)
colord_unregister_printer(p);
#endif
}
#ifdef __APPLE__
static void
apple_init_profile(
ppd_file_t *ppd,
cups_array_t *languages,
CFMutableDictionaryRef profile,
unsigned id,
const char *name,
const char *text,
const char *iccfile)
{
CFURLRef url;
CFMutableDictionaryRef dict;
char *language;
ppd_attr_t *attr;
CFStringRef cflang,
cftext;
(void)id;
dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!dict)
{
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
iccfile);
return;
}
cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
kCFStringEncodingUTF8);
if (cftext)
{
CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
CFRelease(cftext);
}
if (languages)
{
cupsArraySave(ppd->sorted_attrs);
for (language = (char *)cupsArrayFirst(languages);
language;
language = (char *)cupsArrayNext(languages))
{
if (iccfile)
{
if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
language)) == NULL)
attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
}
else
attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
if (attr && attr->text[0])
{
cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
kCFStringEncodingUTF8);
cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
kCFStringEncodingUTF8);
if (cflang && cftext)
CFDictionarySetValue(dict, cflang, cftext);
if (cflang)
CFRelease(cflang);
if (cftext)
CFRelease(cftext);
}
}
cupsArrayRestore(ppd->sorted_attrs);
}
if (iccfile)
{
url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(const UInt8 *)iccfile,
strlen(iccfile), false);
if (url)
{
CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
CFRelease(url);
}
}
CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
CFRelease(dict);
}
static void
apple_register_profiles(
cupsd_printer_t *p)
{
int i;
char ppdfile[1024],
iccfile[1024],
selector[PPD_MAX_NAME];
ppd_file_t *ppd;
ppd_attr_t *attr,
*profileid_attr,
*q1_attr,
*q2_attr,
*q3_attr;
char q_keyword[PPD_MAX_NAME];
const char *q1_choice,
*q2_choice,
*q3_choice;
ppd_option_t *cm_option;
ppd_choice_t *cm_choice;
int num_profiles;
OSStatus error = 0;
unsigned device_id,
profile_id = 0,
default_profile_id = 0;
CFMutableDictionaryRef device_name;
CFStringRef printer_name;
cups_array_t *languages;
CFMutableDictionaryRef profiles,
profile;
CFStringRef dict_key;
if (ColorSyncRegisterDevice == NULL)
return;
snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
return;
for (num_profiles = 0, attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
attr;
attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
if (attr->spec[0] && attr->value && attr->value[0])
{
if (attr->value[0] != '/')
snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
attr->value);
else
strlcpy(iccfile, attr->value, sizeof(iccfile));
if (access(iccfile, 0))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"%s: ICC Profile \"%s\" does not exist.", p->name,
iccfile);
cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
continue;
}
num_profiles ++;
}
profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!profiles)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to allocate memory for factory profiles.");
ppdClose(ppd);
return;
}
if (num_profiles > 0)
{
if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
attr->value && attr->value[0])
{
snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
}
else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
if (q1_attr && q1_attr->value && q1_attr->value[0])
q1_choice = q1_attr->value;
else
q1_choice = "";
if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
attr->value && attr->value[0])
{
snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
}
else
q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
if (q2_attr && q2_attr->value && q2_attr->value[0])
q2_choice = q2_attr->value;
else
q2_choice = NULL;
if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
attr->value && attr->value[0])
{
snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
}
else
q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
if (q3_attr && q3_attr->value && q3_attr->value[0])
q3_choice = q3_attr->value;
else
q3_choice = NULL;
languages = _ppdGetLanguages(ppd);
for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
attr;
attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
if (attr->spec[0] && attr->value && attr->value[0])
{
if (attr->value[0] != '/')
snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
attr->value);
else
strlcpy(iccfile, attr->value, sizeof(iccfile));
if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
cupsdLogFCMessage, p))
continue;
cupsArraySave(ppd->sorted_attrs);
if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
attr->spec)) != NULL &&
profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
else
profile_id = _ppdHashName(attr->spec);
cupsArrayRestore(ppd->sorted_attrs);
profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!profile)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to allocate memory for color profile.");
CFRelease(profiles);
ppdClose(ppd);
return;
}
apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
attr->text[0] ? attr->text : attr->spec, iccfile);
dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("%u"), profile_id);
if (dict_key)
{
CFDictionarySetValue(profiles, dict_key, profile);
CFRelease(dict_key);
}
CFRelease(profile);
if (!default_profile_id && q1_choice && q2_choice && q3_choice)
{
snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
q3_choice);
if (!strcmp(selector, attr->spec))
default_profile_id = profile_id;
}
if (!default_profile_id && q1_choice && q2_choice)
{
snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
if (!strcmp(selector, attr->spec))
default_profile_id = profile_id;
}
if (!default_profile_id && q1_choice && q3_choice)
{
snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
if (!strcmp(selector, attr->spec))
default_profile_id = profile_id;
}
if (!default_profile_id && q1_choice)
{
snprintf(selector, sizeof(selector), "%s..", q1_choice);
if (!strcmp(selector, attr->spec))
default_profile_id = profile_id;
}
if (!default_profile_id && q2_choice && q3_choice)
{
snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
if (!strcmp(selector, attr->spec))
default_profile_id = profile_id;
}
if (!default_profile_id && q2_choice)
{
snprintf(selector, sizeof(selector), ".%s.", q2_choice);
if (!strcmp(selector, attr->spec))
default_profile_id = profile_id;
}
if (!default_profile_id && q3_choice)
{
snprintf(selector, sizeof(selector), "..%s", q3_choice);
if (!strcmp(selector, attr->spec))
default_profile_id = profile_id;
}
}
_ppdFreeLanguages(languages);
}
else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
{
const char *profile_name;
num_profiles = cm_option->num_choices;
for (i = cm_option->num_choices, cm_choice = cm_option->choices;
i > 0;
i --, cm_choice ++)
{
if (!strcmp(cm_choice->choice, "Gray") ||
!strcmp(cm_choice->choice, "Black"))
profile_name = "Gray";
else if (!strcmp(cm_choice->choice, "RGB") ||
!strcmp(cm_choice->choice, "CMY"))
profile_name = "RGB";
else if (!strcmp(cm_choice->choice, "CMYK") ||
!strcmp(cm_choice->choice, "KCMY"))
profile_name = "CMYK";
else
profile_name = "DeviceN";
snprintf(selector, sizeof(selector), "%s..", profile_name);
profile_id = _ppdHashName(selector);
profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!profile)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to allocate memory for color profile.");
CFRelease(profiles);
ppdClose(ppd);
return;
}
apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
cm_choice->text, NULL);
dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("%u"), profile_id);
if (dict_key)
{
CFDictionarySetValue(profiles, dict_key, profile);
CFRelease(dict_key);
}
CFRelease(profile);
if (cm_choice->marked)
default_profile_id = profile_id;
}
}
else
{
attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!profile)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to allocate memory for color profile.");
CFRelease(profiles);
ppdClose(ppd);
return;
}
profile_id = _ppdHashName("Gray..");
apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
profile_id);
if (dict_key)
{
CFDictionarySetValue(profiles, dict_key, profile);
CFRelease(dict_key);
}
CFRelease(profile);
profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!profile)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to allocate memory for color profile.");
CFRelease(profiles);
ppdClose(ppd);
return;
}
switch (ppd->colorspace)
{
default :
case PPD_CS_RGB :
case PPD_CS_CMY :
profile_id = _ppdHashName("RGB..");
apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
NULL);
break;
case PPD_CS_RGBK :
case PPD_CS_CMYK :
profile_id = _ppdHashName("CMYK..");
apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
NULL);
break;
case PPD_CS_GRAY :
if (attr)
break;
case PPD_CS_N :
profile_id = _ppdHashName("DeviceN..");
apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
"DeviceN", NULL);
break;
}
if (CFDictionaryGetCount(profile) > 0)
{
dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("%u"), profile_id);
if (dict_key)
{
CFDictionarySetValue(profiles, dict_key, profile);
CFRelease(dict_key);
}
}
CFRelease(profile);
}
if (num_profiles > 0)
{
if (!default_profile_id)
default_profile_id = profile_id;
dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
default_profile_id);
if (dict_key)
{
CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
dict_key);
CFRelease(dict_key);
}
cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
p->name);
device_id = _ppdHashName(p->name);
device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
p->name, kCFStringEncodingUTF8);
if (device_name && printer_name)
{
CFTypeRef deviceDictKeys[] =
{
kColorSyncDeviceDescriptions,
kColorSyncFactoryProfiles,
kColorSyncDeviceUserScope,
kColorSyncDeviceHostScope
};
CFTypeRef deviceDictVals[] =
{
device_name,
profiles,
kCFPreferencesAnyUser,
kCFPreferencesCurrentHost
};
CFDictionaryRef deviceDict;
CFUUIDRef deviceUUID;
CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
(const void **)deviceDictKeys,
(const void **)deviceDictVals,
sizeof(deviceDictKeys) /
sizeof(deviceDictKeys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
if (!deviceDict || !deviceUUID ||
!ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
deviceDict))
error = 1001;
if (deviceUUID)
CFRelease(deviceUUID);
if (deviceDict)
CFRelease(deviceDict);
}
else
error = 1000;
if (error != noErr)
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to register ICC color profiles for \"%s\": %d",
p->name, (int)error);
if (printer_name)
CFRelease(printer_name);
if (device_name)
CFRelease(device_name);
}
CFRelease(profiles);
ppdClose(ppd);
}
static void
apple_unregister_profiles(
cupsd_printer_t *p)
{
if (ColorSyncUnregisterDevice != NULL)
{
CFUUIDRef deviceUUID;
deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
if (deviceUUID)
{
ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
CFRelease(deviceUUID);
}
}
}
#elif defined(HAVE_DBUS)
static void
colord_create_device(
cupsd_printer_t *p,
ppd_file_t *ppd,
cups_array_t *profiles,
const char *colorspace,
char **format,
const char *relation,
const char *scope)
{
DBusMessage *message = NULL;
DBusMessage *reply = NULL;
DBusMessageIter args;
DBusMessageIter dict;
DBusError error;
const char *device_path;
const char *profile_path;
char *default_profile_path = NULL;
char device_id[1024];
char format_str[1024];
snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
device_path = device_id;
message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "CreateDevice");
dbus_message_iter_init_append(message, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_path);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
format[2]);
dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
colord_dict_add_strings(&dict, "Colorspace", colorspace);
colord_dict_add_strings(&dict, "Mode", COLORD_MODE_PHYSICAL);
if (ppd->manufacturer)
colord_dict_add_strings(&dict, "Vendor", ppd->manufacturer);
if (ppd->modelname)
colord_dict_add_strings(&dict, "Model", ppd->modelname);
if (p->sanitized_device_uri)
colord_dict_add_strings(&dict, "Serial", p->sanitized_device_uri);
colord_dict_add_strings(&dict, "Format", format_str);
colord_dict_add_strings(&dict, "Kind", COLORD_KIND_PRINTER);
dbus_message_iter_close_container(&args, &dict);
dbus_error_init(&error);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateDevice(%s,%s)", device_id,
scope);
reply = dbus_connection_send_with_reply_and_block(colord_con, message,
COLORD_DBUS_TIMEOUT,
&error);
if (!reply)
{
cupsdLogMessage(CUPSD_LOG_WARN, "CreateDevice failed: %s:%s", error.name,
error.message);
dbus_error_free(&error);
goto out;
}
dbus_message_iter_init(reply, &args);
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
{
cupsdLogMessage(CUPSD_LOG_WARN,
"CreateDevice failed: Incorrect reply type.");
goto out;
}
dbus_message_iter_get_basic(&args, &device_path);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Created device \"%s\".", device_path);
for (profile_path = cupsArrayFirst(profiles);
profile_path;
profile_path = cupsArrayNext(profiles))
{
colord_device_add_profile(device_path, profile_path, relation);
}
out:
if (default_profile_path)
free(default_profile_path);
if (message)
dbus_message_unref(message);
if (reply)
dbus_message_unref(reply);
}
static void
colord_create_profile(
cups_array_t *profiles,
const char *printer_name,
const char *qualifier,
const char *colorspace,
char **format,
const char *iccfile,
const char *scope)
{
DBusMessage *message = NULL;
DBusMessage *reply = NULL;
DBusMessageIter args;
DBusMessageIter dict;
DBusError error;
char *idstr;
size_t idstrlen;
const char *profile_path;
char format_str[1024];
message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "CreateProfile");
idstrlen = strlen(printer_name) + 1 + strlen(qualifier) + 1;
if ((idstr = malloc(idstrlen)) == NULL)
goto out;
snprintf(idstr, idstrlen, "%s-%s", printer_name, qualifier);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Using profile ID \"%s\".", idstr);
dbus_message_iter_init_append(message, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &idstr);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
format[2]);
dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
colord_dict_add_strings(&dict, "Qualifier", qualifier);
colord_dict_add_strings(&dict, "Format", format_str);
colord_dict_add_strings(&dict, "Colorspace", colorspace);
if (iccfile)
colord_dict_add_strings(&dict, "Filename", iccfile);
dbus_message_iter_close_container(&args, &dict);
dbus_error_init(&error);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateProfile(%s,%s)", idstr,
scope);
reply = dbus_connection_send_with_reply_and_block(colord_con, message,
COLORD_DBUS_TIMEOUT,
&error);
if (!reply)
{
cupsdLogMessage(CUPSD_LOG_WARN, "CreateProfile failed: %s:%s", error.name,
error.message);
dbus_error_free(&error);
goto out;
}
dbus_message_iter_init(reply, &args);
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
{
cupsdLogMessage(CUPSD_LOG_WARN,
"CreateProfile failed: Incorrect reply type.");
goto out;
}
dbus_message_iter_get_basic(&args, &profile_path);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Created profile \"%s\".", profile_path);
cupsArrayAdd(profiles, strdup(profile_path));
out:
if (message)
dbus_message_unref(message);
if (reply)
dbus_message_unref(reply);
if (idstr)
free(idstr);
}
static void
colord_delete_device(
const char *device_id)
{
DBusMessage *message = NULL;
DBusMessage *reply = NULL;
DBusMessageIter args;
DBusError error;
char *device_path;
if ((device_path = colord_find_device(device_id)) == NULL)
goto out;
message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "DeleteDevice");
dbus_message_iter_init_append(message, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &device_path);
dbus_error_init(&error);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling DeleteDevice(%s)", device_path);
reply = dbus_connection_send_with_reply_and_block(colord_con, message,
COLORD_DBUS_TIMEOUT,
&error);
if (!reply)
{
cupsdLogMessage(CUPSD_LOG_DEBUG, "DeleteDevice failed: %s:%s", error.name,
error.message);
dbus_error_free(&error);
goto out;
}
out:
if (device_path)
free(device_path);
if (message)
dbus_message_unref(message);
if (reply)
dbus_message_unref(reply);
}
static void
colord_device_add_profile(
const char *device_path,
const char *profile_path,
const char *relation)
{
DBusMessage *message = NULL;
DBusMessage *reply = NULL;
DBusMessageIter args;
DBusError error;
message = COLORD_DBUS_MSG(device_path, "AddProfile");
dbus_message_iter_init_append(message, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &relation);
dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &profile_path);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling %s:AddProfile(%s) [%s]",
device_path, profile_path, relation);
dbus_error_init(&error);
reply = dbus_connection_send_with_reply_and_block(colord_con, message,
COLORD_DBUS_TIMEOUT,
&error);
if (!reply)
{
cupsdLogMessage(CUPSD_LOG_WARN, "AddProfile failed: %s:%s", error.name,
error.message);
dbus_error_free(&error);
goto out;
}
out:
if (message)
dbus_message_unref(message);
if (reply)
dbus_message_unref(reply);
}
static void
colord_dict_add_strings(
DBusMessageIter *dict,
const char *key,
const char *value)
{
DBusMessageIter entry;
dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
dbus_message_iter_close_container(dict, &entry);
}
static char *
colord_find_device(
const char *device_id)
{
DBusMessage *message = NULL;
DBusMessage *reply = NULL;
DBusMessageIter args;
DBusError error;
const char *device_path_tmp;
char *device_path = NULL;
message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "FindDeviceById");
dbus_message_iter_init_append(message, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id);
dbus_error_init(&error);
cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling FindDeviceById(%s)", device_id);
reply = dbus_connection_send_with_reply_and_block(colord_con, message,
COLORD_DBUS_TIMEOUT,
&error);
if (!reply)
{
cupsdLogMessage(CUPSD_LOG_DEBUG, "FindDeviceById failed: %s:%s",
error.name, error.message);
dbus_error_free(&error);
goto out;
}
dbus_message_iter_init(reply, &args);
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
{
cupsdLogMessage(CUPSD_LOG_WARN,
"FindDeviceById failed: Incorrect reply type.");
goto out;
}
dbus_message_iter_get_basic(&args, &device_path_tmp);
if (device_path_tmp)
device_path = strdup(device_path_tmp);
out:
if (message)
dbus_message_unref(message);
if (reply)
dbus_message_unref(reply);
return (device_path);
}
static void
colord_get_qualifier_format(
ppd_file_t *ppd,
char *format[3])
{
const char *tmp;
ppd_attr_t *attr;
if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL)
tmp = attr->value;
else if (ppdFindAttr(ppd, "DefaultColorModel", NULL))
tmp = "ColorModel";
else if (ppdFindAttr(ppd, "DefaultColorSpace", NULL))
tmp = "ColorSpace";
else
tmp = "";
format[0] = strdup(tmp);
if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL)
tmp = attr->value;
else
tmp = "MediaType";
format[1] = strdup(tmp);
if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL)
tmp = attr->value;
else
tmp = "Resolution";
format[2] = strdup(tmp);
}
static void
colord_register_printer(
cupsd_printer_t *p)
{
char ppdfile[1024],
iccfile[1024];
ppd_file_t *ppd;
cups_array_t *profiles;
ppd_attr_t *attr;
const char *device_colorspace;
char *format[3];
if (!colord_con)
return;
snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
return;
colord_get_qualifier_format(ppd, format);
profiles = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup,
(cups_afree_func_t)free);
for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
attr;
attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
if (attr->spec[0] && attr->value && attr->value[0])
{
if (attr->value[0] != '/')
snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
attr->value);
else
strlcpy(iccfile, attr->value, sizeof(iccfile));
if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
cupsdLogFCMessage, p))
continue;
colord_create_profile(profiles, p->name, attr->spec, COLORD_SPACE_UNKNOWN,
format, iccfile, COLORD_SCOPE_TEMP);
}
colord_create_profile(profiles, p->name, "Gray..", COLORD_SPACE_GRAY,
format, NULL, COLORD_SCOPE_TEMP);
device_colorspace = "unknown";
switch (ppd->colorspace)
{
case PPD_CS_RGB :
case PPD_CS_CMY :
device_colorspace = COLORD_SPACE_RGB;
colord_create_profile(profiles, p->name, "RGB..", COLORD_SPACE_RGB,
format, NULL, COLORD_SCOPE_TEMP);
break;
case PPD_CS_RGBK :
case PPD_CS_CMYK :
device_colorspace = COLORD_SPACE_CMYK;
colord_create_profile(profiles, p->name, "CMYK..", COLORD_SPACE_CMYK,
format, NULL, COLORD_SCOPE_TEMP);
break;
case PPD_CS_GRAY :
device_colorspace = COLORD_SPACE_GRAY;
break;
case PPD_CS_N :
colord_create_profile(profiles, p->name, "DeviceN..",
COLORD_SPACE_UNKNOWN, format, NULL,
COLORD_SCOPE_TEMP);
break;
}
cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\".",
p->name);
colord_create_device(p, ppd, profiles, device_colorspace, format,
COLORD_RELATION_SOFT, COLORD_SCOPE_TEMP);
cupsArrayDelete(profiles);
free(format[0]);
free(format[1]);
free(format[2]);
ppdClose(ppd);
}
static void
colord_unregister_printer(
cupsd_printer_t *p)
{
char device_id[1024];
if (!colord_con)
return;
snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
colord_delete_device(device_id);
}
#endif