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