]> git.ipfire.org Git - thirdparty/cups.git/blob - cgi-bin/admin.c
Remove remaining references to subscriptions and samba.
[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 char filename[1024]; /* Temporary filename */
2153 const char *datadir; /* Location of data files */
2154 ipp_t *request, /* IPP request */
2155 *response; /* IPP response */
2156
2157
2158 /*
2159 * Get the current server settings...
2160 */
2161
2162 if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
2163 {
2164 cgiSetVariable("SETTINGS_MESSAGE",
2165 cgiText(_("Unable to open cupsd.conf file:")));
2166 cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
2167 }
2168
2169 if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
2170 settings)) != NULL && atoi(val))
2171 cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2172
2173 if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
2174 settings)) != NULL && atoi(val))
2175 cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2176
2177 if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
2178 settings)) != NULL && atoi(val))
2179 cgiSetVariable("REMOTE_ANY", "CHECKED");
2180
2181 if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
2182 settings)) != NULL && atoi(val))
2183 cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2184
2185 if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
2186 settings)) != NULL && atoi(val))
2187 cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2188
2189 #ifdef HAVE_GSSAPI
2190 cgiSetVariable("HAVE_GSSAPI", "1");
2191
2192 if ((val = cupsGetOption("DefaultAuthType", num_settings,
2193 settings)) != NULL && !_cups_strcasecmp(val, "Negotiate"))
2194 cgiSetVariable("KERBEROS", "CHECKED");
2195 else
2196 #endif /* HAVE_GSSAPI */
2197 cgiSetVariable("KERBEROS", "");
2198
2199 if ((val = cupsGetOption("BrowseWebIF", num_settings,
2200 settings)) == NULL)
2201 val = "No";
2202
2203 if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") ||
2204 !_cups_strcasecmp(val, "true"))
2205 cgiSetVariable("BROWSE_WEB_IF", "CHECKED");
2206
2207 if ((val = cupsGetOption("PreserveJobHistory", num_settings,
2208 settings)) == NULL)
2209 val = "Yes";
2210
2211 if (val &&
2212 (!_cups_strcasecmp(val, "0") || !_cups_strcasecmp(val, "no") ||
2213 !_cups_strcasecmp(val, "off") || !_cups_strcasecmp(val, "false") ||
2214 !_cups_strcasecmp(val, "disabled")))
2215 {
2216 cgiSetVariable("PRESERVE_JOB_HISTORY", "0");
2217 cgiSetVariable("PRESERVE_JOB_FILES", "0");
2218 }
2219 else
2220 {
2221 cgiSetVariable("PRESERVE_JOBS", "CHECKED");
2222 cgiSetVariable("PRESERVE_JOB_HISTORY", val);
2223
2224 if ((val = cupsGetOption("PreserveJobFiles", num_settings,
2225 settings)) == NULL)
2226 val = "1d";
2227
2228 cgiSetVariable("PRESERVE_JOB_FILES", val);
2229
2230 }
2231
2232 if ((val = cupsGetOption("MaxClients", num_settings, settings)) == NULL)
2233 val = "100";
2234
2235 cgiSetVariable("MAX_CLIENTS", val);
2236
2237 if ((val = cupsGetOption("MaxJobs", num_settings, settings)) == NULL)
2238 val = "500";
2239
2240 cgiSetVariable("MAX_JOBS", val);
2241
2242 if ((val = cupsGetOption("MaxLogSize", num_settings, settings)) == NULL)
2243 val = "1m";
2244
2245 cgiSetVariable("MAX_LOG_SIZE", val);
2246
2247 cupsFreeOptions(num_settings, settings);
2248
2249 /*
2250 * Finally, show the main menu template...
2251 */
2252
2253 cgiStartHTML(cgiText(_("Administration")));
2254
2255 cgiCopyTemplateLang("admin.tmpl");
2256
2257 cgiEndHTML();
2258 }
2259
2260
2261 /*
2262 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
2263 */
2264
2265 static void
2266 do_set_allowed_users(http_t *http) /* I - HTTP connection */
2267 {
2268 int i; /* Looping var */
2269 ipp_t *request, /* IPP request */
2270 *response; /* IPP response */
2271 char uri[HTTP_MAX_URI]; /* Printer URI */
2272 const char *printer, /* Printer name (purge-jobs) */
2273 *is_class, /* Is a class? */
2274 *users, /* List of users or groups */
2275 *type; /* Allow/deny type */
2276 int num_users; /* Number of users */
2277 char *ptr, /* Pointer into users string */
2278 *end, /* Pointer to end of users string */
2279 quote; /* Quote character */
2280 ipp_attribute_t *attr; /* Attribute */
2281 static const char * const attrs[] = /* Requested attributes */
2282 {
2283 "requesting-user-name-allowed",
2284 "requesting-user-name-denied"
2285 };
2286
2287
2288 is_class = cgiGetVariable("IS_CLASS");
2289 printer = cgiGetVariable("PRINTER_NAME");
2290
2291 if (!printer)
2292 {
2293 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2294 cgiStartHTML(cgiText(_("Set Allowed Users")));
2295 cgiCopyTemplateLang("error.tmpl");
2296 cgiEndHTML();
2297 return;
2298 }
2299
2300 users = cgiGetVariable("users");
2301 type = cgiGetVariable("type");
2302
2303 if (!users || !type ||
2304 (strcmp(type, "requesting-user-name-allowed") &&
2305 strcmp(type, "requesting-user-name-denied")))
2306 {
2307 /*
2308 * Build a Get-Printer-Attributes request, which requires the following
2309 * attributes:
2310 *
2311 * attributes-charset
2312 * attributes-natural-language
2313 * printer-uri
2314 * requested-attributes
2315 */
2316
2317 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
2318
2319 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2320 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2321 printer);
2322 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2323 NULL, uri);
2324
2325 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2326 "requested-attributes",
2327 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
2328
2329 /*
2330 * Do the request and get back a response...
2331 */
2332
2333 if ((response = cupsDoRequest(http, request, "/")) != NULL)
2334 {
2335 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2336
2337 ippDelete(response);
2338 }
2339
2340 cgiStartHTML(cgiText(_("Set Allowed Users")));
2341
2342 if (cupsLastError() == IPP_NOT_AUTHORIZED)
2343 {
2344 puts("Status: 401\n");
2345 exit(0);
2346 }
2347 else if (cupsLastError() > IPP_OK_CONFLICT)
2348 cgiShowIPPError(_("Unable to get printer attributes"));
2349 else
2350 cgiCopyTemplateLang("users.tmpl");
2351
2352 cgiEndHTML();
2353 }
2354 else
2355 {
2356 /*
2357 * Save the changes...
2358 */
2359
2360 for (num_users = 0, ptr = (char *)users; *ptr; num_users ++)
2361 {
2362 /*
2363 * Skip whitespace and commas...
2364 */
2365
2366 while (*ptr == ',' || isspace(*ptr & 255))
2367 ptr ++;
2368
2369 if (!*ptr)
2370 break;
2371
2372 if (*ptr == '\'' || *ptr == '\"')
2373 {
2374 /*
2375 * Scan quoted name...
2376 */
2377
2378 quote = *ptr++;
2379
2380 for (end = ptr; *end; end ++)
2381 if (*end == quote)
2382 break;
2383 }
2384 else
2385 {
2386 /*
2387 * Scan space or comma-delimited name...
2388 */
2389
2390 for (end = ptr; *end; end ++)
2391 if (isspace(*end & 255) || *end == ',')
2392 break;
2393 }
2394
2395 /*
2396 * Advance to the next name...
2397 */
2398
2399 ptr = end;
2400 }
2401
2402 /*
2403 * Build a CUPS-Add-Printer/Class request, which requires the following
2404 * attributes:
2405 *
2406 * attributes-charset
2407 * attributes-natural-language
2408 * printer-uri
2409 * requesting-user-name-{allowed,denied}
2410 */
2411
2412 request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
2413
2414 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2415 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2416 printer);
2417 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2418 NULL, uri);
2419
2420 if (num_users == 0)
2421 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2422 "requesting-user-name-allowed", NULL, "all");
2423 else
2424 {
2425 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2426 type, num_users, NULL, NULL);
2427
2428 for (i = 0, ptr = (char *)users; *ptr; i ++)
2429 {
2430 /*
2431 * Skip whitespace and commas...
2432 */
2433
2434 while (*ptr == ',' || isspace(*ptr & 255))
2435 ptr ++;
2436
2437 if (!*ptr)
2438 break;
2439
2440 if (*ptr == '\'' || *ptr == '\"')
2441 {
2442 /*
2443 * Scan quoted name...
2444 */
2445
2446 quote = *ptr++;
2447
2448 for (end = ptr; *end; end ++)
2449 if (*end == quote)
2450 break;
2451 }
2452 else
2453 {
2454 /*
2455 * Scan space or comma-delimited name...
2456 */
2457
2458 for (end = ptr; *end; end ++)
2459 if (isspace(*end & 255) || *end == ',')
2460 break;
2461 }
2462
2463 /*
2464 * Terminate the name...
2465 */
2466
2467 if (*end)
2468 *end++ = '\0';
2469
2470 /*
2471 * Add the name...
2472 */
2473
2474 ippSetString(request, &attr, i, ptr);
2475
2476 /*
2477 * Advance to the next name...
2478 */
2479
2480 ptr = end;
2481 }
2482 }
2483
2484 /*
2485 * Do the request and get back a response...
2486 */
2487
2488 ippDelete(cupsDoRequest(http, request, "/admin/"));
2489
2490 if (cupsLastError() == IPP_NOT_AUTHORIZED)
2491 {
2492 puts("Status: 401\n");
2493 exit(0);
2494 }
2495 else if (cupsLastError() > IPP_OK_CONFLICT)
2496 {
2497 cgiStartHTML(cgiText(_("Set Allowed Users")));
2498 cgiShowIPPError(_("Unable to change printer"));
2499 }
2500 else
2501 {
2502 /*
2503 * Redirect successful updates back to the printer page...
2504 */
2505
2506 char url[1024], /* Printer/class URL */
2507 refresh[1024]; /* Refresh URL */
2508
2509
2510 cgiRewriteURL(uri, url, sizeof(url), NULL);
2511 cgiFormEncode(uri, url, sizeof(uri));
2512 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s",
2513 uri);
2514 cgiSetVariable("refresh_page", refresh);
2515
2516 cgiStartHTML(cgiText(_("Set Allowed Users")));
2517
2518 cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
2519 "printer-modified.tmpl");
2520 }
2521
2522 cgiEndHTML();
2523 }
2524 }
2525
2526
2527 /*
2528 * 'do_set_default()' - Set the server default printer/class.
2529 */
2530
2531 static void
2532 do_set_default(http_t *http) /* I - HTTP connection */
2533 {
2534 const char *title; /* Page title */
2535 ipp_t *request; /* IPP request */
2536 char uri[HTTP_MAX_URI]; /* Printer URI */
2537 const char *printer, /* Printer name (purge-jobs) */
2538 *is_class; /* Is a class? */
2539
2540
2541 is_class = cgiGetVariable("IS_CLASS");
2542 printer = cgiGetVariable("PRINTER_NAME");
2543 title = cgiText(_("Set As Server Default"));
2544
2545 if (!printer)
2546 {
2547 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2548 cgiStartHTML(title);
2549 cgiCopyTemplateLang("error.tmpl");
2550 cgiEndHTML();
2551 return;
2552 }
2553
2554 /*
2555 * Build a printer request, which requires the following
2556 * attributes:
2557 *
2558 * attributes-charset
2559 * attributes-natural-language
2560 * printer-uri
2561 */
2562
2563 request = ippNewRequest(CUPS_SET_DEFAULT);
2564
2565 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2566 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2567 printer);
2568 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2569 NULL, uri);
2570
2571 /*
2572 * Do the request and get back a response...
2573 */
2574
2575 ippDelete(cupsDoRequest(http, request, "/admin/"));
2576
2577 if (cupsLastError() == IPP_NOT_AUTHORIZED)
2578 {
2579 puts("Status: 401\n");
2580 exit(0);
2581 }
2582 else if (cupsLastError() > IPP_OK_CONFLICT)
2583 {
2584 cgiStartHTML(title);
2585 cgiShowIPPError(_("Unable to set server default"));
2586 }
2587 else
2588 {
2589 /*
2590 * Redirect successful updates back to the printer page...
2591 */
2592
2593 char url[1024], /* Printer/class URL */
2594 refresh[1024]; /* Refresh URL */
2595
2596
2597 cgiRewriteURL(uri, url, sizeof(url), NULL);
2598 cgiFormEncode(uri, url, sizeof(uri));
2599 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
2600 cgiSetVariable("refresh_page", refresh);
2601
2602 cgiStartHTML(title);
2603 cgiCopyTemplateLang("printer-default.tmpl");
2604 }
2605
2606 cgiEndHTML();
2607 }
2608
2609
2610 /*
2611 * 'do_set_options()' - Configure the default options for a queue.
2612 */
2613
2614 static void
2615 do_set_options(http_t *http, /* I - HTTP connection */
2616 int is_class) /* I - Set options for class? */
2617 {
2618 int i, j, k, m; /* Looping vars */
2619 int have_options; /* Have options? */
2620 ipp_t *request, /* IPP request */
2621 *response; /* IPP response */
2622 ipp_attribute_t *attr; /* IPP attribute */
2623 char uri[HTTP_MAX_URI]; /* Job URI */
2624 const char *var; /* Variable value */
2625 const char *printer; /* Printer printer name */
2626 const char *filename; /* PPD filename */
2627 char tempfile[1024]; /* Temporary filename */
2628 cups_file_t *in, /* Input file */
2629 *out; /* Output file */
2630 char line[1024], /* Line from PPD file */
2631 value[1024], /* Option value */
2632 keyword[1024], /* Keyword from Default line */
2633 *keyptr; /* Pointer into keyword... */
2634 ppd_file_t *ppd; /* PPD file */
2635 ppd_group_t *group; /* Option group */
2636 ppd_option_t *option; /* Option */
2637 ppd_coption_t *coption; /* Custom option */
2638 ppd_cparam_t *cparam; /* Custom parameter */
2639 ppd_attr_t *ppdattr; /* PPD attribute */
2640 const char *title; /* Page title */
2641
2642
2643 title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options"));
2644
2645 fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http,
2646 is_class);
2647
2648 /*
2649 * Get the printer name...
2650 */
2651
2652 if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
2653 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2654 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2655 printer);
2656 else
2657 {
2658 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2659 cgiStartHTML(title);
2660 cgiCopyTemplateLang("error.tmpl");
2661 cgiEndHTML();
2662 return;
2663 }
2664
2665 fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
2666
2667 /*
2668 * If the user clicks on the Auto-Configure button, send an AutoConfigure
2669 * command file to the printer...
2670 */
2671
2672 if (cgiGetVariable("AUTOCONFIGURE"))
2673 {
2674 cgiPrintCommand(http, printer, "AutoConfigure", "Set Default Options");
2675 return;
2676 }
2677
2678 /*
2679 * Get the PPD file...
2680 */
2681
2682 if (is_class)
2683 filename = NULL;
2684 else
2685 filename = cupsGetPPD2(http, printer);
2686
2687 if (filename)
2688 {
2689 fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
2690
2691 if ((ppd = ppdOpenFile(filename)) == NULL)
2692 {
2693 cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i)));
2694 cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file")));
2695 cgiStartHTML(title);
2696 cgiCopyTemplateLang("error.tmpl");
2697 cgiEndHTML();
2698 return;
2699 }
2700 }
2701 else
2702 {
2703 fputs("DEBUG: No PPD file\n", stderr);
2704 ppd = NULL;
2705 }
2706
2707 if (cgiGetVariable("job_sheets_start") != NULL ||
2708 cgiGetVariable("job_sheets_end") != NULL)
2709 have_options = 1;
2710 else
2711 have_options = 0;
2712
2713 if (ppd)
2714 {
2715 ppdMarkDefaults(ppd);
2716
2717 for (option = ppdFirstOption(ppd);
2718 option;
2719 option = ppdNextOption(ppd))
2720 {
2721 if ((var = cgiGetVariable(option->keyword)) != NULL)
2722 {
2723 have_options = 1;
2724 ppdMarkOption(ppd, option->keyword, var);
2725 fprintf(stderr, "DEBUG: Set %s to %s...\n", option->keyword, var);
2726 }
2727 else
2728 fprintf(stderr, "DEBUG: Didn't find %s...\n", option->keyword);
2729 }
2730 }
2731
2732 if (!have_options || ppdConflicts(ppd))
2733 {
2734 /*
2735 * Show the options to the user...
2736 */
2737
2738 fputs("DEBUG: Showing options...\n", stderr);
2739
2740 /*
2741 * Show auto-configure button if supported...
2742 */
2743
2744 if (ppd)
2745 {
2746 if (ppd->num_filters == 0 ||
2747 ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
2748 ppdattr->value && strstr(ppdattr->value, "AutoConfigure")))
2749 cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
2750 else
2751 {
2752 for (i = 0; i < ppd->num_filters; i ++)
2753 if (!strncmp(ppd->filters[i], "application/vnd.cups-postscript", 31))
2754 {
2755 cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
2756 break;
2757 }
2758 }
2759 }
2760
2761 /*
2762 * Get the printer attributes...
2763 */
2764
2765 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
2766
2767 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2768 "localhost", 0, "/printers/%s", printer);
2769 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2770 NULL, uri);
2771
2772 response = cupsDoRequest(http, request, "/");
2773
2774 /*
2775 * List the groups used as "tabs"...
2776 */
2777
2778 i = 0;
2779
2780 if (ppd)
2781 {
2782 for (group = ppd->groups;
2783 i < ppd->num_groups;
2784 i ++, group ++)
2785 {
2786 cgiSetArray("GROUP_ID", i, group->name);
2787
2788 if (!strcmp(group->name, "InstallableOptions"))
2789 cgiSetArray("GROUP", i, cgiText(_("Options Installed")));
2790 else
2791 cgiSetArray("GROUP", i, group->text);
2792 }
2793 }
2794
2795 if (ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO))
2796 {
2797 cgiSetArray("GROUP_ID", i, "CUPS_BANNERS");
2798 cgiSetArray("GROUP", i ++, cgiText(_("Banners")));
2799 }
2800
2801 if (ippFindAttribute(response, "printer-error-policy-supported",
2802 IPP_TAG_ZERO) ||
2803 ippFindAttribute(response, "printer-op-policy-supported",
2804 IPP_TAG_ZERO))
2805 {
2806 cgiSetArray("GROUP_ID", i, "CUPS_POLICIES");
2807 cgiSetArray("GROUP", i ++, cgiText(_("Policies")));
2808 }
2809
2810 if ((attr = ippFindAttribute(response, "port-monitor-supported",
2811 IPP_TAG_NAME)) != NULL && attr->num_values > 1)
2812 {
2813 cgiSetArray("GROUP_ID", i, "CUPS_PORT_MONITOR");
2814 cgiSetArray("GROUP", i, cgiText(_("Port Monitor")));
2815 }
2816
2817 cgiStartHTML(cgiText(_("Set Printer Options")));
2818 cgiCopyTemplateLang("set-printer-options-header.tmpl");
2819
2820 if (ppd)
2821 {
2822 ppdLocalize(ppd);
2823
2824 if (ppdConflicts(ppd))
2825 {
2826 for (i = ppd->num_groups, k = 0, group = ppd->groups;
2827 i > 0;
2828 i --, group ++)
2829 for (j = group->num_options, option = group->options;
2830 j > 0;
2831 j --, option ++)
2832 if (option->conflicted)
2833 {
2834 cgiSetArray("ckeyword", k, option->keyword);
2835 cgiSetArray("ckeytext", k, option->text);
2836
2837 for (m = 0; m < option->num_choices; m ++)
2838 {
2839 if (option->choices[m].marked)
2840 {
2841 cgiSetArray("cchoice", k, option->choices[m].text);
2842 break;
2843 }
2844 }
2845
2846 k ++;
2847 }
2848
2849 cgiCopyTemplateLang("option-conflict.tmpl");
2850 }
2851
2852 for (i = ppd->num_groups, group = ppd->groups;
2853 i > 0;
2854 i --, group ++)
2855 {
2856 for (j = group->num_options, option = group->options;
2857 j > 0;
2858 j --, option ++)
2859 {
2860 if (!strcmp(option->keyword, "PageRegion"))
2861 continue;
2862
2863 if (option->num_choices > 1)
2864 break;
2865 }
2866
2867 if (j == 0)
2868 continue;
2869
2870 cgiSetVariable("GROUP_ID", group->name);
2871
2872 if (!strcmp(group->name, "InstallableOptions"))
2873 cgiSetVariable("GROUP", cgiText(_("Options Installed")));
2874 else
2875 cgiSetVariable("GROUP", group->text);
2876
2877 cgiCopyTemplateLang("option-header.tmpl");
2878
2879 for (j = group->num_options, option = group->options;
2880 j > 0;
2881 j --, option ++)
2882 {
2883 if (!strcmp(option->keyword, "PageRegion") || option->num_choices < 2)
2884 continue;
2885
2886 cgiSetVariable("KEYWORD", option->keyword);
2887 cgiSetVariable("KEYTEXT", option->text);
2888
2889 if (option->conflicted)
2890 cgiSetVariable("CONFLICTED", "1");
2891 else
2892 cgiSetVariable("CONFLICTED", "0");
2893
2894 cgiSetSize("CHOICES", 0);
2895 cgiSetSize("TEXT", 0);
2896 for (k = 0, m = 0; k < option->num_choices; k ++)
2897 {
2898 cgiSetArray("CHOICES", m, option->choices[k].choice);
2899 cgiSetArray("TEXT", m, option->choices[k].text);
2900
2901 m ++;
2902
2903 if (option->choices[k].marked)
2904 cgiSetVariable("DEFCHOICE", option->choices[k].choice);
2905 }
2906
2907 cgiSetSize("PARAMS", 0);
2908 cgiSetSize("PARAMTEXT", 0);
2909 cgiSetSize("PARAMVALUE", 0);
2910 cgiSetSize("INPUTTYPE", 0);
2911
2912 if ((coption = ppdFindCustomOption(ppd, option->keyword)))
2913 {
2914 const char *units = NULL; /* Units value, if any */
2915
2916 cgiSetVariable("ISCUSTOM", "1");
2917
2918 for (cparam = ppdFirstCustomParam(coption), m = 0;
2919 cparam;
2920 cparam = ppdNextCustomParam(coption), m ++)
2921 {
2922 if (!_cups_strcasecmp(option->keyword, "PageSize") &&
2923 _cups_strcasecmp(cparam->name, "Width") &&
2924 _cups_strcasecmp(cparam->name, "Height"))
2925 {
2926 m --;
2927 continue;
2928 }
2929
2930 cgiSetArray("PARAMS", m, cparam->name);
2931 cgiSetArray("PARAMTEXT", m, cparam->text);
2932 cgiSetArray("INPUTTYPE", m, "text");
2933
2934 switch (cparam->type)
2935 {
2936 case PPD_CUSTOM_POINTS :
2937 if (!_cups_strncasecmp(option->defchoice, "Custom.", 7))
2938 {
2939 units = option->defchoice + strlen(option->defchoice) - 2;
2940
2941 if (strcmp(units, "mm") && strcmp(units, "cm") &&
2942 strcmp(units, "in") && strcmp(units, "ft"))
2943 {
2944 if (units[1] == 'm')
2945 units ++;
2946 else
2947 units = "pt";
2948 }
2949 }
2950 else
2951 units = "pt";
2952
2953 if (!strcmp(units, "mm"))
2954 snprintf(value, sizeof(value), "%g",
2955 cparam->current.custom_points / 72.0 * 25.4);
2956 else if (!strcmp(units, "cm"))
2957 snprintf(value, sizeof(value), "%g",
2958 cparam->current.custom_points / 72.0 * 2.54);
2959 else if (!strcmp(units, "in"))
2960 snprintf(value, sizeof(value), "%g",
2961 cparam->current.custom_points / 72.0);
2962 else if (!strcmp(units, "ft"))
2963 snprintf(value, sizeof(value), "%g",
2964 cparam->current.custom_points / 72.0 / 12.0);
2965 else if (!strcmp(units, "m"))
2966 snprintf(value, sizeof(value), "%g",
2967 cparam->current.custom_points / 72.0 * 0.0254);
2968 else
2969 snprintf(value, sizeof(value), "%g",
2970 cparam->current.custom_points);
2971 cgiSetArray("PARAMVALUE", m, value);
2972 break;
2973
2974 case PPD_CUSTOM_CURVE :
2975 case PPD_CUSTOM_INVCURVE :
2976 case PPD_CUSTOM_REAL :
2977 snprintf(value, sizeof(value), "%g",
2978 cparam->current.custom_real);
2979 cgiSetArray("PARAMVALUE", m, value);
2980 break;
2981
2982 case PPD_CUSTOM_INT:
2983 snprintf(value, sizeof(value), "%d",
2984 cparam->current.custom_int);
2985 cgiSetArray("PARAMVALUE", m, value);
2986 break;
2987
2988 case PPD_CUSTOM_PASSCODE:
2989 case PPD_CUSTOM_PASSWORD:
2990 if (cparam->current.custom_password)
2991 cgiSetArray("PARAMVALUE", m,
2992 cparam->current.custom_password);
2993 else
2994 cgiSetArray("PARAMVALUE", m, "");
2995 cgiSetArray("INPUTTYPE", m, "password");
2996 break;
2997
2998 case PPD_CUSTOM_STRING:
2999 if (cparam->current.custom_string)
3000 cgiSetArray("PARAMVALUE", m,
3001 cparam->current.custom_string);
3002 else
3003 cgiSetArray("PARAMVALUE", m, "");
3004 break;
3005 }
3006 }
3007
3008 if (units)
3009 {
3010 cgiSetArray("PARAMS", m, "Units");
3011 cgiSetArray("PARAMTEXT", m, cgiText(_("Units")));
3012 cgiSetArray("PARAMVALUE", m, units);
3013 }
3014 }
3015 else
3016 cgiSetVariable("ISCUSTOM", "0");
3017
3018 switch (option->ui)
3019 {
3020 case PPD_UI_BOOLEAN :
3021 cgiCopyTemplateLang("option-boolean.tmpl");
3022 break;
3023 case PPD_UI_PICKONE :
3024 cgiCopyTemplateLang("option-pickone.tmpl");
3025 break;
3026 case PPD_UI_PICKMANY :
3027 cgiCopyTemplateLang("option-pickmany.tmpl");
3028 break;
3029 }
3030 }
3031
3032 cgiCopyTemplateLang("option-trailer.tmpl");
3033 }
3034 }
3035
3036 if ((attr = ippFindAttribute(response, "job-sheets-supported",
3037 IPP_TAG_ZERO)) != NULL)
3038 {
3039 /*
3040 * Add the job sheets options...
3041 */
3042
3043 cgiSetVariable("GROUP_ID", "CUPS_BANNERS");
3044 cgiSetVariable("GROUP", cgiText(_("Banners")));
3045 cgiCopyTemplateLang("option-header.tmpl");
3046
3047 cgiSetSize("CHOICES", attr->num_values);
3048 cgiSetSize("TEXT", attr->num_values);
3049 for (k = 0; k < attr->num_values; k ++)
3050 {
3051 cgiSetArray("CHOICES", k, attr->values[k].string.text);
3052 cgiSetArray("TEXT", k, attr->values[k].string.text);
3053 }
3054
3055 attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
3056
3057 cgiSetVariable("KEYWORD", "job_sheets_start");
3058 cgiSetVariable("KEYTEXT",
3059 /* TRANSLATORS: Banner/cover sheet before the print job. */
3060 cgiText(_("Starting Banner")));
3061 cgiSetVariable("DEFCHOICE", attr != NULL ?
3062 attr->values[0].string.text : "");
3063
3064 cgiCopyTemplateLang("option-pickone.tmpl");
3065
3066 cgiSetVariable("KEYWORD", "job_sheets_end");
3067 cgiSetVariable("KEYTEXT",
3068 /* TRANSLATORS: Banner/cover sheet after the print job. */
3069 cgiText(_("Ending Banner")));
3070 cgiSetVariable("DEFCHOICE", attr != NULL && attr->num_values > 1 ?
3071 attr->values[1].string.text : "");
3072
3073 cgiCopyTemplateLang("option-pickone.tmpl");
3074
3075 cgiCopyTemplateLang("option-trailer.tmpl");
3076 }
3077
3078 if (ippFindAttribute(response, "printer-error-policy-supported",
3079 IPP_TAG_ZERO) ||
3080 ippFindAttribute(response, "printer-op-policy-supported",
3081 IPP_TAG_ZERO))
3082 {
3083 /*
3084 * Add the error and operation policy options...
3085 */
3086
3087 cgiSetVariable("GROUP_ID", "CUPS_POLICIES");
3088 cgiSetVariable("GROUP", cgiText(_("Policies")));
3089 cgiCopyTemplateLang("option-header.tmpl");
3090
3091 /*
3092 * Error policy...
3093 */
3094
3095 attr = ippFindAttribute(response, "printer-error-policy-supported",
3096 IPP_TAG_ZERO);
3097
3098 if (attr)
3099 {
3100 cgiSetSize("CHOICES", attr->num_values);
3101 cgiSetSize("TEXT", attr->num_values);
3102 for (k = 0; k < attr->num_values; k ++)
3103 {
3104 cgiSetArray("CHOICES", k, attr->values[k].string.text);
3105 cgiSetArray("TEXT", k, attr->values[k].string.text);
3106 }
3107
3108 attr = ippFindAttribute(response, "printer-error-policy",
3109 IPP_TAG_ZERO);
3110
3111 cgiSetVariable("KEYWORD", "printer_error_policy");
3112 cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
3113 cgiSetVariable("DEFCHOICE", attr == NULL ?
3114 "" : attr->values[0].string.text);
3115 }
3116
3117 cgiCopyTemplateLang("option-pickone.tmpl");
3118
3119 /*
3120 * Operation policy...
3121 */
3122
3123 attr = ippFindAttribute(response, "printer-op-policy-supported",
3124 IPP_TAG_ZERO);
3125
3126 if (attr)
3127 {
3128 cgiSetSize("CHOICES", attr->num_values);
3129 cgiSetSize("TEXT", attr->num_values);
3130 for (k = 0; k < attr->num_values; k ++)
3131 {
3132 cgiSetArray("CHOICES", k, attr->values[k].string.text);
3133 cgiSetArray("TEXT", k, attr->values[k].string.text);
3134 }
3135
3136 attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
3137
3138 cgiSetVariable("KEYWORD", "printer_op_policy");
3139 cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
3140 cgiSetVariable("DEFCHOICE", attr == NULL ?
3141 "" : attr->values[0].string.text);
3142
3143 cgiCopyTemplateLang("option-pickone.tmpl");
3144 }
3145
3146 cgiCopyTemplateLang("option-trailer.tmpl");
3147 }
3148
3149 /*
3150 * Binary protocol support...
3151 */
3152
3153 if ((attr = ippFindAttribute(response, "port-monitor-supported",
3154 IPP_TAG_NAME)) != NULL && attr->num_values > 1)
3155 {
3156 cgiSetVariable("GROUP_ID", "CUPS_PORT_MONITOR");
3157 cgiSetVariable("GROUP", cgiText(_("Port Monitor")));
3158
3159 cgiSetSize("CHOICES", attr->num_values);
3160 cgiSetSize("TEXT", attr->num_values);
3161
3162 for (i = 0; i < attr->num_values; i ++)
3163 {
3164 cgiSetArray("CHOICES", i, attr->values[i].string.text);
3165 cgiSetArray("TEXT", i, attr->values[i].string.text);
3166 }
3167
3168 attr = ippFindAttribute(response, "port-monitor", IPP_TAG_NAME);
3169 cgiSetVariable("KEYWORD", "port_monitor");
3170 cgiSetVariable("KEYTEXT", cgiText(_("Port Monitor")));
3171 cgiSetVariable("DEFCHOICE", attr ? attr->values[0].string.text : "none");
3172
3173 cgiCopyTemplateLang("option-header.tmpl");
3174 cgiCopyTemplateLang("option-pickone.tmpl");
3175 cgiCopyTemplateLang("option-trailer.tmpl");
3176 }
3177
3178 cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
3179 cgiEndHTML();
3180
3181 ippDelete(response);
3182 }
3183 else
3184 {
3185 /*
3186 * Set default options...
3187 */
3188
3189 fputs("DEBUG: Setting options...\n", stderr);
3190
3191 if (filename)
3192 {
3193 out = cupsTempFile2(tempfile, sizeof(tempfile));
3194 in = cupsFileOpen(filename, "r");
3195
3196 if (!in || !out)
3197 {
3198 cgiSetVariable("ERROR", strerror(errno));
3199 cgiStartHTML(cgiText(_("Set Printer Options")));
3200 cgiCopyTemplateLang("error.tmpl");
3201 cgiEndHTML();
3202
3203 if (in)
3204 cupsFileClose(in);
3205
3206 if (out)
3207 {
3208 cupsFileClose(out);
3209 unlink(tempfile);
3210 }
3211
3212 unlink(filename);
3213 return;
3214 }
3215
3216 while (cupsFileGets(in, line, sizeof(line)))
3217 {
3218 if (!strncmp(line, "*cupsProtocol:", 14))
3219 continue;
3220 else if (strncmp(line, "*Default", 8))
3221 cupsFilePrintf(out, "%s\n", line);
3222 else
3223 {
3224 /*
3225 * Get default option name...
3226 */
3227
3228 strlcpy(keyword, line + 8, sizeof(keyword));
3229
3230 for (keyptr = keyword; *keyptr; keyptr ++)
3231 if (*keyptr == ':' || isspace(*keyptr & 255))
3232 break;
3233
3234 *keyptr = '\0';
3235
3236 if (!strcmp(keyword, "PageRegion") ||
3237 !strcmp(keyword, "PaperDimension") ||
3238 !strcmp(keyword, "ImageableArea"))
3239 var = get_option_value(ppd, "PageSize", value, sizeof(value));
3240 else
3241 var = get_option_value(ppd, keyword, value, sizeof(value));
3242
3243 if (!var)
3244 cupsFilePrintf(out, "%s\n", line);
3245 else
3246 cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
3247 }
3248 }
3249
3250 cupsFileClose(in);
3251 cupsFileClose(out);
3252 }
3253 else
3254 {
3255 /*
3256 * Make sure temporary filename is cleared when there is no PPD...
3257 */
3258
3259 tempfile[0] = '\0';
3260 }
3261
3262 /*
3263 * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the
3264 * following attributes:
3265 *
3266 * attributes-charset
3267 * attributes-natural-language
3268 * printer-uri
3269 * job-sheets-default
3270 * printer-error-policy
3271 * printer-op-policy
3272 * [ppd file]
3273 */
3274
3275 request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS :
3276 CUPS_ADD_MODIFY_PRINTER);
3277
3278 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3279 NULL, uri);
3280
3281 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3282 "job-sheets-default", 2, NULL, NULL);
3283 ippSetString(request, &attr, 0, cgiGetVariable("job_sheets_start"));
3284 ippSetString(request, &attr, 1, cgiGetVariable("job_sheets_end"));
3285
3286 if ((var = cgiGetVariable("printer_error_policy")) != NULL)
3287 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3288 "printer-error-policy", NULL, var);
3289
3290 if ((var = cgiGetVariable("printer_op_policy")) != NULL)
3291 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3292 "printer-op-policy", NULL, var);
3293
3294 if ((var = cgiGetVariable("port_monitor")) != NULL)
3295 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3296 "port-monitor", NULL, var);
3297
3298 /*
3299 * Do the request and get back a response...
3300 */
3301
3302 if (filename)
3303 ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile));
3304 else
3305 ippDelete(cupsDoRequest(http, request, "/admin/"));
3306
3307 if (cupsLastError() == IPP_NOT_AUTHORIZED)
3308 {
3309 puts("Status: 401\n");
3310 exit(0);
3311 }
3312 else if (cupsLastError() > IPP_OK_CONFLICT)
3313 {
3314 cgiStartHTML(title);
3315 cgiShowIPPError(_("Unable to set options"));
3316 }
3317 else
3318 {
3319 /*
3320 * Redirect successful updates back to the printer page...
3321 */
3322
3323 char refresh[1024]; /* Refresh URL */
3324
3325
3326 cgiFormEncode(uri, printer, sizeof(uri));
3327 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s",
3328 is_class ? "classes" : "printers", uri);
3329 cgiSetVariable("refresh_page", refresh);
3330
3331 cgiStartHTML(title);
3332
3333 cgiCopyTemplateLang("printer-configured.tmpl");
3334 }
3335
3336 cgiEndHTML();
3337
3338 if (filename)
3339 unlink(tempfile);
3340 }
3341
3342 if (filename)
3343 unlink(filename);
3344 }
3345
3346
3347 /*
3348 * 'do_set_sharing()' - Set printer-is-shared value.
3349 */
3350
3351 static void
3352 do_set_sharing(http_t *http) /* I - HTTP connection */
3353 {
3354 ipp_t *request, /* IPP request */
3355 *response; /* IPP response */
3356 char uri[HTTP_MAX_URI]; /* Printer URI */
3357 const char *printer, /* Printer name */
3358 *is_class, /* Is a class? */
3359 *shared; /* Sharing value */
3360
3361
3362 is_class = cgiGetVariable("IS_CLASS");
3363 printer = cgiGetVariable("PRINTER_NAME");
3364 shared = cgiGetVariable("SHARED");
3365
3366 if (!printer || !shared)
3367 {
3368 cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3369 cgiStartHTML(cgiText(_("Set Publishing")));
3370 cgiCopyTemplateLang("error.tmpl");
3371 cgiEndHTML();
3372 return;
3373 }
3374
3375 /*
3376 * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the
3377 * following attributes:
3378 *
3379 * attributes-charset
3380 * attributes-natural-language
3381 * printer-uri
3382 * printer-is-shared
3383 */
3384
3385 request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
3386
3387 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3388 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3389 printer);
3390 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3391 NULL, uri);
3392
3393 ippAddBoolean(request, IPP_TAG_OPERATION, "printer-is-shared", (char)atoi(shared));
3394
3395 /*
3396 * Do the request and get back a response...
3397 */
3398
3399 if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
3400 {
3401 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
3402
3403 ippDelete(response);
3404 }
3405
3406 if (cupsLastError() == IPP_NOT_AUTHORIZED)
3407 {
3408 puts("Status: 401\n");
3409 exit(0);
3410 }
3411 else if (cupsLastError() > IPP_OK_CONFLICT)
3412 {
3413 cgiStartHTML(cgiText(_("Set Publishing")));
3414 cgiShowIPPError(_("Unable to change printer-is-shared attribute"));
3415 }
3416 else
3417 {
3418 /*
3419 * Redirect successful updates back to the printer page...
3420 */
3421
3422 char url[1024], /* Printer/class URL */
3423 refresh[1024]; /* Refresh URL */
3424
3425
3426 cgiRewriteURL(uri, url, sizeof(url), NULL);
3427 cgiFormEncode(uri, url, sizeof(uri));
3428 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
3429 cgiSetVariable("refresh_page", refresh);
3430
3431 cgiStartHTML(cgiText(_("Set Publishing")));
3432 cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
3433 "printer-modified.tmpl");
3434 }
3435
3436 cgiEndHTML();
3437 }
3438
3439
3440 /*
3441 * 'get_option_value()' - Return the value of an option.
3442 *
3443 * This function also handles generation of custom option values.
3444 */
3445
3446 static char * /* O - Value string or NULL on error */
3447 get_option_value(
3448 ppd_file_t *ppd, /* I - PPD file */
3449 const char *name, /* I - Option name */
3450 char *buffer, /* I - String buffer */
3451 size_t bufsize) /* I - Size of buffer */
3452 {
3453 char *bufptr, /* Pointer into buffer */
3454 *bufend; /* End of buffer */
3455 ppd_coption_t *coption; /* Custom option */
3456 ppd_cparam_t *cparam; /* Current custom parameter */
3457 char keyword[256]; /* Parameter name */
3458 const char *val, /* Parameter value */
3459 *uval; /* Units value */
3460 long integer; /* Integer value */
3461 double number, /* Number value */
3462 number_points; /* Number in points */
3463
3464
3465 /*
3466 * See if we have a custom option choice...
3467 */
3468
3469 if ((val = cgiGetVariable(name)) == NULL)
3470 {
3471 /*
3472 * Option not found!
3473 */
3474
3475 return (NULL);
3476 }
3477 else if (_cups_strcasecmp(val, "Custom") ||
3478 (coption = ppdFindCustomOption(ppd, name)) == NULL)
3479 {
3480 /*
3481 * Not a custom choice...
3482 */
3483
3484 strlcpy(buffer, val, bufsize);
3485 return (buffer);
3486 }
3487
3488 /*
3489 * OK, we have a custom option choice, format it...
3490 */
3491
3492 *buffer = '\0';
3493
3494 if (!strcmp(coption->keyword, "PageSize"))
3495 {
3496 const char *lval; /* Length string value */
3497 double width, /* Width value */
3498 width_points, /* Width in points */
3499 length, /* Length value */
3500 length_points; /* Length in points */
3501
3502
3503 val = cgiGetVariable("PageSize.Width");
3504 lval = cgiGetVariable("PageSize.Height");
3505 uval = cgiGetVariable("PageSize.Units");
3506
3507 if (!val || !lval || !uval ||
3508 (width = strtod(val, NULL)) == 0.0 ||
3509 (length = strtod(lval, NULL)) == 0.0 ||
3510 (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
3511 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
3512 return (NULL);
3513
3514 width_points = get_points(width, uval);
3515 length_points = get_points(length, uval);
3516
3517 if (width_points < ppd->custom_min[0] ||
3518 width_points > ppd->custom_max[0] ||
3519 length_points < ppd->custom_min[1] ||
3520 length_points > ppd->custom_max[1])
3521 return (NULL);
3522
3523 snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval);
3524 }
3525 else if (cupsArrayCount(coption->params) == 1)
3526 {
3527 cparam = ppdFirstCustomParam(coption);
3528 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name);
3529
3530 if ((val = cgiGetVariable(keyword)) == NULL)
3531 return (NULL);
3532
3533 switch (cparam->type)
3534 {
3535 case PPD_CUSTOM_CURVE :
3536 case PPD_CUSTOM_INVCURVE :
3537 case PPD_CUSTOM_REAL :
3538 if ((number = strtod(val, NULL)) == 0.0 ||
3539 number < cparam->minimum.custom_real ||
3540 number > cparam->maximum.custom_real)
3541 return (NULL);
3542
3543 snprintf(buffer, bufsize, "Custom.%g", number);
3544 break;
3545
3546 case PPD_CUSTOM_INT :
3547 if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
3548 integer == LONG_MAX ||
3549 integer < cparam->minimum.custom_int ||
3550 integer > cparam->maximum.custom_int)
3551 return (NULL);
3552
3553 snprintf(buffer, bufsize, "Custom.%ld", integer);
3554 break;
3555
3556 case PPD_CUSTOM_POINTS :
3557 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
3558
3559 if ((number = strtod(val, NULL)) == 0.0 ||
3560 (uval = cgiGetVariable(keyword)) == NULL ||
3561 (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
3562 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
3563 return (NULL);
3564
3565 number_points = get_points(number, uval);
3566 if (number_points < cparam->minimum.custom_points ||
3567 number_points > cparam->maximum.custom_points)
3568 return (NULL);
3569
3570 snprintf(buffer, bufsize, "Custom.%g%s", number, uval);
3571 break;
3572
3573 case PPD_CUSTOM_PASSCODE :
3574 for (uval = val; *uval; uval ++)
3575 if (!isdigit(*uval & 255))
3576 return (NULL);
3577
3578 case PPD_CUSTOM_PASSWORD :
3579 case PPD_CUSTOM_STRING :
3580 integer = (long)strlen(val);
3581 if (integer < cparam->minimum.custom_string ||
3582 integer > cparam->maximum.custom_string)
3583 return (NULL);
3584
3585 snprintf(buffer, bufsize, "Custom.%s", val);
3586 break;
3587 }
3588 }
3589 else
3590 {
3591 const char *prefix = "{"; /* Prefix string */
3592
3593
3594 bufptr = buffer;
3595 bufend = buffer + bufsize;
3596
3597 for (cparam = ppdFirstCustomParam(coption);
3598 cparam;
3599 cparam = ppdNextCustomParam(coption))
3600 {
3601 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword,
3602 cparam->name);
3603
3604 if ((val = cgiGetVariable(keyword)) == NULL)
3605 return (NULL);
3606
3607 snprintf(bufptr, (size_t)(bufend - bufptr), "%s%s=", prefix, cparam->name);
3608 bufptr += strlen(bufptr);
3609 prefix = " ";
3610
3611 switch (cparam->type)
3612 {
3613 case PPD_CUSTOM_CURVE :
3614 case PPD_CUSTOM_INVCURVE :
3615 case PPD_CUSTOM_REAL :
3616 if ((number = strtod(val, NULL)) == 0.0 ||
3617 number < cparam->minimum.custom_real ||
3618 number > cparam->maximum.custom_real)
3619 return (NULL);
3620
3621 snprintf(bufptr, (size_t)(bufend - bufptr), "%g", number);
3622 break;
3623
3624 case PPD_CUSTOM_INT :
3625 if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
3626 integer == LONG_MAX ||
3627 integer < cparam->minimum.custom_int ||
3628 integer > cparam->maximum.custom_int)
3629 return (NULL);
3630
3631 snprintf(bufptr, (size_t)(bufend - bufptr), "%ld", integer);
3632 break;
3633
3634 case PPD_CUSTOM_POINTS :
3635 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
3636
3637 if ((number = strtod(val, NULL)) == 0.0 ||
3638 (uval = cgiGetVariable(keyword)) == NULL ||
3639 (strcmp(uval, "pt") && strcmp(uval, "in") &&
3640 strcmp(uval, "ft") && strcmp(uval, "cm") &&
3641 strcmp(uval, "mm") && strcmp(uval, "m")))
3642 return (NULL);
3643
3644 number_points = get_points(number, uval);
3645 if (number_points < cparam->minimum.custom_points ||
3646 number_points > cparam->maximum.custom_points)
3647 return (NULL);
3648
3649 snprintf(bufptr, (size_t)(bufend - bufptr), "%g%s", number, uval);
3650 break;
3651
3652 case PPD_CUSTOM_PASSCODE :
3653 for (uval = val; *uval; uval ++)
3654 if (!isdigit(*uval & 255))
3655 return (NULL);
3656
3657 case PPD_CUSTOM_PASSWORD :
3658 case PPD_CUSTOM_STRING :
3659 integer = (long)strlen(val);
3660 if (integer < cparam->minimum.custom_string ||
3661 integer > cparam->maximum.custom_string)
3662 return (NULL);
3663
3664 if ((bufptr + 2) > bufend)
3665 return (NULL);
3666
3667 bufend --;
3668 *bufptr++ = '\"';
3669
3670 while (*val && bufptr < bufend)
3671 {
3672 if (*val == '\\' || *val == '\"')
3673 {
3674 if ((bufptr + 1) >= bufend)
3675 return (NULL);
3676
3677 *bufptr++ = '\\';
3678 }
3679
3680 *bufptr++ = *val++;
3681 }
3682
3683 if (bufptr >= bufend)
3684 return (NULL);
3685
3686 *bufptr++ = '\"';
3687 *bufptr = '\0';
3688 bufend ++;
3689 break;
3690 }
3691
3692 bufptr += strlen(bufptr);
3693 }
3694
3695 if (bufptr == buffer || (bufend - bufptr) < 2)
3696 return (NULL);
3697
3698 memcpy(bufptr, "}", 2);
3699 }
3700
3701 return (buffer);
3702 }
3703
3704
3705 /*
3706 * 'get_points()' - Get a value in points.
3707 */
3708
3709 static double /* O - Number in points */
3710 get_points(double number, /* I - Original number */
3711 const char *uval) /* I - Units */
3712 {
3713 if (!strcmp(uval, "mm")) /* Millimeters */
3714 return (number * 72.0 / 25.4);
3715 else if (!strcmp(uval, "cm")) /* Centimeters */
3716 return (number * 72.0 / 2.54);
3717 else if (!strcmp(uval, "in")) /* Inches */
3718 return (number * 72.0);
3719 else if (!strcmp(uval, "ft")) /* Feet */
3720 return (number * 72.0 * 12.0);
3721 else if (!strcmp(uval, "m")) /* Meters */
3722 return (number * 72.0 / 0.0254);
3723 else /* Points */
3724 return (number);
3725 }
3726
3727
3728 /*
3729 * 'get_printer_ppd()' - Get an IPP Everywhere PPD file for the given URI.
3730 */
3731
3732 static char * /* O - Filename or NULL */
3733 get_printer_ppd(const char *uri, /* I - Printer URI */
3734 char *buffer, /* I - Filename buffer */
3735 size_t bufsize) /* I - Size of filename buffer */
3736 {
3737 http_t *http; /* Connection to printer */
3738 ipp_t *request, /* Get-Printer-Attributes request */
3739 *response; /* Get-Printer-Attributes response */
3740 char resolved[1024], /* Resolved URI */
3741 scheme[32], /* URI scheme */
3742 userpass[256], /* Username:password */
3743 host[256], /* Hostname */
3744 resource[256]; /* Resource path */
3745 int port; /* Port number */
3746 static const char * const pattrs[] = /* Printer attributes we need */
3747 {
3748 "all",
3749 "media-col-database"
3750 };
3751
3752
3753 /*
3754 * Connect to the printer...
3755 */
3756
3757 if (strstr(uri, "._tcp"))
3758 {
3759 /*
3760 * Resolve URI...
3761 */
3762
3763 if (!_httpResolveURI(uri, resolved, sizeof(resolved), _HTTP_RESOLVE_DEFAULT, NULL, NULL))
3764 {
3765 fprintf(stderr, "ERROR: Unable to resolve \"%s\".\n", uri);
3766 return (NULL);
3767 }
3768
3769 uri = resolved;
3770 }
3771
3772 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
3773 {
3774 fprintf(stderr, "ERROR: Bad printer URI \"%s\".\n", uri);
3775 return (NULL);
3776 }
3777
3778 http = httpConnect2(host, port, NULL, AF_UNSPEC, !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
3779 if (!http)
3780 {
3781 fprintf(stderr, "ERROR: Unable to connect to \"%s:%d\": %s\n", host, port, cupsLastErrorString());
3782 return (NULL);
3783 }
3784
3785 /*
3786 * Send a Get-Printer-Attributes request...
3787 */
3788
3789 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
3790 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
3791 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
3792 response = cupsDoRequest(http, request, resource);
3793
3794 if (!_ppdCreateFromIPP(buffer, bufsize, response))
3795 fprintf(stderr, "ERROR: Unable to create PPD file: %s\n", strerror(errno));
3796
3797 ippDelete(response);
3798 httpClose(http);
3799
3800 if (buffer[0])
3801 return (buffer);
3802 else
3803 return (NULL);
3804 }