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