The CUPS HTTP and IPP APIs provide low-level access to the HTTP and IPP protocols and CUPS scheduler. They are typically used by monitoring and administration programs to perform specific functions not supported by the high-level CUPS API functions.
The HTTP APIs use an opaque structure called
http_t to manage connections to
a particular HTTP or IPP server. The
httpConnectEncrypt function is
used to create an instance of this structure for a particular server.
The constant CUPS_HTTP_DEFAULT can be used with all of the
cups functions to refer to the default CUPS server - the functions
create a per-thread http_t as needed.
The IPP APIs use two structures for requests (messages sent to the CUPS
scheduler) and responses (messages sent back to your application from the
scheduler). The ipp_t structure holds a
complete request or response and is allocated using the
ippNew or
ippNewRequest functions and
freed using the ippDelete function.
The second structure is called
ipp_attribute_t and holds a
single IPP attribute which consists of a group tag (group_tag), a
value type tag (value_tag), the attribute name (name),
and 1 or more values (values[]). Attributes are added to an
ipp_t structure using one of the
ippAdd functions. For example, use
ippAddString to add a
"requesting-user-name" string attribute to a request:
ipp_t *request = ippNewRequest(IPP_GET_JOBS);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
Once you have created an IPP request, use the cups
functions to send the request to and read the response from the server.
For example, the cupsDoRequest
function can be used for simple query operations that do not involve files:
#include <cups/cups.h>
ipp_t *get_jobs(void)
{
ipp_t *request = ippNewRequest(IPP_GET_JOBS);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
return (cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/"));
}
The cupsDoRequest function frees
the request structure and returns an IPP response structure or NULL pointer if
the request could not be sent to the server. Once you have a response from
the server, you can either use the
ippFindAttribute and
ippFindNextAttribute functions
to find specific attributes, for example:
ipp_t *response; ipp_attribute_t *attr; attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM);
You can also walk the list of attributes with a simple for loop
like this:
ipp_t *response;
ipp_attribute_t *attr;
for (attr = response->attrs; attr != NULL; attr = attr->next)
if (attr->name == NULL)
puts("--SEPARATOR--");
else
puts(attr->name);
The for loop approach is normally used when collecting
attributes for multiple objects (jobs, printers, etc.) in a response. Attributes
with NULL names indicate a separator between the attributes of
each object. For example, the following code will list the jobs returned from
our previous get_jobs example code:
ipp_t *response = get_jobs();
if (response != NULL)
{
ipp_attribute_t *attr;
int job_id = 0;
char *job_name = NULL;
char *job_originating_user_name = NULL;
puts("Job ID Owner Title");
puts("------ ---------------- ---------------------------------");
for (attr = response->attrs; attr != NULL; attr = attr->next)
{
/* Attributes without names are separators between jobs */
if (attr->name == NULL)
{
if (job_id > 0 && job_name != NULL && job_originating_user_name != NULL)
printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
job_id = 0;
job_name = NULL;
job_originating_user_name = NULL;
continue;
}
else if (!strcmp(attr->name, "job-id") && attr->value_tag == IPP_TAG_INTEGER)
job_id = attr->values[0].integer;
else if (!strcmp(attr->name, "job-name") && attr->value_tag == IPP_TAG_NAME)
job_name = attr->values[0].string.text;
else if (!strcmp(attr->name, "job-originating-user-name") &&
attr->value_tag == IPP_TAG_NAME)
job_originating_user_name = attr->values[0].string.text;
}
if (job_id > 0 && job_name != NULL && job_originating_user_name != NULL)
printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
}
To ensure proper encoding, the
httpAssembleURIf function must be
used to format a "printer-uri" string for all printer-based requests:
const char *name = "Foo";
char uri[1024];
ipp_t *request;
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
ippPort(), "/printers/%s", name);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
The cupsDoFileRequest and
cupsDoIORequest functions are
used for requests involving files. The
cupsDoFileRequest function
attaches the named file to a request and is typically used when sending a print
file or changing a printer's PPD file:
const char *filename = "/usr/share/cups/data/testprint.ps";
const char *name = "Foo";
char uri[1024];
char resource[1024];
ipp_t *request = ippNewRequest(IPP_PRINT_JOB);
ipp_t *response;
/* Use httpAssembleURIf for the printer-uri string */
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
ippPort(), "/printers/%s", name);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
NULL, "testprint.ps");
/* Use snprintf for the resource path */
snprintf(resource, sizeof(resource), "/printers/%s", name);
response = cupsDoFileRequest(CUPS_HTTP_DEFAULT, request, resource, filename);
The cupsDoIORequest function
optionally attaches a file to the request and optionally saves a file in the
response from the server. It is used when using a pipe for the request
attachment or when using a request that returns a file, currently only
CUPS_GET_DOCUMENT and CUPS_GET_PPD. For example,
the following code will download the PPD file for the sample HP LaserJet
printer driver:
char tempfile[1024];
int tempfd;
ipp_t *request = ippNewRequest(CUPS_GET_PPD);
ipp_t *response;
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
NULL, "laserjet.ppd");
tempfd = cupsTempFd(tempfile, sizeof(tempfile));
response = cupsDoIORequest(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
The example passes -1 for the input file descriptor to specify
that no file is to be attached to the request. The PPD file attached to the
response is written to the temporary file descriptor we created using the
cupsTempFd function.
The cupsSendRequest and
cupsGetResponse support
asynchronous communications with the server. Unlike the other request
functions, the IPP request is not automatically freed, so remember to
free your request with the ippDelete
function.
File data is attached to the request using the
cupsWriteRequestData
function, while file data returned from the server is read using the
cupsReadResponseData
function. We can rewrite the previous CUPS_GET_PPD example
to use the asynchronous functions quite easily:
char tempfile[1024];
int tempfd;
ipp_t *request = ippNewRequest(CUPS_GET_PPD);
ipp_t *response;
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
NULL, "laserjet.ppd");
tempfd = cupsTempFd(tempfile, sizeof(tempfile));
if (cupsSendRequest(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
{
response = cupsGetResponse(CUPS_HTTP_DEFAULT, "/");
if (response != NULL)
{
ssize_t bytes;
char buffer[8192];
while ((bytes = cupsReadResponseData(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
write(tempfd, buffer, bytes);
}
}
/* Free the request! */
ippDelete(request);
The cupsSendRequest function
returns the initial HTTP request status, typically either
HTTP_CONTINUE or HTTP_UNAUTHORIZED. The latter status
is returned when the request requires authentication of some sort. The
cupsDoAuthentication function
must be called when your see HTTP_UNAUTHORIZED and the request
re-sent. We can add authentication support to our example code by using a
do ... while loop:
char tempfile[1024];
int tempfd;
ipp_t *request = ippNewRequest(CUPS_GET_PPD);
ipp_t *response;
http_status_t status;
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
NULL, "laserjet.ppd");
tempfd = cupsTempFd(tempfile, sizeof(tempfile));
/* Loop for authentication */
do
{
status = cupsSendRequest(CUPS_HTTP_DEFAULT, request, "/");
if (status == HTTP_UNAUTHORIZED)
{
/* Try to authenticate, break out of the loop if that fails */
if (cupsDoAuthentication(CUPS_HTTP_DEFAULT, "POST", "/"))
break;
}
}
while (status != HTTP_CONTINUE && status != HTTP_UNAUTHORIZED);
if (status == HTTP_CONTINUE)
{
response = cupsGetResponse(CUPS_HTTP_DEFAULT, "/");
if (response != NULL)
{
ssize_t bytes;
char buffer[8192];
while ((bytes = cupsReadResponseData(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
write(tempfd, buffer, bytes);
}
}
/* Free the request! */
ippDelete(request);