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