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