]> git.ipfire.org Git - thirdparty/cups.git/blob - cgi-bin/admin.c
Fix build errors on Fedora.
[thirdparty/cups.git] / cgi-bin / admin.c
1 /*
2 * "$Id$"
3 *
4 * Administration CGI for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 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 "cgi-private.h"
21 #include <cups/adminutil.h>
22 #include <cups/ppd.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/wait.h>
27 #include <limits.h>
28
29
30 /*
31 * Local globals...
32 */
33
34 static int current_device = 0; /* Current device shown */
35
36
37 /*
38 * Local functions...
39 */
40
41 static void choose_device_cb(const char *device_class,
42 const char *device_id, const char *device_info,
43 const char *device_make_and_model,
44 const char *device_uri,
45 const char *device_location,
46 const char *title);
47 static void do_add_rss_subscription(http_t *http);
48 static void do_am_class(http_t *http, int modify);
49 static void do_am_printer(http_t *http, int modify);
50 static void do_cancel_subscription(http_t *http);
51 static void do_config_server(http_t *http);
52 static void do_delete_class(http_t *http);
53 static void do_delete_printer(http_t *http);
54 static void do_export(http_t *http);
55 static void do_list_printers(http_t *http);
56 static void do_menu(http_t *http);
57 static void do_set_allowed_users(http_t *http);
58 static void do_set_default(http_t *http);
59 static void do_set_options(http_t *http, int is_class);
60 static void do_set_sharing(http_t *http);
61 static char *get_option_value(ppd_file_t *ppd, const char *name,
62 char *buffer, size_t bufsize);
63 static double get_points(double number, const char *uval);
64
65
66 /*
67 * 'main()' - Main entry for CGI.
68 */
69
70 int /* O - Exit status */
71 main(void)
72 {
73 http_t *http; /* Connection to the server */
74 const char *op; /* Operation name */
75
76
77 /*
78 * Connect to the HTTP server...
79 */
80
81 fputs("DEBUG: admin.cgi started...\n", stderr);
82
83 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
84
85 if (!http)
86 {
87 perror("ERROR: Unable to connect to cupsd");
88 fprintf(stderr, "DEBUG: cupsServer()=\"%s\"\n",
89 cupsServer() ? cupsServer() : "(null)");
90 fprintf(stderr, "DEBUG: ippPort()=%d\n", ippPort());
91 fprintf(stderr, "DEBUG: cupsEncryption()=%d\n", cupsEncryption());
92 exit(1);
93 }
94
95 fprintf(stderr, "DEBUG: http=%p\n", http);
96
97 /*
98 * Set the web interface section...
99 */
100
101 cgiSetVariable("SECTION", "admin");
102 cgiSetVariable("REFRESH_PAGE", "");
103
104 /*
105 * See if we have form data...
106 */
107
108 if (!cgiInitialize() || !cgiGetVariable("OP"))
109 {
110 /*
111 * Nope, send the administration menu...
112 */
113
114 fputs("DEBUG: No form data, showing main menu...\n", stderr);
115
116 do_menu(http);
117 }
118 else if ((op = cgiGetVariable("OP")) != NULL && cgiIsPOST())
119 {
120 /*
121 * Do the operation...
122 */
123
124 fprintf(stderr, "DEBUG: op=\"%s\"...\n", op);
125
126 if (!*op)
127 {
128 const char *printer = getenv("PRINTER_NAME"),
129 /* Printer or class name */
130 *server_port = getenv("SERVER_PORT");
131 /* Port number string */
132 int port = atoi(server_port ? server_port : "0");
133 /* Port number */
134 char uri[1024]; /* URL */
135
136 if (printer)
137 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri),
138 getenv("HTTPS") ? "https" : "http", NULL,
139 getenv("SERVER_NAME"), port, "/%s/%s",
140 cgiGetVariable("IS_CLASS") ? "classes" : "printers",
141 printer);
142 else
143 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri),
144 getenv("HTTPS") ? "https" : "http", NULL,
145 getenv("SERVER_NAME"), port, "/admin");
146
147 printf("Location: %s\n\n", uri);
148 }
149 else if (!strcmp(op, "set-allowed-users"))
150 do_set_allowed_users(http);
151 else if (!strcmp(op, "set-as-default"))
152 do_set_default(http);
153 else if (!strcmp(op, "set-sharing"))
154 do_set_sharing(http);
155 else if (!strcmp(op, "find-new-printers") ||
156 !strcmp(op, "list-available-printers"))
157 do_list_printers(http);
158 else if (!strcmp(op, "add-class"))
159 do_am_class(http, 0);
160 else if (!strcmp(op, "add-printer"))
161 do_am_printer(http, 0);
162 else if (!strcmp(op, "modify-class"))
163 do_am_class(http, 1);
164 else if (!strcmp(op, "modify-printer"))
165 do_am_printer(http, 1);
166 else if (!strcmp(op, "delete-class"))
167 do_delete_class(http);
168 else if (!strcmp(op, "delete-printer"))
169 do_delete_printer(http);
170 else if (!strcmp(op, "set-class-options"))
171 do_set_options(http, 1);
172 else if (!strcmp(op, "set-printer-options"))
173 do_set_options(http, 0);
174 else if (!strcmp(op, "config-server"))
175 do_config_server(http);
176 else if (!strcmp(op, "export-samba"))
177 do_export(http);
178 else if (!strcmp(op, "add-rss-subscription"))
179 do_add_rss_subscription(http);
180 else if (!strcmp(op, "cancel-subscription"))
181 do_cancel_subscription(http);
182 else
183 {
184 /*
185 * Bad operation code - display an error...
186 */
187
188 cgiStartHTML(cgiText(_("Administration")));
189 cgiCopyTemplateLang("error-op.tmpl");
190 cgiEndHTML();
191 }
192 }
193 else if (op && !strcmp(op, "redirect"))
194 {
195 const char *url; /* Redirection URL... */
196 char prefix[1024]; /* URL prefix */
197
198
199 if (getenv("HTTPS"))
200 snprintf(prefix, sizeof(prefix), "https://%s:%s",
201 getenv("SERVER_NAME"), getenv("SERVER_PORT"));
202 else
203 snprintf(prefix, sizeof(prefix), "http://%s:%s",
204 getenv("SERVER_NAME"), getenv("SERVER_PORT"));
205
206 fprintf(stderr, "DEBUG: redirecting with prefix %s!\n", prefix);
207
208 if ((url = cgiGetVariable("URL")) != NULL)
209 {
210 char encoded[1024], /* Encoded URL string */
211 *ptr; /* Pointer into encoded string */
212
213
214 ptr = encoded;
215 if (*url != '/')
216 *ptr++ = '/';
217
218 for (; *url && ptr < (encoded + sizeof(encoded) - 4); url ++)
219 {
220 if (strchr("%@&+ <>#=", *url) || *url < ' ' || *url & 128)
221 {
222 /*
223 * Percent-encode this character; safe because we have at least 4
224 * bytes left in the array...
225 */
226
227 sprintf(ptr, "%%%02X", *url & 255);
228 ptr += 3;
229 }
230 else
231 *ptr++ = *url;
232 }
233
234 *ptr = '\0';
235
236 if (*url)
237 {
238 /*
239 * URL was too long, just redirect to the admin page...
240 */
241
242 printf("Location: %s/admin\n\n", prefix);
243 }
244 else
245 {
246 /*
247 * URL is OK, redirect there...
248 */
249
250 printf("Location: %s%s\n\n", prefix, encoded);
251 }
252 }
253 else
254 printf("Location: %s/admin\n\n", prefix);
255 }
256 else
257 {
258 /*
259 * Form data but no operation code - display an error...
260 */
261
262 cgiStartHTML(cgiText(_("Administration")));
263 cgiCopyTemplateLang("error-op.tmpl");
264 cgiEndHTML();
265 }
266
267 /*
268 * Close the HTTP server connection...
269 */
270
271 httpClose(http);
272
273 /*
274 * Return with no errors...
275 */
276
277 return (0);
278 }
279
280
281 /*
282 * 'choose_device_cb()' - Add a device to the device selection page.
283 */
284
285 static void
286 choose_device_cb(
287 const char *device_class, /* I - Class */
288 const char *device_id, /* I - 1284 device ID */
289 const char *device_info, /* I - Description */
290 const char *device_make_and_model, /* I - Make and model */
291 const char *device_uri, /* I - Device URI */
292 const char *device_location, /* I - Location */
293 const char *title) /* I - Page title */
294 {
295 /*
296 * For modern browsers, start a multi-part page so we can show that something
297 * is happening. Non-modern browsers just get everything at the end...
298 */
299
300 if (current_device == 0 && cgiSupportsMultipart())
301 {
302 cgiStartMultipart();
303 cgiStartHTML(title);
304 cgiCopyTemplateLang("choose-device.tmpl");
305 cgiEndHTML();
306 fflush(stdout);
307 }
308
309
310 /*
311 * Add the device to the array...
312 */
313
314 cgiSetArray("device_class", current_device, device_class);
315 cgiSetArray("device_id", current_device, device_id);
316 cgiSetArray("device_info", current_device, device_info);
317 cgiSetArray("device_make_and_model", current_device, device_make_and_model);
318 cgiSetArray("device_uri", current_device, device_uri);
319 cgiSetArray("device_location", current_device, device_location);
320
321 current_device ++;
322 }
323
324
325 /*
326 * 'do_add_rss_subscription()' - Add a RSS subscription.
327 */
328
329 static void
330 do_add_rss_subscription(http_t *http) /* I - HTTP connection */
331 {
332 ipp_t *request, /* IPP request data */
333 *response; /* IPP response data */
334 char rss_uri[1024]; /* RSS notify-recipient URI */
335 int num_events; /* Number of events */
336 const char *events[12], /* Subscribed events */
337 *subscription_name, /* Subscription name */
338 *printer_uri, /* Printer URI */
339 *ptr, /* Pointer into name */
340 *user; /* Username */
341 int max_events; /* Maximum number of events */
342
343
344 /*
345 * See if we have all of the required information...
346 */
347
348 subscription_name = cgiGetVariable("SUBSCRIPTION_NAME");
349 printer_uri = cgiGetVariable("PRINTER_URI");
350 num_events = 0;
351
352 if (cgiGetVariable("EVENT_JOB_CREATED"))
353 events[num_events ++] = "job-created";
354 if (cgiGetVariable("EVENT_JOB_COMPLETED"))
355 events[num_events ++] = "job-completed";
356 if (cgiGetVariable("EVENT_JOB_STOPPED"))
357 events[num_events ++] = "job-stopped";
358 if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED"))
359 events[num_events ++] = "job-config-changed";
360 if (cgiGetVariable("EVENT_PRINTER_STOPPED"))
361 events[num_events ++] = "printer-stopped";
362 if (cgiGetVariable("EVENT_PRINTER_ADDED"))
363 events[num_events ++] = "printer-added";
364 if (cgiGetVariable("EVENT_PRINTER_MODIFIED"))
365 events[num_events ++] = "printer-modified";
366 if (cgiGetVariable("EVENT_PRINTER_DELETED"))
367 events[num_events ++] = "printer-deleted";
368 if (cgiGetVariable("EVENT_SERVER_STARTED"))
369 events[num_events ++] = "server-started";
370 if (cgiGetVariable("EVENT_SERVER_STOPPED"))
371 events[num_events ++] = "server-stopped";
372 if (cgiGetVariable("EVENT_SERVER_RESTARTED"))
373 events[num_events ++] = "server-restarted";
374 if (cgiGetVariable("EVENT_SERVER_AUDIT"))
375 events[num_events ++] = "server-audit";
376
377 if ((ptr = cgiGetVariable("MAX_EVENTS")) != NULL)
378 max_events = atoi(ptr);
379 else
380 max_events = 0;
381
382 if (!subscription_name || !printer_uri || !num_events ||
383 max_events <= 0 || max_events > 9999)
384 {
385 /*
386 * Don't have everything we need, so get the available printers
387 * and classes and (re)show the add page...
388 */
389
390 if (cgiGetVariable("EVENT_JOB_CREATED"))
391 cgiSetVariable("EVENT_JOB_CREATED", "CHECKED");
392 if (cgiGetVariable("EVENT_JOB_COMPLETED"))
393 cgiSetVariable("EVENT_JOB_COMPLETED", "CHECKED");
394 if (cgiGetVariable("EVENT_JOB_STOPPED"))
395 cgiSetVariable("EVENT_JOB_STOPPED", "CHECKED");
396 if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED"))
397 cgiSetVariable("EVENT_JOB_CONFIG_CHANGED", "CHECKED");
398 if (cgiGetVariable("EVENT_PRINTER_STOPPED"))
399 cgiSetVariable("EVENT_PRINTER_STOPPED", "CHECKED");
400 if (cgiGetVariable("EVENT_PRINTER_ADDED"))
401 cgiSetVariable("EVENT_PRINTER_ADDED", "CHECKED");
402 if (cgiGetVariable("EVENT_PRINTER_MODIFIED"))
403 cgiSetVariable("EVENT_PRINTER_MODIFIED", "CHECKED");
404 if (cgiGetVariable("EVENT_PRINTER_DELETED"))
405 cgiSetVariable("EVENT_PRINTER_DELETED", "CHECKED");
406 if (cgiGetVariable("EVENT_SERVER_STARTED"))
407 cgiSetVariable("EVENT_SERVER_STARTED", "CHECKED");
408 if (cgiGetVariable("EVENT_SERVER_STOPPED"))
409 cgiSetVariable("EVENT_SERVER_STOPPED", "CHECKED");
410 if (cgiGetVariable("EVENT_SERVER_RESTARTED"))
411 cgiSetVariable("EVENT_SERVER_RESTARTED", "CHECKED");
412 if (cgiGetVariable("EVENT_SERVER_AUDIT"))
413 cgiSetVariable("EVENT_SERVER_AUDIT", "CHECKED");
414
415 request = ippNewRequest(CUPS_GET_PRINTERS);
416 response = cupsDoRequest(http, request, "/");
417
418 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
419
420 ippDelete(response);
421
422 cgiStartHTML(cgiText(_("Add RSS Subscription")));
423
424 cgiCopyTemplateLang("add-rss-subscription.tmpl");
425
426 cgiEndHTML();
427 return;
428 }
429
430 /*
431 * Make sure we have a username...
432 */
433
434 if ((user = getenv("REMOTE_USER")) == NULL)
435 {
436 puts("Status: 401\n");
437 exit(0);
438 }
439
440 /*
441 * Validate the subscription name...
442 */
443
444 for (ptr = subscription_name; *ptr; ptr ++)
445 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' ||
446 *ptr == '?' || *ptr == '#')
447 break;
448
449 if (*ptr)
450 {
451 cgiSetVariable("ERROR",
452 cgiText(_("The subscription name may not "
453 "contain spaces, slashes (/), question marks (?), "
454 "or the pound sign (#).")));
455 cgiStartHTML(_("Add RSS Subscription"));
456 cgiCopyTemplateLang("error.tmpl");
457 cgiEndHTML();
458 return;
459 }
460
461 /*
462 * Add the subscription...
463 */
464
465 ptr = subscription_name + strlen(subscription_name) - 4;
466 if (ptr < subscription_name || strcmp(ptr, ".rss"))
467 httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss",
468 NULL, NULL, 0, "/%s.rss?max_events=%d", subscription_name,
469 max_events);
470 else
471 httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss",
472 NULL, NULL, 0, "/%s?max_events=%d", subscription_name,
473 max_events);
474
475 request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
476
477 if (!_cups_strcasecmp(printer_uri, "#ALL#"))
478 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
479 NULL, "ipp://localhost/");
480 else
481 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
482 NULL, printer_uri);
483
484 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
485 NULL, user);
486
487 ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
488 "notify-recipient-uri", NULL, rss_uri);
489 ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events",
490 num_events, NULL, events);
491 ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
492 "notify-lease-duration", 0);
493
494 ippDelete(cupsDoRequest(http, request, "/"));
495
496 if (cupsLastError() == IPP_NOT_AUTHORIZED)
497 {
498 puts("Status: 401\n");
499 exit(0);
500 }
501 else if (cupsLastError() > IPP_OK_CONFLICT)
502 {
503 cgiStartHTML(_("Add RSS Subscription"));
504 cgiShowIPPError(_("Unable to add RSS subscription"));
505 }
506 else
507 {
508 /*
509 * Redirect successful updates back to the admin page...
510 */
511
512 cgiSetVariable("refresh_page", "5;URL=/admin");
513 cgiStartHTML(_("Add RSS Subscription"));
514 cgiCopyTemplateLang("subscription-added.tmpl");
515 }
516
517 cgiEndHTML();
518 }
519
520
521 /*
522 * 'do_am_class()' - Add or modify a class.
523 */
524
525 static void
526 do_am_class(http_t *http, /* I - HTTP connection */
527 int modify) /* I - Modify the printer? */
528 {
529 int i, j; /* Looping vars */
530 int element; /* Element number */
531 int num_printers; /* Number of printers */
532 ipp_t *request, /* IPP request */
533 *response; /* IPP response */
534 ipp_attribute_t *attr; /* member-uris attribute */
535 char uri[HTTP_MAX_URI]; /* Device or printer URI */
536 const char *name, /* Pointer to class name */
537 *op, /* Operation name */
538 *ptr; /* Pointer to CGI variable */
539 const char *title; /* Title of page */
540 static const char * const pattrs[] = /* Requested printer attributes */
541 {
542 "member-names",
543 "printer-info",
544 "printer-location"
545 };
546
547
548 title = cgiText(modify ? _("Modify Class") : _("Add Class"));
549 op = cgiGetVariable("OP");
550 name = cgiGetVariable("PRINTER_NAME");
551
552 if (cgiGetVariable("PRINTER_LOCATION") == NULL)
553 {
554 /*
555 * Build a CUPS_GET_PRINTERS request, which requires the
556 * following attributes:
557 *
558 * attributes-charset
559 * attributes-natural-language
560 */
561
562 request = ippNewRequest(CUPS_GET_PRINTERS);
563
564 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
565 CUPS_PRINTER_LOCAL);
566 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
567 CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
568
569 /*
570 * Do the request and get back a response...
571 */
572
573 cgiClearVariables();
574 if (op)
575 cgiSetVariable("OP", op);
576 if (name)
577 cgiSetVariable("PRINTER_NAME", name);
578
579 if ((response = cupsDoRequest(http, request, "/")) != NULL)
580 {
581 /*
582 * Create MEMBER_URIS and MEMBER_NAMES arrays...
583 */
584
585 for (element = 0, attr = response->attrs;
586 attr != NULL;
587 attr = attr->next)
588 if (attr->name && !strcmp(attr->name, "printer-uri-supported"))
589 {
590 if ((ptr = strrchr(attr->values[0].string.text, '/')) != NULL &&
591 (!name || _cups_strcasecmp(name, ptr + 1)))
592 {
593 /*
594 * Don't show the current class...
595 */
596
597 cgiSetArray("MEMBER_URIS", element, attr->values[0].string.text);
598 element ++;
599 }
600 }
601
602 for (element = 0, attr = response->attrs;
603 attr != NULL;
604 attr = attr->next)
605 if (attr->name && !strcmp(attr->name, "printer-name"))
606 {
607 if (!name || _cups_strcasecmp(name, attr->values[0].string.text))
608 {
609 /*
610 * Don't show the current class...
611 */
612
613 cgiSetArray("MEMBER_NAMES", element, attr->values[0].string.text);
614 element ++;
615 }
616 }
617
618 num_printers = cgiGetSize("MEMBER_URIS");
619
620 ippDelete(response);
621 }
622 else
623 num_printers = 0;
624
625 if (modify)
626 {
627 /*
628 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
629 * following attributes:
630 *
631 * attributes-charset
632 * attributes-natural-language
633 * printer-uri
634 */
635
636 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
637
638 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
639 "localhost", 0, "/classes/%s", name);
640 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
641 NULL, uri);
642
643 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
644 "requested-attributes",
645 (int)(sizeof(pattrs) / sizeof(pattrs[0])),
646 NULL, pattrs);
647
648 /*
649 * Do the request and get back a response...
650 */
651
652 if ((response = cupsDoRequest(http, request, "/")) != NULL)
653 {
654 if ((attr = ippFindAttribute(response, "member-names",
655 IPP_TAG_NAME)) != NULL)
656 {
657 /*
658 * Mark any current members in the class...
659 */
660
661 for (j = 0; j < num_printers; j ++)
662 cgiSetArray("MEMBER_SELECTED", j, "");
663
664 for (i = 0; i < attr->num_values; i ++)
665 {
666 for (j = 0; j < num_printers; j ++)
667 {
668 if (!_cups_strcasecmp(attr->values[i].string.text,
669 cgiGetArray("MEMBER_NAMES", j)))
670 {
671 cgiSetArray("MEMBER_SELECTED", j, "SELECTED");
672 break;
673 }
674 }
675 }
676 }
677
678 if ((attr = ippFindAttribute(response, "printer-info",
679 IPP_TAG_TEXT)) != NULL)
680 cgiSetVariable("PRINTER_INFO", attr->values[0].string.text);
681
682 if ((attr = ippFindAttribute(response, "printer-location",
683 IPP_TAG_TEXT)) != NULL)
684 cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text);
685
686 ippDelete(response);
687 }
688
689 /*
690 * Update the location and description of an existing printer...
691 */
692
693 cgiStartHTML(title);
694 cgiCopyTemplateLang("modify-class.tmpl");
695 }
696 else
697 {
698 /*
699 * Get the name, location, and description for a new printer...
700 */
701
702 cgiStartHTML(title);
703 cgiCopyTemplateLang("add-class.tmpl");
704 }
705
706 cgiEndHTML();
707
708 return;
709 }
710
711 if (!name)
712 {
713 cgiStartHTML(title);
714 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
715 cgiCopyTemplateLang("error.tmpl");
716 cgiEndHTML();
717 return;
718 }
719
720 for (ptr = name; *ptr; ptr ++)
721 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
722 break;
723
724 if (*ptr || ptr == name || strlen(name) > 127)
725 {
726 cgiSetVariable("ERROR",
727 cgiText(_("The class name may only contain up to "
728 "127 printable characters and may not "
729 "contain spaces, slashes (/), or the "
730 "pound sign (#).")));
731 cgiStartHTML(title);
732 cgiCopyTemplateLang("error.tmpl");
733 cgiEndHTML();
734 return;
735 }
736
737 /*
738 * Build a CUPS_ADD_CLASS request, which requires the following
739 * attributes:
740 *
741 * attributes-charset
742 * attributes-natural-language
743 * printer-uri
744 * printer-location
745 * printer-info
746 * printer-is-accepting-jobs
747 * printer-state
748 * member-uris
749 */
750
751 request = ippNewRequest(CUPS_ADD_CLASS);
752
753 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
754 "localhost", 0, "/classes/%s", name);
755 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
756 NULL, uri);
757
758 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
759 NULL, cgiGetVariable("PRINTER_LOCATION"));
760
761 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
762 NULL, cgiGetVariable("PRINTER_INFO"));
763
764 ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
765
766 ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
767 IPP_PRINTER_IDLE);
768
769 if ((num_printers = cgiGetSize("MEMBER_URIS")) > 0)
770 {
771 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris",
772 num_printers, NULL, NULL);
773 for (i = 0; i < num_printers; i ++)
774 attr->values[i].string.text = _cupsStrAlloc(cgiGetArray("MEMBER_URIS", i));
775 }
776
777 /*
778 * Do the request and get back a response...
779 */
780
781 ippDelete(cupsDoRequest(http, request, "/admin/"));
782
783 if (cupsLastError() == IPP_NOT_AUTHORIZED)
784 {
785 puts("Status: 401\n");
786 exit(0);
787 }
788 else if (cupsLastError() > IPP_OK_CONFLICT)
789 {
790 cgiStartHTML(title);
791 cgiShowIPPError(modify ? _("Unable to modify class") :
792 _("Unable to add class"));
793 }
794 else
795 {
796 /*
797 * Redirect successful updates back to the class page...
798 */
799
800 char refresh[1024]; /* Refresh URL */
801
802 cgiFormEncode(uri, name, sizeof(uri));
803 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/classes/%s",
804 uri);
805 cgiSetVariable("refresh_page", refresh);
806
807 cgiStartHTML(title);
808
809 if (modify)
810 cgiCopyTemplateLang("class-modified.tmpl");
811 else
812 cgiCopyTemplateLang("class-added.tmpl");
813 }
814
815 cgiEndHTML();
816 }
817
818
819 /*
820 * 'do_am_printer()' - Add or modify a printer.
821 */
822
823 static void
824 do_am_printer(http_t *http, /* I - HTTP connection */
825 int modify) /* I - Modify the printer? */
826 {
827 int i; /* Looping var */
828 ipp_attribute_t *attr; /* Current attribute */
829 ipp_t *request, /* IPP request */
830 *response, /* IPP response */
831 *oldinfo; /* Old printer information */
832 const cgi_file_t *file; /* Uploaded file, if any */
833 const char *var; /* CGI variable */
834 char uri[HTTP_MAX_URI], /* Device or printer URI */
835 *uriptr; /* Pointer into URI */
836 int maxrate; /* Maximum baud rate */
837 char baudrate[255]; /* Baud rate string */
838 const char *name, /* Pointer to class name */
839 *ptr; /* Pointer to CGI variable */
840 const char *title; /* Title of page */
841 static int baudrates[] = /* Baud rates */
842 {
843 1200,
844 2400,
845 4800,
846 9600,
847 19200,
848 38400,
849 57600,
850 115200,
851 230400,
852 460800
853 };
854
855
856 ptr = cgiGetVariable("DEVICE_URI");
857 fprintf(stderr, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n",
858 ptr ? ptr : "(null)");
859
860 title = cgiText(modify ? _("Modify Printer") : _("Add Printer"));
861
862 if (modify)
863 {
864 /*
865 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
866 * following attributes:
867 *
868 * attributes-charset
869 * attributes-natural-language
870 * printer-uri
871 */
872
873 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
874
875 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
876 "localhost", 0, "/printers/%s",
877 cgiGetVariable("PRINTER_NAME"));
878 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
879 NULL, uri);
880
881 /*
882 * Do the request and get back a response...
883 */
884
885 oldinfo = cupsDoRequest(http, request, "/");
886 }
887 else
888 oldinfo = NULL;
889
890 file = cgiGetFile();
891
892 if (file)
893 {
894 fprintf(stderr, "DEBUG: file->tempfile=%s\n", file->tempfile);
895 fprintf(stderr, "DEBUG: file->name=%s\n", file->name);
896 fprintf(stderr, "DEBUG: file->filename=%s\n", file->filename);
897 fprintf(stderr, "DEBUG: file->mimetype=%s\n", file->mimetype);
898 }
899
900 if ((name = cgiGetVariable("PRINTER_NAME")) != NULL)
901 {
902 for (ptr = name; *ptr; ptr ++)
903 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
904 break;
905
906 if (*ptr || ptr == name || strlen(name) > 127)
907 {
908 cgiSetVariable("ERROR",
909 cgiText(_("The printer name may only contain up to "
910 "127 printable characters and may not "
911 "contain spaces, slashes (/), or the "
912 "pound sign (#).")));
913 cgiStartHTML(title);
914 cgiCopyTemplateLang("error.tmpl");
915 cgiEndHTML();
916 return;
917 }
918 }
919
920 if ((var = cgiGetVariable("DEVICE_URI")) != NULL)
921 {
922 if ((uriptr = strrchr(var, '|')) != NULL)
923 {
924 /*
925 * Extract make and make/model from device URI string...
926 */
927
928 char make[1024], /* Make string */
929 *makeptr; /* Pointer into make */
930
931
932 *uriptr++ = '\0';
933
934 strlcpy(make, uriptr, sizeof(make));
935
936 if ((makeptr = strchr(make, ' ')) != NULL)
937 *makeptr = '\0';
938 else if ((makeptr = strchr(make, '-')) != NULL)
939 *makeptr = '\0';
940 else if (!_cups_strncasecmp(make, "laserjet", 8) ||
941 !_cups_strncasecmp(make, "deskjet", 7) ||
942 !_cups_strncasecmp(make, "designjet", 9))
943 strlcpy(make, "HP", sizeof(make));
944 else if (!_cups_strncasecmp(make, "phaser", 6))
945 strlcpy(make, "Xerox", sizeof(make));
946 else if (!_cups_strncasecmp(make, "stylus", 6))
947 strlcpy(make, "Epson", sizeof(make));
948 else
949 strlcpy(make, "Generic", sizeof(make));
950
951 if (!cgiGetVariable("CURRENT_MAKE"))
952 cgiSetVariable("CURRENT_MAKE", make);
953
954 if (!cgiGetVariable("CURRENT_MAKE_AND_MODEL"))
955 cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr);
956
957 if (!modify)
958 {
959 char template[128], /* Template name */
960 *tptr; /* Pointer into template name */
961
962 cgiSetVariable("PRINTER_INFO", uriptr);
963
964 for (tptr = template;
965 tptr < (template + sizeof(template) - 1) && *uriptr;
966 uriptr ++)
967 if (isalnum(*uriptr & 255) || *uriptr == '_' || *uriptr == '-' ||
968 *uriptr == '.')
969 *tptr++ = *uriptr;
970 else if ((*uriptr == ' ' || *uriptr == '/') && tptr > template &&
971 tptr[-1] != '_')
972 *tptr++ = '_';
973 else if (*uriptr == '?' || *uriptr == '(')
974 break;
975
976 *tptr = '\0';
977
978 cgiSetVariable("TEMPLATE_NAME", template);
979 }
980 }
981 }
982
983 if (!var)
984 {
985 /*
986 * Look for devices so the user can pick something...
987 */
988
989 if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
990 {
991 strlcpy(uri, attr->values[0].string.text, sizeof(uri));
992 if ((uriptr = strchr(uri, ':')) != NULL && strncmp(uriptr, "://", 3) == 0)
993 *uriptr = '\0';
994
995 cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text);
996 cgiSetVariable("CURRENT_DEVICE_SCHEME", uri);
997 }
998
999 /*
1000 * Scan for devices for up to 30 seconds...
1001 */
1002
1003 fputs("DEBUG: Getting list of devices...\n", stderr);
1004
1005 current_device = 0;
1006 if (cupsGetDevices(http, 5, CUPS_INCLUDE_ALL, CUPS_EXCLUDE_NONE,
1007 (cups_device_cb_t)choose_device_cb,
1008 (void *)title) == IPP_OK)
1009 {
1010 fputs("DEBUG: Got device list!\n", stderr);
1011
1012 if (cgiSupportsMultipart())
1013 cgiStartMultipart();
1014
1015 cgiSetVariable("CUPS_GET_DEVICES_DONE", "1");
1016 cgiStartHTML(title);
1017 cgiCopyTemplateLang("choose-device.tmpl");
1018 cgiEndHTML();
1019
1020 if (cgiSupportsMultipart())
1021 cgiEndMultipart();
1022 }
1023 else
1024 {
1025 fprintf(stderr,
1026 "ERROR: CUPS-Get-Devices request failed with status %x: %s\n",
1027 cupsLastError(), cupsLastErrorString());
1028 if (cupsLastError() == IPP_NOT_AUTHORIZED)
1029 {
1030 puts("Status: 401\n");
1031 exit(0);
1032 }
1033 else
1034 {
1035 cgiStartHTML(title);
1036 cgiShowIPPError(modify ? _("Unable to modify printer") :
1037 _("Unable to add printer"));
1038 cgiEndHTML();
1039 return;
1040 }
1041 }
1042 }
1043 else if (!strchr(var, '/') ||
1044 (!strncmp(var, "lpd://", 6) && !strchr(var + 6, '/')))
1045 {
1046 if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
1047 {
1048 /*
1049 * Set the current device URI for the form to the old one...
1050 */
1051
1052 if (strncmp(attr->values[0].string.text, var, strlen(var)) == 0)
1053 cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text);
1054 }
1055
1056 /*
1057 * User needs to set the full URI...
1058 */
1059
1060 cgiStartHTML(title);
1061 cgiCopyTemplateLang("choose-uri.tmpl");
1062 cgiEndHTML();
1063 }
1064 else if (!strncmp(var, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
1065 {
1066 /*
1067 * Need baud rate, parity, etc.
1068 */
1069
1070 if ((var = strchr(var, '?')) != NULL &&
1071 strncmp(var, "?baud=", 6) == 0)
1072 maxrate = atoi(var + 6);
1073 else
1074 maxrate = 19200;
1075
1076 for (i = 0; i < 10; i ++)
1077 if (baudrates[i] > maxrate)
1078 break;
1079 else
1080 {
1081 sprintf(baudrate, "%d", baudrates[i]);
1082 cgiSetArray("BAUDRATES", i, baudrate);
1083 }
1084
1085 cgiStartHTML(title);
1086 cgiCopyTemplateLang("choose-serial.tmpl");
1087 cgiEndHTML();
1088 }
1089 else if (!name || !cgiGetVariable("PRINTER_LOCATION"))
1090 {
1091 cgiStartHTML(title);
1092
1093 if (modify)
1094 {
1095 /*
1096 * Update the location and description of an existing printer...
1097 */
1098
1099 if (oldinfo)
1100 {
1101 if ((attr = ippFindAttribute(oldinfo, "printer-info",
1102 IPP_TAG_TEXT)) != NULL)
1103 cgiSetVariable("PRINTER_INFO", attr->values[0].string.text);
1104
1105 if ((attr = ippFindAttribute(oldinfo, "printer-location",
1106 IPP_TAG_TEXT)) != NULL)
1107 cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text);
1108
1109 if ((attr = ippFindAttribute(oldinfo, "printer-is-shared",
1110 IPP_TAG_BOOLEAN)) != NULL)
1111 cgiSetVariable("PRINTER_IS_SHARED",
1112 attr->values[0].boolean ? "1" : "0");
1113 }
1114
1115 cgiCopyTemplateLang("modify-printer.tmpl");
1116 }
1117 else
1118 {
1119 /*
1120 * Get the name, location, and description for a new printer...
1121 */
1122
1123 #ifdef __APPLE__
1124 if (!strncmp(var, "usb:", 4))
1125 cgiSetVariable("printer_is_shared", "1");
1126 else
1127 #endif /* __APPLE__ */
1128 cgiSetVariable("printer_is_shared", "0");
1129
1130 cgiCopyTemplateLang("add-printer.tmpl");
1131 }
1132
1133 cgiEndHTML();
1134
1135 if (oldinfo)
1136 ippDelete(oldinfo);
1137
1138 return;
1139 }
1140 else if (!file &&
1141 (!cgiGetVariable("PPD_NAME") || cgiGetVariable("SELECT_MAKE")))
1142 {
1143 if (modify && !cgiGetVariable("SELECT_MAKE"))
1144 {
1145 /*
1146 * Get the PPD file...
1147 */
1148
1149 int fd; /* PPD file */
1150 char filename[1024]; /* PPD filename */
1151 ppd_file_t *ppd; /* PPD information */
1152 char buffer[1024]; /* Buffer */
1153 ssize_t bytes; /* Number of bytes */
1154 http_status_t get_status; /* Status of GET */
1155
1156
1157 /* TODO: Use cupsGetFile() API... */
1158 snprintf(uri, sizeof(uri), "/printers/%s.ppd", name);
1159
1160 if (httpGet(http, uri))
1161 httpGet(http, uri);
1162
1163 while ((get_status = httpUpdate(http)) == HTTP_CONTINUE);
1164
1165 if (get_status != HTTP_OK)
1166 {
1167 httpFlush(http);
1168
1169 fprintf(stderr, "ERROR: Unable to get PPD file %s: %d - %s\n",
1170 uri, get_status, httpStatus(get_status));
1171 }
1172 else if ((fd = cupsTempFd(filename, sizeof(filename))) >= 0)
1173 {
1174 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
1175 write(fd, buffer, (size_t)bytes);
1176
1177 close(fd);
1178
1179 if ((ppd = ppdOpenFile(filename)) != NULL)
1180 {
1181 if (ppd->manufacturer)
1182 cgiSetVariable("CURRENT_MAKE", ppd->manufacturer);
1183
1184 if (ppd->nickname)
1185 cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd->nickname);
1186
1187 ppdClose(ppd);
1188 unlink(filename);
1189 }
1190 else
1191 {
1192 int linenum; /* Line number */
1193
1194 fprintf(stderr, "ERROR: Unable to open PPD file %s: %s\n",
1195 filename, ppdErrorString(ppdLastError(&linenum)));
1196 }
1197 }
1198 else
1199 {
1200 httpFlush(http);
1201
1202 fprintf(stderr,
1203 "ERROR: Unable to create temporary file for PPD file: %s\n",
1204 strerror(errno));
1205 }
1206 }
1207
1208 /*
1209 * Build a CUPS_GET_PPDS request, which requires the following
1210 * attributes:
1211 *
1212 * attributes-charset
1213 * attributes-natural-language
1214 * printer-uri
1215 */
1216
1217 request = ippNewRequest(CUPS_GET_PPDS);
1218
1219 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1220 NULL, "ipp://localhost/printers/");
1221
1222 if ((var = cgiGetVariable("PPD_MAKE")) == NULL)
1223 var = cgiGetVariable("CURRENT_MAKE");
1224 if (var && !cgiGetVariable("SELECT_MAKE"))
1225 {
1226 const char *make_model; /* Make and model */
1227
1228
1229 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1230 "ppd-make", NULL, var);
1231
1232 if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL)
1233 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1234 "ppd-make-and-model", NULL, make_model);
1235 }
1236 else
1237 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1238 "requested-attributes", NULL, "ppd-make");
1239
1240 /*
1241 * Do the request and get back a response...
1242 */
1243
1244 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1245 {
1246 /*
1247 * Got the list of PPDs, see if the user has selected a make...
1248 */
1249
1250 if (cgiSetIPPVars(response, NULL, NULL, NULL, 0) == 0 && !modify)
1251 {
1252 /*
1253 * No PPD files with this make, try again with all makes...
1254 */
1255
1256 ippDelete(response);
1257
1258 request = ippNewRequest(CUPS_GET_PPDS);
1259
1260 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1261 NULL, "ipp://localhost/printers/");
1262
1263 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1264 "requested-attributes", NULL, "ppd-make");
1265
1266 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1267 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
1268
1269 cgiStartHTML(title);
1270 cgiCopyTemplateLang("choose-make.tmpl");
1271 cgiEndHTML();
1272 }
1273 else if (!var || cgiGetVariable("SELECT_MAKE"))
1274 {
1275 cgiStartHTML(title);
1276 cgiCopyTemplateLang("choose-make.tmpl");
1277 cgiEndHTML();
1278 }
1279 else
1280 {
1281 /*
1282 * Let the user choose a model...
1283 */
1284
1285 cgiStartHTML(title);
1286 if (!cgiGetVariable("PPD_MAKE"))
1287 cgiSetVariable("PPD_MAKE", cgiGetVariable("CURRENT_MAKE"));
1288 if (!modify)
1289 cgiSetVariable("CURRENT_MAKE_AND_MODEL",
1290 cgiGetArray("PPD_MAKE_AND_MODEL", 0));
1291 cgiCopyTemplateLang("choose-model.tmpl");
1292 cgiEndHTML();
1293 }
1294
1295 ippDelete(response);
1296 }
1297 else
1298 {
1299 cgiStartHTML(title);
1300 cgiShowIPPError(_("Unable to get list of printer drivers"));
1301 cgiCopyTemplateLang("error.tmpl");
1302 cgiEndHTML();
1303 }
1304 }
1305 else
1306 {
1307 /*
1308 * Build a CUPS_ADD_PRINTER request, which requires the following
1309 * attributes:
1310 *
1311 * attributes-charset
1312 * attributes-natural-language
1313 * printer-uri
1314 * printer-location
1315 * printer-info
1316 * ppd-name
1317 * device-uri
1318 * printer-is-accepting-jobs
1319 * printer-is-shared
1320 * printer-state
1321 */
1322
1323 request = ippNewRequest(CUPS_ADD_PRINTER);
1324
1325 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1326 "localhost", 0, "/printers/%s",
1327 cgiGetVariable("PRINTER_NAME"));
1328 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1329 NULL, uri);
1330
1331 if (!file)
1332 {
1333 var = cgiGetVariable("PPD_NAME");
1334 if (strcmp(var, "__no_change__"))
1335 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
1336 NULL, var);
1337 }
1338
1339 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
1340 NULL, cgiGetVariable("PRINTER_LOCATION"));
1341
1342 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1343 NULL, cgiGetVariable("PRINTER_INFO"));
1344
1345 strlcpy(uri, cgiGetVariable("DEVICE_URI"), sizeof(uri));
1346
1347 /*
1348 * Strip make and model from URI...
1349 */
1350
1351 if ((uriptr = strrchr(uri, '|')) != NULL)
1352 *uriptr = '\0';
1353
1354 if (!strncmp(uri, "serial:", 7))
1355 {
1356 /*
1357 * Update serial port URI to include baud rate, etc.
1358 */
1359
1360 if ((uriptr = strchr(uri, '?')) == NULL)
1361 uriptr = uri + strlen(uri);
1362
1363 snprintf(uriptr, sizeof(uri) - (size_t)(uriptr - uri),
1364 "?baud=%s+bits=%s+parity=%s+flow=%s",
1365 cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
1366 cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
1367 }
1368
1369 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri",
1370 NULL, uri);
1371
1372 ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1373
1374 var = cgiGetVariable("printer_is_shared");
1375 ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-shared",
1376 var && (!strcmp(var, "1") || !strcmp(var, "on")));
1377
1378 ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
1379 IPP_PRINTER_IDLE);
1380
1381 /*
1382 * Do the request and get back a response...
1383 */
1384
1385 if (file)
1386 ippDelete(cupsDoFileRequest(http, request, "/admin/", file->tempfile));
1387 else
1388 ippDelete(cupsDoRequest(http, request, "/admin/"));
1389
1390 if (cupsLastError() == IPP_NOT_AUTHORIZED)
1391 {
1392 puts("Status: 401\n");
1393 exit(0);
1394 }
1395 else if (cupsLastError() > IPP_OK_CONFLICT)
1396 {
1397 cgiStartHTML(title);
1398 cgiShowIPPError(modify ? _("Unable to modify printer") :
1399 _("Unable to add printer"));
1400 }
1401 else if (modify)
1402 {
1403 /*
1404 * Redirect successful updates back to the printer page...
1405 */
1406
1407 char refresh[1024]; /* Refresh URL */
1408
1409
1410 cgiFormEncode(uri, name, sizeof(uri));
1411
1412 snprintf(refresh, sizeof(refresh),
1413 "5;/admin/?OP=redirect&URL=/printers/%s", uri);
1414
1415 cgiSetVariable("refresh_page", refresh);
1416
1417 cgiStartHTML(title);
1418
1419 cgiCopyTemplateLang("printer-modified.tmpl");
1420 }
1421 else
1422 {
1423 /*
1424 * Set the printer options...
1425 */
1426
1427 cgiSetVariable("OP", "set-printer-options");
1428 do_set_options(http, 0);
1429 return;
1430 }
1431
1432 cgiEndHTML();
1433 }
1434
1435 if (oldinfo)
1436 ippDelete(oldinfo);
1437 }
1438
1439
1440 /*
1441 * 'do_cancel_subscription()' - Cancel a subscription.
1442 */
1443
1444 static void
1445 do_cancel_subscription(http_t *http)/* I - HTTP connection */
1446 {
1447 ipp_t *request; /* IPP request data */
1448 const char *var, /* Form variable */
1449 *user; /* Username */
1450 int id; /* Subscription ID */
1451
1452
1453 /*
1454 * See if we have all of the required information...
1455 */
1456
1457 if ((var = cgiGetVariable("NOTIFY_SUBSCRIPTION_ID")) != NULL)
1458 id = atoi(var);
1459 else
1460 id = 0;
1461
1462 if (id <= 0)
1463 {
1464 cgiSetVariable("ERROR", cgiText(_("Bad subscription ID")));
1465 cgiStartHTML(_("Cancel RSS Subscription"));
1466 cgiCopyTemplateLang("error.tmpl");
1467 cgiEndHTML();
1468 return;
1469 }
1470
1471 /*
1472 * Require a username...
1473 */
1474
1475 if ((user = getenv("REMOTE_USER")) == NULL)
1476 {
1477 puts("Status: 401\n");
1478 exit(0);
1479 }
1480
1481 /*
1482 * Cancel the subscription...
1483 */
1484
1485 request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
1486
1487 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1488 NULL, "ipp://localhost/");
1489 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1490 "notify-subscription-id", id);
1491
1492 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1493 NULL, user);
1494
1495 ippDelete(cupsDoRequest(http, request, "/"));
1496
1497 if (cupsLastError() == IPP_NOT_AUTHORIZED)
1498 {
1499 puts("Status: 401\n");
1500 exit(0);
1501 }
1502 else if (cupsLastError() > IPP_OK_CONFLICT)
1503 {
1504 cgiStartHTML(_("Cancel RSS Subscription"));
1505 cgiShowIPPError(_("Unable to cancel RSS subscription"));
1506 }
1507 else
1508 {
1509 /*
1510 * Redirect successful updates back to the admin page...
1511 */
1512
1513 cgiSetVariable("refresh_page", "5;URL=/admin");
1514 cgiStartHTML(_("Cancel RSS Subscription"));
1515 cgiCopyTemplateLang("subscription-canceled.tmpl");
1516 }
1517
1518 cgiEndHTML();
1519 }
1520
1521
1522 /*
1523 * 'do_config_server()' - Configure server settings.
1524 */
1525
1526 static void
1527 do_config_server(http_t *http) /* I - HTTP connection */
1528 {
1529 if (cgiGetVariable("CHANGESETTINGS"))
1530 {
1531 /*
1532 * Save basic setting changes...
1533 */
1534
1535 int num_settings; /* Number of server settings */
1536 cups_option_t *settings; /* Server settings */
1537 int advanced, /* Advanced settings shown? */
1538 changed; /* Have settings changed? */
1539 const char *debug_logging, /* DEBUG_LOGGING value */
1540 *preserve_jobs = NULL,
1541 /* PRESERVE_JOBS value */
1542 *remote_admin, /* REMOTE_ADMIN value */
1543 *remote_any, /* REMOTE_ANY value */
1544 *share_printers,/* SHARE_PRINTERS value */
1545 *user_cancel_any,
1546 /* USER_CANCEL_ANY value */
1547 *browse_web_if = NULL,
1548 /* BrowseWebIF value */
1549 *preserve_job_history = NULL,
1550 /* PreserveJobHistory value */
1551 *preserve_job_files = NULL,
1552 /* PreserveJobFiles value */
1553 *max_clients = NULL,
1554 /* MaxClients value */
1555 *max_jobs = NULL,
1556 /* MaxJobs value */
1557 *max_log_size = NULL;
1558 /* MaxLogSize value */
1559 const char *current_browse_web_if,
1560 /* BrowseWebIF value */
1561 *current_preserve_job_history,
1562 /* PreserveJobHistory value */
1563 *current_preserve_job_files,
1564 /* PreserveJobFiles value */
1565 *current_max_clients,
1566 /* MaxClients value */
1567 *current_max_jobs,
1568 /* MaxJobs value */
1569 *current_max_log_size;
1570 /* MaxLogSize value */
1571 #ifdef HAVE_GSSAPI
1572 char default_auth_type[255];
1573 /* DefaultAuthType value */
1574 const char *val; /* Setting value */
1575 #endif /* HAVE_GSSAPI */
1576
1577
1578 /*
1579 * Get the checkbox values from the form...
1580 */
1581
1582 debug_logging = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0";
1583 remote_admin = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0";
1584 remote_any = cgiGetVariable("REMOTE_ANY") ? "1" : "0";
1585 share_printers = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0";
1586 user_cancel_any = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0";
1587
1588 advanced = cgiGetVariable("ADVANCEDSETTINGS") != NULL;
1589 if (advanced)
1590 {
1591 /*
1592 * Get advanced settings...
1593 */
1594
1595 browse_web_if = cgiGetVariable("BROWSE_WEB_IF") ? "Yes" : "No";
1596 max_clients = cgiGetVariable("MAX_CLIENTS");
1597 max_log_size = cgiGetVariable("MAX_LOG_SIZE");
1598 preserve_jobs = cgiGetVariable("PRESERVE_JOBS");
1599
1600 if (preserve_jobs)
1601 {
1602 max_jobs = cgiGetVariable("MAX_JOBS");
1603 preserve_job_history = cgiGetVariable("PRESERVE_JOB_HISTORY");
1604 preserve_job_files = cgiGetVariable("PRESERVE_JOB_FILES");
1605
1606 if (!max_jobs || atoi(max_jobs) < 0)
1607 max_jobs = "500";
1608
1609 if (!preserve_job_history)
1610 preserve_job_history = "On";
1611
1612 if (!preserve_job_files)
1613 preserve_job_files = "1d";
1614 }
1615 else
1616 {
1617 max_jobs = "0";
1618 preserve_job_history = "No";
1619 preserve_job_files = "No";
1620 }
1621
1622 if (!max_clients || atoi(max_clients) <= 0)
1623 max_clients = "100";
1624
1625 if (!max_log_size || atoi(max_log_size) <= 0.0)
1626 max_log_size = "1m";
1627 }
1628
1629 /*
1630 * Get the current server settings...
1631 */
1632
1633 if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
1634 {
1635 cgiStartHTML(cgiText(_("Change Settings")));
1636 cgiSetVariable("MESSAGE",
1637 cgiText(_("Unable to change server settings")));
1638 cgiSetVariable("ERROR", cupsLastErrorString());
1639 cgiCopyTemplateLang("error.tmpl");
1640 cgiEndHTML();
1641 return;
1642 }
1643
1644 #ifdef HAVE_GSSAPI
1645 /*
1646 * Get authentication settings...
1647 */
1648
1649 if (cgiGetVariable("KERBEROS"))
1650 strlcpy(default_auth_type, "Negotiate", sizeof(default_auth_type));
1651 else
1652 {
1653 val = cupsGetOption("DefaultAuthType", num_settings, settings);
1654
1655 if (!val || !_cups_strcasecmp(val, "Negotiate"))
1656 strlcpy(default_auth_type, "Basic", sizeof(default_auth_type));
1657 else
1658 strlcpy(default_auth_type, val, sizeof(default_auth_type));
1659 }
1660
1661 fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type);
1662 #endif /* HAVE_GSSAPI */
1663
1664 if ((current_browse_web_if = cupsGetOption("BrowseWebIF", num_settings,
1665 settings)) == NULL)
1666 current_browse_web_if = "No";
1667
1668 if ((current_preserve_job_history = cupsGetOption("PreserveJobHistory",
1669 num_settings,
1670 settings)) == NULL)
1671 current_preserve_job_history = "Yes";
1672
1673 if ((current_preserve_job_files = cupsGetOption("PreserveJobFiles",
1674 num_settings,
1675 settings)) == NULL)
1676 current_preserve_job_files = "1d";
1677
1678 if ((current_max_clients = cupsGetOption("MaxClients", num_settings,
1679 settings)) == NULL)
1680 current_max_clients = "100";
1681
1682 if ((current_max_jobs = cupsGetOption("MaxJobs", num_settings,
1683 settings)) == NULL)
1684 current_max_jobs = "500";
1685
1686 if ((current_max_log_size = cupsGetOption("MaxLogSize", num_settings,
1687 settings)) == NULL)
1688 current_max_log_size = "1m";
1689
1690 /*
1691 * See if the settings have changed...
1692 */
1693
1694 changed = strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING,
1695 num_settings, settings)) ||
1696 strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN,
1697 num_settings, settings)) ||
1698 strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY,
1699 num_settings, settings)) ||
1700 strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS,
1701 num_settings, settings)) ||
1702 #ifdef HAVE_GSSAPI
1703 !cupsGetOption("DefaultAuthType", num_settings, settings) ||
1704 strcmp(default_auth_type, cupsGetOption("DefaultAuthType",
1705 num_settings, settings)) ||
1706 #endif /* HAVE_GSSAPI */
1707 strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY,
1708 num_settings, settings));
1709
1710 if (advanced && !changed)
1711 changed = _cups_strcasecmp(browse_web_if, current_browse_web_if) ||
1712 _cups_strcasecmp(preserve_job_history, current_preserve_job_history) ||
1713 _cups_strcasecmp(preserve_job_files, current_preserve_job_files) ||
1714 _cups_strcasecmp(max_clients, current_max_clients) ||
1715 _cups_strcasecmp(max_jobs, current_max_jobs) ||
1716 _cups_strcasecmp(max_log_size, current_max_log_size);
1717
1718 if (changed)
1719 {
1720 /*
1721 * Settings *have* changed, so save the changes...
1722 */
1723
1724 cupsFreeOptions(num_settings, settings);
1725
1726 num_settings = 0;
1727 num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
1728 debug_logging, num_settings, &settings);
1729 num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
1730 remote_admin, num_settings, &settings);
1731 num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
1732 remote_any, num_settings, &settings);
1733 num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
1734 share_printers, num_settings, &settings);
1735 num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
1736 user_cancel_any, num_settings, &settings);
1737 #ifdef HAVE_GSSAPI
1738 num_settings = cupsAddOption("DefaultAuthType", default_auth_type,
1739 num_settings, &settings);
1740 #endif /* HAVE_GSSAPI */
1741
1742 if (advanced)
1743 {
1744 /*
1745 * Add advanced settings...
1746 */
1747
1748 if (_cups_strcasecmp(browse_web_if, current_browse_web_if))
1749 num_settings = cupsAddOption("BrowseWebIF", browse_web_if,
1750 num_settings, &settings);
1751 if (_cups_strcasecmp(preserve_job_history, current_preserve_job_history))
1752 num_settings = cupsAddOption("PreserveJobHistory",
1753 preserve_job_history, num_settings,
1754 &settings);
1755 if (_cups_strcasecmp(preserve_job_files, current_preserve_job_files))
1756 num_settings = cupsAddOption("PreserveJobFiles", preserve_job_files,
1757 num_settings, &settings);
1758 if (_cups_strcasecmp(max_clients, current_max_clients))
1759 num_settings = cupsAddOption("MaxClients", max_clients, num_settings,
1760 &settings);
1761 if (_cups_strcasecmp(max_jobs, current_max_jobs))
1762 num_settings = cupsAddOption("MaxJobs", max_jobs, num_settings,
1763 &settings);
1764 if (_cups_strcasecmp(max_log_size, current_max_log_size))
1765 num_settings = cupsAddOption("MaxLogSize", max_log_size, num_settings,
1766 &settings);
1767 }
1768
1769 if (!cupsAdminSetServerSettings(http, num_settings, settings))
1770 {
1771 if (cupsLastError() == IPP_NOT_AUTHORIZED)
1772 {
1773 puts("Status: 401\n");
1774 exit(0);
1775 }
1776
1777 cgiStartHTML(cgiText(_("Change Settings")));
1778 cgiSetVariable("MESSAGE",
1779 cgiText(_("Unable to change server settings")));
1780 cgiSetVariable("ERROR", cupsLastErrorString());
1781 cgiCopyTemplateLang("error.tmpl");
1782 }
1783 else
1784 {
1785 if (advanced)
1786 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&"
1787 "URL=/admin/?ADVANCEDSETTINGS=YES");
1788 else
1789 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1790 cgiStartHTML(cgiText(_("Change Settings")));
1791 cgiCopyTemplateLang("restart.tmpl");
1792 }
1793 }
1794 else
1795 {
1796 /*
1797 * No changes...
1798 */
1799
1800 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1801 cgiStartHTML(cgiText(_("Change Settings")));
1802 cgiCopyTemplateLang("norestart.tmpl");
1803 }
1804
1805 cupsFreeOptions(num_settings, settings);
1806
1807 cgiEndHTML();
1808 }
1809 else if (cgiGetVariable("SAVECHANGES") && cgiGetVariable("CUPSDCONF"))
1810 {
1811 /*
1812 * Save hand-edited config file...
1813 */
1814
1815 http_status_t status; /* PUT status */
1816 char tempfile[1024]; /* Temporary new cupsd.conf */
1817 int tempfd; /* Temporary file descriptor */
1818 cups_file_t *temp; /* Temporary file */
1819 const char *start, /* Start of line */
1820 *end; /* End of line */
1821
1822
1823 /*
1824 * Create a temporary file for the new cupsd.conf file...
1825 */
1826
1827 if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
1828 {
1829 cgiStartHTML(cgiText(_("Edit Configuration File")));
1830 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file")));
1831 cgiSetVariable("ERROR", strerror(errno));
1832 cgiCopyTemplateLang("error.tmpl");
1833 cgiEndHTML();
1834
1835 perror(tempfile);
1836 return;
1837 }
1838
1839 if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL)
1840 {
1841 cgiStartHTML(cgiText(_("Edit Configuration File")));
1842 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file")));
1843 cgiSetVariable("ERROR", strerror(errno));
1844 cgiCopyTemplateLang("error.tmpl");
1845 cgiEndHTML();
1846
1847 perror(tempfile);
1848 close(tempfd);
1849 unlink(tempfile);
1850 return;
1851 }
1852
1853 /*
1854 * Copy the cupsd.conf text from the form variable...
1855 */
1856
1857 start = cgiGetVariable("CUPSDCONF");
1858 while (start)
1859 {
1860 if ((end = strstr(start, "\r\n")) == NULL)
1861 if ((end = strstr(start, "\n")) == NULL)
1862 end = start + strlen(start);
1863
1864 cupsFileWrite(temp, start, (size_t)(end - start));
1865 cupsFilePutChar(temp, '\n');
1866
1867 if (*end == '\r')
1868 start = end + 2;
1869 else if (*end == '\n')
1870 start = end + 1;
1871 else
1872 start = NULL;
1873 }
1874
1875 cupsFileClose(temp);
1876
1877 /*
1878 * Upload the configuration file to the server...
1879 */
1880
1881 status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
1882
1883 if (status == HTTP_UNAUTHORIZED)
1884 {
1885 puts("Status: 401\n");
1886 unlink(tempfile);
1887 exit(0);
1888 }
1889 else if (status != HTTP_CREATED)
1890 {
1891 cgiSetVariable("MESSAGE",
1892 cgiText(_("Unable to upload cupsd.conf file")));
1893 cgiSetVariable("ERROR", httpStatus(status));
1894
1895 cgiStartHTML(cgiText(_("Edit Configuration File")));
1896 cgiCopyTemplateLang("error.tmpl");
1897 }
1898 else
1899 {
1900 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1901
1902 cgiStartHTML(cgiText(_("Edit Configuration File")));
1903 cgiCopyTemplateLang("restart.tmpl");
1904 }
1905
1906 cgiEndHTML();
1907
1908 unlink(tempfile);
1909 }
1910 else
1911 {
1912 struct stat info; /* cupsd.conf information */
1913 cups_file_t *cupsd; /* cupsd.conf file */
1914 char *buffer, /* Buffer for entire file */
1915 *bufptr, /* Pointer into buffer */
1916 *bufend; /* End of buffer */
1917 int ch; /* Character from file */
1918 char filename[1024]; /* Filename */
1919 const char *server_root; /* Location of config files */
1920
1921
1922 /*
1923 * Locate the cupsd.conf file...
1924 */
1925
1926 if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
1927 server_root = CUPS_SERVERROOT;
1928
1929 snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root);
1930
1931 /*
1932 * Figure out the size...
1933 */
1934
1935 if (stat(filename, &info))
1936 {
1937 cgiStartHTML(cgiText(_("Edit Configuration File")));
1938 cgiSetVariable("MESSAGE",
1939 cgiText(_("Unable to access cupsd.conf file")));
1940 cgiSetVariable("ERROR", strerror(errno));
1941 cgiCopyTemplateLang("error.tmpl");
1942 cgiEndHTML();
1943
1944 perror(filename);
1945 return;
1946 }
1947
1948 if (info.st_size > (1024 * 1024))
1949 {
1950 cgiStartHTML(cgiText(_("Edit Configuration File")));
1951 cgiSetVariable("MESSAGE",
1952 cgiText(_("Unable to access cupsd.conf file")));
1953 cgiSetVariable("ERROR",
1954 cgiText(_("Unable to edit cupsd.conf files larger than "
1955 "1MB")));
1956 cgiCopyTemplateLang("error.tmpl");
1957 cgiEndHTML();
1958
1959 fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename,
1960 (long)info.st_size);
1961 return;
1962 }
1963
1964 /*
1965 * Open the cupsd.conf file...
1966 */
1967
1968 if ((cupsd = cupsFileOpen(filename, "r")) == NULL)
1969 {
1970 /*
1971 * Unable to open - log an error...
1972 */
1973
1974 cgiStartHTML(cgiText(_("Edit Configuration File")));
1975 cgiSetVariable("MESSAGE",
1976 cgiText(_("Unable to access cupsd.conf file")));
1977 cgiSetVariable("ERROR", strerror(errno));
1978 cgiCopyTemplateLang("error.tmpl");
1979 cgiEndHTML();
1980
1981 perror(filename);
1982 return;
1983 }
1984
1985 /*
1986 * Allocate memory and load the file into a string buffer...
1987 */
1988
1989 if ((buffer = calloc(1, (size_t)info.st_size + 1)) != NULL)
1990 {
1991 cupsFileRead(cupsd, buffer, (size_t)info.st_size);
1992 cgiSetVariable("CUPSDCONF", buffer);
1993 free(buffer);
1994 }
1995
1996 cupsFileClose(cupsd);
1997
1998 /*
1999 * Then get the default cupsd.conf file and put that into a string as
2000 * well...
2001 */
2002
2003 strlcat(filename, ".default", sizeof(filename));
2004
2005 if (!stat(filename, &info) && info.st_size < (1024 * 1024) &&
2006 (cupsd = cupsFileOpen(filename, "r")) != NULL)
2007 {
2008 if ((buffer = calloc(1, 2 * (size_t)info.st_size + 1)) != NULL)
2009 {
2010 bufend = buffer + 2 * info.st_size - 1;
2011
2012 for (bufptr = buffer;
2013 bufptr < bufend && (ch = cupsFileGetChar(cupsd)) != EOF;)
2014 {
2015 if (ch == '\\' || ch == '\"')
2016 {
2017 *bufptr++ = '\\';
2018 *bufptr++ = (char)ch;
2019 }
2020 else if (ch == '\n')
2021 {
2022 *bufptr++ = '\\';
2023 *bufptr++ = 'n';
2024 }
2025 else if (ch == '\t')
2026 {
2027 *bufptr++ = '\\';
2028 *bufptr++ = 't';
2029 }
2030 else if (ch >= ' ')
2031 *bufptr++ = (char)ch;
2032 }
2033
2034 *bufptr = '\0';
2035
2036 cgiSetVariable("CUPSDCONF_DEFAULT", buffer);
2037 free(buffer);
2038 }
2039
2040 cupsFileClose(cupsd);
2041 }
2042
2043 /*
2044 * Show the current config file...
2045 */
2046
2047 cgiStartHTML(cgiText(_("Edit Configuration File")));
2048
2049 cgiCopyTemplateLang("edit-config.tmpl");
2050
2051 cgiEndHTML();
2052 }
2053 }
2054
2055
2056 /*
2057 * 'do_delete_class()' - Delete a class.
2058 */
2059
2060 static void
2061 do_delete_class(http_t *http) /* I - HTTP connection */
2062 {
2063 ipp_t *request; /* IPP request */
2064 char uri[HTTP_MAX_URI]; /* Job URI */
2065 const char *pclass; /* Printer class name */
2066
2067
2068 /*
2069 * Get form variables...
2070 */
2071
2072 if (cgiGetVariable("CONFIRM") == NULL)
2073 {
2074 cgiStartHTML(cgiText(_("Delete Class")));
2075 cgiCopyTemplateLang("class-confirm.tmpl");
2076 cgiEndHTML();
2077 return;
2078 }
2079
2080 if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL)
2081 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2082 "localhost", 0, "/classes/%s", pclass);
2083 else
2084 {
2085 cgiStartHTML(cgiText(_("Delete Class")));
2086 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2087 cgiCopyTemplateLang("error.tmpl");
2088 cgiEndHTML();
2089 return;
2090 }
2091
2092 /*
2093 * Build a CUPS_DELETE_CLASS request, which requires the following
2094 * attributes:
2095 *
2096 * attributes-charset
2097 * attributes-natural-language
2098 * printer-uri
2099 */
2100
2101 request = ippNewRequest(CUPS_DELETE_CLASS);
2102
2103 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2104 NULL, uri);
2105
2106 /*
2107 * Do the request and get back a response...
2108 */
2109
2110 ippDelete(cupsDoRequest(http, request, "/admin/"));
2111
2112 /*
2113 * Show the results...
2114 */
2115
2116 if (cupsLastError() == IPP_NOT_AUTHORIZED)
2117 {
2118 puts("Status: 401\n");
2119 exit(0);
2120 }
2121 else if (cupsLastError() <= IPP_OK_CONFLICT)
2122 {
2123 /*
2124 * Redirect successful updates back to the classes page...
2125 */
2126
2127 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes");
2128 }
2129
2130 cgiStartHTML(cgiText(_("Delete Class")));
2131
2132 if (cupsLastError() > IPP_OK_CONFLICT)
2133 cgiShowIPPError(_("Unable to delete class"));
2134 else
2135 cgiCopyTemplateLang("class-deleted.tmpl");
2136
2137 cgiEndHTML();
2138 }
2139
2140
2141 /*
2142 * 'do_delete_printer()' - Delete a printer.
2143 */
2144
2145 static void
2146 do_delete_printer(http_t *http) /* I - HTTP connection */
2147 {
2148 ipp_t *request; /* IPP request */
2149 char uri[HTTP_MAX_URI]; /* Job URI */
2150 const char *printer; /* Printer printer name */
2151
2152
2153 /*
2154 * Get form variables...
2155 */
2156
2157 if (cgiGetVariable("CONFIRM") == NULL)
2158 {
2159 cgiStartHTML(cgiText(_("Delete Printer")));
2160 cgiCopyTemplateLang("printer-confirm.tmpl");
2161 cgiEndHTML();
2162 return;
2163 }
2164
2165 if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
2166 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2167 "localhost", 0, "/printers/%s", printer);
2168 else
2169 {
2170 cgiStartHTML(cgiText(_("Delete Printer")));
2171 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2172 cgiCopyTemplateLang("error.tmpl");
2173 cgiEndHTML();
2174 return;
2175 }
2176
2177 /*
2178 * Build a CUPS_DELETE_PRINTER request, which requires the following
2179 * attributes:
2180 *
2181 * attributes-charset
2182 * attributes-natural-language
2183 * printer-uri
2184 */
2185
2186 request = ippNewRequest(CUPS_DELETE_PRINTER);
2187
2188 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2189 NULL, uri);
2190
2191 /*
2192 * Do the request and get back a response...
2193 */
2194
2195 ippDelete(cupsDoRequest(http, request, "/admin/"));
2196
2197 /*
2198 * Show the results...
2199 */
2200
2201 if (cupsLastError() == IPP_NOT_AUTHORIZED)
2202 {
2203 puts("Status: 401\n");
2204 exit(0);
2205 }
2206 else if (cupsLastError() <= IPP_OK_CONFLICT)
2207 {
2208 /*
2209 * Redirect successful updates back to the printers page...
2210 */
2211
2212 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers");
2213 }
2214
2215 cgiStartHTML(cgiText(_("Delete Printer")));
2216
2217 if (cupsLastError() > IPP_OK_CONFLICT)
2218 cgiShowIPPError(_("Unable to delete printer"));
2219 else
2220 cgiCopyTemplateLang("printer-deleted.tmpl");
2221
2222 cgiEndHTML();
2223 }
2224
2225
2226 /*
2227 * 'do_export()' - Export printers to Samba.
2228 */
2229
2230 static void
2231 do_export(http_t *http) /* I - HTTP connection */
2232 {
2233 int i, j; /* Looping vars */
2234 ipp_t *request, /* IPP request */
2235 *response; /* IPP response */
2236 const char *username, /* Samba username */
2237 *password, /* Samba password */
2238 *export_all; /* Export all printers? */
2239 int export_count, /* Number of printers to export */
2240 printer_count; /* Number of available printers */
2241 const char *name, /* What name to pull */
2242 *dest; /* Current destination */
2243 char ppd[1024]; /* PPD file */
2244
2245
2246 /*
2247 * Get form data...
2248 */
2249
2250 username = cgiGetVariable("USERNAME");
2251 password = cgiGetVariable("PASSWORD");
2252 export_all = cgiGetVariable("EXPORT_ALL");
2253 export_count = cgiGetSize("EXPORT_NAME");
2254
2255 /*
2256 * Get list of available printers...
2257 */
2258
2259 cgiSetSize("PRINTER_NAME", 0);
2260 cgiSetSize("PRINTER_EXPORT", 0);
2261
2262 request = ippNewRequest(CUPS_GET_PRINTERS);
2263
2264 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
2265 "printer-type", 0);
2266
2267 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
2268 "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
2269
2270 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2271 "requested-attributes", NULL, "printer-name");
2272
2273 if ((response = cupsDoRequest(http, request, "/")) != NULL)
2274 {
2275 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2276 ippDelete(response);
2277
2278 if (!export_all)
2279 {
2280 printer_count = cgiGetSize("PRINTER_NAME");
2281
2282 for (i = 0; i < printer_count; i ++)
2283 {
2284 dest = cgiGetArray("PRINTER_NAME", i);
2285
2286 for (j = 0; j < export_count; j ++)
2287 if (!_cups_strcasecmp(dest, cgiGetArray("EXPORT_NAME", j)))
2288 break;
2289
2290 cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : "");
2291 }
2292 }
2293 }
2294
2295 /*
2296 * Export or get the printers to export...
2297 */
2298
2299 if (username && *username && password && *password &&
2300 (export_all || export_count > 0))
2301 {
2302 /*
2303 * Do export...
2304 */
2305
2306 fputs("DEBUG: Export printers...\n", stderr);
2307
2308 if (export_all)
2309 {
2310 name = "PRINTER_NAME";
2311 export_count = cgiGetSize("PRINTER_NAME");
2312 }
2313 else
2314 name = "EXPORT_NAME";
2315
2316 for (i = 0; i < export_count; i ++)
2317 {
2318 dest = cgiGetArray(name, i);
2319
2320 if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd)))
2321 break;
2322
2323 j = cupsAdminExportSamba(dest, ppd, "localhost", username, password,
2324 stderr);
2325
2326 unlink(ppd);
2327
2328 if (!j)
2329 break;
2330 }
2331
2332 if (i < export_count)
2333 cgiSetVariable("ERROR", cupsLastErrorString());
2334 else
2335 {
2336 cgiStartHTML(cgiText(_("Export Printers to Samba")));
2337 cgiCopyTemplateLang("samba-exported.tmpl");
2338 cgiEndHTML();
2339 return;
2340 }
2341 }
2342 else if (username && !*username)
2343 cgiSetVariable("ERROR",
2344 cgiText(_("A Samba username is required to export "
2345 "printer drivers")));
2346 else if (username && (!password || !*password))
2347 cgiSetVariable("ERROR",
2348 cgiText(_("A Samba password is required to export "
2349 "printer drivers")));
2350
2351 /*
2352 * Show form...
2353 */
2354
2355 cgiStartHTML(cgiText(_("Export Printers to Samba")));
2356 cgiCopyTemplateLang("samba-export.tmpl");
2357 cgiEndHTML();
2358 }
2359
2360
2361 /*
2362 * 'do_list_printers()' - List available printers.
2363 */
2364
2365 static void
2366 do_list_printers(http_t *http) /* I - HTTP connection */
2367 {
2368 ipp_t *request, /* IPP request */
2369 *response; /* IPP response */
2370 ipp_attribute_t *attr; /* IPP attribute */
2371
2372
2373 cgiStartHTML(cgiText(_("List Available Printers")));
2374 fflush(stdout);
2375
2376 /*
2377 * Get the list of printers and their devices...
2378 */
2379
2380 request = ippNewRequest(CUPS_GET_PRINTERS);
2381
2382 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2383 "requested-attributes", NULL, "device-uri");
2384
2385 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
2386 CUPS_PRINTER_LOCAL);
2387 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
2388 CUPS_PRINTER_LOCAL);
2389
2390 if ((response = cupsDoRequest(http, request, "/")) != NULL)
2391 {
2392 /*
2393 * Got the printer list, now load the devices...
2394 */
2395
2396 int i; /* Looping var */
2397 cups_array_t *printer_devices; /* Printer devices for local printers */
2398 char *printer_device; /* Current printer device */
2399
2400
2401 /*
2402 * Allocate an array and copy the device strings...
2403 */
2404
2405 printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2406
2407 for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI);
2408 attr;
2409 attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI))
2410 {
2411 cupsArrayAdd(printer_devices, _cupsStrAlloc(attr->values[0].string.text));
2412 }
2413
2414 /*
2415 * Free the printer list and get the device list...
2416 */
2417
2418 ippDelete(response);
2419
2420 request = ippNewRequest(CUPS_GET_DEVICES);
2421
2422 if ((response = cupsDoRequest(http, request, "/")) != NULL)
2423 {
2424 /*
2425 * Got the device list, let's parse it...
2426 */
2427
2428 const char *device_uri, /* device-uri attribute value */
2429 *device_make_and_model, /* device-make-and-model value */
2430 *device_info; /* device-info value */
2431
2432
2433 for (i = 0, attr = response->attrs; attr; attr = attr->next)
2434 {
2435 /*
2436 * Skip leading attributes until we hit a device...
2437 */
2438
2439 while (attr && attr->group_tag != IPP_TAG_PRINTER)
2440 attr = attr->next;
2441
2442 if (!attr)
2443 break;
2444
2445 /*
2446 * Pull the needed attributes from this device...
2447 */
2448
2449 device_info = NULL;
2450 device_make_and_model = NULL;
2451 device_uri = NULL;
2452
2453 while (attr && attr->group_tag == IPP_TAG_PRINTER)
2454 {
2455 if (!strcmp(attr->name, "device-info") &&
2456 attr->value_tag == IPP_TAG_TEXT)
2457 device_info = attr->values[0].string.text;
2458
2459 if (!strcmp(attr->name, "device-make-and-model") &&
2460 attr->value_tag == IPP_TAG_TEXT)
2461 device_make_and_model = attr->values[0].string.text;
2462
2463 if (!strcmp(attr->name, "device-uri") &&
2464 attr->value_tag == IPP_TAG_URI)
2465 device_uri = attr->values[0].string.text;
2466
2467 attr = attr->next;
2468 }
2469
2470 /*
2471 * See if we have everything needed...
2472 */
2473
2474 if (device_info && device_make_and_model && device_uri &&
2475 _cups_strcasecmp(device_make_and_model, "unknown") &&
2476 strchr(device_uri, ':'))
2477 {
2478 /*
2479 * Yes, now see if there is already a printer for this
2480 * device...
2481 */
2482
2483 if (!cupsArrayFind(printer_devices, (void *)device_uri))
2484 {
2485 /*
2486 * Not found, so it must be a new printer...
2487 */
2488
2489 char option[1024], /* Form variables for this device */
2490 *option_ptr; /* Pointer into string */
2491 const char *ptr; /* Pointer into device string */
2492
2493
2494 /*
2495 * Format the printer name variable for this device...
2496 *
2497 * We use the device-info string first, then device-uri,
2498 * and finally device-make-and-model to come up with a
2499 * suitable name.
2500 */
2501
2502 if (_cups_strncasecmp(device_info, "unknown", 7))
2503 ptr = device_info;
2504 else if ((ptr = strstr(device_uri, "://")) != NULL)
2505 ptr += 3;
2506 else
2507 ptr = device_make_and_model;
2508
2509 for (option_ptr = option;
2510 option_ptr < (option + sizeof(option) - 1) && *ptr;
2511 ptr ++)
2512 if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' ||
2513 *ptr == '.')
2514 *option_ptr++ = *ptr;
2515 else if ((*ptr == ' ' || *ptr == '/') && option_ptr > option &&
2516 option_ptr[-1] != '_')
2517 *option_ptr++ = '_';
2518 else if (*ptr == '?' || *ptr == '(')
2519 break;
2520
2521 *option_ptr = '\0';
2522
2523 cgiSetArray("TEMPLATE_NAME", i, option);
2524
2525 /*
2526 * Finally, set the form variables for this printer...
2527 */
2528
2529 cgiSetArray("device_info", i, device_info);
2530 cgiSetArray("device_make_and_model", i, device_make_and_model);
2531 cgiSetArray("device_uri", i, device_uri);
2532 i ++;
2533 }
2534 }
2535
2536 if (!attr)
2537 break;
2538 }
2539
2540 ippDelete(response);
2541
2542 /*
2543 * Free the device list...
2544 */
2545
2546 for (printer_device = (char *)cupsArrayFirst(printer_devices);
2547 printer_device;
2548 printer_device = (char *)cupsArrayNext(printer_devices))
2549 _cupsStrFree(printer_device);
2550
2551 cupsArrayDelete(printer_devices);
2552 }
2553 }
2554
2555 /*
2556 * Finally, show the printer list...
2557 */
2558
2559 cgiCopyTemplateLang("list-available-printers.tmpl");
2560
2561 cgiEndHTML();
2562 }
2563
2564
2565 /*
2566 * 'do_menu()' - Show the main menu.
2567 */
2568
2569 static void
2570 do_menu(http_t *http) /* I - HTTP connection */
2571 {
2572 int num_settings; /* Number of server settings */
2573 cups_option_t *settings; /* Server settings */
2574 const char *val; /* Setting value */
2575 char filename[1024]; /* Temporary filename */
2576 const char *datadir; /* Location of data files */
2577 ipp_t *request, /* IPP request */
2578 *response; /* IPP response */
2579
2580
2581 /*
2582 * Get the current server settings...
2583 */
2584
2585 if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
2586 {
2587 cgiSetVariable("SETTINGS_MESSAGE",
2588 cgiText(_("Unable to open cupsd.conf file:")));
2589 cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
2590 }
2591
2592 if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
2593 settings)) != NULL && atoi(val))
2594 cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2595
2596 if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
2597 settings)) != NULL && atoi(val))
2598 cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2599
2600 if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
2601 settings)) != NULL && atoi(val))
2602 cgiSetVariable("REMOTE_ANY", "CHECKED");
2603
2604 if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
2605 settings)) != NULL && atoi(val))
2606 cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2607
2608 if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
2609 settings)) != NULL && atoi(val))
2610 cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2611
2612 #ifdef HAVE_GSSAPI
2613 cgiSetVariable("HAVE_GSSAPI", "1");
2614
2615 if ((val = cupsGetOption("DefaultAuthType", num_settings,
2616 settings)) != NULL && !_cups_strcasecmp(val, "Negotiate"))
2617 cgiSetVariable("KERBEROS", "CHECKED");
2618 else
2619 #endif /* HAVE_GSSAPI */
2620 cgiSetVariable("KERBEROS", "");
2621
2622 if ((val = cupsGetOption("BrowseWebIF", num_settings,
2623 settings)) == NULL)
2624 val = "No";
2625
2626 if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") ||
2627 !_cups_strcasecmp(val, "true"))
2628 cgiSetVariable("BROWSE_WEB_IF", "CHECKED");
2629
2630 if ((val = cupsGetOption("PreserveJobHistory", num_settings,
2631 settings)) == NULL)
2632 val = "Yes";
2633
2634 if (val &&
2635 (!_cups_strcasecmp(val, "0") || !_cups_strcasecmp(val, "no") ||
2636 !_cups_strcasecmp(val, "off") || !_cups_strcasecmp(val, "false") ||
2637 !_cups_strcasecmp(val, "disabled")))
2638 {
2639 cgiSetVariable("PRESERVE_JOB_HISTORY", "0");
2640 cgiSetVariable("PRESERVE_JOB_FILES", "0");
2641 }
2642 else
2643 {
2644 cgiSetVariable("PRESERVE_JOBS", "CHECKED");
2645 cgiSetVariable("PRESERVE_JOB_HISTORY", val);
2646
2647 if ((val = cupsGetOption("PreserveJobFiles", num_settings,
2648 settings)) == NULL)
2649 val = "1d";
2650
2651 cgiSetVariable("PRESERVE_JOB_FILES", val);
2652
2653 }
2654
2655 if ((val = cupsGetOption("MaxClients", num_settings, settings)) == NULL)
2656 val = "100";
2657
2658 cgiSetVariable("MAX_CLIENTS", val);
2659
2660 if ((val = cupsGetOption("MaxJobs", num_settings, settings)) == NULL)
2661 val = "500";
2662
2663 cgiSetVariable("MAX_JOBS", val);
2664
2665 if ((val = cupsGetOption("MaxLogSize", num_settings, settings)) == NULL)
2666 val = "1m";
2667
2668 cgiSetVariable("MAX_LOG_SIZE", val);
2669
2670 cupsFreeOptions(num_settings, settings);
2671
2672 /*
2673 * See if Samba and the Windows drivers are installed...
2674 */
2675
2676 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
2677 datadir = CUPS_DATADIR;
2678
2679 snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir);
2680 if (!access(filename, R_OK))
2681 {
2682 /*
2683 * Found Windows 2000 driver file, see if we have smbclient and
2684 * rpcclient...
2685 */
2686
2687 if (cupsFileFind("smbclient", getenv("PATH"), 1, filename,
2688 sizeof(filename)) &&
2689 cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
2690 sizeof(filename)))
2691 cgiSetVariable("HAVE_SAMBA", "Y");
2692 else
2693 {
2694 if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename,
2695 sizeof(filename)))
2696 fputs("ERROR: smbclient not found!\n", stderr);
2697
2698 if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
2699 sizeof(filename)))
2700 fputs("ERROR: rpcclient not found!\n", stderr);
2701 }
2702 }
2703 else
2704 perror(filename);
2705
2706 /*
2707 * Subscriptions...
2708 */
2709
2710 request = ippNewRequest(IPP_GET_SUBSCRIPTIONS);
2711
2712 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2713 NULL, "ipp://localhost/");
2714
2715 if ((response = cupsDoRequest(http, request, "/")) != NULL)
2716 {
2717 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2718 ippDelete(response);
2719 }
2720
2721 /*
2722 * Finally, show the main menu template...
2723 */
2724
2725 cgiStartHTML(cgiText(_("Administration")));
2726
2727 cgiCopyTemplateLang("admin.tmpl");
2728
2729 cgiEndHTML();
2730 }
2731
2732
2733 /*
2734 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
2735 */
2736
2737 static void
2738 do_set_allowed_users(http_t *http) /* I - HTTP connection */
2739 {
2740 int i; /* Looping var */
2741 ipp_t *request, /* IPP request */
2742 *response; /* IPP response */
2743 char uri[HTTP_MAX_URI]; /* Printer URI */
2744 const char *printer, /* Printer name (purge-jobs) */
2745 *is_class, /* Is a class? */
2746 *users, /* List of users or groups */
2747 *type; /* Allow/deny type */
2748 int num_users; /* Number of users */
2749 char *ptr, /* Pointer into users string */
2750 *end, /* Pointer to end of users string */
2751 quote; /* Quote character */
2752 ipp_attribute_t *attr; /* Attribute */
2753 static const char * const attrs[] = /* Requested attributes */
2754 {
2755 "requesting-user-name-allowed",
2756 "requesting-user-name-denied"
2757 };
2758
2759
2760 is_class = cgiGetVariable("IS_CLASS");
2761 printer = cgiGetVariable("PRINTER_NAME");
2762
2763 if (!printer)
2764 {
2765 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2766 cgiStartHTML(cgiText(_("Set Allowed Users")));
2767 cgiCopyTemplateLang("error.tmpl");
2768 cgiEndHTML();
2769 return;
2770 }
2771
2772 users = cgiGetVariable("users");
2773 type = cgiGetVariable("type");
2774
2775 if (!users || !type ||
2776 (strcmp(type, "requesting-user-name-allowed") &&
2777 strcmp(type, "requesting-user-name-denied")))
2778 {
2779 /*
2780 * Build a Get-Printer-Attributes request, which requires the following
2781 * attributes:
2782 *
2783 * attributes-charset
2784 * attributes-natural-language
2785 * printer-uri
2786 * requested-attributes
2787 */
2788
2789 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
2790
2791 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2792 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2793 printer);
2794 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2795 NULL, uri);
2796
2797 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2798 "requested-attributes",
2799 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
2800
2801 /*
2802 * Do the request and get back a response...
2803 */
2804
2805 if ((response = cupsDoRequest(http, request, "/")) != NULL)
2806 {
2807 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2808
2809 ippDelete(response);
2810 }
2811
2812 cgiStartHTML(cgiText(_("Set Allowed Users")));
2813
2814 if (cupsLastError() == IPP_NOT_AUTHORIZED)
2815 {
2816 puts("Status: 401\n");
2817 exit(0);
2818 }
2819 else if (cupsLastError() > IPP_OK_CONFLICT)
2820 cgiShowIPPError(_("Unable to get printer attributes"));
2821 else
2822 cgiCopyTemplateLang("users.tmpl");
2823
2824 cgiEndHTML();
2825 }
2826 else
2827 {
2828 /*
2829 * Save the changes...
2830 */
2831
2832 for (num_users = 0, ptr = (char *)users; *ptr; num_users ++)
2833 {
2834 /*
2835 * Skip whitespace and commas...
2836 */
2837
2838 while (*ptr == ',' || isspace(*ptr & 255))
2839 ptr ++;
2840
2841 if (!*ptr)
2842 break;
2843
2844 if (*ptr == '\'' || *ptr == '\"')
2845 {
2846 /*
2847 * Scan quoted name...
2848 */
2849
2850 quote = *ptr++;
2851
2852 for (end = ptr; *end; end ++)
2853 if (*end == quote)
2854 break;
2855 }
2856 else
2857 {
2858 /*
2859 * Scan space or comma-delimited name...
2860 */
2861
2862 for (end = ptr; *end; end ++)
2863 if (isspace(*end & 255) || *end == ',')
2864 break;
2865 }
2866
2867 /*
2868 * Advance to the next name...
2869 */
2870
2871 ptr = end;
2872 }
2873
2874 /*
2875 * Build a CUPS-Add-Printer/Class request, which requires the following
2876 * attributes:
2877 *
2878 * attributes-charset
2879 * attributes-natural-language
2880 * printer-uri
2881 * requesting-user-name-{allowed,denied}
2882 */
2883
2884 request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
2885
2886 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2887 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2888 printer);
2889 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2890 NULL, uri);
2891
2892 if (num_users == 0)
2893 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2894 "requesting-user-name-allowed", NULL, "all");
2895 else
2896 {
2897 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2898 type, num_users, NULL, NULL);
2899
2900 for (i = 0, ptr = (char *)users; *ptr; i ++)
2901 {
2902 /*
2903 * Skip whitespace and commas...
2904 */
2905
2906 while (*ptr == ',' || isspace(*ptr & 255))
2907 ptr ++;
2908
2909 if (!*ptr)
2910 break;
2911
2912 if (*ptr == '\'' || *ptr == '\"')
2913 {
2914 /*
2915 * Scan quoted name...
2916 */
2917
2918 quote = *ptr++;
2919
2920 for (end = ptr; *end; end ++)
2921 if (*end == quote)
2922 break;
2923 }
2924 else
2925 {
2926 /*
2927 * Scan space or comma-delimited name...
2928 */
2929
2930 for (end = ptr; *end; end ++)
2931 if (isspace(*end & 255) || *end == ',')
2932 break;
2933 }
2934
2935 /*
2936 * Terminate the name...
2937 */
2938
2939 if (*end)
2940 *end++ = '\0';
2941
2942 /*
2943 * Add the name...
2944 */
2945
2946 attr->values[i].string.text = _cupsStrAlloc(ptr);
2947
2948 /*
2949 * Advance to the next name...
2950 */
2951
2952 ptr = end;
2953 }
2954 }
2955
2956 /*
2957 * Do the request and get back a response...
2958 */
2959
2960 ippDelete(cupsDoRequest(http, request, "/admin/"));
2961
2962 if (cupsLastError() == IPP_NOT_AUTHORIZED)
2963 {
2964 puts("Status: 401\n");
2965 exit(0);
2966 }
2967 else if (cupsLastError() > IPP_OK_CONFLICT)
2968 {
2969 cgiStartHTML(cgiText(_("Set Allowed Users")));
2970 cgiShowIPPError(_("Unable to change printer"));
2971 }
2972 else
2973 {
2974 /*
2975 * Redirect successful updates back to the printer page...
2976 */
2977
2978 char url[1024], /* Printer/class URL */
2979 refresh[1024]; /* Refresh URL */
2980
2981
2982 cgiRewriteURL(uri, url, sizeof(url), NULL);
2983 cgiFormEncode(uri, url, sizeof(uri));
2984 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s",
2985 uri);
2986 cgiSetVariable("refresh_page", refresh);
2987
2988 cgiStartHTML(cgiText(_("Set Allowed Users")));
2989
2990 cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
2991 "printer-modified.tmpl");
2992 }
2993
2994 cgiEndHTML();
2995 }
2996 }
2997
2998
2999 /*
3000 * 'do_set_default()' - Set the server default printer/class.
3001 */
3002
3003 static void
3004 do_set_default(http_t *http) /* I - HTTP connection */
3005 {
3006 const char *title; /* Page title */
3007 ipp_t *request; /* IPP request */
3008 char uri[HTTP_MAX_URI]; /* Printer URI */
3009 const char *printer, /* Printer name (purge-jobs) */
3010 *is_class; /* Is a class? */
3011
3012
3013 is_class = cgiGetVariable("IS_CLASS");
3014 printer = cgiGetVariable("PRINTER_NAME");
3015 title = cgiText(_("Set As Server Default"));
3016
3017 if (!printer)
3018 {
3019 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3020 cgiStartHTML(title);
3021 cgiCopyTemplateLang("error.tmpl");
3022 cgiEndHTML();
3023 return;
3024 }
3025
3026 /*
3027 * Build a printer request, which requires the following
3028 * attributes:
3029 *
3030 * attributes-charset
3031 * attributes-natural-language
3032 * printer-uri
3033 */
3034
3035 request = ippNewRequest(CUPS_SET_DEFAULT);
3036
3037 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3038 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3039 printer);
3040 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3041 NULL, uri);
3042
3043 /*
3044 * Do the request and get back a response...
3045 */
3046
3047 ippDelete(cupsDoRequest(http, request, "/admin/"));
3048
3049 if (cupsLastError() == IPP_NOT_AUTHORIZED)
3050 {
3051 puts("Status: 401\n");
3052 exit(0);
3053 }
3054 else if (cupsLastError() > IPP_OK_CONFLICT)
3055 {
3056 cgiStartHTML(title);
3057 cgiShowIPPError(_("Unable to set server default"));
3058 }
3059 else
3060 {
3061 /*
3062 * Redirect successful updates back to the printer page...
3063 */
3064
3065 char url[1024], /* Printer/class URL */
3066 refresh[1024]; /* Refresh URL */
3067
3068
3069 cgiRewriteURL(uri, url, sizeof(url), NULL);
3070 cgiFormEncode(uri, url, sizeof(uri));
3071 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
3072 cgiSetVariable("refresh_page", refresh);
3073
3074 cgiStartHTML(title);
3075 cgiCopyTemplateLang("printer-default.tmpl");
3076 }
3077
3078 cgiEndHTML();
3079 }
3080
3081
3082 /*
3083 * 'do_set_options()' - Configure the default options for a queue.
3084 */
3085
3086 static void
3087 do_set_options(http_t *http, /* I - HTTP connection */
3088 int is_class) /* I - Set options for class? */
3089 {
3090 int i, j, k, m; /* Looping vars */
3091 int have_options; /* Have options? */
3092 ipp_t *request, /* IPP request */
3093 *response; /* IPP response */
3094 ipp_attribute_t *attr; /* IPP attribute */
3095 char uri[HTTP_MAX_URI]; /* Job URI */
3096 const char *var; /* Variable value */
3097 const char *printer; /* Printer printer name */
3098 const char *filename; /* PPD filename */
3099 char tempfile[1024]; /* Temporary filename */
3100 cups_file_t *in, /* Input file */
3101 *out; /* Output file */
3102 char line[1024], /* Line from PPD file */
3103 value[1024], /* Option value */
3104 keyword[1024], /* Keyword from Default line */
3105 *keyptr; /* Pointer into keyword... */
3106 ppd_file_t *ppd; /* PPD file */
3107 ppd_group_t *group; /* Option group */
3108 ppd_option_t *option; /* Option */
3109 ppd_coption_t *coption; /* Custom option */
3110 ppd_cparam_t *cparam; /* Custom parameter */
3111 ppd_attr_t *ppdattr; /* PPD attribute */
3112 const char *title; /* Page title */
3113
3114
3115 title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options"));
3116
3117 fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http,
3118 is_class);
3119
3120 /*
3121 * Get the printer name...
3122 */
3123
3124 if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
3125 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3126 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3127 printer);
3128 else
3129 {
3130 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3131 cgiStartHTML(title);
3132 cgiCopyTemplateLang("error.tmpl");
3133 cgiEndHTML();
3134 return;
3135 }
3136
3137 fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
3138
3139 /*
3140 * If the user clicks on the Auto-Configure button, send an AutoConfigure
3141 * command file to the printer...
3142 */
3143
3144 if (cgiGetVariable("AUTOCONFIGURE"))
3145 {
3146 cgiPrintCommand(http, printer, "AutoConfigure", "Set Default Options");
3147 return;
3148 }
3149
3150 /*
3151 * Get the PPD file...
3152 */
3153
3154 if (is_class)
3155 filename = NULL;
3156 else
3157 filename = cupsGetPPD2(http, printer);
3158
3159 if (filename)
3160 {
3161 fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
3162
3163 if ((ppd = ppdOpenFile(filename)) == NULL)
3164 {
3165 cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i)));
3166 cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file")));
3167 cgiStartHTML(title);
3168 cgiCopyTemplateLang("error.tmpl");
3169 cgiEndHTML();
3170 return;
3171 }
3172 }
3173 else
3174 {
3175 fputs("DEBUG: No PPD file\n", stderr);
3176 ppd = NULL;
3177 }
3178
3179 if (cgiGetVariable("job_sheets_start") != NULL ||
3180 cgiGetVariable("job_sheets_end") != NULL)
3181 have_options = 1;
3182 else
3183 have_options = 0;
3184
3185 if (ppd)
3186 {
3187 ppdMarkDefaults(ppd);
3188
3189 for (option = ppdFirstOption(ppd);
3190 option;
3191 option = ppdNextOption(ppd))
3192 {
3193 if ((var = cgiGetVariable(option->keyword)) != NULL)
3194 {
3195 have_options = 1;
3196 ppdMarkOption(ppd, option->keyword, var);
3197 fprintf(stderr, "DEBUG: Set %s to %s...\n", option->keyword, var);
3198 }
3199 else
3200 fprintf(stderr, "DEBUG: Didn't find %s...\n", option->keyword);
3201 }
3202 }
3203
3204 if (!have_options || ppdConflicts(ppd))
3205 {
3206 /*
3207 * Show the options to the user...
3208 */
3209
3210 fputs("DEBUG: Showing options...\n", stderr);
3211
3212 /*
3213 * Show auto-configure button if supported...
3214 */
3215
3216 if (ppd)
3217 {
3218 if (ppd->num_filters == 0 ||
3219 ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
3220 ppdattr->value && strstr(ppdattr->value, "AutoConfigure")))
3221 cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
3222 else
3223 {
3224 for (i = 0; i < ppd->num_filters; i ++)
3225 if (!strncmp(ppd->filters[i], "application/vnd.cups-postscript", 31))
3226 {
3227 cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
3228 break;
3229 }
3230 }
3231 }
3232
3233 /*
3234 * Get the printer attributes...
3235 */
3236
3237 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
3238
3239 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3240 "localhost", 0, "/printers/%s", printer);
3241 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3242 NULL, uri);
3243
3244 response = cupsDoRequest(http, request, "/");
3245
3246 /*
3247 * List the groups used as "tabs"...
3248 */
3249
3250 i = 0;
3251
3252 if (ppd)
3253 {
3254 for (group = ppd->groups;
3255 i < ppd->num_groups;
3256 i ++, group ++)
3257 {
3258 cgiSetArray("GROUP_ID", i, group->name);
3259
3260 if (!strcmp(group->name, "InstallableOptions"))
3261 cgiSetArray("GROUP", i, cgiText(_("Options Installed")));
3262 else
3263 cgiSetArray("GROUP", i, group->text);
3264 }
3265 }
3266
3267 if (ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO))
3268 {
3269 cgiSetArray("GROUP_ID", i, "CUPS_BANNERS");
3270 cgiSetArray("GROUP", i ++, cgiText(_("Banners")));
3271 }
3272
3273 if (ippFindAttribute(response, "printer-error-policy-supported",
3274 IPP_TAG_ZERO) ||
3275 ippFindAttribute(response, "printer-op-policy-supported",
3276 IPP_TAG_ZERO))
3277 {
3278 cgiSetArray("GROUP_ID", i, "CUPS_POLICIES");
3279 cgiSetArray("GROUP", i ++, cgiText(_("Policies")));
3280 }
3281
3282 if ((attr = ippFindAttribute(response, "port-monitor-supported",
3283 IPP_TAG_NAME)) != NULL && attr->num_values > 1)
3284 {
3285 cgiSetArray("GROUP_ID", i, "CUPS_PORT_MONITOR");
3286 cgiSetArray("GROUP", i, cgiText(_("Port Monitor")));
3287 }
3288
3289 cgiStartHTML(cgiText(_("Set Printer Options")));
3290 cgiCopyTemplateLang("set-printer-options-header.tmpl");
3291
3292 if (ppd)
3293 {
3294 ppdLocalize(ppd);
3295
3296 if (ppdConflicts(ppd))
3297 {
3298 for (i = ppd->num_groups, k = 0, group = ppd->groups;
3299 i > 0;
3300 i --, group ++)
3301 for (j = group->num_options, option = group->options;
3302 j > 0;
3303 j --, option ++)
3304 if (option->conflicted)
3305 {
3306 cgiSetArray("ckeyword", k, option->keyword);
3307 cgiSetArray("ckeytext", k, option->text);
3308
3309 for (m = 0; m < option->num_choices; m ++)
3310 {
3311 if (option->choices[m].marked)
3312 {
3313 cgiSetArray("cchoice", k, option->choices[m].text);
3314 break;
3315 }
3316 }
3317
3318 k ++;
3319 }
3320
3321 cgiCopyTemplateLang("option-conflict.tmpl");
3322 }
3323
3324 for (i = ppd->num_groups, group = ppd->groups;
3325 i > 0;
3326 i --, group ++)
3327 {
3328 for (j = group->num_options, option = group->options;
3329 j > 0;
3330 j --, option ++)
3331 {
3332 if (!strcmp(option->keyword, "PageRegion"))
3333 continue;
3334
3335 if (option->num_choices > 1)
3336 break;
3337 }
3338
3339 if (j == 0)
3340 continue;
3341
3342 cgiSetVariable("GROUP_ID", group->name);
3343
3344 if (!strcmp(group->name, "InstallableOptions"))
3345 cgiSetVariable("GROUP", cgiText(_("Options Installed")));
3346 else
3347 cgiSetVariable("GROUP", group->text);
3348
3349 cgiCopyTemplateLang("option-header.tmpl");
3350
3351 for (j = group->num_options, option = group->options;
3352 j > 0;
3353 j --, option ++)
3354 {
3355 if (!strcmp(option->keyword, "PageRegion") || option->num_choices < 2)
3356 continue;
3357
3358 cgiSetVariable("KEYWORD", option->keyword);
3359 cgiSetVariable("KEYTEXT", option->text);
3360
3361 if (option->conflicted)
3362 cgiSetVariable("CONFLICTED", "1");
3363 else
3364 cgiSetVariable("CONFLICTED", "0");
3365
3366 cgiSetSize("CHOICES", 0);
3367 cgiSetSize("TEXT", 0);
3368 for (k = 0, m = 0; k < option->num_choices; k ++)
3369 {
3370 cgiSetArray("CHOICES", m, option->choices[k].choice);
3371 cgiSetArray("TEXT", m, option->choices[k].text);
3372
3373 m ++;
3374
3375 if (option->choices[k].marked)
3376 cgiSetVariable("DEFCHOICE", option->choices[k].choice);
3377 }
3378
3379 cgiSetSize("PARAMS", 0);
3380 cgiSetSize("PARAMTEXT", 0);
3381 cgiSetSize("PARAMVALUE", 0);
3382 cgiSetSize("INPUTTYPE", 0);
3383
3384 if ((coption = ppdFindCustomOption(ppd, option->keyword)))
3385 {
3386 const char *units = NULL; /* Units value, if any */
3387
3388 cgiSetVariable("ISCUSTOM", "1");
3389
3390 for (cparam = ppdFirstCustomParam(coption), m = 0;
3391 cparam;
3392 cparam = ppdNextCustomParam(coption), m ++)
3393 {
3394 if (!_cups_strcasecmp(option->keyword, "PageSize") &&
3395 _cups_strcasecmp(cparam->name, "Width") &&
3396 _cups_strcasecmp(cparam->name, "Height"))
3397 {
3398 m --;
3399 continue;
3400 }
3401
3402 cgiSetArray("PARAMS", m, cparam->name);
3403 cgiSetArray("PARAMTEXT", m, cparam->text);
3404 cgiSetArray("INPUTTYPE", m, "text");
3405
3406 switch (cparam->type)
3407 {
3408 case PPD_CUSTOM_POINTS :
3409 if (!_cups_strncasecmp(option->defchoice, "Custom.", 7))
3410 {
3411 units = option->defchoice + strlen(option->defchoice) - 2;
3412
3413 if (strcmp(units, "mm") && strcmp(units, "cm") &&
3414 strcmp(units, "in") && strcmp(units, "ft"))
3415 {
3416 if (units[1] == 'm')
3417 units ++;
3418 else
3419 units = "pt";
3420 }
3421 }
3422 else
3423 units = "pt";
3424
3425 if (!strcmp(units, "mm"))
3426 snprintf(value, sizeof(value), "%g",
3427 cparam->current.custom_points / 72.0 * 25.4);
3428 else if (!strcmp(units, "cm"))
3429 snprintf(value, sizeof(value), "%g",
3430 cparam->current.custom_points / 72.0 * 2.54);
3431 else if (!strcmp(units, "in"))
3432 snprintf(value, sizeof(value), "%g",
3433 cparam->current.custom_points / 72.0);
3434 else if (!strcmp(units, "ft"))
3435 snprintf(value, sizeof(value), "%g",
3436 cparam->current.custom_points / 72.0 / 12.0);
3437 else if (!strcmp(units, "m"))
3438 snprintf(value, sizeof(value), "%g",
3439 cparam->current.custom_points / 72.0 * 0.0254);
3440 else
3441 snprintf(value, sizeof(value), "%g",
3442 cparam->current.custom_points);
3443 cgiSetArray("PARAMVALUE", m, value);
3444 break;
3445
3446 case PPD_CUSTOM_CURVE :
3447 case PPD_CUSTOM_INVCURVE :
3448 case PPD_CUSTOM_REAL :
3449 snprintf(value, sizeof(value), "%g",
3450 cparam->current.custom_real);
3451 cgiSetArray("PARAMVALUE", m, value);
3452 break;
3453
3454 case PPD_CUSTOM_INT:
3455 snprintf(value, sizeof(value), "%d",
3456 cparam->current.custom_int);
3457 cgiSetArray("PARAMVALUE", m, value);
3458 break;
3459
3460 case PPD_CUSTOM_PASSCODE:
3461 case PPD_CUSTOM_PASSWORD:
3462 if (cparam->current.custom_password)
3463 cgiSetArray("PARAMVALUE", m,
3464 cparam->current.custom_password);
3465 else
3466 cgiSetArray("PARAMVALUE", m, "");
3467 cgiSetArray("INPUTTYPE", m, "password");
3468 break;
3469
3470 case PPD_CUSTOM_STRING:
3471 if (cparam->current.custom_string)
3472 cgiSetArray("PARAMVALUE", m,
3473 cparam->current.custom_string);
3474 else
3475 cgiSetArray("PARAMVALUE", m, "");
3476 break;
3477 }
3478 }
3479
3480 if (units)
3481 {
3482 cgiSetArray("PARAMS", m, "Units");
3483 cgiSetArray("PARAMTEXT", m, cgiText(_("Units")));
3484 cgiSetArray("PARAMVALUE", m, units);
3485 }
3486 }
3487 else
3488 cgiSetVariable("ISCUSTOM", "0");
3489
3490 switch (option->ui)
3491 {
3492 case PPD_UI_BOOLEAN :
3493 cgiCopyTemplateLang("option-boolean.tmpl");
3494 break;
3495 case PPD_UI_PICKONE :
3496 cgiCopyTemplateLang("option-pickone.tmpl");
3497 break;
3498 case PPD_UI_PICKMANY :
3499 cgiCopyTemplateLang("option-pickmany.tmpl");
3500 break;
3501 }
3502 }
3503
3504 cgiCopyTemplateLang("option-trailer.tmpl");
3505 }
3506 }
3507
3508 if ((attr = ippFindAttribute(response, "job-sheets-supported",
3509 IPP_TAG_ZERO)) != NULL)
3510 {
3511 /*
3512 * Add the job sheets options...
3513 */
3514
3515 cgiSetVariable("GROUP_ID", "CUPS_BANNERS");
3516 cgiSetVariable("GROUP", cgiText(_("Banners")));
3517 cgiCopyTemplateLang("option-header.tmpl");
3518
3519 cgiSetSize("CHOICES", attr->num_values);
3520 cgiSetSize("TEXT", attr->num_values);
3521 for (k = 0; k < attr->num_values; k ++)
3522 {
3523 cgiSetArray("CHOICES", k, attr->values[k].string.text);
3524 cgiSetArray("TEXT", k, attr->values[k].string.text);
3525 }
3526
3527 attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
3528
3529 cgiSetVariable("KEYWORD", "job_sheets_start");
3530 cgiSetVariable("KEYTEXT",
3531 /* TRANSLATORS: Banner/cover sheet before the print job. */
3532 cgiText(_("Starting Banner")));
3533 cgiSetVariable("DEFCHOICE", attr != NULL ?
3534 attr->values[0].string.text : "");
3535
3536 cgiCopyTemplateLang("option-pickone.tmpl");
3537
3538 cgiSetVariable("KEYWORD", "job_sheets_end");
3539 cgiSetVariable("KEYTEXT",
3540 /* TRANSLATORS: Banner/cover sheet after the print job. */
3541 cgiText(_("Ending Banner")));
3542 cgiSetVariable("DEFCHOICE", attr != NULL && attr->num_values > 1 ?
3543 attr->values[1].string.text : "");
3544
3545 cgiCopyTemplateLang("option-pickone.tmpl");
3546
3547 cgiCopyTemplateLang("option-trailer.tmpl");
3548 }
3549
3550 if (ippFindAttribute(response, "printer-error-policy-supported",
3551 IPP_TAG_ZERO) ||
3552 ippFindAttribute(response, "printer-op-policy-supported",
3553 IPP_TAG_ZERO))
3554 {
3555 /*
3556 * Add the error and operation policy options...
3557 */
3558
3559 cgiSetVariable("GROUP_ID", "CUPS_POLICIES");
3560 cgiSetVariable("GROUP", cgiText(_("Policies")));
3561 cgiCopyTemplateLang("option-header.tmpl");
3562
3563 /*
3564 * Error policy...
3565 */
3566
3567 attr = ippFindAttribute(response, "printer-error-policy-supported",
3568 IPP_TAG_ZERO);
3569
3570 if (attr)
3571 {
3572 cgiSetSize("CHOICES", attr->num_values);
3573 cgiSetSize("TEXT", attr->num_values);
3574 for (k = 0; k < attr->num_values; k ++)
3575 {
3576 cgiSetArray("CHOICES", k, attr->values[k].string.text);
3577 cgiSetArray("TEXT", k, attr->values[k].string.text);
3578 }
3579
3580 attr = ippFindAttribute(response, "printer-error-policy",
3581 IPP_TAG_ZERO);
3582
3583 cgiSetVariable("KEYWORD", "printer_error_policy");
3584 cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
3585 cgiSetVariable("DEFCHOICE", attr == NULL ?
3586 "" : attr->values[0].string.text);
3587 }
3588
3589 cgiCopyTemplateLang("option-pickone.tmpl");
3590
3591 /*
3592 * Operation policy...
3593 */
3594
3595 attr = ippFindAttribute(response, "printer-op-policy-supported",
3596 IPP_TAG_ZERO);
3597
3598 if (attr)
3599 {
3600 cgiSetSize("CHOICES", attr->num_values);
3601 cgiSetSize("TEXT", attr->num_values);
3602 for (k = 0; k < attr->num_values; k ++)
3603 {
3604 cgiSetArray("CHOICES", k, attr->values[k].string.text);
3605 cgiSetArray("TEXT", k, attr->values[k].string.text);
3606 }
3607
3608 attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
3609
3610 cgiSetVariable("KEYWORD", "printer_op_policy");
3611 cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
3612 cgiSetVariable("DEFCHOICE", attr == NULL ?
3613 "" : attr->values[0].string.text);
3614
3615 cgiCopyTemplateLang("option-pickone.tmpl");
3616 }
3617
3618 cgiCopyTemplateLang("option-trailer.tmpl");
3619 }
3620
3621 /*
3622 * Binary protocol support...
3623 */
3624
3625 if ((attr = ippFindAttribute(response, "port-monitor-supported",
3626 IPP_TAG_NAME)) != NULL && attr->num_values > 1)
3627 {
3628 cgiSetVariable("GROUP_ID", "CUPS_PORT_MONITOR");
3629 cgiSetVariable("GROUP", cgiText(_("Port Monitor")));
3630
3631 cgiSetSize("CHOICES", attr->num_values);
3632 cgiSetSize("TEXT", attr->num_values);
3633
3634 for (i = 0; i < attr->num_values; i ++)
3635 {
3636 cgiSetArray("CHOICES", i, attr->values[i].string.text);
3637 cgiSetArray("TEXT", i, attr->values[i].string.text);
3638 }
3639
3640 attr = ippFindAttribute(response, "port-monitor", IPP_TAG_NAME);
3641 cgiSetVariable("KEYWORD", "port_monitor");
3642 cgiSetVariable("KEYTEXT", cgiText(_("Port Monitor")));
3643 cgiSetVariable("DEFCHOICE", attr ? attr->values[0].string.text : "none");
3644
3645 cgiCopyTemplateLang("option-header.tmpl");
3646 cgiCopyTemplateLang("option-pickone.tmpl");
3647 cgiCopyTemplateLang("option-trailer.tmpl");
3648 }
3649
3650 cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
3651 cgiEndHTML();
3652
3653 ippDelete(response);
3654 }
3655 else
3656 {
3657 /*
3658 * Set default options...
3659 */
3660
3661 fputs("DEBUG: Setting options...\n", stderr);
3662
3663 if (filename)
3664 {
3665 out = cupsTempFile2(tempfile, sizeof(tempfile));
3666 in = cupsFileOpen(filename, "r");
3667
3668 if (!in || !out)
3669 {
3670 cgiSetVariable("ERROR", strerror(errno));
3671 cgiStartHTML(cgiText(_("Set Printer Options")));
3672 cgiCopyTemplateLang("error.tmpl");
3673 cgiEndHTML();
3674
3675 if (in)
3676 cupsFileClose(in);
3677
3678 if (out)
3679 {
3680 cupsFileClose(out);
3681 unlink(tempfile);
3682 }
3683
3684 unlink(filename);
3685 return;
3686 }
3687
3688 while (cupsFileGets(in, line, sizeof(line)))
3689 {
3690 if (!strncmp(line, "*cupsProtocol:", 14))
3691 continue;
3692 else if (strncmp(line, "*Default", 8))
3693 cupsFilePrintf(out, "%s\n", line);
3694 else
3695 {
3696 /*
3697 * Get default option name...
3698 */
3699
3700 strlcpy(keyword, line + 8, sizeof(keyword));
3701
3702 for (keyptr = keyword; *keyptr; keyptr ++)
3703 if (*keyptr == ':' || isspace(*keyptr & 255))
3704 break;
3705
3706 *keyptr = '\0';
3707
3708 if (!strcmp(keyword, "PageRegion") ||
3709 !strcmp(keyword, "PaperDimension") ||
3710 !strcmp(keyword, "ImageableArea"))
3711 var = get_option_value(ppd, "PageSize", value, sizeof(value));
3712 else
3713 var = get_option_value(ppd, keyword, value, sizeof(value));
3714
3715 if (!var)
3716 cupsFilePrintf(out, "%s\n", line);
3717 else
3718 cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
3719 }
3720 }
3721
3722 cupsFileClose(in);
3723 cupsFileClose(out);
3724 }
3725 else
3726 {
3727 /*
3728 * Make sure temporary filename is cleared when there is no PPD...
3729 */
3730
3731 tempfile[0] = '\0';
3732 }
3733
3734 /*
3735 * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the
3736 * following attributes:
3737 *
3738 * attributes-charset
3739 * attributes-natural-language
3740 * printer-uri
3741 * job-sheets-default
3742 * printer-error-policy
3743 * printer-op-policy
3744 * [ppd file]
3745 */
3746
3747 request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS :
3748 CUPS_ADD_MODIFY_PRINTER);
3749
3750 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3751 NULL, uri);
3752
3753 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3754 "job-sheets-default", 2, NULL, NULL);
3755 attr->values[0].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_start"));
3756 attr->values[1].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_end"));
3757
3758 if ((var = cgiGetVariable("printer_error_policy")) != NULL)
3759 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3760 "printer-error-policy", NULL, var);
3761
3762 if ((var = cgiGetVariable("printer_op_policy")) != NULL)
3763 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3764 "printer-op-policy", NULL, var);
3765
3766 if ((var = cgiGetVariable("port_monitor")) != NULL)
3767 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3768 "port-monitor", NULL, var);
3769
3770 /*
3771 * Do the request and get back a response...
3772 */
3773
3774 if (filename)
3775 ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile));
3776 else
3777 ippDelete(cupsDoRequest(http, request, "/admin/"));
3778
3779 if (cupsLastError() == IPP_NOT_AUTHORIZED)
3780 {
3781 puts("Status: 401\n");
3782 exit(0);
3783 }
3784 else if (cupsLastError() > IPP_OK_CONFLICT)
3785 {
3786 cgiStartHTML(title);
3787 cgiShowIPPError(_("Unable to set options"));
3788 }
3789 else
3790 {
3791 /*
3792 * Redirect successful updates back to the printer page...
3793 */
3794
3795 char refresh[1024]; /* Refresh URL */
3796
3797
3798 cgiFormEncode(uri, printer, sizeof(uri));
3799 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s",
3800 is_class ? "classes" : "printers", uri);
3801 cgiSetVariable("refresh_page", refresh);
3802
3803 cgiStartHTML(title);
3804
3805 cgiCopyTemplateLang("printer-configured.tmpl");
3806 }
3807
3808 cgiEndHTML();
3809
3810 if (filename)
3811 unlink(tempfile);
3812 }
3813
3814 if (filename)
3815 unlink(filename);
3816 }
3817
3818
3819 /*
3820 * 'do_set_sharing()' - Set printer-is-shared value.
3821 */
3822
3823 static void
3824 do_set_sharing(http_t *http) /* I - HTTP connection */
3825 {
3826 ipp_t *request, /* IPP request */
3827 *response; /* IPP response */
3828 char uri[HTTP_MAX_URI]; /* Printer URI */
3829 const char *printer, /* Printer name */
3830 *is_class, /* Is a class? */
3831 *shared; /* Sharing value */
3832
3833
3834 is_class = cgiGetVariable("IS_CLASS");
3835 printer = cgiGetVariable("PRINTER_NAME");
3836 shared = cgiGetVariable("SHARED");
3837
3838 if (!printer || !shared)
3839 {
3840 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3841 cgiStartHTML(cgiText(_("Set Publishing")));
3842 cgiCopyTemplateLang("error.tmpl");
3843 cgiEndHTML();
3844 return;
3845 }
3846
3847 /*
3848 * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the
3849 * following attributes:
3850 *
3851 * attributes-charset
3852 * attributes-natural-language
3853 * printer-uri
3854 * printer-is-shared
3855 */
3856
3857 request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
3858
3859 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3860 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3861 printer);
3862 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3863 NULL, uri);
3864
3865 ippAddBoolean(request, IPP_TAG_OPERATION, "printer-is-shared", (char)atoi(shared));
3866
3867 /*
3868 * Do the request and get back a response...
3869 */
3870
3871 if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
3872 {
3873 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
3874
3875 ippDelete(response);
3876 }
3877
3878 if (cupsLastError() == IPP_NOT_AUTHORIZED)
3879 {
3880 puts("Status: 401\n");
3881 exit(0);
3882 }
3883 else if (cupsLastError() > IPP_OK_CONFLICT)
3884 {
3885 cgiStartHTML(cgiText(_("Set Publishing")));
3886 cgiShowIPPError(_("Unable to change printer-is-shared attribute"));
3887 }
3888 else
3889 {
3890 /*
3891 * Redirect successful updates back to the printer page...
3892 */
3893
3894 char url[1024], /* Printer/class URL */
3895 refresh[1024]; /* Refresh URL */
3896
3897
3898 cgiRewriteURL(uri, url, sizeof(url), NULL);
3899 cgiFormEncode(uri, url, sizeof(uri));
3900 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
3901 cgiSetVariable("refresh_page", refresh);
3902
3903 cgiStartHTML(cgiText(_("Set Publishing")));
3904 cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
3905 "printer-modified.tmpl");
3906 }
3907
3908 cgiEndHTML();
3909 }
3910
3911
3912 /*
3913 * 'get_option_value()' - Return the value of an option.
3914 *
3915 * This function also handles generation of custom option values.
3916 */
3917
3918 static char * /* O - Value string or NULL on error */
3919 get_option_value(
3920 ppd_file_t *ppd, /* I - PPD file */
3921 const char *name, /* I - Option name */
3922 char *buffer, /* I - String buffer */
3923 size_t bufsize) /* I - Size of buffer */
3924 {
3925 char *bufptr, /* Pointer into buffer */
3926 *bufend; /* End of buffer */
3927 ppd_coption_t *coption; /* Custom option */
3928 ppd_cparam_t *cparam; /* Current custom parameter */
3929 char keyword[256]; /* Parameter name */
3930 const char *val, /* Parameter value */
3931 *uval; /* Units value */
3932 long integer; /* Integer value */
3933 double number, /* Number value */
3934 number_points; /* Number in points */
3935
3936
3937 /*
3938 * See if we have a custom option choice...
3939 */
3940
3941 if ((val = cgiGetVariable(name)) == NULL)
3942 {
3943 /*
3944 * Option not found!
3945 */
3946
3947 return (NULL);
3948 }
3949 else if (_cups_strcasecmp(val, "Custom") ||
3950 (coption = ppdFindCustomOption(ppd, name)) == NULL)
3951 {
3952 /*
3953 * Not a custom choice...
3954 */
3955
3956 strlcpy(buffer, val, bufsize);
3957 return (buffer);
3958 }
3959
3960 /*
3961 * OK, we have a custom option choice, format it...
3962 */
3963
3964 *buffer = '\0';
3965
3966 if (!strcmp(coption->keyword, "PageSize"))
3967 {
3968 const char *lval; /* Length string value */
3969 double width, /* Width value */
3970 width_points, /* Width in points */
3971 length, /* Length value */
3972 length_points; /* Length in points */
3973
3974
3975 val = cgiGetVariable("PageSize.Width");
3976 lval = cgiGetVariable("PageSize.Height");
3977 uval = cgiGetVariable("PageSize.Units");
3978
3979 if (!val || !lval || !uval ||
3980 (width = strtod(val, NULL)) == 0.0 ||
3981 (length = strtod(lval, NULL)) == 0.0 ||
3982 (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
3983 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
3984 return (NULL);
3985
3986 width_points = get_points(width, uval);
3987 length_points = get_points(length, uval);
3988
3989 if (width_points < ppd->custom_min[0] ||
3990 width_points > ppd->custom_max[0] ||
3991 length_points < ppd->custom_min[1] ||
3992 length_points > ppd->custom_max[1])
3993 return (NULL);
3994
3995 snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval);
3996 }
3997 else if (cupsArrayCount(coption->params) == 1)
3998 {
3999 cparam = ppdFirstCustomParam(coption);
4000 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name);
4001
4002 if ((val = cgiGetVariable(keyword)) == NULL)
4003 return (NULL);
4004
4005 switch (cparam->type)
4006 {
4007 case PPD_CUSTOM_CURVE :
4008 case PPD_CUSTOM_INVCURVE :
4009 case PPD_CUSTOM_REAL :
4010 if ((number = strtod(val, NULL)) == 0.0 ||
4011 number < cparam->minimum.custom_real ||
4012 number > cparam->maximum.custom_real)
4013 return (NULL);
4014
4015 snprintf(buffer, bufsize, "Custom.%g", number);
4016 break;
4017
4018 case PPD_CUSTOM_INT :
4019 if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
4020 integer == LONG_MAX ||
4021 integer < cparam->minimum.custom_int ||
4022 integer > cparam->maximum.custom_int)
4023 return (NULL);
4024
4025 snprintf(buffer, bufsize, "Custom.%ld", integer);
4026 break;
4027
4028 case PPD_CUSTOM_POINTS :
4029 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
4030
4031 if ((number = strtod(val, NULL)) == 0.0 ||
4032 (uval = cgiGetVariable(keyword)) == NULL ||
4033 (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
4034 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
4035 return (NULL);
4036
4037 number_points = get_points(number, uval);
4038 if (number_points < cparam->minimum.custom_points ||
4039 number_points > cparam->maximum.custom_points)
4040 return (NULL);
4041
4042 snprintf(buffer, bufsize, "Custom.%g%s", number, uval);
4043 break;
4044
4045 case PPD_CUSTOM_PASSCODE :
4046 for (uval = val; *uval; uval ++)
4047 if (!isdigit(*uval & 255))
4048 return (NULL);
4049
4050 case PPD_CUSTOM_PASSWORD :
4051 case PPD_CUSTOM_STRING :
4052 integer = (long)strlen(val);
4053 if (integer < cparam->minimum.custom_string ||
4054 integer > cparam->maximum.custom_string)
4055 return (NULL);
4056
4057 snprintf(buffer, bufsize, "Custom.%s", val);
4058 break;
4059 }
4060 }
4061 else
4062 {
4063 const char *prefix = "{"; /* Prefix string */
4064
4065
4066 bufptr = buffer;
4067 bufend = buffer + bufsize;
4068
4069 for (cparam = ppdFirstCustomParam(coption);
4070 cparam;
4071 cparam = ppdNextCustomParam(coption))
4072 {
4073 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword,
4074 cparam->name);
4075
4076 if ((val = cgiGetVariable(keyword)) == NULL)
4077 return (NULL);
4078
4079 snprintf(bufptr, (size_t)(bufend - bufptr), "%s%s=", prefix, cparam->name);
4080 bufptr += strlen(bufptr);
4081 prefix = " ";
4082
4083 switch (cparam->type)
4084 {
4085 case PPD_CUSTOM_CURVE :
4086 case PPD_CUSTOM_INVCURVE :
4087 case PPD_CUSTOM_REAL :
4088 if ((number = strtod(val, NULL)) == 0.0 ||
4089 number < cparam->minimum.custom_real ||
4090 number > cparam->maximum.custom_real)
4091 return (NULL);
4092
4093 snprintf(bufptr, (size_t)(bufend - bufptr), "%g", number);
4094 break;
4095
4096 case PPD_CUSTOM_INT :
4097 if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
4098 integer == LONG_MAX ||
4099 integer < cparam->minimum.custom_int ||
4100 integer > cparam->maximum.custom_int)
4101 return (NULL);
4102
4103 snprintf(bufptr, (size_t)(bufend - bufptr), "%ld", integer);
4104 break;
4105
4106 case PPD_CUSTOM_POINTS :
4107 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
4108
4109 if ((number = strtod(val, NULL)) == 0.0 ||
4110 (uval = cgiGetVariable(keyword)) == NULL ||
4111 (strcmp(uval, "pt") && strcmp(uval, "in") &&
4112 strcmp(uval, "ft") && strcmp(uval, "cm") &&
4113 strcmp(uval, "mm") && strcmp(uval, "m")))
4114 return (NULL);
4115
4116 number_points = get_points(number, uval);
4117 if (number_points < cparam->minimum.custom_points ||
4118 number_points > cparam->maximum.custom_points)
4119 return (NULL);
4120
4121 snprintf(bufptr, (size_t)(bufend - bufptr), "%g%s", number, uval);
4122 break;
4123
4124 case PPD_CUSTOM_PASSCODE :
4125 for (uval = val; *uval; uval ++)
4126 if (!isdigit(*uval & 255))
4127 return (NULL);
4128
4129 case PPD_CUSTOM_PASSWORD :
4130 case PPD_CUSTOM_STRING :
4131 integer = (long)strlen(val);
4132 if (integer < cparam->minimum.custom_string ||
4133 integer > cparam->maximum.custom_string)
4134 return (NULL);
4135
4136 if ((bufptr + 2) > bufend)
4137 return (NULL);
4138
4139 bufend --;
4140 *bufptr++ = '\"';
4141
4142 while (*val && bufptr < bufend)
4143 {
4144 if (*val == '\\' || *val == '\"')
4145 {
4146 if ((bufptr + 1) >= bufend)
4147 return (NULL);
4148
4149 *bufptr++ = '\\';
4150 }
4151
4152 *bufptr++ = *val++;
4153 }
4154
4155 if (bufptr >= bufend)
4156 return (NULL);
4157
4158 *bufptr++ = '\"';
4159 *bufptr = '\0';
4160 bufend ++;
4161 break;
4162 }
4163
4164 bufptr += strlen(bufptr);
4165 }
4166
4167 if (bufptr == buffer || (bufend - bufptr) < 2)
4168 return (NULL);
4169
4170 memcpy(bufptr, "}", 2);
4171 }
4172
4173 return (buffer);
4174 }
4175
4176
4177 /*
4178 * 'get_points()' - Get a value in points.
4179 */
4180
4181 static double /* O - Number in points */
4182 get_points(double number, /* I - Original number */
4183 const char *uval) /* I - Units */
4184 {
4185 if (!strcmp(uval, "mm")) /* Millimeters */
4186 return (number * 72.0 / 25.4);
4187 else if (!strcmp(uval, "cm")) /* Centimeters */
4188 return (number * 72.0 / 2.54);
4189 else if (!strcmp(uval, "in")) /* Inches */
4190 return (number * 72.0);
4191 else if (!strcmp(uval, "ft")) /* Feet */
4192 return (number * 72.0 * 12.0);
4193 else if (!strcmp(uval, "m")) /* Meters */
4194 return (number * 72.0 / 0.0254);
4195 else /* Points */
4196 return (number);
4197 }
4198
4199
4200 /*
4201 * End of "$Id$".
4202 */