| <!-- |
| "$Id: api-httpipp.shtml 6649 2007-07-11 21:46:42Z mike $" |
| |
| HTTP and IPP API introduction for the Common UNIX Printing System (CUPS). |
| |
| 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 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 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>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='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> |
| |
| <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"); |
| |
| /* 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> |