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