Home | History | Annotate | Download | only in cups
      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