blob: 252e1dffdff20be626493ba9e2d255ebe6ebce0c [file]
/*
* "$Id: ipptest.c 5878 2006-08-24 15:55:42Z mike $"
*
* IPP test command for the Common UNIX Printing System (CUPS).
*
* Copyright 1997-2006 by Easy Software Products.
*
* These coded instructions, statements, and computer programs are the
* property of Easy Software Products and are protected by Federal
* copyright law. Distribution and use rights are outlined in the file
* "LICENSE.txt" which should have been included with this file. If this
* file is missing or damaged please contact Easy Software Products
* at:
*
* Attn: CUPS Licensing Information
* Easy Software Products
* 44141 Airport View Drive, Suite 204
* Hollywood, Maryland 20636 USA
*
* Voice: (301) 373-9600
* EMail: cups-info@cups.org
* WWW: http://www.cups.org
*
* Contents:
*
* main() - Parse options and do tests.
* do_tests() - Do tests as specified in the test file.
* get_tag() - Get an IPP value or group tag from a name...
* get_token() - Get a token from a file.
* print_attr() - Print an attribute on the screen.
* usage() - Show program usage.
*/
/*
* Include necessary headers...
*/
#include <stdio.h>
#include <stdlib.h>
#include <cups/string.h>
#include <errno.h>
#include <ctype.h>
#include <cups/cups.h>
#include <cups/language.h>
/*
* Globals...
*/
int Verbosity = 0; /* Show all attributes? */
const char * const TagNames[] =
{ /* Value/group tag names */
"zero", /* 0x00 */
"operation", /* 0x01 */
"job", /* 0x02 */
"end", /* 0x03 */
"printer", /* 0x04 */
"unsupported-group", /* 0x05 */
"subscription", /* 0x06 */
"event-notification", /* 0x07 */
"", "", "", "", "", "", "", "",
"unsupported-value", /* 0x10 */
"default", /* 0x11 */
"unknown", /* 0x12 */
"novalue", /* 0x13 */
"",
"notsettable", /* 0x15 */
"deleteattr", /* 0x16 */
"anyvalue", /* 0x17 */
"", "", "", "", "", "", "", "", "",
"integer", /* 0x21 */
"boolean", /* 0x22 */
"enum", /* 0x23 */
"", "", "", "", "", "", "", "", "", "", "", "",
"string", /* 0x30 */
"date", /* 0x31 */
"resolution", /* 0x32 */
"range", /* 0x33 */
"collection", /* 0x34 */
"textlang", /* 0x35 */
"namelang", /* 0x36 */
"", "", "", "", "", "", "", "", "", "",
"text", /* 0x41 */
"name", /* 0x42 */
"",
"keyword", /* 0x44 */
"uri", /* 0x45 */
"urischeme", /* 0x46 */
"charset", /* 0x47 */
"language", /* 0x48 */
"mimetype" /* 0x49 */
};
/*
* Local functions...
*/
int do_tests(const char *, const char *);
ipp_op_t ippOpValue(const char *);
ipp_status_t ippErrorValue(const char *);
ipp_tag_t get_tag(const char *);
const char *get_tag_string(ipp_tag_t tag);
char *get_token(FILE *, char *, int, int *linenum);
void print_attr(ipp_attribute_t *);
void usage(const char *option);
/*
* 'main()' - Parse options and do tests.
*/
int /* O - Exit status */
main(int argc, /* I - Number of command-line arguments */
char *argv[]) /* I - Command-line arguments */
{
int i; /* Looping var */
int status; /* Status of tests... */
const char *uri; /* URI to use */
const char *testfile; /* Test file to use */
int interval; /* Test interval */
/*
* We need at least:
*
* testipp URL testfile
*/
uri = NULL;
testfile = NULL;
status = 0;
interval = 0;
for (i = 1; i < argc; i ++)
{
if (argv[i][0] == '-')
{
if (!strcmp(argv[i], "-v"))
Verbosity ++;
else if (!strcmp(argv[i], "-i"))
{
i++;
if (i >= argc)
usage(NULL);
else
interval = atoi(argv[i]);
}
else
usage(argv[i]);
}
else if (!strncmp(argv[i], "ipp://", 6) ||
!strncmp(argv[i], "http://", 7) ||
!strncmp(argv[i], "https://", 8))
{
/*
* Set URI...
*/
if (!testfile && uri)
usage(NULL);
uri = argv[i];
testfile = NULL;
}
else
{
/*
* Run test...
*/
testfile = argv[i];
if (!do_tests(uri, testfile))
status ++;
}
}
if (!uri || !testfile)
usage(NULL);
/*
* Loop if the interval is set...
*/
if (interval)
{
for (;;)
{
sleep(interval);
do_tests(uri, testfile);
}
}
/*
* Exit...
*/
return (status);
}
/*
* 'do_tests()' - Do tests as specified in the test file.
*/
int /* 1 = success, 0 = failure */
do_tests(const char *uri, /* I - URI to connect on */
const char *testfile) /* I - Test file to use */
{
int i; /* Looping var */
int linenum; /* Current line number */
int version; /* IPP version number to use */
http_t *http; /* HTTP connection to server */
char method[HTTP_MAX_URI], /* URI method */
userpass[HTTP_MAX_URI], /* username:password */
server[HTTP_MAX_URI], /* Server */
resource[HTTP_MAX_URI]; /* Resource path */
int port; /* Port number */
FILE *fp; /* Test file */
char token[1024], /* Token from file */
*tokenptr, /* Pointer into token */
temp[1024], /* Temporary string */
*tempptr; /* Pointer into temp string */
ipp_t *request; /* IPP request */
ipp_t *response; /* IPP response */
ipp_op_t op; /* Operation */
ipp_tag_t group; /* Current group */
ipp_tag_t value; /* Current value type */
ipp_attribute_t *attrptr; /* Attribute pointer */
char attr[128]; /* Attribute name */
int num_statuses; /* Number of valid status codes */
ipp_status_t statuses[100]; /* Valid status codes */
int num_expects; /* Number of expected attributes */
char *expects[100]; /* Expected attributes */
int num_displayed; /* Number of displayed attributes */
char *displayed[100]; /* Displayed attributes */
char name[1024]; /* Name of test */
char filename[1024]; /* Filename */
int pass; /* Did we pass the test? */
int job_id; /* Job ID from last operation */
int subscription_id; /* Subscription ID from last operation */
/*
* Open the test file...
*/
if ((fp = fopen(testfile, "r")) == NULL)
{
printf("Unable to open test file %s - %s\n", testfile, strerror(errno));
return (0);
}
/*
* Connect to the server...
*/
httpSeparateURI(HTTP_URI_CODING_ALL, uri, method, sizeof(method), userpass,
sizeof(userpass), server, sizeof(server), &port, resource,
sizeof(resource));
if ((http = httpConnect(server, port)) == NULL)
{
printf("Unable to connect to %s on port %d - %s\n", server, port,
strerror(errno));
return (0);
}
/*
* Loop on tests...
*/
printf("\"%s\":\n", testfile);
pass = 1;
job_id = 0;
subscription_id = 0;
version = 1;
linenum = 1;
while (get_token(fp, token, sizeof(token), &linenum) != NULL)
{
/*
* Expect an open brace...
*/
if (strcmp(token, "{"))
{
printf("Unexpected token %s seen on line %d - aborting test!\n", token,
linenum);
httpClose(http);
return (0);
}
/*
* Initialize things...
*/
httpSeparateURI(HTTP_URI_CODING_ALL, uri, method, sizeof(method), userpass,
sizeof(userpass), server, sizeof(server), &port, resource,
sizeof(resource));
request = ippNew();
op = (ipp_op_t)0;
group = IPP_TAG_ZERO;
value = IPP_TAG_ZERO;
num_statuses = 0;
num_expects = 0;
num_displayed = 0;
filename[0] = '\0';
strcpy(name, testfile);
if (strrchr(name, '.') != NULL)
*strrchr(name, '.') = '\0';
/*
* Parse until we see a close brace...
*/
while (get_token(fp, token, sizeof(token), &linenum) != NULL)
{
if (!strcmp(token, "}"))
break;
else if (!strcasecmp(token, "NAME"))
{
/*
* Name of test...
*/
get_token(fp, name, sizeof(name), &linenum);
}
else if (!strcasecmp(token, "VERSION"))
{
/*
* IPP version number for test...
*/
get_token(fp, temp, sizeof(temp), &linenum);
sscanf(temp, "%*d.%d", &version);
}
else if (!strcasecmp(token, "RESOURCE"))
{
/*
* Resource name...
*/
get_token(fp, resource, sizeof(resource), &linenum);
}
else if (!strcasecmp(token, "OPERATION"))
{
/*
* Operation...
*/
get_token(fp, token, sizeof(token), &linenum);
op = ippOpValue(token);
}
else if (!strcasecmp(token, "GROUP"))
{
/*
* Attribute group...
*/
get_token(fp, token, sizeof(token), &linenum);
value = get_tag(token);
if (value == group)
ippAddSeparator(request);
group = value;
}
else if (!strcasecmp(token, "DELAY"))
{
/*
* Delay before operation...
*/
int delay;
get_token(fp, token, sizeof(token), &linenum);
if ((delay = atoi(token)) > 0)
sleep(delay);
}
else if (!strcasecmp(token, "ATTR"))
{
/*
* Attribute...
*/
get_token(fp, token, sizeof(token), &linenum);
value = get_tag(token);
get_token(fp, attr, sizeof(attr), &linenum);
get_token(fp, temp, sizeof(temp), &linenum);
token[sizeof(token) - 1] = '\0';
for (tempptr = temp, tokenptr = token;
*tempptr && tokenptr < (token + sizeof(token) - 1);)
if (*tempptr == '$')
{
/*
* Substitute a string/number...
*/
if (!strncasecmp(tempptr + 1, "uri", 3))
{
strlcpy(tokenptr, uri, sizeof(token) - (tokenptr - token));
tempptr += 4;
}
else if (!strncasecmp(tempptr + 1, "method", 6))
{
strlcpy(tokenptr, method, sizeof(token) - (tokenptr - token));
tempptr += 7;
}
else if (!strncasecmp(tempptr + 1, "username", 8))
{
strlcpy(tokenptr, userpass, sizeof(token) - (tokenptr - token));
tempptr += 9;
}
else if (!strncasecmp(tempptr + 1, "hostname", 8))
{
strlcpy(tokenptr, server, sizeof(token) - (tokenptr - token));
tempptr += 9;
}
else if (!strncasecmp(tempptr + 1, "port", 4))
{
snprintf(tokenptr, sizeof(token) - (tokenptr - token),
"%d", port);
tempptr += 5;
}
else if (!strncasecmp(tempptr + 1, "resource", 8))
{
strlcpy(tokenptr, resource, sizeof(token) - (tokenptr - token));
tempptr += 9;
}
else if (!strncasecmp(tempptr + 1, "job-id", 6))
{
snprintf(tokenptr, sizeof(token) - (tokenptr - token),
"%d", job_id);
tempptr += 7;
}
else if (!strncasecmp(tempptr + 1, "notify-subscription-id", 22))
{
snprintf(tokenptr, sizeof(token) - (tokenptr - token),
"%d", subscription_id);
tempptr += 23;
}
else if (!strncasecmp(tempptr + 1, "user", 4))
{
strlcpy(tokenptr, cupsUser(), sizeof(token) - (tokenptr - token));
tempptr += 5;
}
else
{
*tokenptr++ = *tempptr ++;
*tokenptr = '\0';
}
tokenptr += strlen(tokenptr);
}
else
{
*tokenptr++ = *tempptr++;
*tokenptr = '\0';
}
switch (value)
{
case IPP_TAG_BOOLEAN :
if (!strcasecmp(token, "true"))
ippAddBoolean(request, group, attr, 1);
else
ippAddBoolean(request, group, attr, atoi(token));
break;
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
ippAddInteger(request, group, value, attr, atoi(token));
break;
case IPP_TAG_RESOLUTION :
puts(" ERROR: resolution tag not yet supported!");
break;
case IPP_TAG_RANGE :
puts(" ERROR: range tag not yet supported!");
break;
default :
if (!strchr(token, ','))
ippAddString(request, group, value, attr, NULL, token);
else
{
/*
* Multiple string values...
*/
int num_values; /* Number of values */
char *values[100], /* Values */
*ptr; /* Pointer to next value */
values[0] = token;
num_values = 1;
for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
{
*ptr++ = '\0';
values[num_values] = ptr;
num_values ++;
}
ippAddStrings(request, group, value, attr, num_values,
NULL, (const char **)values);
}
break;
}
}
else if (!strcasecmp(token, "FILE"))
{
/*
* File...
*/
get_token(fp, filename, sizeof(filename), &linenum);
}
else if (!strcasecmp(token, "STATUS") &&
num_statuses < (int)(sizeof(statuses) / sizeof(statuses[0])))
{
/*
* Status...
*/
get_token(fp, token, sizeof(token), &linenum);
statuses[num_statuses] = ippErrorValue(token);
num_statuses ++;
}
else if (!strcasecmp(token, "EXPECT") &&
num_expects < (int)(sizeof(expects) / sizeof(expects[0])))
{
/*
* Expected attributes...
*/
get_token(fp, token, sizeof(token), &linenum);
expects[num_expects] = strdup(token);
num_expects ++;
}
else if (!strcasecmp(token, "DISPLAY") &&
num_displayed < (int)(sizeof(displayed) / sizeof(displayed[0])))
{
/*
* Display attributes...
*/
get_token(fp, token, sizeof(token), &linenum);
displayed[num_displayed] = strdup(token);
num_displayed ++;
}
else
{
printf("Unexpected token %s seen on line %d - aborting test!\n", token,
linenum);
httpClose(http);
ippDelete(request);
return (0);
}
}
/*
* Submit the IPP request...
*/
request->request.op.version[1] = version;
request->request.op.operation_id = op;
request->request.op.request_id = 1;
if (Verbosity)
{
printf("%s:\n", ippOpString(op));
for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
print_attr(attrptr);
}
printf(" %-60.60s [", name);
fflush(stdout);
if (filename[0])
response = cupsDoFileRequest(http, request, resource, filename);
else
response = cupsDoRequest(http, request, resource);
if (response == NULL)
{
time_t curtime;
curtime = time(NULL);
puts("FAIL]");
printf(" ERROR %04x (%s) @ %s\n", cupsLastError(),
cupsLastErrorString(), ctime(&curtime));
pass = 0;
}
else
{
if ((attrptr = ippFindAttribute(response, "job-id",
IPP_TAG_INTEGER)) != NULL)
job_id = attrptr->values[0].integer;
if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
IPP_TAG_INTEGER)) != NULL)
subscription_id = attrptr->values[0].integer;
for (i = 0; i < num_statuses; i ++)
if (response->request.status.status_code == statuses[i])
break;
if (i == num_statuses && num_statuses > 0)
pass = 0;
else
{
for (i = 0; i < num_expects; i ++)
if (ippFindAttribute(response, expects[i], IPP_TAG_ZERO) == NULL)
{
pass = 0;
break;
}
}
if (pass)
{
puts("PASS]");
printf(" RECEIVED: %lu bytes in response\n",
(unsigned long)ippLength(response));
if (Verbosity)
{
for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next)
print_attr(attrptr);
}
else if (num_displayed > 0)
{
for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next)
if (attrptr->name)
{
for (i = 0; i < num_displayed; i ++)
if (!strcmp(displayed[i], attrptr->name))
{
print_attr(attrptr);
break;
}
}
}
}
else
{
puts("FAIL]");
printf(" RECEIVED: %lu bytes in response\n",
(unsigned long)ippLength(response));
for (i = 0; i < num_statuses; i ++)
if (response->request.status.status_code == statuses[i])
break;
if (i == num_statuses && num_statuses > 0)
puts(" BAD STATUS");
printf(" status-code = %04x (%s)\n",
cupsLastError(), cupsLastErrorString());
for (i = 0; i < num_expects; i ++)
if (ippFindAttribute(response, expects[i], IPP_TAG_ZERO) == NULL)
printf(" EXPECTED: %s\n", expects[i]);
for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next)
print_attr(attrptr);
}
ippDelete(response);
}
for (i = 0; i < num_expects; i ++)
free(expects[i]);
if (!pass)
break;
}
fclose(fp);
httpClose(http);
return (pass);
}
/*
* 'get_tag()' - Get an IPP value or group tag from a name...
*/
ipp_tag_t /* O - Value/group tag */
get_tag(const char *name) /* I - Name of value/group tag */
{
int i; /* Looping var */
for (i = 0; i < (sizeof(TagNames) / sizeof(TagNames[0])); i ++)
if (!strcasecmp(name, TagNames[i]))
return ((ipp_tag_t)i);
return (IPP_TAG_ZERO);
}
/*
* 'get_tag_string()' - Get the string associated with a tag.
*/
const char * /* O - Tag name string */
get_tag_string(ipp_tag_t tag) /* I - IPP tag */
{
if (tag < (ipp_tag_t)(sizeof(TagNames) / sizeof(TagNames[0])))
return (TagNames[tag]);
else
return ("UNKNOWN");
}
/*
* 'get_token()' - Get a token from a file.
*/
char * /* O - Token from file or NULL on EOF */
get_token(FILE *fp, /* I - File to read from */
char *buf, /* I - Buffer to read into */
int buflen, /* I - Length of buffer */
int *linenum) /* IO - Current line number */
{
int ch, /* Character from file */
quote; /* Quoting character */
char *bufptr, /* Pointer into buffer */
*bufend; /* End of buffer */
for (;;)
{
/*
* Skip whitespace...
*/
while (isspace(ch = getc(fp)))
{
if (ch == '\n')
(*linenum) ++;
}
/*
* Read a token...
*/
if (ch == EOF)
return (NULL);
else if (ch == '\'' || ch == '\"')
{
/*
* Quoted text...
*/
quote = ch;
bufptr = buf;
bufend = buf + buflen - 1;
while ((ch = getc(fp)) != EOF)
if (ch == quote)
break;
else if (bufptr < bufend)
*bufptr++ = ch;
*bufptr = '\0';
return (buf);
}
else if (ch == '#')
{
/*
* Comment...
*/
while ((ch = getc(fp)) != EOF)
if (ch == '\n')
break;
(*linenum) ++;
}
else
{
/*
* Whitespace delimited text...
*/
ungetc(ch, fp);
bufptr = buf;
bufend = buf + buflen - 1;
while ((ch = getc(fp)) != EOF)
if (isspace(ch) || ch == '#')
break;
else if (bufptr < bufend)
*bufptr++ = ch;
if (ch == '#')
ungetc(ch, fp);
*bufptr = '\0';
return (buf);
}
}
}
/*
* 'print_attr()' - Print an attribute on the screen.
*/
void
print_attr(ipp_attribute_t *attr) /* I - Attribute to print */
{
int i; /* Looping var */
if (attr->name == NULL)
{
puts(" -- separator --");
return;
}
printf(" %s (%s%s) = ", attr->name,
attr->num_values > 1 ? "1setOf " : "",
get_tag_string(attr->value_tag));
switch (attr->value_tag)
{
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
for (i = 0; i < attr->num_values; i ++)
printf("%d ", attr->values[i].integer);
break;
case IPP_TAG_BOOLEAN :
for (i = 0; i < attr->num_values; i ++)
if (attr->values[i].boolean)
printf("true ");
else
printf("false ");
break;
case IPP_TAG_NOVALUE :
printf("novalue");
break;
case IPP_TAG_RANGE :
for (i = 0; i < attr->num_values; i ++)
printf("%d-%d ", attr->values[i].range.lower,
attr->values[i].range.upper);
break;
case IPP_TAG_RESOLUTION :
for (i = 0; i < attr->num_values; i ++)
printf("%dx%d%s ", attr->values[i].resolution.xres,
attr->values[i].resolution.yres,
attr->values[i].resolution.units == IPP_RES_PER_INCH ?
"dpi" : "dpc");
break;
case IPP_TAG_STRING :
case IPP_TAG_TEXT :
case IPP_TAG_NAME :
case IPP_TAG_KEYWORD :
case IPP_TAG_CHARSET :
case IPP_TAG_URI :
case IPP_TAG_MIMETYPE :
case IPP_TAG_LANGUAGE :
for (i = 0; i < attr->num_values; i ++)
printf("\"%s\" ", attr->values[i].string.text);
break;
case IPP_TAG_TEXTLANG :
case IPP_TAG_NAMELANG :
for (i = 0; i < attr->num_values; i ++)
printf("\"%s\",%s ", attr->values[i].string.text,
attr->values[i].string.charset);
break;
default :
break; /* anti-compiler-warning-code */
}
putchar('\n');
}
/*
* 'usage()' - Show program usage.
*/
void
usage(const char *option) /* I - Option string or NULL */
{
if (option)
fprintf(stderr, "ipptest: Unknown option \"%s\"!\n", option);
fputs("Usage: ipptest [options] URL testfile [ ... testfileN ]\n", stderr);
fputs("Options:\n", stderr);
fputs("\n", stderr);
fputs("-i N Repeat the last test file once every N seconds.\n", stderr);
fputs("-v Show all attributes in response, even on success.\n", stderr);
exit(1);
}
/*
* End of "$Id: ipptest.c 5878 2006-08-24 15:55:42Z mike $".
*/