<!--
- "$Id: api-httpipp.shtml 177 2006-06-21 00:20:03Z jlovell $"
+ "$Id: api-httpipp.shtml 7684 2008-06-23 16:47:38Z mike $"
HTTP and IPP API introduction for the Common UNIX Printing System (CUPS).
- Copyright 1997-2006 by Easy Software Products.
+ Copyright 2007-2008 by Apple Inc.
+ Copyright 1997-2006 by Easy Software Products, all rights reserved.
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
+ 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'>Introduction</h2>
+<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 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>
-<h2 class='title'>General Usage</h2>
+<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 <var><cups/cups.h></var> header file must be included to
-use the HTTP and IPP functions.</p>
+<p>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 <a href='#ipp_t'><code>ipp_t</code></a> structure 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>Programs using these functions must be linked to the CUPS
-library: <var>libcups.a</var>, <var>libcups.so.2</var>,
-<var>libcups.2.dylib</var>, <var>libcups_s.a</var>, or
-<var>libcups2.lib</var> depending on the platform. The following
-command compiles <var>myprogram.c</var> using GCC and the CUPS
-library:</p>
+<p>The second 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 (<code>group_tag</code>), a
+value type tag (<code>value_tag</code>), the attribute name (<code>name</code>),
+and 1 or more values (<code>values[]</code>). Attributes are added to an
+<a href='#ipp_t'><code>ipp_t</code></a> structure using one of the
+<code>ippAdd</code> functions. For example, use
+<a href='#ippAddString'><code>ippAddString</code></a> to add a
+"requesting-user-name" string attribute to a request:</p>
-<pre class='command'>
-<kbd>gcc -o myprogram myprogram.c -lcups</kbd>
+<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_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_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 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
+<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 = response->attrs; attr != NULL; attr = attr->next)
+ if (attr->name == NULL)
+ puts("--SEPARATOR--");
+ else
+ puts(attr->name);
</pre>
-<h2 class='title'>Compatibility</h2>
+<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;
+ 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);
+}
+</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");
-<p>Unless otherwise specified, the HTTP and IPP API functions
-require CUPS 1.1 or higher.</p>
+/* 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>