]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - test/ipptool.c
More localization work.
[thirdparty/cups.git] / test / ipptool.c
index bf79830ceeb07c7350ab63e1d1bed52106342d79..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.
@@ -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_not_defined;                /* 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,6 +194,7 @@ 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,
@@ -210,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 */
@@ -226,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:
@@ -247,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;
@@ -255,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;
@@ -273,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();
               }
 
@@ -302,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();
              }
@@ -314,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;
@@ -329,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();
               }
 
@@ -348,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();
               }
 
@@ -371,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;
@@ -395,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
@@ -404,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;
@@ -422,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...
@@ -439,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),
@@ -453,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
@@ -504,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);
     }
   }
@@ -606,8 +654,16 @@ 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));
+    pass = 0;
+    goto test_exit;
+  }
+
+  if (httpReconnect(http))
   {
     print_fatal_error("Unable to connect to %s on port %d - %s", vars->hostname,
                       vars->port, strerror(errno));
@@ -615,6 +671,9 @@ do_tests(_cups_vars_t *vars,                /* I - Variables */
     goto test_exit;
   }
 
+  if (vars->timeout > 0.0)
+    _httpSetTimeout(http, vars->timeout, timeout_cb, NULL);
+
  /*
   * Loop on tests...
   */
@@ -1181,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))
        {
@@ -1190,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);
@@ -1198,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"))
       {
@@ -1666,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;
@@ -1675,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 == '/')
          {
           /*
@@ -1783,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;
     }
@@ -1847,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",
@@ -2137,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],
@@ -3039,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.
  */
@@ -3409,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);
 }
 
 
@@ -3645,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.
  */
@@ -3652,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);
 }
@@ -4476,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 :
@@ -4484,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 :
@@ -4527,7 +4708,7 @@ with_value(char            *value,        /* I - Value string */
            if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
            {
              if (report)
-               print_test_error("GOT: %s[%d]=\"%s\"", attr->name, i,
+               print_test_error("GOT: %s=\"%s\"", attr->name,
                                 attr->values[i].string.text);
              else
                break;
@@ -4550,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;