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