+ if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
+ printer);
+ else
+ {
+ cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ return;
+ }
+
+ fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
+
+ /*
+ * If the user clicks on the Auto-Configure button, send an AutoConfigure
+ * command file to the printer...
+ */
+
+ if (cgiGetVariable("AUTOCONFIGURE"))
+ {
+ cgiPrintCommand(http, printer, "AutoConfigure", "Set Default Options");
+ return;
+ }
+
+ /*
+ * Get the PPD file...
+ */
+
+ if (is_class)
+ filename = NULL;
+ else
+ filename = cupsGetPPD2(http, printer);
+
+ if (filename)
+ {
+ fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
+
+ if ((ppd = ppdOpenFile(filename)) == NULL)
+ {
+ cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i)));
+ cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file")));
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ return;
+ }
+ }
+ else
+ {
+ fputs("DEBUG: No PPD file\n", stderr);
+ ppd = NULL;
+ }
+
+ if (cgiGetVariable("job_sheets_start") != NULL ||
+ cgiGetVariable("job_sheets_end") != NULL)
+ have_options = 1;
+ else
+ have_options = 0;
+
+ if (ppd)
+ {
+ ppdMarkDefaults(ppd);
+
+ for (option = ppdFirstOption(ppd);
+ option;
+ option = ppdNextOption(ppd))
+ {
+ if ((var = cgiGetVariable(option->keyword)) != NULL)
+ {
+ have_options = 1;
+ ppdMarkOption(ppd, option->keyword, var);
+ fprintf(stderr, "DEBUG: Set %s to %s...\n", option->keyword, var);
+ }
+ else
+ fprintf(stderr, "DEBUG: Didn't find %s...\n", option->keyword);
+ }
+ }
+
+ if (!have_options || ppdConflicts(ppd))
+ {
+ /*
+ * Show the options to the user...
+ */
+
+ fputs("DEBUG: Showing options...\n", stderr);
+
+ /*
+ * Show auto-configure button if supported...
+ */
+
+ if (ppd)
+ {
+ if (ppd->num_filters == 0 ||
+ ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
+ ppdattr->value && strstr(ppdattr->value, "AutoConfigure")))
+ cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
+ else
+ {
+ for (i = 0; i < ppd->num_filters; i ++)
+ if (!strncmp(ppd->filters[i], "application/vnd.cups-postscript", 31))
+ {
+ cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
+ break;
+ }
+ }
+ }
+
+ /*
+ * Get the printer attributes...
+ */
+
+ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ "localhost", 0, "/printers/%s", printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ response = cupsDoRequest(http, request, "/");
+
+ /*
+ * List the groups used as "tabs"...
+ */
+
+ i = 0;
+
+ if (ppd)
+ {
+ for (group = ppd->groups;
+ i < ppd->num_groups;
+ i ++, group ++)
+ {
+ cgiSetArray("GROUP_ID", i, group->name);
+
+ if (!strcmp(group->name, "InstallableOptions"))
+ cgiSetArray("GROUP", i, cgiText(_("Options Installed")));
+ else
+ cgiSetArray("GROUP", i, group->text);
+ }
+ }
+
+ if (ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO))
+ {
+ cgiSetArray("GROUP_ID", i, "CUPS_BANNERS");
+ cgiSetArray("GROUP", i ++, cgiText(_("Banners")));
+ }
+
+ if (ippFindAttribute(response, "printer-error-policy-supported",
+ IPP_TAG_ZERO) ||
+ ippFindAttribute(response, "printer-op-policy-supported",
+ IPP_TAG_ZERO))
+ {
+ cgiSetArray("GROUP_ID", i, "CUPS_POLICIES");
+ cgiSetArray("GROUP", i ++, cgiText(_("Policies")));
+ }
+
+ if ((attr = ippFindAttribute(response, "port-monitor-supported",
+ IPP_TAG_NAME)) != NULL && attr->num_values > 1)
+ {
+ cgiSetArray("GROUP_ID", i, "CUPS_PORT_MONITOR");
+ cgiSetArray("GROUP", i, cgiText(_("Port Monitor")));
+ }
+
+ cgiStartHTML(cgiText(_("Set Printer Options")));
+ cgiCopyTemplateLang("set-printer-options-header.tmpl");
+
+ if (ppd)
+ {
+ ppdLocalize(ppd);
+
+ if (ppdConflicts(ppd))
+ {
+ for (i = ppd->num_groups, k = 0, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ if (option->conflicted)
+ {
+ cgiSetArray("ckeyword", k, option->keyword);
+ cgiSetArray("ckeytext", k, option->text);
+
+ for (m = 0; m < option->num_choices; m ++)
+ {
+ if (option->choices[m].marked)
+ {
+ cgiSetArray("cchoice", k, option->choices[m].text);
+ break;
+ }
+ }
+
+ k ++;
+ }
+
+ cgiCopyTemplateLang("option-conflict.tmpl");
+ }
+
+ for (i = ppd->num_groups, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ {
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ {
+ if (!strcmp(option->keyword, "PageRegion"))
+ continue;
+
+ if (option->num_choices > 1)
+ break;
+ }
+
+ if (j == 0)
+ continue;
+
+ cgiSetVariable("GROUP_ID", group->name);
+
+ if (!strcmp(group->name, "InstallableOptions"))
+ cgiSetVariable("GROUP", cgiText(_("Options Installed")));
+ else
+ cgiSetVariable("GROUP", group->text);
+
+ cgiCopyTemplateLang("option-header.tmpl");
+
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ {
+ if (!strcmp(option->keyword, "PageRegion") || option->num_choices < 2)
+ continue;
+
+ cgiSetVariable("KEYWORD", option->keyword);
+ cgiSetVariable("KEYTEXT", option->text);
+
+ if (option->conflicted)
+ cgiSetVariable("CONFLICTED", "1");
+ else
+ cgiSetVariable("CONFLICTED", "0");
+
+ cgiSetSize("CHOICES", 0);
+ cgiSetSize("TEXT", 0);
+ for (k = 0, m = 0; k < option->num_choices; k ++)
+ {
+ cgiSetArray("CHOICES", m, option->choices[k].choice);
+ cgiSetArray("TEXT", m, option->choices[k].text);
+
+ m ++;
+
+ if (option->choices[k].marked)
+ cgiSetVariable("DEFCHOICE", option->choices[k].choice);
+ }
+
+ cgiSetSize("PARAMS", 0);
+ cgiSetSize("PARAMTEXT", 0);
+ cgiSetSize("PARAMVALUE", 0);
+ cgiSetSize("INPUTTYPE", 0);
+
+ if ((coption = ppdFindCustomOption(ppd, option->keyword)))
+ {
+ const char *units = NULL; /* Units value, if any */
+
+ cgiSetVariable("ISCUSTOM", "1");
+
+ for (cparam = ppdFirstCustomParam(coption), m = 0;
+ cparam;
+ cparam = ppdNextCustomParam(coption), m ++)
+ {
+ if (!_cups_strcasecmp(option->keyword, "PageSize") &&
+ _cups_strcasecmp(cparam->name, "Width") &&
+ _cups_strcasecmp(cparam->name, "Height"))
+ {
+ m --;
+ continue;
+ }
+
+ cgiSetArray("PARAMS", m, cparam->name);
+ cgiSetArray("PARAMTEXT", m, cparam->text);
+ cgiSetArray("INPUTTYPE", m, "text");
+
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_POINTS :
+ if (!_cups_strncasecmp(option->defchoice, "Custom.", 7))
+ {
+ units = option->defchoice + strlen(option->defchoice) - 2;
+
+ if (strcmp(units, "mm") && strcmp(units, "cm") &&
+ strcmp(units, "in") && strcmp(units, "ft"))
+ {
+ if (units[1] == 'm')
+ units ++;
+ else
+ units = "pt";
+ }
+ }
+ else
+ units = "pt";
+
+ if (!strcmp(units, "mm"))
+ snprintf(value, sizeof(value), "%g",
+ cparam->current.custom_points / 72.0 * 25.4);
+ else if (!strcmp(units, "cm"))
+ snprintf(value, sizeof(value), "%g",
+ cparam->current.custom_points / 72.0 * 2.54);
+ else if (!strcmp(units, "in"))
+ snprintf(value, sizeof(value), "%g",
+ cparam->current.custom_points / 72.0);
+ else if (!strcmp(units, "ft"))
+ snprintf(value, sizeof(value), "%g",
+ cparam->current.custom_points / 72.0 / 12.0);
+ else if (!strcmp(units, "m"))
+ snprintf(value, sizeof(value), "%g",
+ cparam->current.custom_points / 72.0 * 0.0254);
+ else
+ snprintf(value, sizeof(value), "%g",
+ cparam->current.custom_points);
+ cgiSetArray("PARAMVALUE", m, value);
+ break;
+
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_REAL :
+ snprintf(value, sizeof(value), "%g",
+ cparam->current.custom_real);
+ cgiSetArray("PARAMVALUE", m, value);
+ break;
+
+ case PPD_CUSTOM_INT:
+ snprintf(value, sizeof(value), "%d",
+ cparam->current.custom_int);
+ cgiSetArray("PARAMVALUE", m, value);
+ break;
+
+ case PPD_CUSTOM_PASSCODE:
+ case PPD_CUSTOM_PASSWORD:
+ if (cparam->current.custom_password)
+ cgiSetArray("PARAMVALUE", m,
+ cparam->current.custom_password);
+ else
+ cgiSetArray("PARAMVALUE", m, "");
+ cgiSetArray("INPUTTYPE", m, "password");
+ break;
+
+ case PPD_CUSTOM_STRING:
+ if (cparam->current.custom_string)
+ cgiSetArray("PARAMVALUE", m,
+ cparam->current.custom_string);
+ else
+ cgiSetArray("PARAMVALUE", m, "");
+ break;
+ }
+ }
+
+ if (units)
+ {
+ cgiSetArray("PARAMS", m, "Units");
+ cgiSetArray("PARAMTEXT", m, cgiText(_("Units")));
+ cgiSetArray("PARAMVALUE", m, units);
+ }
+ }
+ else
+ cgiSetVariable("ISCUSTOM", "0");
+
+ switch (option->ui)
+ {
+ case PPD_UI_BOOLEAN :
+ cgiCopyTemplateLang("option-boolean.tmpl");
+ break;
+ case PPD_UI_PICKONE :
+ cgiCopyTemplateLang("option-pickone.tmpl");
+ break;
+ case PPD_UI_PICKMANY :
+ cgiCopyTemplateLang("option-pickmany.tmpl");
+ break;
+ }
+ }
+
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+ }
+
+ if ((attr = ippFindAttribute(response, "job-sheets-supported",
+ IPP_TAG_ZERO)) != NULL)
+ {
+ /*
+ * Add the job sheets options...
+ */
+
+ cgiSetVariable("GROUP_ID", "CUPS_BANNERS");
+ cgiSetVariable("GROUP", cgiText(_("Banners")));
+ cgiCopyTemplateLang("option-header.tmpl");
+
+ cgiSetSize("CHOICES", attr->num_values);
+ cgiSetSize("TEXT", attr->num_values);
+ for (k = 0; k < attr->num_values; k ++)
+ {
+ cgiSetArray("CHOICES", k, attr->values[k].string.text);
+ cgiSetArray("TEXT", k, attr->values[k].string.text);
+ }
+
+ attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
+
+ cgiSetVariable("KEYWORD", "job_sheets_start");
+ cgiSetVariable("KEYTEXT",
+ /* TRANSLATORS: Banner/cover sheet before the print job. */
+ cgiText(_("Starting Banner")));
+ cgiSetVariable("DEFCHOICE", attr != NULL ?
+ attr->values[0].string.text : "");
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+
+ cgiSetVariable("KEYWORD", "job_sheets_end");
+ cgiSetVariable("KEYTEXT",
+ /* TRANSLATORS: Banner/cover sheet after the print job. */
+ cgiText(_("Ending Banner")));
+ cgiSetVariable("DEFCHOICE", attr != NULL && attr->num_values > 1 ?
+ attr->values[1].string.text : "");
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+
+ if (ippFindAttribute(response, "printer-error-policy-supported",
+ IPP_TAG_ZERO) ||
+ ippFindAttribute(response, "printer-op-policy-supported",
+ IPP_TAG_ZERO))
+ {
+ /*
+ * Add the error and operation policy options...
+ */
+
+ cgiSetVariable("GROUP_ID", "CUPS_POLICIES");
+ cgiSetVariable("GROUP", cgiText(_("Policies")));
+ cgiCopyTemplateLang("option-header.tmpl");
+
+ /*
+ * Error policy...
+ */
+
+ attr = ippFindAttribute(response, "printer-error-policy-supported",
+ IPP_TAG_ZERO);
+
+ if (attr)
+ {
+ cgiSetSize("CHOICES", attr->num_values);
+ cgiSetSize("TEXT", attr->num_values);
+ for (k = 0; k < attr->num_values; k ++)
+ {
+ cgiSetArray("CHOICES", k, attr->values[k].string.text);
+ cgiSetArray("TEXT", k, attr->values[k].string.text);
+ }
+
+ attr = ippFindAttribute(response, "printer-error-policy",
+ IPP_TAG_ZERO);
+
+ cgiSetVariable("KEYWORD", "printer_error_policy");
+ cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
+ cgiSetVariable("DEFCHOICE", attr == NULL ?
+ "" : attr->values[0].string.text);
+ }
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+
+ /*
+ * Operation policy...
+ */
+
+ attr = ippFindAttribute(response, "printer-op-policy-supported",
+ IPP_TAG_ZERO);
+
+ if (attr)
+ {
+ cgiSetSize("CHOICES", attr->num_values);
+ cgiSetSize("TEXT", attr->num_values);
+ for (k = 0; k < attr->num_values; k ++)
+ {
+ cgiSetArray("CHOICES", k, attr->values[k].string.text);
+ cgiSetArray("TEXT", k, attr->values[k].string.text);
+ }
+
+ attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
+
+ cgiSetVariable("KEYWORD", "printer_op_policy");
+ cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
+ cgiSetVariable("DEFCHOICE", attr == NULL ?
+ "" : attr->values[0].string.text);
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+ }
+
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+
+ /*
+ * Binary protocol support...
+ */
+
+ if ((attr = ippFindAttribute(response, "port-monitor-supported",
+ IPP_TAG_NAME)) != NULL && attr->num_values > 1)
+ {
+ cgiSetVariable("GROUP_ID", "CUPS_PORT_MONITOR");
+ cgiSetVariable("GROUP", cgiText(_("Port Monitor")));
+
+ cgiSetSize("CHOICES", attr->num_values);
+ cgiSetSize("TEXT", attr->num_values);
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ cgiSetArray("CHOICES", i, attr->values[i].string.text);
+ cgiSetArray("TEXT", i, attr->values[i].string.text);
+ }
+
+ attr = ippFindAttribute(response, "port-monitor", IPP_TAG_NAME);
+ cgiSetVariable("KEYWORD", "port_monitor");
+ cgiSetVariable("KEYTEXT", cgiText(_("Port Monitor")));
+ cgiSetVariable("DEFCHOICE", attr ? attr->values[0].string.text : "none");
+
+ cgiCopyTemplateLang("option-header.tmpl");
+ cgiCopyTemplateLang("option-pickone.tmpl");
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+
+ cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
+ cgiEndHTML();
+
+ ippDelete(response);
+ }
+ else
+ {
+ /*
+ * Set default options...
+ */
+
+ fputs("DEBUG: Setting options...\n", stderr);
+
+ if (filename)
+ {
+ out = cupsTempFile2(tempfile, sizeof(tempfile));
+ in = cupsFileOpen(filename, "r");
+
+ if (!in || !out)
+ {
+ cgiSetVariable("ERROR", strerror(errno));
+ cgiStartHTML(cgiText(_("Set Printer Options")));
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+
+ if (in)
+ cupsFileClose(in);
+
+ if (out)
+ {
+ cupsFileClose(out);
+ unlink(tempfile);
+ }
+
+ unlink(filename);
+ return;
+ }
+
+ while (cupsFileGets(in, line, sizeof(line)))
+ {
+ if (!strncmp(line, "*cupsProtocol:", 14))
+ continue;
+ else if (strncmp(line, "*Default", 8))
+ cupsFilePrintf(out, "%s\n", line);
+ else
+ {
+ /*
+ * Get default option name...
+ */
+
+ strlcpy(keyword, line + 8, sizeof(keyword));
+
+ for (keyptr = keyword; *keyptr; keyptr ++)
+ if (*keyptr == ':' || isspace(*keyptr & 255))
+ break;
+
+ *keyptr = '\0';
+
+ if (!strcmp(keyword, "PageRegion") ||
+ !strcmp(keyword, "PaperDimension") ||
+ !strcmp(keyword, "ImageableArea"))
+ var = get_option_value(ppd, "PageSize", value, sizeof(value));
+ else
+ var = get_option_value(ppd, keyword, value, sizeof(value));
+
+ if (!var)
+ cupsFilePrintf(out, "%s\n", line);
+ else
+ cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
+ }
+ }
+
+ cupsFileClose(in);
+ cupsFileClose(out);
+ }
+ else
+ {
+ /*
+ * Make sure temporary filename is cleared when there is no PPD...
+ */
+
+ tempfile[0] = '\0';
+ }
+
+ /*
+ * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * job-sheets-default
+ * printer-error-policy
+ * printer-op-policy
+ * [ppd file]