• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. */
cupsGetDevices(http_t * http,int timeout,const char * include_schemes,const char * exclude_schemes,cups_device_cb_t callback,void * user_data)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 }
270