]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Implement CUPS-Create-Local-Printer operation and add test file.
authormsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Tue, 16 Feb 2016 22:52:10 +0000 (22:52 +0000)
committermsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Tue, 16 Feb 2016 22:52:10 +0000 (22:52 +0000)
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@13095 a1ca3aef-8c08-0410-bb20-df032aa958be

Makefile
cups/ipp-support.c
doc/help/spec-ipp.html
scheduler/ipp.c
scheduler/printers.c
scheduler/printers.h
test/cups-create-local-printer.test [new file with mode: 0644]

index bde412c95bc986bb757ef6e72b627234a369452e..272b9a8002ed573b35bf6664a07f1900b6aa34b2 100644 (file)
--- 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:
index 5b16bc29d3d163e7775d7c56fb780dfffe64f9b8..9928a91d4c79adac1dcec8ffdee613641c157432 100644 (file)
@@ -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 */
index 54cd00c58b89e059169395c26db49f57077f3c27..7095443b53277b9de51353e281767ebd4ca92f95 100644 (file)
@@ -1330,7 +1330,11 @@ CUPS-Get-PPDs Response:
 
 <h3 class='title'><a name='CUPS_CREATE_LOCAL_PRINTER'>CUPS-Create-Local-Printer</a></h3>
 
-<p>The CUPS-Create-Local-Printer operation (0x4028) creates a local (temporary) print queue pointing to a remote IPP Everywhere printer.</p>
+<p>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'.</p>
+
+<p>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.</p>
+
+<p>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.</p>
 
 <h4>CUPS-Create-Local-Printer Request</h4>
 
@@ -1350,29 +1354,29 @@ CUPS-Get-PPDs Response:
 
 <dl>
 
+       <dt>"printer-name" (name(127)):
+
+       <dd>The Client MUST supply this attribute which provides the name for the new Printer.
+
        <dt>"device-uri" (uri):
 
-       <dd>An "ipp" or "ipps" URI pointing to an IPP Everywhere printer.
+       <dd>The Client MUST supply this attribute which provides an "ipp" or "ipps" URI pointing to an IPP Everywhere Printer.
 
        <dt>"printer-device-id" (text(1023)):
 
-       <dd>The IEEE 1284 device ID for the new printer.
+       <dd>The Client OPTIONALLY supplies this attribute which provides the IEEE 1284 device ID for the new Printer.
 
        <dt>"printer-geo-location" (uri):
 
-       <dd>The geo-location of the new printer as a "geo" URI.
+       <dd>The Client OPTIONALLY supplies this attribute which provides the geo-location of the new Printer as a "geo" URI.
 
        <dt>"printer-info" (text(127)):
 
-       <dd>The description for the new printer.
+       <dd>The Client OPTIONALLY supplies this attribute which provides the description for the new Printer.
 
        <dt>"printer-location" (text(127)):
 
-       <dd>The location of the new printer.
-
-       <dt>"printer-name" (name(127)):
-
-       <dd>The name for the new printer.
+       <dd>The Client OPTIONALLY supplies this attribute which provides the location of the new Printer.
 
 </dl>
 
@@ -1392,21 +1396,31 @@ CUPS-Get-PPDs Response:
 
        <dd>The standard response status message.
 
+</dl>
+
+<p>Group 2: Printer Attributes
+
+<dl>
+
+       <dt>"printer-id" (integer(0:65535)):
+
+       <dd>The numeric identifier for the created Printer.
+
        <dt>"printer-is-accepting-jobs" (boolean):
 
-       <dd>Whether the new printer is accepting jobs at the time of the response.
+       <dd>Whether the created Printer is accepting jobs at the time of the response.
 
        <dt>"printer-state" (type1 enum):
 
-       <dd>The state of the created printer at the time of the response.
+       <dd>The state of the created Printer at the time of the response.
 
        <dt>"printer-state-reasons" (1setOf type2 keyword):
 
-       <dd>The state keywords for the created printer at the time of the response.
+       <dd>The state keywords for the created Printer at the time of the response.
 
        <dt>"printer-uri-supported" (1setOf uri):
 
-       <dd>The URIs for the created printer.
+       <dd>The URIs for the created Printer.
 
 </dl>
 
index 9f076098d188df202e34695f0ec7800aed3b6f7e..bbd5058598e39ed8498d22ad999ab27eda561733 100644 (file)
@@ -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);
 }
 
 
index f8511373e070b0a3fb6c25d73807532ad0df44b5..c3f92777147d1527ac4f3d59e726d6edb45bd7c2 100644 (file)
@@ -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
   */
index c786a6b18da960cb567e84ce8d5f82fccb5625eb..6c12cf8c45b3cd386d12c0045efd08816b1c6d5d 100644 (file)
@@ -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 (file)
index 0000000..bdf4fc5
--- /dev/null
@@ -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
+}