| /* |
| * CGI <-> IPP variable routines for CUPS. |
| * |
| * Copyright 2007-2016 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" |
| |
| |
| /* |
| * 'cgiGetAttributes()' - Get the list of attributes that are needed |
| * by the template file. |
| */ |
| |
| void |
| cgiGetAttributes(ipp_t *request, /* I - IPP request */ |
| const char *tmpl) /* I - Base filename */ |
| { |
| int num_attrs; /* Number of attributes */ |
| char *attrs[1000]; /* Attributes */ |
| int i; /* Looping var */ |
| char filename[1024], /* Filename */ |
| locale[16]; /* Locale name */ |
| const char *directory, /* Directory */ |
| *lang; /* Language */ |
| FILE *in; /* Input file */ |
| int ch; /* Character from file */ |
| char name[255], /* Name of variable */ |
| *nameptr; /* Pointer into name */ |
| |
| |
| /* |
| * Convert the language to a locale name... |
| */ |
| |
| if ((lang = getenv("LANG")) != NULL) |
| { |
| for (i = 0; lang[i] && i < 15; i ++) |
| if (isalnum(lang[i] & 255)) |
| locale[i] = (char)tolower(lang[i]); |
| else |
| locale[i] = '_'; |
| |
| locale[i] = '\0'; |
| } |
| else |
| locale[0] = '\0'; |
| |
| /* |
| * See if we have a template file for this language... |
| */ |
| |
| directory = cgiGetTemplateDir(); |
| |
| snprintf(filename, sizeof(filename), "%s/%s/%s", directory, locale, tmpl); |
| if (access(filename, 0)) |
| { |
| locale[2] = '\0'; |
| |
| snprintf(filename, sizeof(filename), "%s/%s/%s", directory, locale, tmpl); |
| if (access(filename, 0)) |
| snprintf(filename, sizeof(filename), "%s/%s", directory, tmpl); |
| } |
| |
| /* |
| * Open the template file... |
| */ |
| |
| if ((in = fopen(filename, "r")) == NULL) |
| return; |
| |
| /* |
| * Loop through the file adding attribute names as needed... |
| */ |
| |
| num_attrs = 0; |
| attrs[0] = NULL; /* Eliminate compiler warning */ |
| |
| while ((ch = getc(in)) != EOF) |
| if (ch == '\\') |
| getc(in); |
| else if (ch == '{' && num_attrs < (int)(sizeof(attrs) / sizeof(attrs[0]))) |
| { |
| /* |
| * Grab the name... |
| */ |
| |
| for (nameptr = name; (ch = getc(in)) != EOF;) |
| if (strchr("}]<>=!~ \t\n", ch)) |
| break; |
| else if (nameptr > name && ch == '?') |
| break; |
| else if (nameptr < (name + sizeof(name) - 1)) |
| { |
| if (ch == '_') |
| *nameptr++ = '-'; |
| else |
| *nameptr++ = (char)ch; |
| } |
| |
| *nameptr = '\0'; |
| |
| if (!strncmp(name, "printer_state_history", 21)) |
| strlcpy(name, "printer_state_history", sizeof(name)); |
| |
| /* |
| * Possibly add it to the list of attributes... |
| */ |
| |
| for (i = 0; i < num_attrs; i ++) |
| if (!strcmp(attrs[i], name)) |
| break; |
| |
| if (i >= num_attrs) |
| { |
| attrs[num_attrs] = strdup(name); |
| num_attrs ++; |
| } |
| } |
| |
| /* |
| * If we have attributes, add a requested-attributes attribute to the |
| * request... |
| */ |
| |
| if (num_attrs > 0) |
| { |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
| "requested-attributes", num_attrs, NULL, (const char **)attrs); |
| |
| for (i = 0; i < num_attrs; i ++) |
| free(attrs[i]); |
| } |
| |
| fclose(in); |
| } |
| |
| |
| /* |
| * 'cgiGetIPPObjects()' - Get the objects in an IPP response. |
| */ |
| |
| cups_array_t * /* O - Array of objects */ |
| cgiGetIPPObjects(ipp_t *response, /* I - IPP response */ |
| void *search) /* I - Search filter */ |
| { |
| int i; /* Looping var */ |
| cups_array_t *objs; /* Array of objects */ |
| ipp_attribute_t *attr, /* Current attribute */ |
| *first; /* First attribute for object */ |
| ipp_tag_t group; /* Current group tag */ |
| int add; /* Add this object to the array? */ |
| |
| |
| if (!response) |
| return (0); |
| |
| for (add = 0, first = NULL, objs = cupsArrayNew(NULL, NULL), |
| group = IPP_TAG_ZERO, attr = response->attrs; |
| attr; |
| attr = attr->next) |
| { |
| if (attr->group_tag != group) |
| { |
| group = attr->group_tag; |
| |
| if (group != IPP_TAG_ZERO && group != IPP_TAG_OPERATION) |
| { |
| first = attr; |
| add = 0; |
| } |
| else if (add && first) |
| { |
| cupsArrayAdd(objs, first); |
| |
| add = 0; |
| first = NULL; |
| } |
| } |
| |
| if (attr->name && attr->group_tag != IPP_TAG_OPERATION && !add) |
| { |
| if (!search) |
| { |
| /* |
| * Add all objects if there is no search... |
| */ |
| |
| add = 1; |
| } |
| else |
| { |
| /* |
| * Check the search string against the string and integer values. |
| */ |
| |
| switch (attr->value_tag) |
| { |
| case IPP_TAG_TEXTLANG : |
| case IPP_TAG_NAMELANG : |
| case IPP_TAG_TEXT : |
| case IPP_TAG_NAME : |
| case IPP_TAG_KEYWORD : |
| case IPP_TAG_URI : |
| case IPP_TAG_MIMETYPE : |
| for (i = 0; !add && i < attr->num_values; i ++) |
| if (cgiDoSearch(search, attr->values[i].string.text)) |
| add = 1; |
| break; |
| |
| case IPP_TAG_INTEGER : |
| if (!strncmp(ippGetName(attr), "time-at-", 8)) |
| break; /* Ignore time-at-xxx */ |
| |
| for (i = 0; !add && i < attr->num_values; i ++) |
| { |
| char buf[255]; /* Number buffer */ |
| |
| |
| sprintf(buf, "%d", attr->values[i].integer); |
| |
| if (cgiDoSearch(search, buf)) |
| add = 1; |
| } |
| break; |
| |
| default : |
| break; |
| } |
| } |
| } |
| } |
| |
| if (add && first) |
| cupsArrayAdd(objs, first); |
| |
| return (objs); |
| } |
| |
| |
| /* |
| * 'cgiMoveJobs()' - Move one or more jobs. |
| * |
| * At least one of dest or job_id must be non-zero/NULL. |
| */ |
| |
| void |
| cgiMoveJobs(http_t *http, /* I - Connection to server */ |
| const char *dest, /* I - Destination or NULL */ |
| int job_id) /* I - Job ID or 0 for all */ |
| { |
| int i; /* Looping var */ |
| const char *user; /* Username */ |
| ipp_t *request, /* IPP request */ |
| *response; /* IPP response */ |
| ipp_attribute_t *attr; /* Current attribute */ |
| const char *name; /* Destination name */ |
| const char *job_printer_uri; /* JOB_PRINTER_URI form variable */ |
| char current_dest[1024]; /* Current destination */ |
| |
| |
| /* |
| * Make sure we have a username... |
| */ |
| |
| if ((user = getenv("REMOTE_USER")) == NULL) |
| { |
| puts("Status: 401\n"); |
| exit(0); |
| } |
| |
| /* |
| * See if the user has already selected a new destination... |
| */ |
| |
| if ((job_printer_uri = cgiGetVariable("JOB_PRINTER_URI")) == NULL) |
| { |
| /* |
| * Make sure necessary form variables are set... |
| */ |
| |
| if (job_id) |
| { |
| char temp[255]; /* Temporary string */ |
| |
| |
| sprintf(temp, "%d", job_id); |
| cgiSetVariable("JOB_ID", temp); |
| } |
| |
| if (dest) |
| cgiSetVariable("PRINTER_NAME", dest); |
| |
| /* |
| * No new destination specified, show the user what the available |
| * printers/classes are... |
| */ |
| |
| if (!dest) |
| { |
| /* |
| * Get the current destination for job N... |
| */ |
| |
| char job_uri[1024]; /* Job URI */ |
| |
| |
| request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES); |
| |
| snprintf(job_uri, sizeof(job_uri), "ipp://localhost/jobs/%d", job_id); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", |
| NULL, job_uri); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
| "requested-attributes", NULL, "job-printer-uri"); |
| |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) |
| { |
| if ((attr = ippFindAttribute(response, "job-printer-uri", |
| IPP_TAG_URI)) != NULL) |
| { |
| /* |
| * Pull the name from the URI... |
| */ |
| |
| strlcpy(current_dest, strrchr(attr->values[0].string.text, '/') + 1, |
| sizeof(current_dest)); |
| dest = current_dest; |
| } |
| |
| ippDelete(response); |
| } |
| |
| if (!dest) |
| { |
| /* |
| * Couldn't get the current destination... |
| */ |
| |
| cgiStartHTML(cgiText(_("Move Job"))); |
| cgiShowIPPError(_("Unable to find destination for job")); |
| cgiEndHTML(); |
| return; |
| } |
| } |
| |
| /* |
| * Get the list of available destinations... |
| */ |
| |
| request = ippNewRequest(CUPS_GET_PRINTERS); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
| "requested-attributes", NULL, "printer-uri-supported"); |
| |
| if (user) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, |
| "requesting-user-name", NULL, user); |
| |
| 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_SCANNER); |
| |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) |
| { |
| for (i = 0, attr = ippFindAttribute(response, "printer-uri-supported", |
| IPP_TAG_URI); |
| attr; |
| attr = ippFindNextAttribute(response, "printer-uri-supported", |
| IPP_TAG_URI)) |
| { |
| /* |
| * Pull the name from the URI... |
| */ |
| |
| name = strrchr(attr->values[0].string.text, '/') + 1; |
| |
| /* |
| * If the name is not the same as the current destination, add it! |
| */ |
| |
| if (_cups_strcasecmp(name, dest)) |
| { |
| cgiSetArray("JOB_PRINTER_URI", i, attr->values[0].string.text); |
| cgiSetArray("JOB_PRINTER_NAME", i, name); |
| i ++; |
| } |
| } |
| |
| ippDelete(response); |
| } |
| |
| /* |
| * Show the form... |
| */ |
| |
| if (job_id) |
| cgiStartHTML(cgiText(_("Move Job"))); |
| else |
| cgiStartHTML(cgiText(_("Move All Jobs"))); |
| |
| if (cgiGetSize("JOB_PRINTER_NAME") > 0) |
| cgiCopyTemplateLang("job-move.tmpl"); |
| else |
| { |
| if (job_id) |
| cgiSetVariable("MESSAGE", cgiText(_("Unable to move job"))); |
| else |
| cgiSetVariable("MESSAGE", cgiText(_("Unable to move jobs"))); |
| |
| cgiSetVariable("ERROR", cgiText(_("No destinations added."))); |
| cgiCopyTemplateLang("error.tmpl"); |
| } |
| } |
| else |
| { |
| /* |
| * Try moving the job or jobs... |
| */ |
| |
| char uri[1024], /* Job/printer URI */ |
| resource[1024], /* Post resource */ |
| refresh[1024]; /* Refresh URL */ |
| const char *job_printer_name; /* New printer name */ |
| |
| |
| request = ippNewRequest(CUPS_MOVE_JOB); |
| |
| if (job_id) |
| { |
| /* |
| * Move 1 job... |
| */ |
| |
| snprintf(resource, sizeof(resource), "/jobs/%d", job_id); |
| |
| snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", |
| NULL, uri); |
| } |
| else |
| { |
| /* |
| * Move all active jobs on a destination... |
| */ |
| |
| snprintf(resource, sizeof(resource), "/%s/%s", |
| cgiGetVariable("SECTION"), dest); |
| |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, |
| "localhost", ippPort(), "/%s/%s", |
| cgiGetVariable("SECTION"), dest); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", |
| NULL, uri); |
| } |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-printer-uri", |
| NULL, job_printer_uri); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, |
| "requesting-user-name", NULL, user); |
| |
| ippDelete(cupsDoRequest(http, request, resource)); |
| |
| /* |
| * Show the results... |
| */ |
| |
| job_printer_name = strrchr(job_printer_uri, '/') + 1; |
| |
| if (cupsLastError() <= IPP_OK_CONFLICT) |
| { |
| const char *path = strstr(job_printer_uri, "/printers/"); |
| if (!path) |
| { |
| path = strstr(job_printer_uri, "/classes/"); |
| cgiSetVariable("IS_CLASS", "YES"); |
| } |
| |
| if (path) |
| { |
| cgiFormEncode(uri, path, sizeof(uri)); |
| snprintf(refresh, sizeof(refresh), "2;URL=%s", uri); |
| cgiSetVariable("refresh_page", refresh); |
| } |
| } |
| |
| if (job_id) |
| cgiStartHTML(cgiText(_("Move Job"))); |
| else |
| cgiStartHTML(cgiText(_("Move All Jobs"))); |
| |
| if (cupsLastError() > IPP_OK_CONFLICT) |
| { |
| if (job_id) |
| cgiShowIPPError(_("Unable to move job")); |
| else |
| cgiShowIPPError(_("Unable to move jobs")); |
| } |
| else |
| { |
| cgiSetVariable("JOB_PRINTER_NAME", job_printer_name); |
| cgiCopyTemplateLang("job-moved.tmpl"); |
| } |
| } |
| |
| cgiEndHTML(); |
| } |
| |
| |
| /* |
| * 'cgiPrintCommand()' - Print a CUPS command job. |
| */ |
| |
| void |
| cgiPrintCommand(http_t *http, /* I - Connection to server */ |
| const char *dest, /* I - Destination printer */ |
| const char *command, /* I - Command to send */ |
| const char *title) /* I - Page/job title */ |
| { |
| int job_id; /* Command file job */ |
| char uri[HTTP_MAX_URI], /* Job URI */ |
| resource[1024], /* Printer resource path */ |
| refresh[1024], /* Refresh URL */ |
| command_file[1024]; /* Command "file" */ |
| http_status_t status; /* Document status */ |
| cups_option_t hold_option; /* job-hold-until option */ |
| const char *user; /* User name */ |
| ipp_t *request, /* Get-Job-Attributes request */ |
| *response; /* Get-Job-Attributes response */ |
| ipp_attribute_t *attr; /* Current job attribute */ |
| static const char * const job_attrs[] =/* Job attributes we want */ |
| { |
| "job-state", |
| "job-printer-state-message" |
| }; |
| |
| |
| /* |
| * Create the CUPS command file... |
| */ |
| |
| snprintf(command_file, sizeof(command_file), "#CUPS-COMMAND\n%s\n", command); |
| |
| /* |
| * Show status... |
| */ |
| |
| if (cgiSupportsMultipart()) |
| { |
| cgiStartMultipart(); |
| cgiStartHTML(title); |
| cgiCopyTemplateLang("command.tmpl"); |
| cgiEndHTML(); |
| fflush(stdout); |
| } |
| |
| /* |
| * Send the command file job... |
| */ |
| |
| hold_option.name = "job-hold-until"; |
| hold_option.value = "no-hold"; |
| |
| if ((user = getenv("REMOTE_USER")) != NULL) |
| cupsSetUser(user); |
| else |
| cupsSetUser("anonymous"); |
| |
| if ((job_id = cupsCreateJob(http, dest, title, |
| 1, &hold_option)) < 1) |
| { |
| cgiSetVariable("MESSAGE", cgiText(_("Unable to send command to printer driver"))); |
| cgiSetVariable("ERROR", cupsLastErrorString()); |
| cgiStartHTML(title); |
| cgiCopyTemplateLang("error.tmpl"); |
| cgiEndHTML(); |
| |
| if (cgiSupportsMultipart()) |
| cgiEndMultipart(); |
| return; |
| } |
| |
| status = cupsStartDocument(http, dest, job_id, NULL, CUPS_FORMAT_COMMAND, 1); |
| if (status == HTTP_CONTINUE) |
| status = cupsWriteRequestData(http, command_file, |
| strlen(command_file)); |
| if (status == HTTP_CONTINUE) |
| cupsFinishDocument(http, dest); |
| |
| if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE) |
| { |
| cgiSetVariable("MESSAGE", cgiText(_("Unable to send command to printer driver"))); |
| cgiSetVariable("ERROR", cupsLastErrorString()); |
| cgiStartHTML(title); |
| cgiCopyTemplateLang("error.tmpl"); |
| cgiEndHTML(); |
| |
| if (cgiSupportsMultipart()) |
| cgiEndMultipart(); |
| |
| cupsCancelJob(dest, job_id); |
| return; |
| } |
| |
| /* |
| * Wait for the job to complete... |
| */ |
| |
| if (cgiSupportsMultipart()) |
| { |
| for (;;) |
| { |
| /* |
| * Get the current job state... |
| */ |
| |
| snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); |
| request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", |
| NULL, uri); |
| if (user) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, |
| "requesting-user-name", NULL, user); |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
| "requested-attributes", 2, NULL, job_attrs); |
| |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) |
| cgiSetIPPVars(response, NULL, NULL, NULL, 0); |
| |
| attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM); |
| if (!attr || attr->values[0].integer >= IPP_JOB_STOPPED || |
| attr->values[0].integer == IPP_JOB_HELD) |
| { |
| ippDelete(response); |
| break; |
| } |
| |
| /* |
| * Job not complete, so update the status... |
| */ |
| |
| ippDelete(response); |
| |
| cgiStartHTML(title); |
| cgiCopyTemplateLang("command.tmpl"); |
| cgiEndHTML(); |
| fflush(stdout); |
| |
| sleep(5); |
| } |
| } |
| |
| /* |
| * Send the final page that reloads the printer's page... |
| */ |
| |
| snprintf(resource, sizeof(resource), "/printers/%s", dest); |
| |
| cgiFormEncode(uri, resource, sizeof(uri)); |
| snprintf(refresh, sizeof(refresh), "5;URL=%s", uri); |
| cgiSetVariable("refresh_page", refresh); |
| |
| cgiStartHTML(title); |
| cgiCopyTemplateLang("command.tmpl"); |
| cgiEndHTML(); |
| |
| if (cgiSupportsMultipart()) |
| cgiEndMultipart(); |
| } |
| |
| |
| /* |
| * 'cgiPrintTestPage()' - Print a test page. |
| */ |
| |
| void |
| cgiPrintTestPage(http_t *http, /* I - Connection to server */ |
| const char *dest) /* I - Destination printer/class */ |
| { |
| ipp_t *request, /* IPP request */ |
| *response; /* IPP response */ |
| char uri[HTTP_MAX_URI], /* Printer URI */ |
| resource[1024], /* POST resource path */ |
| refresh[1024], /* Refresh URL */ |
| filename[1024]; /* Test page filename */ |
| const char *datadir; /* CUPS_DATADIR env var */ |
| const char *user; /* Username */ |
| |
| |
| /* |
| * See who is logged in... |
| */ |
| |
| user = getenv("REMOTE_USER"); |
| |
| /* |
| * Locate the test page file... |
| */ |
| |
| if ((datadir = getenv("CUPS_DATADIR")) == NULL) |
| datadir = CUPS_DATADIR; |
| |
| snprintf(filename, sizeof(filename), "%s/data/testprint", datadir); |
| |
| /* |
| * Point to the printer/class... |
| */ |
| |
| snprintf(resource, sizeof(resource), "/%s/%s", cgiGetVariable("SECTION"), |
| dest); |
| |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, |
| "localhost", ippPort(), "/%s/%s", cgiGetVariable("SECTION"), |
| dest); |
| |
| /* |
| * Build an IPP_PRINT_JOB request, which requires the following |
| * attributes: |
| * |
| * attributes-charset |
| * attributes-natural-language |
| * printer-uri |
| * requesting-user-name |
| */ |
| |
| request = ippNewRequest(IPP_PRINT_JOB); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", |
| NULL, uri); |
| |
| if (user) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, |
| "requesting-user-name", NULL, user); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", |
| NULL, "Test Page"); |
| |
| /* |
| * Do the request and get back a response... |
| */ |
| |
| if ((response = cupsDoFileRequest(http, request, resource, |
| filename)) != NULL) |
| { |
| cgiSetIPPVars(response, NULL, NULL, NULL, 0); |
| |
| ippDelete(response); |
| } |
| |
| if (cupsLastError() <= IPP_OK_CONFLICT) |
| { |
| /* |
| * Automatically reload the printer status page... |
| */ |
| |
| cgiFormEncode(uri, resource, sizeof(uri)); |
| snprintf(refresh, sizeof(refresh), "2;URL=%s", uri); |
| cgiSetVariable("refresh_page", refresh); |
| } |
| else if (cupsLastError() == IPP_NOT_AUTHORIZED) |
| { |
| puts("Status: 401\n"); |
| exit(0); |
| } |
| |
| cgiStartHTML(cgiText(_("Print Test Page"))); |
| |
| if (cupsLastError() > IPP_OK_CONFLICT) |
| cgiShowIPPError(_("Unable to print test page")); |
| else |
| { |
| cgiSetVariable("PRINTER_NAME", dest); |
| |
| cgiCopyTemplateLang("test-page.tmpl"); |
| } |
| |
| cgiEndHTML(); |
| } |
| |
| |
| /* |
| * 'cgiRewriteURL()' - Rewrite a printer URI into a web browser URL... |
| */ |
| |
| char * /* O - New URL */ |
| cgiRewriteURL(const char *uri, /* I - Current URI */ |
| char *url, /* O - New URL */ |
| int urlsize, /* I - Size of URL buffer */ |
| const char *newresource) /* I - Replacement resource */ |
| { |
| char scheme[HTTP_MAX_URI], |
| userpass[HTTP_MAX_URI], |
| hostname[HTTP_MAX_URI], |
| rawresource[HTTP_MAX_URI], |
| resource[HTTP_MAX_URI], |
| /* URI components... */ |
| *rawptr, /* Pointer into rawresource */ |
| *resptr; /* Pointer into resource */ |
| int port; /* Port number */ |
| static int ishttps = -1; /* Using encryption? */ |
| static const char *server; /* Name of server */ |
| static char servername[1024]; |
| /* Local server name */ |
| static const char hexchars[] = "0123456789ABCDEF"; |
| /* Hexadecimal conversion characters */ |
| |
| |
| /* |
| * Check if we have been called before... |
| */ |
| |
| if (ishttps < 0) |
| { |
| /* |
| * No, initialize static vars for the conversion... |
| * |
| * First get the server name associated with the client interface as |
| * well as the locally configured hostname. We'll check *both* of |
| * these to see if the printer URL is local... |
| */ |
| |
| if ((server = getenv("SERVER_NAME")) == NULL) |
| server = ""; |
| |
| httpGetHostname(NULL, servername, sizeof(servername)); |
| |
| /* |
| * Then flag whether we are using SSL on this connection... |
| */ |
| |
| ishttps = getenv("HTTPS") != NULL; |
| } |
| |
| /* |
| * Convert the URI to a URL... |
| */ |
| |
| httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, |
| sizeof(userpass), hostname, sizeof(hostname), &port, |
| rawresource, sizeof(rawresource)); |
| |
| if (!strcmp(scheme, "ipp") || |
| !strcmp(scheme, "http") || |
| !strcmp(scheme, "https")) |
| { |
| if (newresource) |
| { |
| /* |
| * Force the specified resource name instead of the one in the URL... |
| */ |
| |
| strlcpy(resource, newresource, sizeof(resource)); |
| } |
| else |
| { |
| /* |
| * Rewrite the resource string so it doesn't contain any |
| * illegal chars... |
| */ |
| |
| for (rawptr = rawresource, resptr = resource; *rawptr; rawptr ++) |
| if ((*rawptr & 128) || *rawptr == '%' || *rawptr == ' ' || |
| *rawptr == '#' || *rawptr == '?' || |
| *rawptr == '.') /* For MSIE */ |
| { |
| if (resptr < (resource + sizeof(resource) - 3)) |
| { |
| *resptr++ = '%'; |
| *resptr++ = hexchars[(*rawptr >> 4) & 15]; |
| *resptr++ = hexchars[*rawptr & 15]; |
| } |
| } |
| else if (resptr < (resource + sizeof(resource) - 1)) |
| *resptr++ = *rawptr; |
| |
| *resptr = '\0'; |
| } |
| |
| /* |
| * Map local access to a local URI... |
| */ |
| |
| if (!_cups_strcasecmp(hostname, "127.0.0.1") || |
| !_cups_strcasecmp(hostname, "[::1]") || |
| !_cups_strcasecmp(hostname, "localhost") || |
| !_cups_strncasecmp(hostname, "localhost.", 10) || |
| !_cups_strcasecmp(hostname, server) || |
| !_cups_strcasecmp(hostname, servername)) |
| { |
| /* |
| * Make URI relative to the current server... |
| */ |
| |
| strlcpy(url, resource, (size_t)urlsize); |
| } |
| else |
| { |
| /* |
| * Rewrite URI with HTTP/HTTPS scheme... |
| */ |
| |
| if (userpass[0]) |
| snprintf(url, (size_t)urlsize, "%s://%s@%s:%d%s", ishttps ? "https" : "http", userpass, hostname, port, resource); |
| else |
| snprintf(url, (size_t)urlsize, "%s://%s:%d%s", ishttps ? "https" : "http", hostname, port, resource); |
| } |
| } |
| else |
| strlcpy(url, uri, (size_t)urlsize); |
| |
| return (url); |
| } |
| |
| |
| /* |
| * 'cgiSetIPPObjectVars()' - Set CGI variables from an IPP object. |
| */ |
| |
| ipp_attribute_t * /* O - Next object */ |
| cgiSetIPPObjectVars( |
| ipp_attribute_t *obj, /* I - Response data to be copied... */ |
| const char *prefix, /* I - Prefix for name or NULL */ |
| int element) /* I - Parent element number */ |
| { |
| ipp_attribute_t *attr; /* Attribute in response... */ |
| int i; /* Looping var */ |
| char name[1024], /* Name of attribute */ |
| *nameptr, /* Pointer into name */ |
| value[16384], /* Value(s) */ |
| *valptr; /* Pointer into value */ |
| |
| |
| fprintf(stderr, "DEBUG2: cgiSetIPPObjectVars(obj=%p, prefix=\"%s\", " |
| "element=%d)\n", |
| obj, prefix ? prefix : "(null)", element); |
| |
| /* |
| * Set common CGI template variables... |
| */ |
| |
| if (!prefix) |
| cgiSetServerVersion(); |
| |
| /* |
| * Loop through the attributes and set them for the template... |
| */ |
| |
| for (attr = obj; attr && attr->group_tag != IPP_TAG_ZERO; attr = attr->next) |
| { |
| /* |
| * Copy the attribute name, substituting "_" for "-"... |
| */ |
| |
| if (!attr->name) |
| continue; |
| |
| if (prefix) |
| { |
| snprintf(name, sizeof(name), "%s.", prefix); |
| nameptr = name + strlen(name); |
| } |
| else |
| nameptr = name; |
| |
| for (i = 0; attr->name[i] && nameptr < (name + sizeof(name) - 1); i ++) |
| if (attr->name[i] == '-') |
| *nameptr++ = '_'; |
| else |
| *nameptr++ = attr->name[i]; |
| |
| *nameptr = '\0'; |
| |
| /* |
| * Add "job_printer_name" variable if we have a "job_printer_uri" |
| * attribute... |
| */ |
| |
| if (!strcmp(name, "job_printer_uri")) |
| { |
| if ((valptr = strrchr(attr->values[0].string.text, '/')) == NULL) |
| valptr = "unknown"; |
| else |
| valptr ++; |
| |
| cgiSetArray("job_printer_name", element, valptr); |
| } |
| |
| /* |
| * Localize event names in "notify_events" variable... |
| */ |
| |
| if (!strcmp(name, "notify_events")) |
| { |
| size_t remaining; /* Remaining bytes in buffer */ |
| |
| |
| value[0] = '\0'; |
| valptr = value; |
| |
| for (i = 0; i < attr->num_values; i ++) |
| { |
| if (valptr >= (value + sizeof(value) - 3)) |
| break; |
| |
| if (i) |
| { |
| *valptr++ = ','; |
| *valptr++ = ' '; |
| } |
| |
| remaining = sizeof(value) - (size_t)(valptr - value); |
| |
| if (!strcmp(attr->values[i].string.text, "printer-stopped")) |
| strlcpy(valptr, _("Printer Paused"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "printer-added")) |
| strlcpy(valptr, _("Printer Added"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "printer-modified")) |
| strlcpy(valptr, _("Printer Modified"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "printer-deleted")) |
| strlcpy(valptr, _("Printer Deleted"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "job-created")) |
| strlcpy(valptr, _("Job Created"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "job-completed")) |
| strlcpy(valptr, _("Job Completed"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "job-stopped")) |
| strlcpy(valptr, _("Job Stopped"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "job-config-changed")) |
| strlcpy(valptr, _("Job Options Changed"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "server-restarted")) |
| strlcpy(valptr, _("Server Restarted"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "server-started")) |
| strlcpy(valptr, _("Server Started"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "server-stopped")) |
| strlcpy(valptr, _("Server Stopped"), remaining); |
| else if (!strcmp(attr->values[i].string.text, "server-audit")) |
| strlcpy(valptr, _("Server Security Auditing"), remaining); |
| else |
| strlcpy(valptr, attr->values[i].string.text, remaining); |
| |
| valptr += strlen(valptr); |
| } |
| |
| cgiSetArray("notify_events", element, value); |
| continue; |
| } |
| |
| /* |
| * Add "notify_printer_name" variable if we have a "notify_printer_uri" |
| * attribute... |
| */ |
| |
| if (!strcmp(name, "notify_printer_uri")) |
| { |
| if ((valptr = strrchr(attr->values[0].string.text, '/')) == NULL) |
| valptr = "unknown"; |
| else |
| valptr ++; |
| |
| cgiSetArray("notify_printer_name", element, valptr); |
| } |
| |
| /* |
| * Add "notify_recipient_name" variable if we have a "notify_recipient_uri" |
| * attribute, and rewrite recipient URI... |
| */ |
| |
| if (!strcmp(name, "notify_recipient_uri")) |
| { |
| char uri[1024], /* New URI */ |
| scheme[32], /* Scheme portion of URI */ |
| userpass[256], /* Username/password portion of URI */ |
| host[1024], /* Hostname portion of URI */ |
| resource[1024], /* Resource portion of URI */ |
| *options; /* Options in URI */ |
| int port; /* Port number */ |
| |
| |
| httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, |
| scheme, sizeof(scheme), userpass, sizeof(userpass), |
| host, sizeof(host), &port, resource, sizeof(resource)); |
| |
| if (!strcmp(scheme, "rss")) |
| { |
| /* |
| * RSS notification... |
| */ |
| |
| if ((options = strchr(resource, '?')) != NULL) |
| *options = '\0'; |
| |
| if (host[0]) |
| { |
| /* |
| * Link to remote feed... |
| */ |
| |
| httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "http", |
| userpass, host, port, resource); |
| strlcpy(name, uri, sizeof(name)); |
| } |
| else |
| { |
| /* |
| * Link to local feed... |
| */ |
| |
| snprintf(uri, sizeof(uri), "/rss%s", resource); |
| strlcpy(name, resource + 1, sizeof(name)); |
| } |
| } |
| else |
| { |
| /* |
| * Other... |
| */ |
| |
| strlcpy(uri, attr->values[0].string.text, sizeof(uri)); |
| strlcpy(name, resource, sizeof(name)); |
| } |
| |
| cgiSetArray("notify_recipient_uri", element, uri); |
| cgiSetArray("notify_recipient_name", element, name); |
| continue; |
| } |
| |
| /* |
| * Add "admin_uri" variable if we have a "printer_uri_supported" |
| * attribute... |
| */ |
| |
| if (!strcmp(name, "printer_uri_supported")) |
| { |
| cgiRewriteURL(attr->values[0].string.text, value, sizeof(value), |
| "/admin/"); |
| |
| cgiSetArray("admin_uri", element, value); |
| } |
| |
| /* |
| * Copy values... |
| */ |
| |
| value[0] = '\0'; /* Initially an empty string */ |
| valptr = value; /* Start at the beginning */ |
| |
| for (i = 0; i < attr->num_values; i ++) |
| { |
| if (i) |
| strlcat(valptr, ", ", sizeof(value) - (size_t)(valptr - value)); |
| |
| valptr += strlen(valptr); |
| |
| switch (attr->value_tag) |
| { |
| case IPP_TAG_INTEGER : |
| case IPP_TAG_ENUM : |
| if (strncmp(name, "time_at_", 8) == 0) |
| _cupsStrDate(valptr, sizeof(value) - (size_t)(valptr - value), (time_t)ippGetInteger(attr, i)); |
| else |
| snprintf(valptr, sizeof(value) - (size_t)(valptr - value), "%d", ippGetInteger(attr, i)); |
| break; |
| |
| case IPP_TAG_BOOLEAN : |
| snprintf(valptr, sizeof(value) - (size_t)(valptr - value), |
| "%d", attr->values[i].boolean); |
| break; |
| |
| case IPP_TAG_NOVALUE : |
| strlcat(valptr, "novalue", sizeof(value) - (size_t)(valptr - value)); |
| break; |
| |
| case IPP_TAG_RANGE : |
| snprintf(valptr, sizeof(value) - (size_t)(valptr - value), |
| "%d-%d", attr->values[i].range.lower, |
| attr->values[i].range.upper); |
| break; |
| |
| case IPP_TAG_RESOLUTION : |
| snprintf(valptr, sizeof(value) - (size_t)(valptr - value), |
| "%dx%d%s", attr->values[i].resolution.xres, |
| attr->values[i].resolution.yres, |
| attr->values[i].resolution.units == IPP_RES_PER_INCH ? |
| "dpi" : "dpcm"); |
| break; |
| |
| case IPP_TAG_URI : |
| if (strchr(attr->values[i].string.text, ':') && |
| strcmp(name, "device_uri")) |
| { |
| /* |
| * Rewrite URIs... |
| */ |
| |
| cgiRewriteURL(attr->values[i].string.text, valptr, (int)(sizeof(value) - (size_t)(valptr - value)), NULL); |
| break; |
| } |
| |
| case IPP_TAG_STRING : |
| case IPP_TAG_TEXT : |
| case IPP_TAG_NAME : |
| case IPP_TAG_KEYWORD : |
| case IPP_TAG_CHARSET : |
| case IPP_TAG_LANGUAGE : |
| case IPP_TAG_MIMETYPE : |
| strlcat(valptr, attr->values[i].string.text, |
| sizeof(value) - (size_t)(valptr - value)); |
| break; |
| |
| case IPP_TAG_BEGIN_COLLECTION : |
| snprintf(value, sizeof(value), "%s%d", name, i + 1); |
| cgiSetIPPVars(attr->values[i].collection, NULL, NULL, value, |
| element); |
| break; |
| |
| default : |
| break; /* anti-compiler-warning-code */ |
| } |
| } |
| |
| /* |
| * Add the element... |
| */ |
| |
| if (attr->value_tag != IPP_TAG_BEGIN_COLLECTION) |
| { |
| cgiSetArray(name, element, value); |
| |
| fprintf(stderr, "DEBUG2: %s[%d]=\"%s\"\n", name, element, value); |
| } |
| } |
| |
| return (attr ? attr->next : NULL); |
| } |
| |
| |
| /* |
| * 'cgiSetIPPVars()' - Set CGI variables from an IPP response. |
| */ |
| |
| int /* O - Maximum number of elements */ |
| cgiSetIPPVars(ipp_t *response, /* I - Response data to be copied... */ |
| const char *filter_name, /* I - Filter name */ |
| const char *filter_value, /* I - Filter value */ |
| const char *prefix, /* I - Prefix for name or NULL */ |
| int parent_el) /* I - Parent element number */ |
| { |
| int element; /* Element in CGI array */ |
| ipp_attribute_t *attr, /* Attribute in response... */ |
| *filter; /* Filtering attribute */ |
| |
| |
| fprintf(stderr, "DEBUG2: cgiSetIPPVars(response=%p, filter_name=\"%s\", " |
| "filter_value=\"%s\", prefix=\"%s\", parent_el=%d)\n", |
| response, filter_name ? filter_name : "(null)", |
| filter_value ? filter_value : "(null)", |
| prefix ? prefix : "(null)", parent_el); |
| |
| /* |
| * Set common CGI template variables... |
| */ |
| |
| if (!prefix) |
| cgiSetServerVersion(); |
| |
| /* |
| * Loop through the attributes and set them for the template... |
| */ |
| |
| attr = response->attrs; |
| |
| if (!prefix) |
| while (attr && attr->group_tag == IPP_TAG_OPERATION) |
| attr = attr->next; |
| |
| for (element = parent_el; attr; element ++) |
| { |
| /* |
| * Copy attributes to a separator... |
| */ |
| |
| while (attr && attr->group_tag == IPP_TAG_ZERO) |
| attr= attr->next; |
| |
| if (!attr) |
| break; |
| |
| if (filter_name) |
| { |
| for (filter = attr; |
| filter != NULL && filter->group_tag != IPP_TAG_ZERO; |
| filter = filter->next) |
| if (filter->name && !strcmp(filter->name, filter_name) && |
| (filter->value_tag == IPP_TAG_STRING || |
| (filter->value_tag >= IPP_TAG_TEXTLANG && |
| filter->value_tag <= IPP_TAG_MIMETYPE)) && |
| filter->values[0].string.text != NULL && |
| !_cups_strcasecmp(filter->values[0].string.text, filter_value)) |
| break; |
| |
| if (!filter) |
| return (element + 1); |
| |
| if (filter->group_tag == IPP_TAG_ZERO) |
| { |
| attr = filter; |
| element --; |
| continue; |
| } |
| } |
| |
| attr = cgiSetIPPObjectVars(attr, prefix, element); |
| } |
| |
| fprintf(stderr, "DEBUG2: Returning %d from cgiSetIPPVars()...\n", element); |
| |
| return (element); |
| } |
| |
| |
| /* |
| * 'cgiShowIPPError()' - Show the last IPP error message. |
| * |
| * The caller must still call cgiStartHTML() and cgiEndHTML(). |
| */ |
| |
| void |
| cgiShowIPPError(const char *message) /* I - Contextual message */ |
| { |
| cgiSetVariable("MESSAGE", cgiText(message)); |
| cgiSetVariable("ERROR", cupsLastErrorString()); |
| cgiCopyTemplateLang("error.tmpl"); |
| } |
| |
| |
| /* |
| * 'cgiShowJobs()' - Show print jobs. |
| */ |
| |
| void |
| cgiShowJobs(http_t *http, /* I - Connection to server */ |
| const char *dest) /* I - Destination name or NULL */ |
| { |
| int i; /* Looping var */ |
| const char *which_jobs; /* Which jobs to show */ |
| ipp_t *request, /* IPP request */ |
| *response; /* IPP response */ |
| cups_array_t *jobs; /* Array of job objects */ |
| ipp_attribute_t *job; /* Job object */ |
| int first, /* First job to show */ |
| count; /* Number of jobs */ |
| const char *var, /* Form variable */ |
| *query, /* Query string */ |
| *section; /* Section in web interface */ |
| void *search; /* Search data */ |
| char url[1024], /* Printer URI */ |
| val[1024]; /* Form variable */ |
| |
| |
| /* |
| * Build an IPP_GET_JOBS request, which requires the following |
| * attributes: |
| * |
| * attributes-charset |
| * attributes-natural-language |
| * printer-uri |
| */ |
| |
| request = ippNewRequest(IPP_GET_JOBS); |
| |
| if (dest) |
| { |
| httpAssembleURIf(HTTP_URI_CODING_ALL, url, sizeof(url), "ipp", NULL, |
| "localhost", ippPort(), "/printers/%s", dest); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", |
| NULL, url); |
| } |
| else |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, |
| "ipp://localhost/"); |
| |
| if ((which_jobs = cgiGetVariable("which_jobs")) != NULL && *which_jobs) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs", |
| NULL, which_jobs); |
| |
| if ((var = cgiGetVariable("FIRST")) != NULL) |
| { |
| if ((first = atoi(var)) < 0) |
| first = 0; |
| } |
| else |
| first = 0; |
| |
| if (first > 0) |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "first-index", first + 1); |
| |
| cgiGetAttributes(request, "jobs.tmpl"); |
| |
| /* |
| * Do the request and get back a response... |
| */ |
| |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) |
| { |
| /* |
| * Get a list of matching job objects. |
| */ |
| |
| if ((query = cgiGetVariable("QUERY")) != NULL && |
| !cgiGetVariable("CLEAR")) |
| search = cgiCompileSearch(query); |
| else |
| { |
| query = NULL; |
| search = NULL; |
| } |
| |
| jobs = cgiGetIPPObjects(response, search); |
| count = cupsArrayCount(jobs) + first; |
| |
| if (search) |
| cgiFreeSearch(search); |
| |
| /* |
| * Figure out which jobs to display... |
| */ |
| |
| section = cgiGetVariable("SECTION"); |
| |
| cgiClearVariables(); |
| |
| if (query) |
| cgiSetVariable("QUERY", query); |
| |
| cgiSetVariable("SECTION", section); |
| |
| sprintf(val, "%d", count); |
| cgiSetVariable("TOTAL", val); |
| |
| if (which_jobs) |
| cgiSetVariable("WHICH_JOBS", which_jobs); |
| |
| for (i = 0, job = (ipp_attribute_t *)cupsArrayFirst(jobs); |
| i < CUPS_PAGE_MAX && job; |
| i ++, job = (ipp_attribute_t *)cupsArrayNext(jobs)) |
| cgiSetIPPObjectVars(job, NULL, i); |
| |
| /* |
| * Save navigation URLs... |
| */ |
| |
| if (dest) |
| { |
| snprintf(val, sizeof(val), "/%s/%s", section, dest); |
| cgiSetVariable("PRINTER_NAME", dest); |
| cgiSetVariable("PRINTER_URI_SUPPORTED", val); |
| } |
| else |
| strlcpy(val, "/jobs/", sizeof(val)); |
| |
| cgiSetVariable("THISURL", val); |
| |
| if (first > 0) |
| { |
| sprintf(val, "%d", first - CUPS_PAGE_MAX); |
| cgiSetVariable("PREV", val); |
| } |
| |
| if ((first + CUPS_PAGE_MAX) < count) |
| { |
| sprintf(val, "%d", first + CUPS_PAGE_MAX); |
| cgiSetVariable("NEXT", val); |
| } |
| |
| if (count > CUPS_PAGE_MAX) |
| { |
| snprintf(val, sizeof(val), "%d", CUPS_PAGE_MAX * (count / CUPS_PAGE_MAX)); |
| cgiSetVariable("LAST", val); |
| } |
| |
| /* |
| * Then show everything... |
| */ |
| |
| if (dest) |
| cgiSetVariable("SEARCH_DEST", dest); |
| |
| cgiCopyTemplateLang("search.tmpl"); |
| |
| cgiCopyTemplateLang("jobs-header.tmpl"); |
| |
| if (count > CUPS_PAGE_MAX) |
| cgiCopyTemplateLang("pager.tmpl"); |
| |
| cgiCopyTemplateLang("jobs.tmpl"); |
| |
| if (count > CUPS_PAGE_MAX) |
| cgiCopyTemplateLang("pager.tmpl"); |
| |
| cupsArrayDelete(jobs); |
| ippDelete(response); |
| } |
| } |
| |
| |
| /* |
| * 'cgiText()' - Return localized text. |
| */ |
| |
| const char * /* O - Localized message */ |
| cgiText(const char *message) /* I - Message */ |
| { |
| static cups_lang_t *language = NULL; |
| /* Language */ |
| |
| |
| if (!language) |
| language = cupsLangDefault(); |
| |
| return (_cupsLangString(language, message)); |
| } |