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