Coverage Report

Created: 2025-06-24 07:01

/src/cups/cups/getdevices.c
Line
Count
Source (jump to first uncovered line)
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
0
{
44
0
  ipp_t   *request,   /* CUPS-Get-Devices request */
45
0
    *response;    /* CUPS-Get-Devices response */
46
0
  ipp_attribute_t *attr;    /* Current attribute */
47
0
  const char  *device_class,    /* device-class value */
48
0
    *device_id,   /* device-id value */
49
0
    *device_info,   /* device-info value */
50
0
    *device_location, /* device-location value */
51
0
    *device_make_and_model, /* device-make-and-model value */
52
0
    *device_uri;    /* device-uri value */
53
0
  int   blocking;   /* Current blocking-IO mode */
54
0
  cups_option_t option;     /* in/exclude-schemes option */
55
0
  http_status_t status;     /* HTTP status of request */
56
0
  ipp_state_t state;      /* IPP response state */
57
58
59
 /*
60
  * Range check input...
61
  */
62
63
0
  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
0
  if (!callback)
66
0
    return (IPP_STATUS_ERROR_INTERNAL);
67
68
0
  if (!http)
69
0
    http = _cupsConnect();
70
71
0
  if (!http)
72
0
    return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
73
74
 /*
75
  * Create a CUPS-Get-Devices request...
76
  */
77
78
0
  request = ippNewRequest(IPP_OP_CUPS_GET_DEVICES);
79
80
0
  if (timeout > 0)
81
0
    ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "timeout",
82
0
                  timeout);
83
84
0
  if (include_schemes)
85
0
  {
86
0
    option.name  = "include-schemes";
87
0
    option.value = (char *)include_schemes;
88
89
0
    cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
90
0
  }
91
92
0
  if (exclude_schemes)
93
0
  {
94
0
    option.name  = "exclude-schemes";
95
0
    option.value = (char *)exclude_schemes;
96
97
0
    cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
98
0
  }
99
100
 /*
101
  * Send the request and do any necessary authentication...
102
  */
103
104
0
  do
105
0
  {
106
0
    DEBUG_puts("2cupsGetDevices: Sending request...");
107
0
    status = cupsSendRequest(http, request, "/", ippLength(request));
108
109
0
    DEBUG_puts("2cupsGetDevices: Waiting for response status...");
110
0
    while (status == HTTP_STATUS_CONTINUE)
111
0
      status = httpUpdate(http);
112
113
0
    if (status != HTTP_STATUS_OK)
114
0
    {
115
0
      httpFlush(http);
116
117
0
      if (status == HTTP_STATUS_UNAUTHORIZED)
118
0
      {
119
       /*
120
  * See if we can do authentication...
121
  */
122
123
0
  DEBUG_puts("2cupsGetDevices: Need authorization...");
124
125
0
  if (!cupsDoAuthentication(http, "POST", "/"))
126
0
    httpReconnect2(http, 30000, NULL);
127
0
  else
128
0
  {
129
0
    status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
130
0
    break;
131
0
  }
132
0
      }
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
0
    }
148
0
  }
149
0
  while (status == HTTP_STATUS_UNAUTHORIZED ||
150
0
         status == HTTP_STATUS_UPGRADE_REQUIRED);
151
152
0
  DEBUG_printf(("2cupsGetDevices: status=%d", status));
153
154
0
  ippDelete(request);
155
156
0
  if (status != HTTP_STATUS_OK)
157
0
  {
158
0
    _cupsSetHTTPError(status);
159
0
    return (cupsLastError());
160
0
  }
161
162
 /*
163
  * Read the response in non-blocking mode...
164
  */
165
166
0
  blocking = httpGetBlocking(http);
167
0
  httpBlocking(http, 0);
168
169
0
  response              = ippNew();
170
0
  device_class          = NULL;
171
0
  device_id             = NULL;
172
0
  device_info           = NULL;
173
0
  device_location       = "";
174
0
  device_make_and_model = NULL;
175
0
  device_uri            = NULL;
176
0
  attr                  = NULL;
177
178
0
  DEBUG_puts("2cupsGetDevices: Reading response...");
179
180
0
  do
181
0
  {
182
0
    if ((state = ippRead(http, response)) == IPP_STATE_ERROR)
183
0
      break;
184
185
0
    DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state, (void *)response->last));
186
187
0
    if (!response->attrs)
188
0
      continue;
189
190
0
    while (attr != response->last)
191
0
    {
192
0
      if (!attr)
193
0
  attr = response->attrs;
194
0
      else
195
0
        attr = attr->next;
196
197
0
      DEBUG_printf(("2cupsGetDevices: attr->name=\"%s\", attr->value_tag=%d",
198
0
                    attr->name, attr->value_tag));
199
200
0
      if (!attr->name)
201
0
      {
202
0
        if (device_class && device_id && device_info && device_make_and_model &&
203
0
      device_uri)
204
0
          (*callback)(device_class, device_id, device_info,
205
0
                device_make_and_model, device_uri, device_location,
206
0
          user_data);
207
208
0
  device_class          = NULL;
209
0
  device_id             = NULL;
210
0
  device_info           = NULL;
211
0
  device_location       = "";
212
0
  device_make_and_model = NULL;
213
0
  device_uri            = NULL;
214
0
      }
215
0
      else if (!strcmp(attr->name, "device-class") &&
216
0
               attr->value_tag == IPP_TAG_KEYWORD)
217
0
        device_class = attr->values[0].string.text;
218
0
      else if (!strcmp(attr->name, "device-id") &&
219
0
               attr->value_tag == IPP_TAG_TEXT)
220
0
        device_id = attr->values[0].string.text;
221
0
      else if (!strcmp(attr->name, "device-info") &&
222
0
               attr->value_tag == IPP_TAG_TEXT)
223
0
        device_info = attr->values[0].string.text;
224
0
      else if (!strcmp(attr->name, "device-location") &&
225
0
               attr->value_tag == IPP_TAG_TEXT)
226
0
        device_location = attr->values[0].string.text;
227
0
      else if (!strcmp(attr->name, "device-make-and-model") &&
228
0
               attr->value_tag == IPP_TAG_TEXT)
229
0
        device_make_and_model = attr->values[0].string.text;
230
0
      else if (!strcmp(attr->name, "device-uri") &&
231
0
               attr->value_tag == IPP_TAG_URI)
232
0
        device_uri = attr->values[0].string.text;
233
0
    }
234
0
  }
235
0
  while (state != IPP_STATE_DATA);
236
237
0
  DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state, (void *)response->last));
238
239
0
  if (device_class && device_id && device_info && device_make_and_model &&
240
0
      device_uri)
241
0
    (*callback)(device_class, device_id, device_info,
242
0
    device_make_and_model, device_uri, device_location, user_data);
243
244
 /*
245
  * Set the IPP status and return...
246
  */
247
248
0
  httpBlocking(http, blocking);
249
0
  httpFlush(http);
250
251
0
  if (status == HTTP_STATUS_ERROR)
252
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0);
253
0
  else
254
0
  {
255
0
    attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
256
257
0
    DEBUG_printf(("cupsGetDevices: status-code=%s, status-message=\"%s\"",
258
0
      ippErrorString(response->request.status.status_code),
259
0
      attr ? attr->values[0].string.text : ""));
260
261
0
    _cupsSetError(response->request.status.status_code,
262
0
      attr ? attr->values[0].string.text :
263
0
          ippErrorString(response->request.status.status_code), 0);
264
0
  }
265
266
0
  ippDelete(response);
267
268
0
  return (cupsLastError());
269
0
}