xref: /aosp_15_r20/external/libcups/systemv/lpstat.c (revision 5e7646d21f1134fb0638875d812ef646c12ab91e)
1 /*
2  * "lpstat" 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 
17 
18 /*
19  * Local functions...
20  */
21 
22 static void	check_dest(const char *command, const char *name,
23 		           int *num_dests, cups_dest_t **dests);
24 static int	match_list(const char *list, const char *name);
25 static int	show_accepting(const char *printers, int num_dests,
26 		               cups_dest_t *dests);
27 static int	show_classes(const char *dests);
28 static void	show_default(cups_dest_t *dest);
29 static int	show_devices(const char *printers, int num_dests,
30 		             cups_dest_t *dests);
31 static int	show_jobs(const char *dests, const char *users, int long_status,
32 		          int ranking, const char *which);
33 static int	show_printers(const char *printers, int num_dests,
34 		              cups_dest_t *dests, int long_status);
35 static void	show_scheduler(void);
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 		status;			/* Exit status */
49   char		*opt;			/* Option pointer */
50   int		num_dests;		/* Number of user destinations */
51   cups_dest_t	*dests;			/* User destinations */
52   int		long_status;		/* Long status report? */
53   int		ranking;		/* Show job ranking? */
54   const char	*which;			/* Which jobs to show? */
55   char		op;			/* Last operation on command-line */
56 
57 
58   _cupsSetLocale(argv);
59 
60  /*
61   * Parse command-line options...
62   */
63 
64   num_dests   = 0;
65   dests       = NULL;
66   long_status = 0;
67   ranking     = 0;
68   status      = 0;
69   which       = "not-completed";
70   op          = 0;
71 
72   for (i = 1; i < argc; i ++)
73   {
74     if (!strcmp(argv[i], "--help"))
75       usage();
76     else if (argv[i][0] == '-')
77     {
78       for (opt = argv[i] + 1; *opt; opt ++)
79       {
80 	switch (*opt)
81 	{
82 	  case 'D' : /* Show description */
83 	      long_status = 1;
84 	      break;
85 
86 	  case 'E' : /* Encrypt */
87 #ifdef HAVE_SSL
88 	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
89 #else
90 	      _cupsLangPrintf(stderr,
91 			      _("%s: Sorry, no encryption support."),
92 			      argv[0]);
93 #endif /* HAVE_SSL */
94 	      break;
95 
96 	  case 'H' : /* Show server and port */
97 	      if (cupsServer()[0] == '/')
98 		_cupsLangPuts(stdout, cupsServer());
99 	      else
100 		_cupsLangPrintf(stdout, "%s:%d", cupsServer(), ippPort());
101 	      op = 'H';
102 	      break;
103 
104 	  case 'P' : /* Show paper types */
105 	      op = 'P';
106 	      break;
107 
108 	  case 'R' : /* Show ranking */
109 	      ranking = 1;
110 	      break;
111 
112 	  case 'S' : /* Show charsets */
113 	      op = 'S';
114 	      if (!argv[i][2])
115 		i ++;
116 	      break;
117 
118 	  case 'U' : /* Username */
119 	      if (opt[1] != '\0')
120 	      {
121 		cupsSetUser(opt + 1);
122 		opt += strlen(opt) - 1;
123 	      }
124 	      else
125 	      {
126 		i ++;
127 		if (i >= argc)
128 		{
129 		  _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
130 		  usage();
131 		}
132 
133 		cupsSetUser(argv[i]);
134 	      }
135 	      break;
136 
137 	  case 'W' : /* Show which jobs? */
138 	      if (opt[1] != '\0')
139 	      {
140 		which = opt + 1;
141 		opt += strlen(opt) - 1;
142 	      }
143 	      else
144 	      {
145 		i ++;
146 
147 		if (i >= argc)
148 		{
149 		  _cupsLangPrintf(stderr, _("%s: Error - need \"completed\", \"not-completed\", or \"all\" after \"-W\" option."), argv[0]);
150 		  usage();
151 		}
152 
153 		which = argv[i];
154 	      }
155 
156 	      if (strcmp(which, "completed") && strcmp(which, "not-completed") && strcmp(which, "all"))
157 	      {
158 		_cupsLangPrintf(stderr, _("%s: Error - need \"completed\", \"not-completed\", or \"all\" after \"-W\" option."), argv[0]);
159 		usage();
160 	      }
161 	      break;
162 
163 	  case 'a' : /* Show acceptance status */
164 	      op = 'a';
165 
166 	      if (opt[1] != '\0')
167 	      {
168 		check_dest(argv[0], opt + 1, &num_dests, &dests);
169 
170 		status |= show_accepting(opt + 1, num_dests, dests);
171 	        opt += strlen(opt) - 1;
172 	      }
173 	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
174 	      {
175 		i ++;
176 
177 		check_dest(argv[0], argv[i], &num_dests, &dests);
178 
179 		status |= show_accepting(argv[i], num_dests, dests);
180 	      }
181 	      else
182 	      {
183 		if (num_dests <= 1)
184 		{
185 		  cupsFreeDests(num_dests, dests);
186 		  num_dests = cupsGetDests(&dests);
187 
188 		  if (num_dests == 0 && (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST || cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
189 		  {
190 		    _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
191 		    return (1);
192 		  }
193 		}
194 
195 		status |= show_accepting(NULL, num_dests, dests);
196 	      }
197 	      break;
198 
199 	  case 'c' : /* Show classes and members */
200 	      op = 'c';
201 
202 	      if (opt[1] != '\0')
203 	      {
204 		check_dest(argv[0], opt + 1, &num_dests, &dests);
205 
206 		status |= show_classes(opt + 1);
207 	        opt += strlen(opt) - 1;
208 	      }
209 	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
210 	      {
211 		i ++;
212 
213 		check_dest(argv[0], argv[i], &num_dests, &dests);
214 
215 		status |= show_classes(argv[i]);
216 	      }
217 	      else
218 		status |= show_classes(NULL);
219 	      break;
220 
221 	  case 'd' : /* Show default destination */
222 	      op = 'd';
223 
224 	      if (num_dests != 1 || !dests[0].is_default)
225 	      {
226 		cupsFreeDests(num_dests, dests);
227 
228 		dests     = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
229 		num_dests = dests ? 1 : 0;
230 
231 		if (num_dests == 0 &&
232 		    (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
233 		     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
234 		{
235 		  _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
236 		  return (1);
237 		}
238 	      }
239 
240 	      show_default(dests);
241 	      break;
242 
243 	  case 'e' : /* List destinations */
244 	      {
245                 cups_dest_t *temp = NULL, *dest;
246                 int j, num_temp = cupsGetDests(&temp);
247 
248                 op = 'e';
249 
250                 for (j = num_temp, dest = temp; j > 0; j --, dest ++)
251                 {
252                   if (dest->instance)
253                     printf("%s/%s", dest->name, dest->instance);
254                   else
255                     fputs(dest->name, stdout);
256 
257                   if (long_status)
258                   {
259                     const char *printer_uri_supported = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
260                     const char *printer_is_temporary = cupsGetOption("printer-is-temporary", dest->num_options, dest->options);
261                     const char *type = "network";
262 
263                     if (printer_is_temporary && !strcmp(printer_is_temporary, "true"))
264                       type = "temporary";
265                     else if (printer_uri_supported)
266                       type = "permanent";
267 
268                     printf(" %s %s %s\n", type, printer_uri_supported ? printer_uri_supported : "none", cupsGetOption("device-uri", dest->num_options, dest->options));
269                   }
270                   else
271                     putchar('\n');
272                 }
273 
274                 cupsFreeDests(num_temp, temp);
275               }
276               break;
277 
278 	  case 'f' : /* Show forms */
279 	      op   = 'f';
280 	      if (opt[1] != '\0')
281 	      {
282 	        opt += strlen(opt) - 1;
283 	      }
284 	      else
285 	      {
286 		i ++;
287 		if (i >= argc)
288 		  return (1);
289 	      }
290 	      break;
291 
292 	  case 'h' : /* Connect to host */
293 	      if (opt[1] != '\0')
294 	      {
295 		cupsSetServer(opt + 1);
296 	        opt += strlen(opt) - 1;
297 	      }
298 	      else
299 	      {
300 		i ++;
301 
302 		if (i >= argc)
303 		{
304 		  _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
305 		  return (1);
306 		}
307 
308 		cupsSetServer(argv[i]);
309 	      }
310 	      break;
311 
312 	  case 'l' : /* Long status or long job status */
313 	      long_status = 2;
314 	      break;
315 
316 	  case 'o' : /* Show jobs by destination */
317 	      op = 'o';
318 
319 	      if (opt[1])
320 	      {
321 		check_dest(argv[0], opt + 1, &num_dests, &dests);
322 
323 		status |= show_jobs(opt + 1, NULL, long_status, ranking, which);
324 	        opt += strlen(opt) - 1;
325 	      }
326 	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
327 	      {
328 		i ++;
329 
330 		check_dest(argv[0], argv[i], &num_dests, &dests);
331 
332 		status |= show_jobs(argv[i], NULL, long_status, ranking, which);
333 	      }
334 	      else
335 		status |= show_jobs(NULL, NULL, long_status, ranking, which);
336 	      break;
337 
338 	  case 'p' : /* Show printers */
339 	      op = 'p';
340 
341 	      if (opt[1] != '\0')
342 	      {
343 		check_dest(argv[0], opt + 1, &num_dests, &dests);
344 
345 		status |= show_printers(opt + 1, num_dests, dests,
346 					long_status);
347 	        opt += strlen(opt) - 1;
348 	      }
349 	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
350 	      {
351 		i ++;
352 
353 		check_dest(argv[0], argv[i], &num_dests, &dests);
354 
355 		status |= show_printers(argv[i], num_dests, dests, long_status);
356 	      }
357 	      else
358 	      {
359 		if (num_dests <= 1)
360 		{
361 		  cupsFreeDests(num_dests, dests);
362 		  num_dests = cupsGetDests(&dests);
363 
364 		  if (num_dests == 0 &&
365 		      (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
366 		       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
367 		  {
368 		    _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
369 		    return (1);
370 		  }
371 		}
372 
373 		status |= show_printers(NULL, num_dests, dests, long_status);
374 	      }
375 	      break;
376 
377 	  case 'r' : /* Show scheduler status */
378 	      op = 'r';
379 
380 	      show_scheduler();
381 	      break;
382 
383 	  case 's' : /* Show summary */
384 	      op = 's';
385 
386 	      if (num_dests <= 1)
387 	      {
388 		cupsFreeDests(num_dests, dests);
389 		num_dests = cupsGetDests(&dests);
390 
391 		if (num_dests == 0 &&
392 		    (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
393 		     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
394 		{
395 		  _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
396 		  return (1);
397 		}
398 	      }
399 
400 	      show_default(cupsGetDest(NULL, NULL, num_dests, dests));
401 	      status |= show_classes(NULL);
402 	      status |= show_devices(NULL, num_dests, dests);
403 	      break;
404 
405 	  case 't' : /* Show all info */
406 	      op = 't';
407 
408 	      if (num_dests <= 1)
409 	      {
410 		cupsFreeDests(num_dests, dests);
411 		num_dests = cupsGetDests(&dests);
412 
413 		if (num_dests == 0 &&
414 		    (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
415 		     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
416 		{
417 		  _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
418 		  return (1);
419 		}
420 	      }
421 
422 	      show_scheduler();
423 	      show_default(cupsGetDest(NULL, NULL, num_dests, dests));
424 	      status |= show_classes(NULL);
425 	      status |= show_devices(NULL, num_dests, dests);
426 	      status |= show_accepting(NULL, num_dests, dests);
427 	      status |= show_printers(NULL, num_dests, dests, long_status);
428 	      status |= show_jobs(NULL, NULL, long_status, ranking, which);
429 	      break;
430 
431 	  case 'u' : /* Show jobs by user */
432 	      op = 'u';
433 
434 	      if (opt[1] != '\0')
435 	      {
436 		status |= show_jobs(NULL, opt + 1, long_status, ranking, which);
437 	        opt += strlen(opt) - 1;
438 	      }
439 	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
440 	      {
441 		i ++;
442 		status |= show_jobs(NULL, argv[i], long_status, ranking, which);
443 	      }
444 	      else
445 		status |= show_jobs(NULL, NULL, long_status, ranking, which);
446 	      break;
447 
448 	  case 'v' : /* Show printer devices */
449 	      op = 'v';
450 
451 	      if (opt[1] != '\0')
452 	      {
453 		check_dest(argv[0], opt + 1, &num_dests, &dests);
454 
455 		status |= show_devices(opt + 1, num_dests, dests);
456 	        opt += strlen(opt) - 1;
457 	      }
458 	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
459 	      {
460 		i ++;
461 
462 		check_dest(argv[0], argv[i], &num_dests, &dests);
463 
464 		status |= show_devices(argv[i], num_dests, dests);
465 	      }
466 	      else
467 	      {
468 		if (num_dests <= 1)
469 		{
470 		  cupsFreeDests(num_dests, dests);
471 		  num_dests = cupsGetDests(&dests);
472 
473 		  if (num_dests == 0 &&
474 		      (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
475 		       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
476 		  {
477 		    _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
478 		    return (1);
479 		  }
480 		}
481 
482 		status |= show_devices(NULL, num_dests, dests);
483 	      }
484 	      break;
485 
486 	  default :
487 	      _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], argv[i][1]);
488 	      usage();
489 	}
490       }
491     }
492     else
493     {
494       status |= show_jobs(argv[i], NULL, long_status, ranking, which);
495       op = 'o';
496     }
497   }
498 
499   if (!op)
500     status |= show_jobs(NULL, cupsUser(), long_status, ranking, which);
501 
502   return (status);
503 }
504 
505 
506 /*
507  * 'check_dest()' - Verify that the named destination(s) exists.
508  */
509 
510 static void
check_dest(const char * command,const char * name,int * num_dests,cups_dest_t ** dests)511 check_dest(const char  *command,	/* I  - Command name */
512            const char  *name,		/* I  - List of printer/class names */
513            int         *num_dests,	/* IO - Number of destinations */
514 	   cups_dest_t **dests)		/* IO - Destinations */
515 {
516   const char	*dptr;			/* Pointer into name */
517   char		*pptr,			/* Pointer into printer */
518 		printer[1024];		/* Current printer/class name */
519 
520 
521  /*
522   * Load the destination list as necessary...
523   */
524 
525   if (*num_dests <= 1)
526   {
527     if (*num_dests)
528       cupsFreeDests(*num_dests, *dests);
529 
530     if (strchr(name, ','))
531       *num_dests = cupsGetDests(dests);
532     else
533     {
534       strlcpy(printer, name, sizeof(printer));
535       if ((pptr = strchr(printer, '/')) != NULL)
536         *pptr++ = '\0';
537 
538       if ((*dests = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer, pptr)) == NULL)
539       {
540 	if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
541 	    cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
542 	  _cupsLangPrintf(stderr,
543 			  _("%s: Error - add '/version=1.1' to server name."),
544 			  command);
545 	else
546 	  _cupsLangPrintf(stderr,
547 			  _("%s: Invalid destination name in list \"%s\"."),
548 			  command, name);
549 
550         exit(1);
551       }
552       else
553       {
554         *num_dests = 1;
555         return;
556       }
557     }
558   }
559 
560  /*
561   * Scan the name string for printer/class name(s)...
562   */
563 
564   for (dptr = name; *dptr;)
565   {
566    /*
567     * Skip leading whitespace and commas...
568     */
569 
570     while (isspace(*dptr & 255) || *dptr == ',')
571       dptr ++;
572 
573     if (!*dptr)
574       break;
575 
576    /*
577     * Extract a single destination name from the name string...
578     */
579 
580     for (pptr = printer; !isspace(*dptr & 255) && *dptr != ',' && *dptr;)
581     {
582       if ((size_t)(pptr - printer) < (sizeof(printer) - 1))
583         *pptr++ = *dptr++;
584       else
585       {
586         _cupsLangPrintf(stderr,
587 	                _("%s: Invalid destination name in list \"%s\"."),
588 			command, name);
589         exit(1);
590       }
591     }
592 
593     *pptr = '\0';
594 
595    /*
596     * Check the destination...
597     */
598 
599     if (!cupsGetDest(printer, NULL, *num_dests, *dests))
600     {
601       if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
602           cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
603 	_cupsLangPrintf(stderr,
604 	                _("%s: Error - add '/version=1.1' to server name."),
605 			command);
606       else
607 	_cupsLangPrintf(stderr,
608 			_("%s: Unknown destination \"%s\"."), command, printer);
609 
610       exit(1);
611     }
612   }
613 }
614 
615 
616 /*
617  * 'match_list()' - Match a name from a list of comma or space-separated names.
618  */
619 
620 static int				/* O - 1 on match, 0 on no match */
match_list(const char * list,const char * name)621 match_list(const char *list,		/* I - List of names */
622            const char *name)		/* I - Name to find */
623 {
624   const char	*nameptr;		/* Pointer into name */
625 
626 
627  /*
628   * An empty list always matches...
629   */
630 
631   if (!list || !*list)
632     return (1);
633 
634   if (!name)
635     return (0);
636 
637   do
638   {
639    /*
640     * Skip leading whitespace and commas...
641     */
642 
643     while (isspace(*list & 255) || *list == ',')
644       list ++;
645 
646     if (!*list)
647       break;
648 
649    /*
650     * Compare names...
651     */
652 
653     for (nameptr = name;
654 	 *nameptr && *list && tolower(*nameptr & 255) == tolower(*list & 255);
655 	 nameptr ++, list ++);
656 
657     if (!*nameptr && (!*list || *list == ',' || isspace(*list & 255)))
658       return (1);
659 
660     while (*list && !isspace(*list & 255) && *list != ',')
661       list ++;
662   }
663   while (*list);
664 
665   return (0);
666 }
667 
668 
669 /*
670  * 'show_accepting()' - Show acceptance status.
671  */
672 
673 static int				/* O - 0 on success, 1 on fail */
show_accepting(const char * printers,int num_dests,cups_dest_t * dests)674 show_accepting(const char  *printers,	/* I - Destinations */
675                int         num_dests,	/* I - Number of user-defined dests */
676 	       cups_dest_t *dests)	/* I - User-defined destinations */
677 {
678   int		i;			/* Looping var */
679   ipp_t		*request,		/* IPP Request */
680 		*response;		/* IPP Response */
681   ipp_attribute_t *attr;		/* Current attribute */
682   const char	*printer,		/* Printer name */
683 		*message;		/* Printer device URI */
684   int		accepting;		/* Accepting requests? */
685   time_t	ptime;			/* Printer state time */
686   char		printer_state_time[255];/* Printer state time */
687   static const char *pattrs[] =		/* Attributes we need for printers... */
688 		{
689 		  "printer-name",
690 		  "printer-state-change-time",
691 		  "printer-state-message",
692 		  "printer-is-accepting-jobs"
693 		};
694 
695 
696   if (printers != NULL && !strcmp(printers, "all"))
697     printers = NULL;
698 
699  /*
700   * Build a CUPS_GET_PRINTERS request, which requires the following
701   * attributes:
702   *
703   *    attributes-charset
704   *    attributes-natural-language
705   *    requested-attributes
706   *    requesting-user-name
707   */
708 
709   request = ippNewRequest(CUPS_GET_PRINTERS);
710 
711   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
712                 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
713 		NULL, pattrs);
714 
715   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
716                NULL, cupsUser());
717 
718  /*
719   * Do the request and get back a response...
720   */
721 
722   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
723 
724   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
725       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
726   {
727     _cupsLangPrintf(stderr,
728 		    _("%s: Error - add '/version=1.1' to server name."),
729 		    "lpstat");
730     ippDelete(response);
731     return (1);
732   }
733   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
734   {
735     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
736     ippDelete(response);
737     return (1);
738   }
739 
740   if (response)
741   {
742    /*
743     * Loop through the printers returned in the list and display
744     * their devices...
745     */
746 
747     for (attr = response->attrs; attr != NULL; attr = attr->next)
748     {
749      /*
750       * Skip leading attributes until we hit a printer...
751       */
752 
753       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
754         attr = attr->next;
755 
756       if (attr == NULL)
757         break;
758 
759      /*
760       * Pull the needed attributes from this printer...
761       */
762 
763       printer   = NULL;
764       message   = NULL;
765       accepting = 1;
766       ptime     = 0;
767 
768       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
769       {
770         if (!strcmp(attr->name, "printer-name") &&
771 	    attr->value_tag == IPP_TAG_NAME)
772 	  printer = attr->values[0].string.text;
773         else if (!strcmp(attr->name, "printer-state-change-time") &&
774 	         attr->value_tag == IPP_TAG_INTEGER)
775 	  ptime = (time_t)attr->values[0].integer;
776         else if (!strcmp(attr->name, "printer-state-message") &&
777 	         attr->value_tag == IPP_TAG_TEXT)
778 	  message = attr->values[0].string.text;
779         else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
780 	         attr->value_tag == IPP_TAG_BOOLEAN)
781 	  accepting = attr->values[0].boolean;
782 
783         attr = attr->next;
784       }
785 
786      /*
787       * See if we have everything needed...
788       */
789 
790       if (printer == NULL)
791       {
792         if (attr == NULL)
793 	  break;
794 	else
795           continue;
796       }
797 
798      /*
799       * Display the printer entry if needed...
800       */
801 
802       if (match_list(printers, printer))
803       {
804         _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
805 
806         if (accepting)
807 	  _cupsLangPrintf(stdout, _("%s accepting requests since %s"),
808 			  printer, printer_state_time);
809 	else
810 	{
811 	  _cupsLangPrintf(stdout, _("%s not accepting requests since %s -"),
812 			  printer, printer_state_time);
813 	  _cupsLangPrintf(stdout, _("\t%s"),
814 			  (message && *message) ?
815 			      message : "reason unknown");
816         }
817 
818         for (i = 0; i < num_dests; i ++)
819 	  if (!_cups_strcasecmp(dests[i].name, printer) && dests[i].instance)
820 	  {
821             if (accepting)
822 	      _cupsLangPrintf(stdout, _("%s/%s accepting requests since %s"),
823 			      printer, dests[i].instance, printer_state_time);
824 	    else
825 	    {
826 	      _cupsLangPrintf(stdout,
827 	                      _("%s/%s not accepting requests since %s -"),
828 			      printer, dests[i].instance, printer_state_time);
829 	      _cupsLangPrintf(stdout, _("\t%s"),
830 	        	      (message && *message) ?
831 			          message : "reason unknown");
832             }
833 	  }
834       }
835 
836       if (attr == NULL)
837         break;
838     }
839 
840     ippDelete(response);
841   }
842 
843   return (0);
844 }
845 
846 
847 /*
848  * 'show_classes()' - Show printer classes.
849  */
850 
851 static int				/* O - 0 on success, 1 on fail */
show_classes(const char * dests)852 show_classes(const char *dests)		/* I - Destinations */
853 {
854   int		i;			/* Looping var */
855   ipp_t		*request,		/* IPP Request */
856 		*response,		/* IPP Response */
857 		*response2;		/* IPP response from remote server */
858   http_t	*http2;			/* Remote server */
859   ipp_attribute_t *attr;		/* Current attribute */
860   const char	*printer,		/* Printer class name */
861 		*printer_uri;		/* Printer class URI */
862   ipp_attribute_t *members;		/* Printer members */
863   char		method[HTTP_MAX_URI],	/* Request method */
864 		username[HTTP_MAX_URI],	/* Username:password */
865 		server[HTTP_MAX_URI],	/* Server name */
866 		resource[HTTP_MAX_URI];	/* Resource name */
867   int		port;			/* Port number */
868   static const char *cattrs[] =		/* Attributes we need for classes... */
869 		{
870 		  "printer-name",
871 		  "printer-uri-supported",
872 		  "member-names"
873 		};
874 
875 
876   if (dests != NULL && !strcmp(dests, "all"))
877     dests = NULL;
878 
879  /*
880   * Build a CUPS_GET_CLASSES request, which requires the following
881   * attributes:
882   *
883   *    attributes-charset
884   *    attributes-natural-language
885   *    requested-attributes
886   *    requesting-user-name
887   */
888 
889   request = ippNewRequest(CUPS_GET_CLASSES);
890 
891   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
892                 "requested-attributes", sizeof(cattrs) / sizeof(cattrs[0]),
893 		NULL, cattrs);
894 
895   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
896                NULL, cupsUser());
897 
898  /*
899   * Do the request and get back a response...
900   */
901 
902   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
903 
904   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
905       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
906   {
907     _cupsLangPrintf(stderr,
908 		    _("%s: Error - add '/version=1.1' to server name."),
909 		    "lpstat");
910     ippDelete(response);
911     return (1);
912   }
913   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
914   {
915     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
916     ippDelete(response);
917     return (1);
918   }
919 
920   if (response)
921   {
922     if (response->request.status.status_code > IPP_OK_CONFLICT)
923     {
924       _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
925       ippDelete(response);
926       return (1);
927     }
928 
929    /*
930     * Loop through the printers returned in the list and display
931     * their devices...
932     */
933 
934     for (attr = response->attrs; attr != NULL; attr = attr->next)
935     {
936      /*
937       * Skip leading attributes until we hit a job...
938       */
939 
940       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
941         attr = attr->next;
942 
943       if (attr == NULL)
944         break;
945 
946      /*
947       * Pull the needed attributes from this job...
948       */
949 
950       printer     = NULL;
951       printer_uri = NULL;
952       members     = NULL;
953 
954       do
955       {
956         if (!strcmp(attr->name, "printer-name") &&
957 	    attr->value_tag == IPP_TAG_NAME)
958 	  printer = attr->values[0].string.text;
959 
960         if (!strcmp(attr->name, "printer-uri-supported") &&
961 	    attr->value_tag == IPP_TAG_URI)
962 	  printer_uri = attr->values[0].string.text;
963 
964         if (!strcmp(attr->name, "member-names") &&
965 	    attr->value_tag == IPP_TAG_NAME)
966 	  members = attr;
967 
968         attr = attr->next;
969       }
970 	  while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER);
971 
972      /*
973       * If this is a remote class, grab the class info from the
974       * remote server...
975       */
976 
977       response2 = NULL;
978       if (members == NULL && printer_uri != NULL)
979       {
980         httpSeparateURI(HTTP_URI_CODING_ALL, printer_uri, method, sizeof(method),
981 	                username, sizeof(username), server, sizeof(server),
982 			&port, resource, sizeof(resource));
983 
984         if (!_cups_strcasecmp(server, cupsServer()))
985 	  http2 = CUPS_HTTP_DEFAULT;
986 	else
987 	  http2 = httpConnectEncrypt(server, port, cupsEncryption());
988 
989        /*
990 	* Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
991 	* following attributes:
992 	*
993 	*    attributes-charset
994 	*    attributes-natural-language
995 	*    printer-uri
996 	*    requested-attributes
997 	*/
998 
999 	request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1000 
1001 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1002 		     "printer-uri", NULL, printer_uri);
1003 
1004 	ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1005 		      "requested-attributes",
1006 		      sizeof(cattrs) / sizeof(cattrs[0]),
1007 		      NULL, cattrs);
1008 
1009 	if ((response2 = cupsDoRequest(http2, request, "/")) != NULL)
1010 	  members = ippFindAttribute(response2, "member-names", IPP_TAG_NAME);
1011 
1012 	if (http2)
1013 	  httpClose(http2);
1014       }
1015 
1016      /*
1017       * See if we have everything needed...
1018       */
1019 
1020       if (printer == NULL)
1021       {
1022         if (response2)
1023 	  ippDelete(response2);
1024 
1025         if (attr == NULL)
1026 	  break;
1027 	else
1028           continue;
1029       }
1030 
1031      /*
1032       * Display the printer entry if needed...
1033       */
1034 
1035       if (match_list(dests, printer))
1036       {
1037         _cupsLangPrintf(stdout, _("members of class %s:"), printer);
1038 
1039 	if (members)
1040 	{
1041 	  for (i = 0; i < members->num_values; i ++)
1042 	    _cupsLangPrintf(stdout, "\t%s", members->values[i].string.text);
1043         }
1044 	else
1045 	  _cupsLangPuts(stdout, "\tunknown");
1046       }
1047 
1048       if (response2)
1049 	ippDelete(response2);
1050 
1051       if (attr == NULL)
1052         break;
1053     }
1054 
1055     ippDelete(response);
1056   }
1057 
1058   return (0);
1059 }
1060 
1061 
1062 /*
1063  * 'show_default()' - Show default destination.
1064  */
1065 
1066 static void
show_default(cups_dest_t * dest)1067 show_default(cups_dest_t *dest)		/* I - Default destination */
1068 {
1069   const char	*printer,		/* Printer name */
1070 		*val;			/* Environment variable name */
1071 
1072 
1073   if (dest)
1074   {
1075     if (dest->instance)
1076       _cupsLangPrintf(stdout, _("system default destination: %s/%s"),
1077                       dest->name, dest->instance);
1078     else
1079       _cupsLangPrintf(stdout, _("system default destination: %s"),
1080                       dest->name);
1081   }
1082   else
1083   {
1084     val = NULL;
1085 
1086     if ((printer = getenv("LPDEST")) == NULL)
1087     {
1088       if ((printer = getenv("PRINTER")) != NULL)
1089       {
1090         if (!strcmp(printer, "lp"))
1091           printer = NULL;
1092 	else
1093 	  val = "PRINTER";
1094       }
1095     }
1096     else
1097       val = "LPDEST";
1098 
1099     if (printer)
1100       _cupsLangPrintf(stdout,
1101                       _("lpstat: error - %s environment variable names "
1102 		        "non-existent destination \"%s\"."),
1103         	      val, printer);
1104     else
1105       _cupsLangPuts(stdout, _("no system default destination"));
1106   }
1107 }
1108 
1109 
1110 /*
1111  * 'show_devices()' - Show printer devices.
1112  */
1113 
1114 static int				/* O - 0 on success, 1 on fail */
show_devices(const char * printers,int num_dests,cups_dest_t * dests)1115 show_devices(const char  *printers,	/* I - Destinations */
1116              int         num_dests,	/* I - Number of user-defined dests */
1117 	     cups_dest_t *dests)	/* I - User-defined destinations */
1118 {
1119   int		i;			/* Looping var */
1120   ipp_t		*request,		/* IPP Request */
1121 		*response;		/* IPP Response */
1122   ipp_attribute_t *attr;		/* Current attribute */
1123   const char	*printer,		/* Printer name */
1124 		*uri,			/* Printer URI */
1125 		*device;		/* Printer device URI */
1126   static const char *pattrs[] =		/* Attributes we need for printers... */
1127 		{
1128 		  "printer-name",
1129 		  "printer-uri-supported",
1130 		  "device-uri"
1131 		};
1132 
1133 
1134   if (printers != NULL && !strcmp(printers, "all"))
1135     printers = NULL;
1136 
1137  /*
1138   * Build a CUPS_GET_PRINTERS request, which requires the following
1139   * attributes:
1140   *
1141   *    attributes-charset
1142   *    attributes-natural-language
1143   *    requested-attributes
1144   *    requesting-user-name
1145   */
1146 
1147   request = ippNewRequest(CUPS_GET_PRINTERS);
1148 
1149   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1150                 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1151 		NULL, pattrs);
1152 
1153   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1154                NULL, cupsUser());
1155 
1156  /*
1157   * Do the request and get back a response...
1158   */
1159 
1160   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1161 
1162   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1163       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1164   {
1165     _cupsLangPrintf(stderr,
1166 		    _("%s: Error - add '/version=1.1' to server name."),
1167 		    "lpstat");
1168     ippDelete(response);
1169     return (1);
1170   }
1171   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1172   {
1173     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1174     ippDelete(response);
1175     return (1);
1176   }
1177 
1178   if (response)
1179   {
1180    /*
1181     * Loop through the printers returned in the list and display
1182     * their devices...
1183     */
1184 
1185     for (attr = response->attrs; attr != NULL; attr = attr->next)
1186     {
1187      /*
1188       * Skip leading attributes until we hit a job...
1189       */
1190 
1191       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1192         attr = attr->next;
1193 
1194       if (attr == NULL)
1195         break;
1196 
1197      /*
1198       * Pull the needed attributes from this job...
1199       */
1200 
1201       printer = NULL;
1202       device  = NULL;
1203       uri     = NULL;
1204 
1205       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1206       {
1207         if (!strcmp(attr->name, "printer-name") &&
1208 	    attr->value_tag == IPP_TAG_NAME)
1209 	  printer = attr->values[0].string.text;
1210 
1211         if (!strcmp(attr->name, "printer-uri-supported") &&
1212 	    attr->value_tag == IPP_TAG_URI)
1213 	  uri = attr->values[0].string.text;
1214 
1215         if (!strcmp(attr->name, "device-uri") &&
1216 	    attr->value_tag == IPP_TAG_URI)
1217 	  device = attr->values[0].string.text;
1218 
1219         attr = attr->next;
1220       }
1221 
1222      /*
1223       * See if we have everything needed...
1224       */
1225 
1226       if (printer == NULL)
1227       {
1228         if (attr == NULL)
1229 	  break;
1230 	else
1231           continue;
1232       }
1233 
1234      /*
1235       * Display the printer entry if needed...
1236       */
1237 
1238       if (match_list(printers, printer))
1239       {
1240         if (device == NULL)
1241           _cupsLangPrintf(stdout, _("device for %s: %s"),
1242 	                  printer, uri);
1243         else if (!strncmp(device, "file:", 5))
1244           _cupsLangPrintf(stdout, _("device for %s: %s"),
1245 	                  printer, device + 5);
1246         else
1247           _cupsLangPrintf(stdout, _("device for %s: %s"),
1248 	                  printer, device);
1249 
1250         for (i = 0; i < num_dests; i ++)
1251         {
1252 	  if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
1253 	  {
1254             if (device == NULL)
1255               _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1256 	                      printer, dests[i].instance, uri);
1257             else if (!strncmp(device, "file:", 5))
1258               _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1259 	                      printer, dests[i].instance, device + 5);
1260             else
1261               _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1262 	                      printer, dests[i].instance, device);
1263 	  }
1264 	}
1265       }
1266 
1267       if (attr == NULL)
1268         break;
1269     }
1270 
1271     ippDelete(response);
1272   }
1273 
1274   return (0);
1275 }
1276 
1277 
1278 /*
1279  * 'show_jobs()' - Show active print jobs.
1280  */
1281 
1282 static int				/* O - 0 on success, 1 on fail */
show_jobs(const char * dests,const char * users,int long_status,int ranking,const char * which)1283 show_jobs(const char *dests,		/* I - Destinations */
1284           const char *users,		/* I - Users */
1285           int        long_status,	/* I - Show long status? */
1286           int        ranking,		/* I - Show job ranking? */
1287 	  const char *which)		/* I - Show which jobs? */
1288 {
1289   int		i;			/* Looping var */
1290   ipp_t		*request,		/* IPP Request */
1291 		*response;		/* IPP Response */
1292   ipp_attribute_t *attr,		/* Current attribute */
1293 		*reasons;		/* Job state reasons attribute */
1294   const char	*dest,			/* Pointer into job-printer-uri */
1295 		*username,		/* Pointer to job-originating-user-name */
1296 		*message,		/* Pointer to job-printer-state-message */
1297 		*time_at;		/* time-at-xxx attribute name to use */
1298   int		rank,			/* Rank in queue */
1299 		jobid,			/* job-id */
1300 		size;			/* job-k-octets */
1301   time_t	jobtime;		/* time-at-creation */
1302   char		temp[255],		/* Temporary buffer */
1303 		date[255];		/* Date buffer */
1304   static const char *jattrs[] =		/* Attributes we need for jobs... */
1305 		{
1306 		  "job-id",
1307 		  "job-k-octets",
1308 		  "job-name",
1309 		  "job-originating-user-name",
1310 		  "job-printer-state-message",
1311 		  "job-printer-uri",
1312 		  "job-state-reasons",
1313 		  "time-at-creation",
1314 		  "time-at-completed"
1315 		};
1316 
1317 
1318   if (dests != NULL && !strcmp(dests, "all"))
1319     dests = NULL;
1320 
1321  /*
1322   * Build a IPP_GET_JOBS request, which requires the following
1323   * attributes:
1324   *
1325   *    attributes-charset
1326   *    attributes-natural-language
1327   *    printer-uri
1328   *    requested-attributes
1329   *    requesting-user-name
1330   *    which-jobs
1331   */
1332 
1333   request = ippNewRequest(IPP_GET_JOBS);
1334 
1335   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1336                NULL, "ipp://localhost/");
1337 
1338   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1339                 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1340 		NULL, jattrs);
1341 
1342   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1343                NULL, cupsUser());
1344 
1345   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
1346                NULL, which);
1347 
1348  /*
1349   * Do the request and get back a response...
1350   */
1351 
1352   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1353 
1354   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1355       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1356   {
1357     _cupsLangPrintf(stderr,
1358 		    _("%s: Error - add '/version=1.1' to server name."),
1359 		    "lpstat");
1360     ippDelete(response);
1361     return (1);
1362   }
1363   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1364   {
1365     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1366     ippDelete(response);
1367     return (1);
1368   }
1369 
1370   if (response)
1371   {
1372    /*
1373     * Loop through the job list and display them...
1374     */
1375 
1376     if (!strcmp(which, "aborted") ||
1377         !strcmp(which, "canceled") ||
1378         !strcmp(which, "completed"))
1379       time_at = "time-at-completed";
1380     else
1381       time_at = "time-at-creation";
1382 
1383     rank = -1;
1384 
1385     for (attr = response->attrs; attr != NULL; attr = attr->next)
1386     {
1387      /*
1388       * Skip leading attributes until we hit a job...
1389       */
1390 
1391       while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
1392         attr = attr->next;
1393 
1394       if (attr == NULL)
1395         break;
1396 
1397      /*
1398       * Pull the needed attributes from this job...
1399       */
1400 
1401       jobid    = 0;
1402       size     = 0;
1403       username = NULL;
1404       dest     = NULL;
1405       jobtime  = 0;
1406       message  = NULL;
1407       reasons  = NULL;
1408 
1409       while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
1410       {
1411         if (!strcmp(attr->name, "job-id") &&
1412 	    attr->value_tag == IPP_TAG_INTEGER)
1413 	  jobid = attr->values[0].integer;
1414         else if (!strcmp(attr->name, "job-k-octets") &&
1415 		 attr->value_tag == IPP_TAG_INTEGER)
1416 	  size = attr->values[0].integer;
1417         else if (!strcmp(attr->name, time_at) && attr->value_tag == IPP_TAG_INTEGER)
1418 	  jobtime = attr->values[0].integer;
1419         else if (!strcmp(attr->name, "job-printer-state-message") &&
1420 	         attr->value_tag == IPP_TAG_TEXT)
1421 	  message = attr->values[0].string.text;
1422         else if (!strcmp(attr->name, "job-printer-uri") &&
1423 	         attr->value_tag == IPP_TAG_URI)
1424 	{
1425 	  if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
1426 	    dest ++;
1427         }
1428         else if (!strcmp(attr->name, "job-originating-user-name") &&
1429 	         attr->value_tag == IPP_TAG_NAME)
1430 	  username = attr->values[0].string.text;
1431         else if (!strcmp(attr->name, "job-state-reasons") &&
1432 	         attr->value_tag == IPP_TAG_KEYWORD)
1433 	  reasons = attr;
1434 
1435         attr = attr->next;
1436       }
1437 
1438      /*
1439       * See if we have everything needed...
1440       */
1441 
1442       if (dest == NULL || jobid == 0)
1443       {
1444         if (attr == NULL)
1445 	  break;
1446 	else
1447           continue;
1448       }
1449 
1450      /*
1451       * Display the job...
1452       */
1453 
1454       rank ++;
1455 
1456       if (match_list(dests, dest) && match_list(users, username))
1457       {
1458         snprintf(temp, sizeof(temp), "%s-%d", dest, jobid);
1459 
1460 	_cupsStrDate(date, sizeof(date), jobtime);
1461 
1462 	if (ranking)
1463 	  _cupsLangPrintf(stdout, "%3d %-21s %-13s %8.0f %s",
1464 			  rank, temp, username ? username : "unknown",
1465 			  1024.0 * size, date);
1466 	else
1467 	  _cupsLangPrintf(stdout, "%-23s %-13s %8.0f   %s",
1468 			  temp, username ? username : "unknown",
1469 			  1024.0 * size, date);
1470 	if (long_status)
1471 	{
1472 	  if (message)
1473 	    _cupsLangPrintf(stdout, _("\tStatus: %s"), message);
1474 
1475 	  if (reasons)
1476 	  {
1477 	    char	alerts[1024],	/* Alerts string */
1478 		      *aptr;		/* Pointer into alerts string */
1479 
1480 	    for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1481 	    {
1482 	      if (i)
1483 		snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1484 	      else
1485 		strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1486 
1487 	      aptr += strlen(aptr);
1488 	    }
1489 
1490 	    _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1491 	  }
1492 
1493 	  _cupsLangPrintf(stdout, _("\tqueued for %s"), dest);
1494 	}
1495       }
1496 
1497       if (attr == NULL)
1498         break;
1499     }
1500 
1501     ippDelete(response);
1502   }
1503 
1504   return (0);
1505 }
1506 
1507 
1508 /*
1509  * 'show_printers()' - Show printers.
1510  */
1511 
1512 static int				/* O - 0 on success, 1 on fail */
show_printers(const char * printers,int num_dests,cups_dest_t * dests,int long_status)1513 show_printers(const char  *printers,	/* I - Destinations */
1514               int         num_dests,	/* I - Number of user-defined dests */
1515 	      cups_dest_t *dests,	/* I - User-defined destinations */
1516               int         long_status)	/* I - Show long status? */
1517 {
1518   int		i, j;			/* Looping vars */
1519   ipp_t		*request,		/* IPP Request */
1520 		*response,		/* IPP Response */
1521 		*jobs;			/* IPP Get Jobs response */
1522   ipp_attribute_t *attr,		/* Current attribute */
1523 		*jobattr,		/* Job ID attribute */
1524 		*reasons;		/* Job state reasons attribute */
1525   const char	*printer,		/* Printer name */
1526 		*message,		/* Printer state message */
1527 		*description,		/* Description of printer */
1528 		*location,		/* Location of printer */
1529 		*make_model,		/* Make and model of printer */
1530 		*uri;			/* URI of printer */
1531   ipp_attribute_t *allowed,		/* requesting-user-name-allowed */
1532 		*denied;		/* requestint-user-name-denied */
1533   ipp_pstate_t	pstate;			/* Printer state */
1534   cups_ptype_t	ptype;			/* Printer type */
1535   time_t	ptime;			/* Printer state time */
1536   int		jobid;			/* Job ID of current job */
1537   char		printer_uri[HTTP_MAX_URI],
1538 					/* Printer URI */
1539 		printer_state_time[255];/* Printer state time */
1540   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
1541   static const char *pattrs[] =		/* Attributes we need for printers... */
1542 		{
1543 		  "printer-name",
1544 		  "printer-state",
1545 		  "printer-state-message",
1546 		  "printer-state-reasons",
1547 		  "printer-state-change-time",
1548 		  "printer-type",
1549 		  "printer-info",
1550                   "printer-location",
1551 		  "printer-make-and-model",
1552 		  "printer-uri-supported",
1553 		  "requesting-user-name-allowed",
1554 		  "requesting-user-name-denied"
1555 		};
1556   static const char *jattrs[] =		/* Attributes we need for jobs... */
1557 		{
1558 		  "job-id",
1559 		  "job-state"
1560 		};
1561 
1562 
1563   if (printers != NULL && !strcmp(printers, "all"))
1564     printers = NULL;
1565 
1566  /*
1567   * Build a CUPS_GET_PRINTERS request, which requires the following
1568   * attributes:
1569   *
1570   *    attributes-charset
1571   *    attributes-natural-language
1572   *    requested-attributes
1573   *    requesting-user-name
1574   */
1575 
1576   request = ippNewRequest(CUPS_GET_PRINTERS);
1577 
1578   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1579                 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1580 		NULL, pattrs);
1581 
1582   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1583                NULL, cupsUser());
1584 
1585  /*
1586   * Do the request and get back a response...
1587   */
1588 
1589   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1590 
1591   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1592       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1593   {
1594     _cupsLangPrintf(stderr,
1595 		    _("%s: Error - add '/version=1.1' to server name."),
1596 		    "lpstat");
1597     ippDelete(response);
1598     return (1);
1599   }
1600   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1601   {
1602     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1603     ippDelete(response);
1604     return (1);
1605   }
1606 
1607   if (response)
1608   {
1609    /*
1610     * Loop through the printers returned in the list and display
1611     * their status...
1612     */
1613 
1614     for (attr = response->attrs; attr != NULL; attr = attr->next)
1615     {
1616      /*
1617       * Skip leading attributes until we hit a job...
1618       */
1619 
1620       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1621         attr = attr->next;
1622 
1623       if (attr == NULL)
1624         break;
1625 
1626      /*
1627       * Pull the needed attributes from this job...
1628       */
1629 
1630       printer     = NULL;
1631       ptime       = 0;
1632       ptype       = CUPS_PRINTER_LOCAL;
1633       pstate      = IPP_PRINTER_IDLE;
1634       message     = NULL;
1635       description = NULL;
1636       location    = NULL;
1637       make_model  = NULL;
1638       reasons     = NULL;
1639       uri         = NULL;
1640       jobid       = 0;
1641       allowed     = NULL;
1642       denied      = NULL;
1643 
1644       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1645       {
1646         if (!strcmp(attr->name, "printer-name") &&
1647 	    attr->value_tag == IPP_TAG_NAME)
1648 	  printer = attr->values[0].string.text;
1649         else if (!strcmp(attr->name, "printer-state") &&
1650 	         attr->value_tag == IPP_TAG_ENUM)
1651 	  pstate = (ipp_pstate_t)attr->values[0].integer;
1652         else if (!strcmp(attr->name, "printer-type") &&
1653 	         attr->value_tag == IPP_TAG_ENUM)
1654 	  ptype = (cups_ptype_t)attr->values[0].integer;
1655         else if (!strcmp(attr->name, "printer-state-message") &&
1656 	         attr->value_tag == IPP_TAG_TEXT)
1657 	  message = attr->values[0].string.text;
1658         else if (!strcmp(attr->name, "printer-state-change-time") &&
1659 	         attr->value_tag == IPP_TAG_INTEGER)
1660 	  ptime = (time_t)attr->values[0].integer;
1661 	else if (!strcmp(attr->name, "printer-info") &&
1662 	         attr->value_tag == IPP_TAG_TEXT)
1663 	  description = attr->values[0].string.text;
1664         else if (!strcmp(attr->name, "printer-location") &&
1665 	         attr->value_tag == IPP_TAG_TEXT)
1666 	  location = attr->values[0].string.text;
1667         else if (!strcmp(attr->name, "printer-make-and-model") &&
1668 	         attr->value_tag == IPP_TAG_TEXT)
1669 	  make_model = attr->values[0].string.text;
1670         else if (!strcmp(attr->name, "printer-uri-supported") &&
1671 	         attr->value_tag == IPP_TAG_URI)
1672 	  uri = attr->values[0].string.text;
1673         else if (!strcmp(attr->name, "printer-state-reasons") &&
1674 	         attr->value_tag == IPP_TAG_KEYWORD)
1675 	  reasons = attr;
1676         else if (!strcmp(attr->name, "requesting-user-name-allowed") &&
1677 	         attr->value_tag == IPP_TAG_NAME)
1678 	  allowed = attr;
1679         else if (!strcmp(attr->name, "requesting-user-name-denied") &&
1680 	         attr->value_tag == IPP_TAG_NAME)
1681 	  denied = attr;
1682 
1683         attr = attr->next;
1684       }
1685 
1686      /*
1687       * See if we have everything needed...
1688       */
1689 
1690       if (printer == NULL)
1691       {
1692         if (attr == NULL)
1693 	  break;
1694 	else
1695           continue;
1696       }
1697 
1698      /*
1699       * Display the printer entry if needed...
1700       */
1701 
1702       if (match_list(printers, printer))
1703       {
1704        /*
1705         * If the printer state is "IPP_PRINTER_PROCESSING", then grab the
1706 	* current job for the printer.
1707 	*/
1708 
1709         if (pstate == IPP_PRINTER_PROCESSING)
1710 	{
1711 	 /*
1712 	  * Build an IPP_GET_JOBS request, which requires the following
1713 	  * attributes:
1714 	  *
1715 	  *    attributes-charset
1716 	  *    attributes-natural-language
1717 	  *    printer-uri
1718 	  *    limit
1719           *    requested-attributes
1720 	  */
1721 
1722 	  request = ippNewRequest(IPP_GET_JOBS);
1723 
1724 	  request->request.op.operation_id = IPP_GET_JOBS;
1725 	  request->request.op.request_id   = 1;
1726 
1727 	  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1728                 	"requested-attributes",
1729 		        sizeof(jattrs) / sizeof(jattrs[0]), NULL, jattrs);
1730 
1731 	  httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
1732 	                   "ipp", NULL, "localhost", 0, "/printers/%s", printer);
1733 	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1734 	               "printer-uri", NULL, printer_uri);
1735 
1736           if ((jobs = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) != NULL)
1737 	  {
1738 	   /*
1739 	    * Get the current active job on this queue...
1740 	    */
1741 
1742             ipp_jstate_t jobstate = IPP_JOB_PENDING;
1743 	    jobid = 0;
1744 
1745 	    for (jobattr = jobs->attrs; jobattr; jobattr = jobattr->next)
1746 	    {
1747 	      if (!jobattr->name)
1748 	      {
1749 	        if (jobstate == IPP_JOB_PROCESSING)
1750 		  break;
1751 	        else
1752 		  continue;
1753               }
1754 
1755 	      if (!strcmp(jobattr->name, "job-id") &&
1756 	          jobattr->value_tag == IPP_TAG_INTEGER)
1757 		jobid = jobattr->values[0].integer;
1758               else if (!strcmp(jobattr->name, "job-state") &&
1759 	               jobattr->value_tag == IPP_TAG_ENUM)
1760 		jobstate = (ipp_jstate_t)jobattr->values[0].integer;
1761 	    }
1762 
1763             if (jobstate != IPP_JOB_PROCESSING)
1764 	      jobid = 0;
1765 
1766             ippDelete(jobs);
1767 	  }
1768         }
1769 
1770        /*
1771         * Display it...
1772 	*/
1773 
1774         _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
1775 
1776         switch (pstate)
1777 	{
1778 	  case IPP_PRINTER_IDLE :
1779 	      if (ippContainsString(reasons, "hold-new-jobs"))
1780 		_cupsLangPrintf(stdout, _("printer %s is holding new jobs.  enabled since %s"), printer, printer_state_time);
1781 	      else
1782 		_cupsLangPrintf(stdout, _("printer %s is idle.  enabled since %s"), printer, printer_state_time);
1783 	      break;
1784 	  case IPP_PRINTER_PROCESSING :
1785 	      _cupsLangPrintf(stdout, _("printer %s now printing %s-%d.  enabled since %s"), printer, printer, jobid, printer_state_time);
1786 	      break;
1787 	  case IPP_PRINTER_STOPPED :
1788 	      _cupsLangPrintf(stdout, _("printer %s disabled since %s -"), printer, printer_state_time);
1789 	      break;
1790 	}
1791 
1792         if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
1793 	{
1794 	  if (message && *message)
1795 	  	_cupsLangPrintf(stdout, "\t%s", message);
1796 	  else
1797 	    _cupsLangPuts(stdout, _("\treason unknown"));
1798 	}
1799 
1800         if (long_status > 1)
1801 	{
1802 	  _cupsLangPuts(stdout, _("\tForm mounted:"));
1803 	  _cupsLangPuts(stdout, _("\tContent types: any"));
1804 	  _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
1805 	}
1806 
1807         if (long_status)
1808 	{
1809 	  _cupsLangPrintf(stdout, _("\tDescription: %s"),
1810 	                  description ? description : "");
1811 
1812 	  if (reasons)
1813 	  {
1814 	    char	alerts[1024],	/* Alerts string */
1815 			*aptr;		/* Pointer into alerts string */
1816 
1817 	    for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1818 	    {
1819 	      if (i)
1820 		snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1821 	      else
1822 		strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1823 
1824 	      aptr += strlen(aptr);
1825 	    }
1826 
1827 	    _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1828 	  }
1829 	}
1830         if (long_status > 1)
1831 	{
1832 	  _cupsLangPrintf(stdout, _("\tLocation: %s"),
1833 	                  location ? location : "");
1834 
1835 	  if (ptype & CUPS_PRINTER_REMOTE)
1836 	  {
1837 	    _cupsLangPuts(stdout, _("\tConnection: remote"));
1838 
1839 	    if (make_model && !strstr(make_model, "System V Printer") &&
1840 	             !strstr(make_model, "Raw Printer") && uri)
1841 	      _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"),
1842 	                      uri);
1843 	  }
1844 	  else
1845 	  {
1846 	    _cupsLangPuts(stdout, _("\tConnection: direct"));
1847 
1848 	    if (make_model && !strstr(make_model, "Raw Printer"))
1849 	      _cupsLangPrintf(stdout,
1850 	                      _("\tInterface: %s/ppd/%s.ppd"),
1851 			      cg->cups_serverroot, printer);
1852           }
1853 	  _cupsLangPuts(stdout, _("\tOn fault: no alert"));
1854 	  _cupsLangPuts(stdout, _("\tAfter fault: continue"));
1855 	      /* TODO update to use printer-error-policy */
1856           if (allowed)
1857 	  {
1858 	    _cupsLangPuts(stdout, _("\tUsers allowed:"));
1859 	    for (j = 0; j < allowed->num_values; j ++)
1860 	      _cupsLangPrintf(stdout, "\t\t%s",
1861 	                      allowed->values[j].string.text);
1862 	  }
1863 	  else if (denied)
1864 	  {
1865 	    _cupsLangPuts(stdout, _("\tUsers denied:"));
1866 	    for (j = 0; j < denied->num_values; j ++)
1867 	      _cupsLangPrintf(stdout, "\t\t%s",
1868 	                      denied->values[j].string.text);
1869 	  }
1870 	  else
1871 	  {
1872 	    _cupsLangPuts(stdout, _("\tUsers allowed:"));
1873 	    _cupsLangPuts(stdout, _("\t\t(all)"));
1874 	  }
1875 	  _cupsLangPuts(stdout, _("\tForms allowed:"));
1876 	  _cupsLangPuts(stdout, _("\t\t(none)"));
1877 	  _cupsLangPuts(stdout, _("\tBanner required"));
1878 	  _cupsLangPuts(stdout, _("\tCharset sets:"));
1879 	  _cupsLangPuts(stdout, _("\t\t(none)"));
1880 	  _cupsLangPuts(stdout, _("\tDefault pitch:"));
1881 	  _cupsLangPuts(stdout, _("\tDefault page size:"));
1882 	  _cupsLangPuts(stdout, _("\tDefault port settings:"));
1883 	}
1884 
1885         for (i = 0; i < num_dests; i ++)
1886 	  if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
1887 	  {
1888             switch (pstate)
1889 	    {
1890 	      case IPP_PRINTER_IDLE :
1891 		  _cupsLangPrintf(stdout,
1892 		                  _("printer %s/%s is idle.  "
1893 				    "enabled since %s"),
1894 				  printer, dests[i].instance,
1895 				  printer_state_time);
1896 		  break;
1897 	      case IPP_PRINTER_PROCESSING :
1898 		  _cupsLangPrintf(stdout,
1899 		                  _("printer %s/%s now printing %s-%d.  "
1900 				    "enabled since %s"),
1901 				  printer, dests[i].instance, printer, jobid,
1902 				  printer_state_time);
1903 		  break;
1904 	      case IPP_PRINTER_STOPPED :
1905 		  _cupsLangPrintf(stdout,
1906 		                  _("printer %s/%s disabled since %s -"),
1907 				  printer, dests[i].instance,
1908 				  printer_state_time);
1909 		  break;
1910 	    }
1911 
1912             if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
1913 	    {
1914 	      if (message && *message)
1915 		_cupsLangPrintf(stdout, "\t%s", message);
1916 	      else
1917 		_cupsLangPuts(stdout, _("\treason unknown"));
1918             }
1919 
1920             if (long_status > 1)
1921 	    {
1922 	      _cupsLangPuts(stdout, _("\tForm mounted:"));
1923 	      _cupsLangPuts(stdout, _("\tContent types: any"));
1924 	      _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
1925 	    }
1926 
1927             if (long_status)
1928 	    {
1929 	      _cupsLangPrintf(stdout, _("\tDescription: %s"),
1930 	                      description ? description : "");
1931 
1932 	      if (reasons)
1933 	      {
1934 		char	alerts[1024],	/* Alerts string */
1935 			*aptr;		/* Pointer into alerts string */
1936 
1937 		for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1938 		{
1939 		  if (i)
1940 		    snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1941 		  else
1942 		    strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1943 
1944 		  aptr += strlen(aptr);
1945 		}
1946 
1947 		_cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1948 	      }
1949 	    }
1950             if (long_status > 1)
1951 	    {
1952 	      _cupsLangPrintf(stdout, _("\tLocation: %s"),
1953 	                      location ? location : "");
1954 
1955 	      if (ptype & CUPS_PRINTER_REMOTE)
1956 	      {
1957 		_cupsLangPuts(stdout, _("\tConnection: remote"));
1958 
1959 		if (make_model && !strstr(make_model, "System V Printer") &&
1960 	        	 !strstr(make_model, "Raw Printer") && uri)
1961 		  _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"), uri);
1962 	      }
1963 	      else
1964 	      {
1965 		_cupsLangPuts(stdout, _("\tConnection: direct"));
1966 
1967 		if (make_model && !strstr(make_model, "Raw Printer"))
1968 		  _cupsLangPrintf(stdout,
1969 	                	  _("\tInterface: %s/ppd/%s.ppd"),
1970 				  cg->cups_serverroot, printer);
1971               }
1972 	      _cupsLangPuts(stdout, _("\tOn fault: no alert"));
1973 	      _cupsLangPuts(stdout, _("\tAfter fault: continue"));
1974 		  /* TODO update to use printer-error-policy */
1975               if (allowed)
1976 	      {
1977 		_cupsLangPuts(stdout, _("\tUsers allowed:"));
1978 		for (j = 0; j < allowed->num_values; j ++)
1979 		  _cupsLangPrintf(stdout, "\t\t%s",
1980 	                	  allowed->values[j].string.text);
1981 	      }
1982 	      else if (denied)
1983 	      {
1984 		_cupsLangPuts(stdout, _("\tUsers denied:"));
1985 		for (j = 0; j < denied->num_values; j ++)
1986 		  _cupsLangPrintf(stdout, "\t\t%s",
1987 	                	  denied->values[j].string.text);
1988 	      }
1989 	      else
1990 	      {
1991 		_cupsLangPuts(stdout, _("\tUsers allowed:"));
1992 		_cupsLangPuts(stdout, _("\t\t(all)"));
1993 	      }
1994 	      _cupsLangPuts(stdout, _("\tForms allowed:"));
1995 	      _cupsLangPuts(stdout, _("\t\t(none)"));
1996 	      _cupsLangPuts(stdout, _("\tBanner required"));
1997 	      _cupsLangPuts(stdout, _("\tCharset sets:"));
1998 	      _cupsLangPuts(stdout, _("\t\t(none)"));
1999 	      _cupsLangPuts(stdout, _("\tDefault pitch:"));
2000 	      _cupsLangPuts(stdout, _("\tDefault page size:"));
2001 	      _cupsLangPuts(stdout, _("\tDefault port settings:"));
2002 	    }
2003 	  }
2004       }
2005 
2006       if (attr == NULL)
2007         break;
2008     }
2009 
2010     ippDelete(response);
2011   }
2012 
2013   return (0);
2014 }
2015 
2016 
2017 /*
2018  * 'show_scheduler()' - Show scheduler status.
2019  */
2020 
2021 static void
show_scheduler(void)2022 show_scheduler(void)
2023 {
2024   http_t	*http;			/* Connection to server */
2025 
2026 
2027   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
2028                                  cupsEncryption())) != NULL)
2029   {
2030     _cupsLangPuts(stdout, _("scheduler is running"));
2031     httpClose(http);
2032   }
2033   else
2034     _cupsLangPuts(stdout, _("scheduler is not running"));
2035 }
2036 
2037 
2038 /*
2039  * 'usage()' - Show program usage and exit.
2040  */
2041 
2042 static void
usage(void)2043 usage(void)
2044 {
2045   _cupsLangPuts(stdout, _("Usage: lpstat [options]"));
2046   _cupsLangPuts(stdout, _("Options:"));
2047   _cupsLangPuts(stdout, _("-E                      Encrypt the connection to the server"));
2048   _cupsLangPuts(stdout, _("-h server[:port]        Connect to the named server and port"));
2049   _cupsLangPuts(stdout, _("-l                      Show verbose (long) output"));
2050   _cupsLangPuts(stdout, _("-U username             Specify the username to use for authentication"));
2051 
2052   _cupsLangPuts(stdout, _("-H                      Show the default server and port"));
2053   _cupsLangPuts(stdout, _("-W completed            Show completed jobs"));
2054   _cupsLangPuts(stdout, _("-W not-completed        Show pending jobs"));
2055   _cupsLangPuts(stdout, _("-a [destination(s)]     Show the accepting state of destinations"));
2056   _cupsLangPuts(stdout, _("-c [class(es)]          Show classes and their member printers"));
2057   _cupsLangPuts(stdout, _("-d                      Show the default destination"));
2058   _cupsLangPuts(stdout, _("-e                      Show available destinations on the network"));
2059   _cupsLangPuts(stdout, _("-o [destination(s)]     Show jobs"));
2060   _cupsLangPuts(stdout, _("-p [printer(s)]         Show the processing state of destinations"));
2061   _cupsLangPuts(stdout, _("-r                      Show whether the CUPS server is running"));
2062   _cupsLangPuts(stdout, _("-R                      Show the ranking of jobs"));
2063   _cupsLangPuts(stdout, _("-s                      Show a status summary"));
2064   _cupsLangPuts(stdout, _("-t                      Show all status information"));
2065   _cupsLangPuts(stdout, _("-u [user(s)]            Show jobs queued by the current or specified users"));
2066   _cupsLangPuts(stdout, _("-v [printer(s)]         Show the devices for each destination"));
2067 
2068   exit(1);
2069 }
2070