#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 do_tests(const char *, const char *);
ipp_op_t get_operation(const char *);
ipp_status_t get_status(const char *);
ipp_tag_t get_tag(const char *);
char *get_token(FILE *, char *, int);
void print_attr(ipp_attribute_t *);
int
main(int argc,
char *argv[])
{
int i;
int status;
if (argc < 3)
{
fputs("Usage: testipp URL testfile [ ... testfileN ]\n", stderr);
return (1);
}
for (i = 2, status = 1; status && i < argc; i ++)
status = status && do_tests(argv[1], argv[i]);
return (!status);
}
int
do_tests(const char *uri,
const char *testfile)
{
int i;
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];
char name[1024];
char filename[1024];
int pass;
int job_id;
if ((fp = fopen(testfile, "r")) == NULL)
{
printf("Unable to open test file %s - %s\n", testfile, strerror(errno));
return (0);
}
httpSeparate(uri, method, userpass, server, &port, 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;
version = 1;
while (get_token(fp, token, sizeof(token)) != NULL)
{
if (strcmp(token, "{") != 0)
{
printf("Unexpected token %s seen - aborting test!\n", token);
httpClose(http);
return (0);
}
httpSeparate(uri, method, userpass, server, &port, resource);
request = ippNew();
op = (ipp_op_t)0;
group = IPP_TAG_OPERATION;
value = IPP_TAG_ZERO;
num_statuses = 0;
num_expects = 0;
filename[0] = '\0';
strcpy(name, testfile);
if (strrchr(name, '.') != NULL)
*strrchr(name, '.') = '\0';
while (get_token(fp, token, sizeof(token)) != NULL)
{
if (strcmp(token, "}") == 0)
break;
else if (strcasecmp(token, "NAME") == 0)
{
get_token(fp, name, sizeof(name));
}
else if (strcasecmp(token, "VERSION") == 0)
{
get_token(fp, temp, sizeof(temp));
sscanf(temp, "%*d.%d", &version);
}
else if (strcasecmp(token, "RESOURCE") == 0)
{
get_token(fp, resource, sizeof(resource));
}
else if (strcasecmp(token, "OPERATION") == 0)
{
get_token(fp, token, sizeof(token));
op = get_operation(token);
}
else if (strcasecmp(token, "GROUP") == 0)
{
get_token(fp, token, sizeof(token));
group = get_tag(token);
}
else if (strcasecmp(token, "ATTR") == 0)
{
get_token(fp, token, sizeof(token));
value = get_tag(token);
get_token(fp, attr, sizeof(attr));
get_token(fp, temp, sizeof(temp));
token[sizeof(token) - 1] = '\0';
for (tempptr = temp, tokenptr = token;
*tempptr && tokenptr < (token + sizeof(token) - 1);)
if (*tempptr == '$')
{
if (strncasecmp(tempptr + 1, "uri", 3) == 0)
{
strlcpy(tokenptr, uri, sizeof(token) - (tokenptr - token));
tempptr += 4;
}
else if (strncasecmp(tempptr + 1, "method", 6) == 0)
{
strlcpy(tokenptr, method, sizeof(token) - (tokenptr - token));
tempptr += 7;
}
else if (strncasecmp(tempptr + 1, "username", 8) == 0)
{
strlcpy(tokenptr, userpass, sizeof(token) - (tokenptr - token));
tempptr += 9;
}
else if (strncasecmp(tempptr + 1, "hostname", 8) == 0)
{
strlcpy(tokenptr, server, sizeof(token) - (tokenptr - token));
tempptr += 9;
}
else if (strncasecmp(tempptr + 1, "port", 4) == 0)
{
snprintf(tokenptr, sizeof(token) - (tokenptr - token),
"%d", port);
tempptr += 5;
}
else if (strncasecmp(tempptr + 1, "resource", 8) == 0)
{
strlcpy(tokenptr, resource, sizeof(token) - (tokenptr - token));
tempptr += 9;
}
else if (strncasecmp(tempptr + 1, "job-id", 6) == 0)
{
snprintf(tokenptr, sizeof(token) - (tokenptr - token),
"%d", job_id);
tempptr += 7;
}
else if (strncasecmp(tempptr + 1, "user", 4) == 0)
{
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") == 0)
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 :
ippAddString(request, group, value, attr, NULL, token);
break;
}
}
else if (strcasecmp(token, "FILE") == 0)
{
get_token(fp, filename, sizeof(filename));
}
else if (strcasecmp(token, "STATUS") == 0)
{
get_token(fp, token, sizeof(token));
statuses[num_statuses] = get_status(token);
num_statuses ++;
}
else if (strcasecmp(token, "EXPECT") == 0)
{
get_token(fp, token, sizeof(token));
expects[num_expects] = strdup(token);
num_expects ++;
}
else
{
printf("Unexpected token %s seen - aborting test!\n", token);
httpClose(http);
ippDelete(request);
return (0);
}
}
request->request.op.version[1] = version;
request->request.op.operation_id = op;
request->request.op.request_id = 1;
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);
printf("\b\b\b\b\bFAIL]\n");
printf(" ERROR %x\n", cupsLastError());
printf(" (%s) @ %s\n",
ippErrorString(cupsLastError()), ctime(&curtime));
pass = 0;
}
else
{
if ((attrptr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
job_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)
{
printf("\b\b\b\b\bFAIL]\n");
printf(" STATUS %x\n", response->request.status.status_code);
printf(" (%s)\n",
ippErrorString(response->request.status.status_code));
printf(" (%lu bytes in response)\n",
(unsigned long)ippLength(response));
pass = 0;
}
else
{
for (i = 0; i < num_expects; i ++)
if (ippFindAttribute(response, expects[i], IPP_TAG_ZERO) == NULL)
{
if (pass)
{
printf("\b\b\b\b\bFAIL]\n");
printf(" (%lu bytes in response)\n",
(unsigned long)ippLength(response));
pass = 0;
}
printf(" EXPECTED %s\n", expects[i]);
}
if (pass)
{
printf("\b\b\b\b\bPASS]\n");
printf(" (%lu bytes in response)\n",
(unsigned long)ippLength(response));
}
else
{
puts(" RECEIVED");
printf(" status-code = %04x\n",
response->request.status.status_code);
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_op_t
get_operation(const char *name)
{
int i;
static char *ipp_ops[] =
{
"",
"",
"print-job",
"print-uri",
"validate-job",
"create-job",
"send-document",
"send-uri",
"cancel-job",
"get-job-attributes",
"get-jobs",
"get-printer-attributes",
"hold-job",
"release-job",
"restart-job",
"",
"pause-printer",
"resume-printer",
"purge-jobs",
"set-printer-attributes",
"set-job-attributes",
"get-printer-supported-values"
},
*cups_ops[] =
{
"cups-get-default",
"cups-get-printers",
"cups-add-printer",
"cups-delete-printer",
"cups-get-classes",
"cups-add-class",
"cups-delete-class",
"cups-accept-jobs",
"cups-reject-jobs",
"cups-set-default",
"cups-get-devices",
"cups-get-ppds",
"cups-move-job"
};
for (i = 0; i < (sizeof(ipp_ops) / sizeof(ipp_ops[0])); i ++)
if (strcasecmp(name, ipp_ops[i]) == 0)
return ((ipp_op_t)i);
if (strcasecmp(name, "windows-ext") == 0)
return (IPP_PRIVATE);
for (i = 0; i < (sizeof(cups_ops) / sizeof(cups_ops[0])); i ++)
if (strcasecmp(name, cups_ops[i]) == 0)
return ((ipp_op_t)(i + 0x4001));
return ((ipp_op_t)0);
}
ipp_status_t
get_status(const char *name)
{
int i;
static const char *status_oks[] =
{
"successful-ok",
"successful-ok-ignored-or-substituted-attributes",
"successful-ok-conflicting-attributes"
},
*status_400s[] =
{
"client-error-bad-request",
"client-error-forbidden",
"client-error-not-authenticated",
"client-error-not-authorized",
"client-error-not-possible",
"client-error-timeout",
"client-error-not-found",
"client-error-gone",
"client-error-request-entity-too-large",
"client-error-request-value-too-long",
"client-error-document-format-not-supported",
"client-error-attributes-or-values-not-supported",
"client-error-uri-scheme-not-supported",
"client-error-charset-not-supported",
"client-error-conflicting-attributes",
"client-error-compression-not-supported",
"client-error-compression-error",
"client-error-document-format-error",
"client-error-document-access-error"
},
*status_500s[] =
{
"server-error-internal-error",
"server-error-operation-not-supported",
"server-error-service-unavailable",
"server-error-version-not-supported",
"server-error-device-error",
"server-error-temporary-error",
"server-error-not-accepting-jobs",
"server-error-busy",
"server-error-job-canceled",
"server-error-multiple-document-jobs-not-supported"
};
for (i = 0; i < (sizeof(status_oks) / sizeof(status_oks[0])); i ++)
if (strcasecmp(name, status_oks[i]) == 0)
return ((ipp_status_t)i);
for (i = 0; i < (sizeof(status_400s) / sizeof(status_400s[0])); i ++)
if (strcasecmp(name, status_400s[i]) == 0)
return ((ipp_status_t)(i + 0x400));
for (i = 0; i < (sizeof(status_500s) / sizeof(status_500s[0])); i ++)
if (strcasecmp(name, status_500s[i]) == 0)
return ((ipp_status_t)(i + 0x500));
return ((ipp_status_t)-1);
}
ipp_tag_t
get_tag(const char *name)
{
int i;
static char *names[] =
{
"zero", "operation", "job", "end", "printer",
"unsupported-group", "", "", "", "", "", "", "",
"", "", "", "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"
};
for (i = 0; i < (sizeof(names) / sizeof(names[0])); i ++)
if (strcasecmp(name, names[i]) == 0)
return ((ipp_tag_t)i);
return (IPP_TAG_ZERO);
}
char *
get_token(FILE *fp,
char *buf,
int buflen)
{
int ch,
quote;
char *bufptr,
*bufend;
for (;;)
{
while (isspace(ch = getc(fp)));
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;
}
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 = ", attr->name);
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');
}