| /* | |
| * Administration CGI for CUPS. | |
| * | |
| * Copyright 2007-2017 by Apple Inc. | |
| * Copyright 1997-2007 by Easy Software Products. | |
| * | |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more information. | |
| */ | |
| /* | |
| * Include necessary headers... | |
| */ | |
| #include "cgi-private.h" | |
| #include <cups/http-private.h> | |
| #include <cups/ppd-private.h> | |
| #include <cups/adminutil.h> | |
| #include <cups/ppd.h> | |
| #include <errno.h> | |
| #include <unistd.h> | |
| #include <fcntl.h> | |
| #include <sys/wait.h> | |
| #include <limits.h> | |
| /* | |
| * Local globals... | |
| */ | |
| static int current_device = 0; /* Current device shown */ | |
| /* | |
| * Local functions... | |
| */ | |
| static void choose_device_cb(const char *device_class, const char *device_id, const char *device_info, const char *device_make_and_model, const char *device_uri, const char *device_location, const char *title); | |
| static void do_am_class(http_t *http, int modify); | |
| static void do_am_printer(http_t *http, int modify); | |
| static void do_config_server(http_t *http); | |
| static void do_delete_class(http_t *http); | |
| static void do_delete_printer(http_t *http); | |
| static void do_export(http_t *http); | |
| static void do_list_printers(http_t *http); | |
| static void do_menu(http_t *http); | |
| static void do_set_allowed_users(http_t *http); | |
| static void do_set_default(http_t *http); | |
| static void do_set_options(http_t *http, int is_class); | |
| static void do_set_sharing(http_t *http); | |
| static char *get_option_value(ppd_file_t *ppd, const char *name, | |
| char *buffer, size_t bufsize); | |
| static double get_points(double number, const char *uval); | |
| static char *get_printer_ppd(const char *uri, char *buffer, size_t bufsize); | |
| /* | |
| * 'main()' - Main entry for CGI. | |
| */ | |
| int /* O - Exit status */ | |
| main(void) | |
| { | |
| http_t *http; /* Connection to the server */ | |
| const char *op; /* Operation name */ | |
| /* | |
| * Connect to the HTTP server... | |
| */ | |
| fputs("DEBUG: admin.cgi started...\n", stderr); | |
| http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption()); | |
| if (!http) | |
| { | |
| perror("ERROR: Unable to connect to cupsd"); | |
| fprintf(stderr, "DEBUG: cupsServer()=\"%s\"\n", | |
| cupsServer() ? cupsServer() : "(null)"); | |
| fprintf(stderr, "DEBUG: ippPort()=%d\n", ippPort()); | |
| fprintf(stderr, "DEBUG: cupsEncryption()=%d\n", cupsEncryption()); | |
| exit(1); | |
| } | |
| fprintf(stderr, "DEBUG: http=%p\n", http); | |
| /* | |
| * Set the web interface section... | |
| */ | |
| cgiSetVariable("SECTION", "admin"); | |
| cgiSetVariable("REFRESH_PAGE", ""); | |
| /* | |
| * See if we have form data... | |
| */ | |
| if (!cgiInitialize() || !cgiGetVariable("OP")) | |
| { | |
| /* | |
| * Nope, send the administration menu... | |
| */ | |
| fputs("DEBUG: No form data, showing main menu...\n", stderr); | |
| do_menu(http); | |
| } | |
| else if ((op = cgiGetVariable("OP")) != NULL && cgiIsPOST()) | |
| { | |
| /* | |
| * Do the operation... | |
| */ | |
| fprintf(stderr, "DEBUG: op=\"%s\"...\n", op); | |
| if (!*op) | |
| { | |
| const char *printer = getenv("PRINTER_NAME"), | |
| /* Printer or class name */ | |
| *server_port = getenv("SERVER_PORT"); | |
| /* Port number string */ | |
| int port = atoi(server_port ? server_port : "0"); | |
| /* Port number */ | |
| char uri[1024]; /* URL */ | |
| if (printer) | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), | |
| getenv("HTTPS") ? "https" : "http", NULL, | |
| getenv("SERVER_NAME"), port, "/%s/%s", | |
| cgiGetVariable("IS_CLASS") ? "classes" : "printers", | |
| printer); | |
| else | |
| httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), | |
| getenv("HTTPS") ? "https" : "http", NULL, | |
| getenv("SERVER_NAME"), port, "/admin"); | |
| printf("Location: %s\n\n", uri); | |
| } | |
| else if (!strcmp(op, "set-allowed-users")) | |
| do_set_allowed_users(http); | |
| else if (!strcmp(op, "set-as-default")) | |
| do_set_default(http); | |
| else if (!strcmp(op, "set-sharing")) | |
| do_set_sharing(http); | |
| else if (!strcmp(op, "find-new-printers") || | |
| !strcmp(op, "list-available-printers")) | |
| do_list_printers(http); | |
| else if (!strcmp(op, "add-class")) | |
| do_am_class(http, 0); | |
| else if (!strcmp(op, "add-printer")) | |
| do_am_printer(http, 0); | |
| else if (!strcmp(op, "modify-class")) | |
| do_am_class(http, 1); | |
| else if (!strcmp(op, "modify-printer")) | |
| do_am_printer(http, 1); | |
| else if (!strcmp(op, "delete-class")) | |
| do_delete_class(http); | |
| else if (!strcmp(op, "delete-printer")) | |
| do_delete_printer(http); | |
| else if (!strcmp(op, "set-class-options")) | |
| do_set_options(http, 1); | |
| else if (!strcmp(op, "set-printer-options")) | |
| do_set_options(http, 0); | |
| else if (!strcmp(op, "config-server")) | |
| do_config_server(http); | |
| else if (!strcmp(op, "export-samba")) | |
| do_export(http); | |
| else | |
| { | |
| /* | |
| * Bad operation code - display an error... | |
| */ | |
| cgiStartHTML(cgiText(_("Administration"))); | |
| cgiCopyTemplateLang("error-op.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| } | |
| else if (op && !strcmp(op, "redirect")) | |
| { | |
| const char *url; /* Redirection URL... */ | |
| char prefix[1024]; /* URL prefix */ | |
| if (getenv("HTTPS")) | |
| snprintf(prefix, sizeof(prefix), "https://%s:%s", | |
| getenv("SERVER_NAME"), getenv("SERVER_PORT")); | |
| else | |
| snprintf(prefix, sizeof(prefix), "http://%s:%s", | |
| getenv("SERVER_NAME"), getenv("SERVER_PORT")); | |
| fprintf(stderr, "DEBUG: redirecting with prefix %s!\n", prefix); | |
| if ((url = cgiGetVariable("URL")) != NULL) | |
| { | |
| char encoded[1024], /* Encoded URL string */ | |
| *ptr; /* Pointer into encoded string */ | |
| ptr = encoded; | |
| if (*url != '/') | |
| *ptr++ = '/'; | |
| for (; *url && ptr < (encoded + sizeof(encoded) - 4); url ++) | |
| { | |
| if (strchr("%@&+ <>#=", *url) || *url < ' ' || *url & 128) | |
| { | |
| /* | |
| * Percent-encode this character; safe because we have at least 4 | |
| * bytes left in the array... | |
| */ | |
| sprintf(ptr, "%%%02X", *url & 255); | |
| ptr += 3; | |
| } | |
| else | |
| *ptr++ = *url; | |
| } | |
| *ptr = '\0'; | |
| if (*url) | |
| { | |
| /* | |
| * URL was too long, just redirect to the admin page... | |
| */ | |
| printf("Location: %s/admin\n\n", prefix); | |
| } | |
| else | |
| { | |
| /* | |
| * URL is OK, redirect there... | |
| */ | |
| printf("Location: %s%s\n\n", prefix, encoded); | |
| } | |
| } | |
| else | |
| printf("Location: %s/admin\n\n", prefix); | |
| } | |
| else | |
| { | |
| /* | |
| * Form data but no operation code - display an error... | |
| */ | |
| cgiStartHTML(cgiText(_("Administration"))); | |
| cgiCopyTemplateLang("error-op.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * Close the HTTP server connection... | |
| */ | |
| httpClose(http); | |
| /* | |
| * Return with no errors... | |
| */ | |
| return (0); | |
| } | |
| /* | |
| * 'choose_device_cb()' - Add a device to the device selection page. | |
| */ | |
| static void | |
| choose_device_cb( | |
| const char *device_class, /* I - Class */ | |
| const char *device_id, /* I - 1284 device ID */ | |
| const char *device_info, /* I - Description */ | |
| const char *device_make_and_model, /* I - Make and model */ | |
| const char *device_uri, /* I - Device URI */ | |
| const char *device_location, /* I - Location */ | |
| const char *title) /* I - Page title */ | |
| { | |
| /* | |
| * For modern browsers, start a multi-part page so we can show that something | |
| * is happening. Non-modern browsers just get everything at the end... | |
| */ | |
| if (current_device == 0 && cgiSupportsMultipart()) | |
| { | |
| cgiStartMultipart(); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("choose-device.tmpl"); | |
| cgiEndHTML(); | |
| fflush(stdout); | |
| } | |
| /* | |
| * Add the device to the array... | |
| */ | |
| cgiSetArray("device_class", current_device, device_class); | |
| cgiSetArray("device_id", current_device, device_id); | |
| cgiSetArray("device_info", current_device, device_info); | |
| cgiSetArray("device_make_and_model", current_device, device_make_and_model); | |
| cgiSetArray("device_uri", current_device, device_uri); | |
| cgiSetArray("device_location", current_device, device_location); | |
| current_device ++; | |
| } | |
| /* | |
| * 'do_am_class()' - Add or modify a class. | |
| */ | |
| static void | |
| do_am_class(http_t *http, /* I - HTTP connection */ | |
| int modify) /* I - Modify the printer? */ | |
| { | |
| int i, j; /* Looping vars */ | |
| int element; /* Element number */ | |
| int num_printers; /* Number of printers */ | |
| ipp_t *request, /* IPP request */ | |
| *response; /* IPP response */ | |
| ipp_attribute_t *attr; /* member-uris attribute */ | |
| char uri[HTTP_MAX_URI]; /* Device or printer URI */ | |
| const char *name, /* Pointer to class name */ | |
| *op, /* Operation name */ | |
| *ptr; /* Pointer to CGI variable */ | |
| const char *title; /* Title of page */ | |
| static const char * const pattrs[] = /* Requested printer attributes */ | |
| { | |
| "member-names", | |
| "printer-info", | |
| "printer-location" | |
| }; | |
| title = cgiText(modify ? _("Modify Class") : _("Add Class")); | |
| op = cgiGetVariable("OP"); | |
| name = cgiGetVariable("PRINTER_NAME"); | |
| if (cgiGetVariable("PRINTER_LOCATION") == NULL) | |
| { | |
| /* | |
| * Build a CUPS_GET_PRINTERS request, which requires the | |
| * following attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| */ | |
| request = ippNewRequest(CUPS_GET_PRINTERS); | |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type", | |
| CUPS_PRINTER_LOCAL); | |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask", | |
| CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| cgiClearVariables(); | |
| if (op) | |
| cgiSetVariable("OP", op); | |
| if (name) | |
| cgiSetVariable("PRINTER_NAME", name); | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| /* | |
| * Create MEMBER_URIS and MEMBER_NAMES arrays... | |
| */ | |
| for (element = 0, attr = response->attrs; | |
| attr != NULL; | |
| attr = attr->next) | |
| if (attr->name && !strcmp(attr->name, "printer-uri-supported")) | |
| { | |
| if ((ptr = strrchr(attr->values[0].string.text, '/')) != NULL && | |
| (!name || _cups_strcasecmp(name, ptr + 1))) | |
| { | |
| /* | |
| * Don't show the current class... | |
| */ | |
| cgiSetArray("MEMBER_URIS", element, attr->values[0].string.text); | |
| element ++; | |
| } | |
| } | |
| for (element = 0, attr = response->attrs; | |
| attr != NULL; | |
| attr = attr->next) | |
| if (attr->name && !strcmp(attr->name, "printer-name")) | |
| { | |
| if (!name || _cups_strcasecmp(name, attr->values[0].string.text)) | |
| { | |
| /* | |
| * Don't show the current class... | |
| */ | |
| cgiSetArray("MEMBER_NAMES", element, attr->values[0].string.text); | |
| element ++; | |
| } | |
| } | |
| num_printers = cgiGetSize("MEMBER_URIS"); | |
| ippDelete(response); | |
| } | |
| else | |
| num_printers = 0; | |
| if (modify) | |
| { | |
| /* | |
| * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the | |
| * following attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| */ | |
| request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, "/classes/%s", name); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
| "requested-attributes", | |
| (int)(sizeof(pattrs) / sizeof(pattrs[0])), | |
| NULL, pattrs); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| if ((attr = ippFindAttribute(response, "member-names", | |
| IPP_TAG_NAME)) != NULL) | |
| { | |
| /* | |
| * Mark any current members in the class... | |
| */ | |
| for (j = 0; j < num_printers; j ++) | |
| cgiSetArray("MEMBER_SELECTED", j, ""); | |
| for (i = 0; i < attr->num_values; i ++) | |
| { | |
| for (j = 0; j < num_printers; j ++) | |
| { | |
| if (!_cups_strcasecmp(attr->values[i].string.text, | |
| cgiGetArray("MEMBER_NAMES", j))) | |
| { | |
| cgiSetArray("MEMBER_SELECTED", j, "SELECTED"); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if ((attr = ippFindAttribute(response, "printer-info", | |
| IPP_TAG_TEXT)) != NULL) | |
| cgiSetVariable("PRINTER_INFO", attr->values[0].string.text); | |
| if ((attr = ippFindAttribute(response, "printer-location", | |
| IPP_TAG_TEXT)) != NULL) | |
| cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text); | |
| ippDelete(response); | |
| } | |
| /* | |
| * Update the location and description of an existing printer... | |
| */ | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("modify-class.tmpl"); | |
| } | |
| else | |
| { | |
| /* | |
| * Get the name, location, and description for a new printer... | |
| */ | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("add-class.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| return; | |
| } | |
| if (!name) | |
| { | |
| cgiStartHTML(title); | |
| cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| for (ptr = name; *ptr; ptr ++) | |
| if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#') | |
| break; | |
| if (*ptr || ptr == name || strlen(name) > 127) | |
| { | |
| cgiSetVariable("ERROR", | |
| cgiText(_("The class name may only contain up to " | |
| "127 printable characters and may not " | |
| "contain spaces, slashes (/), or the " | |
| "pound sign (#)."))); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| /* | |
| * Build a CUPS_ADD_CLASS request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| * printer-location | |
| * printer-info | |
| * printer-is-accepting-jobs | |
| * printer-state | |
| * member-uris | |
| */ | |
| request = ippNewRequest(CUPS_ADD_CLASS); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, "/classes/%s", name); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", | |
| NULL, cgiGetVariable("PRINTER_LOCATION")); | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", | |
| NULL, cgiGetVariable("PRINTER_INFO")); | |
| ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1); | |
| ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", | |
| IPP_PRINTER_IDLE); | |
| if ((num_printers = cgiGetSize("MEMBER_URIS")) > 0) | |
| { | |
| attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris", | |
| num_printers, NULL, NULL); | |
| for (i = 0; i < num_printers; i ++) | |
| attr->values[i].string.text = _cupsStrAlloc(cgiGetArray("MEMBER_URIS", i)); | |
| } | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| ippDelete(cupsDoRequest(http, request, "/admin/")); | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() > IPP_OK_CONFLICT) | |
| { | |
| cgiStartHTML(title); | |
| cgiShowIPPError(modify ? _("Unable to modify class") : | |
| _("Unable to add class")); | |
| } | |
| else | |
| { | |
| /* | |
| * Redirect successful updates back to the class page... | |
| */ | |
| char refresh[1024]; /* Refresh URL */ | |
| cgiFormEncode(uri, name, sizeof(uri)); | |
| snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/classes/%s", | |
| uri); | |
| cgiSetVariable("refresh_page", refresh); | |
| cgiStartHTML(title); | |
| if (modify) | |
| cgiCopyTemplateLang("class-modified.tmpl"); | |
| else | |
| cgiCopyTemplateLang("class-added.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'do_am_printer()' - Add or modify a printer. | |
| */ | |
| static void | |
| do_am_printer(http_t *http, /* I - HTTP connection */ | |
| int modify) /* I - Modify the printer? */ | |
| { | |
| int i; /* Looping var */ | |
| ipp_attribute_t *attr; /* Current attribute */ | |
| ipp_t *request, /* IPP request */ | |
| *response, /* IPP response */ | |
| *oldinfo; /* Old printer information */ | |
| const cgi_file_t *file; /* Uploaded file, if any */ | |
| const char *var; /* CGI variable */ | |
| char uri[HTTP_MAX_URI], /* Device or printer URI */ | |
| *uriptr, /* Pointer into URI */ | |
| evefile[1024] = ""; /* IPP Everywhere PPD file */ | |
| int maxrate; /* Maximum baud rate */ | |
| char baudrate[255]; /* Baud rate string */ | |
| const char *name, /* Pointer to class name */ | |
| *ptr; /* Pointer to CGI variable */ | |
| const char *title; /* Title of page */ | |
| static int baudrates[] = /* Baud rates */ | |
| { | |
| 1200, | |
| 2400, | |
| 4800, | |
| 9600, | |
| 19200, | |
| 38400, | |
| 57600, | |
| 115200, | |
| 230400, | |
| 460800 | |
| }; | |
| ptr = cgiGetVariable("DEVICE_URI"); | |
| fprintf(stderr, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n", | |
| ptr ? ptr : "(null)"); | |
| title = cgiText(modify ? _("Modify Printer") : _("Add Printer")); | |
| if (modify) | |
| { | |
| /* | |
| * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the | |
| * following attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| */ | |
| request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, "/printers/%s", | |
| cgiGetVariable("PRINTER_NAME")); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| oldinfo = cupsDoRequest(http, request, "/"); | |
| } | |
| else | |
| oldinfo = NULL; | |
| file = cgiGetFile(); | |
| if (file) | |
| { | |
| fprintf(stderr, "DEBUG: file->tempfile=%s\n", file->tempfile); | |
| fprintf(stderr, "DEBUG: file->name=%s\n", file->name); | |
| fprintf(stderr, "DEBUG: file->filename=%s\n", file->filename); | |
| fprintf(stderr, "DEBUG: file->mimetype=%s\n", file->mimetype); | |
| } | |
| if ((name = cgiGetVariable("PRINTER_NAME")) != NULL) | |
| { | |
| for (ptr = name; *ptr; ptr ++) | |
| if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '\\' || *ptr == '?' || *ptr == '\'' || *ptr == '\"' || *ptr == '#') | |
| break; | |
| if (*ptr || ptr == name || strlen(name) > 127) | |
| { | |
| cgiSetVariable("ERROR", | |
| cgiText(_("The printer name may only contain up to 127 printable characters and may not contain spaces, slashes (/ \\), quotes (' \"), question mark (?), or the pound sign (#)."))); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| } | |
| if ((var = cgiGetVariable("DEVICE_URI")) != NULL) | |
| { | |
| if ((uriptr = strrchr(var, '|')) != NULL) | |
| { | |
| /* | |
| * Extract make and make/model from device URI string... | |
| */ | |
| char make[1024], /* Make string */ | |
| *makeptr; /* Pointer into make */ | |
| *uriptr++ = '\0'; | |
| strlcpy(make, uriptr, sizeof(make)); | |
| if ((makeptr = strchr(make, ' ')) != NULL) | |
| *makeptr = '\0'; | |
| else if ((makeptr = strchr(make, '-')) != NULL) | |
| *makeptr = '\0'; | |
| else if (!_cups_strncasecmp(make, "laserjet", 8) || | |
| !_cups_strncasecmp(make, "deskjet", 7) || | |
| !_cups_strncasecmp(make, "designjet", 9)) | |
| strlcpy(make, "HP", sizeof(make)); | |
| else if (!_cups_strncasecmp(make, "phaser", 6)) | |
| strlcpy(make, "Xerox", sizeof(make)); | |
| else if (!_cups_strncasecmp(make, "stylus", 6)) | |
| strlcpy(make, "Epson", sizeof(make)); | |
| else | |
| strlcpy(make, "Generic", sizeof(make)); | |
| if (!cgiGetVariable("CURRENT_MAKE")) | |
| cgiSetVariable("CURRENT_MAKE", make); | |
| if (!cgiGetVariable("CURRENT_MAKE_AND_MODEL")) | |
| cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr); | |
| if (!modify) | |
| { | |
| char template[128], /* Template name */ | |
| *tptr; /* Pointer into template name */ | |
| cgiSetVariable("PRINTER_INFO", uriptr); | |
| for (tptr = template; | |
| tptr < (template + sizeof(template) - 1) && *uriptr; | |
| uriptr ++) | |
| if (isalnum(*uriptr & 255) || *uriptr == '_' || *uriptr == '-' || | |
| *uriptr == '.') | |
| *tptr++ = *uriptr; | |
| else if ((*uriptr == ' ' || *uriptr == '/') && tptr > template && | |
| tptr[-1] != '_') | |
| *tptr++ = '_'; | |
| else if (*uriptr == '?' || *uriptr == '(') | |
| break; | |
| *tptr = '\0'; | |
| cgiSetVariable("TEMPLATE_NAME", template); | |
| } | |
| } | |
| } | |
| if (!var) | |
| { | |
| /* | |
| * Look for devices so the user can pick something... | |
| */ | |
| if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL) | |
| { | |
| strlcpy(uri, attr->values[0].string.text, sizeof(uri)); | |
| if ((uriptr = strchr(uri, ':')) != NULL && strncmp(uriptr, "://", 3) == 0) | |
| *uriptr = '\0'; | |
| cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text); | |
| cgiSetVariable("CURRENT_DEVICE_SCHEME", uri); | |
| } | |
| /* | |
| * Scan for devices for up to 30 seconds... | |
| */ | |
| fputs("DEBUG: Getting list of devices...\n", stderr); | |
| current_device = 0; | |
| if (cupsGetDevices(http, 5, CUPS_INCLUDE_ALL, CUPS_EXCLUDE_NONE, | |
| (cups_device_cb_t)choose_device_cb, | |
| (void *)title) == IPP_OK) | |
| { | |
| fputs("DEBUG: Got device list!\n", stderr); | |
| if (cgiSupportsMultipart()) | |
| cgiStartMultipart(); | |
| cgiSetVariable("CUPS_GET_DEVICES_DONE", "1"); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("choose-device.tmpl"); | |
| cgiEndHTML(); | |
| if (cgiSupportsMultipart()) | |
| cgiEndMultipart(); | |
| } | |
| else | |
| { | |
| fprintf(stderr, | |
| "ERROR: CUPS-Get-Devices request failed with status %x: %s\n", | |
| cupsLastError(), cupsLastErrorString()); | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else | |
| { | |
| cgiStartHTML(title); | |
| cgiShowIPPError(modify ? _("Unable to modify printer") : | |
| _("Unable to add printer")); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| } | |
| } | |
| else if (!strchr(var, '/') || | |
| (!strncmp(var, "lpd://", 6) && !strchr(var + 6, '/'))) | |
| { | |
| if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL) | |
| { | |
| /* | |
| * Set the current device URI for the form to the old one... | |
| */ | |
| if (strncmp(attr->values[0].string.text, var, strlen(var)) == 0) | |
| cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text); | |
| } | |
| /* | |
| * User needs to set the full URI... | |
| */ | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("choose-uri.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| else if (!strncmp(var, "serial:", 7) && !cgiGetVariable("BAUDRATE")) | |
| { | |
| /* | |
| * Need baud rate, parity, etc. | |
| */ | |
| if ((var = strchr(var, '?')) != NULL && | |
| strncmp(var, "?baud=", 6) == 0) | |
| maxrate = atoi(var + 6); | |
| else | |
| maxrate = 19200; | |
| for (i = 0; i < 10; i ++) | |
| if (baudrates[i] > maxrate) | |
| break; | |
| else | |
| { | |
| sprintf(baudrate, "%d", baudrates[i]); | |
| cgiSetArray("BAUDRATES", i, baudrate); | |
| } | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("choose-serial.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| else if (!name || !cgiGetVariable("PRINTER_LOCATION")) | |
| { | |
| cgiStartHTML(title); | |
| if (modify) | |
| { | |
| /* | |
| * Update the location and description of an existing printer... | |
| */ | |
| if (oldinfo) | |
| { | |
| if ((attr = ippFindAttribute(oldinfo, "printer-info", | |
| IPP_TAG_TEXT)) != NULL) | |
| cgiSetVariable("PRINTER_INFO", attr->values[0].string.text); | |
| if ((attr = ippFindAttribute(oldinfo, "printer-location", | |
| IPP_TAG_TEXT)) != NULL) | |
| cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text); | |
| if ((attr = ippFindAttribute(oldinfo, "printer-is-shared", | |
| IPP_TAG_BOOLEAN)) != NULL) | |
| cgiSetVariable("PRINTER_IS_SHARED", | |
| attr->values[0].boolean ? "1" : "0"); | |
| } | |
| cgiCopyTemplateLang("modify-printer.tmpl"); | |
| } | |
| else | |
| { | |
| /* | |
| * Get the name, location, and description for a new printer... | |
| */ | |
| #ifdef __APPLE__ | |
| if (!strncmp(var, "usb:", 4)) | |
| cgiSetVariable("printer_is_shared", "1"); | |
| else | |
| #endif /* __APPLE__ */ | |
| cgiSetVariable("printer_is_shared", "0"); | |
| cgiCopyTemplateLang("add-printer.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| if (oldinfo) | |
| ippDelete(oldinfo); | |
| return; | |
| } | |
| else if (!file && | |
| (!cgiGetVariable("PPD_NAME") || cgiGetVariable("SELECT_MAKE"))) | |
| { | |
| if (modify && !cgiGetVariable("SELECT_MAKE")) | |
| { | |
| /* | |
| * Get the PPD file... | |
| */ | |
| int fd; /* PPD file */ | |
| char filename[1024]; /* PPD filename */ | |
| ppd_file_t *ppd; /* PPD information */ | |
| char buffer[1024]; /* Buffer */ | |
| ssize_t bytes; /* Number of bytes */ | |
| http_status_t get_status; /* Status of GET */ | |
| /* TODO: Use cupsGetFile() API... */ | |
| snprintf(uri, sizeof(uri), "/printers/%s.ppd", name); | |
| if (httpGet(http, uri)) | |
| httpGet(http, uri); | |
| while ((get_status = httpUpdate(http)) == HTTP_CONTINUE); | |
| if (get_status != HTTP_OK) | |
| { | |
| httpFlush(http); | |
| fprintf(stderr, "ERROR: Unable to get PPD file %s: %d - %s\n", | |
| uri, get_status, httpStatus(get_status)); | |
| } | |
| else if ((fd = cupsTempFd(filename, sizeof(filename))) >= 0) | |
| { | |
| while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) | |
| write(fd, buffer, (size_t)bytes); | |
| close(fd); | |
| if ((ppd = ppdOpenFile(filename)) != NULL) | |
| { | |
| if (ppd->manufacturer) | |
| cgiSetVariable("CURRENT_MAKE", ppd->manufacturer); | |
| if (ppd->nickname) | |
| cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd->nickname); | |
| ppdClose(ppd); | |
| unlink(filename); | |
| } | |
| else | |
| { | |
| int linenum; /* Line number */ | |
| fprintf(stderr, "ERROR: Unable to open PPD file %s: %s\n", | |
| filename, ppdErrorString(ppdLastError(&linenum))); | |
| } | |
| } | |
| else | |
| { | |
| httpFlush(http); | |
| fprintf(stderr, | |
| "ERROR: Unable to create temporary file for PPD file: %s\n", | |
| strerror(errno)); | |
| } | |
| } | |
| /* | |
| * Build a CUPS_GET_PPDS request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| */ | |
| request = ippNewRequest(CUPS_GET_PPDS); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, "ipp://localhost/printers/"); | |
| if ((var = cgiGetVariable("PPD_MAKE")) == NULL) | |
| var = cgiGetVariable("CURRENT_MAKE"); | |
| if (var && !cgiGetVariable("SELECT_MAKE")) | |
| { | |
| const char *make_model; /* Make and model */ | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, | |
| "ppd-make", NULL, var); | |
| if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL) | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, | |
| "ppd-make-and-model", NULL, make_model); | |
| } | |
| else | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
| "requested-attributes", NULL, "ppd-make"); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| /* | |
| * Got the list of PPDs, see if the user has selected a make... | |
| */ | |
| if (cgiSetIPPVars(response, NULL, NULL, NULL, 0) == 0 && !modify) | |
| { | |
| /* | |
| * No PPD files with this make, try again with all makes... | |
| */ | |
| ippDelete(response); | |
| request = ippNewRequest(CUPS_GET_PPDS); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, "ipp://localhost/printers/"); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
| "requested-attributes", NULL, "ppd-make"); | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| cgiSetIPPVars(response, NULL, NULL, NULL, 0); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("choose-make.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| else if (!var || cgiGetVariable("SELECT_MAKE")) | |
| { | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("choose-make.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| else | |
| { | |
| /* | |
| * Let the user choose a model... | |
| */ | |
| cgiStartHTML(title); | |
| if (!cgiGetVariable("PPD_MAKE")) | |
| cgiSetVariable("PPD_MAKE", cgiGetVariable("CURRENT_MAKE")); | |
| if (!modify) | |
| cgiSetVariable("CURRENT_MAKE_AND_MODEL", | |
| cgiGetArray("PPD_MAKE_AND_MODEL", 0)); | |
| cgiCopyTemplateLang("choose-model.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| ippDelete(response); | |
| } | |
| else | |
| { | |
| cgiStartHTML(title); | |
| cgiShowIPPError(_("Unable to get list of printer drivers")); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| } | |
| else | |
| { | |
| /* | |
| * Build a CUPS_ADD_PRINTER request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| * printer-location | |
| * printer-info | |
| * ppd-name | |
| * device-uri | |
| * printer-is-accepting-jobs | |
| * printer-is-shared | |
| * printer-state | |
| */ | |
| request = ippNewRequest(CUPS_ADD_PRINTER); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, "/printers/%s", | |
| cgiGetVariable("PRINTER_NAME")); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| if (!file) | |
| { | |
| var = cgiGetVariable("PPD_NAME"); | |
| if (!strcmp(var, "everywhere")) | |
| get_printer_ppd(cgiGetVariable("DEVICE_URI"), evefile, sizeof(evefile)); | |
| else if (strcmp(var, "__no_change__")) | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", | |
| NULL, var); | |
| } | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", | |
| NULL, cgiGetVariable("PRINTER_LOCATION")); | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", | |
| NULL, cgiGetVariable("PRINTER_INFO")); | |
| strlcpy(uri, cgiGetVariable("DEVICE_URI"), sizeof(uri)); | |
| /* | |
| * Strip make and model from URI... | |
| */ | |
| if ((uriptr = strrchr(uri, '|')) != NULL) | |
| *uriptr = '\0'; | |
| if (!strncmp(uri, "serial:", 7)) | |
| { | |
| /* | |
| * Update serial port URI to include baud rate, etc. | |
| */ | |
| if ((uriptr = strchr(uri, '?')) == NULL) | |
| uriptr = uri + strlen(uri); | |
| snprintf(uriptr, sizeof(uri) - (size_t)(uriptr - uri), | |
| "?baud=%s+bits=%s+parity=%s+flow=%s", | |
| cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"), | |
| cgiGetVariable("PARITY"), cgiGetVariable("FLOW")); | |
| } | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", | |
| NULL, uri); | |
| ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1); | |
| var = cgiGetVariable("printer_is_shared"); | |
| ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-shared", | |
| var && (!strcmp(var, "1") || !strcmp(var, "on"))); | |
| ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", | |
| IPP_PRINTER_IDLE); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| if (file) | |
| ippDelete(cupsDoFileRequest(http, request, "/admin/", file->tempfile)); | |
| else if (evefile[0]) | |
| { | |
| ippDelete(cupsDoFileRequest(http, request, "/admin/", evefile)); | |
| unlink(evefile); | |
| } | |
| else | |
| ippDelete(cupsDoRequest(http, request, "/admin/")); | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() > IPP_OK_CONFLICT) | |
| { | |
| cgiStartHTML(title); | |
| cgiShowIPPError(modify ? _("Unable to modify printer") : | |
| _("Unable to add printer")); | |
| } | |
| else if (modify) | |
| { | |
| /* | |
| * Redirect successful updates back to the printer page... | |
| */ | |
| char refresh[1024]; /* Refresh URL */ | |
| cgiFormEncode(uri, name, sizeof(uri)); | |
| snprintf(refresh, sizeof(refresh), | |
| "5;/admin/?OP=redirect&URL=/printers/%s", uri); | |
| cgiSetVariable("refresh_page", refresh); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("printer-modified.tmpl"); | |
| } | |
| else | |
| { | |
| /* | |
| * Set the printer options... | |
| */ | |
| cgiSetVariable("OP", "set-printer-options"); | |
| do_set_options(http, 0); | |
| return; | |
| } | |
| cgiEndHTML(); | |
| } | |
| if (oldinfo) | |
| ippDelete(oldinfo); | |
| } | |
| /* | |
| * 'do_config_server()' - Configure server settings. | |
| */ | |
| static void | |
| do_config_server(http_t *http) /* I - HTTP connection */ | |
| { | |
| if (cgiGetVariable("CHANGESETTINGS")) | |
| { | |
| /* | |
| * Save basic setting changes... | |
| */ | |
| int num_settings; /* Number of server settings */ | |
| cups_option_t *settings; /* Server settings */ | |
| int advanced, /* Advanced settings shown? */ | |
| changed; /* Have settings changed? */ | |
| const char *debug_logging, /* DEBUG_LOGGING value */ | |
| *preserve_jobs = NULL, | |
| /* PRESERVE_JOBS value */ | |
| *remote_admin, /* REMOTE_ADMIN value */ | |
| *remote_any, /* REMOTE_ANY value */ | |
| *share_printers,/* SHARE_PRINTERS value */ | |
| *user_cancel_any, | |
| /* USER_CANCEL_ANY value */ | |
| *browse_web_if = NULL, | |
| /* BrowseWebIF value */ | |
| *preserve_job_history = NULL, | |
| /* PreserveJobHistory value */ | |
| *preserve_job_files = NULL, | |
| /* PreserveJobFiles value */ | |
| *max_clients = NULL, | |
| /* MaxClients value */ | |
| *max_jobs = NULL, | |
| /* MaxJobs value */ | |
| *max_log_size = NULL; | |
| /* MaxLogSize value */ | |
| const char *current_browse_web_if, | |
| /* BrowseWebIF value */ | |
| *current_preserve_job_history, | |
| /* PreserveJobHistory value */ | |
| *current_preserve_job_files, | |
| /* PreserveJobFiles value */ | |
| *current_max_clients, | |
| /* MaxClients value */ | |
| *current_max_jobs, | |
| /* MaxJobs value */ | |
| *current_max_log_size; | |
| /* MaxLogSize value */ | |
| #ifdef HAVE_GSSAPI | |
| char default_auth_type[255]; | |
| /* DefaultAuthType value */ | |
| const char *val; /* Setting value */ | |
| #endif /* HAVE_GSSAPI */ | |
| /* | |
| * Get the checkbox values from the form... | |
| */ | |
| debug_logging = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0"; | |
| remote_admin = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0"; | |
| remote_any = cgiGetVariable("REMOTE_ANY") ? "1" : "0"; | |
| share_printers = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0"; | |
| user_cancel_any = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0"; | |
| advanced = cgiGetVariable("ADVANCEDSETTINGS") != NULL; | |
| if (advanced) | |
| { | |
| /* | |
| * Get advanced settings... | |
| */ | |
| browse_web_if = cgiGetVariable("BROWSE_WEB_IF") ? "Yes" : "No"; | |
| max_clients = cgiGetVariable("MAX_CLIENTS"); | |
| max_log_size = cgiGetVariable("MAX_LOG_SIZE"); | |
| preserve_jobs = cgiGetVariable("PRESERVE_JOBS"); | |
| if (preserve_jobs) | |
| { | |
| max_jobs = cgiGetVariable("MAX_JOBS"); | |
| preserve_job_history = cgiGetVariable("PRESERVE_JOB_HISTORY"); | |
| preserve_job_files = cgiGetVariable("PRESERVE_JOB_FILES"); | |
| if (!max_jobs || atoi(max_jobs) < 0) | |
| max_jobs = "500"; | |
| if (!preserve_job_history) | |
| preserve_job_history = "On"; | |
| if (!preserve_job_files) | |
| preserve_job_files = "1d"; | |
| } | |
| else | |
| { | |
| max_jobs = "0"; | |
| preserve_job_history = "No"; | |
| preserve_job_files = "No"; | |
| } | |
| if (!max_clients || atoi(max_clients) <= 0) | |
| max_clients = "100"; | |
| if (!max_log_size || atoi(max_log_size) <= 0.0) | |
| max_log_size = "1m"; | |
| } | |
| /* | |
| * Get the current server settings... | |
| */ | |
| if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) | |
| { | |
| cgiStartHTML(cgiText(_("Change Settings"))); | |
| cgiSetVariable("MESSAGE", | |
| cgiText(_("Unable to change server settings"))); | |
| cgiSetVariable("ERROR", cupsLastErrorString()); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| #ifdef HAVE_GSSAPI | |
| /* | |
| * Get authentication settings... | |
| */ | |
| if (cgiGetVariable("KERBEROS")) | |
| strlcpy(default_auth_type, "Negotiate", sizeof(default_auth_type)); | |
| else | |
| { | |
| val = cupsGetOption("DefaultAuthType", num_settings, settings); | |
| if (!val || !_cups_strcasecmp(val, "Negotiate")) | |
| strlcpy(default_auth_type, "Basic", sizeof(default_auth_type)); | |
| else | |
| strlcpy(default_auth_type, val, sizeof(default_auth_type)); | |
| } | |
| fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type); | |
| #endif /* HAVE_GSSAPI */ | |
| if ((current_browse_web_if = cupsGetOption("BrowseWebIF", num_settings, | |
| settings)) == NULL) | |
| current_browse_web_if = "No"; | |
| if ((current_preserve_job_history = cupsGetOption("PreserveJobHistory", | |
| num_settings, | |
| settings)) == NULL) | |
| current_preserve_job_history = "Yes"; | |
| if ((current_preserve_job_files = cupsGetOption("PreserveJobFiles", | |
| num_settings, | |
| settings)) == NULL) | |
| current_preserve_job_files = "1d"; | |
| if ((current_max_clients = cupsGetOption("MaxClients", num_settings, | |
| settings)) == NULL) | |
| current_max_clients = "100"; | |
| if ((current_max_jobs = cupsGetOption("MaxJobs", num_settings, | |
| settings)) == NULL) | |
| current_max_jobs = "500"; | |
| if ((current_max_log_size = cupsGetOption("MaxLogSize", num_settings, | |
| settings)) == NULL) | |
| current_max_log_size = "1m"; | |
| /* | |
| * See if the settings have changed... | |
| */ | |
| changed = strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, | |
| num_settings, settings)) || | |
| strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, | |
| num_settings, settings)) || | |
| strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY, | |
| num_settings, settings)) || | |
| strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, | |
| num_settings, settings)) || | |
| #ifdef HAVE_GSSAPI | |
| !cupsGetOption("DefaultAuthType", num_settings, settings) || | |
| strcmp(default_auth_type, cupsGetOption("DefaultAuthType", | |
| num_settings, settings)) || | |
| #endif /* HAVE_GSSAPI */ | |
| strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, | |
| num_settings, settings)); | |
| if (advanced && !changed) | |
| changed = _cups_strcasecmp(browse_web_if, current_browse_web_if) || | |
| _cups_strcasecmp(preserve_job_history, current_preserve_job_history) || | |
| _cups_strcasecmp(preserve_job_files, current_preserve_job_files) || | |
| _cups_strcasecmp(max_clients, current_max_clients) || | |
| _cups_strcasecmp(max_jobs, current_max_jobs) || | |
| _cups_strcasecmp(max_log_size, current_max_log_size); | |
| if (changed) | |
| { | |
| /* | |
| * Settings *have* changed, so save the changes... | |
| */ | |
| cupsFreeOptions(num_settings, settings); | |
| num_settings = 0; | |
| num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING, | |
| debug_logging, num_settings, &settings); | |
| num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN, | |
| remote_admin, num_settings, &settings); | |
| num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY, | |
| remote_any, num_settings, &settings); | |
| num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS, | |
| share_printers, num_settings, &settings); | |
| num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY, | |
| user_cancel_any, num_settings, &settings); | |
| #ifdef HAVE_GSSAPI | |
| num_settings = cupsAddOption("DefaultAuthType", default_auth_type, | |
| num_settings, &settings); | |
| #endif /* HAVE_GSSAPI */ | |
| if (advanced) | |
| { | |
| /* | |
| * Add advanced settings... | |
| */ | |
| if (_cups_strcasecmp(browse_web_if, current_browse_web_if)) | |
| num_settings = cupsAddOption("BrowseWebIF", browse_web_if, | |
| num_settings, &settings); | |
| if (_cups_strcasecmp(preserve_job_history, current_preserve_job_history)) | |
| num_settings = cupsAddOption("PreserveJobHistory", | |
| preserve_job_history, num_settings, | |
| &settings); | |
| if (_cups_strcasecmp(preserve_job_files, current_preserve_job_files)) | |
| num_settings = cupsAddOption("PreserveJobFiles", preserve_job_files, | |
| num_settings, &settings); | |
| if (_cups_strcasecmp(max_clients, current_max_clients)) | |
| num_settings = cupsAddOption("MaxClients", max_clients, num_settings, | |
| &settings); | |
| if (_cups_strcasecmp(max_jobs, current_max_jobs)) | |
| num_settings = cupsAddOption("MaxJobs", max_jobs, num_settings, | |
| &settings); | |
| if (_cups_strcasecmp(max_log_size, current_max_log_size)) | |
| num_settings = cupsAddOption("MaxLogSize", max_log_size, num_settings, | |
| &settings); | |
| } | |
| if (!cupsAdminSetServerSettings(http, num_settings, settings)) | |
| { | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| cgiStartHTML(cgiText(_("Change Settings"))); | |
| cgiSetVariable("MESSAGE", | |
| cgiText(_("Unable to change server settings"))); | |
| cgiSetVariable("ERROR", cupsLastErrorString()); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| } | |
| else | |
| { | |
| if (advanced) | |
| cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&" | |
| "URL=/admin/?ADVANCEDSETTINGS=YES"); | |
| else | |
| cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); | |
| cgiStartHTML(cgiText(_("Change Settings"))); | |
| cgiCopyTemplateLang("restart.tmpl"); | |
| } | |
| } | |
| else | |
| { | |
| /* | |
| * No changes... | |
| */ | |
| cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); | |
| cgiStartHTML(cgiText(_("Change Settings"))); | |
| cgiCopyTemplateLang("norestart.tmpl"); | |
| } | |
| cupsFreeOptions(num_settings, settings); | |
| cgiEndHTML(); | |
| } | |
| else if (cgiGetVariable("SAVECHANGES") && cgiGetVariable("CUPSDCONF")) | |
| { | |
| /* | |
| * Save hand-edited config file... | |
| */ | |
| http_status_t status; /* PUT status */ | |
| char tempfile[1024]; /* Temporary new cupsd.conf */ | |
| int tempfd; /* Temporary file descriptor */ | |
| cups_file_t *temp; /* Temporary file */ | |
| const char *start, /* Start of line */ | |
| *end; /* End of line */ | |
| /* | |
| * Create a temporary file for the new cupsd.conf file... | |
| */ | |
| if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) | |
| { | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file"))); | |
| cgiSetVariable("ERROR", strerror(errno)); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| perror(tempfile); | |
| return; | |
| } | |
| if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL) | |
| { | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file"))); | |
| cgiSetVariable("ERROR", strerror(errno)); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| perror(tempfile); | |
| close(tempfd); | |
| unlink(tempfile); | |
| return; | |
| } | |
| /* | |
| * Copy the cupsd.conf text from the form variable... | |
| */ | |
| start = cgiGetVariable("CUPSDCONF"); | |
| while (start) | |
| { | |
| if ((end = strstr(start, "\r\n")) == NULL) | |
| if ((end = strstr(start, "\n")) == NULL) | |
| end = start + strlen(start); | |
| cupsFileWrite(temp, start, (size_t)(end - start)); | |
| cupsFilePutChar(temp, '\n'); | |
| if (*end == '\r') | |
| start = end + 2; | |
| else if (*end == '\n') | |
| start = end + 1; | |
| else | |
| start = NULL; | |
| } | |
| cupsFileClose(temp); | |
| /* | |
| * Upload the configuration file to the server... | |
| */ | |
| status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile); | |
| if (status == HTTP_UNAUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| unlink(tempfile); | |
| exit(0); | |
| } | |
| else if (status != HTTP_CREATED) | |
| { | |
| cgiSetVariable("MESSAGE", | |
| cgiText(_("Unable to upload cupsd.conf file"))); | |
| cgiSetVariable("ERROR", httpStatus(status)); | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| } | |
| else | |
| { | |
| cgiSetVariable("refresh_page", "5;URL=/admin/"); | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiCopyTemplateLang("restart.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| unlink(tempfile); | |
| } | |
| else | |
| { | |
| struct stat info; /* cupsd.conf information */ | |
| cups_file_t *cupsd; /* cupsd.conf file */ | |
| char *buffer, /* Buffer for entire file */ | |
| *bufptr, /* Pointer into buffer */ | |
| *bufend; /* End of buffer */ | |
| int ch; /* Character from file */ | |
| char filename[1024]; /* Filename */ | |
| const char *server_root; /* Location of config files */ | |
| /* | |
| * Locate the cupsd.conf file... | |
| */ | |
| if ((server_root = getenv("CUPS_SERVERROOT")) == NULL) | |
| server_root = CUPS_SERVERROOT; | |
| snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root); | |
| /* | |
| * Figure out the size... | |
| */ | |
| if (stat(filename, &info)) | |
| { | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiSetVariable("MESSAGE", | |
| cgiText(_("Unable to access cupsd.conf file"))); | |
| cgiSetVariable("ERROR", strerror(errno)); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| perror(filename); | |
| return; | |
| } | |
| if (info.st_size > (1024 * 1024)) | |
| { | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiSetVariable("MESSAGE", | |
| cgiText(_("Unable to access cupsd.conf file"))); | |
| cgiSetVariable("ERROR", | |
| cgiText(_("Unable to edit cupsd.conf files larger than " | |
| "1MB"))); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename, | |
| (long)info.st_size); | |
| return; | |
| } | |
| /* | |
| * Open the cupsd.conf file... | |
| */ | |
| if ((cupsd = cupsFileOpen(filename, "r")) == NULL) | |
| { | |
| /* | |
| * Unable to open - log an error... | |
| */ | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiSetVariable("MESSAGE", | |
| cgiText(_("Unable to access cupsd.conf file"))); | |
| cgiSetVariable("ERROR", strerror(errno)); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| perror(filename); | |
| return; | |
| } | |
| /* | |
| * Allocate memory and load the file into a string buffer... | |
| */ | |
| if ((buffer = calloc(1, (size_t)info.st_size + 1)) != NULL) | |
| { | |
| cupsFileRead(cupsd, buffer, (size_t)info.st_size); | |
| cgiSetVariable("CUPSDCONF", buffer); | |
| free(buffer); | |
| } | |
| cupsFileClose(cupsd); | |
| /* | |
| * Then get the default cupsd.conf file and put that into a string as | |
| * well... | |
| */ | |
| strlcat(filename, ".default", sizeof(filename)); | |
| if (!stat(filename, &info) && info.st_size < (1024 * 1024) && | |
| (cupsd = cupsFileOpen(filename, "r")) != NULL) | |
| { | |
| if ((buffer = calloc(1, 2 * (size_t)info.st_size + 1)) != NULL) | |
| { | |
| bufend = buffer + 2 * info.st_size - 1; | |
| for (bufptr = buffer; | |
| bufptr < bufend && (ch = cupsFileGetChar(cupsd)) != EOF;) | |
| { | |
| if (ch == '\\' || ch == '\"') | |
| { | |
| *bufptr++ = '\\'; | |
| *bufptr++ = (char)ch; | |
| } | |
| else if (ch == '\n') | |
| { | |
| *bufptr++ = '\\'; | |
| *bufptr++ = 'n'; | |
| } | |
| else if (ch == '\t') | |
| { | |
| *bufptr++ = '\\'; | |
| *bufptr++ = 't'; | |
| } | |
| else if (ch >= ' ') | |
| *bufptr++ = (char)ch; | |
| } | |
| *bufptr = '\0'; | |
| cgiSetVariable("CUPSDCONF_DEFAULT", buffer); | |
| free(buffer); | |
| } | |
| cupsFileClose(cupsd); | |
| } | |
| /* | |
| * Show the current config file... | |
| */ | |
| cgiStartHTML(cgiText(_("Edit Configuration File"))); | |
| cgiCopyTemplateLang("edit-config.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| } | |
| /* | |
| * 'do_delete_class()' - Delete a class. | |
| */ | |
| static void | |
| do_delete_class(http_t *http) /* I - HTTP connection */ | |
| { | |
| ipp_t *request; /* IPP request */ | |
| char uri[HTTP_MAX_URI]; /* Job URI */ | |
| const char *pclass; /* Printer class name */ | |
| /* | |
| * Get form variables... | |
| */ | |
| if (cgiGetVariable("CONFIRM") == NULL) | |
| { | |
| cgiStartHTML(cgiText(_("Delete Class"))); | |
| cgiCopyTemplateLang("class-confirm.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL) | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, "/classes/%s", pclass); | |
| else | |
| { | |
| cgiStartHTML(cgiText(_("Delete Class"))); | |
| cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| /* | |
| * Build a CUPS_DELETE_CLASS request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| */ | |
| request = ippNewRequest(CUPS_DELETE_CLASS); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| ippDelete(cupsDoRequest(http, request, "/admin/")); | |
| /* | |
| * Show the results... | |
| */ | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() <= IPP_OK_CONFLICT) | |
| { | |
| /* | |
| * Redirect successful updates back to the classes page... | |
| */ | |
| cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes"); | |
| } | |
| cgiStartHTML(cgiText(_("Delete Class"))); | |
| if (cupsLastError() > IPP_OK_CONFLICT) | |
| cgiShowIPPError(_("Unable to delete class")); | |
| else | |
| cgiCopyTemplateLang("class-deleted.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'do_delete_printer()' - Delete a printer. | |
| */ | |
| static void | |
| do_delete_printer(http_t *http) /* I - HTTP connection */ | |
| { | |
| ipp_t *request; /* IPP request */ | |
| char uri[HTTP_MAX_URI]; /* Job URI */ | |
| const char *printer; /* Printer printer name */ | |
| /* | |
| * Get form variables... | |
| */ | |
| if (cgiGetVariable("CONFIRM") == NULL) | |
| { | |
| cgiStartHTML(cgiText(_("Delete Printer"))); | |
| cgiCopyTemplateLang("printer-confirm.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL) | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, "/printers/%s", printer); | |
| else | |
| { | |
| cgiStartHTML(cgiText(_("Delete Printer"))); | |
| cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| /* | |
| * Build a CUPS_DELETE_PRINTER request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| */ | |
| request = ippNewRequest(CUPS_DELETE_PRINTER); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| ippDelete(cupsDoRequest(http, request, "/admin/")); | |
| /* | |
| * Show the results... | |
| */ | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() <= IPP_OK_CONFLICT) | |
| { | |
| /* | |
| * Redirect successful updates back to the printers page... | |
| */ | |
| cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers"); | |
| } | |
| cgiStartHTML(cgiText(_("Delete Printer"))); | |
| if (cupsLastError() > IPP_OK_CONFLICT) | |
| cgiShowIPPError(_("Unable to delete printer")); | |
| else | |
| cgiCopyTemplateLang("printer-deleted.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'do_export()' - Export printers to Samba. | |
| */ | |
| static void | |
| do_export(http_t *http) /* I - HTTP connection */ | |
| { | |
| int i, j; /* Looping vars */ | |
| ipp_t *request, /* IPP request */ | |
| *response; /* IPP response */ | |
| const char *username, /* Samba username */ | |
| *password, /* Samba password */ | |
| *export_all; /* Export all printers? */ | |
| int export_count, /* Number of printers to export */ | |
| printer_count; /* Number of available printers */ | |
| const char *name, /* What name to pull */ | |
| *dest; /* Current destination */ | |
| char ppd[1024]; /* PPD file */ | |
| /* | |
| * Get form data... | |
| */ | |
| username = cgiGetVariable("USERNAME"); | |
| password = cgiGetVariable("PASSWORD"); | |
| export_all = cgiGetVariable("EXPORT_ALL"); | |
| export_count = cgiGetSize("EXPORT_NAME"); | |
| /* | |
| * Get list of available printers... | |
| */ | |
| cgiSetSize("PRINTER_NAME", 0); | |
| cgiSetSize("PRINTER_EXPORT", 0); | |
| request = ippNewRequest(CUPS_GET_PRINTERS); | |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, | |
| "printer-type", 0); | |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, | |
| "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
| "requested-attributes", NULL, "printer-name"); | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| cgiSetIPPVars(response, NULL, NULL, NULL, 0); | |
| ippDelete(response); | |
| if (!export_all) | |
| { | |
| printer_count = cgiGetSize("PRINTER_NAME"); | |
| for (i = 0; i < printer_count; i ++) | |
| { | |
| dest = cgiGetArray("PRINTER_NAME", i); | |
| for (j = 0; j < export_count; j ++) | |
| if (!_cups_strcasecmp(dest, cgiGetArray("EXPORT_NAME", j))) | |
| break; | |
| cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : ""); | |
| } | |
| } | |
| } | |
| /* | |
| * Export or get the printers to export... | |
| */ | |
| if (username && *username && password && *password && | |
| (export_all || export_count > 0)) | |
| { | |
| /* | |
| * Do export... | |
| */ | |
| fputs("DEBUG: Export printers...\n", stderr); | |
| if (export_all) | |
| { | |
| name = "PRINTER_NAME"; | |
| export_count = cgiGetSize("PRINTER_NAME"); | |
| } | |
| else | |
| name = "EXPORT_NAME"; | |
| for (i = 0; i < export_count; i ++) | |
| { | |
| dest = cgiGetArray(name, i); | |
| if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd))) | |
| break; | |
| j = cupsAdminExportSamba(dest, ppd, "localhost", username, password, | |
| stderr); | |
| unlink(ppd); | |
| if (!j) | |
| break; | |
| } | |
| if (i < export_count) | |
| cgiSetVariable("ERROR", cupsLastErrorString()); | |
| else | |
| { | |
| cgiStartHTML(cgiText(_("Export Printers to Samba"))); | |
| cgiCopyTemplateLang("samba-exported.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| } | |
| else if (username && !*username) | |
| cgiSetVariable("ERROR", | |
| cgiText(_("A Samba username is required to export " | |
| "printer drivers"))); | |
| else if (username && (!password || !*password)) | |
| cgiSetVariable("ERROR", | |
| cgiText(_("A Samba password is required to export " | |
| "printer drivers"))); | |
| /* | |
| * Show form... | |
| */ | |
| cgiStartHTML(cgiText(_("Export Printers to Samba"))); | |
| cgiCopyTemplateLang("samba-export.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'do_list_printers()' - List available printers. | |
| */ | |
| static void | |
| do_list_printers(http_t *http) /* I - HTTP connection */ | |
| { | |
| ipp_t *request, /* IPP request */ | |
| *response; /* IPP response */ | |
| ipp_attribute_t *attr; /* IPP attribute */ | |
| cgiStartHTML(cgiText(_("List Available Printers"))); | |
| fflush(stdout); | |
| /* | |
| * Get the list of printers and their devices... | |
| */ | |
| request = ippNewRequest(CUPS_GET_PRINTERS); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
| "requested-attributes", NULL, "device-uri"); | |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type", | |
| CUPS_PRINTER_LOCAL); | |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask", | |
| CUPS_PRINTER_LOCAL); | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| /* | |
| * Got the printer list, now load the devices... | |
| */ | |
| int i; /* Looping var */ | |
| cups_array_t *printer_devices; /* Printer devices for local printers */ | |
| char *printer_device; /* Current printer device */ | |
| /* | |
| * Allocate an array and copy the device strings... | |
| */ | |
| printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL); | |
| for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI); | |
| attr; | |
| attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI)) | |
| { | |
| cupsArrayAdd(printer_devices, _cupsStrAlloc(attr->values[0].string.text)); | |
| } | |
| /* | |
| * Free the printer list and get the device list... | |
| */ | |
| ippDelete(response); | |
| request = ippNewRequest(CUPS_GET_DEVICES); | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| /* | |
| * Got the device list, let's parse it... | |
| */ | |
| const char *device_uri, /* device-uri attribute value */ | |
| *device_make_and_model, /* device-make-and-model value */ | |
| *device_info; /* device-info value */ | |
| for (i = 0, attr = response->attrs; attr; attr = attr->next) | |
| { | |
| /* | |
| * Skip leading attributes until we hit a device... | |
| */ | |
| while (attr && attr->group_tag != IPP_TAG_PRINTER) | |
| attr = attr->next; | |
| if (!attr) | |
| break; | |
| /* | |
| * Pull the needed attributes from this device... | |
| */ | |
| device_info = NULL; | |
| device_make_and_model = NULL; | |
| device_uri = NULL; | |
| while (attr && attr->group_tag == IPP_TAG_PRINTER) | |
| { | |
| if (!strcmp(attr->name, "device-info") && | |
| attr->value_tag == IPP_TAG_TEXT) | |
| device_info = attr->values[0].string.text; | |
| if (!strcmp(attr->name, "device-make-and-model") && | |
| attr->value_tag == IPP_TAG_TEXT) | |
| device_make_and_model = attr->values[0].string.text; | |
| if (!strcmp(attr->name, "device-uri") && | |
| attr->value_tag == IPP_TAG_URI) | |
| device_uri = attr->values[0].string.text; | |
| attr = attr->next; | |
| } | |
| /* | |
| * See if we have everything needed... | |
| */ | |
| if (device_info && device_make_and_model && device_uri && | |
| _cups_strcasecmp(device_make_and_model, "unknown") && | |
| strchr(device_uri, ':')) | |
| { | |
| /* | |
| * Yes, now see if there is already a printer for this | |
| * device... | |
| */ | |
| if (!cupsArrayFind(printer_devices, (void *)device_uri)) | |
| { | |
| /* | |
| * Not found, so it must be a new printer... | |
| */ | |
| char option[1024], /* Form variables for this device */ | |
| *option_ptr; /* Pointer into string */ | |
| const char *ptr; /* Pointer into device string */ | |
| /* | |
| * Format the printer name variable for this device... | |
| * | |
| * We use the device-info string first, then device-uri, | |
| * and finally device-make-and-model to come up with a | |
| * suitable name. | |
| */ | |
| if (_cups_strncasecmp(device_info, "unknown", 7)) | |
| ptr = device_info; | |
| else if ((ptr = strstr(device_uri, "://")) != NULL) | |
| ptr += 3; | |
| else | |
| ptr = device_make_and_model; | |
| for (option_ptr = option; | |
| option_ptr < (option + sizeof(option) - 1) && *ptr; | |
| ptr ++) | |
| if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' || | |
| *ptr == '.') | |
| *option_ptr++ = *ptr; | |
| else if ((*ptr == ' ' || *ptr == '/') && option_ptr > option && | |
| option_ptr[-1] != '_') | |
| *option_ptr++ = '_'; | |
| else if (*ptr == '?' || *ptr == '(') | |
| break; | |
| *option_ptr = '\0'; | |
| cgiSetArray("TEMPLATE_NAME", i, option); | |
| /* | |
| * Finally, set the form variables for this printer... | |
| */ | |
| cgiSetArray("device_info", i, device_info); | |
| cgiSetArray("device_make_and_model", i, device_make_and_model); | |
| cgiSetArray("device_uri", i, device_uri); | |
| i ++; | |
| } | |
| } | |
| if (!attr) | |
| break; | |
| } | |
| ippDelete(response); | |
| /* | |
| * Free the device list... | |
| */ | |
| for (printer_device = (char *)cupsArrayFirst(printer_devices); | |
| printer_device; | |
| printer_device = (char *)cupsArrayNext(printer_devices)) | |
| _cupsStrFree(printer_device); | |
| cupsArrayDelete(printer_devices); | |
| } | |
| } | |
| /* | |
| * Finally, show the printer list... | |
| */ | |
| cgiCopyTemplateLang("list-available-printers.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'do_menu()' - Show the main menu. | |
| */ | |
| static void | |
| do_menu(http_t *http) /* I - HTTP connection */ | |
| { | |
| int num_settings; /* Number of server settings */ | |
| cups_option_t *settings; /* Server settings */ | |
| const char *val; /* Setting value */ | |
| char filename[1024]; /* Temporary filename */ | |
| const char *datadir; /* Location of data files */ | |
| ipp_t *request, /* IPP request */ | |
| *response; /* IPP response */ | |
| /* | |
| * Get the current server settings... | |
| */ | |
| if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) | |
| { | |
| cgiSetVariable("SETTINGS_MESSAGE", | |
| cgiText(_("Unable to open cupsd.conf file:"))); | |
| cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString()); | |
| } | |
| if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings, | |
| settings)) != NULL && atoi(val)) | |
| cgiSetVariable("DEBUG_LOGGING", "CHECKED"); | |
| if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings, | |
| settings)) != NULL && atoi(val)) | |
| cgiSetVariable("REMOTE_ADMIN", "CHECKED"); | |
| if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings, | |
| settings)) != NULL && atoi(val)) | |
| cgiSetVariable("REMOTE_ANY", "CHECKED"); | |
| if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings, | |
| settings)) != NULL && atoi(val)) | |
| cgiSetVariable("SHARE_PRINTERS", "CHECKED"); | |
| if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings, | |
| settings)) != NULL && atoi(val)) | |
| cgiSetVariable("USER_CANCEL_ANY", "CHECKED"); | |
| #ifdef HAVE_GSSAPI | |
| cgiSetVariable("HAVE_GSSAPI", "1"); | |
| if ((val = cupsGetOption("DefaultAuthType", num_settings, | |
| settings)) != NULL && !_cups_strcasecmp(val, "Negotiate")) | |
| cgiSetVariable("KERBEROS", "CHECKED"); | |
| else | |
| #endif /* HAVE_GSSAPI */ | |
| cgiSetVariable("KERBEROS", ""); | |
| if ((val = cupsGetOption("BrowseWebIF", num_settings, | |
| settings)) == NULL) | |
| val = "No"; | |
| if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") || | |
| !_cups_strcasecmp(val, "true")) | |
| cgiSetVariable("BROWSE_WEB_IF", "CHECKED"); | |
| if ((val = cupsGetOption("PreserveJobHistory", num_settings, | |
| settings)) == NULL) | |
| val = "Yes"; | |
| if (val && | |
| (!_cups_strcasecmp(val, "0") || !_cups_strcasecmp(val, "no") || | |
| !_cups_strcasecmp(val, "off") || !_cups_strcasecmp(val, "false") || | |
| !_cups_strcasecmp(val, "disabled"))) | |
| { | |
| cgiSetVariable("PRESERVE_JOB_HISTORY", "0"); | |
| cgiSetVariable("PRESERVE_JOB_FILES", "0"); | |
| } | |
| else | |
| { | |
| cgiSetVariable("PRESERVE_JOBS", "CHECKED"); | |
| cgiSetVariable("PRESERVE_JOB_HISTORY", val); | |
| if ((val = cupsGetOption("PreserveJobFiles", num_settings, | |
| settings)) == NULL) | |
| val = "1d"; | |
| cgiSetVariable("PRESERVE_JOB_FILES", val); | |
| } | |
| if ((val = cupsGetOption("MaxClients", num_settings, settings)) == NULL) | |
| val = "100"; | |
| cgiSetVariable("MAX_CLIENTS", val); | |
| if ((val = cupsGetOption("MaxJobs", num_settings, settings)) == NULL) | |
| val = "500"; | |
| cgiSetVariable("MAX_JOBS", val); | |
| if ((val = cupsGetOption("MaxLogSize", num_settings, settings)) == NULL) | |
| val = "1m"; | |
| cgiSetVariable("MAX_LOG_SIZE", val); | |
| cupsFreeOptions(num_settings, settings); | |
| /* | |
| * See if Samba and the Windows drivers are installed... | |
| */ | |
| if ((datadir = getenv("CUPS_DATADIR")) == NULL) | |
| datadir = CUPS_DATADIR; | |
| snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir); | |
| if (!access(filename, R_OK)) | |
| { | |
| /* | |
| * Found Windows 2000 driver file, see if we have smbclient and | |
| * rpcclient... | |
| */ | |
| if (cupsFileFind("smbclient", getenv("PATH"), 1, filename, | |
| sizeof(filename)) && | |
| cupsFileFind("rpcclient", getenv("PATH"), 1, filename, | |
| sizeof(filename))) | |
| cgiSetVariable("HAVE_SAMBA", "Y"); | |
| else | |
| { | |
| if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename, | |
| sizeof(filename))) | |
| fputs("ERROR: smbclient not found!\n", stderr); | |
| if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename, | |
| sizeof(filename))) | |
| fputs("ERROR: rpcclient not found!\n", stderr); | |
| } | |
| } | |
| else | |
| perror(filename); | |
| /* | |
| * Subscriptions... | |
| */ | |
| request = ippNewRequest(IPP_GET_SUBSCRIPTIONS); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, "ipp://localhost/"); | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| cgiSetIPPVars(response, NULL, NULL, NULL, 0); | |
| ippDelete(response); | |
| } | |
| /* | |
| * Finally, show the main menu template... | |
| */ | |
| cgiStartHTML(cgiText(_("Administration"))); | |
| cgiCopyTemplateLang("admin.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'do_set_allowed_users()' - Set the allowed/denied users for a queue. | |
| */ | |
| static void | |
| do_set_allowed_users(http_t *http) /* I - HTTP connection */ | |
| { | |
| int i; /* Looping var */ | |
| ipp_t *request, /* IPP request */ | |
| *response; /* IPP response */ | |
| char uri[HTTP_MAX_URI]; /* Printer URI */ | |
| const char *printer, /* Printer name (purge-jobs) */ | |
| *is_class, /* Is a class? */ | |
| *users, /* List of users or groups */ | |
| *type; /* Allow/deny type */ | |
| int num_users; /* Number of users */ | |
| char *ptr, /* Pointer into users string */ | |
| *end, /* Pointer to end of users string */ | |
| quote; /* Quote character */ | |
| ipp_attribute_t *attr; /* Attribute */ | |
| static const char * const attrs[] = /* Requested attributes */ | |
| { | |
| "requesting-user-name-allowed", | |
| "requesting-user-name-denied" | |
| }; | |
| is_class = cgiGetVariable("IS_CLASS"); | |
| printer = cgiGetVariable("PRINTER_NAME"); | |
| if (!printer) | |
| { | |
| cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); | |
| cgiStartHTML(cgiText(_("Set Allowed Users"))); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| users = cgiGetVariable("users"); | |
| type = cgiGetVariable("type"); | |
| if (!users || !type || | |
| (strcmp(type, "requesting-user-name-allowed") && | |
| strcmp(type, "requesting-user-name-denied"))) | |
| { | |
| /* | |
| * Build a Get-Printer-Attributes request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| * requested-attributes | |
| */ | |
| request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", | |
| printer); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
| "requested-attributes", | |
| (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
| { | |
| cgiSetIPPVars(response, NULL, NULL, NULL, 0); | |
| ippDelete(response); | |
| } | |
| cgiStartHTML(cgiText(_("Set Allowed Users"))); | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() > IPP_OK_CONFLICT) | |
| cgiShowIPPError(_("Unable to get printer attributes")); | |
| else | |
| cgiCopyTemplateLang("users.tmpl"); | |
| cgiEndHTML(); | |
| } | |
| else | |
| { | |
| /* | |
| * Save the changes... | |
| */ | |
| for (num_users = 0, ptr = (char *)users; *ptr; num_users ++) | |
| { | |
| /* | |
| * Skip whitespace and commas... | |
| */ | |
| while (*ptr == ',' || isspace(*ptr & 255)) | |
| ptr ++; | |
| if (!*ptr) | |
| break; | |
| if (*ptr == '\'' || *ptr == '\"') | |
| { | |
| /* | |
| * Scan quoted name... | |
| */ | |
| quote = *ptr++; | |
| for (end = ptr; *end; end ++) | |
| if (*end == quote) | |
| break; | |
| } | |
| else | |
| { | |
| /* | |
| * Scan space or comma-delimited name... | |
| */ | |
| for (end = ptr; *end; end ++) | |
| if (isspace(*end & 255) || *end == ',') | |
| break; | |
| } | |
| /* | |
| * Advance to the next name... | |
| */ | |
| ptr = end; | |
| } | |
| /* | |
| * Build a CUPS-Add-Printer/Class request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| * requesting-user-name-{allowed,denied} | |
| */ | |
| request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", | |
| printer); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| if (num_users == 0) | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, | |
| "requesting-user-name-allowed", NULL, "all"); | |
| else | |
| { | |
| attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, | |
| type, num_users, NULL, NULL); | |
| for (i = 0, ptr = (char *)users; *ptr; i ++) | |
| { | |
| /* | |
| * Skip whitespace and commas... | |
| */ | |
| while (*ptr == ',' || isspace(*ptr & 255)) | |
| ptr ++; | |
| if (!*ptr) | |
| break; | |
| if (*ptr == '\'' || *ptr == '\"') | |
| { | |
| /* | |
| * Scan quoted name... | |
| */ | |
| quote = *ptr++; | |
| for (end = ptr; *end; end ++) | |
| if (*end == quote) | |
| break; | |
| } | |
| else | |
| { | |
| /* | |
| * Scan space or comma-delimited name... | |
| */ | |
| for (end = ptr; *end; end ++) | |
| if (isspace(*end & 255) || *end == ',') | |
| break; | |
| } | |
| /* | |
| * Terminate the name... | |
| */ | |
| if (*end) | |
| *end++ = '\0'; | |
| /* | |
| * Add the name... | |
| */ | |
| attr->values[i].string.text = _cupsStrAlloc(ptr); | |
| /* | |
| * Advance to the next name... | |
| */ | |
| ptr = end; | |
| } | |
| } | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| ippDelete(cupsDoRequest(http, request, "/admin/")); | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() > IPP_OK_CONFLICT) | |
| { | |
| cgiStartHTML(cgiText(_("Set Allowed Users"))); | |
| cgiShowIPPError(_("Unable to change printer")); | |
| } | |
| else | |
| { | |
| /* | |
| * Redirect successful updates back to the printer page... | |
| */ | |
| char url[1024], /* Printer/class URL */ | |
| refresh[1024]; /* Refresh URL */ | |
| cgiRewriteURL(uri, url, sizeof(url), NULL); | |
| cgiFormEncode(uri, url, sizeof(uri)); | |
| snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", | |
| uri); | |
| cgiSetVariable("refresh_page", refresh); | |
| cgiStartHTML(cgiText(_("Set Allowed Users"))); | |
| cgiCopyTemplateLang(is_class ? "class-modified.tmpl" : | |
| "printer-modified.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| } | |
| } | |
| /* | |
| * 'do_set_default()' - Set the server default printer/class. | |
| */ | |
| static void | |
| do_set_default(http_t *http) /* I - HTTP connection */ | |
| { | |
| const char *title; /* Page title */ | |
| ipp_t *request; /* IPP request */ | |
| char uri[HTTP_MAX_URI]; /* Printer URI */ | |
| const char *printer, /* Printer name (purge-jobs) */ | |
| *is_class; /* Is a class? */ | |
| is_class = cgiGetVariable("IS_CLASS"); | |
| printer = cgiGetVariable("PRINTER_NAME"); | |
| title = cgiText(_("Set As Server Default")); | |
| if (!printer) | |
| { | |
| cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| /* | |
| * Build a printer request, which requires the following | |
| * attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| */ | |
| request = ippNewRequest(CUPS_SET_DEFAULT); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", | |
| printer); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| ippDelete(cupsDoRequest(http, request, "/admin/")); | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() > IPP_OK_CONFLICT) | |
| { | |
| cgiStartHTML(title); | |
| cgiShowIPPError(_("Unable to set server default")); | |
| } | |
| else | |
| { | |
| /* | |
| * Redirect successful updates back to the printer page... | |
| */ | |
| char url[1024], /* Printer/class URL */ | |
| refresh[1024]; /* Refresh URL */ | |
| cgiRewriteURL(uri, url, sizeof(url), NULL); | |
| cgiFormEncode(uri, url, sizeof(uri)); | |
| snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri); | |
| cgiSetVariable("refresh_page", refresh); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("printer-default.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'do_set_options()' - Configure the default options for a queue. | |
| */ | |
| static void | |
| do_set_options(http_t *http, /* I - HTTP connection */ | |
| int is_class) /* I - Set options for class? */ | |
| { | |
| int i, j, k, m; /* Looping vars */ | |
| int have_options; /* Have options? */ | |
| ipp_t *request, /* IPP request */ | |
| *response; /* IPP response */ | |
| ipp_attribute_t *attr; /* IPP attribute */ | |
| char uri[HTTP_MAX_URI]; /* Job URI */ | |
| const char *var; /* Variable value */ | |
| const char *printer; /* Printer printer name */ | |
| const char *filename; /* PPD filename */ | |
| char tempfile[1024]; /* Temporary filename */ | |
| cups_file_t *in, /* Input file */ | |
| *out; /* Output file */ | |
| char line[1024], /* Line from PPD file */ | |
| value[1024], /* Option value */ | |
| keyword[1024], /* Keyword from Default line */ | |
| *keyptr; /* Pointer into keyword... */ | |
| ppd_file_t *ppd; /* PPD file */ | |
| ppd_group_t *group; /* Option group */ | |
| ppd_option_t *option; /* Option */ | |
| ppd_coption_t *coption; /* Custom option */ | |
| ppd_cparam_t *cparam; /* Custom parameter */ | |
| ppd_attr_t *ppdattr; /* PPD attribute */ | |
| const char *title; /* Page title */ | |
| title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options")); | |
| fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http, | |
| is_class); | |
| /* | |
| * Get the printer name... | |
| */ | |
| 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] | |
| */ | |
| request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS : | |
| CUPS_ADD_MODIFY_PRINTER); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, | |
| "job-sheets-default", 2, NULL, NULL); | |
| attr->values[0].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_start")); | |
| attr->values[1].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_end")); | |
| if ((var = cgiGetVariable("printer_error_policy")) != NULL) | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, | |
| "printer-error-policy", NULL, var); | |
| if ((var = cgiGetVariable("printer_op_policy")) != NULL) | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, | |
| "printer-op-policy", NULL, var); | |
| if ((var = cgiGetVariable("port_monitor")) != NULL) | |
| ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, | |
| "port-monitor", NULL, var); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| if (filename) | |
| ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile)); | |
| else | |
| ippDelete(cupsDoRequest(http, request, "/admin/")); | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() > IPP_OK_CONFLICT) | |
| { | |
| cgiStartHTML(title); | |
| cgiShowIPPError(_("Unable to set options")); | |
| } | |
| else | |
| { | |
| /* | |
| * Redirect successful updates back to the printer page... | |
| */ | |
| char refresh[1024]; /* Refresh URL */ | |
| cgiFormEncode(uri, printer, sizeof(uri)); | |
| snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s", | |
| is_class ? "classes" : "printers", uri); | |
| cgiSetVariable("refresh_page", refresh); | |
| cgiStartHTML(title); | |
| cgiCopyTemplateLang("printer-configured.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| if (filename) | |
| unlink(tempfile); | |
| } | |
| if (filename) | |
| unlink(filename); | |
| } | |
| /* | |
| * 'do_set_sharing()' - Set printer-is-shared value. | |
| */ | |
| static void | |
| do_set_sharing(http_t *http) /* I - HTTP connection */ | |
| { | |
| ipp_t *request, /* IPP request */ | |
| *response; /* IPP response */ | |
| char uri[HTTP_MAX_URI]; /* Printer URI */ | |
| const char *printer, /* Printer name */ | |
| *is_class, /* Is a class? */ | |
| *shared; /* Sharing value */ | |
| is_class = cgiGetVariable("IS_CLASS"); | |
| printer = cgiGetVariable("PRINTER_NAME"); | |
| shared = cgiGetVariable("SHARED"); | |
| if (!printer || !shared) | |
| { | |
| cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); | |
| cgiStartHTML(cgiText(_("Set Publishing"))); | |
| cgiCopyTemplateLang("error.tmpl"); | |
| cgiEndHTML(); | |
| return; | |
| } | |
| /* | |
| * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the | |
| * following attributes: | |
| * | |
| * attributes-charset | |
| * attributes-natural-language | |
| * printer-uri | |
| * printer-is-shared | |
| */ | |
| request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER); | |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, | |
| "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", | |
| printer); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
| NULL, uri); | |
| ippAddBoolean(request, IPP_TAG_OPERATION, "printer-is-shared", (char)atoi(shared)); | |
| /* | |
| * Do the request and get back a response... | |
| */ | |
| if ((response = cupsDoRequest(http, request, "/admin/")) != NULL) | |
| { | |
| cgiSetIPPVars(response, NULL, NULL, NULL, 0); | |
| ippDelete(response); | |
| } | |
| if (cupsLastError() == IPP_NOT_AUTHORIZED) | |
| { | |
| puts("Status: 401\n"); | |
| exit(0); | |
| } | |
| else if (cupsLastError() > IPP_OK_CONFLICT) | |
| { | |
| cgiStartHTML(cgiText(_("Set Publishing"))); | |
| cgiShowIPPError(_("Unable to change printer-is-shared attribute")); | |
| } | |
| else | |
| { | |
| /* | |
| * Redirect successful updates back to the printer page... | |
| */ | |
| char url[1024], /* Printer/class URL */ | |
| refresh[1024]; /* Refresh URL */ | |
| cgiRewriteURL(uri, url, sizeof(url), NULL); | |
| cgiFormEncode(uri, url, sizeof(uri)); | |
| snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri); | |
| cgiSetVariable("refresh_page", refresh); | |
| cgiStartHTML(cgiText(_("Set Publishing"))); | |
| cgiCopyTemplateLang(is_class ? "class-modified.tmpl" : | |
| "printer-modified.tmpl"); | |
| } | |
| cgiEndHTML(); | |
| } | |
| /* | |
| * 'get_option_value()' - Return the value of an option. | |
| * | |
| * This function also handles generation of custom option values. | |
| */ | |
| static char * /* O - Value string or NULL on error */ | |
| get_option_value( | |
| ppd_file_t *ppd, /* I - PPD file */ | |
| const char *name, /* I - Option name */ | |
| char *buffer, /* I - String buffer */ | |
| size_t bufsize) /* I - Size of buffer */ | |
| { | |
| char *bufptr, /* Pointer into buffer */ | |
| *bufend; /* End of buffer */ | |
| ppd_coption_t *coption; /* Custom option */ | |
| ppd_cparam_t *cparam; /* Current custom parameter */ | |
| char keyword[256]; /* Parameter name */ | |
| const char *val, /* Parameter value */ | |
| *uval; /* Units value */ | |
| long integer; /* Integer value */ | |
| double number, /* Number value */ | |
| number_points; /* Number in points */ | |
| /* | |
| * See if we have a custom option choice... | |
| */ | |
| if ((val = cgiGetVariable(name)) == NULL) | |
| { | |
| /* | |
| * Option not found! | |
| */ | |
| return (NULL); | |
| } | |
| else if (_cups_strcasecmp(val, "Custom") || | |
| (coption = ppdFindCustomOption(ppd, name)) == NULL) | |
| { | |
| /* | |
| * Not a custom choice... | |
| */ | |
| strlcpy(buffer, val, bufsize); | |
| return (buffer); | |
| } | |
| /* | |
| * OK, we have a custom option choice, format it... | |
| */ | |
| *buffer = '\0'; | |
| if (!strcmp(coption->keyword, "PageSize")) | |
| { | |
| const char *lval; /* Length string value */ | |
| double width, /* Width value */ | |
| width_points, /* Width in points */ | |
| length, /* Length value */ | |
| length_points; /* Length in points */ | |
| val = cgiGetVariable("PageSize.Width"); | |
| lval = cgiGetVariable("PageSize.Height"); | |
| uval = cgiGetVariable("PageSize.Units"); | |
| if (!val || !lval || !uval || | |
| (width = strtod(val, NULL)) == 0.0 || | |
| (length = strtod(lval, NULL)) == 0.0 || | |
| (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && | |
| strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) | |
| return (NULL); | |
| width_points = get_points(width, uval); | |
| length_points = get_points(length, uval); | |
| if (width_points < ppd->custom_min[0] || | |
| width_points > ppd->custom_max[0] || | |
| length_points < ppd->custom_min[1] || | |
| length_points > ppd->custom_max[1]) | |
| return (NULL); | |
| snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval); | |
| } | |
| else if (cupsArrayCount(coption->params) == 1) | |
| { | |
| cparam = ppdFirstCustomParam(coption); | |
| snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name); | |
| if ((val = cgiGetVariable(keyword)) == NULL) | |
| return (NULL); | |
| switch (cparam->type) | |
| { | |
| case PPD_CUSTOM_CURVE : | |
| case PPD_CUSTOM_INVCURVE : | |
| case PPD_CUSTOM_REAL : | |
| if ((number = strtod(val, NULL)) == 0.0 || | |
| number < cparam->minimum.custom_real || | |
| number > cparam->maximum.custom_real) | |
| return (NULL); | |
| snprintf(buffer, bufsize, "Custom.%g", number); | |
| break; | |
| case PPD_CUSTOM_INT : | |
| if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN || | |
| integer == LONG_MAX || | |
| integer < cparam->minimum.custom_int || | |
| integer > cparam->maximum.custom_int) | |
| return (NULL); | |
| snprintf(buffer, bufsize, "Custom.%ld", integer); | |
| break; | |
| case PPD_CUSTOM_POINTS : | |
| snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); | |
| if ((number = strtod(val, NULL)) == 0.0 || | |
| (uval = cgiGetVariable(keyword)) == NULL || | |
| (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && | |
| strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) | |
| return (NULL); | |
| number_points = get_points(number, uval); | |
| if (number_points < cparam->minimum.custom_points || | |
| number_points > cparam->maximum.custom_points) | |
| return (NULL); | |
| snprintf(buffer, bufsize, "Custom.%g%s", number, uval); | |
| break; | |
| case PPD_CUSTOM_PASSCODE : | |
| for (uval = val; *uval; uval ++) | |
| if (!isdigit(*uval & 255)) | |
| return (NULL); | |
| case PPD_CUSTOM_PASSWORD : | |
| case PPD_CUSTOM_STRING : | |
| integer = (long)strlen(val); | |
| if (integer < cparam->minimum.custom_string || | |
| integer > cparam->maximum.custom_string) | |
| return (NULL); | |
| snprintf(buffer, bufsize, "Custom.%s", val); | |
| break; | |
| } | |
| } | |
| else | |
| { | |
| const char *prefix = "{"; /* Prefix string */ | |
| bufptr = buffer; | |
| bufend = buffer + bufsize; | |
| for (cparam = ppdFirstCustomParam(coption); | |
| cparam; | |
| cparam = ppdNextCustomParam(coption)) | |
| { | |
| snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, | |
| cparam->name); | |
| if ((val = cgiGetVariable(keyword)) == NULL) | |
| return (NULL); | |
| snprintf(bufptr, (size_t)(bufend - bufptr), "%s%s=", prefix, cparam->name); | |
| bufptr += strlen(bufptr); | |
| prefix = " "; | |
| switch (cparam->type) | |
| { | |
| case PPD_CUSTOM_CURVE : | |
| case PPD_CUSTOM_INVCURVE : | |
| case PPD_CUSTOM_REAL : | |
| if ((number = strtod(val, NULL)) == 0.0 || | |
| number < cparam->minimum.custom_real || | |
| number > cparam->maximum.custom_real) | |
| return (NULL); | |
| snprintf(bufptr, (size_t)(bufend - bufptr), "%g", number); | |
| break; | |
| case PPD_CUSTOM_INT : | |
| if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN || | |
| integer == LONG_MAX || | |
| integer < cparam->minimum.custom_int || | |
| integer > cparam->maximum.custom_int) | |
| return (NULL); | |
| snprintf(bufptr, (size_t)(bufend - bufptr), "%ld", integer); | |
| break; | |
| case PPD_CUSTOM_POINTS : | |
| snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); | |
| if ((number = strtod(val, NULL)) == 0.0 || | |
| (uval = cgiGetVariable(keyword)) == NULL || | |
| (strcmp(uval, "pt") && strcmp(uval, "in") && | |
| strcmp(uval, "ft") && strcmp(uval, "cm") && | |
| strcmp(uval, "mm") && strcmp(uval, "m"))) | |
| return (NULL); | |
| number_points = get_points(number, uval); | |
| if (number_points < cparam->minimum.custom_points || | |
| number_points > cparam->maximum.custom_points) | |
| return (NULL); | |
| snprintf(bufptr, (size_t)(bufend - bufptr), "%g%s", number, uval); | |
| break; | |
| case PPD_CUSTOM_PASSCODE : | |
| for (uval = val; *uval; uval ++) | |
| if (!isdigit(*uval & 255)) | |
| return (NULL); | |
| case PPD_CUSTOM_PASSWORD : | |
| case PPD_CUSTOM_STRING : | |
| integer = (long)strlen(val); | |
| if (integer < cparam->minimum.custom_string || | |
| integer > cparam->maximum.custom_string) | |
| return (NULL); | |
| if ((bufptr + 2) > bufend) | |
| return (NULL); | |
| bufend --; | |
| *bufptr++ = '\"'; | |
| while (*val && bufptr < bufend) | |
| { | |
| if (*val == '\\' || *val == '\"') | |
| { | |
| if ((bufptr + 1) >= bufend) | |
| return (NULL); | |
| *bufptr++ = '\\'; | |
| } | |
| *bufptr++ = *val++; | |
| } | |
| if (bufptr >= bufend) | |
| return (NULL); | |
| *bufptr++ = '\"'; | |
| *bufptr = '\0'; | |
| bufend ++; | |
| break; | |
| } | |
| bufptr += strlen(bufptr); | |
| } | |
| if (bufptr == buffer || (bufend - bufptr) < 2) | |
| return (NULL); | |
| memcpy(bufptr, "}", 2); | |
| } | |
| return (buffer); | |
| } | |
| /* | |
| * 'get_points()' - Get a value in points. | |
| */ | |
| static double /* O - Number in points */ | |
| get_points(double number, /* I - Original number */ | |
| const char *uval) /* I - Units */ | |
| { | |
| if (!strcmp(uval, "mm")) /* Millimeters */ | |
| return (number * 72.0 / 25.4); | |
| else if (!strcmp(uval, "cm")) /* Centimeters */ | |
| return (number * 72.0 / 2.54); | |
| else if (!strcmp(uval, "in")) /* Inches */ | |
| return (number * 72.0); | |
| else if (!strcmp(uval, "ft")) /* Feet */ | |
| return (number * 72.0 * 12.0); | |
| else if (!strcmp(uval, "m")) /* Meters */ | |
| return (number * 72.0 / 0.0254); | |
| else /* Points */ | |
| return (number); | |
| } | |
| /* | |
| * 'get_printer_ppd()' - Get an IPP Everywhere PPD file for the given URI. | |
| */ | |
| static char * /* O - Filename or NULL */ | |
| get_printer_ppd(const char *uri, /* I - Printer URI */ | |
| char *buffer, /* I - Filename buffer */ | |
| size_t bufsize) /* I - Size of filename buffer */ | |
| { | |
| http_t *http; /* Connection to printer */ | |
| ipp_t *request, /* Get-Printer-Attributes request */ | |
| *response; /* Get-Printer-Attributes response */ | |
| char resolved[1024], /* Resolved URI */ | |
| scheme[32], /* URI scheme */ | |
| userpass[256], /* Username:password */ | |
| host[256], /* Hostname */ | |
| resource[256]; /* Resource path */ | |
| int port; /* Port number */ | |
| /* | |
| * Connect to the printer... | |
| */ | |
| if (strstr(uri, "._tcp")) | |
| { | |
| /* | |
| * Resolve URI... | |
| */ | |
| if (!_httpResolveURI(uri, resolved, sizeof(resolved), _HTTP_RESOLVE_DEFAULT, NULL, NULL)) | |
| { | |
| fprintf(stderr, "ERROR: Unable to resolve \"%s\".\n", uri); | |
| return (NULL); | |
| } | |
| uri = resolved; | |
| } | |
| if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) | |
| { | |
| fprintf(stderr, "ERROR: Bad printer URI \"%s\".\n", uri); | |
| return (NULL); | |
| } | |
| http = httpConnect2(host, port, NULL, AF_UNSPEC, !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL); | |
| if (!http) | |
| { | |
| fprintf(stderr, "ERROR: Unable to connect to \"%s:%d\": %s\n", host, port, cupsLastErrorString()); | |
| return (NULL); | |
| } | |
| /* | |
| * Send a Get-Printer-Attributes request... | |
| */ | |
| request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); | |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); | |
| response = cupsDoRequest(http, request, resource); | |
| if (!_ppdCreateFromIPP(buffer, bufsize, response)) | |
| fprintf(stderr, "ERROR: Unable to create PPD file: %s\n", strerror(errno)); | |
| ippDelete(response); | |
| httpClose(http); | |
| if (buffer[0]) | |
| return (buffer); | |
| else | |
| return (NULL); | |
| } |