#include <stdio.h>
#include <stdlib.h>
#include <cups/string.h>
#include <errno.h>
#include <ctype.h>
#include <cups/cups.h>
#include <cups/language.h>
int Verbosity = 0;
const char * const TagNames[] =
{
"zero",
"operation",
"job",
"end",
"printer",
"unsupported-group",
"subscription",
"event-notification",
"", "", "", "", "", "", "", "",
"unsupported-value",
"default",
"unknown",
"novalue",
"",
"notsettable",
"deleteattr",
"anyvalue",
"", "", "", "", "", "", "", "", "",
"integer",
"boolean",
"enum",
"", "", "", "", "", "", "", "", "", "", "", "",
"string",
"date",
"resolution",
"range",
"collection",
"textlang",
"namelang",
"", "", "", "", "", "", "", "", "", "",
"text",
"name",
"",
"keyword",
"uri",
"urischeme",
"charset",
"language",
"mimetype"
};
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);
int
main(int argc,
char *argv[])
{
int i;
int status;
const char *uri;
const char *testfile;
int interval;
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], "-d"))
{
i ++;
if (i >= argc)
usage(NULL);
else
putenv(argv[i]);
}
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))
{
if (!testfile && uri)
usage(NULL);
uri = argv[i];
testfile = NULL;
}
else
{
testfile = argv[i];
if (!do_tests(uri, testfile))
status ++;
}
}
if (!uri || !testfile)
usage(NULL);
if (interval)
{
for (;;)
{
sleep(interval);
do_tests(uri, testfile);
}
}
return (status);
}
int
do_tests(const char *uri,
const char *testfile)
{
int i;
int linenum;
int version;
http_t *http;
char method[HTTP_MAX_URI],
userpass[HTTP_MAX_URI],
server[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
FILE *fp;
char token[1024],
*tokenptr,
temp[1024],
*tempptr;
ipp_t *request;
ipp_t *response;
ipp_op_t op;
ipp_tag_t group;
ipp_tag_t value;
ipp_attribute_t *attrptr;
char attr[128];
int num_statuses;
ipp_status_t statuses[100];
int num_expects;
char *expects[100];
int num_displayed;
char *displayed[100];
char name[1024];
char filename[1024];
int pass;
int job_id;
int subscription_id;
if ((fp = fopen(testfile, "r")) == NULL)
{
printf("Unable to open test file %s - %s\n", testfile, strerror(errno));
return (0);
}
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);
}
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)
{
if (strcmp(token, "{"))
{
printf("Unexpected token %s seen on line %d - aborting test!\n", token,
linenum);
httpClose(http);
return (0);
}
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';
while (get_token(fp, token, sizeof(token), &linenum) != NULL)
{
if (!strcmp(token, "}"))
break;
else if (!strcasecmp(token, "NAME"))
{
get_token(fp, name, sizeof(name), &linenum);
}
else if (!strcasecmp(token, "VERSION"))
{
get_token(fp, temp, sizeof(temp), &linenum);
sscanf(temp, "%*d.%d", &version);
}
else if (!strcasecmp(token, "RESOURCE"))
{
get_token(fp, resource, sizeof(resource), &linenum);
}
else if (!strcasecmp(token, "OPERATION"))
{
get_token(fp, token, sizeof(token), &linenum);
op = ippOpValue(token);
}
else if (!strcasecmp(token, "GROUP"))
{
get_token(fp, token, sizeof(token), &linenum);
value = get_tag(token);
if (value == group)
ippAddSeparator(request);
group = value;
}
else if (!strcasecmp(token, "DELAY"))
{
int delay;
get_token(fp, token, sizeof(token), &linenum);
if ((delay = atoi(token)) > 0)
sleep(delay);
}
else if (!strcasecmp(token, "ATTR"))
{
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 == '$')
{
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 if (!strncasecmp(tempptr + 1, "ENV[", 4))
{
char *end;
if ((end = strchr(tempptr + 5, ']')) != NULL)
{
*end++ = '\0';
strlcpy(tokenptr,
getenv(tempptr + 5) ? getenv(tempptr + 5) : tempptr + 5,
sizeof(token) - (tokenptr - token));
tempptr = end;
}
else
{
*tokenptr++ = *tempptr++;
*tokenptr = '\0';
}
}
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
{
int num_values;
char *values[100],
*ptr;
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"))
{
get_token(fp, filename, sizeof(filename), &linenum);
}
else if (!strcasecmp(token, "STATUS") &&
num_statuses < (int)(sizeof(statuses) / sizeof(statuses[0])))
{
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])))
{
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])))
{
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);
}
}
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 = cupsDoIORequest(http, request, resource, -1,
Verbosity ? 1 : -1);
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(), ippErrorString(cupsLastError()));
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);
}
ipp_tag_t
get_tag(const char *name)
{
int i;
for (i = 0; i < (sizeof(TagNames) / sizeof(TagNames[0])); i ++)
if (!strcasecmp(name, TagNames[i]))
return ((ipp_tag_t)i);
return (IPP_TAG_ZERO);
}
const char *
get_tag_string(ipp_tag_t tag)
{
if (tag < (ipp_tag_t)(sizeof(TagNames) / sizeof(TagNames[0])))
return (TagNames[tag]);
else
return ("UNKNOWN");
}
char *
get_token(FILE *fp,
char *buf,
int buflen,
int *linenum)
{
int ch,
quote;
char *bufptr,
*bufend;
for (;;)
{
while (isspace(ch = getc(fp)))
{
if (ch == '\n')
(*linenum) ++;
}
if (ch == EOF)
return (NULL);
else if (ch == '\'' || ch == '\"')
{
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 == '#')
{
while ((ch = getc(fp)) != EOF)
if (ch == '\n')
break;
(*linenum) ++;
}
else
{
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);
}
}
}
void
print_attr(ipp_attribute_t *attr)
{
int i;
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;
}
putchar('\n');
}
void
usage(const char *option)
{
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);
}