]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/api-httpipp.shtml
Merge changes from CUPS 1.6svn-r10390.
[thirdparty/cups.git] / cups / api-httpipp.shtml
CommitLineData
ef416fc2 1<!--
75bd9771 2 "$Id: api-httpipp.shtml 7684 2008-06-23 16:47:38Z mike $"
ef416fc2 3
eac3a0a0 4 HTTP and IPP API introduction for CUPS.
ef416fc2 5
82cc1f9a 6 Copyright 2007-2012 by Apple Inc.
bc44d920 7 Copyright 1997-2006 by Easy Software Products, all rights reserved.
ef416fc2 8
9 These coded instructions, statements, and computer programs are the
bc44d920 10 property of Apple Inc. and are protected by Federal copyright
11 law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 which should have been included with this file. If this file is
13 file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14-->
15
5a738aea 16<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
ef416fc2 17
5a738aea
MS
18<p>The CUPS HTTP and IPP APIs provide low-level access to the HTTP and IPP
19protocols and CUPS scheduler. They are typically used by monitoring and
20administration programs to perform specific functions not supported by the
21high-level CUPS API functions.</p>
ef416fc2 22
5a738aea
MS
23<p>The HTTP APIs use an opaque structure called
24<a href='#http_t'><code>http_t</code></a> to manage connections to
25a particular HTTP or IPP server. The
26<a href='#httpConnectEncrypt'><code>httpConnectEncrypt</code></a> function is
27used to create an instance of this structure for a particular server.
28The constant <code>CUPS_HTTP_DEFAULT</code> can be used with all of the
29<code>cups</code> functions to refer to the default CUPS server - the functions
30create a per-thread <a href='#http_t'><code>http_t</code></a> as needed.</p>
ef416fc2 31
82cc1f9a
MS
32<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>
33
34<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>
ef416fc2 35
5a738aea
MS
36<pre class='example'>
37<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
38
82cc1f9a
MS
39<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
40 NULL, "ipp://localhost/printers/");
5a738aea
MS
41<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
42 NULL, cupsUser());
43</pre>
44
82cc1f9a 45<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>
5a738aea
MS
46
47<pre class='example'>
48#include &lt;cups/cups.h&gt;
49
50
51<a href='#ipp_t'>ipp_t</a> *<a name='get_jobs'>get_jobs</a>(void)
52{
53 <a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
54
82cc1f9a
MS
55 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
56 NULL, "ipp://localhost/printers/");
5a738aea
MS
57 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
58 NULL, cupsUser());
59
60 return (<a href='#cupsDoRequest'>cupsDoRequest</a>(CUPS_HTTP_DEFAULT, request, "/"));
61}
62</pre>
63
82cc1f9a 64<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>
5a738aea
MS
65
66<pre class='example'>
67<a href='#ipp_t'>ipp_t</a> *response;
68<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
69
70attr = <a href='#ippFindAttribute'>ippFindAttribute</a>(response, "printer-state", IPP_TAG_ENUM);
71</pre>
72
82cc1f9a 73<p>You can also walk the list of attributes with a simple <code>for</code> loop like this:</p>
5a738aea
MS
74
75<pre class='example'>
76<a href='#ipp_t'>ipp_t</a> *response;
77<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
78
82cc1f9a
MS
79for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
80 if (ippGetName(attr) == NULL)
5a738aea
MS
81 puts("--SEPARATOR--");
82 else
82cc1f9a 83 puts(ippGetName(attr));
ef416fc2 84</pre>
85
82cc1f9a 86<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>
5a738aea
MS
87
88<pre class='example'>
89<a href='#ipp_t'>ipp_t</a> *response = <a href='#get_jobs'>get_jobs</a>();
90
91if (response != NULL)
92{
93 <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
82cc1f9a 94 const char *attrname;
5a738aea 95 int job_id = 0;
82cc1f9a
MS
96 const char *job_name = NULL;
97 const char *job_originating_user_name = NULL;
5a738aea
MS
98
99 puts("Job ID Owner Title");
100 puts("------ ---------------- ---------------------------------");
101
82cc1f9a 102 for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
5a738aea
MS
103 {
104 /* Attributes without names are separators between jobs */
82cc1f9a
MS
105 attrname = ippGetName(attr);
106 if (attrname == NULL)
5a738aea 107 {
82cc1f9a
MS
108 if (job_id > 0)
109 {
110 if (job_name == NULL)
111 job_name = "(withheld)";
112
113 if (job_originating_user_name == NULL)
114 job_originating_user_name = "(withheld)";
115
5a738aea 116 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
82cc1f9a 117 }
5a738aea
MS
118
119 job_id = 0;
120 job_name = NULL;
121 job_originating_user_name = NULL;
122 continue;
123 }
82cc1f9a
MS
124 else if (!strcmp(attrname, "job-id") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_INTEGER)
125 job_id = ippGetInteger(attr, 0);
126 else if (!strcmp(attrname, "job-name") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_NAME)
127 job_name = ippGetString(attr, 0, NULL);
128 else if (!strcmp(attrname, "job-originating-user-name") &amp;&amp;
129 ippGetValueTag(attr) == IPP_TAG_NAME)
130 job_originating_user_name = ippGetString(attr, 0, NULL);
5a738aea
MS
131 }
132
82cc1f9a
MS
133 if (job_id > 0)
134 {
135 if (job_name == NULL)
136 job_name = "(withheld)";
137
138 if (job_originating_user_name == NULL)
139 job_originating_user_name = "(withheld)";
140
5a738aea 141 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
82cc1f9a 142 }
5a738aea
MS
143}
144</pre>
145
146<h3><a name='CREATING_URI_STRINGS'>Creating URI Strings</a></h3>
147
148<p>To ensure proper encoding, the
149<a href='#httpAssembleURIf'><code>httpAssembleURIf</code></a> function must be
150used to format a "printer-uri" string for all printer-based requests:</p>
151
152<pre class='example'>
153const char *name = "Foo";
154char uri[1024];
155<a href='#ipp_t'>ipp_t</a> *request;
156
157<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
158 ippPort(), "/printers/%s", name);
159<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
160</pre>
161
162<h3><a name='SENDING_REQUESTS_WITH_FILES'>Sending Requests with Files</a></h3>
163
164<p>The <a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> and
165<a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> functions are
166used for requests involving files. The
167<a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> function
168attaches the named file to a request and is typically used when sending a print
169file or changing a printer's PPD file:</p>
170
171<pre class='example'>
172const char *filename = "/usr/share/cups/data/testprint.ps";
173const char *name = "Foo";
174char uri[1024];
175char resource[1024];
176<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_PRINT_JOB);
177<a href='#ipp_t'>ipp_t</a> *response;
178
179/* Use httpAssembleURIf for the printer-uri string */
180<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
181 ippPort(), "/printers/%s", name);
182<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
183<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
184 NULL, cupsUser());
185<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
186 NULL, "testprint.ps");
ef416fc2 187
5a738aea
MS
188/* Use snprintf for the resource path */
189snprintf(resource, sizeof(resource), "/printers/%s", name);
190
191response = <a href='#cupsDoFileRequest'>cupsDoFileRequest</a>(CUPS_HTTP_DEFAULT, request, resource, filename);
192</pre>
193
194<p>The <a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> function
195optionally attaches a file to the request and optionally saves a file in the
196response from the server. It is used when using a pipe for the request
197attachment or when using a request that returns a file, currently only
198<code>CUPS_GET_DOCUMENT</code> and <code>CUPS_GET_PPD</code>. For example,
199the following code will download the PPD file for the sample HP LaserJet
200printer driver:</p>
201
202<pre class='example'>
203char tempfile[1024];
204int tempfd;
205<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
206<a href='#ipp_t'>ipp_t</a> *response;
207
208<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
209 NULL, "laserjet.ppd");
210
211tempfd = cupsTempFd(tempfile, sizeof(tempfile));
212
213response = <a href='#cupsDoIORequest'>cupsDoIORequest</a>(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
214</pre>
215
216<p>The example passes <code>-1</code> for the input file descriptor to specify
217that no file is to be attached to the request. The PPD file attached to the
218response is written to the temporary file descriptor we created using the
219<code>cupsTempFd</code> function.</p>
220
221<h3><a name='ASYNCHRONOUS_REQUEST_PROCESSING'>Asynchronous Request Processing</a></h3>
222
223<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> and
224<a href='#cupsGetResponse'><code>cupsGetResponse</code></a> support
225asynchronous communications with the server. Unlike the other request
226functions, the IPP request is not automatically freed, so remember to
227free your request with the <a href='#ippDelete'><code>ippDelete</code></a>
228function.</p>
229
230<p>File data is attached to the request using the
231<a href='#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>
232function, while file data returned from the server is read using the
233<a href='#cupsReadResponseData'><code>cupsReadResponseData</code></a>
234function. We can rewrite the previous <code>CUPS_GET_PPD</code> example
235to use the asynchronous functions quite easily:</p>
236
237<pre class='example'>
238char tempfile[1024];
239int tempfd;
240<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
241<a href='#ipp_t'>ipp_t</a> *response;
242
243<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
244 NULL, "laserjet.ppd");
245
246tempfd = cupsTempFd(tempfile, sizeof(tempfile));
247
248if (<a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
249{
250 response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
251
252 if (response != NULL)
253 {
254 ssize_t bytes;
255 char buffer[8192];
256
257 while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
258 write(tempfd, buffer, bytes);
259 }
260}
261
262/* Free the request! */
263<a href='#ippDelete'>ippDelete</a>(request);
264</pre>
265
266<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> function
267returns the initial HTTP request status, typically either
268<code>HTTP_CONTINUE</code> or <code>HTTP_UNAUTHORIZED</code>. The latter status
269is returned when the request requires authentication of some sort. The
270<a href='#cupsDoAuthentication'><code>cupsDoAuthentication</code></a> function
271must be called when your see <code>HTTP_UNAUTHORIZED</code> and the request
272re-sent. We can add authentication support to our example code by using a
273<code>do ... while</code> loop:</p>
274
275<pre class='example'>
276char tempfile[1024];
277int tempfd;
278<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
279<a href='#ipp_t'>ipp_t</a> *response;
280http_status_t status;
281
282<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
283 NULL, "laserjet.ppd");
284
285tempfd = cupsTempFd(tempfile, sizeof(tempfile));
286
287/* Loop for authentication */
288do
289{
75bd9771 290 status = <a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/");
5a738aea
MS
291
292 if (status == HTTP_UNAUTHORIZED)
293 {
294 /* Try to authenticate, break out of the loop if that fails */
295 if (<a href='#cupsDoAuthentication'>cupsDoAuthentication</a>(CUPS_HTTP_DEFAULT, "POST", "/"))
296 break;
297 }
298}
299while (status != HTTP_CONTINUE &amp;&amp; status != HTTP_UNAUTHORIZED);
300
301if (status == HTTP_CONTINUE)
302{
303 response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
304
305 if (response != NULL)
306 {
307 ssize_t bytes;
308 char buffer[8192];
309
310 while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
311 write(tempfd, buffer, bytes);
312 }
313}
314
315/* Free the request! */
316<a href='#ippDelete'>ippDelete</a>(request);
317</pre>