2 "$Id: api-httpipp.shtml 7684 2008-06-23 16:47:38Z mike $"
4 HTTP and IPP API introduction for CUPS.
6 Copyright 2007-2012 by Apple Inc.
7 Copyright 1997-2006 by Easy Software Products, all rights reserved.
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/".
16 <h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
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>
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>
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>
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>
37 <a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
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",
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>
48 #include <cups/cups.h>
51 <a href='#ipp_t'>ipp_t</a> *<a name='get_jobs'>get_jobs</a>(void)
53 <a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
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",
60 return (<a href='#cupsDoRequest'>cupsDoRequest</a>(CUPS_HTTP_DEFAULT, request, "/"));
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>
67 <a href='#ipp_t'>ipp_t</a> *response;
68 <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
70 attr = <a href='#ippFindAttribute'>ippFindAttribute</a>(response, "printer-state", IPP_TAG_ENUM);
73 <p>You can also walk the list of attributes with a simple <code>for</code> loop like this:</p>
76 <a href='#ipp_t'>ipp_t</a> *response;
77 <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
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--");
83 puts(ippGetName(attr));
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>
89 <a href='#ipp_t'>ipp_t</a> *response = <a href='#get_jobs'>get_jobs</a>();
93 <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
96 const char *job_name = NULL;
97 const char *job_originating_user_name = NULL;
99 puts("Job ID Owner Title");
100 puts("------ ---------------- ---------------------------------");
102 for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
104 /* Attributes without names are separators between jobs */
105 attrname = ippGetName(attr);
106 if (attrname == NULL)
110 if (job_name == NULL)
111 job_name = "(withheld)";
113 if (job_originating_user_name == NULL)
114 job_originating_user_name = "(withheld)";
116 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
121 job_originating_user_name = NULL;
124 else if (!strcmp(attrname, "job-id") && ippGetValueTag(attr) == IPP_TAG_INTEGER)
125 job_id = ippGetInteger(attr, 0);
126 else if (!strcmp(attrname, "job-name") && ippGetValueTag(attr) == IPP_TAG_NAME)
127 job_name = ippGetString(attr, 0, NULL);
128 else if (!strcmp(attrname, "job-originating-user-name") &&
129 ippGetValueTag(attr) == IPP_TAG_NAME)
130 job_originating_user_name = ippGetString(attr, 0, NULL);
135 if (job_name == NULL)
136 job_name = "(withheld)";
138 if (job_originating_user_name == NULL)
139 job_originating_user_name = "(withheld)";
141 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
146 <h3><a name='CREATING_URI_STRINGS'>Creating URI Strings</a></h3>
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>
152 <pre class='example'>
153 const char *name = "Foo";
155 <a href='#ipp_t'>ipp_t</a> *request;
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);
162 <h3><a name='SENDING_REQUESTS_WITH_FILES'>Sending Requests with Files</a></h3>
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>
171 <pre class='example'>
172 const char *filename = "/usr/share/cups/data/testprint.ps";
173 const char *name = "Foo";
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;
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",
185 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
186 NULL, "testprint.ps");
188 /* Use snprintf for the resource path */
189 snprintf(resource, sizeof(resource), "/printers/%s", name);
191 response = <a href='#cupsDoFileRequest'>cupsDoFileRequest</a>(CUPS_HTTP_DEFAULT, request, resource, filename);
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
202 <pre class='example'>
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;
208 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
209 NULL, "laserjet.ppd");
211 tempfd = cupsTempFd(tempfile, sizeof(tempfile));
213 response = <a href='#cupsDoIORequest'>cupsDoIORequest</a>(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
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>
221 <h3><a name='ASYNCHRONOUS_REQUEST_PROCESSING'>Asynchronous Request Processing</a></h3>
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>
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>
237 <pre class='example'>
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;
243 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
244 NULL, "laserjet.ppd");
246 tempfd = cupsTempFd(tempfile, sizeof(tempfile));
248 if (<a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
250 response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
252 if (response != NULL)
257 while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
258 write(tempfd, buffer, bytes);
262 /* Free the request! */
263 <a href='#ippDelete'>ippDelete</a>(request);
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>
275 <pre class='example'>
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;
282 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
283 NULL, "laserjet.ppd");
285 tempfd = cupsTempFd(tempfile, sizeof(tempfile));
287 /* Loop for authentication */
290 status = <a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/");
292 if (status == HTTP_UNAUTHORIZED)
294 /* Try to authenticate, break out of the loop if that fails */
295 if (<a href='#cupsDoAuthentication'>cupsDoAuthentication</a>(CUPS_HTTP_DEFAULT, "POST", "/"))
299 while (status != HTTP_CONTINUE && status != HTTP_UNAUTHORIZED);
301 if (status == HTTP_CONTINUE)
303 response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
305 if (response != NULL)
310 while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
311 write(tempfd, buffer, bytes);
315 /* Free the request! */
316 <a href='#ippDelete'>ippDelete</a>(request);