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 } 270