Imported Upstream version 2.2.2
[platform/upstream/cups.git] / cups / getdevices.c
1 /*
2  * cupsGetDevices implementation for CUPS.
3  *
4  * Copyright 2008-2016 by Apple Inc.
5  *
6  * These coded instructions, statements, and computer programs are the
7  * property of Apple Inc. and are protected by Federal copyright
8  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
9  * which should have been included with this file.  If this file is
10  * missing or damaged, see the license at "http://www.cups.org/".
11  *
12  * This file is subject to the Apple OS-Developed Software exception.
13  */
14
15 /*
16  * Include necessary headers...
17  */
18
19 #include "cups-private.h"
20 #include "adminutil.h"
21
22
23 /*
24  * 'cupsGetDevices()' - Get available printer devices.
25  *
26  * This function sends a CUPS-Get-Devices request and streams the discovered
27  * devices to the specified callback function. The "timeout" parameter controls
28  * how long the request lasts, while the "include_schemes" and "exclude_schemes"
29  * parameters provide comma-delimited lists of backends to include or omit from
30  * the request respectively.
31  *
32  * @since CUPS 1.4/macOS 10.6@
33  */
34
35 ipp_status_t                            /* O - Request status - @code IPP_OK@ on success. */
36 cupsGetDevices(
37     http_t           *http,             /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
38     int              timeout,           /* I - Timeout in seconds or @code CUPS_TIMEOUT_DEFAULT@ */
39     const char       *include_schemes,  /* I - Comma-separated URI schemes to include or @code CUPS_INCLUDE_ALL@ */
40     const char       *exclude_schemes,  /* I - Comma-separated URI schemes to exclude or @code CUPS_EXCLUDE_NONE@ */
41     cups_device_cb_t callback,          /* I - Callback function */
42     void             *user_data)        /* I - User data pointer */
43 {
44   ipp_t         *request,               /* CUPS-Get-Devices request */
45                 *response;              /* CUPS-Get-Devices response */
46   ipp_attribute_t *attr;                /* Current attribute */
47   const char    *device_class,          /* device-class value */
48                 *device_id,             /* device-id value */
49                 *device_info,           /* device-info value */
50                 *device_location,       /* device-location value */
51                 *device_make_and_model, /* device-make-and-model value */
52                 *device_uri;            /* device-uri value */
53   int           blocking;               /* Current blocking-IO mode */
54   cups_option_t option;                 /* in/exclude-schemes option */
55   http_status_t status;                 /* HTTP status of request */
56   ipp_state_t   state;                  /* IPP response state */
57
58
59  /*
60   * Range check input...
61   */
62
63   DEBUG_printf(("cupsGetDevices(http=%p, timeout=%d, include_schemes=\"%s\", exclude_schemes=\"%s\", callback=%p, user_data=%p)", (void *)http, timeout, include_schemes, exclude_schemes, (void *)callback, user_data));
64
65   if (!callback)
66     return (IPP_STATUS_ERROR_INTERNAL);
67
68   if (!http)
69     http = _cupsConnect();
70
71   if (!http)
72     return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
73
74  /*
75   * Create a CUPS-Get-Devices request...
76   */
77
78   request = ippNewRequest(IPP_OP_CUPS_GET_DEVICES);
79
80   if (timeout > 0)
81     ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "timeout",
82                   timeout);
83
84   if (include_schemes)
85   {
86     option.name  = "include-schemes";
87     option.value = (char *)include_schemes;
88
89     cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
90   }
91
92   if (exclude_schemes)
93   {
94     option.name  = "exclude-schemes";
95     option.value = (char *)exclude_schemes;
96
97     cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
98   }
99
100  /*
101   * Send the request and do any necessary authentication...
102   */
103
104   do
105   {
106     DEBUG_puts("2cupsGetDevices: Sending request...");
107     status = cupsSendRequest(http, request, "/", ippLength(request));
108
109     DEBUG_puts("2cupsGetDevices: Waiting for response status...");
110     while (status == HTTP_STATUS_CONTINUE)
111       status = httpUpdate(http);
112
113     if (status != HTTP_STATUS_OK)
114     {
115       httpFlush(http);
116
117       if (status == HTTP_STATUS_UNAUTHORIZED)
118       {
119        /*
120         * See if we can do authentication...
121         */
122
123         DEBUG_puts("2cupsGetDevices: Need authorization...");
124
125         if (!cupsDoAuthentication(http, "POST", "/"))
126           httpReconnect2(http, 30000, NULL);
127         else
128         {
129           status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
130           break;
131         }
132       }
133
134 #ifdef HAVE_SSL
135       else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
136       {
137        /*
138         * Force a reconnect with encryption...
139         */
140
141         DEBUG_puts("2cupsGetDevices: Need encryption...");
142
143         if (!httpReconnect2(http, 30000, NULL))
144           httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
145       }
146 #endif /* HAVE_SSL */
147     }
148   }
149   while (status == HTTP_STATUS_UNAUTHORIZED ||
150          status == HTTP_STATUS_UPGRADE_REQUIRED);
151
152   DEBUG_printf(("2cupsGetDevices: status=%d", status));
153
154   ippDelete(request);
155
156   if (status != HTTP_STATUS_OK)
157   {
158     _cupsSetHTTPError(status);
159     return (cupsLastError());
160   }
161
162  /*
163   * Read the response in non-blocking mode...
164   */
165
166   blocking = httpGetBlocking(http);
167   httpBlocking(http, 0);
168
169   response              = ippNew();
170   device_class          = NULL;
171   device_id             = NULL;
172   device_info           = NULL;
173   device_location       = "";
174   device_make_and_model = NULL;
175   device_uri            = NULL;
176   attr                  = NULL;
177
178   DEBUG_puts("2cupsGetDevices: Reading response...");
179
180   do
181   {
182     if ((state = ippRead(http, response)) == IPP_STATE_ERROR)
183       break;
184
185     DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state, (void *)response->last));
186
187     if (!response->attrs)
188       continue;
189
190     while (attr != response->last)
191     {
192       if (!attr)
193         attr = response->attrs;
194       else
195         attr = attr->next;
196
197       DEBUG_printf(("2cupsGetDevices: attr->name=\"%s\", attr->value_tag=%d",
198                     attr->name, attr->value_tag));
199
200       if (!attr->name)
201       {
202         if (device_class && device_id && device_info && device_make_and_model &&
203             device_uri)
204           (*callback)(device_class, device_id, device_info,
205                       device_make_and_model, device_uri, device_location,
206                       user_data);
207
208         device_class          = NULL;
209         device_id             = NULL;
210         device_info           = NULL;
211         device_location       = "";
212         device_make_and_model = NULL;
213         device_uri            = NULL;
214       }
215       else if (!strcmp(attr->name, "device-class") &&
216                attr->value_tag == IPP_TAG_KEYWORD)
217         device_class = attr->values[0].string.text;
218       else if (!strcmp(attr->name, "device-id") &&
219                attr->value_tag == IPP_TAG_TEXT)
220         device_id = attr->values[0].string.text;
221       else if (!strcmp(attr->name, "device-info") &&
222                attr->value_tag == IPP_TAG_TEXT)
223         device_info = attr->values[0].string.text;
224       else if (!strcmp(attr->name, "device-location") &&
225                attr->value_tag == IPP_TAG_TEXT)
226         device_location = attr->values[0].string.text;
227       else if (!strcmp(attr->name, "device-make-and-model") &&
228                attr->value_tag == IPP_TAG_TEXT)
229         device_make_and_model = attr->values[0].string.text;
230       else if (!strcmp(attr->name, "device-uri") &&
231                attr->value_tag == IPP_TAG_URI)
232         device_uri = attr->values[0].string.text;
233     }
234   }
235   while (state != IPP_STATE_DATA);
236
237   DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state, (void *)response->last));
238
239   if (device_class && device_id && device_info && device_make_and_model &&
240       device_uri)
241     (*callback)(device_class, device_id, device_info,
242                 device_make_and_model, device_uri, device_location, user_data);
243
244  /*
245   * Set the IPP status and return...
246   */
247
248   httpBlocking(http, blocking);
249   httpFlush(http);
250
251   if (status == HTTP_STATUS_ERROR)
252     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0);
253   else
254   {
255     attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
256
257     DEBUG_printf(("cupsGetDevices: status-code=%s, status-message=\"%s\"",
258                   ippErrorString(response->request.status.status_code),
259                   attr ? attr->values[0].string.text : ""));
260
261     _cupsSetError(response->request.status.status_code,
262                   attr ? attr->values[0].string.text :
263                       ippErrorString(response->request.status.status_code), 0);
264   }
265
266   ippDelete(response);
267
268   return (cupsLastError());
269 }