1 /*
2 * Printer status CGI for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2016 by Apple Inc.
6 * Copyright © 1997-2006 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include "cgi-private.h"
17 #include <errno.h>
18
19
20 /*
21 * Local functions...
22 */
23
24 static void do_printer_op(http_t *http, const char *printer, ipp_op_t op,
25 const char *title);
26 static void show_all_printers(http_t *http, const char *username);
27 static void show_printer(http_t *http, const char *printer);
28
29
30 /*
31 * 'main()' - Main entry for CGI.
32 */
33
34 int /* O - Exit status */
main(void)35 main(void)
36 {
37 const char *printer; /* Printer name */
38 const char *user; /* Username */
39 http_t *http; /* Connection to the server */
40 ipp_t *request, /* IPP request */
41 *response; /* IPP response */
42 ipp_attribute_t *attr; /* IPP attribute */
43 const char *op; /* Operation to perform, if any */
44 static const char *def_attrs[] = /* Attributes for default printer */
45 {
46 "printer-name",
47 "printer-uri-supported"
48 };
49
50
51 /*
52 * Get any form variables...
53 */
54
55 cgiInitialize();
56
57 op = cgiGetVariable("OP");
58
59 /*
60 * Set the web interface section...
61 */
62
63 cgiSetVariable("SECTION", "printers");
64 cgiSetVariable("REFRESH_PAGE", "");
65
66 /*
67 * See if we are displaying a printer or all printers...
68 */
69
70 if ((printer = getenv("PATH_INFO")) != NULL)
71 {
72 printer ++;
73
74 if (!*printer)
75 printer = NULL;
76
77 if (printer)
78 cgiSetVariable("PRINTER_NAME", printer);
79 }
80
81 /*
82 * See who is logged in...
83 */
84
85 user = getenv("REMOTE_USER");
86
87 /*
88 * Connect to the HTTP server...
89 */
90
91 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
92
93 /*
94 * Get the default printer...
95 */
96
97 if (!op || !cgiIsPOST())
98 {
99 /*
100 * Get the default destination...
101 */
102
103 request = ippNewRequest(CUPS_GET_DEFAULT);
104
105 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
106 "requested-attributes",
107 sizeof(def_attrs) / sizeof(def_attrs[0]), NULL, def_attrs);
108
109 if ((response = cupsDoRequest(http, request, "/")) != NULL)
110 {
111 if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL)
112 cgiSetVariable("DEFAULT_NAME", attr->values[0].string.text);
113
114 if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
115 {
116 char url[HTTP_MAX_URI]; /* New URL */
117
118
119 cgiSetVariable("DEFAULT_URI",
120 cgiRewriteURL(attr->values[0].string.text,
121 url, sizeof(url), NULL));
122 }
123
124 ippDelete(response);
125 }
126
127 /*
128 * See if we need to show a list of printers or the status of a
129 * single printer...
130 */
131
132 if (!printer)
133 show_all_printers(http, user);
134 else
135 show_printer(http, printer);
136 }
137 else if (printer)
138 {
139 if (!*op)
140 {
141 const char *server_port = getenv("SERVER_PORT");
142 /* Port number string */
143 int port = atoi(server_port ? server_port : "0");
144 /* Port number */
145 char uri[1024]; /* URL */
146
147 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri),
148 getenv("HTTPS") ? "https" : "http", NULL,
149 getenv("SERVER_NAME"), port, "/printers/%s", printer);
150
151 printf("Location: %s\n\n", uri);
152 }
153 else if (!strcmp(op, "start-printer"))
154 do_printer_op(http, printer, IPP_RESUME_PRINTER,
155 cgiText(_("Resume Printer")));
156 else if (!strcmp(op, "stop-printer"))
157 do_printer_op(http, printer, IPP_PAUSE_PRINTER,
158 cgiText(_("Pause Printer")));
159 else if (!strcmp(op, "accept-jobs"))
160 do_printer_op(http, printer, CUPS_ACCEPT_JOBS, cgiText(_("Accept Jobs")));
161 else if (!strcmp(op, "reject-jobs"))
162 do_printer_op(http, printer, CUPS_REJECT_JOBS, cgiText(_("Reject Jobs")));
163 else if (!strcmp(op, "cancel-jobs"))
164 do_printer_op(http, printer, IPP_OP_CANCEL_JOBS, cgiText(_("Cancel Jobs")));
165 else if (!_cups_strcasecmp(op, "print-self-test-page"))
166 cgiPrintCommand(http, printer, "PrintSelfTestPage",
167 cgiText(_("Print Self-Test Page")));
168 else if (!_cups_strcasecmp(op, "clean-print-heads"))
169 cgiPrintCommand(http, printer, "Clean all",
170 cgiText(_("Clean Print Heads")));
171 else if (!_cups_strcasecmp(op, "print-test-page"))
172 cgiPrintTestPage(http, printer);
173 else if (!_cups_strcasecmp(op, "move-jobs"))
174 cgiMoveJobs(http, printer, 0);
175 else
176 {
177 /*
178 * Unknown/bad operation...
179 */
180
181 cgiStartHTML(printer);
182 cgiCopyTemplateLang("error-op.tmpl");
183 cgiEndHTML();
184 }
185 }
186 else
187 {
188 /*
189 * Unknown/bad operation...
190 */
191
192 cgiStartHTML(cgiText(_("Printers")));
193 cgiCopyTemplateLang("error-op.tmpl");
194 cgiEndHTML();
195 }
196
197 /*
198 * Close the HTTP server connection...
199 */
200
201 httpClose(http);
202
203 /*
204 * Return with no errors...
205 */
206
207 return (0);
208 }
209
210
211 /*
212 * 'do_printer_op()' - Do a printer operation.
213 */
214
215 static void
do_printer_op(http_t * http,const char * printer,ipp_op_t op,const char * title)216 do_printer_op(http_t *http, /* I - HTTP connection */
217 const char *printer, /* I - Printer name */
218 ipp_op_t op, /* I - Operation to perform */
219 const char *title) /* I - Title of page */
220 {
221 ipp_t *request; /* IPP request */
222 char uri[HTTP_MAX_URI], /* Printer URI */
223 resource[HTTP_MAX_URI]; /* Path for request */
224
225
226 /*
227 * Build a printer request, which requires the following
228 * attributes:
229 *
230 * attributes-charset
231 * attributes-natural-language
232 * printer-uri
233 */
234
235 request = ippNewRequest(op);
236
237 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
238 "localhost", 0, "/printers/%s", printer);
239 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
240 NULL, uri);
241
242 /*
243 * Do the request and get back a response...
244 */
245
246 snprintf(resource, sizeof(resource), "/printers/%s", printer);
247 ippDelete(cupsDoRequest(http, request, resource));
248
249 if (cupsLastError() == IPP_NOT_AUTHORIZED)
250 {
251 puts("Status: 401\n");
252 exit(0);
253 }
254 else if (cupsLastError() > IPP_OK_CONFLICT)
255 {
256 cgiStartHTML(title);
257 cgiShowIPPError(_("Unable to do maintenance command"));
258 }
259 else
260 {
261 /*
262 * Redirect successful updates back to the printer page...
263 */
264
265 char url[1024], /* Printer/class URL */
266 refresh[1024]; /* Refresh URL */
267
268
269 cgiRewriteURL(uri, url, sizeof(url), NULL);
270 cgiFormEncode(uri, url, sizeof(uri));
271 snprintf(refresh, sizeof(refresh), "5;URL=%s", uri);
272 cgiSetVariable("refresh_page", refresh);
273
274 cgiStartHTML(title);
275
276 if (op == IPP_PAUSE_PRINTER)
277 cgiCopyTemplateLang("printer-stop.tmpl");
278 else if (op == IPP_RESUME_PRINTER)
279 cgiCopyTemplateLang("printer-start.tmpl");
280 else if (op == CUPS_ACCEPT_JOBS)
281 cgiCopyTemplateLang("printer-accept.tmpl");
282 else if (op == CUPS_REJECT_JOBS)
283 cgiCopyTemplateLang("printer-reject.tmpl");
284 else if (op == IPP_OP_CANCEL_JOBS)
285 cgiCopyTemplateLang("printer-cancel-jobs.tmpl");
286 }
287
288 cgiEndHTML();
289 }
290
291
292 /*
293 * 'show_all_printers()' - Show all printers...
294 */
295
296 static void
show_all_printers(http_t * http,const char * user)297 show_all_printers(http_t *http, /* I - Connection to server */
298 const char *user) /* I - Username */
299 {
300 int i; /* Looping var */
301 ipp_t *request, /* IPP request */
302 *response; /* IPP response */
303 cups_array_t *printers; /* Array of printer objects */
304 ipp_attribute_t *printer; /* Printer object */
305 int first, /* First printer to show */
306 count; /* Number of printers */
307 const char *var; /* Form variable */
308 void *search; /* Search data */
309 char val[1024]; /* Form variable */
310
311
312 fprintf(stderr, "DEBUG: show_all_printers(http=%p, user=\"%s\")\n",
313 http, user ? user : "(null)");
314
315 /*
316 * Show the standard header...
317 */
318
319 cgiStartHTML(cgiText(_("Printers")));
320
321 /*
322 * Build a CUPS_GET_PRINTERS request, which requires the following
323 * attributes:
324 *
325 * attributes-charset
326 * attributes-natural-language
327 * printer-type
328 * printer-type-mask
329 * requesting-user-name
330 */
331
332 request = ippNewRequest(CUPS_GET_PRINTERS);
333
334 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
335 "printer-type", 0);
336 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
337 "printer-type-mask", CUPS_PRINTER_CLASS);
338
339 if (user)
340 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
341 "requesting-user-name", NULL, user);
342
343 cgiGetAttributes(request, "printers.tmpl");
344
345 /*
346 * Do the request and get back a response...
347 */
348
349 if ((response = cupsDoRequest(http, request, "/")) != NULL)
350 {
351 /*
352 * Get a list of matching job objects.
353 */
354
355 if ((var = cgiGetTextfield("QUERY")) != NULL &&
356 !cgiGetVariable("CLEAR"))
357 search = cgiCompileSearch(var);
358 else
359 search = NULL;
360
361 printers = cgiGetIPPObjects(response, search);
362 count = cupsArrayCount(printers);
363
364 if (search)
365 cgiFreeSearch(search);
366
367 /*
368 * Figure out which printers to display...
369 */
370
371 if ((var = cgiGetVariable("FIRST")) != NULL)
372 first = atoi(var);
373 else
374 first = 0;
375
376 if (first >= count)
377 first = count - CUPS_PAGE_MAX;
378
379 first = (first / CUPS_PAGE_MAX) * CUPS_PAGE_MAX;
380
381 if (first < 0)
382 first = 0;
383
384 snprintf(val, sizeof(val), "%d", count);
385 cgiSetVariable("TOTAL", val);
386
387 for (i = 0, printer = (ipp_attribute_t *)cupsArrayIndex(printers, first);
388 i < CUPS_PAGE_MAX && printer;
389 i ++, printer = (ipp_attribute_t *)cupsArrayNext(printers))
390 cgiSetIPPObjectVars(printer, NULL, i);
391
392 /*
393 * Save navigation URLs...
394 */
395
396 cgiSetVariable("THISURL", "/printers/");
397
398 if (first > 0)
399 {
400 snprintf(val, sizeof(val), "%d", first - CUPS_PAGE_MAX);
401 cgiSetVariable("PREV", val);
402 }
403
404 if ((first + CUPS_PAGE_MAX) < count)
405 {
406 snprintf(val, sizeof(val), "%d", first + CUPS_PAGE_MAX);
407 cgiSetVariable("NEXT", val);
408 }
409
410 if (count > CUPS_PAGE_MAX)
411 {
412 snprintf(val, sizeof(val), "%d", CUPS_PAGE_MAX * (count / CUPS_PAGE_MAX));
413 cgiSetVariable("LAST", val);
414 }
415
416 /*
417 * Then show everything...
418 */
419
420 cgiCopyTemplateLang("search.tmpl");
421
422 cgiCopyTemplateLang("printers-header.tmpl");
423
424 if (count > CUPS_PAGE_MAX)
425 cgiCopyTemplateLang("pager.tmpl");
426
427 cgiCopyTemplateLang("printers.tmpl");
428
429 if (count > CUPS_PAGE_MAX)
430 cgiCopyTemplateLang("pager.tmpl");
431
432 /*
433 * Delete the response...
434 */
435
436 cupsArrayDelete(printers);
437 ippDelete(response);
438 }
439 else
440 {
441 /*
442 * Show the error...
443 */
444
445 cgiShowIPPError(_("Unable to get printer list"));
446 }
447
448 cgiEndHTML();
449 }
450
451
452 /*
453 * 'show_printer()' - Show a single printer.
454 */
455
456 static void
show_printer(http_t * http,const char * printer)457 show_printer(http_t *http, /* I - Connection to server */
458 const char *printer) /* I - Name of printer */
459 {
460 ipp_t *request, /* IPP request */
461 *response; /* IPP response */
462 ipp_attribute_t *attr; /* IPP attribute */
463 char uri[HTTP_MAX_URI]; /* Printer URI */
464 char refresh[1024]; /* Refresh URL */
465
466
467 fprintf(stderr, "DEBUG: show_printer(http=%p, printer=\"%s\")\n",
468 http, printer ? printer : "(null)");
469
470 /*
471 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
472 * attributes:
473 *
474 * attributes-charset
475 * attributes-natural-language
476 * printer-uri
477 */
478
479 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
480
481 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
482 "localhost", 0, "/printers/%s", printer);
483 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
484 uri);
485
486 cgiGetAttributes(request, "printer.tmpl");
487
488 /*
489 * Do the request and get back a response...
490 */
491
492 if ((response = cupsDoRequest(http, request, "/")) != NULL)
493 {
494 /*
495 * Got the result; set the CGI variables and check the status of a
496 * single-queue request...
497 */
498
499 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
500
501 if (printer && (attr = ippFindAttribute(response, "printer-state",
502 IPP_TAG_ENUM)) != NULL &&
503 attr->values[0].integer == IPP_PRINTER_PROCESSING)
504 {
505 /*
506 * Printer is processing - automatically refresh the page until we
507 * are done printing...
508 */
509
510 cgiFormEncode(uri, printer, sizeof(uri));
511 snprintf(refresh, sizeof(refresh), "10;URL=/printers/%s", uri);
512 cgiSetVariable("refresh_page", refresh);
513 }
514
515 /*
516 * Delete the response...
517 */
518
519 ippDelete(response);
520
521 /*
522 * Show the standard header...
523 */
524
525 cgiStartHTML(printer);
526
527 /*
528 * Show the printer status...
529 */
530
531 cgiCopyTemplateLang("printer.tmpl");
532
533 /*
534 * Show jobs for the specified printer...
535 */
536
537 cgiCopyTemplateLang("printer-jobs-header.tmpl");
538 cgiShowJobs(http, printer);
539 }
540 else
541 {
542 /*
543 * Show the IPP error...
544 */
545
546 cgiStartHTML(printer);
547 cgiShowIPPError(_("Unable to get printer status"));
548 }
549
550 cgiEndHTML();
551 }
552