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