+ 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))
+ continue;
+
+ num_profiles ++;
+ }
+
+
+ /*
+ * If we have profiles, add them...
+ */
+
+ if (num_profiles > 0)
+ {
+ if (profile_key[0] == 'A')
+ {
+ /*
+ * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile
+ * attribute...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL &&
+ attr->value)
+ default_profile_id = atoi(attr->value);
+
+ q1_choice = q2_choice = q3_choice = NULL;
+ }
+ else
+ {
+ /*
+ * For CUPS PPDs, figure out the default profile selector values...
+ */
+
+ 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;
+ }
+
+ /*
+ * Build the array of profiles...
+ *
+ * Note: This calloc actually requests slightly more memory than needed.
+ */
+
+ if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for %d profiles!",
+ num_profiles);
+ ppdClose(ppd);
+ return;
+ }
+
+ profiles->profileCount = num_profiles;
+ languages = _ppdGetLanguages(ppd);
+
+ for (profile = profiles->profiles,
+ attr = ppdFindAttr(ppd, profile_key, NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, profile_key, NULL))
+ if (attr->spec[0] && attr->value && attr->value[0])
+ {
+ /*
+ * Add this profile...
+ */
+
+ 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))
+ continue;
+
+ if (profile_key[0] == 'c')
+ {
+ 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);
+ }
+ else
+ profile_id = atoi(attr->spec);
+
+ apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
+ attr->text[0] ? attr->text : attr->spec, iccfile);
+
+ profile ++;
+
+ /*
+ * See if this is the default profile...
+ */
+
+ if (!default_profile_id)
+ {
+ if (q2_choice)
+ {
+ if (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)
+ {
+ snprintf(selector, sizeof(selector), "%s.%s.", q1_choice,
+ q2_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+ }
+
+ if (!default_profile_id && 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)
+ {
+ snprintf(selector, sizeof(selector), "%s..", q1_choice);
+ if (!strcmp(selector, attr->spec))
+ default_profile_id = profile_id;
+ }
+ }
+ }
+
+ _ppdFreeLanguages(languages);
+ }
+ else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
+ {
+ /*
+ * Extract profiles from ColorModel option...
+ */
+
+ const char *profile_name; /* Name of generic profile */
+
+
+ num_profiles = cm_option->num_choices;
+
+ if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for %d profiles!",
+ num_profiles);
+ ppdClose(ppd);
+ return;
+ }
+
+ profiles->profileCount = num_profiles;
+
+ for (profile = profiles->profiles, i = cm_option->num_choices,
+ cm_choice = cm_option->choices;
+ i > 0;
+ i --, cm_choice ++, profile ++)
+ {
+ 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);
+
+ apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
+ cm_choice->text, NULL);
+
+ if (cm_choice->marked)
+ default_profile_id = profile_id;
+ }
+ }
+ else
+ {
+ /*
+ * Use the default colorspace...
+ */
+
+ attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
+
+ num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
+
+ if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate memory for %d profiles!",
+ num_profiles);
+ ppdClose(ppd);
+ return;
+ }
+
+ profiles->profileCount = num_profiles;
+
+ apple_init_profile(ppd, NULL, profiles->profiles, _ppdHashName("Gray.."),
+ "Gray", "Gray", NULL);
+
+ switch (ppd->colorspace)
+ {
+ case PPD_CS_RGB :
+ case PPD_CS_CMY :
+ apple_init_profile(ppd, NULL, profiles->profiles + 1,
+ _ppdHashName("RGB.."), "RGB", "RGB", NULL);
+ break;
+ case PPD_CS_RGBK :
+ case PPD_CS_CMYK :
+ apple_init_profile(ppd, NULL, profiles->profiles + 1,
+ _ppdHashName("CMYK.."), "CMYK", "CMYK", NULL);
+ break;
+
+ case PPD_CS_GRAY :
+ if (attr)
+ break;
+
+ case PPD_CS_N :
+ apple_init_profile(ppd, NULL, profiles->profiles + 1,
+ _ppdHashName("DeviceN.."), "DeviceN", "DeviceN",
+ NULL);
+ break;
+ }
+ }
+
+ if (num_profiles > 0)
+ {
+ /*
+ * Make sure we have a default profile ID...
+ */
+
+ if (!default_profile_id)
+ default_profile_id = profiles->profiles[num_profiles - 1].profileID;
+
+ /*
+ * Get the device ID hash and pathelogical name dictionary.
+ */
+
+ 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)
+ {
+ CFDictionarySetValue(device_name, CFSTR("en"), printer_name);
+
+ /*
+ * Register the device with ColorSync...
+ */
+
+ error = CMRegisterColorDevice(cmPrinterDeviceClass, device_id,
+ device_name, &scope);
+
+ /*
+ * Register the profiles...
+ */
+
+ if (error == noErr)
+ error = CMSetDeviceFactoryProfiles(cmPrinterDeviceClass, device_id,
+ default_profile_id, profiles);
+ }
+ else
+ error = 1000;
+
+ /*
+ * Clean up...
+ */
+
+ if (error != noErr)
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to register ICC color profiles for \"%s\" - %d",
+ p->name, (int)error);
+
+ for (profile = profiles->profiles;
+ num_profiles > 0;
+ profile ++, num_profiles --)
+ CFRelease(profile->profileName);
+
+ free(profiles);
+
+ if (printer_name)
+ CFRelease(printer_name);
+
+ if (device_name)
+ CFRelease(device_name);
+ }
+
+ ppdClose(ppd);
+}
+
+
+/*
+ * 'apple_unregister_profiles()' - Remove color profiles for the specified
+ * printer.
+ */
+
+static void
+apple_unregister_profiles(
+ cupsd_printer_t *p) /* I - Printer */
+{
+ /*
+ * Make sure ColorSync is available...
+ */
+
+ if (CMUnregisterColorDevice != NULL)
+ CMUnregisterColorDevice(cmPrinterDeviceClass, _ppdHashName(p->name));
+}
+#endif /* __APPLE__ */
+
+/*
+ * 'apply_printer_defaults()' - Apply printer default options to a job.
+ */
+
+static void
+apply_printer_defaults(
+ cupsd_printer_t *printer, /* I - Printer */
+ cupsd_job_t *job) /* I - Job */
+{
+ int i, /* Looping var */
+ num_options; /* Number of default options */
+ cups_option_t *options, /* Default options */
+ *option; /* Current option */
+
+
+ /*
+ * Collect all of the default options and add the missing ones to the
+ * job object...
+ */
+
+ for (i = printer->num_options, num_options = 0, options = NULL,
+ option = printer->options;
+ i > 0;
+ i --, option ++)
+ if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
+ {
+ num_options = cupsAddOption(option->name, option->value, num_options,
+ &options);
+ }
+
+ /*
+ * Encode these options as attributes in the job object...
+ */
+
+ cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
+ cupsFreeOptions(num_options, options);
+}
+
+
+/*
+ * 'authenticate_job()' - Set job authentication info.
+ */
+
+static void
+authenticate_job(cupsd_client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job URI */
+{
+ ipp_attribute_t *attr, /* job-id attribute */
+ *auth_info; /* auth-info attribute */
+ int jobid; /* Job ID */
+ cupsd_job_t *job; /* Current job */
+ char scheme[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
+ con, con->http.fd, uri->values[0].string.text);
+
+ /*
+ * Start with "everything is OK" status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+
+ /*
+ * See if we have a job URI or a printer URI...
+ */
+
+ if (!strcmp(uri->name, "printer-uri"))
+ {
+ /*
+ * Got a printer URI; see if we also have a job-id attribute...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "job-id",
+ IPP_TAG_INTEGER)) == NULL)
+ {
+ send_ipp_status(con, IPP_BAD_REQUEST,
+ _("Got a printer-uri attribute but no job-id!"));
+ return;
+ }