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