]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - test/ipptool.c
More localization work.
[thirdparty/cups.git] / test / ipptool.c
index b05abc460ed0c8b321ae9664e9ff26a2b4a0efe8..2c972f9991319d162f2d3e7df46aec497340593b 100644 (file)
  *   get_variable()      - Get the value of a variable.
  *   iso_date()          - Return an ISO 8601 date/time string for the given IPP
  *                         dateTime value.
+ *   password_cb()       - Password callback for authenticated tests.
  *   print_attr()        - Print an attribute on the screen.
  *   print_col()         - Print a collection attribute on the screen.
  *   print_csv()         - Print a line of CSV text.
  *   print_fatal_error() - Print a fatal error message.
- *   print_line()        - Print a line of formatted text.
+ *   print_line()        - Print a line of formatted or CSV text.
  *   print_test_error()  - Print a test error message.
  *   print_xml_header()  - Print a standard XML plist header.
  *   print_xml_string()  - Print an XML string with escaping.
  *   print_xml_trailer() - Print the XML trailer with success/fail value.
  *   set_variable()      - Set a variable value.
+ *   timeout_cb()        - Handle HTTP timeouts.
  *   usage()             - Show program usage.
  *   validate_attr()     - Determine whether an attribute is valid.
  *   with_value()        - Test a WITH-VALUE predicate.
@@ -81,7 +83,7 @@ typedef struct _cups_expect_s         /**** Expected attribute info ****/
                *of_type,               /* Type name */
                *same_count_as,         /* Parallel attribute name */
                *if_defined,            /* Only required if variable defined */
-               *if_undefined,          /* Only required if variable is not defined */
+               *if_not_defined,        /* Only required if variable is not defined */
                *with_value,            /* Attribute must include this value */
                *define_match,          /* Variable to define on match */
                *define_no_match,       /* Variable to define on no-match */
@@ -95,7 +97,7 @@ typedef struct _cups_status_s         /**** Status info ****/
 {
   ipp_status_t status;                 /* Expected status code */
   char         *if_defined,            /* Only if variable is defined */
-               *if_undefined;          /* Only if variable is not defined */
+               *if_not_defined;        /* Only if variable is not defined */
 } _cups_status_t;
 
 typedef struct _cups_var_s             /**** Variable ****/
@@ -114,6 +116,8 @@ typedef struct _cups_vars_s         /**** Set of variables ****/
                resource[1024];         /* Resource path from URI */
   int          port;                   /* Port number from URI */
   http_encryption_t encryption;                /* Encryption for connection? */
+  double       timeout;                /* Timeout for connection */
+  int          family;                 /* Address family */
   cups_array_t *vars;                  /* Array of variables */
 } _cups_vars_t;
 
@@ -130,6 +134,7 @@ int         IgnoreErrors = 0,       /* Ignore errors? */
                Verbosity = 0,          /* Show all attributes? */
                Version = 11,           /* Default IPP version */
                XMLHeader = 0;          /* 1 if header is written */
+char           *Password = NULL;       /* Password from URI */
 const char * const URIStatusStrings[] =        /* URI status strings */
 {
   "URI too large",
@@ -167,6 +172,7 @@ static char *get_token(FILE *fp, char *buf, int buflen,
                           int *linenum);
 static char    *get_variable(_cups_vars_t *vars, const char *name);
 static char    *iso_date(ipp_uchar_t *date);
+static const char *password_cb(const char *prompt);
 static void    print_attr(ipp_attribute_t *attr);
 static void    print_col(ipp_t *col);
 static void    print_csv(ipp_attribute_t *attr, int num_displayed,
@@ -188,9 +194,11 @@ static void        print_xml_string(const char *element, const char *s);
 static void    print_xml_trailer(int success, const char *message);
 static void    set_variable(_cups_vars_t *vars, const char *name,
                             const char *value);
+static int     timeout_cb(http_t *http, void *user_data);
 static void    usage(void);
 static int     validate_attr(ipp_attribute_t *attr, int print);
-static int      with_value(char *value, int regex, ipp_attribute_t *attr);
+static int      with_value(char *value, int regex, ipp_attribute_t *attr,
+                          int report);
 
 
 /*
@@ -209,7 +217,7 @@ main(int  argc,                             /* I - Number of command-line args */
                        filename[1024], /* Real filename */
                        testname[1024]; /* Real test filename */
   const char           *testfile;      /* Test file to use */
-  int                  interval,       /* Test interval */
+  int                  interval,       /* Test interval in microseconds */
                        repeat;         /* Repeat count */
   _cups_vars_t         vars;           /* Variables */
   http_uri_status_t    uri_status;     /* URI separation status */
@@ -225,7 +233,8 @@ main(int  argc,                             /* I - Number of command-line args */
   _cupsSetLocale(argv);
 
   memset(&vars, 0, sizeof(vars));
-  vars.vars = cupsArrayNew((cups_array_func_t)compare_vars, NULL);
+  vars.family = AF_UNSPEC;
+  vars.vars   = cupsArrayNew((cups_array_func_t)compare_vars, NULL);
 
  /*
   * We need at least:
@@ -246,6 +255,16 @@ main(int  argc,                            /* I - Number of command-line args */
       {
         switch (*opt)
         {
+         case '4' : /* Connect using IPv4 only */
+             vars.family = AF_INET;
+             break;
+
+#ifdef AF_INET6
+         case '6' : /* Connect using IPv6 only */
+             vars.family = AF_INET6;
+             break;
+#endif /* AF_INET6 */
+
           case 'C' : /* Enable HTTP chunking */
               Transfer = _CUPS_TRANSFER_CHUNKED;
               break;
@@ -254,8 +273,7 @@ main(int  argc,                             /* I - Number of command-line args */
 #ifdef HAVE_SSL
              vars.encryption = HTTP_ENCRYPT_REQUIRED;
 #else
-             _cupsLangPrintf(stderr,
-                             _("%s: Sorry, no encryption support compiled in\n"),
+             _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
                              argv[0]);
 #endif /* HAVE_SSL */
              break;
@@ -272,19 +290,31 @@ main(int  argc,                           /* I - Number of command-line args */
 #ifdef HAVE_SSL
              vars.encryption = HTTP_ENCRYPT_ALWAYS;
 #else
-             _cupsLangPrintf(stderr,
-                             _("%s: Sorry, no encryption support compiled in\n"),
+             _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
                              argv[0]);
 #endif /* HAVE_SSL */
              break;
 
+         case 'T' : /* Set timeout */
+             i ++;
+
+             if (i >= argc)
+             {
+               _cupsLangPuts(stderr,
+                             _("ipptool: Missing timeout for \"-T\"."));
+               usage();
+              }
+
+             vars.timeout = _cupsStrScand(argv[i], NULL, localeconv());
+             break;
+
          case 'V' : /* Set IPP version */
              i ++;
 
              if (i >= argc)
              {
                _cupsLangPuts(stderr,
-                             _("ipptool: Missing version for \"-V\".\n"));
+                             _("ipptool: Missing version for \"-V\"."));
                usage();
               }
 
@@ -301,7 +331,7 @@ main(int  argc,                             /* I - Number of command-line args */
              else
              {
                _cupsLangPrintf(stderr,
-                               _("ipptool: Bad version %s for \"-V\".\n"),
+                               _("ipptool: Bad version %s for \"-V\"."),
                                argv[i]);
                usage();
              }
@@ -313,7 +343,7 @@ main(int  argc,                             /* I - Number of command-line args */
               if (interval || repeat)
              {
                _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are "
-                                       "incompatible with -X\".\n"));
+                                       "incompatible with -X\"."));
                usage();
              }
              break;
@@ -328,7 +358,7 @@ main(int  argc,                             /* I - Number of command-line args */
              if (i >= argc)
              {
                _cupsLangPuts(stderr,
-                             _("ipptool: Missing name=value for \"-d\".\n"));
+                             _("ipptool: Missing name=value for \"-d\"."));
                usage();
               }
 
@@ -347,7 +377,7 @@ main(int  argc,                             /* I - Number of command-line args */
              if (i >= argc)
              {
                _cupsLangPuts(stderr,
-                             _("ipptool: Missing filename for \"-f\".\n"));
+                             _("ipptool: Missing filename for \"-f\"."));
                usage();
               }
 
@@ -370,16 +400,25 @@ main(int  argc,                           /* I - Number of command-line args */
              if (i >= argc)
              {
                _cupsLangPuts(stderr,
-                             _("ipptool: Missing seconds for \"-i\".\n"));
+                             _("ipptool: Missing seconds for \"-i\"."));
                usage();
               }
              else
-               interval = atoi(argv[i]);
+             {
+               interval = (int)(_cupsStrScand(argv[i], NULL, localeconv()) *
+                                1000000.0);
+               if (interval <= 0)
+               {
+                 _cupsLangPuts(stderr,
+                               _("ipptool: Invalid seconds for \"-i\"."));
+                 usage();
+               }
+              }
 
               if (Output == _CUPS_OUTPUT_PLIST && interval)
              {
                _cupsLangPuts(stderr, _("ipptool: \"-i\" is incompatible with "
-                                       "\"-X\".\n"));
+                                       "\"-X\"."));
                usage();
              }
              break;
@@ -394,7 +433,7 @@ main(int  argc,                             /* I - Number of command-line args */
              if (i >= argc)
              {
                _cupsLangPuts(stderr,
-                             _("ipptool: Missing count for \"-n\".\n"));
+                             _("ipptool: Missing count for \"-n\"."));
                usage();
               }
              else
@@ -403,7 +442,7 @@ main(int  argc,                             /* I - Number of command-line args */
               if (Output == _CUPS_OUTPUT_PLIST && repeat)
              {
                _cupsLangPuts(stderr, _("ipptool: \"-n\" is incompatible with "
-                                       "\"-X\".\n"));
+                                       "\"-X\"."));
                usage();
              }
              break;
@@ -421,16 +460,19 @@ main(int  argc,                           /* I - Number of command-line args */
              break;
 
          default :
-             _cupsLangPrintf(stderr, _("ipptool: Unknown option \"-%c\".\n"),
+             _cupsLangPrintf(stderr, _("ipptool: Unknown option \"-%c\"."),
                              *opt);
              usage();
              break;
        }
       }
     }
-    else if (!strncmp(argv[i], "ipp://", 6) ||
-             !strncmp(argv[i], "http://", 7) ||
-             !strncmp(argv[i], "https://", 8))
+    else if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "http://", 7)
+#ifdef HAVE_SSL
+            || !strncmp(argv[i], "ipps://", 7)
+            || !strncmp(argv[i], "https://", 8)
+#endif /* HAVE_SSL */
+            )
     {
      /*
       * Set URI...
@@ -438,10 +480,15 @@ main(int  argc,                           /* I - Number of command-line args */
 
       if (vars.uri)
       {
-        _cupsLangPuts(stderr, _("ipptool: May only specify a single URI.\n"));
+        _cupsLangPuts(stderr, _("ipptool: May only specify a single URI."));
         usage();
       }
 
+#ifdef HAVE_SSL
+      if (!strncmp(argv[i], "ipps://", 7) || !strncmp(argv[i], "https://", 8))
+        vars.encryption = HTTP_ENCRYPT_ALWAYS;
+#endif /* HAVE_SSL */
+
       vars.uri   = argv[i];
       uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, vars.uri,
                                    vars.scheme, sizeof(vars.scheme),
@@ -452,17 +499,19 @@ main(int  argc,                           /* I - Number of command-line args */
 
       if (uri_status != HTTP_URI_OK)
       {
-        _cupsLangPrintf(stderr, _("ipptool: Bad URI - %s.\n"),
+        _cupsLangPrintf(stderr, _("ipptool: Bad URI - %s."),
                        URIStatusStrings[uri_status - HTTP_URI_OVERFLOW]);
         return (1);
       }
 
-      if (strcmp(vars.scheme, "http") && strcmp(vars.scheme, "https") &&
-          strcmp(vars.scheme, "ipp"))
+      if (vars.userpass[0])
       {
-        _cupsLangPuts(stderr, _("ipptool: Only http, https, and ipp URIs are "
-                               "supported."));
-       return (1);
+        if ((Password = strchr(vars.userpass, ':')) != NULL)
+         *Password++ = '\0';
+
+        cupsSetUser(vars.userpass);
+       cupsSetPasswordCB(password_cb);
+       set_variable(&vars, "uriuser", vars.userpass);
       }
     }
     else
@@ -503,20 +552,20 @@ main(int  argc,                           /* I - Number of command-line args */
 
   if (Output == _CUPS_OUTPUT_PLIST)
     print_xml_trailer(!status, NULL);
-  else if (interval && repeat > 0)
+  else if (interval > 0 && repeat > 0)
   {
     while (repeat > 1)
     {
-      sleep(interval);
+      usleep(interval);
       do_tests(&vars, testfile);
       repeat --;
     }
   }
-  else if (interval)
+  else if (interval > 0)
   {
     for (;;)
     {
-      sleep(interval);
+      usleep(interval);
       do_tests(&vars, testfile);
     }
   }
@@ -605,8 +654,8 @@ do_tests(_cups_vars_t *vars,                /* I - Variables */
   * Connect to the server...
   */
 
-  if ((http = httpConnectEncrypt(vars->hostname, vars->port,
-                                 vars->encryption)) == NULL)
+  if ((http = _httpCreate(vars->hostname, vars->port, vars->encryption,
+                         vars->family)) == NULL)
   {
     print_fatal_error("Unable to connect to %s on port %d - %s", vars->hostname,
                       vars->port, strerror(errno));
@@ -614,6 +663,17 @@ do_tests(_cups_vars_t *vars,               /* I - Variables */
     goto test_exit;
   }
 
+  if (httpReconnect(http))
+  {
+    print_fatal_error("Unable to connect to %s on port %d - %s", vars->hostname,
+                      vars->port, strerror(errno));
+    pass = 0;
+    goto test_exit;
+  }
+
+  if (vars->timeout > 0.0)
+    _httpSetTimeout(http, vars->timeout, timeout_cb, NULL);
+
  /*
   * Loop on tests...
   */
@@ -855,7 +915,7 @@ do_tests(_cups_vars_t *vars,                /* I - Variables */
           strcasecmp(token, "DEFINE-NO-MATCH") &&
           strcasecmp(token, "DEFINE-VALUE") &&
           strcasecmp(token, "IF-DEFINED") &&
-          strcasecmp(token, "IF-UNDEFINED") &&
+          strcasecmp(token, "IF-NOT-DEFINED") &&
           strcasecmp(token, "IN-GROUP") &&
           strcasecmp(token, "OF-TYPE") &&
           strcasecmp(token, "SAME-COUNT-AS") &&
@@ -863,7 +923,7 @@ do_tests(_cups_vars_t *vars,                /* I - Variables */
         last_expect = NULL;
 
       if (strcasecmp(token, "IF-DEFINED") &&
-          strcasecmp(token, "IF-UNDEFINED"))
+          strcasecmp(token, "IF-NOT-DEFINED"))
         last_status = NULL;
 
       if (!strcmp(token, "}"))
@@ -1180,7 +1240,7 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
         * Delay before operation...
        */
 
-        int delay;
+        double delay;
 
        if (!get_token(fp, token, sizeof(token), &linenum))
        {
@@ -1189,7 +1249,7 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
          goto test_exit;
        }
 
-       if ((delay = atoi(token)) <= 0)
+       if ((delay = _cupsStrScand(token, NULL, localeconv())) <= 0.0)
        {
          print_fatal_error("Bad DELAY value \"%s\" on line %d.", token,
                            linenum);
@@ -1197,7 +1257,12 @@ do_tests(_cups_vars_t *vars,             /* I - Variables */
          goto test_exit;
        }
        else
-         sleep(delay);
+       {
+         if (Output == _CUPS_OUTPUT_TEST)
+           printf("    [%g second delay]\n", delay);
+
+         usleep((int)(1000000.0 * delay));
+       }
       }
       else if (!strcasecmp(token, "ATTR"))
       {
@@ -1418,7 +1483,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
          goto test_exit;
        }
 
-       if ((statuses[num_statuses].status = ippErrorValue(token)) < 0)
+       if ((statuses[num_statuses].status = ippErrorValue(token)) < 0 &&
+           (statuses[num_statuses].status = strtol(token, NULL, 0)) == 0)
        {
          print_fatal_error("Bad STATUS code \"%s\" on line %d.", token,
                            linenum);
@@ -1430,7 +1496,7 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
        num_statuses ++;
 
        last_status->if_defined   = NULL;
-       last_status->if_undefined = NULL;
+       last_status->if_not_defined = NULL;
       }
       else if (!strcasecmp(token, "EXPECT"))
       {
@@ -1641,22 +1707,22 @@ do_tests(_cups_vars_t *vars,            /* I - Variables */
          goto test_exit;
        }
       }
-      else if (!strcasecmp(token, "IF-UNDEFINED"))
+      else if (!strcasecmp(token, "IF-NOT-DEFINED"))
       {
        if (!get_token(fp, token, sizeof(token), &linenum))
        {
-         print_fatal_error("Missing IF-UNDEFINED name on line %d.", linenum);
+         print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum);
          pass = 0;
          goto test_exit;
        }
 
        if (last_expect)
-         last_expect->if_undefined = strdup(token);
+         last_expect->if_not_defined = strdup(token);
        else if (last_status)
-         last_status->if_undefined = strdup(token);
+         last_status->if_not_defined = strdup(token);
        else
        {
-         print_fatal_error("IF-UNDEFINED without a preceding EXPECT or STATUS "
+         print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
                            "on line %d.", linenum);
          pass = 0;
          goto test_exit;
@@ -1664,7 +1730,7 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
       }
       else if (!strcasecmp(token, "WITH-VALUE"))
       {
-       if (!get_token(fp, token, sizeof(token), &linenum))
+       if (!get_token(fp, temp, sizeof(temp), &linenum))
        {
          print_fatal_error("Missing WITH-VALUE value on line %d.", linenum);
          pass = 0;
@@ -1673,7 +1739,14 @@ do_tests(_cups_vars_t *vars,             /* I - Variables */
 
         if (last_expect)
        {
+        /*
+         * Expand any variables in the value and then save it.
+         */
+
+         expand_variables(vars, token, temp, sizeof(token));
+
          tokenptr = token + strlen(token) - 1;
+
          if (token[0] == '/' && tokenptr > token && *tokenptr == '/')
          {
           /*
@@ -1781,13 +1854,13 @@ do_tests(_cups_vars_t *vars,            /* I - Variables */
        puts("<key>Successful</key>");
        puts("<true />");
        puts("<key>StatusCode</key>");
-       print_xml_string("string", "skipped");
+       print_xml_string("string", "skip");
        puts("<key>ResponseAttributes</key>");
        puts("<dict>");
        puts("</dict>");
       }
       else if (Output == _CUPS_OUTPUT_TEST)
-       puts("SKIPPED]");
+       puts("SKIP]");
 
       goto skip_error;
     }
@@ -1845,9 +1918,12 @@ do_tests(_cups_vars_t *vars,             /* I - Variables */
       if (http->version != HTTP_1_1)
         prev_pass = pass = 0;
 
-      if (response->request.status.version[0] != (version / 10) ||
-         response->request.status.version[1] != (version % 10) ||
-         response->request.status.request_id != request_id)
+      if (response->request.status.request_id != request_id)
+        prev_pass = pass = 0;
+
+      if (version &&
+          (response->request.status.version[0] != (version / 10) ||
+          response->request.status.version[1] != (version % 10)))
         prev_pass = pass = 0;
 
       if ((attrptr = ippFindAttribute(response, "job-id",
@@ -1928,8 +2004,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
            !get_variable(vars, statuses[i].if_defined))
          continue;
 
-        if (statuses[i].if_undefined &&
-           get_variable(vars, statuses[i].if_undefined))
+        if (statuses[i].if_not_defined &&
+           get_variable(vars, statuses[i].if_not_defined))
          continue;
 
         if (response->request.status.status_code == statuses[i].status)
@@ -1945,7 +2021,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
           if (expect->if_defined && !get_variable(vars, expect->if_defined))
             continue;
 
-          if (expect->if_undefined && get_variable(vars, expect->if_undefined))
+          if (expect->if_not_defined &&
+             get_variable(vars, expect->if_not_defined))
             continue;
 
           found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
@@ -1958,18 +2035,18 @@ do_tests(_cups_vars_t *vars,            /* I - Variables */
           {
            if (expect->define_no_match)
              set_variable(vars, expect->define_no_match, "1");
-           else
+           else if (!expect->define_match)
              prev_pass = pass = 0;
 
            continue;
           }
 
           if (found &&
-             !with_value(expect->with_value, expect->with_regex, found))
+             !with_value(expect->with_value, expect->with_regex, found, 0))
           {
            if (expect->define_no_match)
              set_variable(vars, expect->define_no_match, "1");
-           else
+           else if (!expect->define_match)
              prev_pass = pass = 0;
 
             continue;
@@ -1979,7 +2056,7 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
          {
            if (expect->define_no_match)
              set_variable(vars, expect->define_no_match, "1");
-           else
+           else if (!expect->define_match)
              prev_pass = pass = 0;
 
             continue;
@@ -1994,7 +2071,7 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
             {
              if (expect->define_no_match)
                set_variable(vars, expect->define_no_match, "1");
-             else
+             else if (!expect->define_match)
                prev_pass = pass = 0;
 
               continue;
@@ -2134,8 +2211,9 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
                         cupsLastErrorString());
       else
       {
-       if (response->request.status.version[0] != (version / 10) ||
-           response->request.status.version[1] != (version % 10))
+       if (version &&
+           (response->request.status.version[0] != (version / 10) ||
+            response->request.status.version[1] != (version % 10)))
           print_test_error("Bad version %d.%d in response - expected %d.%d "
                           "(RFC 2911 section 3.1.8).",
                           response->request.status.version[0],
@@ -2252,8 +2330,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
              !get_variable(vars, statuses[i].if_defined))
            continue;
 
-         if (statuses[i].if_undefined &&
-             get_variable(vars, statuses[i].if_undefined))
+         if (statuses[i].if_not_defined &&
+             get_variable(vars, statuses[i].if_not_defined))
            continue;
 
          if (response->request.status.status_code == statuses[i].status)
@@ -2269,13 +2347,14 @@ do_tests(_cups_vars_t *vars,            /* I - Variables */
 
        for (i = num_expects, expect = expects; i > 0; i --, expect ++)
        {
-         if (expect->define_no_match)
+         if (expect->define_match || expect->define_no_match)
            continue;
 
          if (expect->if_defined && !get_variable(vars, expect->if_defined))
            continue;
 
-         if (expect->if_undefined && get_variable(vars, expect->if_undefined))
+         if (expect->if_not_defined &&
+             get_variable(vars, expect->if_not_defined))
            continue;
 
          found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
@@ -2296,7 +2375,7 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
                               expect->name, ippTagString(expect->in_group),
                               ippTagString(found->group_tag));
 
-           if (!with_value(expect->with_value, expect->with_regex, found))
+           if (!with_value(expect->with_value, expect->with_regex, found, 0))
            {
              if (expect->with_regex)
                print_test_error("EXPECTED: %s WITH-VALUE /%s/",
@@ -2304,6 +2383,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
              else
                print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
                                 expect->name, expect->with_value);
+
+             with_value(expect->with_value, expect->with_regex, found, 1);
            }
 
            if (expect->count > 0 && found->num_values != expect->count)
@@ -2346,8 +2427,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
     {
       if (statuses[i].if_defined)
         free(statuses[i].if_defined);
-      if (statuses[i].if_undefined)
-        free(statuses[i].if_undefined);
+      if (statuses[i].if_not_defined)
+        free(statuses[i].if_not_defined);
     }
     num_statuses = 0;
 
@@ -2360,8 +2441,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
         free(expect->same_count_as);
       if (expect->if_defined)
         free(expect->if_defined);
-      if (expect->if_undefined)
-        free(expect->if_undefined);
+      if (expect->if_not_defined)
+        free(expect->if_not_defined);
       if (expect->with_value)
         free(expect->with_value);
       if (expect->define_match)
@@ -2394,8 +2475,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
   {
     if (statuses[i].if_defined)
       free(statuses[i].if_defined);
-    if (statuses[i].if_undefined)
-      free(statuses[i].if_undefined);
+    if (statuses[i].if_not_defined)
+      free(statuses[i].if_not_defined);
   }
 
   for (i = num_expects, expect = expects; i > 0; i --, expect ++)
@@ -2407,8 +2488,8 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
       free(expect->same_count_as);
     if (expect->if_defined)
       free(expect->if_defined);
-    if (expect->if_undefined)
-      free(expect->if_undefined);
+    if (expect->if_not_defined)
+      free(expect->if_not_defined);
     if (expect->with_value)
       free(expect->with_value);
     if (expect->define_match)
@@ -3033,6 +3114,19 @@ iso_date(ipp_uchar_t *date)              /* I - IPP (RFC 1903) date/time value */
 }
 
 
+/*
+ * 'password_cb()' - Password callback for authenticated tests.
+ */
+
+static const char *                    /* O - Password */
+password_cb(const char *prompt)                /* I - Prompt (unused) */
+{
+  (void)prompt;
+
+  return (Password);
+}
+
+
 /*
  * 'print_attr()' - Print an attribute on the screen.
  */
@@ -3403,7 +3497,7 @@ print_fatal_error(const char *s,  /* I - Printf-style format string */
     print_xml_trailer(0, buffer);
   }
   else
-    _cupsLangPrintf(stderr, "ipptool: %s\n", buffer);
+    _cupsLangPrintf(stderr, "ipptool: %s", buffer);
 }
 
 
@@ -3639,6 +3733,21 @@ set_variable(_cups_vars_t *vars, /* I - Variables */
 }
 
 
+/*
+ * 'timeout_cb()' - Handle HTTP timeouts.
+ */
+
+static int                             /* O - 1 to continue, 0 to cancel */
+timeout_cb(http_t *http,               /* I - Connection to server (unused) */
+           void   *user_data)          /* I - User data (unused) */
+{
+  (void)http;
+  (void)user_data;
+
+  return (0);
+}
+
+
 /*
  * 'usage()' - Show program usage.
  */
@@ -3646,28 +3755,34 @@ set_variable(_cups_vars_t *vars,        /* I - Variables */
 static void
 usage(void)
 {
-  _cupsLangPuts(stderr,
-                _("Usage: ipptool [options] URI filename [ ... "
-                 "filenameN ]\n"
-                 "\n"
-                 "Options:\n"
-                 "\n"
-                 "-C             Send requests using chunking (default)\n"
-                 "-E             Test with TLS encryption.\n"
-                 "-I             Ignore errors\n"
-                 "-L             Send requests using content-length\n"
-                 "-S             Test with SSL encryption.\n"
-                 "-V version     Set default IPP version.\n"
-                 "-X             Produce XML plist instead of plain text.\n"
-                 "-d name=value  Define variable.\n"
-                 "-f filename    Set default request filename.\n"
-                 "-i seconds     Repeat the last file with the given time "
-                 "interval.\n"
-                 "-n count       Repeat the last file the given number of "
-                 "times.\n"
-                 "-q             Be quiet - no output except errors.\n"
-                 "-t             Produce a test report.\n"
-                 "-v             Show all attributes sent and received.\n"));
+  _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... "
+                         "filenameN ]"));
+  _cupsLangPuts(stderr, _("Options:"));
+  _cupsLangPuts(stderr, _("  -4             Connect using IPv4."));
+  _cupsLangPuts(stderr, _("  -6             Connect using IPv6."));
+  _cupsLangPuts(stderr, _("  -C             Send requests using chunking "
+                          "(default)."));
+  _cupsLangPuts(stderr, _("  -E             Test with TLS encryption."));
+  _cupsLangPuts(stderr, _("  -I             Ignore errors."));
+  _cupsLangPuts(stderr, _("  -L             Send requests using "
+                          "content-length."));
+  _cupsLangPuts(stderr, _("  -S             Test with SSL encryption."));
+  _cupsLangPuts(stderr, _("  -T             Set the receive/send timeout in "
+                          "seconds."));
+  _cupsLangPuts(stderr, _("  -V version     Set default IPP version."));
+  _cupsLangPuts(stderr, _("  -X             Produce XML plist instead of plain "
+                          "text."));
+  _cupsLangPuts(stderr, _("  -d name=value  Define variable."));
+  _cupsLangPuts(stderr, _("  -f filename    Set default request filename."));
+  _cupsLangPuts(stderr, _("  -i seconds     Repeat the last file with the "
+                          "given time interval."));
+  _cupsLangPuts(stderr, _("  -n count       Repeat the last file the given "
+                          "number of times."));
+  _cupsLangPuts(stderr, _("  -q             Be quiet - no output except "
+                          "errors."));
+  _cupsLangPuts(stderr, _("  -t             Produce a test report."));
+  _cupsLangPuts(stderr, _("  -v             Show all attributes sent and "
+                          "received."));
 
   exit(1);
 }
@@ -4400,7 +4515,8 @@ validate_attr(ipp_attribute_t *attr,      /* I - Attribute to validate */
 static int                             /* O - 1 on match, 0 on non-match */
 with_value(char            *value,     /* I - Value string */
            int             regex,      /* I - Value is a regular expression */
-           ipp_attribute_t *attr)      /* I - Attribute to compare */
+           ipp_attribute_t *attr,      /* I - Attribute to compare */
+          int             report)      /* I - 1 = report failures */
 {
   int  i;                              /* Looping var */
   char *valptr;                        /* Pointer into value */
@@ -4469,6 +4585,71 @@ with_value(char            *value,       /* I - Value string */
            }
          }
         }
+
+       if (report)
+       {
+         for (i = 0; i < attr->num_values; i ++)
+           print_test_error("GOT: %s=%d", attr->name, attr->values[i].integer);
+       }
+       break;
+
+    case IPP_TAG_RANGE :
+        for (i = 0; i < attr->num_values; i ++)
+        {
+         char  op,                     /* Comparison operator */
+               *nextptr;               /* Next pointer */
+         int   intvalue;               /* Integer value */
+
+
+          valptr = value;
+         if (!strncmp(valptr, "no-value,", 9))
+           valptr += 9;
+
+         while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
+                *valptr == '-' || *valptr == ',' || *valptr == '<' ||
+                *valptr == '=' || *valptr == '>')
+         {
+           op = '=';
+           while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
+           {
+             if (*valptr == '<' || *valptr == '>' || *valptr == '=')
+               op = *valptr;
+             valptr ++;
+           }
+
+            if (!*valptr)
+             break;
+
+           intvalue = strtol(valptr, &nextptr, 0);
+           if (nextptr == valptr)
+             break;
+           valptr = nextptr;
+
+           switch (op)
+           {
+             case '=' :
+                 if (attr->values[i].range.upper == intvalue)
+                   return (1);
+                 break;
+             case '<' :
+                 if (attr->values[i].range.upper < intvalue)
+                   return (1);
+                 break;
+             case '>' :
+                 if (attr->values[i].range.upper > intvalue)
+                   return (1);
+                 break;
+           }
+         }
+        }
+
+       if (report)
+       {
+         for (i = 0; i < attr->num_values; i ++)
+           print_test_error("GOT: %s=%d-%d", attr->name,
+                            attr->values[i].range.lower,
+                            attr->values[i].range.upper);
+       }
        break;
 
     case IPP_TAG_BOOLEAN :
@@ -4477,6 +4658,13 @@ with_value(char            *value,       /* I - Value string */
           if (!strcmp(value, "true") == attr->values[i].boolean)
            return (1);
        }
+
+       if (report)
+       {
+         for (i = 0; i < attr->num_values; i ++)
+           print_test_error("GOT: %s=%s", attr->name,
+                            attr->values[i].boolean ? "true" : "false");
+       }
        break;
 
     case IPP_TAG_NOVALUE :
@@ -4518,7 +4706,13 @@ with_value(char            *value,       /* I - Value string */
          for (i = 0; i < attr->num_values; i ++)
          {
            if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
-             break;
+           {
+             if (report)
+               print_test_error("GOT: %s=\"%s\"", attr->name,
+                                attr->values[i].string.text);
+             else
+               break;
+           }
          }
 
          regfree(&re);
@@ -4537,6 +4731,13 @@ with_value(char            *value,       /* I - Value string */
            if (!strcmp(value, attr->values[i].string.text))
              return (1);
          }
+
+         if (report)
+         {
+           for (i = 0; i < attr->num_values; i ++)
+             print_test_error("GOT: %s=\"%s\"", attr->name,
+                              attr->values[i].string.text);
+         }
        }
        break;