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