From 95ece0cb8da64db966621650af60edb20abc1bfc Mon Sep 17 00:00:00 2001 From: msweet Date: Tue, 16 Feb 2016 22:52:10 +0000 Subject: [PATCH] Implement CUPS-Create-Local-Printer operation and add test file. git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@13095 a1ca3aef-8c08-0410-bb20-df032aa958be --- Makefile | 3 + cups/ipp-support.c | 3 +- doc/help/spec-ipp.html | 42 ++++-- scheduler/ipp.c | 215 +++++++++++++++++++++++++++- scheduler/printers.c | 6 + scheduler/printers.h | 1 + test/cups-create-local-printer.test | 32 +++++ 7 files changed, 285 insertions(+), 17 deletions(-) create mode 100644 test/cups-create-local-printer.test diff --git a/Makefile b/Makefile index bde412c95..272b9a800 100644 --- a/Makefile +++ b/Makefile @@ -130,6 +130,9 @@ depend: # # At least checker-231 is required. # +# Alternatively, use "--analyze -Xanalyzer -analyzer-output=text" for OPTIM (text +# output instead of HTML...) +# .PHONY: clang clang-changes clang: diff --git a/cups/ipp-support.c b/cups/ipp-support.c index 5b16bc29d..9928a91d4 100644 --- a/cups/ipp-support.c +++ b/cups/ipp-support.c @@ -247,7 +247,8 @@ static const char * const ipp_std_ops[] = }, * const ipp_cups_ops2[] = { - "CUPS-Get-Document" + "CUPS-Get-Document", + "CUPS-Create-Local-Printer" }, * const ipp_tag_names[] = { /* Value/group tag names */ diff --git a/doc/help/spec-ipp.html b/doc/help/spec-ipp.html index 54cd00c58..7095443b5 100644 --- a/doc/help/spec-ipp.html +++ b/doc/help/spec-ipp.html @@ -1330,7 +1330,11 @@ CUPS-Get-PPDs Response:

CUPS-Create-Local-Printer

-

The CUPS-Create-Local-Printer operation (0x4028) creates a local (temporary) print queue pointing to a remote IPP Everywhere printer.

+

The CUPS-Create-Local-Printer operation (0x4028) creates a local (temporary) print queue pointing to a remote IPP Everywhere Printer. The queue will remain until the scheduler idle exits, is restarted, or the system is restarted or shutdown. Temporary print queues can be made permanent by an administrator by setting the "printer-is-shared" attribute to 'true'.

+ +

At a minimum, the scheduler requires a name and URI for the Printer to add. When successful, the local "printer-uri" values are returned and may be used by the Client to submit Job Creation Requests, monitor for state changes, and so forth.

+ +

Access Rights: The authenticated user performing this operation MUST be a Local User of the system, and the request MUST be made over a local (domain socket or loopback interface) address. Otherwise, the request will be rejected with the 'client-error-forbidden' status code.

CUPS-Create-Local-Printer Request

@@ -1350,29 +1354,29 @@ CUPS-Get-PPDs Response:
+
"printer-name" (name(127)): + +
The Client MUST supply this attribute which provides the name for the new Printer. +
"device-uri" (uri): -
An "ipp" or "ipps" URI pointing to an IPP Everywhere printer. +
The Client MUST supply this attribute which provides an "ipp" or "ipps" URI pointing to an IPP Everywhere Printer.
"printer-device-id" (text(1023)): -
The IEEE 1284 device ID for the new printer. +
The Client OPTIONALLY supplies this attribute which provides the IEEE 1284 device ID for the new Printer.
"printer-geo-location" (uri): -
The geo-location of the new printer as a "geo" URI. +
The Client OPTIONALLY supplies this attribute which provides the geo-location of the new Printer as a "geo" URI.
"printer-info" (text(127)): -
The description for the new printer. +
The Client OPTIONALLY supplies this attribute which provides the description for the new Printer.
"printer-location" (text(127)): -
The location of the new printer. - -
"printer-name" (name(127)): - -
The name for the new printer. +
The Client OPTIONALLY supplies this attribute which provides the location of the new Printer.
@@ -1392,21 +1396,31 @@ CUPS-Get-PPDs Response:
The standard response status message. + + +

Group 2: Printer Attributes + +

+ +
"printer-id" (integer(0:65535)): + +
The numeric identifier for the created Printer. +
"printer-is-accepting-jobs" (boolean): -
Whether the new printer is accepting jobs at the time of the response. +
Whether the created Printer is accepting jobs at the time of the response.
"printer-state" (type1 enum): -
The state of the created printer at the time of the response. +
The state of the created Printer at the time of the response.
"printer-state-reasons" (1setOf type2 keyword): -
The state keywords for the created printer at the time of the response. +
The state keywords for the created Printer at the time of the response.
"printer-uri-supported" (1setOf uri): -
The URIs for the created printer. +
The URIs for the created Printer.
diff --git a/scheduler/ipp.c b/scheduler/ipp.c index 9f076098d..bbd505859 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -5200,6 +5200,101 @@ create_job(cupsd_client_t *con, /* I - Client connection */ } +/* + * 'create_local_bg_thread()' - Background thread for creating a local print queue. + */ + +static void * /* O - Exit status */ +create_local_bg_thread( + cupsd_printer_t *printer) /* I - Printer */ +{ + cups_file_t *from, /* Source file */ + *to; /* Destination file */ + char fromppd[1024], /* Source PPD */ + toppd[1024], /* Destination PPD */ + scheme[32], /* URI scheme */ + userpass[256], /* User:pass */ + host[256], /* Hostname */ + resource[1024], /* Resource path */ + line[1024]; /* Line from PPD */ + int port; /* Port number */ + http_encryption_t encryption; /* Type of encryption to use */ + http_t *http; /* Connection to printer */ + ipp_t *request, /* Request to printer */ + *response; /* Response from printer */ + ipp_attribute_t *attr; /* Attribute in response */ + + + /* + * Try connecting to the printer... + */ + + if (httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) + return (NULL); + + if (!strcmp(scheme, "ipps") || port == 443) + encryption = HTTP_ENCRYPTION_ALWAYS; + else + encryption = HTTP_ENCRYPTION_IF_REQUESTED; + + if ((http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) + return (NULL); + + /* + * Query the printer for its capabilities... + */ + + request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer->device_uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "all"); + + response = cupsDoRequest(http, request, resource); + + // TODO: Grab printer icon file... + httpClose(http); + + /* + * Write the PPD for the queue... + */ + + if (_ppdCreateFromIPP(fromppd, sizeof(fromppd), response)) + { + if ((!printer->info || !*(printer->info)) && (attr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT)) != NULL) + cupsdSetString(&printer->info, ippGetString(attr, 0, NULL)); + + if ((!printer->location || !*(printer->location)) && (attr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL) + cupsdSetString(&printer->location, ippGetString(attr, 0, NULL)); + + if ((!printer->geo_location || !*(printer->geo_location)) && (attr = ippFindAttribute(response, "printer-geo-location", IPP_TAG_URI)) != NULL) + cupsdSetString(&printer->geo_location, ippGetString(attr, 0, NULL)); + + if ((from = cupsFileOpen(fromppd, "r")) == NULL) + return (NULL); + + snprintf(toppd, sizeof(toppd), "%s/ppd/%s.ppd", ServerRoot, printer->name); + if ((to = cupsdCreateConfFile(toppd, ConfigFilePerm)) == NULL) + { + cupsFileClose(from); + return (NULL); + } + + while (cupsFileGets(from, line, sizeof(line))) + cupsFilePrintf(to, "%s\n", line); + + cupsFileClose(from); + if (!cupsdCloseCreatedConfFile(to, toppd)) + { + printer->state = IPP_PSTATE_IDLE; + printer->accepting = 1; + + cupsdSetPrinterAttrs(printer); + } + } + + return (NULL); +} + + /* * 'create_local_printer()' - Create a local (temporary) print queue. */ @@ -5208,8 +5303,124 @@ static void create_local_printer( cupsd_client_t *con) /* I - Client connection */ { - // TODO: Finish me - (void)con; + ipp_attribute_t *device_uri, /* device-uri attribute */ + *printer_geo_location, /* printer-geo-location attribute */ + *printer_info, /* printer-info attribute */ + *printer_location, /* printer-location attribute */ + *printer_name; /* printer-name attribute */ + cupsd_printer_t *printer; /* New printer */ + http_status_t status; /* Policy status */ + char name[128], /* Sanitized printer name */ + *nameptr, /* Pointer into name */ + uri[1024]; /* printer-uri-supported value */ + const char *ptr; /* Pointer into attribute value */ + + + /* + * Require local access to create a local printer... + */ + + if (!httpAddrLocalhost(httpGetAddress(con->http))) + { + send_ipp_status(con, IPP_STATUS_ERROR_FORBIDDEN, _("Only local users can create a local printer.")); + return; + } + + /* + * Check any other policy limits... + */ + + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + + /* + * Grab needed attributes... + */ + + if ((printer_name = ippFindAttribute(con->request, "printer-name", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(printer_name) != IPP_TAG_PRINTER || ippGetValueTag(printer_name) != IPP_TAG_NAME) + { + if (!printer_name) + send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Missing required attribute \"%s\"."), "printer-name"); + else if (ippGetGroupTag(printer_name) != IPP_TAG_PRINTER) + send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is in the wrong group."), "printer-name"); + else + send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is the wrong value type."), "printer-name"); + + return; + } + + for (nameptr = name, ptr = ippGetString(printer_name, 0, NULL); *ptr && nameptr < (name + sizeof(name) - 1); ptr ++) + { + /* + * Sanitize the printer name... + */ + + if (_cups_isalnum(*ptr)) + *nameptr++ = *ptr; + else if (nameptr == name || nameptr[-1] != '_') + *nameptr++ = '_'; + } + + *nameptr = '\0'; + + if ((device_uri = ippFindAttribute(con->request, "device-uri", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(device_uri) != IPP_TAG_PRINTER || ippGetValueTag(device_uri) != IPP_TAG_URI) + { + if (!device_uri) + send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Missing required attribute \"%s\"."), "device-uri"); + else if (ippGetGroupTag(device_uri) != IPP_TAG_PRINTER) + send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is in the wrong group."), "device-uri"); + else + send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is the wrong value type."), "device-uri"); + + return; + } + + printer_geo_location = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI); + printer_info = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT); + printer_location = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT); + + /* + * Create the printer... + */ + + if ((printer = cupsdAddPrinter(name)) == NULL) + { + send_ipp_status(con, IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer.")); + return; + } + + cupsdSetDeviceURI(printer, ippGetString(device_uri, 0, NULL)); + + if (printer_geo_location) + cupsdSetString(&printer->geo_location, ippGetString(printer_geo_location, 0, NULL)); + if (printer_info) + cupsdSetString(&printer->info, ippGetString(printer_info, 0, NULL)); + if (printer_location) + cupsdSetString(&printer->location, ippGetString(printer_location, 0, NULL)); + + cupsdSetPrinterAttrs(printer); + + /* + * Run a background thread to create the PPD... + */ + + _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, printer); + + /* + * Return printer attributes... + */ + + send_ipp_status(con, IPP_STATUS_OK, _("Local printer created.")); + + ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting); + ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", printer->state); + add_printer_state_reasons(con, printer); + + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name); + ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri); } diff --git a/scheduler/printers.c b/scheduler/printers.c index f8511373e..c3f927771 100644 --- a/scheduler/printers.c +++ b/scheduler/printers.c @@ -84,6 +84,8 @@ cupsdAddPrinter(const char *name) /* I - Name of printer */ return (NULL); } + _cupsRWInit(&p->lock); + cupsdSetString(&p->name, name); cupsdSetString(&p->info, name); cupsdSetString(&p->hostname, ServerName); @@ -2193,6 +2195,8 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ if (!CommonData) cupsdCreateCommonData(); + _cupsRWLockWrite(&p->lock); + /* * Clear out old filters, if any... */ @@ -2521,6 +2525,8 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ add_printer_defaults(p); + _cupsRWUnlock(&p->lock); + /* * Let the browse protocols reflect the change */ diff --git a/scheduler/printers.h b/scheduler/printers.h index c786a6b18..6c12cf8c4 100644 --- a/scheduler/printers.h +++ b/scheduler/printers.h @@ -59,6 +59,7 @@ typedef struct cupsd_job_s cupsd_job_t; struct cupsd_printer_s { + _cups_rwlock_t lock; /* Concurrency lock for background updates */ char *uri, /* Printer URI */ *uuid, /* Printer UUID */ *hostname, /* Host printer resides on */ diff --git a/test/cups-create-local-printer.test b/test/cups-create-local-printer.test new file mode 100644 index 000000000..bdf4fc54d --- /dev/null +++ b/test/cups-create-local-printer.test @@ -0,0 +1,32 @@ +# Create a local (temporary) print queue +# +# Usage: +# +# ipptool -tv -d name=... -d device=ipp://... ipp://localhost:port/ cups-create-local-printer.test +{ + # The name of the test... + NAME "Create local print queue" + + # The operation to use + OPERATION CUPS-Create-Local-Printer + + # Attributes, starting in the operation group... + GROUP operation-attributes-tag + ATTR charset attributes-charset utf-8 + ATTR language attributes-natural-language en + ATTR uri printer-uri $uri + ATTR name requesting-user-name $user + + GROUP printer-attributes-tag + ATTR name printer-name $name + ATTR uri device-uri $device + + # What statuses are OK? + STATUS successful-ok + + # What attributes do we expect? + EXPECT printer-is-accepting-jobs OF-TYPE boolean + EXPECT printer-state OF-TYPE enum + EXPECT printer-state-reasons OF-TYPE keyword + EXPECT printer-uri-supported OF-TYPE uri +} -- 2.39.5