mirror of
https://github.com/MopriaAlliance/CUPS-for-Android.git
synced 2025-08-04 03:14:37 +08:00
318 lines
14 KiB
Plaintext
318 lines
14 KiB
Plaintext
<!--
|
|
"$Id: api-httpipp.shtml 7684 2008-06-23 16:47:38Z mike $"
|
|
|
|
HTTP and IPP API introduction for CUPS.
|
|
|
|
Copyright 2007-2012 by Apple Inc.
|
|
Copyright 1997-2006 by Easy Software Products, all rights reserved.
|
|
|
|
These coded instructions, statements, and computer programs are the
|
|
property of Apple Inc. 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
|
|
file is missing or damaged, see the license at "http://www.cups.org/".
|
|
-->
|
|
|
|
<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>The HTTP APIs use an opaque structure called
|
|
<a href='#http_t'><code>http_t</code></a> to manage connections to
|
|
a particular HTTP or IPP server. The
|
|
<a href='#httpConnectEncrypt'><code>httpConnectEncrypt</code></a> function is
|
|
used to create an instance of this structure for a particular server.
|
|
The constant <code>CUPS_HTTP_DEFAULT</code> can be used with all of the
|
|
<code>cups</code> functions to refer to the default CUPS server - the functions
|
|
create a per-thread <a href='#http_t'><code>http_t</code></a> as needed.</p>
|
|
|
|
<p>The IPP APIs use two opaque structures for requests (messages sent to the CUPS scheduler) and responses (messages sent back to your application from the scheduler). The <a href='#ipp_t'><code>ipp_t</code></a> type holds a complete request or response and is allocated using the <a href='#ippNew'><code>ippNew</code></a> or <a href='#ippNewRequest'><code>ippNewRequest</code></a> functions and freed using the <a href='#ippDelete'><code>ippDelete</code></a> function.</p>
|
|
|
|
<p>The second opaque structure is called <a href='#ipp_attribute_t'><code>ipp_attribute_t</code></a> and holds a single IPP attribute which consists of a group tag (<a href='#ippGetGroupTag'><code>ippGetGroupTag</code></a>), a value type tag (<a href='#ippGetValueTag'><code>ippGetValueTag</code></a>), the attribute name (<a href='#ippGetName'><code>ippGetName</code></a>), and 1 or more values (<a href='#ippGetCount'><code>ippGetCount</code></a>, <a href='#ippGetBoolean'><code>ippGetBoolean</code></a>, <a href='#ippGetCollection'><code>ippGetCollection</code></a>, <a href='#ippGetDate'><code>ippGetDate</code></a>, <a href='#ippGetInteger'><code>ippGetInteger</code></a>, <a href='#ippGetRange'><code>ippGetRange</code></a>, <a href='#ippGetResolution'><code>ippGetResolution</code></a>, and <a href='#ippGetString'><code>ippGetString</code></a>). Attributes are added to an <a href='#ipp_t'><code>ipp_t</code></a> pointer using one of the <code>ippAdd</code> functions. For example, use <a href='#ippAddString'><code>ippAddString</code></a> to add the "printer-uri" and "requesting-user-name" string attributes to a request:</p>
|
|
|
|
<pre class='example'>
|
|
<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
|
|
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
|
|
NULL, "ipp://localhost/printers/");
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
|
|
NULL, cupsUser());
|
|
</pre>
|
|
|
|
<p>Once you have created an IPP request, use the <code>cups</code> functions to send the request to and read the response from the server. For example, the <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function can be used for simple query operations that do not involve files:</p>
|
|
|
|
<pre class='example'>
|
|
#include <cups/cups.h>
|
|
|
|
|
|
<a href='#ipp_t'>ipp_t</a> *<a name='get_jobs'>get_jobs</a>(void)
|
|
{
|
|
<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
|
|
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
|
|
NULL, "ipp://localhost/printers/");
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
|
|
NULL, cupsUser());
|
|
|
|
return (<a href='#cupsDoRequest'>cupsDoRequest</a>(CUPS_HTTP_DEFAULT, request, "/"));
|
|
}
|
|
</pre>
|
|
|
|
<p>The <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function frees the request and returns an IPP response or <code>NULL</code> pointer if the request could not be sent to the server. Once you have a response from the server, you can either use the <a href='#ippFindAttribute'><code>ippFindAttribute</code></a> and <a href='#ippFindNextAttribute'><code>ippFindNextAttribute</code></a> functions to find specific attributes, for example:</p>
|
|
|
|
<pre class='example'>
|
|
<a href='#ipp_t'>ipp_t</a> *response;
|
|
<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
|
|
|
|
attr = <a href='#ippFindAttribute'>ippFindAttribute</a>(response, "printer-state", IPP_TAG_ENUM);
|
|
</pre>
|
|
|
|
<p>You can also walk the list of attributes with a simple <code>for</code> loop like this:</p>
|
|
|
|
<pre class='example'>
|
|
<a href='#ipp_t'>ipp_t</a> *response;
|
|
<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
|
|
|
|
for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
|
|
if (ippGetName(attr) == NULL)
|
|
puts("--SEPARATOR--");
|
|
else
|
|
puts(ippGetName(attr));
|
|
</pre>
|
|
|
|
<p>The <code>for</code> loop approach is normally used when collecting attributes for multiple objects (jobs, printers, etc.) in a response. Attributes with <code>NULL</code> names indicate a separator between the attributes of each object. For example, the following code will list the jobs returned from our previous <a href='#get_jobs'><code>get_jobs</code></a> example code:</p>
|
|
|
|
<pre class='example'>
|
|
<a href='#ipp_t'>ipp_t</a> *response = <a href='#get_jobs'>get_jobs</a>();
|
|
|
|
if (response != NULL)
|
|
{
|
|
<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
|
|
const char *attrname;
|
|
int job_id = 0;
|
|
const char *job_name = NULL;
|
|
const char *job_originating_user_name = NULL;
|
|
|
|
puts("Job ID Owner Title");
|
|
puts("------ ---------------- ---------------------------------");
|
|
|
|
for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
|
|
{
|
|
/* Attributes without names are separators between jobs */
|
|
attrname = ippGetName(attr);
|
|
if (attrname == NULL)
|
|
{
|
|
if (job_id > 0)
|
|
{
|
|
if (job_name == NULL)
|
|
job_name = "(withheld)";
|
|
|
|
if (job_originating_user_name == NULL)
|
|
job_originating_user_name = "(withheld)";
|
|
|
|
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(attrname, "job-id") && ippGetValueTag(attr) == IPP_TAG_INTEGER)
|
|
job_id = ippGetInteger(attr, 0);
|
|
else if (!strcmp(attrname, "job-name") && ippGetValueTag(attr) == IPP_TAG_NAME)
|
|
job_name = ippGetString(attr, 0, NULL);
|
|
else if (!strcmp(attrname, "job-originating-user-name") &&
|
|
ippGetValueTag(attr) == IPP_TAG_NAME)
|
|
job_originating_user_name = ippGetString(attr, 0, NULL);
|
|
}
|
|
|
|
if (job_id > 0)
|
|
{
|
|
if (job_name == NULL)
|
|
job_name = "(withheld)";
|
|
|
|
if (job_originating_user_name == NULL)
|
|
job_originating_user_name = "(withheld)";
|
|
|
|
printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<h3><a name='CREATING_URI_STRINGS'>Creating URI Strings</a></h3>
|
|
|
|
<p>To ensure proper encoding, the
|
|
<a href='#httpAssembleURIf'><code>httpAssembleURIf</code></a> function must be
|
|
used to format a "printer-uri" string for all printer-based requests:</p>
|
|
|
|
<pre class='example'>
|
|
const char *name = "Foo";
|
|
char uri[1024];
|
|
<a href='#ipp_t'>ipp_t</a> *request;
|
|
|
|
<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
|
|
ippPort(), "/printers/%s", name);
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
|
|
</pre>
|
|
|
|
<h3><a name='SENDING_REQUESTS_WITH_FILES'>Sending Requests with Files</a></h3>
|
|
|
|
<p>The <a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> and
|
|
<a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> functions are
|
|
used for requests involving files. The
|
|
<a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> function
|
|
attaches the named file to a request and is typically used when sending a print
|
|
file or changing a printer's PPD file:</p>
|
|
|
|
<pre class='example'>
|
|
const char *filename = "/usr/share/cups/data/testprint.ps";
|
|
const char *name = "Foo";
|
|
char uri[1024];
|
|
char resource[1024];
|
|
<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_PRINT_JOB);
|
|
<a href='#ipp_t'>ipp_t</a> *response;
|
|
|
|
/* Use httpAssembleURIf for the printer-uri string */
|
|
<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
|
|
ippPort(), "/printers/%s", name);
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
|
|
NULL, cupsUser());
|
|
<a href='#ippAddString'>ippAddString</a>(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 = <a href='#cupsDoFileRequest'>cupsDoFileRequest</a>(CUPS_HTTP_DEFAULT, request, resource, filename);
|
|
</pre>
|
|
|
|
<p>The <a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> 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
|
|
<code>CUPS_GET_DOCUMENT</code> and <code>CUPS_GET_PPD</code>. For example,
|
|
the following code will download the PPD file for the sample HP LaserJet
|
|
printer driver:</p>
|
|
|
|
<pre class='example'>
|
|
char tempfile[1024];
|
|
int tempfd;
|
|
<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
|
|
<a href='#ipp_t'>ipp_t</a> *response;
|
|
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
|
|
NULL, "laserjet.ppd");
|
|
|
|
tempfd = cupsTempFd(tempfile, sizeof(tempfile));
|
|
|
|
response = <a href='#cupsDoIORequest'>cupsDoIORequest</a>(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
|
|
</pre>
|
|
|
|
<p>The example passes <code>-1</code> 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
|
|
<code>cupsTempFd</code> function.</p>
|
|
|
|
<h3><a name='ASYNCHRONOUS_REQUEST_PROCESSING'>Asynchronous Request Processing</a></h3>
|
|
|
|
<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> and
|
|
<a href='#cupsGetResponse'><code>cupsGetResponse</code></a> 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 <a href='#ippDelete'><code>ippDelete</code></a>
|
|
function.</p>
|
|
|
|
<p>File data is attached to the request using the
|
|
<a href='#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>
|
|
function, while file data returned from the server is read using the
|
|
<a href='#cupsReadResponseData'><code>cupsReadResponseData</code></a>
|
|
function. We can rewrite the previous <code>CUPS_GET_PPD</code> example
|
|
to use the asynchronous functions quite easily:</p>
|
|
|
|
<pre class='example'>
|
|
char tempfile[1024];
|
|
int tempfd;
|
|
<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
|
|
<a href='#ipp_t'>ipp_t</a> *response;
|
|
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
|
|
NULL, "laserjet.ppd");
|
|
|
|
tempfd = cupsTempFd(tempfile, sizeof(tempfile));
|
|
|
|
if (<a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
|
|
{
|
|
response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
|
|
|
|
if (response != NULL)
|
|
{
|
|
ssize_t bytes;
|
|
char buffer[8192];
|
|
|
|
while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
|
|
write(tempfd, buffer, bytes);
|
|
}
|
|
}
|
|
|
|
/* Free the request! */
|
|
<a href='#ippDelete'>ippDelete</a>(request);
|
|
</pre>
|
|
|
|
<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> function
|
|
returns the initial HTTP request status, typically either
|
|
<code>HTTP_CONTINUE</code> or <code>HTTP_UNAUTHORIZED</code>. The latter status
|
|
is returned when the request requires authentication of some sort. The
|
|
<a href='#cupsDoAuthentication'><code>cupsDoAuthentication</code></a> function
|
|
must be called when your see <code>HTTP_UNAUTHORIZED</code> and the request
|
|
re-sent. We can add authentication support to our example code by using a
|
|
<code>do ... while</code> loop:</p>
|
|
|
|
<pre class='example'>
|
|
char tempfile[1024];
|
|
int tempfd;
|
|
<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
|
|
<a href='#ipp_t'>ipp_t</a> *response;
|
|
http_status_t status;
|
|
|
|
<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
|
|
NULL, "laserjet.ppd");
|
|
|
|
tempfd = cupsTempFd(tempfile, sizeof(tempfile));
|
|
|
|
/* Loop for authentication */
|
|
do
|
|
{
|
|
status = <a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/");
|
|
|
|
if (status == HTTP_UNAUTHORIZED)
|
|
{
|
|
/* Try to authenticate, break out of the loop if that fails */
|
|
if (<a href='#cupsDoAuthentication'>cupsDoAuthentication</a>(CUPS_HTTP_DEFAULT, "POST", "/"))
|
|
break;
|
|
}
|
|
}
|
|
while (status != HTTP_CONTINUE && status != HTTP_UNAUTHORIZED);
|
|
|
|
if (status == HTTP_CONTINUE)
|
|
{
|
|
response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
|
|
|
|
if (response != NULL)
|
|
{
|
|
ssize_t bytes;
|
|
char buffer[8192];
|
|
|
|
while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
|
|
write(tempfd, buffer, bytes);
|
|
}
|
|
}
|
|
|
|
/* Free the request! */
|
|
<a href='#ippDelete'>ippDelete</a>(request);
|
|
</pre>
|