]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/api-httpipp.shtml
cd0fd53b5b134d49f3a122b546e4938f158c692f
[thirdparty/cups.git] / cups / api-httpipp.shtml
1 <!--
2 "$Id: api-httpipp.shtml 7684 2008-06-23 16:47:38Z mike $"
3
4 HTTP and IPP API introduction for CUPS.
5
6 Copyright 2007-2012 by Apple Inc.
7 Copyright 1997-2006 by Easy Software Products, all rights reserved.
8
9 These coded instructions, statements, and computer programs are the
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/".
14 -->
15
16 <h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
17
18 <p>The CUPS HTTP and IPP APIs provide low-level access to the HTTP and IPP
19 protocols and CUPS scheduler. They are typically used by monitoring and
20 administration programs to perform specific functions not supported by the
21 high-level CUPS API functions.</p>
22
23 <p>The HTTP APIs use an opaque structure called
24 <a href='#http_t'><code>http_t</code></a> to manage connections to
25 a particular HTTP or IPP server. The
26 <a href='#httpConnectEncrypt'><code>httpConnectEncrypt</code></a> function is
27 used to create an instance of this structure for a particular server.
28 The 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
30 create a per-thread <a href='#http_t'><code>http_t</code></a> as needed.</p>
31
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>
35
36 <pre class='example'>
37 <a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
38
39 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
40 NULL, "ipp://localhost/printers/");
41 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
42 NULL, cupsUser());
43 </pre>
44
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>
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
55 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
56 NULL, "ipp://localhost/printers/");
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
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>
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
70 attr = <a href='#ippFindAttribute'>ippFindAttribute</a>(response, "printer-state", IPP_TAG_ENUM);
71 </pre>
72
73 <p>You can also walk the list of attributes with a simple <code>for</code> loop like this:</p>
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
79 for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
80 if (ippGetName(attr) == NULL)
81 puts("--SEPARATOR--");
82 else
83 puts(ippGetName(attr));
84 </pre>
85
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>
87
88 <pre class='example'>
89 <a href='#ipp_t'>ipp_t</a> *response = <a href='#get_jobs'>get_jobs</a>();
90
91 if (response != NULL)
92 {
93 <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
94 const char *attrname;
95 int job_id = 0;
96 const char *job_name = NULL;
97 const char *job_originating_user_name = NULL;
98
99 puts("Job ID Owner Title");
100 puts("------ ---------------- ---------------------------------");
101
102 for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
103 {
104 /* Attributes without names are separators between jobs */
105 attrname = ippGetName(attr);
106 if (attrname == NULL)
107 {
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
116 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
117 }
118
119 job_id = 0;
120 job_name = NULL;
121 job_originating_user_name = NULL;
122 continue;
123 }
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);
131 }
132
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
141 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
142 }
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
150 used to format a "printer-uri" string for all printer-based requests:</p>
151
152 <pre class='example'>
153 const char *name = "Foo";
154 char 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
166 used for requests involving files. The
167 <a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> function
168 attaches the named file to a request and is typically used when sending a print
169 file or changing a printer's PPD file:</p>
170
171 <pre class='example'>
172 const char *filename = "/usr/share/cups/data/testprint.ps";
173 const char *name = "Foo";
174 char uri[1024];
175 char 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");
187
188 /* Use snprintf for the resource path */
189 snprintf(resource, sizeof(resource), "/printers/%s", name);
190
191 response = <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
195 optionally attaches a file to the request and optionally saves a file in the
196 response from the server. It is used when using a pipe for the request
197 attachment 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,
199 the following code will download the PPD file for the sample HP LaserJet
200 printer driver:</p>
201
202 <pre class='example'>
203 char tempfile[1024];
204 int 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
211 tempfd = cupsTempFd(tempfile, sizeof(tempfile));
212
213 response = <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
217 that no file is to be attached to the request. The PPD file attached to the
218 response 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
225 asynchronous communications with the server. Unlike the other request
226 functions, the IPP request is not automatically freed, so remember to
227 free your request with the <a href='#ippDelete'><code>ippDelete</code></a>
228 function.</p>
229
230 <p>File data is attached to the request using the
231 <a href='#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>
232 function, while file data returned from the server is read using the
233 <a href='#cupsReadResponseData'><code>cupsReadResponseData</code></a>
234 function. We can rewrite the previous <code>CUPS_GET_PPD</code> example
235 to use the asynchronous functions quite easily:</p>
236
237 <pre class='example'>
238 char tempfile[1024];
239 int 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
246 tempfd = cupsTempFd(tempfile, sizeof(tempfile));
247
248 if (<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
267 returns the initial HTTP request status, typically either
268 <code>HTTP_CONTINUE</code> or <code>HTTP_UNAUTHORIZED</code>. The latter status
269 is returned when the request requires authentication of some sort. The
270 <a href='#cupsDoAuthentication'><code>cupsDoAuthentication</code></a> function
271 must be called when your see <code>HTTP_UNAUTHORIZED</code> and the request
272 re-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'>
276 char tempfile[1024];
277 int 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;
280 http_status_t status;
281
282 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
283 NULL, "laserjet.ppd");
284
285 tempfd = cupsTempFd(tempfile, sizeof(tempfile));
286
287 /* Loop for authentication */
288 do
289 {
290 status = <a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/");
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 }
299 while (status != HTTP_CONTINUE &amp;&amp; status != HTTP_UNAUTHORIZED);
300
301 if (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>