]> git.ipfire.org Git - thirdparty/cups.git/blame - berkeley/lpq.c
SIGSEGV in CUPS web ui when adding a printer
[thirdparty/cups.git] / berkeley / lpq.c
CommitLineData
ef416fc2 1/*
5a1d7a17 2 * "lpq" command for CUPS.
ef416fc2 3 *
860f3d3b
MS
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1997-2006 by Easy Software Products.
ef416fc2 6 *
860f3d3b
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 */
10
11/*
12 * Include necessary headers...
13 */
14
71e16022 15#include <cups/cups-private.h>
ef416fc2 16
17
18/*
19 * Local functions...
20 */
21
d6ae789d 22static http_t *connect_server(const char *, http_t *);
fa73b229 23static int show_jobs(const char *, http_t *, const char *,
24 const char *, const int, const int);
25static void show_printer(const char *, http_t *, const char *);
a32af27c 26static void usage(void) _CUPS_NORETURN;
ef416fc2 27
28
29/*
30 * 'main()' - Parse options and commands.
31 */
32
33int
fa73b229 34main(int argc, /* I - Number of command-line arguments */
35 char *argv[]) /* I - Command-line arguments */
ef416fc2 36{
fa73b229 37 int i; /* Looping var */
38 http_t *http; /* Connection to server */
bdbfacc7
MS
39 const char *opt, /* Option pointer */
40 *dest, /* Desired printer */
fa73b229 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 */
c606bcae 48 cups_dest_t *named_dest; /* Named destination */
ef416fc2 49
50
07725fee 51 _cupsSetLocale(argv);
d09495fa 52
ef416fc2 53 /*
54 * Check for command-line options...
55 */
56
d6ae789d 57 http = NULL;
ef416fc2 58 dest = NULL;
59 user = NULL;
60 id = 0;
61 interval = 0;
62 longstatus = 0;
63 all = 0;
ef416fc2 64
65 for (i = 1; i < argc; i ++)
bdbfacc7 66 {
ef416fc2 67 if (argv[i][0] == '+')
bdbfacc7 68 {
ef416fc2 69 interval = atoi(argv[i] + 1);
bdbfacc7 70 }
860f3d3b
MS
71 else if (!strcmp(argv[i], "--help"))
72 usage();
ef416fc2 73 else if (argv[i][0] == '-')
74 {
bdbfacc7 75 for (opt = argv[i] + 1; *opt; opt ++)
ef416fc2 76 {
bdbfacc7
MS
77 switch (*opt)
78 {
79 case 'E' : /* Encrypt */
ef416fc2 80#ifdef HAVE_SSL
bdbfacc7 81 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
ef416fc2 82
bdbfacc7
MS
83 if (http)
84 httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
ef416fc2 85#else
bdbfacc7 86 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
ef416fc2 87#endif /* HAVE_SSL */
bdbfacc7
MS
88 break;
89
90 case 'U' : /* Username */
91 if (opt[1] != '\0')
fa73b229 92 {
bdbfacc7
MS
93 cupsSetUser(opt + 1);
94 opt += strlen(opt) - 1;
fa73b229 95 }
bdbfacc7
MS
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;
fa73b229 108
bdbfacc7
MS
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 ++;
eac3a0a0 118
bdbfacc7
MS
119 if (i >= argc)
120 {
121 httpClose(http);
ef416fc2 122
bdbfacc7
MS
123 usage();
124 }
eac3a0a0 125
bdbfacc7 126 dest = argv[i];
ef416fc2 127 }
128
bdbfacc7
MS
129 if ((instance = strchr(dest, '/')) != NULL)
130 *instance++ = '\0';
fa73b229 131
bdbfacc7 132 http = connect_server(argv[0], http);
fa73b229 133
bdbfacc7 134 if ((named_dest = cupsGetNamedDest(http, dest, instance)) == NULL)
fa73b229 135 {
bdbfacc7
MS
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
fa73b229 144 return (1);
bdbfacc7
MS
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 }
fa73b229 166 else
bdbfacc7
MS
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;
fa73b229 179
bdbfacc7
MS
180 case 'l' : /* Long status */
181 longstatus = 1;
182 break;
ef416fc2 183
bdbfacc7
MS
184 default :
185 httpClose(http);
ef416fc2 186
bdbfacc7
MS
187 usage();
188 }
ef416fc2 189 }
190 }
191 else if (isdigit(argv[i][0] & 255))
bdbfacc7 192 {
ef416fc2 193 id = atoi(argv[i]);
bdbfacc7 194 }
ef416fc2 195 else
bdbfacc7 196 {
ef416fc2 197 user = argv[i];
bdbfacc7
MS
198 }
199 }
ef416fc2 200
d6ae789d 201 http = connect_server(argv[0], http);
202
ef416fc2 203 if (dest == NULL && !all)
204 {
c606bcae 205 if ((named_dest = cupsGetNamedDest(http, NULL, NULL)) == NULL)
ef416fc2 206 {
c606bcae
MS
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
ef416fc2 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
c606bcae 231 if (dest && val)
fa73b229 232 _cupsLangPrintf(stderr,
84315f46 233 _("%s: Error - %s environment variable names "
0837b7e8
MS
234 "non-existent destination \"%s\"."), argv[0], val,
235 dest);
ef416fc2 236 else
fa73b229 237 _cupsLangPrintf(stderr,
84315f46 238 _("%s: Error - no default destination available."),
fa73b229 239 argv[0]);
ef416fc2 240 httpClose(http);
ef416fc2 241 return (1);
242 }
c606bcae
MS
243
244 dest = named_dest->name;
ef416fc2 245 }
246
247 /*
248 * Show the status in a loop...
249 */
250
251 for (;;)
252 {
253 if (dest)
fa73b229 254 show_printer(argv[0], http, dest);
ef416fc2 255
fa73b229 256 i = show_jobs(argv[0], http, dest, user, id, longstatus);
ef416fc2 257
258 if (i && interval)
259 {
260 fflush(stdout);
7e86f2f6 261 sleep((unsigned)interval);
ef416fc2 262 }
263 else
264 break;
265 }
266
267 /*
268 * Close the connection to the server and return...
269 */
270
ef416fc2 271 httpClose(http);
272
273 return (0);
274}
275
276
d6ae789d 277/*
278 * 'connect_server()' - Connect to the server as necessary...
279 */
280
281static http_t * /* O - New HTTP connection */
282connect_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 {
0837b7e8 292 _cupsLangPrintf(stderr, _("%s: Unable to connect to server."), command);
d6ae789d 293 exit(1);
294 }
295 }
296
297 return (http);
298}
299
300
ef416fc2 301/*
302 * 'show_jobs()' - Show jobs.
303 */
304
fa73b229 305static int /* O - Number of jobs in queue */
306show_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 */
ef416fc2 312{
fa73b229 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 */
fa73b229 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 */
5a662dc0
MS
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 */
ef416fc2 340 {
341 "th",
342 "st",
343 "nd",
344 "rd",
345 "th",
346 "th",
347 "th",
348 "th",
349 "th",
350 "th"
351 };
352
353
ef416fc2 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
5a662dc0 364 * requested-attributes
37e7e6e0 365 * requesting-user-name
ef416fc2 366 */
367
fa73b229 368 request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
ef416fc2 369
1340db2d 370 if (id)
ef416fc2 371 {
1340db2d 372 snprintf(resource, sizeof(resource), "ipp://localhost/jobs/%d", id);
ef416fc2 373 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
374 NULL, resource);
375 }
1340db2d 376 else if (dest)
ef416fc2 377 {
a4d04587 378 httpAssembleURIf(HTTP_URI_CODING_ALL, resource, sizeof(resource), "ipp",
379 NULL, "localhost", 0, "/printers/%s", dest);
ef416fc2 380
381 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
382 NULL, resource);
383 }
1340db2d
MS
384 else
385 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
386 NULL, "ipp://localhost/");
ef416fc2 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 }
37e7e6e0
MS
394 else
395 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
396 "requesting-user-name", NULL, cupsUser());
ef416fc2 397
5a662dc0
MS
398 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
399 "requested-attributes",
400 (int)(sizeof(jobattrs) / sizeof(jobattrs[0])), NULL, jobattrs);
401
ef416fc2 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 {
0837b7e8 412 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
ef416fc2 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;
ef416fc2 441 jobstate = IPP_JOB_PENDING;
37e7e6e0
MS
442 jobname = "unknown";
443 jobuser = "unknown";
ef416fc2 444 jobdest = NULL;
445 jobcopies = 1;
446
447 while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
448 {
fa73b229 449 if (!strcmp(attr->name, "job-id") &&
ef416fc2 450 attr->value_tag == IPP_TAG_INTEGER)
451 jobid = attr->values[0].integer;
452
fa73b229 453 if (!strcmp(attr->name, "job-k-octets") &&
ef416fc2 454 attr->value_tag == IPP_TAG_INTEGER)
455 jobsize = attr->values[0].integer;
456
fa73b229 457 if (!strcmp(attr->name, "job-state") &&
ef416fc2 458 attr->value_tag == IPP_TAG_ENUM)
459 jobstate = (ipp_jstate_t)attr->values[0].integer;
460
fa73b229 461 if (!strcmp(attr->name, "job-printer-uri") &&
ef416fc2 462 attr->value_tag == IPP_TAG_URI)
463 if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
464 jobdest ++;
465
fa73b229 466 if (!strcmp(attr->name, "job-originating-user-name") &&
ef416fc2 467 attr->value_tag == IPP_TAG_NAME)
468 jobuser = attr->values[0].string.text;
469
fa73b229 470 if (!strcmp(attr->name, "job-name") &&
ef416fc2 471 attr->value_tag == IPP_TAG_NAME)
472 jobname = attr->values[0].string.text;
473
fa73b229 474 if (!strcmp(attr->name, "copies") &&
ef416fc2 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)
fa73b229 494 _cupsLangPuts(stdout,
ef416fc2 495 _("Rank Owner Job File(s)"
0837b7e8 496 " Total Size"));
ef416fc2 497
498 jobcount ++;
499
500 /*
501 * Display the job...
502 */
503
504 if (jobstate == IPP_JOB_PROCESSING)
5a9febac 505 strlcpy(rankstr, "active", sizeof(rankstr));
ef416fc2 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 {
fa73b229 523 _cupsLangPuts(stdout, "\n");
ef416fc2 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
0837b7e8 531 _cupsLangPrintf(stdout, _("%s: %-33.33s [job %d localhost]"),
ef416fc2 532 jobuser, rankstr, jobid);
0837b7e8 533 _cupsLangPrintf(stdout, _(" %-39.39s %.0f bytes"),
ef416fc2 534 namestr, 1024.0 * jobsize);
535 }
536 else
fa73b229 537 _cupsLangPrintf(stdout,
0837b7e8 538 _("%-7s %-7.7s %-7d %-31.31s %.0f bytes"),
ef416fc2 539 rankstr, jobuser, jobid, jobname, 1024.0 * jobsize);
ef416fc2 540
541 if (attr == NULL)
542 break;
543 }
544
545 ippDelete(response);
546 }
547 else
548 {
0837b7e8 549 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
ef416fc2 550 return (0);
551 }
552
553 if (jobcount == 0)
0837b7e8 554 _cupsLangPuts(stdout, _("no entries"));
ef416fc2 555
556 return (jobcount);
557}
558
559
560/*
561 * 'show_printer()' - Show printer status.
562 */
563
564static void
fa73b229 565show_printer(const char *command, /* I - Command name */
566 http_t *http, /* I - HTTP connection to server */
567 const char *dest) /* I - Destination */
ef416fc2 568{
fa73b229 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 */
ef416fc2 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
fa73b229 588 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
ef416fc2 589
a4d04587 590 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
591 "localhost", 0, "/printers/%s", dest);
ef416fc2 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 {
0837b7e8 603 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
ef416fc2 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 :
0837b7e8 616 _cupsLangPrintf(stdout, _("%s is ready"), dest);
ef416fc2 617 break;
618 case IPP_PRINTER_PROCESSING :
0837b7e8 619 _cupsLangPrintf(stdout, _("%s is ready and printing"),
ef416fc2 620 dest);
621 break;
622 case IPP_PRINTER_STOPPED :
0837b7e8 623 _cupsLangPrintf(stdout, _("%s is not ready"), dest);
ef416fc2 624 break;
625 }
626
627 ippDelete(response);
628 }
629 else
0837b7e8 630 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
ef416fc2 631}
632
633
634/*
635 * 'usage()' - Show program usage.
636 */
637
638static void
639usage(void)
640{
860f3d3b
MS
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
ef416fc2 650 exit(1);
651}