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   while (*list)
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 
664   return (0);
665 }
666 
667 
668 /*
669  * 'show_accepting()' - Show acceptance status.
670  */
671 
672 static int				/* O - 0 on success, 1 on fail */
show_accepting(const char * printers,int num_dests,cups_dest_t * dests)673 show_accepting(const char  *printers,	/* I - Destinations */
674                int         num_dests,	/* I - Number of user-defined dests */
675 	       cups_dest_t *dests)	/* I - User-defined destinations */
676 {
677   int		i;			/* Looping var */
678   ipp_t		*request,		/* IPP Request */
679 		*response;		/* IPP Response */
680   ipp_attribute_t *attr;		/* Current attribute */
681   const char	*printer,		/* Printer name */
682 		*message;		/* Printer device URI */
683   int		accepting;		/* Accepting requests? */
684   time_t	ptime;			/* Printer state time */
685   char		printer_state_time[255];/* Printer state time */
686   static const char *pattrs[] =		/* Attributes we need for printers... */
687 		{
688 		  "printer-name",
689 		  "printer-state-change-time",
690 		  "printer-state-message",
691 		  "printer-is-accepting-jobs"
692 		};
693 
694 
695   if (printers != NULL && !strcmp(printers, "all"))
696     printers = NULL;
697 
698  /*
699   * Build a CUPS_GET_PRINTERS request, which requires the following
700   * attributes:
701   *
702   *    attributes-charset
703   *    attributes-natural-language
704   *    requested-attributes
705   *    requesting-user-name
706   */
707 
708   request = ippNewRequest(CUPS_GET_PRINTERS);
709 
710   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
711                 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
712 		NULL, pattrs);
713 
714   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
715                NULL, cupsUser());
716 
717  /*
718   * Do the request and get back a response...
719   */
720 
721   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
722 
723   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
724       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
725   {
726     _cupsLangPrintf(stderr,
727 		    _("%s: Error - add '/version=1.1' to server name."),
728 		    "lpstat");
729     ippDelete(response);
730     return (1);
731   }
732   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
733   {
734     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
735     ippDelete(response);
736     return (1);
737   }
738 
739   if (response)
740   {
741    /*
742     * Loop through the printers returned in the list and display
743     * their devices...
744     */
745 
746     for (attr = response->attrs; attr != NULL; attr = attr->next)
747     {
748      /*
749       * Skip leading attributes until we hit a printer...
750       */
751 
752       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
753         attr = attr->next;
754 
755       if (attr == NULL)
756         break;
757 
758      /*
759       * Pull the needed attributes from this printer...
760       */
761 
762       printer   = NULL;
763       message   = NULL;
764       accepting = 1;
765       ptime     = 0;
766 
767       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
768       {
769         if (!strcmp(attr->name, "printer-name") &&
770 	    attr->value_tag == IPP_TAG_NAME)
771 	  printer = attr->values[0].string.text;
772         else if (!strcmp(attr->name, "printer-state-change-time") &&
773 	         attr->value_tag == IPP_TAG_INTEGER)
774 	  ptime = (time_t)attr->values[0].integer;
775         else if (!strcmp(attr->name, "printer-state-message") &&
776 	         attr->value_tag == IPP_TAG_TEXT)
777 	  message = attr->values[0].string.text;
778         else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
779 	         attr->value_tag == IPP_TAG_BOOLEAN)
780 	  accepting = attr->values[0].boolean;
781 
782         attr = attr->next;
783       }
784 
785      /*
786       * See if we have everything needed...
787       */
788 
789       if (printer == NULL)
790       {
791         if (attr == NULL)
792 	  break;
793 	else
794           continue;
795       }
796 
797      /*
798       * Display the printer entry if needed...
799       */
800 
801       if (match_list(printers, printer))
802       {
803         _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
804 
805         if (accepting)
806 	  _cupsLangPrintf(stdout, _("%s accepting requests since %s"),
807 			  printer, printer_state_time);
808 	else
809 	{
810 	  _cupsLangPrintf(stdout, _("%s not accepting requests since %s -"),
811 			  printer, printer_state_time);
812 	  _cupsLangPrintf(stdout, _("\t%s"),
813 			  (message == NULL || !*message) ?
814 			      "reason unknown" : message);
815         }
816 
817         for (i = 0; i < num_dests; i ++)
818 	  if (!_cups_strcasecmp(dests[i].name, printer) && dests[i].instance)
819 	  {
820             if (accepting)
821 	      _cupsLangPrintf(stdout, _("%s/%s accepting requests since %s"),
822 			      printer, dests[i].instance, printer_state_time);
823 	    else
824 	    {
825 	      _cupsLangPrintf(stdout,
826 	                      _("%s/%s not accepting requests since %s -"),
827 			      printer, dests[i].instance, printer_state_time);
828 	      _cupsLangPrintf(stdout, _("\t%s"),
829 	        	      (message == NULL || !*message) ?
830 			          "reason unknown" : message);
831             }
832 	  }
833       }
834 
835       if (attr == NULL)
836         break;
837     }
838 
839     ippDelete(response);
840   }
841 
842   return (0);
843 }
844 
845 
846 /*
847  * 'show_classes()' - Show printer classes.
848  */
849 
850 static int				/* O - 0 on success, 1 on fail */
show_classes(const char * dests)851 show_classes(const char *dests)		/* I - Destinations */
852 {
853   int		i;			/* Looping var */
854   ipp_t		*request,		/* IPP Request */
855 		*response,		/* IPP Response */
856 		*response2;		/* IPP response from remote server */
857   http_t	*http2;			/* Remote server */
858   ipp_attribute_t *attr;		/* Current attribute */
859   const char	*printer,		/* Printer class name */
860 		*printer_uri;		/* Printer class URI */
861   ipp_attribute_t *members;		/* Printer members */
862   char		method[HTTP_MAX_URI],	/* Request method */
863 		username[HTTP_MAX_URI],	/* Username:password */
864 		server[HTTP_MAX_URI],	/* Server name */
865 		resource[HTTP_MAX_URI];	/* Resource name */
866   int		port;			/* Port number */
867   static const char *cattrs[] =		/* Attributes we need for classes... */
868 		{
869 		  "printer-name",
870 		  "printer-uri-supported",
871 		  "member-names"
872 		};
873 
874 
875   if (dests != NULL && !strcmp(dests, "all"))
876     dests = NULL;
877 
878  /*
879   * Build a CUPS_GET_CLASSES request, which requires the following
880   * attributes:
881   *
882   *    attributes-charset
883   *    attributes-natural-language
884   *    requested-attributes
885   *    requesting-user-name
886   */
887 
888   request = ippNewRequest(CUPS_GET_CLASSES);
889 
890   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
891                 "requested-attributes", sizeof(cattrs) / sizeof(cattrs[0]),
892 		NULL, cattrs);
893 
894   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
895                NULL, cupsUser());
896 
897  /*
898   * Do the request and get back a response...
899   */
900 
901   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
902 
903   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
904       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
905   {
906     _cupsLangPrintf(stderr,
907 		    _("%s: Error - add '/version=1.1' to server name."),
908 		    "lpstat");
909     ippDelete(response);
910     return (1);
911   }
912   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
913   {
914     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
915     ippDelete(response);
916     return (1);
917   }
918 
919   if (response)
920   {
921     if (response->request.status.status_code > IPP_OK_CONFLICT)
922     {
923       _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
924       ippDelete(response);
925       return (1);
926     }
927 
928    /*
929     * Loop through the printers returned in the list and display
930     * their devices...
931     */
932 
933     for (attr = response->attrs; attr != NULL; attr = attr->next)
934     {
935      /*
936       * Skip leading attributes until we hit a job...
937       */
938 
939       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
940         attr = attr->next;
941 
942       if (attr == NULL)
943         break;
944 
945      /*
946       * Pull the needed attributes from this job...
947       */
948 
949       printer     = NULL;
950       printer_uri = NULL;
951       members     = NULL;
952 
953       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
954       {
955         if (!strcmp(attr->name, "printer-name") &&
956 	    attr->value_tag == IPP_TAG_NAME)
957 	  printer = attr->values[0].string.text;
958 
959         if (!strcmp(attr->name, "printer-uri-supported") &&
960 	    attr->value_tag == IPP_TAG_URI)
961 	  printer_uri = attr->values[0].string.text;
962 
963         if (!strcmp(attr->name, "member-names") &&
964 	    attr->value_tag == IPP_TAG_NAME)
965 	  members = attr;
966 
967         attr = attr->next;
968       }
969 
970      /*
971       * If this is a remote class, grab the class info from the
972       * remote server...
973       */
974 
975       response2 = NULL;
976       if (members == NULL && printer_uri != NULL)
977       {
978         httpSeparateURI(HTTP_URI_CODING_ALL, printer_uri, method, sizeof(method),
979 	                username, sizeof(username), server, sizeof(server),
980 			&port, resource, sizeof(resource));
981 
982         if (!_cups_strcasecmp(server, cupsServer()))
983 	  http2 = CUPS_HTTP_DEFAULT;
984 	else
985 	  http2 = httpConnectEncrypt(server, port, cupsEncryption());
986 
987        /*
988 	* Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
989 	* following attributes:
990 	*
991 	*    attributes-charset
992 	*    attributes-natural-language
993 	*    printer-uri
994 	*    requested-attributes
995 	*/
996 
997 	request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
998 
999 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1000 		     "printer-uri", NULL, printer_uri);
1001 
1002 	ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1003 		      "requested-attributes",
1004 		      sizeof(cattrs) / sizeof(cattrs[0]),
1005 		      NULL, cattrs);
1006 
1007 	if ((response2 = cupsDoRequest(http2, request, "/")) != NULL)
1008 	  members = ippFindAttribute(response2, "member-names", IPP_TAG_NAME);
1009 
1010 	if (http2)
1011 	  httpClose(http2);
1012       }
1013 
1014      /*
1015       * See if we have everything needed...
1016       */
1017 
1018       if (printer == NULL)
1019       {
1020         if (response2)
1021 	  ippDelete(response2);
1022 
1023         if (attr == NULL)
1024 	  break;
1025 	else
1026           continue;
1027       }
1028 
1029      /*
1030       * Display the printer entry if needed...
1031       */
1032 
1033       if (match_list(dests, printer))
1034       {
1035         _cupsLangPrintf(stdout, _("members of class %s:"), printer);
1036 
1037 	if (members)
1038 	{
1039 	  for (i = 0; i < members->num_values; i ++)
1040 	    _cupsLangPrintf(stdout, "\t%s", members->values[i].string.text);
1041         }
1042 	else
1043 	  _cupsLangPuts(stdout, "\tunknown");
1044       }
1045 
1046       if (response2)
1047 	ippDelete(response2);
1048 
1049       if (attr == NULL)
1050         break;
1051     }
1052 
1053     ippDelete(response);
1054   }
1055 
1056   return (0);
1057 }
1058 
1059 
1060 /*
1061  * 'show_default()' - Show default destination.
1062  */
1063 
1064 static void
show_default(cups_dest_t * dest)1065 show_default(cups_dest_t *dest)		/* I - Default destination */
1066 {
1067   const char	*printer,		/* Printer name */
1068 		*val;			/* Environment variable name */
1069 
1070 
1071   if (dest)
1072   {
1073     if (dest->instance)
1074       _cupsLangPrintf(stdout, _("system default destination: %s/%s"),
1075                       dest->name, dest->instance);
1076     else
1077       _cupsLangPrintf(stdout, _("system default destination: %s"),
1078                       dest->name);
1079   }
1080   else
1081   {
1082     val = NULL;
1083 
1084     if ((printer = getenv("LPDEST")) == NULL)
1085     {
1086       if ((printer = getenv("PRINTER")) != NULL)
1087       {
1088         if (!strcmp(printer, "lp"))
1089           printer = NULL;
1090 	else
1091 	  val = "PRINTER";
1092       }
1093     }
1094     else
1095       val = "LPDEST";
1096 
1097     if (printer)
1098       _cupsLangPrintf(stdout,
1099                       _("lpstat: error - %s environment variable names "
1100 		        "non-existent destination \"%s\"."),
1101         	      val, printer);
1102     else
1103       _cupsLangPuts(stdout, _("no system default destination"));
1104   }
1105 }
1106 
1107 
1108 /*
1109  * 'show_devices()' - Show printer devices.
1110  */
1111 
1112 static int				/* O - 0 on success, 1 on fail */
show_devices(const char * printers,int num_dests,cups_dest_t * dests)1113 show_devices(const char  *printers,	/* I - Destinations */
1114              int         num_dests,	/* I - Number of user-defined dests */
1115 	     cups_dest_t *dests)	/* I - User-defined destinations */
1116 {
1117   int		i;			/* Looping var */
1118   ipp_t		*request,		/* IPP Request */
1119 		*response;		/* IPP Response */
1120   ipp_attribute_t *attr;		/* Current attribute */
1121   const char	*printer,		/* Printer name */
1122 		*uri,			/* Printer URI */
1123 		*device;		/* Printer device URI */
1124   static const char *pattrs[] =		/* Attributes we need for printers... */
1125 		{
1126 		  "printer-name",
1127 		  "printer-uri-supported",
1128 		  "device-uri"
1129 		};
1130 
1131 
1132   if (printers != NULL && !strcmp(printers, "all"))
1133     printers = NULL;
1134 
1135  /*
1136   * Build a CUPS_GET_PRINTERS request, which requires the following
1137   * attributes:
1138   *
1139   *    attributes-charset
1140   *    attributes-natural-language
1141   *    requested-attributes
1142   *    requesting-user-name
1143   */
1144 
1145   request = ippNewRequest(CUPS_GET_PRINTERS);
1146 
1147   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1148                 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1149 		NULL, pattrs);
1150 
1151   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1152                NULL, cupsUser());
1153 
1154  /*
1155   * Do the request and get back a response...
1156   */
1157 
1158   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1159 
1160   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1161       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1162   {
1163     _cupsLangPrintf(stderr,
1164 		    _("%s: Error - add '/version=1.1' to server name."),
1165 		    "lpstat");
1166     ippDelete(response);
1167     return (1);
1168   }
1169   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1170   {
1171     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1172     ippDelete(response);
1173     return (1);
1174   }
1175 
1176   if (response)
1177   {
1178    /*
1179     * Loop through the printers returned in the list and display
1180     * their devices...
1181     */
1182 
1183     for (attr = response->attrs; attr != NULL; attr = attr->next)
1184     {
1185      /*
1186       * Skip leading attributes until we hit a job...
1187       */
1188 
1189       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1190         attr = attr->next;
1191 
1192       if (attr == NULL)
1193         break;
1194 
1195      /*
1196       * Pull the needed attributes from this job...
1197       */
1198 
1199       printer = NULL;
1200       device  = NULL;
1201       uri     = NULL;
1202 
1203       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1204       {
1205         if (!strcmp(attr->name, "printer-name") &&
1206 	    attr->value_tag == IPP_TAG_NAME)
1207 	  printer = attr->values[0].string.text;
1208 
1209         if (!strcmp(attr->name, "printer-uri-supported") &&
1210 	    attr->value_tag == IPP_TAG_URI)
1211 	  uri = attr->values[0].string.text;
1212 
1213         if (!strcmp(attr->name, "device-uri") &&
1214 	    attr->value_tag == IPP_TAG_URI)
1215 	  device = attr->values[0].string.text;
1216 
1217         attr = attr->next;
1218       }
1219 
1220      /*
1221       * See if we have everything needed...
1222       */
1223 
1224       if (printer == NULL)
1225       {
1226         if (attr == NULL)
1227 	  break;
1228 	else
1229           continue;
1230       }
1231 
1232      /*
1233       * Display the printer entry if needed...
1234       */
1235 
1236       if (match_list(printers, printer))
1237       {
1238         if (device == NULL)
1239           _cupsLangPrintf(stdout, _("device for %s: %s"),
1240 	                  printer, uri);
1241         else if (!strncmp(device, "file:", 5))
1242           _cupsLangPrintf(stdout, _("device for %s: %s"),
1243 	                  printer, device + 5);
1244         else
1245           _cupsLangPrintf(stdout, _("device for %s: %s"),
1246 	                  printer, device);
1247 
1248         for (i = 0; i < num_dests; i ++)
1249         {
1250 	  if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
1251 	  {
1252             if (device == NULL)
1253               _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1254 	                      printer, dests[i].instance, uri);
1255             else if (!strncmp(device, "file:", 5))
1256               _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1257 	                      printer, dests[i].instance, device + 5);
1258             else
1259               _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1260 	                      printer, dests[i].instance, device);
1261 	  }
1262 	}
1263       }
1264 
1265       if (attr == NULL)
1266         break;
1267     }
1268 
1269     ippDelete(response);
1270   }
1271 
1272   return (0);
1273 }
1274 
1275 
1276 /*
1277  * 'show_jobs()' - Show active print jobs.
1278  */
1279 
1280 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)1281 show_jobs(const char *dests,		/* I - Destinations */
1282           const char *users,		/* I - Users */
1283           int        long_status,	/* I - Show long status? */
1284           int        ranking,		/* I - Show job ranking? */
1285 	  const char *which)		/* I - Show which jobs? */
1286 {
1287   int		i;			/* Looping var */
1288   ipp_t		*request,		/* IPP Request */
1289 		*response;		/* IPP Response */
1290   ipp_attribute_t *attr,		/* Current attribute */
1291 		*reasons;		/* Job state reasons attribute */
1292   const char	*dest,			/* Pointer into job-printer-uri */
1293 		*username,		/* Pointer to job-originating-user-name */
1294 		*message,		/* Pointer to job-printer-state-message */
1295 		*time_at;		/* time-at-xxx attribute name to use */
1296   int		rank,			/* Rank in queue */
1297 		jobid,			/* job-id */
1298 		size;			/* job-k-octets */
1299   time_t	jobtime;		/* time-at-creation */
1300   char		temp[255],		/* Temporary buffer */
1301 		date[255];		/* Date buffer */
1302   static const char *jattrs[] =		/* Attributes we need for jobs... */
1303 		{
1304 		  "job-id",
1305 		  "job-k-octets",
1306 		  "job-name",
1307 		  "job-originating-user-name",
1308 		  "job-printer-state-message",
1309 		  "job-printer-uri",
1310 		  "job-state-reasons",
1311 		  "time-at-creation",
1312 		  "time-at-completed"
1313 		};
1314 
1315 
1316   if (dests != NULL && !strcmp(dests, "all"))
1317     dests = NULL;
1318 
1319  /*
1320   * Build a IPP_GET_JOBS request, which requires the following
1321   * attributes:
1322   *
1323   *    attributes-charset
1324   *    attributes-natural-language
1325   *    printer-uri
1326   *    requested-attributes
1327   *    requesting-user-name
1328   *    which-jobs
1329   */
1330 
1331   request = ippNewRequest(IPP_GET_JOBS);
1332 
1333   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1334                NULL, "ipp://localhost/");
1335 
1336   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1337                 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1338 		NULL, jattrs);
1339 
1340   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1341                NULL, cupsUser());
1342 
1343   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
1344                NULL, which);
1345 
1346  /*
1347   * Do the request and get back a response...
1348   */
1349 
1350   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1351 
1352   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1353       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1354   {
1355     _cupsLangPrintf(stderr,
1356 		    _("%s: Error - add '/version=1.1' to server name."),
1357 		    "lpstat");
1358     ippDelete(response);
1359     return (1);
1360   }
1361   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1362   {
1363     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1364     ippDelete(response);
1365     return (1);
1366   }
1367 
1368   if (response)
1369   {
1370    /*
1371     * Loop through the job list and display them...
1372     */
1373 
1374     if (!strcmp(which, "aborted") ||
1375         !strcmp(which, "canceled") ||
1376         !strcmp(which, "completed"))
1377       time_at = "time-at-completed";
1378     else
1379       time_at = "time-at-creation";
1380 
1381     rank = -1;
1382 
1383     for (attr = response->attrs; attr != NULL; attr = attr->next)
1384     {
1385      /*
1386       * Skip leading attributes until we hit a job...
1387       */
1388 
1389       while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
1390         attr = attr->next;
1391 
1392       if (attr == NULL)
1393         break;
1394 
1395      /*
1396       * Pull the needed attributes from this job...
1397       */
1398 
1399       jobid    = 0;
1400       size     = 0;
1401       username = NULL;
1402       dest     = NULL;
1403       jobtime  = 0;
1404       message  = NULL;
1405       reasons  = NULL;
1406 
1407       while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
1408       {
1409         if (!strcmp(attr->name, "job-id") &&
1410 	    attr->value_tag == IPP_TAG_INTEGER)
1411 	  jobid = attr->values[0].integer;
1412         else if (!strcmp(attr->name, "job-k-octets") &&
1413 		 attr->value_tag == IPP_TAG_INTEGER)
1414 	  size = attr->values[0].integer;
1415         else if (!strcmp(attr->name, time_at) && attr->value_tag == IPP_TAG_INTEGER)
1416 	  jobtime = attr->values[0].integer;
1417         else if (!strcmp(attr->name, "job-printer-state-message") &&
1418 	         attr->value_tag == IPP_TAG_TEXT)
1419 	  message = attr->values[0].string.text;
1420         else if (!strcmp(attr->name, "job-printer-uri") &&
1421 	         attr->value_tag == IPP_TAG_URI)
1422 	{
1423 	  if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
1424 	    dest ++;
1425         }
1426         else if (!strcmp(attr->name, "job-originating-user-name") &&
1427 	         attr->value_tag == IPP_TAG_NAME)
1428 	  username = attr->values[0].string.text;
1429         else if (!strcmp(attr->name, "job-state-reasons") &&
1430 	         attr->value_tag == IPP_TAG_KEYWORD)
1431 	  reasons = attr;
1432 
1433         attr = attr->next;
1434       }
1435 
1436      /*
1437       * See if we have everything needed...
1438       */
1439 
1440       if (dest == NULL || jobid == 0)
1441       {
1442         if (attr == NULL)
1443 	  break;
1444 	else
1445           continue;
1446       }
1447 
1448      /*
1449       * Display the job...
1450       */
1451 
1452       rank ++;
1453 
1454       if (match_list(dests, dest) && match_list(users, username))
1455       {
1456         snprintf(temp, sizeof(temp), "%s-%d", dest, jobid);
1457 
1458 	_cupsStrDate(date, sizeof(date), jobtime);
1459 
1460 	if (ranking)
1461 	  _cupsLangPrintf(stdout, "%3d %-21s %-13s %8.0f %s",
1462 			  rank, temp, username ? username : "unknown",
1463 			  1024.0 * size, date);
1464 	else
1465 	  _cupsLangPrintf(stdout, "%-23s %-13s %8.0f   %s",
1466 			  temp, username ? username : "unknown",
1467 			  1024.0 * size, date);
1468 	if (long_status)
1469 	{
1470 	  if (message)
1471 	    _cupsLangPrintf(stdout, _("\tStatus: %s"), message);
1472 
1473 	  if (reasons)
1474 	  {
1475 	    char	alerts[1024],	/* Alerts string */
1476 		      *aptr;		/* Pointer into alerts string */
1477 
1478 	    for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1479 	    {
1480 	      if (i)
1481 		snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1482 	      else
1483 		strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1484 
1485 	      aptr += strlen(aptr);
1486 	    }
1487 
1488 	    _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1489 	  }
1490 
1491 	  _cupsLangPrintf(stdout, _("\tqueued for %s"), dest);
1492 	}
1493       }
1494 
1495       if (attr == NULL)
1496         break;
1497     }
1498 
1499     ippDelete(response);
1500   }
1501 
1502   return (0);
1503 }
1504 
1505 
1506 /*
1507  * 'show_printers()' - Show printers.
1508  */
1509 
1510 static int				/* O - 0 on success, 1 on fail */
show_printers(const char * printers,int num_dests,cups_dest_t * dests,int long_status)1511 show_printers(const char  *printers,	/* I - Destinations */
1512               int         num_dests,	/* I - Number of user-defined dests */
1513 	      cups_dest_t *dests,	/* I - User-defined destinations */
1514               int         long_status)	/* I - Show long status? */
1515 {
1516   int		i, j;			/* Looping vars */
1517   ipp_t		*request,		/* IPP Request */
1518 		*response,		/* IPP Response */
1519 		*jobs;			/* IPP Get Jobs response */
1520   ipp_attribute_t *attr,		/* Current attribute */
1521 		*jobattr,		/* Job ID attribute */
1522 		*reasons;		/* Job state reasons attribute */
1523   const char	*printer,		/* Printer name */
1524 		*message,		/* Printer state message */
1525 		*description,		/* Description of printer */
1526 		*location,		/* Location of printer */
1527 		*make_model,		/* Make and model of printer */
1528 		*uri;			/* URI of printer */
1529   ipp_attribute_t *allowed,		/* requesting-user-name-allowed */
1530 		*denied;		/* requestint-user-name-denied */
1531   ipp_pstate_t	pstate;			/* Printer state */
1532   cups_ptype_t	ptype;			/* Printer type */
1533   time_t	ptime;			/* Printer state time */
1534   int		jobid;			/* Job ID of current job */
1535   char		printer_uri[HTTP_MAX_URI],
1536 					/* Printer URI */
1537 		printer_state_time[255];/* Printer state time */
1538   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
1539   static const char *pattrs[] =		/* Attributes we need for printers... */
1540 		{
1541 		  "printer-name",
1542 		  "printer-state",
1543 		  "printer-state-message",
1544 		  "printer-state-reasons",
1545 		  "printer-state-change-time",
1546 		  "printer-type",
1547 		  "printer-info",
1548                   "printer-location",
1549 		  "printer-make-and-model",
1550 		  "printer-uri-supported",
1551 		  "requesting-user-name-allowed",
1552 		  "requesting-user-name-denied"
1553 		};
1554   static const char *jattrs[] =		/* Attributes we need for jobs... */
1555 		{
1556 		  "job-id",
1557 		  "job-state"
1558 		};
1559 
1560 
1561   if (printers != NULL && !strcmp(printers, "all"))
1562     printers = NULL;
1563 
1564  /*
1565   * Build a CUPS_GET_PRINTERS request, which requires the following
1566   * attributes:
1567   *
1568   *    attributes-charset
1569   *    attributes-natural-language
1570   *    requested-attributes
1571   *    requesting-user-name
1572   */
1573 
1574   request = ippNewRequest(CUPS_GET_PRINTERS);
1575 
1576   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1577                 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1578 		NULL, pattrs);
1579 
1580   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1581                NULL, cupsUser());
1582 
1583  /*
1584   * Do the request and get back a response...
1585   */
1586 
1587   response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1588 
1589   if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1590       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1591   {
1592     _cupsLangPrintf(stderr,
1593 		    _("%s: Error - add '/version=1.1' to server name."),
1594 		    "lpstat");
1595     ippDelete(response);
1596     return (1);
1597   }
1598   else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1599   {
1600     _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1601     ippDelete(response);
1602     return (1);
1603   }
1604 
1605   if (response)
1606   {
1607    /*
1608     * Loop through the printers returned in the list and display
1609     * their status...
1610     */
1611 
1612     for (attr = response->attrs; attr != NULL; attr = attr->next)
1613     {
1614      /*
1615       * Skip leading attributes until we hit a job...
1616       */
1617 
1618       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1619         attr = attr->next;
1620 
1621       if (attr == NULL)
1622         break;
1623 
1624      /*
1625       * Pull the needed attributes from this job...
1626       */
1627 
1628       printer     = NULL;
1629       ptime       = 0;
1630       ptype       = CUPS_PRINTER_LOCAL;
1631       pstate      = IPP_PRINTER_IDLE;
1632       message     = NULL;
1633       description = NULL;
1634       location    = NULL;
1635       make_model  = NULL;
1636       reasons     = NULL;
1637       uri         = NULL;
1638       jobid       = 0;
1639       allowed     = NULL;
1640       denied      = NULL;
1641 
1642       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1643       {
1644         if (!strcmp(attr->name, "printer-name") &&
1645 	    attr->value_tag == IPP_TAG_NAME)
1646 	  printer = attr->values[0].string.text;
1647         else if (!strcmp(attr->name, "printer-state") &&
1648 	         attr->value_tag == IPP_TAG_ENUM)
1649 	  pstate = (ipp_pstate_t)attr->values[0].integer;
1650         else if (!strcmp(attr->name, "printer-type") &&
1651 	         attr->value_tag == IPP_TAG_ENUM)
1652 	  ptype = (cups_ptype_t)attr->values[0].integer;
1653         else if (!strcmp(attr->name, "printer-state-message") &&
1654 	         attr->value_tag == IPP_TAG_TEXT)
1655 	  message = attr->values[0].string.text;
1656         else if (!strcmp(attr->name, "printer-state-change-time") &&
1657 	         attr->value_tag == IPP_TAG_INTEGER)
1658 	  ptime = (time_t)attr->values[0].integer;
1659 	else if (!strcmp(attr->name, "printer-info") &&
1660 	         attr->value_tag == IPP_TAG_TEXT)
1661 	  description = attr->values[0].string.text;
1662         else if (!strcmp(attr->name, "printer-location") &&
1663 	         attr->value_tag == IPP_TAG_TEXT)
1664 	  location = attr->values[0].string.text;
1665         else if (!strcmp(attr->name, "printer-make-and-model") &&
1666 	         attr->value_tag == IPP_TAG_TEXT)
1667 	  make_model = attr->values[0].string.text;
1668         else if (!strcmp(attr->name, "printer-uri-supported") &&
1669 	         attr->value_tag == IPP_TAG_URI)
1670 	  uri = attr->values[0].string.text;
1671         else if (!strcmp(attr->name, "printer-state-reasons") &&
1672 	         attr->value_tag == IPP_TAG_KEYWORD)
1673 	  reasons = attr;
1674         else if (!strcmp(attr->name, "requesting-user-name-allowed") &&
1675 	         attr->value_tag == IPP_TAG_NAME)
1676 	  allowed = attr;
1677         else if (!strcmp(attr->name, "requesting-user-name-denied") &&
1678 	         attr->value_tag == IPP_TAG_NAME)
1679 	  denied = attr;
1680 
1681         attr = attr->next;
1682       }
1683 
1684      /*
1685       * See if we have everything needed...
1686       */
1687 
1688       if (printer == NULL)
1689       {
1690         if (attr == NULL)
1691 	  break;
1692 	else
1693           continue;
1694       }
1695 
1696      /*
1697       * Display the printer entry if needed...
1698       */
1699 
1700       if (match_list(printers, printer))
1701       {
1702        /*
1703         * If the printer state is "IPP_PRINTER_PROCESSING", then grab the
1704 	* current job for the printer.
1705 	*/
1706 
1707         if (pstate == IPP_PRINTER_PROCESSING)
1708 	{
1709 	 /*
1710 	  * Build an IPP_GET_JOBS request, which requires the following
1711 	  * attributes:
1712 	  *
1713 	  *    attributes-charset
1714 	  *    attributes-natural-language
1715 	  *    printer-uri
1716 	  *    limit
1717           *    requested-attributes
1718 	  */
1719 
1720 	  request = ippNewRequest(IPP_GET_JOBS);
1721 
1722 	  request->request.op.operation_id = IPP_GET_JOBS;
1723 	  request->request.op.request_id   = 1;
1724 
1725 	  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1726                 	"requested-attributes",
1727 		        sizeof(jattrs) / sizeof(jattrs[0]), NULL, jattrs);
1728 
1729 	  httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
1730 	                   "ipp", NULL, "localhost", 0, "/printers/%s", printer);
1731 	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1732 	               "printer-uri", NULL, printer_uri);
1733 
1734           if ((jobs = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) != NULL)
1735 	  {
1736 	   /*
1737 	    * Get the current active job on this queue...
1738 	    */
1739 
1740             ipp_jstate_t jobstate = IPP_JOB_PENDING;
1741 	    jobid = 0;
1742 
1743 	    for (jobattr = jobs->attrs; jobattr; jobattr = jobattr->next)
1744 	    {
1745 	      if (!jobattr->name)
1746 	      {
1747 	        if (jobstate == IPP_JOB_PROCESSING)
1748 		  break;
1749 	        else
1750 		  continue;
1751               }
1752 
1753 	      if (!strcmp(jobattr->name, "job-id") &&
1754 	          jobattr->value_tag == IPP_TAG_INTEGER)
1755 		jobid = jobattr->values[0].integer;
1756               else if (!strcmp(jobattr->name, "job-state") &&
1757 	               jobattr->value_tag == IPP_TAG_ENUM)
1758 		jobstate = (ipp_jstate_t)jobattr->values[0].integer;
1759 	    }
1760 
1761             if (jobstate != IPP_JOB_PROCESSING)
1762 	      jobid = 0;
1763 
1764             ippDelete(jobs);
1765 	  }
1766         }
1767 
1768        /*
1769         * Display it...
1770 	*/
1771 
1772         _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
1773 
1774         switch (pstate)
1775 	{
1776 	  case IPP_PRINTER_IDLE :
1777 	      if (ippContainsString(reasons, "hold-new-jobs"))
1778 		_cupsLangPrintf(stdout, _("printer %s is holding new jobs.  enabled since %s"), printer, printer_state_time);
1779 	      else
1780 		_cupsLangPrintf(stdout, _("printer %s is idle.  enabled since %s"), printer, printer_state_time);
1781 	      break;
1782 	  case IPP_PRINTER_PROCESSING :
1783 	      _cupsLangPrintf(stdout, _("printer %s now printing %s-%d.  enabled since %s"), printer, printer, jobid, printer_state_time);
1784 	      break;
1785 	  case IPP_PRINTER_STOPPED :
1786 	      _cupsLangPrintf(stdout, _("printer %s disabled since %s -"), printer, printer_state_time);
1787 	      break;
1788 	}
1789 
1790         if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
1791 	{
1792 	  if (!message || !*message)
1793 	    _cupsLangPuts(stdout, _("\treason unknown"));
1794 	  else
1795 	    _cupsLangPrintf(stdout, "\t%s", message);
1796 	}
1797 
1798         if (long_status > 1)
1799 	{
1800 	  _cupsLangPuts(stdout, _("\tForm mounted:"));
1801 	  _cupsLangPuts(stdout, _("\tContent types: any"));
1802 	  _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
1803 	}
1804 
1805         if (long_status)
1806 	{
1807 	  _cupsLangPrintf(stdout, _("\tDescription: %s"),
1808 	                  description ? description : "");
1809 
1810 	  if (reasons)
1811 	  {
1812 	    char	alerts[1024],	/* Alerts string */
1813 			*aptr;		/* Pointer into alerts string */
1814 
1815 	    for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1816 	    {
1817 	      if (i)
1818 		snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1819 	      else
1820 		strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1821 
1822 	      aptr += strlen(aptr);
1823 	    }
1824 
1825 	    _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1826 	  }
1827 	}
1828         if (long_status > 1)
1829 	{
1830 	  _cupsLangPrintf(stdout, _("\tLocation: %s"),
1831 	                  location ? location : "");
1832 
1833 	  if (ptype & CUPS_PRINTER_REMOTE)
1834 	  {
1835 	    _cupsLangPuts(stdout, _("\tConnection: remote"));
1836 
1837 	    if (make_model && !strstr(make_model, "System V Printer") &&
1838 	             !strstr(make_model, "Raw Printer") && uri)
1839 	      _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"),
1840 	                      uri);
1841 	  }
1842 	  else
1843 	  {
1844 	    _cupsLangPuts(stdout, _("\tConnection: direct"));
1845 
1846 	    if (make_model && !strstr(make_model, "Raw Printer"))
1847 	      _cupsLangPrintf(stdout,
1848 	                      _("\tInterface: %s/ppd/%s.ppd"),
1849 			      cg->cups_serverroot, printer);
1850           }
1851 	  _cupsLangPuts(stdout, _("\tOn fault: no alert"));
1852 	  _cupsLangPuts(stdout, _("\tAfter fault: continue"));
1853 	      /* TODO update to use printer-error-policy */
1854           if (allowed)
1855 	  {
1856 	    _cupsLangPuts(stdout, _("\tUsers allowed:"));
1857 	    for (j = 0; j < allowed->num_values; j ++)
1858 	      _cupsLangPrintf(stdout, "\t\t%s",
1859 	                      allowed->values[j].string.text);
1860 	  }
1861 	  else if (denied)
1862 	  {
1863 	    _cupsLangPuts(stdout, _("\tUsers denied:"));
1864 	    for (j = 0; j < denied->num_values; j ++)
1865 	      _cupsLangPrintf(stdout, "\t\t%s",
1866 	                      denied->values[j].string.text);
1867 	  }
1868 	  else
1869 	  {
1870 	    _cupsLangPuts(stdout, _("\tUsers allowed:"));
1871 	    _cupsLangPuts(stdout, _("\t\t(all)"));
1872 	  }
1873 	  _cupsLangPuts(stdout, _("\tForms allowed:"));
1874 	  _cupsLangPuts(stdout, _("\t\t(none)"));
1875 	  _cupsLangPuts(stdout, _("\tBanner required"));
1876 	  _cupsLangPuts(stdout, _("\tCharset sets:"));
1877 	  _cupsLangPuts(stdout, _("\t\t(none)"));
1878 	  _cupsLangPuts(stdout, _("\tDefault pitch:"));
1879 	  _cupsLangPuts(stdout, _("\tDefault page size:"));
1880 	  _cupsLangPuts(stdout, _("\tDefault port settings:"));
1881 	}
1882 
1883         for (i = 0; i < num_dests; i ++)
1884 	  if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
1885 	  {
1886             switch (pstate)
1887 	    {
1888 	      case IPP_PRINTER_IDLE :
1889 		  _cupsLangPrintf(stdout,
1890 		                  _("printer %s/%s is idle.  "
1891 				    "enabled since %s"),
1892 				  printer, dests[i].instance,
1893 				  printer_state_time);
1894 		  break;
1895 	      case IPP_PRINTER_PROCESSING :
1896 		  _cupsLangPrintf(stdout,
1897 		                  _("printer %s/%s now printing %s-%d.  "
1898 				    "enabled since %s"),
1899 				  printer, dests[i].instance, printer, jobid,
1900 				  printer_state_time);
1901 		  break;
1902 	      case IPP_PRINTER_STOPPED :
1903 		  _cupsLangPrintf(stdout,
1904 		                  _("printer %s/%s disabled since %s -"),
1905 				  printer, dests[i].instance,
1906 				  printer_state_time);
1907 		  break;
1908 	    }
1909 
1910             if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
1911 	    {
1912 	      if (!message || !*message)
1913 		_cupsLangPuts(stdout, _("\treason unknown"));
1914 	      else
1915 		_cupsLangPrintf(stdout, "\t%s", message);
1916             }
1917 
1918             if (long_status > 1)
1919 	    {
1920 	      _cupsLangPuts(stdout, _("\tForm mounted:"));
1921 	      _cupsLangPuts(stdout, _("\tContent types: any"));
1922 	      _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
1923 	    }
1924 
1925             if (long_status)
1926 	    {
1927 	      _cupsLangPrintf(stdout, _("\tDescription: %s"),
1928 	                      description ? description : "");
1929 
1930 	      if (reasons)
1931 	      {
1932 		char	alerts[1024],	/* Alerts string */
1933 			*aptr;		/* Pointer into alerts string */
1934 
1935 		for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1936 		{
1937 		  if (i)
1938 		    snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1939 		  else
1940 		    strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1941 
1942 		  aptr += strlen(aptr);
1943 		}
1944 
1945 		_cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1946 	      }
1947 	    }
1948             if (long_status > 1)
1949 	    {
1950 	      _cupsLangPrintf(stdout, _("\tLocation: %s"),
1951 	                      location ? location : "");
1952 
1953 	      if (ptype & CUPS_PRINTER_REMOTE)
1954 	      {
1955 		_cupsLangPuts(stdout, _("\tConnection: remote"));
1956 
1957 		if (make_model && !strstr(make_model, "System V Printer") &&
1958 	        	 !strstr(make_model, "Raw Printer") && uri)
1959 		  _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"), uri);
1960 	      }
1961 	      else
1962 	      {
1963 		_cupsLangPuts(stdout, _("\tConnection: direct"));
1964 
1965 		if (make_model && !strstr(make_model, "Raw Printer"))
1966 		  _cupsLangPrintf(stdout,
1967 	                	  _("\tInterface: %s/ppd/%s.ppd"),
1968 				  cg->cups_serverroot, printer);
1969               }
1970 	      _cupsLangPuts(stdout, _("\tOn fault: no alert"));
1971 	      _cupsLangPuts(stdout, _("\tAfter fault: continue"));
1972 		  /* TODO update to use printer-error-policy */
1973               if (allowed)
1974 	      {
1975 		_cupsLangPuts(stdout, _("\tUsers allowed:"));
1976 		for (j = 0; j < allowed->num_values; j ++)
1977 		  _cupsLangPrintf(stdout, "\t\t%s",
1978 	                	  allowed->values[j].string.text);
1979 	      }
1980 	      else if (denied)
1981 	      {
1982 		_cupsLangPuts(stdout, _("\tUsers denied:"));
1983 		for (j = 0; j < denied->num_values; j ++)
1984 		  _cupsLangPrintf(stdout, "\t\t%s",
1985 	                	  denied->values[j].string.text);
1986 	      }
1987 	      else
1988 	      {
1989 		_cupsLangPuts(stdout, _("\tUsers allowed:"));
1990 		_cupsLangPuts(stdout, _("\t\t(all)"));
1991 	      }
1992 	      _cupsLangPuts(stdout, _("\tForms allowed:"));
1993 	      _cupsLangPuts(stdout, _("\t\t(none)"));
1994 	      _cupsLangPuts(stdout, _("\tBanner required"));
1995 	      _cupsLangPuts(stdout, _("\tCharset sets:"));
1996 	      _cupsLangPuts(stdout, _("\t\t(none)"));
1997 	      _cupsLangPuts(stdout, _("\tDefault pitch:"));
1998 	      _cupsLangPuts(stdout, _("\tDefault page size:"));
1999 	      _cupsLangPuts(stdout, _("\tDefault port settings:"));
2000 	    }
2001 	  }
2002       }
2003 
2004       if (attr == NULL)
2005         break;
2006     }
2007 
2008     ippDelete(response);
2009   }
2010 
2011   return (0);
2012 }
2013 
2014 
2015 /*
2016  * 'show_scheduler()' - Show scheduler status.
2017  */
2018 
2019 static void
show_scheduler(void)2020 show_scheduler(void)
2021 {
2022   http_t	*http;			/* Connection to server */
2023 
2024 
2025   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
2026                                  cupsEncryption())) != NULL)
2027   {
2028     _cupsLangPuts(stdout, _("scheduler is running"));
2029     httpClose(http);
2030   }
2031   else
2032     _cupsLangPuts(stdout, _("scheduler is not running"));
2033 }
2034 
2035 
2036 /*
2037  * 'usage()' - Show program usage and exit.
2038  */
2039 
2040 static void
usage(void)2041 usage(void)
2042 {
2043   _cupsLangPuts(stdout, _("Usage: lpstat [options]"));
2044   _cupsLangPuts(stdout, _("Options:"));
2045   _cupsLangPuts(stdout, _("-E                      Encrypt the connection to the server"));
2046   _cupsLangPuts(stdout, _("-h server[:port]        Connect to the named server and port"));
2047   _cupsLangPuts(stdout, _("-l                      Show verbose (long) output"));
2048   _cupsLangPuts(stdout, _("-U username             Specify the username to use for authentication"));
2049 
2050   _cupsLangPuts(stdout, _("-H                      Show the default server and port"));
2051   _cupsLangPuts(stdout, _("-W completed            Show completed jobs"));
2052   _cupsLangPuts(stdout, _("-W not-completed        Show pending jobs"));
2053   _cupsLangPuts(stdout, _("-a [destination(s)]     Show the accepting state of destinations"));
2054   _cupsLangPuts(stdout, _("-c [class(es)]          Show classes and their member printers"));
2055   _cupsLangPuts(stdout, _("-d                      Show the default destination"));
2056   _cupsLangPuts(stdout, _("-e                      Show available destinations on the network"));
2057   _cupsLangPuts(stdout, _("-o [destination(s)]     Show jobs"));
2058   _cupsLangPuts(stdout, _("-p [printer(s)]         Show the processing state of destinations"));
2059   _cupsLangPuts(stdout, _("-r                      Show whether the CUPS server is running"));
2060   _cupsLangPuts(stdout, _("-R                      Show the ranking of jobs"));
2061   _cupsLangPuts(stdout, _("-s                      Show a status summary"));
2062   _cupsLangPuts(stdout, _("-t                      Show all status information"));
2063   _cupsLangPuts(stdout, _("-u [user(s)]            Show jobs queued by the current or specified users"));
2064   _cupsLangPuts(stdout, _("-v [printer(s)]         Show the devices for each destination"));
2065 
2066   exit(1);
2067 }
2068