1 /*
2 * "lpinfo" command for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2018 by Apple Inc.
6 * Copyright © 1997-2006 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include <cups/cups-private.h>
17 #include <cups/adminutil.h>
18
19
20 /*
21 * Local functions...
22 */
23
24 static void device_cb(const char *device_class, const char *device_id,
25 const char *device_info,
26 const char *device_make_and_model,
27 const char *device_uri, const char *device_location,
28 void *user_data);
29 static int show_devices(int long_status, int timeout,
30 const char *include_schemes,
31 const char *exclude_schemes);
32 static int show_models(int long_status,
33 const char *device_id, const char *language,
34 const char *make_model, const char *product,
35 const char *include_schemes,
36 const char *exclude_schemes);
37 static void usage(void) _CUPS_NORETURN;
38
39
40 /*
41 * 'main()' - Parse options and show status information.
42 */
43
44 int
main(int argc,char * argv[])45 main(int argc, /* I - Number of command-line arguments */
46 char *argv[]) /* I - Command-line arguments */
47 {
48 int i; /* Looping var */
49 int long_status; /* Long listing? */
50 const char *opt, /* Option pointer */
51 *device_id, /* 1284 device ID */
52 *language, /* Language */
53 *make_model, /* Make and model */
54 *product, /* Product */
55 *include_schemes, /* Schemes to include */
56 *exclude_schemes; /* Schemes to exclude */
57 int timeout; /* Device timeout */
58
59
60 _cupsSetLocale(argv);
61
62 long_status = 0;
63 device_id = NULL;
64 language = NULL;
65 make_model = NULL;
66 product = NULL;
67 include_schemes = CUPS_INCLUDE_ALL;
68 exclude_schemes = CUPS_EXCLUDE_NONE;
69 timeout = CUPS_TIMEOUT_DEFAULT;
70
71 for (i = 1; i < argc; i ++)
72 {
73 if (!strcmp(argv[i], "--device-id"))
74 {
75 i ++;
76
77 if (i < argc)
78 device_id = argv[i];
79 else
80 {
81 _cupsLangPuts(stderr, _("lpinfo: Expected 1284 device ID string after \"--device-id\"."));
82 usage();
83 }
84 }
85 else if (!strncmp(argv[i], "--device-id=", 12) && argv[i][12])
86 {
87 device_id = argv[i] + 12;
88 }
89 else if (!strcmp(argv[i], "--exclude-schemes"))
90 {
91 i ++;
92
93 if (i < argc)
94 exclude_schemes = argv[i];
95 else
96 {
97 _cupsLangPuts(stderr, _("lpinfo: Expected scheme list after \"--exclude-schemes\"."));
98 usage();
99 }
100 }
101 else if (!strncmp(argv[i], "--exclude-schemes=", 18) && argv[i][18])
102 {
103 exclude_schemes = argv[i] + 18;
104 }
105 else if (!strcmp(argv[i], "--help"))
106 usage();
107 else if (!strcmp(argv[i], "--include-schemes"))
108 {
109 i ++;
110
111 if (i < argc)
112 include_schemes = argv[i];
113 else
114 {
115 _cupsLangPuts(stderr, _("lpinfo: Expected scheme list after \"--include-schemes\"."));
116 usage();
117 }
118 }
119 else if (!strncmp(argv[i], "--include-schemes=", 18) && argv[i][18])
120 {
121 include_schemes = argv[i] + 18;
122 }
123 else if (!strcmp(argv[i], "--language"))
124 {
125 i ++;
126 if (i < argc)
127 language = argv[i];
128 else
129 {
130 _cupsLangPuts(stderr, _("lpinfo: Expected language after \"--language\"."));
131 usage();
132 }
133 }
134 else if (!strncmp(argv[i], "--language=", 11) && argv[i][11])
135 {
136 language = argv[i] + 11;
137 }
138 else if (!strcmp(argv[i], "--make-and-model"))
139 {
140 i ++;
141 if (i < argc)
142 make_model= argv[i];
143 else
144 {
145 _cupsLangPuts(stderr, _("lpinfo: Expected make and model after \"--make-and-model\"."));
146 usage();
147 }
148 }
149 else if (!strncmp(argv[i], "--make-and-model=", 17) && argv[i][17])
150 {
151 make_model = argv[i] + 17;
152 }
153 else if (!strcmp(argv[i], "--product"))
154 {
155 i ++;
156 if (i < argc)
157 product = argv[i];
158 else
159 {
160 _cupsLangPuts(stderr, _("lpinfo: Expected product string after \"--product\"."));
161 usage();
162 }
163 }
164 else if (!strncmp(argv[i], "--product=", 10) && argv[i][10])
165 {
166 product = argv[i] + 10;
167 }
168 else if (!strcmp(argv[i], "--timeout"))
169 {
170 i ++;
171 if (i < argc)
172 timeout = atoi(argv[i]);
173 else
174 {
175 _cupsLangPuts(stderr, _("lpinfo: Expected timeout after \"--timeout\"."));
176 usage();
177 }
178 }
179 else if (!strncmp(argv[i], "--timeout=", 10) && argv[i][10])
180 {
181 timeout = atoi(argv[i] + 10);
182 }
183 else if (argv[i][0] == '-')
184 {
185 for (opt = argv[i] + 1; *opt; opt ++)
186 {
187 switch (*opt)
188 {
189 case 'E' : /* Encrypt */
190 #ifdef HAVE_TLS
191 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
192 #else
193 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
194 #endif /* HAVE_TLS */
195 break;
196
197 case 'h' : /* Connect to host */
198 if (opt[1] != '\0')
199 {
200 cupsSetServer(opt + 1);
201 opt += strlen(opt) - 1;
202 }
203 else
204 {
205 i ++;
206
207 if (i >= argc)
208 {
209 _cupsLangPuts(stderr, _("Error: need hostname after \"-h\" option."));
210 usage();
211 }
212
213 cupsSetServer(argv[i]);
214 }
215 break;
216
217 case 'l' : /* Show long listing */
218 long_status = 1;
219 break;
220
221 case 'm' : /* Show models */
222 if (show_models(long_status, device_id, language, make_model, product, include_schemes, exclude_schemes))
223 return (1);
224 break;
225
226 case 'v' : /* Show available devices */
227 if (show_devices(long_status, timeout, include_schemes, exclude_schemes))
228 return (1);
229 break;
230
231 default :
232 _cupsLangPrintf(stderr, _("%s: Unknown option \"%c\"."), argv[0], *opt);
233 usage();
234 }
235 }
236 }
237 else
238 {
239 _cupsLangPrintf(stderr, _("%s: Unknown argument \"%s\"."), argv[0], argv[i]);
240 usage();
241 }
242 }
243
244 return (0);
245 }
246
247
248 /*
249 * 'device_cb()' - Device callback.
250 */
251
252 static void
device_cb(const char * device_class,const char * device_id,const char * device_info,const char * device_make_and_model,const char * device_uri,const char * device_location,void * user_data)253 device_cb(
254 const char *device_class, /* I - device-class string */
255 const char *device_id, /* I - device-id string */
256 const char *device_info, /* I - device-info string */
257 const char *device_make_and_model, /* I - device-make-and-model string */
258 const char *device_uri, /* I - device-uri string */
259 const char *device_location, /* I - device-location string */
260 void *user_data) /* I - User data */
261 {
262 int *long_status; /* Show verbose info? */
263
264
265 /*
266 * Display the device...
267 */
268
269 long_status = (int *)user_data;
270
271 if (*long_status)
272 {
273 _cupsLangPrintf(stdout,
274 _("Device: uri = %s\n"
275 " class = %s\n"
276 " info = %s\n"
277 " make-and-model = %s\n"
278 " device-id = %s\n"
279 " location = %s"),
280 device_uri, device_class, device_info,
281 device_make_and_model, device_id, device_location);
282 }
283 else
284 _cupsLangPrintf(stdout, "%s %s", device_class, device_uri);
285 }
286
287
288 /*
289 * 'show_devices()' - Show available devices.
290 */
291
292 static int /* O - 0 on success, 1 on failure */
show_devices(int long_status,int timeout,const char * include_schemes,const char * exclude_schemes)293 show_devices(
294 int long_status, /* I - Long status report? */
295 int timeout, /* I - Timeout */
296 const char *include_schemes, /* I - List of schemes to include */
297 const char *exclude_schemes) /* I - List of schemes to exclude */
298 {
299 if (cupsGetDevices(CUPS_HTTP_DEFAULT, timeout, include_schemes,
300 exclude_schemes, device_cb, &long_status) != IPP_OK)
301 {
302 _cupsLangPrintf(stderr, "lpinfo: %s", cupsLastErrorString());
303 return (1);
304 }
305
306 return (0);
307 }
308
309
310 /*
311 * 'show_models()' - Show available PPDs.
312 */
313
314 static int /* O - 0 on success, 1 on failure */
show_models(int long_status,const char * device_id,const char * language,const char * make_model,const char * product,const char * include_schemes,const char * exclude_schemes)315 show_models(
316 int long_status, /* I - Long status report? */
317 const char *device_id, /* I - 1284 device ID */
318 const char *language, /* I - Language */
319 const char *make_model, /* I - Make and model */
320 const char *product, /* I - Product */
321 const char *include_schemes, /* I - List of schemes to include */
322 const char *exclude_schemes) /* I - List of schemes to exclude */
323 {
324 ipp_t *request, /* IPP Request */
325 *response; /* IPP Response */
326 ipp_attribute_t *attr; /* Current attribute */
327 const char *ppd_device_id, /* Pointer to ppd-device-id */
328 *ppd_language, /* Pointer to ppd-natural-language */
329 *ppd_make_model, /* Pointer to ppd-make-and-model */
330 *ppd_name; /* Pointer to ppd-name */
331 cups_option_t option; /* in/exclude-schemes option */
332
333
334 /*
335 * Build a CUPS_GET_PPDS request...
336 */
337
338 request = ippNewRequest(CUPS_GET_PPDS);
339
340 if (device_id)
341 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id",
342 NULL, device_id);
343 if (language)
344 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "ppd-language",
345 NULL, language);
346 if (make_model)
347 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make-and-model",
348 NULL, make_model);
349 if (product)
350 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-product",
351 NULL, product);
352
353 if (include_schemes)
354 {
355 option.name = "include-schemes";
356 option.value = (char *)include_schemes;
357
358 cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
359 }
360
361 if (exclude_schemes)
362 {
363 option.name = "exclude-schemes";
364 option.value = (char *)exclude_schemes;
365
366 cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION);
367 }
368
369 /*
370 * Do the request and get back a response...
371 */
372
373 if ((response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) != NULL)
374 {
375 /*
376 * Loop through the device list and display them...
377 */
378
379 if (response->request.status.status_code > IPP_OK_CONFLICT)
380 {
381 _cupsLangPrintf(stderr, "lpinfo: %s", cupsLastErrorString());
382 ippDelete(response);
383 return (1);
384 }
385
386 for (attr = response->attrs; attr != NULL; attr = attr->next)
387 {
388 /*
389 * Skip leading attributes until we hit a PPD...
390 */
391
392 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
393 attr = attr->next;
394
395 if (attr == NULL)
396 break;
397
398 /*
399 * Pull the needed attributes from this PPD...
400 */
401
402 ppd_device_id = "NONE";
403 ppd_language = NULL;
404 ppd_make_model = NULL;
405 ppd_name = NULL;
406
407 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
408 {
409 if (!strcmp(attr->name, "ppd-device-id") &&
410 attr->value_tag == IPP_TAG_TEXT)
411 ppd_device_id = attr->values[0].string.text;
412 else if (!strcmp(attr->name, "ppd-natural-language") &&
413 attr->value_tag == IPP_TAG_LANGUAGE)
414 ppd_language = attr->values[0].string.text;
415 else if (!strcmp(attr->name, "ppd-make-and-model") &&
416 attr->value_tag == IPP_TAG_TEXT)
417 ppd_make_model = attr->values[0].string.text;
418 else if (!strcmp(attr->name, "ppd-name") &&
419 attr->value_tag == IPP_TAG_NAME)
420 ppd_name = attr->values[0].string.text;
421
422 attr = attr->next;
423 }
424
425 /*
426 * See if we have everything needed...
427 */
428
429 if (ppd_language == NULL || ppd_make_model == NULL || ppd_name == NULL)
430 {
431 if (attr == NULL)
432 break;
433 else
434 continue;
435 }
436
437 /*
438 * Display the device...
439 */
440
441 if (long_status)
442 {
443 _cupsLangPrintf(stdout,
444 _("Model: name = %s\n"
445 " natural_language = %s\n"
446 " make-and-model = %s\n"
447 " device-id = %s"),
448 ppd_name, ppd_language, ppd_make_model, ppd_device_id);
449 }
450 else
451 _cupsLangPrintf(stdout, "%s %s", ppd_name, ppd_make_model);
452
453 if (attr == NULL)
454 break;
455 }
456
457 ippDelete(response);
458
459 /*
460 * Show the "everywhere" model, which is handled by the lpadmin command...
461 */
462
463 if ((!include_schemes || strstr(include_schemes, "everywhere")) && (!exclude_schemes || !strstr(exclude_schemes, "everywhere")))
464 {
465 if (long_status)
466 {
467 _cupsLangPrintf(stdout,
468 _("Model: name = %s\n"
469 " natural_language = %s\n"
470 " make-and-model = %s\n"
471 " device-id = %s"),
472 "everywhere", cupsLangDefault()->language, "IPP Everywhere™", "CMD:PwgRaster");
473 }
474 else
475 _cupsLangPuts(stdout, "everywhere IPP Everywhere");
476 }
477 }
478 else
479 {
480 _cupsLangPrintf(stderr, "lpinfo: %s", cupsLastErrorString());
481
482 return (1);
483 }
484
485 return (0);
486 }
487
488
489 /*
490 * 'usage()' - Show program usage and exit.
491 */
492
493 static void
usage(void)494 usage(void)
495 {
496 _cupsLangPuts(stdout, _("Usage: lpinfo [options] -m\n"
497 " lpinfo [options] -v"));
498 _cupsLangPuts(stdout, _("Options:"));
499 _cupsLangPuts(stdout, _("-E Encrypt the connection to the server"));
500 _cupsLangPuts(stdout, _("-h server[:port] Connect to the named server and port"));
501 _cupsLangPuts(stdout, _("-l Show verbose (long) output"));
502 _cupsLangPuts(stdout, _("-m Show models"));
503 _cupsLangPuts(stdout, _("-v Show devices"));
504 _cupsLangPuts(stdout, _("--device-id device-id Show models matching the given IEEE 1284 device ID"));
505 _cupsLangPuts(stdout, _("--exclude-schemes scheme-list\n"
506 " Exclude the specified URI schemes"));
507 _cupsLangPuts(stdout, _("--include-schemes scheme-list\n"
508 " Include only the specified URI schemes"));
509 _cupsLangPuts(stdout, _("--language locale Show models matching the given locale"));
510 _cupsLangPuts(stdout, _("--make-and-model name Show models matching the given make and model name"));
511 _cupsLangPuts(stdout, _("--product name Show models matching the given PostScript product"));
512 _cupsLangPuts(stdout, _("--timeout seconds Specify the maximum number of seconds to discover devices"));
513
514 exit(1);
515 }
516