xref: /aosp_15_r20/external/libcups/berkeley/lpq.c (revision 5e7646d21f1134fb0638875d812ef646c12ab91e)
1 /*
2  * "lpq" command for CUPS.
3  *
4  * Copyright © 2007-2018 by Apple Inc.
5  * Copyright © 1997-2006 by Easy Software Products.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include <cups/cups-private.h>
16 
17 
18 /*
19  * Local functions...
20  */
21 
22 static http_t	*connect_server(const char *, http_t *);
23 static int	show_jobs(const char *, http_t *, const char *,
24 		          const char *, const int, const int);
25 static void	show_printer(const char *, http_t *, const char *);
26 static void	usage(void) _CUPS_NORETURN;
27 
28 
29 /*
30  * 'main()' - Parse options and commands.
31  */
32 
33 int
main(int argc,char * argv[])34 main(int  argc,				/* I - Number of command-line arguments */
35      char *argv[])			/* I - Command-line arguments */
36 {
37   int		i;			/* Looping var */
38   http_t	*http;			/* Connection to server */
39   const char	*opt,			/* Option pointer */
40 		*dest,			/* Desired printer */
41 		*user,			/* Desired user */
42 		*val;			/* Environment variable name */
43   char		*instance;		/* Printer instance */
44   int		id,			/* Desired job ID */
45 		all,			/* All printers */
46 		interval,		/* Reporting interval */
47 		longstatus;		/* Show file details */
48   cups_dest_t	*named_dest;		/* Named destination */
49 
50 
51   _cupsSetLocale(argv);
52 
53  /*
54   * Check for command-line options...
55   */
56 
57   http       = NULL;
58   dest       = NULL;
59   user       = NULL;
60   id         = 0;
61   interval   = 0;
62   longstatus = 0;
63   all        = 0;
64 
65   for (i = 1; i < argc; i ++)
66   {
67     if (argv[i][0] == '+')
68     {
69       interval = atoi(argv[i] + 1);
70     }
71     else if (!strcmp(argv[i], "--help"))
72       usage();
73     else if (argv[i][0] == '-')
74     {
75       for (opt = argv[i] + 1; *opt; opt ++)
76       {
77 	switch (*opt)
78 	{
79 	  case 'E' : /* Encrypt */
80 #ifdef HAVE_SSL
81 	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
82 
83 	      if (http)
84 		httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
85 #else
86 	      _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
87 #endif /* HAVE_SSL */
88 	      break;
89 
90 	  case 'U' : /* Username */
91 	      if (opt[1] != '\0')
92 	      {
93 		cupsSetUser(opt + 1);
94 		opt += strlen(opt) - 1;
95 	      }
96 	      else
97 	      {
98 		i ++;
99 		if (i >= argc)
100 		{
101 		  _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
102 		  return (1);
103 		}
104 
105 		cupsSetUser(argv[i]);
106 	      }
107 	      break;
108 
109 	  case 'P' : /* Printer */
110 	      if (opt[1] != '\0')
111 	      {
112 		dest = opt + 1;
113 		opt += strlen(opt) - 1;
114 	      }
115 	      else
116 	      {
117 		i ++;
118 
119 		if (i >= argc)
120 		{
121 		  httpClose(http);
122 
123 		  usage();
124 		}
125 
126 		dest = argv[i];
127 	      }
128 
129 	      if ((instance = strchr(dest, '/')) != NULL)
130 		*instance++ = '\0';
131 
132 	      http = connect_server(argv[0], http);
133 
134 	      if ((named_dest = cupsGetNamedDest(http, dest, instance)) == NULL)
135 	      {
136 		if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
137 		    cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
138 		  _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
139 		else if (instance)
140 		  _cupsLangPrintf(stderr, _("%s: Error - unknown destination \"%s/%s\"."), argv[0], dest, instance);
141 		else
142 		  _cupsLangPrintf(stderr, _("%s: Unknown destination \"%s\"."), argv[0], dest);
143 
144 		return (1);
145 	      }
146 
147 	      cupsFreeDests(1, named_dest);
148 	      break;
149 
150 	  case 'a' : /* All printers */
151 	      all = 1;
152 	      break;
153 
154 	  case 'h' : /* Connect to host */
155 	      if (http)
156 	      {
157 		httpClose(http);
158 		http = NULL;
159 	      }
160 
161 	      if (opt[1] != '\0')
162 	      {
163 		cupsSetServer(opt + 1);
164 		opt += strlen(opt) - 1;
165 	      }
166 	      else
167 	      {
168 		i ++;
169 
170 		if (i >= argc)
171 		{
172 		  _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
173 		  return (1);
174 		}
175 		else
176 		  cupsSetServer(argv[i]);
177 	      }
178 	      break;
179 
180 	  case 'l' : /* Long status */
181 	      longstatus = 1;
182 	      break;
183 
184 	  default :
185 	      httpClose(http);
186 
187 	      usage();
188 	}
189       }
190     }
191     else if (isdigit(argv[i][0] & 255))
192     {
193       id = atoi(argv[i]);
194     }
195     else
196     {
197       user = argv[i];
198     }
199   }
200 
201   http = connect_server(argv[0], http);
202 
203   if (dest == NULL && !all)
204   {
205     if ((named_dest = cupsGetNamedDest(http, NULL, NULL)) == NULL)
206     {
207       if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
208           cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
209       {
210 	_cupsLangPrintf(stderr,
211 	                _("%s: Error - add '/version=1.1' to server name."),
212 			argv[0]);
213         return (1);
214       }
215 
216       val = NULL;
217 
218       if ((dest = getenv("LPDEST")) == NULL)
219       {
220 	if ((dest = getenv("PRINTER")) != NULL)
221 	{
222           if (!strcmp(dest, "lp"))
223             dest = NULL;
224 	  else
225 	    val = "PRINTER";
226 	}
227       }
228       else
229 	val = "LPDEST";
230 
231       if (dest && val)
232 	_cupsLangPrintf(stderr,
233 	                _("%s: Error - %s environment variable names "
234 			  "non-existent destination \"%s\"."), argv[0], val,
235 			dest);
236       else
237 	_cupsLangPrintf(stderr,
238 	                _("%s: Error - no default destination available."),
239 			argv[0]);
240       httpClose(http);
241       return (1);
242     }
243 
244     dest = named_dest->name;
245   }
246 
247  /*
248   * Show the status in a loop...
249   */
250 
251   for (;;)
252   {
253     if (dest)
254       show_printer(argv[0], http, dest);
255 
256     i = show_jobs(argv[0], http, dest, user, id, longstatus);
257 
258     if (i && interval)
259     {
260       fflush(stdout);
261       sleep((unsigned)interval);
262     }
263     else
264       break;
265   }
266 
267  /*
268   * Close the connection to the server and return...
269   */
270 
271   httpClose(http);
272 
273   return (0);
274 }
275 
276 
277 /*
278  * 'connect_server()' - Connect to the server as necessary...
279  */
280 
281 static http_t *				/* O - New HTTP connection */
connect_server(const char * command,http_t * http)282 connect_server(const char *command,	/* I - Command name */
283                http_t     *http)	/* I - Current HTTP connection */
284 {
285   if (!http)
286   {
287     http = httpConnectEncrypt(cupsServer(), ippPort(),
288 	                      cupsEncryption());
289 
290     if (http == NULL)
291     {
292       _cupsLangPrintf(stderr, _("%s: Unable to connect to server."), command);
293       exit(1);
294     }
295   }
296 
297   return (http);
298 }
299 
300 
301 /*
302  * 'show_jobs()' - Show jobs.
303  */
304 
305 static int				/* O - Number of jobs in queue */
show_jobs(const char * command,http_t * http,const char * dest,const char * user,const int id,const int longstatus)306 show_jobs(const char *command,		/* I - Command name */
307           http_t     *http,		/* I - HTTP connection to server */
308           const char *dest,		/* I - Destination */
309 	  const char *user,		/* I - User */
310 	  const int  id,		/* I - Job ID */
311 	  const int  longstatus)	/* I - 1 if long report desired */
312 {
313   ipp_t		*request,		/* IPP Request */
314 		*response;		/* IPP Response */
315   ipp_attribute_t *attr;		/* Current attribute */
316   const char	*jobdest,		/* Pointer into job-printer-uri */
317 		*jobuser,		/* Pointer to job-originating-user-name */
318 		*jobname;		/* Pointer to job-name */
319   ipp_jstate_t	jobstate;		/* job-state */
320   int		jobid,			/* job-id */
321 		jobsize,		/* job-k-octets */
322 		jobcount,		/* Number of jobs */
323 		jobcopies,		/* Number of copies */
324 		rank;			/* Rank of job */
325   char		resource[1024];		/* Resource string */
326   char		rankstr[255];		/* Rank string */
327   char		namestr[1024];		/* Job name string */
328   static const char * const jobattrs[] =/* Job attributes we want to see */
329 		{
330 		  "copies",
331 		  "job-id",
332 		  "job-k-octets",
333 		  "job-name",
334 		  "job-originating-user-name",
335 		  "job-printer-uri",
336 		  "job-priority",
337 		  "job-state"
338 		};
339   static const char * const ranks[10] =	/* Ranking strings */
340 		{
341 		  "th",
342 		  "st",
343 		  "nd",
344 		  "rd",
345 		  "th",
346 		  "th",
347 		  "th",
348 		  "th",
349 		  "th",
350 		  "th"
351 		};
352 
353 
354   if (http == NULL)
355     return (0);
356 
357  /*
358   * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
359   * the following attributes:
360   *
361   *    attributes-charset
362   *    attributes-natural-language
363   *    job-uri or printer-uri
364   *    requested-attributes
365   *    requesting-user-name
366   */
367 
368   request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
369 
370   if (id)
371   {
372     snprintf(resource, sizeof(resource), "ipp://localhost/jobs/%d", id);
373     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
374                  NULL, resource);
375   }
376   else if (dest)
377   {
378     httpAssembleURIf(HTTP_URI_CODING_ALL, resource, sizeof(resource), "ipp",
379                      NULL, "localhost", 0, "/printers/%s", dest);
380 
381     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
382                  NULL, resource);
383   }
384   else
385     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
386                  NULL, "ipp://localhost/");
387 
388   if (user)
389   {
390     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
391                  "requesting-user-name", NULL, user);
392     ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
393   }
394   else
395     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
396                  "requesting-user-name", NULL, cupsUser());
397 
398   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
399                 "requested-attributes",
400                 (int)(sizeof(jobattrs) / sizeof(jobattrs[0])), NULL, jobattrs);
401 
402  /*
403   * Do the request and get back a response...
404   */
405 
406   jobcount = 0;
407 
408   if ((response = cupsDoRequest(http, request, "/")) != NULL)
409   {
410     if (response->request.status.status_code > IPP_OK_CONFLICT)
411     {
412       _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
413       ippDelete(response);
414       return (0);
415     }
416 
417     rank = 1;
418 
419    /*
420     * Loop through the job list and display them...
421     */
422 
423     for (attr = response->attrs; attr != NULL; attr = attr->next)
424     {
425      /*
426       * Skip leading attributes until we hit a job...
427       */
428 
429       while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
430         attr = attr->next;
431 
432       if (attr == NULL)
433         break;
434 
435      /*
436       * Pull the needed attributes from this job...
437       */
438 
439       jobid       = 0;
440       jobsize     = 0;
441       jobstate    = IPP_JOB_PENDING;
442       jobname     = "unknown";
443       jobuser     = "unknown";
444       jobdest     = NULL;
445       jobcopies   = 1;
446 
447       while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
448       {
449         if (!strcmp(attr->name, "job-id") &&
450 	    attr->value_tag == IPP_TAG_INTEGER)
451 	  jobid = attr->values[0].integer;
452 
453         if (!strcmp(attr->name, "job-k-octets") &&
454 	    attr->value_tag == IPP_TAG_INTEGER)
455 	  jobsize = attr->values[0].integer;
456 
457         if (!strcmp(attr->name, "job-state") &&
458 	    attr->value_tag == IPP_TAG_ENUM)
459 	  jobstate = (ipp_jstate_t)attr->values[0].integer;
460 
461         if (!strcmp(attr->name, "job-printer-uri") &&
462 	    attr->value_tag == IPP_TAG_URI)
463 	  if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
464 	    jobdest ++;
465 
466         if (!strcmp(attr->name, "job-originating-user-name") &&
467 	    attr->value_tag == IPP_TAG_NAME)
468 	  jobuser = attr->values[0].string.text;
469 
470         if (!strcmp(attr->name, "job-name") &&
471 	    attr->value_tag == IPP_TAG_NAME)
472 	  jobname = attr->values[0].string.text;
473 
474         if (!strcmp(attr->name, "copies") &&
475 	    attr->value_tag == IPP_TAG_INTEGER)
476 	  jobcopies = attr->values[0].integer;
477 
478         attr = attr->next;
479       }
480 
481      /*
482       * See if we have everything needed...
483       */
484 
485       if (jobdest == NULL || jobid == 0)
486       {
487         if (attr == NULL)
488 	  break;
489 	else
490           continue;
491       }
492 
493       if (!longstatus && jobcount == 0)
494 	_cupsLangPuts(stdout,
495 	              _("Rank    Owner   Job     File(s)"
496 		        "                         Total Size"));
497 
498       jobcount ++;
499 
500      /*
501       * Display the job...
502       */
503 
504       if (jobstate == IPP_JOB_PROCESSING)
505 	strlcpy(rankstr, "active", sizeof(rankstr));
506       else
507       {
508        /*
509         * Make the rank show the "correct" suffix for each number
510 	* (11-13 are the only special cases, for English anyways...)
511 	*/
512 
513 	if ((rank % 100) >= 11 && (rank % 100) <= 13)
514 	  snprintf(rankstr, sizeof(rankstr), "%dth", rank);
515 	else
516 	  snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
517 
518 	rank ++;
519       }
520 
521       if (longstatus)
522       {
523         _cupsLangPuts(stdout, "\n");
524 
525         if (jobcopies > 1)
526 	  snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
527 	           jobname);
528 	else
529 	  strlcpy(namestr, jobname, sizeof(namestr));
530 
531         _cupsLangPrintf(stdout, _("%s: %-33.33s [job %d localhost]"),
532 	                jobuser, rankstr, jobid);
533         _cupsLangPrintf(stdout, _("        %-39.39s %.0f bytes"),
534 	                namestr, 1024.0 * jobsize);
535       }
536       else
537         _cupsLangPrintf(stdout,
538 	                _("%-7s %-7.7s %-7d %-31.31s %.0f bytes"),
539 			rankstr, jobuser, jobid, jobname, 1024.0 * jobsize);
540 
541       if (attr == NULL)
542         break;
543     }
544 
545     ippDelete(response);
546   }
547   else
548   {
549     _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
550     return (0);
551   }
552 
553   if (jobcount == 0)
554     _cupsLangPuts(stdout, _("no entries"));
555 
556   return (jobcount);
557 }
558 
559 
560 /*
561  * 'show_printer()' - Show printer status.
562  */
563 
564 static void
show_printer(const char * command,http_t * http,const char * dest)565 show_printer(const char *command,	/* I - Command name */
566              http_t     *http,		/* I - HTTP connection to server */
567              const char *dest)		/* I - Destination */
568 {
569   ipp_t		*request,		/* IPP Request */
570 		*response;		/* IPP Response */
571   ipp_attribute_t *attr;		/* Current attribute */
572   ipp_pstate_t	state;			/* Printer state */
573   char		uri[HTTP_MAX_URI];	/* Printer URI */
574 
575 
576   if (http == NULL)
577     return;
578 
579  /*
580   * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
581   * attributes:
582   *
583   *    attributes-charset
584   *    attributes-natural-language
585   *    printer-uri
586   */
587 
588   request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
589 
590   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
591                    "localhost", 0, "/printers/%s", dest);
592   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
593                "printer-uri", NULL, uri);
594 
595  /*
596   * Do the request and get back a response...
597   */
598 
599   if ((response = cupsDoRequest(http, request, "/")) != NULL)
600   {
601     if (response->request.status.status_code > IPP_OK_CONFLICT)
602     {
603       _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
604       ippDelete(response);
605       return;
606     }
607 
608     if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
609       state = (ipp_pstate_t)attr->values[0].integer;
610     else
611       state = IPP_PRINTER_STOPPED;
612 
613     switch (state)
614     {
615       case IPP_PRINTER_IDLE :
616           _cupsLangPrintf(stdout, _("%s is ready"), dest);
617 	  break;
618       case IPP_PRINTER_PROCESSING :
619           _cupsLangPrintf(stdout, _("%s is ready and printing"),
620 	                  dest);
621 	  break;
622       case IPP_PRINTER_STOPPED :
623           _cupsLangPrintf(stdout, _("%s is not ready"), dest);
624 	  break;
625     }
626 
627     ippDelete(response);
628   }
629   else
630     _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
631 }
632 
633 
634 /*
635  * 'usage()' - Show program usage.
636  */
637 
638 static void
usage(void)639 usage(void)
640 {
641   _cupsLangPuts(stderr, _("Usage: lpq [options] [+interval]"));
642   _cupsLangPuts(stdout, _("Options:"));
643   _cupsLangPuts(stdout, _("-a                      Show jobs on all destinations"));
644   _cupsLangPuts(stdout, _("-E                      Encrypt the connection to the server"));
645   _cupsLangPuts(stdout, _("-h server[:port]        Connect to the named server and port"));
646   _cupsLangPuts(stdout, _("-l                      Show verbose (long) output"));
647   _cupsLangPuts(stdout, _("-P destination          Show status for the specified destination"));
648   _cupsLangPuts(stdout, _("-U username             Specify the username to use for authentication"));
649 
650   exit(1);
651 }
652