]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Fix additional IPP/1.1 issues with cupsCopyDestInfo (Issue #5211)
authorMichael R Sweet <michael.r.sweet@gmail.com>
Thu, 1 Feb 2018 00:29:59 +0000 (19:29 -0500)
committerMichael R Sweet <michael.r.sweet@gmail.com>
Thu, 1 Feb 2018 00:29:59 +0000 (19:29 -0500)
Update ippserver to support maximum version option (-V max-version)

Update cupsCopyDestInfo to recognize when we are talking to the CUPS server
and when we are talking to the device (so we get the right URI for the printer)

Add "--device" option to testdest.

cups/cups-private.h
cups/dest-options.c
cups/dest.c
cups/request.c
cups/testdest.c
test/ippserver.c

index 847f5e32adc19294b6be9fc537248b4a5de13b76..412308c1c757ee9fb3c6937f3a462ce93ca6e474 100644 (file)
@@ -1,10 +1,11 @@
 /*
  * Private definitions for CUPS.
  *
- * Copyright 2007-2017 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
  *
- * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
  */
 
 #ifndef _CUPS_CUPS_PRIVATE_H_
@@ -232,13 +233,9 @@ extern void                _cupsBufferRelease(char *b);
 extern http_t          *_cupsConnect(void);
 extern char            *_cupsCreateDest(const char *name, const char *info, const char *device_id, const char *device_uri, char *uri, size_t urisize);
 extern void            _cupsEncodeOption(ipp_t *ipp, ipp_tag_t group_tag, _ipp_option_t *map, const char *name, const char *value);
-extern int             _cupsGet1284Values(const char *device_id,
-                                          cups_option_t **values);
-extern const char      *_cupsGetDestResource(cups_dest_t *dest, char *resource,
-                                             size_t resourcesize);
-extern int             _cupsGetDests(http_t *http, ipp_op_t op,
-                                     const char *name, cups_dest_t **dests,
-                                     cups_ptype_t type, cups_ptype_t mask);
+extern int             _cupsGet1284Values(const char *device_id, cups_option_t **values);
+extern const char      *_cupsGetDestResource(cups_dest_t *dest, unsigned flags, char *resource, size_t resourcesize);
+extern int             _cupsGetDests(http_t *http, ipp_op_t op, const char *name, cups_dest_t **dests, cups_ptype_t type, cups_ptype_t mask);
 extern const char      *_cupsGetPassword(const char *prompt);
 extern void            _cupsGlobalLock(void);
 extern _cups_globals_t *_cupsGlobals(void);
@@ -248,13 +245,10 @@ extern const char *_cupsGSSServiceName(void);
 #  endif /* HAVE_GSSAPI */
 extern int             _cupsNextDelay(int current, int *previous);
 extern void            _cupsSetDefaults(void);
-extern void            _cupsSetError(ipp_status_t status, const char *message,
-                                     int localize);
+extern void            _cupsSetError(ipp_status_t status, const char *message, int localize);
 extern void            _cupsSetHTTPError(http_status_t status);
 #  ifdef HAVE_GSSAPI
-extern int             _cupsSetNegotiateAuthString(http_t *http,
-                                                   const char *method,
-                                                   const char *resource);
+extern int             _cupsSetNegotiateAuthString(http_t *http, const char *method, const char *resource);
 #  endif /* HAVE_GSSAPI */
 extern char            *_cupsUserDefault(char *name, size_t namesize);
 
index 11a1b10fbbbed0a832ae55deb4b0f7b4f6c67fc7..38317085efe8a0737153de7eb003a18d037d57b6 100644 (file)
@@ -666,6 +666,7 @@ cupsCopyDestInfo(
     cups_dest_t *dest)                 /* I - Destination */
 {
   cups_dinfo_t *dinfo;                 /* Destination information */
+  unsigned     dflags;                 /* Destination flags */
   ipp_t                *request,               /* Get-Printer-Attributes request */
                *response;              /* Supported attributes */
   int          tries,                  /* Number of tries so far */
@@ -675,6 +676,7 @@ cupsCopyDestInfo(
   char         resource[1024];         /* Resource path */
   int          version;                /* IPP version */
   ipp_status_t status;                 /* Status of request */
+  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
   static const char * const requested_attrs[] =
   {                                    /* Requested attributes */
     "job-template",
@@ -683,14 +685,25 @@ cupsCopyDestInfo(
   };
 
 
-  DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http, (void *)dest, dest ? dest->name : ""));
+  DEBUG_printf(("cupsCopyDestInfo(http=%p, dest=%p(%s))", (void *)http, (void *)dest, dest ? dest->name : ""));
 
  /*
   * Get the default connection as needed...
   */
 
   if (!http)
-    http = _cupsConnect();
+  {
+    http   = _cupsConnect();
+    dflags = CUPS_DEST_FLAGS_NONE;
+  }
+#ifdef AF_LOCAL
+  else if (strcmp(http->hostname, cg->server) || (httpAddrFamily(http->hostaddr) != AF_LOCAL && cg->ipp_port != httpAddrPort(http->hostaddr)))
+#else
+  else if (strcmp(http->hostname, cg->server) || cg->ipp_port != httpAddrPort(http->hostaddr))
+#endif /* AF_LOCAL */
+    dflags = CUPS_DEST_FLAGS_DEVICE;
+  else
+    dflags = CUPS_DEST_FLAGS_NONE;
 
  /*
   * Range check input...
@@ -703,8 +716,11 @@ cupsCopyDestInfo(
   * Get the printer URI and resource path...
   */
 
-  if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
+  if ((uri = _cupsGetDestResource(dest, dflags, resource, sizeof(resource))) == NULL)
+  {
+    DEBUG_puts("1cupsCopyDestInfo: Unable to get resource.");
     return (NULL);
+  }
 
  /*
   * Get the supported attributes...
@@ -724,28 +740,23 @@ cupsCopyDestInfo(
     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
 
     ippSetVersion(request, version / 10, version % 10);
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
-                uri);
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
-                 "requesting-user-name", NULL, cupsUser());
-    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                 "requested-attributes",
-                 (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])),
-                 NULL, requested_attrs);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])), NULL, requested_attrs);
     response = cupsDoRequest(http, request, resource);
     status   = cupsLastError();
 
     if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
     {
-      DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
-                   "returned %s (%s)", dest->name, ippErrorString(status),
-                   cupsLastErrorString()));
+      DEBUG_printf(("1cupsCopyDestInfo: Get-Printer-Attributes for '%s' returned %s (%s)", dest->name, ippErrorString(status), cupsLastErrorString()));
 
       ippDelete(response);
       response = NULL;
 
-      if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
+      if ((status == IPP_STATUS_ERROR_BAD_REQUEST || status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED) && version > 11)
+      {
         version = 11;
+      }
       else if (status == IPP_STATUS_ERROR_BUSY)
       {
         sleep((unsigned)delay);
@@ -761,7 +772,10 @@ cupsCopyDestInfo(
   while (!response && tries < 10);
 
   if (!response)
+  {
+    DEBUG_puts("1cupsCopyDestInfo: Unable to get printer attributes.");
     return (NULL);
+  }
 
  /*
   * Allocate a cups_dinfo_t structure and return it...
@@ -774,6 +788,8 @@ cupsCopyDestInfo(
     return (NULL);
   }
 
+  DEBUG_printf(("1cupsCopyDestInfo: version=%d, uri=\"%s\", resource=\"%s\".", version, uri, resource));
+
   dinfo->version  = version;
   dinfo->uri      = uri;
   dinfo->resource = _cupsStrAlloc(resource);
index 7d08d78c7d3e3d6ae2e6f1ac48dee040bfcd013d..b90be7b3a6329109003d678d6ca574f0c16230d9 100644 (file)
@@ -1097,6 +1097,7 @@ cupsGetDest(const char  *name,            /* I - Destination name or @code NULL@ for the d
 const char *                           /* O - Printer URI */
 _cupsGetDestResource(
     cups_dest_t *dest,                 /* I - Destination */
+    unsigned    flags,                 /* I - Destination flags */
     char        *resource,             /* I - Resource buffer */
     size_t      resourcesize)          /* I - Size of resource buffer */
 {
@@ -1126,52 +1127,64 @@ _cupsGetDestResource(
   * Grab the printer URI...
   */
 
-  if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
+  if (!(flags & CUPS_DEST_FLAGS_DEVICE))
+    uri = NULL;
+  else
+    uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
+
+  if (uri)
+  {
+    DEBUG_printf(("1_cupsGetDestResource: printer-uri-supported=\"%s\"", uri));
+  }
+  else
   {
     if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
     {
 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
       if (strstr(uri, "._tcp"))
+      {
         uri = cups_dnssd_resolve(dest, uri, 5000, NULL, NULL, NULL);
+
+        if (uri)
+          DEBUG_printf(("1_cupsGetDestResource: Resolved device-uri=\"%s\"", uri));
+      }
+      else
 #endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+      DEBUG_printf(("1_cupsGetDestResource: device-uri=\"%s\"", uri));
     }
 
-    if (uri)
+    if (uri && !(flags & CUPS_DEST_FLAGS_DEVICE))
     {
-      DEBUG_printf(("1_cupsGetDestResource: Resolved printer-uri-supported=\"%s\"", uri));
-
       uri = _cupsCreateDest(dest->name, cupsGetOption("printer-info", dest->num_options, dest->options), NULL, uri, resource, resourcesize);
-    }
 
-    if (uri)
-    {
-      DEBUG_printf(("1_cupsGetDestResource: Local printer-uri-supported=\"%s\"", uri));
+      if (uri)
+      {
+       DEBUG_printf(("1_cupsGetDestResource: Local printer-uri-supported=\"%s\"", uri));
 
-      dest->num_options = cupsAddOption("printer-uri-supported", uri, dest->num_options, &dest->options);
+       dest->num_options = cupsAddOption("printer-uri-supported", uri, dest->num_options, &dest->options);
 
-      uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
+       uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
+      }
     }
-    else
-    {
-      DEBUG_puts("1_cupsGetDestResource: No printer-uri-supported found.");
+  }
 
-      if (resource)
-        *resource = '\0';
+  if (!uri)
+  {
+    DEBUG_puts("1_cupsGetDestResource: No printer-uri-supported or device-uri found.");
 
-      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+    if (resource)
+      *resource = '\0';
 
-      return (NULL);
-    }
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+
+    return (NULL);
   }
   else
   {
-    DEBUG_printf(("1_cupsGetDestResource: printer-uri-supported=\"%s\"", uri));
-
-    if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
-                        userpass, sizeof(userpass), hostname, sizeof(hostname),
-                        &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
+    if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
     {
-      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
+      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad URI."), 1);
 
       return (NULL);
     }
index 2960eaccbba9de4b664acd3a5dd0d20f87ff53cf..75eb7a3bf081577dfe9bec91c1a75c4dd65bc4a6 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * IPP utilities for CUPS.
  *
- * Copyright 2007-2018 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2007 by Easy Software Products.
  *
  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
  */
index 60f6e0bf6356a4850c7cb9b5fa4dae7b1d7072aa..afa55dedf2a9fbc30e13e0be19d5cbb7b172fde3 100644 (file)
@@ -37,9 +37,12 @@ int                                  /* O - Exit status */
 main(int  argc,                                /* I - Number of command-line arguments */
      char *argv[])                     /* I - Command-line arguments */
 {
+  int          i;                      /* Looping var */
   http_t       *http;                  /* Connection to destination */
   cups_dest_t  *dest = NULL;           /* Destination */
   cups_dinfo_t *dinfo;                 /* Destination info */
+  unsigned     dflags = CUPS_DEST_FLAGS_NONE;
+                                       /* Destination flags */
 
 
   if (argc < 2)
@@ -110,9 +113,17 @@ main(int  argc,                            /* I - Number of command-line arguments */
 
     return (0);
   }
-  else if (!strncmp(argv[1], "ipp://", 6) || !strncmp(argv[1], "ipps://", 7))
-    dest = cupsGetDestWithURI(NULL, argv[1]);
-  else if (!strcmp(argv[1], "default"))
+
+  i = 1;
+  if (!strcmp(argv[i], "--device"))
+  {
+    dflags = CUPS_DEST_FLAGS_DEVICE;
+    i ++;
+  }
+
+  if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "ipps://", 7))
+    dest = cupsGetDestWithURI(NULL, argv[i]);
+  else if (!strcmp(argv[i], "default"))
   {
     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
     if (dest && dest->instance)
@@ -121,67 +132,70 @@ main(int  argc,                           /* I - Number of command-line arguments */
       printf("default is \"%s\".\n", dest->name);
   }
   else
-    dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[1], NULL);
+    dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[i], NULL);
 
   if (!dest)
   {
-    printf("testdest: Unable to get destination \"%s\": %s\n", argv[1], cupsLastErrorString());
+    printf("testdest: Unable to get destination \"%s\": %s\n", argv[i], cupsLastErrorString());
     return (1);
   }
 
-  if ((http = cupsConnectDest(dest, CUPS_DEST_FLAGS_NONE, 30000, NULL, NULL, 0, NULL, NULL)) == NULL)
+  i ++;
+
+  if ((http = cupsConnectDest(dest, dflags, 30000, NULL, NULL, 0, NULL, NULL)) == NULL)
   {
-    printf("testdest: Unable to connect to destination \"%s\": %s\n", argv[1], cupsLastErrorString());
+    printf("testdest: Unable to connect to destination \"%s\": %s\n", dest->name, cupsLastErrorString());
     return (1);
   }
 
   if ((dinfo = cupsCopyDestInfo(http, dest)) == NULL)
   {
-    printf("testdest: Unable to get information for destination \"%s\": %s\n", argv[1], cupsLastErrorString());
+    printf("testdest: Unable to get information for destination \"%s\": %s\n", dest->name, cupsLastErrorString());
     return (1);
   }
 
-  if (argc == 2 || (!strcmp(argv[2], "supported") && argc < 6))
+  if (i == argc || !strcmp(argv[i], "supported"))
   {
-    if (argc > 3)
-      show_supported(http, dest, dinfo, argv[3], argv[4]);
+    i ++;
+
+    if ((i + 1) < argc)
+      show_supported(http, dest, dinfo, argv[i], argv[i + 1]);
     else if (argc > 2)
-      show_supported(http, dest, dinfo, argv[3], NULL);
+      show_supported(http, dest, dinfo, argv[i], NULL);
     else
       show_supported(http, dest, dinfo, NULL, NULL);
   }
-  else if (!strcmp(argv[2], "conflicts") && argc > 3)
+  else if (!strcmp(argv[i], "conflicts") && (i + 1) < argc)
   {
-    int                        i,              /* Looping var */
-                       num_options = 0;/* Number of options */
+    int                        num_options = 0;/* Number of options */
     cups_option_t      *options = NULL;/* Options */
 
-    for (i = 3; i < argc; i ++)
+    for (i ++; i < argc; i ++)
       num_options = cupsParseOptions(argv[i], num_options, &options);
 
     show_conflicts(http, dest, dinfo, num_options, options);
   }
-  else if (!strcmp(argv[2], "default") && argc == 4)
+  else if (!strcmp(argv[i], "default") && (i + 1) < argc)
   {
-    show_default(http, dest, dinfo, argv[3]);
+    show_default(http, dest, dinfo, argv[i + 1]);
   }
-  else if (!strcmp(argv[2], "localize") && argc < 6)
+  else if (!strcmp(argv[i], "localize"))
   {
-    if (argc > 3)
-      localize(http, dest, dinfo, argv[3], argv[4]);
+    i ++;
+    if ((i + 1) < argc)
+      localize(http, dest, dinfo, argv[i], argv[i + 1]);
     else if (argc > 2)
-      localize(http, dest, dinfo, argv[3], NULL);
+      localize(http, dest, dinfo, argv[i], NULL);
     else
       localize(http, dest, dinfo, NULL, NULL);
   }
-  else if (!strcmp(argv[2], "media"))
+  else if (!strcmp(argv[i], "media"))
   {
-    int                i;                      /* Looping var */
     const char *name = NULL;           /* Media name, if any */
     unsigned   flags = CUPS_MEDIA_FLAGS_DEFAULT;
                                        /* Media selection flags */
 
-    for (i = 3; i < argc; i ++)
+    for (i ++; i < argc; i ++)
     {
       if (!strcmp(argv[i], "borderless"))
        flags = CUPS_MEDIA_FLAGS_BORDERLESS;
@@ -199,19 +213,19 @@ main(int  argc,                           /* I - Number of command-line arguments */
 
     show_media(http, dest, dinfo, flags, name);
   }
-  else if (!strcmp(argv[2], "print") && argc > 3)
+  else if (!strcmp(argv[i], "print") && (i + 1) < argc)
   {
-    int                        i,              /* Looping var */
-                       num_options = 0;/* Number of options */
+    int                        num_options = 0;/* Number of options */
     cups_option_t      *options = NULL;/* Options */
+    const char         *filename = argv[i + 1];
 
-    for (i = 4; i < argc; i ++)
+    for (i += 2; i < argc; i ++)
       num_options = cupsParseOptions(argv[i], num_options, &options);
 
-    print_file(http, dest, dinfo, argv[3], num_options, options);
+    print_file(http, dest, dinfo, filename, num_options, options);
   }
   else
-    usage(argv[2]);
+    usage(argv[i]);
 
   return (0);
 }
@@ -749,9 +763,9 @@ usage(const char *arg)                      /* I - Argument for usage message */
     printf("testdest: Unknown option \"%s\".\n", arg);
 
   puts("Usage:");
-  puts("  ./testdest name [operation ...]");
-  puts("  ./testdest ipp://... [operation ...]");
-  puts("  ./testdest ipps://... [operation ...]");
+  puts("  ./testdest [--device] name [operation ...]");
+  puts("  ./testdest [--device] ipp://... [operation ...]");
+  puts("  ./testdest [--device] ipps://... [operation ...]");
   puts("  ./testdest --get");
   puts("  ./testdest --enum [grayscale] [color] [duplex] [staple] [small]\n"
        "                    [medium] [large]");
index 6180695d0b2411eb3de078c7158b1da63599e54f..46e689fa7432fd0d2d2a7e7d2c0d34618b6af59e 100644 (file)
@@ -1,9 +1,10 @@
 /*
  * Sample IPP Everywhere server for CUPS.
  *
- * Copyright 2010-2017 by Apple Inc.
+ * Copyright © 2010-2018 by Apple Inc.
  *
- * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
  */
 
 /*
@@ -455,6 +456,7 @@ static AvahiClient  *DNSSDClient = NULL;
 #endif /* HAVE_DNSSD */
 
 static int             KeepFiles = 0,
+                       MaxVersion = 20,
                        Verbosity = 0;
 
 
@@ -527,6 +529,23 @@ main(int  argc,                            /* I - Number of command-line args */
               pin = 1;
               break;
 
+          case 'V' : /* -V max-version */
+             i ++;
+             if (i >= argc)
+               usage(1);
+
+              if (!strcmp(argv[i], "2.2"))
+                MaxVersion = 22;
+             else if (!strcmp(argv[i], "2.1"))
+                MaxVersion = 21;
+             else if (!strcmp(argv[i], "2.0"))
+                MaxVersion = 20;
+             else if (!strcmp(argv[i], "1.1"))
+                MaxVersion = 11;
+             else
+               usage(1);
+              break;
+
          case 'a' : /* -a attributes-file */
              i ++;
              if (i >= argc)
@@ -1318,9 +1337,10 @@ create_printer(const char *servername,   /* I - Server hostname (NULL for default)
   };
   static const char * const versions[] =/* ipp-versions-supported values */
   {
-    "1.0",
     "1.1",
-    "2.0"
+    "2.0",
+    "2.1",
+    "2.2"
   };
   static const char * const features[] =/* ipp-features-supported values */
   {
@@ -1732,7 +1752,12 @@ create_printer(const char *servername,   /* I - Server hostname (NULL for default)
 
   /* ipp-versions-supported */
   if (!ippFindAttribute(printer->attrs, "ipp-versions-supported", IPP_TAG_ZERO))
-    ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]), NULL, versions);
+  {
+    int num_versions = MaxVersion == 11 ? 1 : MaxVersion == 20 ? 2 : MaxVersion == 21 ? 3 : 4;
+                                       /* Number of supported versions */
+
+    ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", num_versions, NULL, versions);
+  }
 
   /* job-account-id-default */
   if (!ippFindAttribute(printer->attrs, "job-account-id-default", IPP_TAG_ZERO))
@@ -5794,15 +5819,24 @@ process_ipp(_ipp_client_t *client)      /* I - Client */
     * Return an error, since we only support IPP 1.x and 2.x.
     */
 
-    respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
-                "Bad request version number %d.%d.", major, minor);
+    respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
+  }
+  else if ((major * 10 + minor) > MaxVersion)
+  {
+    if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
+      httpFlush(client->http);         /* Flush trailing (junk) data */
+
+    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
+    return (0);
   }
   else if (ippGetRequestId(client->request) <= 0)
-    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.",
-                ippGetRequestId(client->request));
+  {
+    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
+  }
   else if (!ippFirstAttribute(client->request))
-    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
-                "No attributes in request.");
+  {
+    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
+  }
   else
   {
    /*
@@ -6871,8 +6905,7 @@ usage(int status)                 /* O - Exit status */
 {
   if (!status)
   {
-    puts(CUPS_SVERSION " - Copyright 2010-2015 by Apple Inc. All rights "
-         "reserved.");
+    puts(CUPS_SVERSION " - Copyright (c) 2010-2018 by Apple Inc. All rights reserved.");
     puts("");
   }
 
@@ -6882,6 +6915,7 @@ usage(int status)                 /* O - Exit status */
   puts("-2                      Supports 2-sided printing (default=1-sided)");
   puts("-M manufacturer         Manufacturer name (default=Test)");
   puts("-P                      PIN printing mode");
+  puts("-V max-version          Set maximum supported IPP version");
   puts("-a attributes-file      Load printer attributes from file");
   puts("-c command              Run command for every print job");
   printf("-d spool-directory      Spool directory "