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