]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cgi-bin/admin.c
Import CUPS 1.4svn r7023 into easysw/current.
[thirdparty/cups.git] / cgi-bin / admin.c
index aac69d973b5065c402000c2c7a8054e37979e0ba..bdf42f911ebd8179465bd473c8d3eb3c89f2c63f 100644 (file)
@@ -1,39 +1,33 @@
 /*
- * "$Id: admin.c 5425 2006-04-18 19:59:05Z mike $"
+ * "$Id: admin.c 7012 2007-10-10 21:22:45Z mike $"
  *
  *   Administration CGI for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products.
+ *   Copyright 2007 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  * Contents:
  *
  *   main()                    - Main entry for CGI.
+ *   do_add_rss_subscription() - Add a RSS subscription.
  *   do_am_class()             - Add or modify a class.
  *   do_am_printer()           - Add or modify a printer.
- *   do_config_printer()       - Configure the default options for a printer.
+ *   do_cancel_subscription()  - Cancel a subscription.
  *   do_config_server()        - Configure server settings.
  *   do_delete_class()         - Delete a class...
  *   do_delete_printer()       - Delete a printer...
  *   do_export()               - Export printers to Samba...
+ *   do_list_printers()        - List available printers...
  *   do_menu()                 - Show the main menu...
  *   do_printer_op()           - Do a printer operation.
  *   do_set_allowed_users()    - Set the allowed/denied users for a queue.
+ *   do_set_options()          - Configure the default options for a queue.
  *   do_set_sharing()          - Set printer-is-shared value...
  *   match_string()            - Return the number of matching characters.
  */
  * Local functions...
  */
 
+static void    do_add_rss_subscription(http_t *http);
 static void    do_am_class(http_t *http, int modify);
 static void    do_am_printer(http_t *http, int modify);
-static void    do_config_printer(http_t *http);
+static void    do_cancel_subscription(http_t *http);
+static void    do_set_options(http_t *http, int is_class);
 static void    do_config_server(http_t *http);
 static void    do_delete_class(http_t *http);
 static void    do_delete_printer(http_t *http);
 static void    do_export(http_t *http);
+static void    do_list_printers(http_t *http);
 static void    do_menu(http_t *http);
 static void    do_printer_op(http_t *http,
                              ipp_op_t op, const char *title);
@@ -93,7 +90,8 @@ main(int  argc,                               /* I - Number of command-line arguments */
   if (!http)
   {
     perror("ERROR: Unable to connect to cupsd");
-    fprintf(stderr, "DEBUG: cupsServer()=\"%s\"\n", cupsServer());
+    fprintf(stderr, "DEBUG: cupsServer()=\"%s\"\n",
+            cupsServer() ? cupsServer() : "(null)");
     fprintf(stderr, "DEBUG: ippPort()=%d\n", ippPort());
     fprintf(stderr, "DEBUG: cupsEncryption()=%d\n", cupsEncryption());
     exit(1);
@@ -121,7 +119,7 @@ main(int  argc,                             /* I - Number of command-line arguments */
 
     do_menu(http);
   }
-  else if ((op = cgiGetVariable("OP")) != NULL)
+  else if ((op = cgiGetVariable("OP")) != NULL && cgiIsPOST())
   {
    /*
     * Do the operation...
@@ -129,25 +127,7 @@ main(int  argc,                            /* I - Number of command-line arguments */
 
     fprintf(stderr, "DEBUG: op=\"%s\"...\n", op);
 
-    if (!strcmp(op, "redirect"))
-    {
-      const char *url;                 /* Redirection URL... */
-      char     prefix[1024];           /* URL prefix */
-
-
-      if (getenv("HTTPS"))
-        snprintf(prefix, sizeof(prefix), "https://%s:%s",
-                getenv("SERVER_NAME"), getenv("SERVER_PORT"));
-      else
-        snprintf(prefix, sizeof(prefix), "http://%s:%s",
-                getenv("SERVER_NAME"), getenv("SERVER_PORT"));
-
-      if ((url = cgiGetVariable("URL")) != NULL)
-        printf("Location: %s%s\n\n", prefix, url);
-      else
-        printf("Location: %s/admin\n\n", prefix);
-    }
-    else if (!strcmp(op, "start-printer"))
+    if (!strcmp(op, "start-printer"))
       do_printer_op(http, IPP_RESUME_PRINTER, cgiText(_("Start Printer")));
     else if (!strcmp(op, "stop-printer"))
       do_printer_op(http, IPP_PAUSE_PRINTER, cgiText(_("Stop Printer")));
@@ -167,6 +147,9 @@ main(int  argc,                             /* I - Number of command-line arguments */
       do_printer_op(http, CUPS_SET_DEFAULT, cgiText(_("Set As Default")));
     else if (!strcmp(op, "set-sharing"))
       do_set_sharing(http);
+    else if (!strcmp(op, "find-new-printers") ||
+             !strcmp(op, "list-available-printers"))
+      do_list_printers(http);
     else if (!strcmp(op, "add-class"))
       do_am_class(http, 0);
     else if (!strcmp(op, "add-printer"))
@@ -179,12 +162,18 @@ main(int  argc,                           /* I - Number of command-line arguments */
       do_delete_class(http);
     else if (!strcmp(op, "delete-printer"))
       do_delete_printer(http);
+    else if (!strcmp(op, "set-class-options"))
+      do_set_options(http, 1);
     else if (!strcmp(op, "set-printer-options"))
-      do_config_printer(http);
+      do_set_options(http, 0);
     else if (!strcmp(op, "config-server"))
       do_config_server(http);
     else if (!strcmp(op, "export-samba"))
       do_export(http);
+    else if (!strcmp(op, "add-rss-subscription"))
+      do_add_rss_subscription(http);
+    else if (!strcmp(op, "cancel-subscription"))
+      do_cancel_subscription(http);
     else
     {
      /*
@@ -196,6 +185,24 @@ main(int  argc,                            /* I - Number of command-line arguments */
       cgiEndHTML();
     }
   }
+  else if (op && !strcmp(op, "redirect"))
+  {
+    const char *url;                   /* Redirection URL... */
+    char       prefix[1024];           /* URL prefix */
+
+
+    if (getenv("HTTPS"))
+      snprintf(prefix, sizeof(prefix), "https://%s:%s",
+              getenv("SERVER_NAME"), getenv("SERVER_PORT"));
+    else
+      snprintf(prefix, sizeof(prefix), "http://%s:%s",
+              getenv("SERVER_NAME"), getenv("SERVER_PORT"));
+
+    if ((url = cgiGetVariable("URL")) != NULL)
+      printf("Location: %s%s\n\n", prefix, url);
+    else
+      printf("Location: %s/admin\n\n", prefix);
+  }
   else
   {
    /*
@@ -221,6 +228,170 @@ main(int  argc,                           /* I - Number of command-line arguments */
 }
 
 
+/*
+ * 'do_add_rss_subscription()' - Add a RSS subscription.
+ */
+
+static void
+do_add_rss_subscription(http_t *http)  /* I - HTTP connection */
+{
+  ipp_t                *request,               /* IPP request data */
+               *response;              /* IPP response data */
+  char         rss_uri[1024];          /* RSS notify-recipient URI */
+  int          num_events;             /* Number of events */
+  const char   *events[12],            /* Subscribed events */
+               *subscription_name,     /* Subscription name */
+               *printer_uri,           /* Printer URI */
+               *ptr,                   /* Pointer into name */
+               *user;                  /* Username */
+  int          max_events;             /* Maximum number of events */
+
+
+ /*
+  * See if we have all of the required information...
+  */
+
+  subscription_name = cgiGetVariable("SUBSCRIPTION_NAME");
+  printer_uri       = cgiGetVariable("PRINTER_URI");
+  num_events        = 0;
+
+  if (cgiGetVariable("EVENT_JOB_CREATED"))
+    events[num_events ++] = "job-created";
+  if (cgiGetVariable("EVENT_JOB_COMPLETED"))
+    events[num_events ++] = "job-completed";
+  if (cgiGetVariable("EVENT_JOB_STOPPED"))
+    events[num_events ++] = "job-stopped";
+  if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED"))
+    events[num_events ++] = "job-config-changed";
+  if (cgiGetVariable("EVENT_PRINTER_STOPPED"))
+    events[num_events ++] = "printer-stopped";
+  if (cgiGetVariable("EVENT_PRINTER_ADDED"))
+    events[num_events ++] = "printer-added";
+  if (cgiGetVariable("EVENT_PRINTER_MODIFIED"))
+    events[num_events ++] = "printer-modified";
+  if (cgiGetVariable("EVENT_PRINTER_DELETED"))
+    events[num_events ++] = "printer-deleted";
+  if (cgiGetVariable("EVENT_SERVER_STARTED"))
+    events[num_events ++] = "server-started";
+  if (cgiGetVariable("EVENT_SERVER_STOPPED"))
+    events[num_events ++] = "server-stopped";
+  if (cgiGetVariable("EVENT_SERVER_RESTARTED"))
+    events[num_events ++] = "server-restarted";
+  if (cgiGetVariable("EVENT_SERVER_AUDIT"))
+    events[num_events ++] = "server-audit";
+
+  if ((ptr = cgiGetVariable("MAX_EVENTS")) != NULL)
+    max_events = atoi(ptr);
+  else
+    max_events = 0;
+
+  if (!subscription_name || !printer_uri || !num_events ||
+      max_events <= 0 || max_events > 9999)
+  {
+   /*
+    * Don't have everything we need, so get the available printers
+    * and classes and (re)show the add page...
+    */
+
+    request  = ippNewRequest(CUPS_GET_PRINTERS);
+    response = cupsDoRequest(http, request, "/");
+
+    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
+
+    ippDelete(response);
+
+    cgiStartHTML(cgiText(_("Add RSS Subscription")));
+
+    cgiCopyTemplateLang("add-rss-subscription.tmpl");
+
+    cgiEndHTML();
+    return;
+  }
+
+ /*
+  * Validate the subscription name...
+  */
+
+  for (ptr = subscription_name; *ptr; ptr ++)
+    if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' ||
+        *ptr == '?' || *ptr == '#')
+      break;
+
+  if (*ptr)
+  {
+    cgiSetVariable("ERROR",
+                   cgiText(_("The subscription name may not "
+                            "contain spaces, slashes (/), question marks (?), "
+                            "or the pound sign (#).")));
+    cgiStartHTML(_("Add RSS Subscription"));
+    cgiCopyTemplateLang("error.tmpl");
+    cgiEndHTML();
+    return;
+  }
+
+ /*
+  * Add the subscription...
+  */
+
+  ptr = subscription_name + strlen(subscription_name) - 4;
+  if (ptr < subscription_name || strcmp(ptr, ".rss"))
+    httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss",
+                     NULL, NULL, 0, "/%s.rss?max_events=%d", subscription_name,
+                    max_events);
+  else
+    httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss",
+                     NULL, NULL, 0, "/%s?max_events=%d", subscription_name,
+                    max_events);
+
+  request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
+
+  if (!strcasecmp(printer_uri, "#ALL#"))
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+                 NULL, "ipp://localhost/");
+  else
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+                 NULL, printer_uri);
+
+  if ((user = getenv("REMOTE_USER")) == NULL)
+    user = "guest";
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+               NULL, user);
+
+  ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
+               "notify-recipient-uri", NULL, rss_uri);
+  ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events",
+                num_events, NULL, events);
+  ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
+                "notify-lease-duration", 0);
+
+  ippDelete(cupsDoRequest(http, request, "/"));
+
+  if (cupsLastError() == IPP_NOT_AUTHORIZED)
+  {
+    puts("Status: 401\n");
+    exit(0);
+  }
+  else if (cupsLastError() > IPP_OK_CONFLICT)
+  {
+    cgiStartHTML(_("Add RSS Subscription"));
+    cgiShowIPPError(_("Unable to add RSS subscription:"));
+  }
+  else
+  {
+   /*
+    * Redirect successful updates back to the admin page...
+    */
+
+    cgiSetVariable("refresh_page", "5;URL=/admin");
+    cgiStartHTML(_("Add RSS Subscription"));
+    cgiCopyTemplateLang("subscription-added.tmpl");
+  }
+
+  cgiEndHTML();
+}
+
+
 /*
  * 'do_am_class()' - Add or modify a class.
  */
@@ -258,14 +429,10 @@ do_am_class(http_t *http,         /* I - HTTP connection */
     *
     *    attributes-charset
     *    attributes-natural-language
-    *    printer-uri
     */
 
     request = ippNewRequest(CUPS_GET_PRINTERS);
 
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-                 NULL, "ipp://localhost/printers");
-
    /*
     * Do the request and get back a response...
     */
@@ -466,7 +633,12 @@ do_am_class(http_t *http,          /* I - HTTP connection */
 
   ippDelete(cupsDoRequest(http, request, "/admin/"));
 
-  if (cupsLastError() > IPP_OK_CONFLICT)
+  if (cupsLastError() == IPP_NOT_AUTHORIZED)
+  {
+    puts("Status: 401\n");
+    exit(0);
+  }
+  else if (cupsLastError() > IPP_OK_CONFLICT)
   {
     cgiStartHTML(title);
     cgiShowIPPError(modify ? _("Unable to modify class:") :
@@ -481,7 +653,7 @@ do_am_class(http_t *http,           /* I - HTTP connection */
     char       refresh[1024];          /* Refresh URL */
 
     cgiFormEncode(uri, name, sizeof(uri));
-    snprintf(refresh, sizeof(refresh), "5;/admin/?OP=redirect&URL=/classes/%s",
+    snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/classes/%s",
              uri);
     cgiSetVariable("refresh_page", refresh);
 
@@ -534,8 +706,9 @@ do_am_printer(http_t *http,         /* I - HTTP connection */
                };
 
 
+  ptr = cgiGetVariable("DEVICE_URI");
   fprintf(stderr, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n",
-          cgiGetVariable("DEVICE_URI"));
+          ptr ? ptr : "(null)");
 
   title = cgiText(modify ? _("Modify Printer") : _("Add Printer"));
 
@@ -567,67 +740,101 @@ do_am_printer(http_t *http,              /* I - HTTP connection */
   else
     oldinfo = NULL;
 
-  if ((name = cgiGetVariable("PRINTER_NAME")) == NULL ||
-      cgiGetVariable("PRINTER_LOCATION") == NULL)
-  {
-    cgiStartHTML(title);
+  file = cgiGetFile();
 
-    if (modify)
-    {
-     /*
-      * Update the location and description of an existing printer...
-      */
+  if (file)
+  {
+    fprintf(stderr, "DEBUG: file->tempfile=%s\n", file->tempfile);
+    fprintf(stderr, "DEBUG: file->name=%s\n", file->name);
+    fprintf(stderr, "DEBUG: file->filename=%s\n", file->filename);
+    fprintf(stderr, "DEBUG: file->mimetype=%s\n", file->mimetype);
+  }
 
-      if (oldinfo)
-       cgiSetIPPVars(oldinfo, NULL, NULL, NULL, 0);
+  if ((name = cgiGetVariable("PRINTER_NAME")) != NULL)
+  {
+    for (ptr = name; *ptr; ptr ++)
+      if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
+       break;
 
-      cgiCopyTemplateLang("modify-printer.tmpl");
+    if (*ptr || ptr == name || strlen(name) > 127)
+    {
+      cgiSetVariable("ERROR",
+                    cgiText(_("The printer name may only contain up to "
+                              "127 printable characters and may not "
+                              "contain spaces, slashes (/), or the "
+                              "pound sign (#).")));
+      cgiStartHTML(title);
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
+      return;
     }
-    else
+  }
+
+  if ((var = cgiGetVariable("DEVICE_URI")) != NULL)
+  {
+    if ((uriptr = strrchr(var, '|')) != NULL)
     {
      /*
-      * Get the name, location, and description for a new printer...
+      * Extract make and make/model from device URI string...
       */
 
-      cgiCopyTemplateLang("add-printer.tmpl");
-    }
+      char     make[1024],             /* Make string */
+               *makeptr;               /* Pointer into make */
 
-    cgiEndHTML();
 
-    if (oldinfo)
-      ippDelete(oldinfo);
+      *uriptr++ = '\0';
 
-    return;
-  }
+      strlcpy(make, uriptr, sizeof(make));
 
-  for (ptr = name; *ptr; ptr ++)
-    if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
-      break;
+      if ((makeptr = strchr(make, ' ')) != NULL)
+        *makeptr = '\0';
+      else if ((makeptr = strchr(make, '-')) != NULL)
+        *makeptr = '\0';
+      else if (!strncasecmp(make, "laserjet", 8) ||
+               !strncasecmp(make, "deskjet", 7) ||
+               !strncasecmp(make, "designjet", 9))
+        strcpy(make, "HP");
+      else if (!strncasecmp(make, "phaser", 6))
+        strcpy(make, "Xerox");
+      else if (!strncasecmp(make, "stylus", 6))
+        strcpy(make, "Epson");
+      else
+        strcpy(make, "Generic");
 
-  if (*ptr || ptr == name || strlen(name) > 127)
-  {
-    cgiSetVariable("ERROR",
-                   cgiText(_("The printer name may only contain up to "
-                            "127 printable characters and may not "
-                            "contain spaces, slashes (/), or the "
-                            "pound sign (#).")));
-    cgiStartHTML(title);
-    cgiCopyTemplateLang("error.tmpl");
-    cgiEndHTML();
-    return;
-  }
+      if (!cgiGetVariable("CURRENT_MAKE"))
+        cgiSetVariable("CURRENT_MAKE", make);
 
-  file = cgiGetFile();
+      cgiSetVariable("PPD_MAKE", make);
 
-  if (file)
-  {
-    fprintf(stderr, "DEBUG: file->tempfile=%s\n", file->tempfile);
-    fprintf(stderr, "DEBUG: file->name=%s\n", file->name);
-    fprintf(stderr, "DEBUG: file->filename=%s\n", file->filename);
-    fprintf(stderr, "DEBUG: file->mimetype=%s\n", file->mimetype);
+      if (!cgiGetVariable("CURRENT_MAKE_AND_MODEL"))
+        cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr);
+
+      if (!modify)
+      {
+        char   template[128],          /* Template name */
+               *tptr;                  /* Pointer into template name */
+
+       cgiSetVariable("PRINTER_INFO", uriptr);
+
+       for (tptr = template;
+            tptr < (template + sizeof(template) - 1) && *uriptr;
+            uriptr ++)
+         if (isalnum(*uriptr & 255) || *uriptr == '_' || *uriptr == '-' ||
+             *uriptr == '.')
+           *tptr++ = *uriptr;
+         else if ((*uriptr == ' ' || *uriptr == '/') && tptr[-1] != '_')
+           *tptr++ = '_';
+         else if (*uriptr == '?' || *uriptr == '(')
+           break;
+
+        *tptr = '\0';
+
+        cgiSetVariable("TEMPLATE_NAME", template);
+      }
+    }
   }
 
-  if ((var = cgiGetVariable("DEVICE_URI")) == NULL)
+  if (!var)
   {
    /*
     * Build a CUPS_GET_DEVICES request, which requires the following
@@ -649,8 +856,6 @@ do_am_printer(http_t *http,         /* I - HTTP connection */
     * Do the request and get back a response...
     */
 
-    fprintf(stderr, "DEBUG: http=%p (%s)\n", http, http->hostname);
-
     if ((response = cupsDoRequest(http, request, "/")) != NULL)
     {
       fputs("DEBUG: Got device list!\n", stderr);
@@ -726,6 +931,37 @@ do_am_printer(http_t *http,                /* I - HTTP connection */
     cgiCopyTemplateLang("choose-serial.tmpl");
     cgiEndHTML();
   }
+  else if (!name || !cgiGetVariable("PRINTER_LOCATION"))
+  {
+    cgiStartHTML(title);
+
+    if (modify)
+    {
+     /*
+      * Update the location and description of an existing printer...
+      */
+
+      if (oldinfo)
+       cgiSetIPPVars(oldinfo, NULL, NULL, NULL, 0);
+
+      cgiCopyTemplateLang("modify-printer.tmpl");
+    }
+    else
+    {
+     /*
+      * Get the name, location, and description for a new printer...
+      */
+
+      cgiCopyTemplateLang("add-printer.tmpl");
+    }
+
+    cgiEndHTML();
+
+    if (oldinfo)
+      ippDelete(oldinfo);
+
+    return;
+  }
   else if (!file && (var = cgiGetVariable("PPD_NAME")) == NULL)
   {
     if (modify)
@@ -788,39 +1024,6 @@ do_am_printer(http_t *http,               /* I - HTTP connection */
                strerror(errno));
       }
     }
-    else if ((uriptr = strrchr(cgiGetVariable("DEVICE_URI"), '|')) != NULL)
-    {
-     /*
-      * Extract make and make/model from device URI string...
-      */
-
-      char     make[1024],             /* Make string */
-               *makeptr;               /* Pointer into make */
-
-
-      *uriptr++ = '\0';
-
-      strlcpy(make, uriptr, sizeof(make));
-
-      if ((makeptr = strchr(make, ' ')) != NULL)
-        *makeptr = '\0';
-      else if ((makeptr = strchr(make, '-')) != NULL)
-        *makeptr = '\0';
-      else if (!strncasecmp(make, "laserjet", 8) ||
-               !strncasecmp(make, "deskjet", 7) ||
-               !strncasecmp(make, "designjet", 9))
-        strcpy(make, "HP");
-      else if (!strncasecmp(make, "phaser", 6))
-        strcpy(make, "Xerox");
-      else if (!strncasecmp(make, "stylus", 6))
-        strcpy(make, "Epson");
-      else
-        strcpy(make, "Generic");
-
-      cgiSetVariable("CURRENT_MAKE", make);
-      cgiSetVariable("PPD_MAKE", make);
-      cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr);
-    }
 
    /*
     * Build a CUPS_GET_PPDS request, which requires the following
@@ -836,7 +1039,9 @@ do_am_printer(http_t *http,                /* I - HTTP connection */
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                  NULL, "ipp://localhost/printers/");
 
-    if ((var = cgiGetVariable("PPD_MAKE")) != NULL)
+    if ((var = cgiGetVariable("CURRENT_MAKE")) == NULL)
+      var = cgiGetVariable("PPD_MAKE");
+    if (var)
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
                    "ppd-make", NULL, var);
     else
@@ -876,7 +1081,7 @@ do_am_printer(http_t *http,                /* I - HTTP connection */
        cgiCopyTemplateLang("choose-make.tmpl");
         cgiEndHTML();
       }
-      else if (!var)
+      else if (!var || cgiGetVariable("SELECT_MAKE"))
       {
         cgiStartHTML(title);
        cgiCopyTemplateLang("choose-make.tmpl");
@@ -1019,16 +1224,21 @@ do_am_printer(http_t *http,             /* I - HTTP connection */
     else
       ippDelete(cupsDoRequest(http, request, "/admin/"));
 
-    if (cupsLastError() > IPP_OK_CONFLICT)
+    if (cupsLastError() == IPP_NOT_AUTHORIZED)
+    {
+      puts("Status: 401\n");
+      exit(0);
+    }
+    else if (cupsLastError() > IPP_OK_CONFLICT)
     {
       cgiStartHTML(title);
       cgiShowIPPError(modify ? _("Unable to modify printer:") :
                                _("Unable to add printer:"));
     }
-    else
+    else if (modify)
     {
      /*
-      * Redirect successful updates back to the printer or set-options pages...
+      * Redirect successful updates back to the printer page...
       */
 
       char     refresh[1024];          /* Refresh URL */
@@ -1036,21 +1246,24 @@ do_am_printer(http_t *http,             /* I - HTTP connection */
 
       cgiFormEncode(uri, name, sizeof(uri));
 
-      if (modify)
-       snprintf(refresh, sizeof(refresh),
-                "5;/admin/?OP=redirect&URL=/printers/%s", uri);
-      else
-       snprintf(refresh, sizeof(refresh),
-                "5;/admin/?OP=set-printer-options&PRINTER_NAME=%s", uri);
+      snprintf(refresh, sizeof(refresh),
+              "5;/admin/?OP=redirect&URL=/printers/%s", uri);
 
       cgiSetVariable("refresh_page", refresh);
 
       cgiStartHTML(title);
 
-      if (modify)
-        cgiCopyTemplateLang("printer-modified.tmpl");
-      else
-        cgiCopyTemplateLang("printer-added.tmpl");
+      cgiCopyTemplateLang("printer-modified.tmpl");
+    }
+    else
+    {
+     /*
+      * Set the printer options...
+      */
+
+      cgiSetVariable("OP", "set-printer-options");
+      do_set_options(http, 0);
+      return;
     }
 
     cgiEndHTML();
@@ -1062,792 +1275,488 @@ do_am_printer(http_t *http,           /* I - HTTP connection */
 
 
 /*
- * 'do_config_printer()' - Configure the default options for a printer.
+ * 'do_cancel_subscription()' - Cancel a subscription.
  */
 
 static void
-do_config_printer(http_t *http)                /* I - HTTP connection */
+do_cancel_subscription(http_t *http)/* I - HTTP connection */
 {
-  int          i, j, k, m;             /* Looping vars */
-  int          have_options;           /* Have options? */
-  ipp_t                *request,               /* IPP request */
-               *response;              /* IPP response */
-  ipp_attribute_t *attr;               /* IPP attribute */
-  char         uri[HTTP_MAX_URI];      /* Job URI */
-  const char   *var;                   /* Variable value */
-  const char   *printer;               /* Printer printer name */
-  const char   *filename;              /* PPD filename */
-  char         tempfile[1024];         /* Temporary filename */
-  cups_file_t  *in,                    /* Input file */
-               *out;                   /* Output file */
-  char         line[1024];             /* Line from PPD file */
-  char         keyword[1024],          /* Keyword from Default line */
-               *keyptr;                /* Pointer into keyword... */
-  ppd_file_t   *ppd;                   /* PPD file */
-  ppd_group_t  *group;                 /* Option group */
-  ppd_option_t *option;                /* Option */
-  ppd_attr_t   *protocol;              /* cupsProtocol attribute */
-  const char   *title;                 /* Page title */
-
-
-  title = cgiText(_("Set Printer Options"));
+  ipp_t                *request;               /* IPP request data */
+  const char   *var,                   /* Form variable */
+               *user;                  /* Username */
+  int          id;                     /* Subscription ID */
 
-  fprintf(stderr, "DEBUG: do_config_printer(http=%p)\n", http);
 
  /*
-  * Get the printer name...
+  * See if we have all of the required information...
   */
 
-  if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, "/printers/%s", printer);
+  if ((var = cgiGetVariable("NOTIFY_SUBSCRIPTION_ID")) != NULL)
+    id = atoi(var);
   else
+    id = 0;
+
+  if (id <= 0)
   {
-    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
-    cgiStartHTML(title);
+    cgiSetVariable("ERROR", cgiText(_("Bad subscription ID!")));
+    cgiStartHTML(_("Cancel RSS Subscription"));
     cgiCopyTemplateLang("error.tmpl");
     cgiEndHTML();
     return;
   }
 
-  fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
-
  /*
-  * Get the PPD file...
+  * Cancel the subscription...
   */
 
-  if ((filename = cupsGetPPD2(http, printer)) == NULL)
-  {
-    fputs("DEBUG: No PPD file!?!\n", stderr);
+  request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
 
-    cgiStartHTML(title);
-    cgiShowIPPError(_("Unable to get PPD file!"));
-    cgiEndHTML();
-    return;
-  }
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, "ipp://localhost/");
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+                "notify-subscription-id", id);
 
-  fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
+  if ((user = getenv("REMOTE_USER")) == NULL)
+    user = "guest";
 
-  if ((ppd = ppdOpenFile(filename)) == NULL)
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+               NULL, user);
+
+  ippDelete(cupsDoRequest(http, request, "/"));
+
+  if (cupsLastError() == IPP_NOT_AUTHORIZED)
   {
-    cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i)));
-    cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file:")));
-    cgiStartHTML(title);
-    cgiCopyTemplateLang("error.tmpl");
-    cgiEndHTML();
-    return;
+    puts("Status: 401\n");
+    exit(0);
+  }
+  else if (cupsLastError() > IPP_OK_CONFLICT)
+  {
+    cgiStartHTML(_("Cancel RSS Subscription"));
+    cgiShowIPPError(_("Unable to cancel RSS subscription:"));
   }
-
-  if (cgiGetVariable("job_sheets_start") != NULL ||
-      cgiGetVariable("job_sheets_end") != NULL)
-    have_options = 1;
   else
-    have_options = 0;
+  {
+   /*
+    * Redirect successful updates back to the admin page...
+    */
 
-  ppdMarkDefaults(ppd);
+    cgiSetVariable("refresh_page", "5;URL=/admin");
+    cgiStartHTML(_("Cancel RSS Subscription"));
+    cgiCopyTemplateLang("subscription-canceled.tmpl");
+  }
 
-  DEBUG_printf(("<P>ppd->num_groups = %d\n"
-                "<UL>\n", ppd->num_groups));
+  cgiEndHTML();
+}
 
-  for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
-  {
-    DEBUG_printf(("<LI>%s<UL>\n", group->text));
 
-    for (j = group->num_options, option = group->options;
-         j > 0;
-        j --, option ++)
-      if ((var = cgiGetVariable(option->keyword)) != NULL)
-      {
-        DEBUG_printf(("<LI>%s = \"%s\"</LI>\n", option->keyword, var));
-        have_options = 1;
-       ppdMarkOption(ppd, option->keyword, var);
-      }
-#ifdef DEBUG
-      else
-        printf("<LI>%s not defined!</LI>\n", option->keyword);
-#endif /* DEBUG */
+/*
+ * 'do_config_server()' - Configure server settings.
+ */
 
-    DEBUG_puts("</UL></LI>");
-  }
+static void
+do_config_server(http_t *http)         /* I - HTTP connection */
+{
+  if (cgiGetVariable("CHANGESETTINGS"))
+  {
+   /*
+    * Save basic setting changes...
+    */
+
+    int                        num_settings;   /* Number of server settings */
+    cups_option_t      *settings;      /* Server settings */
+    const char         *debug_logging, /* DEBUG_LOGGING value */
+                       *remote_admin,  /* REMOTE_ADMIN value */
+                       *remote_any,    /* REMOTE_ANY value */
+                       *remote_printers,
+                                       /* REMOTE_PRINTERS value */
+                       *share_printers,/* SHARE_PRINTERS value */
+#ifdef HAVE_GSSAPI
+                       *default_auth_type,
+                                       /* DefaultAuthType value */
+#endif /* HAVE_GSSAPI */
+                       *user_cancel_any;
+                                       /* USER_CANCEL_ANY value */
 
-  DEBUG_printf(("</UL>\n"
-                "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd)));
 
-  if (!have_options || ppdConflicts(ppd))
-  {
    /*
-    * Show the options to the user...
+    * Get the checkbox values from the form...
     */
 
-    fputs("DEBUG: Showing options...\n", stderr);
+    debug_logging     = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0";
+    remote_admin      = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0";
+    remote_any        = cgiGetVariable("REMOTE_ANY") ? "1" : "0";
+    remote_printers   = cgiGetVariable("REMOTE_PRINTERS") ? "1" : "0";
+    share_printers    = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0";
+    user_cancel_any   = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0";
+#ifdef HAVE_GSSAPI
+    default_auth_type = cgiGetVariable("KERBEROS") ? "Negotiate" : "Basic";
 
-    ppdLocalize(ppd);
+    fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type);
+#endif /* HAVE_GSSAPI */
 
-    cgiStartHTML(cgiText(_("Set Printer Options")));
-    cgiCopyTemplateLang("set-printer-options-header.tmpl");
+   /*
+    * Get the current server settings...
+    */
 
-    if (ppdConflicts(ppd))
+    if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
     {
-      for (i = ppd->num_groups, k = 0, group = ppd->groups;
-           i > 0;
-          i --, group ++)
-       for (j = group->num_options, option = group->options;
-            j > 0;
-            j --, option ++)
-          if (option->conflicted)
-         {
-           cgiSetArray("ckeyword", k, option->keyword);
-           cgiSetArray("ckeytext", k, option->text);
-           k ++;
-         }
-
-      cgiCopyTemplateLang("option-conflict.tmpl");
+      cgiStartHTML(cgiText(_("Change Settings")));
+      cgiSetVariable("MESSAGE",
+                     cgiText(_("Unable to change server settings:")));
+      cgiSetVariable("ERROR", cupsLastErrorString());
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
+      return;
     }
 
-    for (i = ppd->num_groups, group = ppd->groups;
-        i > 0;
-        i --, group ++)
+   /*
+    * See if the settings have changed...
+    */
+
+    if (strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING,
+                                            num_settings, settings)) ||
+        strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN,
+                                           num_settings, settings)) ||
+        strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY,
+                                         num_settings, settings)) ||
+        strcmp(remote_printers, cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS,
+                                              num_settings, settings)) ||
+        strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS,
+                                             num_settings, settings)) ||
+#ifdef HAVE_GSSAPI
+        !cupsGetOption("DefaultAuthType", num_settings, settings) ||
+       strcmp(default_auth_type, cupsGetOption("DefaultAuthType",
+                                               num_settings, settings)) ||
+#endif /* HAVE_GSSAPI */
+        strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY,
+                                              num_settings, settings)))
     {
-      if (!strcmp(group->name, "InstallableOptions"))
-       cgiSetVariable("GROUP", cgiText(_("Options Installed")));
-      else
-       cgiSetVariable("GROUP", group->text);
+     /*
+      * Settings *have* changed, so save the changes...
+      */
 
-      cgiCopyTemplateLang("option-header.tmpl");
-      
-      for (j = group->num_options, option = group->options;
-           j > 0;
-          j --, option ++)
+      cupsFreeOptions(num_settings, settings);
+
+      num_settings = 0;
+      num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
+                                   debug_logging, num_settings, &settings);
+      num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
+                                   remote_admin, num_settings, &settings);
+      num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
+                                   remote_any, num_settings, &settings);
+      num_settings = cupsAddOption(CUPS_SERVER_REMOTE_PRINTERS,
+                                   remote_printers, num_settings, &settings);
+      num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
+                                   share_printers, num_settings, &settings);
+      num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
+                                   user_cancel_any, num_settings, &settings);
+#ifdef HAVE_GSSAPI
+      num_settings = cupsAddOption("DefaultAuthType", default_auth_type,
+                                   num_settings, &settings);
+#endif /* HAVE_GSSAPI */
+
+      if (!cupsAdminSetServerSettings(http, num_settings, settings))
       {
-        if (!strcmp(option->keyword, "PageRegion"))
-         continue;
-
-        cgiSetVariable("KEYWORD", option->keyword);
-        cgiSetVariable("KEYTEXT", option->text);
-           
-       if (option->conflicted)
-         cgiSetVariable("CONFLICTED", "1");
-       else
-         cgiSetVariable("CONFLICTED", "0");
-
-       cgiSetSize("CHOICES", 0);
-       cgiSetSize("TEXT", 0);
-       for (k = 0, m = 0; k < option->num_choices; k ++)
+        if (cupsLastError() == IPP_NOT_AUTHORIZED)
        {
-        /*
-         * Hide custom option values...
-         */
-
-         if (!strcmp(option->choices[k].choice, "Custom"))
-           continue;
-
-         cgiSetArray("CHOICES", m, option->choices[k].choice);
-         cgiSetArray("TEXT", m, option->choices[k].text);
-
-          m ++;
-
-          if (option->choices[k].marked)
-           cgiSetVariable("DEFCHOICE", option->choices[k].choice);
+         puts("Status: 401\n");
+         exit(0);
        }
 
-        switch (option->ui)
-       {
-         case PPD_UI_BOOLEAN :
-              cgiCopyTemplateLang("option-boolean.tmpl");
-              break;
-         case PPD_UI_PICKONE :
-              cgiCopyTemplateLang("option-pickone.tmpl");
-              break;
-         case PPD_UI_PICKMANY :
-              cgiCopyTemplateLang("option-pickmany.tmpl");
-              break;
-       }
+       cgiStartHTML(cgiText(_("Change Settings")));
+       cgiSetVariable("MESSAGE",
+                       cgiText(_("Unable to change server settings:")));
+       cgiSetVariable("ERROR", cupsLastErrorString());
+       cgiCopyTemplateLang("error.tmpl");
       }
+      else
+      {
+       cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
+       cgiStartHTML(cgiText(_("Change Settings")));
+       cgiCopyTemplateLang("restart.tmpl");
+      }
+    }
+    else
+    {
+     /*
+      * No changes...
+      */
 
-      cgiCopyTemplateLang("option-trailer.tmpl");
+      cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
+      cgiStartHTML(cgiText(_("Change Settings")));
+      cgiCopyTemplateLang("norestart.tmpl");
     }
 
+    cupsFreeOptions(num_settings, settings);
+
+    cgiEndHTML();
+  }
+  else if (cgiGetVariable("SAVECHANGES") && cgiGetVariable("CUPSDCONF"))
+  {
    /*
-    * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
-    * following attributes:
-    *
-    *    attributes-charset
-    *    attributes-natural-language
-    *    printer-uri
+    * Save hand-edited config file...
     */
 
-    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+    http_status_t status;              /* PUT status */
+    char       tempfile[1024];         /* Temporary new cupsd.conf */
+    int                tempfd;                 /* Temporary file descriptor */
+    cups_file_t        *temp;                  /* Temporary file */
+    const char *start,                 /* Start of line */
+               *end;                   /* End of line */
 
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, "/printers/%s", printer);
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-                 NULL, uri);
 
    /*
-    * Do the request and get back a response...
+    * Create a temporary file for the new cupsd.conf file...
     */
 
-    if ((response = cupsDoRequest(http, request, "/")) != NULL)
+    if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
     {
-      if ((attr = ippFindAttribute(response, "job-sheets-supported",
-                                   IPP_TAG_ZERO)) != NULL)
-      {
-       /*
-       * Add the job sheets options...
-       */
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
+      cgiSetVariable("ERROR", strerror(errno));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
+      
+      perror(tempfile);
+      return;
+    }
 
-       cgiSetVariable("GROUP", cgiText(_("Banners")));
-       cgiCopyTemplateLang("option-header.tmpl");
+    if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL)
+    {
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
+      cgiSetVariable("ERROR", strerror(errno));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
+      
+      perror(tempfile);
+      close(tempfd);
+      unlink(tempfile);
+      return;
+    }
 
-       cgiSetSize("CHOICES", attr->num_values);
-       cgiSetSize("TEXT", attr->num_values);
-       for (k = 0; k < attr->num_values; k ++)
-       {
-         cgiSetArray("CHOICES", k, attr->values[k].string.text);
-         cgiSetArray("TEXT", k, attr->values[k].string.text);
-       }
+   /*
+    * Copy the cupsd.conf text from the form variable...
+    */
 
-        attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
+    start = cgiGetVariable("CUPSDCONF");
+    while (start)
+    {
+      if ((end = strstr(start, "\r\n")) == NULL)
+        if ((end = strstr(start, "\n")) == NULL)
+         end = start + strlen(start);
 
-        cgiSetVariable("KEYWORD", "job_sheets_start");
-       cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner")));
-        cgiSetVariable("DEFCHOICE", attr == NULL ?
-                                   "" : attr->values[0].string.text);
+      cupsFileWrite(temp, start, end - start);
+      cupsFilePutChar(temp, '\n');
 
-       cgiCopyTemplateLang("option-pickone.tmpl");
+      if (*end == '\r')
+        start = end + 2;
+      else if (*end == '\n')
+        start = end + 1;
+      else
+        start = NULL;
+    }
 
-        cgiSetVariable("KEYWORD", "job_sheets_end");
-       cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner")));
-        cgiSetVariable("DEFCHOICE", attr == NULL && attr->num_values > 1 ?
-                                   "" : attr->values[1].string.text);
+    cupsFileClose(temp);
 
-       cgiCopyTemplateLang("option-pickone.tmpl");
+   /*
+    * Upload the configuration file to the server...
+    */
 
-       cgiCopyTemplateLang("option-trailer.tmpl");
-      }
+    status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
 
-      if (ippFindAttribute(response, "printer-error-policy-supported",
-                           IPP_TAG_ZERO) ||
-          ippFindAttribute(response, "printer-op-policy-supported",
-                          IPP_TAG_ZERO))
-      {
-       /*
-       * Add the error and operation policy options...
-       */
+    if (status == HTTP_UNAUTHORIZED)
+    {
+      puts("Status: 401\n");
+      unlink(tempfile);
+      exit(0);
+    }
+    else if (status != HTTP_CREATED)
+    {
+      cgiSetVariable("MESSAGE",
+                     cgiText(_("Unable to upload cupsd.conf file:")));
+      cgiSetVariable("ERROR", httpStatus(status));
 
-       cgiSetVariable("GROUP", cgiText(_("Policies")));
-       cgiCopyTemplateLang("option-header.tmpl");
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiCopyTemplateLang("error.tmpl");
+    }
+    else
+    {
+      cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
 
-       /*
-        * Error policy...
-       */
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiCopyTemplateLang("restart.tmpl");
+    }
 
-        attr = ippFindAttribute(response, "printer-error-policy-supported",
-                               IPP_TAG_ZERO);
-
-        if (attr)
-       {
-         cgiSetSize("CHOICES", attr->num_values);
-         cgiSetSize("TEXT", attr->num_values);
-         for (k = 0; k < attr->num_values; k ++)
-         {
-           cgiSetArray("CHOICES", k, attr->values[k].string.text);
-           cgiSetArray("TEXT", k, attr->values[k].string.text);
-         }
-
-          attr = ippFindAttribute(response, "printer-error-policy",
-                                 IPP_TAG_ZERO);
-
-          cgiSetVariable("KEYWORD", "printer_error_policy");
-         cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
-          cgiSetVariable("DEFCHOICE", attr == NULL ?
-                                     "" : attr->values[0].string.text);
-        }
-
-       cgiCopyTemplateLang("option-pickone.tmpl");
-
-       /*
-        * Operation policy...
-       */
-
-        attr = ippFindAttribute(response, "printer-op-policy-supported",
-                               IPP_TAG_ZERO);
-
-        if (attr)
-       {
-         cgiSetSize("CHOICES", attr->num_values);
-         cgiSetSize("TEXT", attr->num_values);
-         for (k = 0; k < attr->num_values; k ++)
-         {
-           cgiSetArray("CHOICES", k, attr->values[k].string.text);
-           cgiSetArray("TEXT", k, attr->values[k].string.text);
-         }
-
-          attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
-
-          cgiSetVariable("KEYWORD", "printer_op_policy");
-         cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
-          cgiSetVariable("DEFCHOICE", attr == NULL ?
-                                     "" : attr->values[0].string.text);
-
-         cgiCopyTemplateLang("option-pickone.tmpl");
-        }
+    cgiEndHTML();
 
-       cgiCopyTemplateLang("option-trailer.tmpl");
-      }
+    unlink(tempfile);
+  }
+  else
+  {
+    struct stat        info;                   /* cupsd.conf information */
+    cups_file_t        *cupsd;                 /* cupsd.conf file */
+    char       *buffer,                /* Buffer for entire file */
+               *bufptr,                /* Pointer into buffer */
+               *bufend;                /* End of buffer */
+    int                ch;                     /* Character from file */
+    char       filename[1024];         /* Filename */
+    const char *server_root;           /* Location of config files */
 
-      ippDelete(response);
-    }
 
    /*
-    * Binary protocol support...
+    * Locate the cupsd.conf file...
     */
 
-    if (ppd->protocols && strstr(ppd->protocols, "BCP"))
-    {
-      protocol = ppdFindAttr(ppd, "cupsProtocol", NULL);
-
-      cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol")));
-      cgiCopyTemplateLang("option-header.tmpl");
-
-      cgiSetSize("CHOICES", 2);
-      cgiSetSize("TEXT", 2);
-      cgiSetArray("CHOICES", 0, "None");
-      cgiSetArray("TEXT", 0, cgiText(_("None")));
-
-      if (strstr(ppd->protocols, "TBCP"))
-      {
-       cgiSetArray("CHOICES", 1, "TBCP");
-       cgiSetArray("TEXT", 1, "TBCP");
-      }
-      else
-      {
-       cgiSetArray("CHOICES", 1, "BCP");
-       cgiSetArray("TEXT", 1, "BCP");
-      }
-
-      cgiSetVariable("KEYWORD", "protocol");
-      cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol")));
-      cgiSetVariable("DEFCHOICE", protocol ? protocol->value : "None");
-
-      cgiCopyTemplateLang("option-pickone.tmpl");
+    if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
+      server_root = CUPS_SERVERROOT;
 
-      cgiCopyTemplateLang("option-trailer.tmpl");
-    }
+    snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root);
 
-    cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
-    cgiEndHTML();
-  }
-  else
-  {
    /*
-    * Set default options...
+    * Figure out the size...
     */
 
-    fputs("DEBUG: Setting options...\n", stderr);
-
-    out = cupsTempFile2(tempfile, sizeof(tempfile));
-    in  = cupsFileOpen(filename, "r");
-
-    if (!in || !out)
+    if (stat(filename, &info))
     {
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE",
+                     cgiText(_("Unable to access cupsd.conf file:")));
       cgiSetVariable("ERROR", strerror(errno));
-      cgiStartHTML(cgiText(_("Set Printer Options")));
       cgiCopyTemplateLang("error.tmpl");
       cgiEndHTML();
 
-      if (in)
-        cupsFileClose(in);
-
-      if (out)
-      {
-        cupsFileClose(out);
-       unlink(tempfile);
-      }
-
-      unlink(filename);
+      perror(filename);
       return;
     }
 
-    while (cupsFileGets(in, line, sizeof(line)))
+    if (info.st_size > (1024 * 1024))
     {
-      if (!strncmp(line, "*cupsProtocol:", 14) && cgiGetVariable("protocol"))
-        continue;
-      else if (strncmp(line, "*Default", 8))
-        cupsFilePrintf(out, "%s\n", line);
-      else
-      {
-       /*
-        * Get default option name...
-       */
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE",
+                     cgiText(_("Unable to access cupsd.conf file:")));
+      cgiSetVariable("ERROR",
+                     cgiText(_("Unable to edit cupsd.conf files larger than "
+                              "1MB!")));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
 
-        strlcpy(keyword, line + 8, sizeof(keyword));
+      fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename,
+              (long)info.st_size);
+      return;
+    }
 
-       for (keyptr = keyword; *keyptr; keyptr ++)
-         if (*keyptr == ':' || isspace(*keyptr & 255))
-           break;
+   /*
+    * Open the cupsd.conf file...
+    */
 
-        *keyptr = '\0';
+    if ((cupsd = cupsFileOpen(filename, "r")) == NULL)
+    {
+     /*
+      * Unable to open - log an error...
+      */
 
-        if (!strcmp(keyword, "PageRegion"))
-         var = cgiGetVariable("PageSize");
-       else
-         var = cgiGetVariable(keyword);
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE",
+                     cgiText(_("Unable to access cupsd.conf file:")));
+      cgiSetVariable("ERROR", strerror(errno));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
 
-        if (var != NULL)
-         cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
-       else
-         cupsFilePrintf(out, "%s\n", line);
-      }
+      perror(filename);
+      return;
     }
 
-    if ((var = cgiGetVariable("protocol")) != NULL)
-      cupsFilePrintf(out, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
-
-    cupsFileClose(in);
-    cupsFileClose(out);
-
    /*
-    * Build a CUPS_ADD_PRINTER request, which requires the following
-    * attributes:
-    *
-    *    attributes-charset
-    *    attributes-natural-language
-    *    printer-uri
-    *    job-sheets-default
-    *    [ppd file]
+    * Allocate memory and load the file into a string buffer...
     */
 
-    request = ippNewRequest(CUPS_ADD_PRINTER);
-
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, "/printers/%s",
-                    cgiGetVariable("PRINTER_NAME"));
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-                 NULL, uri);
-
-    attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                         "job-sheets-default", 2, NULL, NULL);
-    attr->values[0].string.text = strdup(cgiGetVariable("job_sheets_start"));
-    attr->values[1].string.text = strdup(cgiGetVariable("job_sheets_end"));
+    buffer = calloc(1, info.st_size + 1);
 
-    if ((var = cgiGetVariable("printer_error_policy")) != NULL)
-      attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                          "printer-error-policy", NULL, var);
+    cupsFileRead(cupsd, buffer, info.st_size);
+    cupsFileClose(cupsd);
 
-    if ((var = cgiGetVariable("printer_op_policy")) != NULL)
-      attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                          "printer-op-policy", NULL, var);
+    cgiSetVariable("CUPSDCONF", buffer);
+    free(buffer);
 
    /*
-    * Do the request and get back a response...
+    * Then get the default cupsd.conf file and put that into a string as
+    * well...
     */
 
-    ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile));
+    strlcat(filename, ".default", sizeof(filename));
 
-    if (cupsLastError() > IPP_OK_CONFLICT)
-    {
-      cgiStartHTML(title);
-      cgiShowIPPError(_("Unable to set options:"));
-    }
-    else
+    if (!stat(filename, &info) && info.st_size < (1024 * 1024) &&
+        (cupsd = cupsFileOpen(filename, "r")) != NULL)
     {
-     /*
-      * Redirect successful updates back to the printer page...
-      */
-
-      char     refresh[1024];          /* Refresh URL */
+      buffer = calloc(1, 2 * info.st_size + 1);
+      bufend = buffer + 2 * info.st_size - 1;
 
+      for (bufptr = buffer;
+           bufptr < bufend && (ch = cupsFileGetChar(cupsd)) != EOF;)
+      {
+        if (ch == '\\' || ch == '\"')
+       {
+         *bufptr++ = '\\';
+         *bufptr++ = ch;
+       }
+       else if (ch == '\n')
+       {
+         *bufptr++ = '\\';
+         *bufptr++ = 'n';
+       }
+       else if (ch == '\t')
+       {
+         *bufptr++ = '\\';
+         *bufptr++ = 't';
+       }
+       else if (ch >= ' ')
+         *bufptr++ = ch;
+      }
 
-      cgiFormEncode(uri, printer, sizeof(uri));
-      snprintf(refresh, sizeof(refresh),
-               "5;/admin/?OP=redirect&URL=/printers/%s", uri);
-      cgiSetVariable("refresh_page", refresh);
+      *bufptr = '\0';
 
-      cgiStartHTML(title);
+      cupsFileClose(cupsd);
 
-      cgiCopyTemplateLang("printer-configured.tmpl");
+      cgiSetVariable("CUPSDCONF_DEFAULT", buffer);
+      free(buffer);
     }
 
-    cgiEndHTML();
+   /*
+    * Show the current config file...
+    */
 
-    unlink(tempfile);
-  }
+    cgiStartHTML(cgiText(_("Edit Configuration File")));
 
-  unlink(filename);
+    cgiCopyTemplateLang("edit-config.tmpl");
+
+    cgiEndHTML();
+  }
 }
 
 
 /*
- * 'do_config_server()' - Configure server settings.
+ * 'do_delete_class()' - Delete a class...
  */
 
 static void
-do_config_server(http_t *http)         /* I - HTTP connection */
+do_delete_class(http_t *http)          /* I - HTTP connection */
 {
-  if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF"))
-  {
-   /*
-    * Save basic setting changes...
-    */
-
-    int                        num_settings;   /* Number of server settings */
-    cups_option_t      *settings;      /* Server settings */
-
+  ipp_t                *request;               /* IPP request */
+  char         uri[HTTP_MAX_URI];      /* Job URI */
+  const char   *pclass;                /* Printer class name */
 
-    num_settings = 0;
-    num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
-                                 cgiGetVariable("DEBUG_LOGGING") ? "1" : "0",
-                                num_settings, &settings);
-    num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
-                                 cgiGetVariable("REMOTE_ADMIN") ? "1" : "0",
-                                num_settings, &settings);
-    num_settings = cupsAddOption(CUPS_SERVER_REMOTE_PRINTERS,
-                                 cgiGetVariable("REMOTE_PRINTERS") ? "1" : "0",
-                                num_settings, &settings);
-    num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
-                                 cgiGetVariable("SHARE_PRINTERS") ? "1" : "0",
-                                num_settings, &settings);
-    num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
-                                 cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0",
-                                num_settings, &settings);
-
-
-    if (!_cupsAdminSetServerSettings(http, num_settings, settings))
-    {
-      cgiStartHTML(cgiText(_("Change Settings")));
-      cgiSetVariable("MESSAGE",
-                     cgiText(_("Unable to change server settings:")));
-      cgiSetVariable("ERROR", cupsLastErrorString());
-      cgiCopyTemplateLang("error.tmpl");
-    }
-    else
-    {
-      cgiSetVariable("refresh_page", "5;/admin/?OP=redirect");
-      cgiStartHTML(cgiText(_("Change Settings")));
-      cgiCopyTemplateLang("restart.tmpl");
-    }
 
-    cupsFreeOptions(num_settings, settings);
+ /*
+  * Get form variables...
+  */
 
+  if (cgiGetVariable("CONFIRM") == NULL)
+  {
+    cgiStartHTML(cgiText(_("Delete Class")));
+    cgiCopyTemplateLang("class-confirm.tmpl");
     cgiEndHTML();
-  }
-  else if (cgiIsPOST())
-  {
-   /*
-    * Save hand-edited config file...
-    */
-
-    http_status_t status;              /* PUT status */
-    char       tempfile[1024];         /* Temporary new cupsd.conf */
-    int                tempfd;                 /* Temporary file descriptor */
-    cups_file_t        *temp;                  /* Temporary file */
-    const char *start,                 /* Start of line */
-               *end;                   /* End of line */
-
-
-   /*
-    * Create a temporary file for the new cupsd.conf file...
-    */
-
-    if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
-    {
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
-      cgiSetVariable("ERROR", strerror(errno));
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
-      
-      perror(tempfile);
-      return;
-    }
-
-    if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL)
-    {
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
-      cgiSetVariable("ERROR", strerror(errno));
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
-      
-      perror(tempfile);
-      close(tempfd);
-      unlink(tempfile);
-      return;
-    }
-
-   /*
-    * Copy the cupsd.conf text from the form variable...
-    */
-
-    start = cgiGetVariable("CUPSDCONF");
-    while (start)
-    {
-      if ((end = strstr(start, "\r\n")) == NULL)
-        if ((end = strstr(start, "\n")) == NULL)
-         end = start + strlen(start);
-
-      cupsFileWrite(temp, start, end - start);
-      cupsFilePutChar(temp, '\n');
-
-      if (*end == '\r')
-        start = end + 2;
-      else if (*end == '\n')
-        start = end + 1;
-      else
-        start = NULL;
-    }
-
-    cupsFileClose(temp);
-
-   /*
-    * Upload the configuration file to the server...
-    */
-
-    status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
-
-    if (status != HTTP_CREATED)
-    {
-      cgiSetVariable("MESSAGE",
-                     cgiText(_("Unable to upload cupsd.conf file:")));
-      cgiSetVariable("ERROR", httpStatus(status));
-
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiCopyTemplateLang("error.tmpl");
-    }
-    else
-    {
-      cgiSetVariable("refresh_page", "5;/admin/?OP=redirect");
-
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiCopyTemplateLang("restart.tmpl");
-    }
-
-    cgiEndHTML();
-
-    unlink(tempfile);
-  }
-  else
-  {
-    struct stat        info;                   /* cupsd.conf information */
-    cups_file_t        *cupsd;                 /* cupsd.conf file */
-    char       *buffer;                /* Buffer for entire file */
-    char       filename[1024];         /* Filename */
-    const char *server_root;           /* Location of config files */
-
-
-   /*
-    * Locate the cupsd.conf file...
-    */
-
-    if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
-      server_root = CUPS_SERVERROOT;
-
-    snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root);
-
-   /*
-    * Figure out the size...
-    */
-
-    if (stat(filename, &info))
-    {
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiSetVariable("MESSAGE",
-                     cgiText(_("Unable to access cupsd.conf file:")));
-      cgiSetVariable("ERROR", strerror(errno));
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
-
-      perror(filename);
-      return;
-    }
-
-    if (info.st_size > (1024 * 1024))
-    {
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiSetVariable("MESSAGE",
-                     cgiText(_("Unable to access cupsd.conf file:")));
-      cgiSetVariable("ERROR",
-                     cgiText(_("Unable to edit cupsd.conf files larger than "
-                              "1MB!")));
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
-
-      fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename,
-              (long)info.st_size);
-      return;
-    }
-
-   /*
-    * Open the cupsd.conf file...
-    */
-
-    if ((cupsd = cupsFileOpen(filename, "r")) == NULL)
-    {
-     /*
-      * Unable to open - log an error...
-      */
-
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiSetVariable("MESSAGE",
-                     cgiText(_("Unable to access cupsd.conf file:")));
-      cgiSetVariable("ERROR", strerror(errno));
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
-
-      perror(filename);
-      return;
-    }
-
-   /*
-    * Allocate memory and load the file into a string buffer...
-    */
-
-    buffer = calloc(1, info.st_size + 1);
-
-    cupsFileRead(cupsd, buffer, info.st_size);
-    cupsFileClose(cupsd);
-
-    cgiSetVariable("CUPSDCONF", buffer);
-    free(buffer);
-
-   /*
-    * Show the current config file...
-    */
-
-    cgiStartHTML(cgiText(_("Edit Configuration File")));
-
-    printf("<!-- \"%s\" -->\n", filename);
-
-    cgiCopyTemplateLang("edit-config.tmpl");
-
-    cgiEndHTML();
-  }
-}
-
-
-/*
- * 'do_delete_class()' - Delete a class...
- */
-
-static void
-do_delete_class(http_t *http)          /* I - HTTP connection */
-{
-  ipp_t                *request;               /* IPP request */
-  char         uri[HTTP_MAX_URI];      /* Job URI */
-  const char   *pclass;                /* Printer class name */
-
-
- /*
-  * Get form variables...
-  */
-
-  if (cgiGetVariable("CONFIRM") == NULL)
-  {
-    cgiStartHTML(cgiText(_("Delete Class")));
-    cgiCopyTemplateLang("class-confirm.tmpl");
-    cgiEndHTML();
-    return;
+    return;
   }
 
   if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL)
@@ -1886,6 +1795,20 @@ do_delete_class(http_t *http)            /* I - HTTP connection */
   * Show the results...
   */
 
+  if (cupsLastError() == IPP_NOT_AUTHORIZED)
+  {
+    puts("Status: 401\n");
+    exit(0);
+  }
+  else if (cupsLastError() <= IPP_OK_CONFLICT)
+  {
+   /*
+    * Redirect successful updates back to the classes page...
+    */
+
+    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes");
+  }
+
   cgiStartHTML(cgiText(_("Delete Class")));
 
   if (cupsLastError() > IPP_OK_CONFLICT)
@@ -1957,6 +1880,20 @@ do_delete_printer(http_t *http)          /* I - HTTP connection */
   * Show the results...
   */
 
+  if (cupsLastError() == IPP_NOT_AUTHORIZED)
+  {
+    puts("Status: 401\n");
+    exit(0);
+  }
+  else if (cupsLastError() <= IPP_OK_CONFLICT)
+  {
+   /*
+    * Redirect successful updates back to the printers page...
+    */
+
+    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers");
+  }
+
   cgiStartHTML(cgiText(_("Delete Printer")));
 
   if (cupsLastError() > IPP_OK_CONFLICT)
@@ -2105,68 +2042,33 @@ do_export(http_t *http)                 /* I - HTTP connection */
 
 
 /*
- * 'do_menu()' - Show the main menu...
+ * 'do_list_printers()' - List available printers...
  */
 
 static void
-do_menu(http_t *http)                  /* I - HTTP connection */
+do_list_printers(http_t *http)         /* I - HTTP connection */
 {
-  int          num_settings;           /* Number of server settings */
-  cups_option_t        *settings;              /* Server settings */
-  const char   *val;                   /* Setting value */
-  char         filename[1024];         /* Temporary filename */
-  const char   *datadir;               /* Location of data files */
   ipp_t                *request,               /* IPP request */
                *response;              /* IPP response */
   ipp_attribute_t *attr;               /* IPP attribute */
 
 
+  cgiStartHTML(cgiText(_("List Available Printers")));
+  fflush(stdout);
+
  /*
-  * Get the current server settings...
+  * Get the list of printers and their devices...
   */
 
-  if (!_cupsAdminGetServerSettings(http, &num_settings, &settings))
-  {
-    cgiSetVariable("SETTINGS_MESSAGE",
-                   cgiText(_("Unable to open cupsd.conf file:")));
-    cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
-  }
+  request = ippNewRequest(CUPS_GET_PRINTERS);
 
-  if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("DEBUG_LOGGING", "CHECKED");
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+               "requested-attributes", NULL, "device-uri");
 
-  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("REMOTE_ADMIN", "CHECKED");
-
-  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
-
-  if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("SHARE_PRINTERS", "CHECKED");
-
-  if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
-
-  cupsFreeOptions(num_settings, settings);
-
- /*
-  * Get the list of printers and their devices...
-  */
-
-  request = ippNewRequest(CUPS_GET_PRINTERS);
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-               "requested-attributes", NULL, "device-uri");
-
-  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
-                CUPS_PRINTER_LOCAL);
-  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
-                CUPS_PRINTER_LOCAL);
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
+                CUPS_PRINTER_LOCAL);
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
+                CUPS_PRINTER_LOCAL);
 
   if ((response = cupsDoRequest(http, request, "/")) != NULL)
   {
@@ -2267,8 +2169,8 @@ do_menu(http_t *http)                     /* I - HTTP connection */
            * Not found, so it must be a new printer...
            */
 
-            char       options[1024],  /* Form variables for this device */
-                       *options_ptr;   /* Pointer into string */
+            char       option[1024],   /* Form variables for this device */
+                       *option_ptr;    /* Pointer into string */
            const char  *ptr;           /* Pointer into device string */
 
 
@@ -2280,9 +2182,6 @@ do_menu(http_t *http)                     /* I - HTTP connection */
            * suitable name.
            */
 
-           strcpy(options, "PRINTER_NAME=");
-           options_ptr = options + strlen(options);
-
             if (strncasecmp(device_info, "unknown", 7))
              ptr = device_info;
             else if ((ptr = strstr(device_uri, "://")) != NULL)
@@ -2290,49 +2189,20 @@ do_menu(http_t *http)                   /* I - HTTP connection */
            else
              ptr = device_make_and_model;
 
-           for (;
-                options_ptr < (options + sizeof(options) - 1) && *ptr;
+           for (option_ptr = option;
+                option_ptr < (option + sizeof(option) - 1) && *ptr;
                 ptr ++)
              if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' ||
                  *ptr == '.')
-               *options_ptr++ = *ptr;
-             else if ((*ptr == ' ' || *ptr == '/') && options_ptr[-1] != '_')
-               *options_ptr++ = '_';
+               *option_ptr++ = *ptr;
+             else if ((*ptr == ' ' || *ptr == '/') && option_ptr[-1] != '_')
+               *option_ptr++ = '_';
              else if (*ptr == '?' || *ptr == '(')
                break;
 
-           /*
-           * Then add the make and model in the printer info, so
-           * that MacOS clients see something reasonable...
-           */
-
-            strlcpy(options_ptr, "&PRINTER_LOCATION=Local+Printer"
-                                "&PRINTER_INFO=",
-                   sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
+            *option_ptr = '\0';
 
-            cgiFormEncode(options_ptr, device_make_and_model,
-                         sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
-
-           /*
-           * Then copy the device URI...
-           */
-
-           strlcpy(options_ptr, "&DEVICE_URI=",
-                   sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
-
-            cgiFormEncode(options_ptr, device_uri,
-                         sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
-
-            if (options_ptr < (options + sizeof(options) - 1))
-           {
-             *options_ptr++ = '|';
-             cgiFormEncode(options_ptr, device_make_and_model,
-                         sizeof(options) - (options_ptr - options));
-           }
+            cgiSetArray("TEMPLATE_NAME", i, option);
 
            /*
            * Finally, set the form variables for this printer...
@@ -2340,7 +2210,6 @@ do_menu(http_t *http)                     /* I - HTTP connection */
 
            cgiSetArray("device_info", i, device_info);
            cgiSetArray("device_make_and_model", i, device_make_and_model);
-           cgiSetArray("device_options", i, options);
             cgiSetArray("device_uri", i, device_uri);
            i ++;
          }
@@ -2365,6 +2234,77 @@ do_menu(http_t *http)                    /* I - HTTP connection */
     }
   }
 
+ /*
+  * Finally, show the printer list...
+  */
+
+  cgiCopyTemplateLang("list-available-printers.tmpl");
+
+  cgiEndHTML();
+}
+
+
+/*
+ * 'do_menu()' - Show the main menu...
+ */
+
+static void
+do_menu(http_t *http)                  /* I - HTTP connection */
+{
+  int          num_settings;           /* Number of server settings */
+  cups_option_t        *settings;              /* Server settings */
+  const char   *val;                   /* Setting value */
+  char         filename[1024];         /* Temporary filename */
+  const char   *datadir;               /* Location of data files */
+  ipp_t                *request,               /* IPP request */
+               *response;              /* IPP response */
+
+
+ /*
+  * Get the current server settings...
+  */
+
+  if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
+  {
+    cgiSetVariable("SETTINGS_MESSAGE",
+                   cgiText(_("Unable to open cupsd.conf file:")));
+    cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
+  }
+
+  if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("DEBUG_LOGGING", "CHECKED");
+
+  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("REMOTE_ADMIN", "CHECKED");
+
+  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("REMOTE_ANY", "CHECKED");
+
+  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
+
+  if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("SHARE_PRINTERS", "CHECKED");
+
+  if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
+
+#ifdef HAVE_GSSAPI
+  cgiSetVariable("HAVE_GSSAPI", "1");
+
+  if ((val = cupsGetOption("DefaultAuthType", num_settings,
+                           settings)) != NULL && !strcasecmp(val, "Negotiate"))
+    cgiSetVariable("KERBEROS", "CHECKED");
+#endif /* HAVE_GSSAPI */
+
+  cupsFreeOptions(num_settings, settings);
+
  /*
   * See if Samba and the Windows drivers are installed...
   */
@@ -2399,6 +2339,21 @@ do_menu(http_t *http)                    /* I - HTTP connection */
   else
     perror(filename);
 
+ /*
+  * Subscriptions...
+  */
+
+  request = ippNewRequest(IPP_GET_SUBSCRIPTIONS);
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, "ipp://localhost/");
+
+  if ((response = cupsDoRequest(http, request, "/")) != NULL)
+  {
+    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
+    ippDelete(response);
+  }
+
  /*
   * Finally, show the main menu template...
   */
@@ -2461,7 +2416,12 @@ do_printer_op(http_t      *http, /* I - HTTP connection */
 
   ippDelete(cupsDoRequest(http, request, "/admin/"));
 
-  if (cupsLastError() > IPP_OK_CONFLICT)
+  if (cupsLastError() == IPP_NOT_AUTHORIZED)
+  {
+    puts("Status: 401\n");
+    exit(0);
+  }
+  else if (cupsLastError() > IPP_OK_CONFLICT)
   {
     cgiStartHTML(title);
     cgiShowIPPError(_("Unable to change printer:"));
@@ -2478,7 +2438,7 @@ do_printer_op(http_t      *http,  /* I - HTTP connection */
 
     cgiRewriteURL(uri, url, sizeof(url), NULL);
     cgiFormEncode(uri, url, sizeof(uri));
-    snprintf(refresh, sizeof(refresh), "5;/admin/?OP=redirect&URL=%s", uri);
+    snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
     cgiSetVariable("refresh_page", refresh);
 
     cgiStartHTML(title);
@@ -2528,201 +2488,760 @@ do_set_allowed_users(http_t *http)    /* I - HTTP connection */
                };
 
 
-  is_class = cgiGetVariable("IS_CLASS");
-  printer  = cgiGetVariable("PRINTER_NAME");
+  is_class = cgiGetVariable("IS_CLASS");
+  printer  = cgiGetVariable("PRINTER_NAME");
+
+  if (!printer)
+  {
+    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
+    cgiStartHTML(cgiText(_("Set Allowed Users")));
+    cgiCopyTemplateLang("error.tmpl");
+    cgiEndHTML();
+    return;
+  }
+
+  users = cgiGetVariable("users");
+  type  = cgiGetVariable("type");
+
+  if (!users || !type ||
+      (strcmp(type, "requesting-user-name-allowed") &&
+       strcmp(type, "requesting-user-name-denied")))
+  {
+   /*
+    * Build a Get-Printer-Attributes request, which requires the following
+    * attributes:
+    *
+    *    attributes-charset
+    *    attributes-natural-language
+    *    printer-uri
+    *    requested-attributes
+    */
+
+    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
+                    printer);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+                NULL, uri);
+
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                  "requested-attributes",
+                 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
+
+   /*
+    * Do the request and get back a response...
+    */
+
+    if ((response = cupsDoRequest(http, request, "/")) != NULL)
+    {
+      cgiSetIPPVars(response, NULL, NULL, NULL, 0);
+
+      ippDelete(response);
+    }
+
+    cgiStartHTML(cgiText(_("Set Allowed Users")));
+
+    if (cupsLastError() == IPP_NOT_AUTHORIZED)
+    {
+      puts("Status: 401\n");
+      exit(0);
+    }
+    else if (cupsLastError() > IPP_OK_CONFLICT)
+      cgiShowIPPError(_("Unable to get printer attributes:"));
+    else
+      cgiCopyTemplateLang("users.tmpl");
+
+    cgiEndHTML();
+  }
+  else
+  {
+   /*
+    * Save the changes...
+    */
+
+    for (num_users = 0, ptr = (char *)users; *ptr; num_users ++)
+    {
+     /*
+      * Skip whitespace and commas...
+      */
+
+      while (*ptr == ',' || isspace(*ptr & 255))
+       ptr ++;
+
+      if (*ptr == '\'' || *ptr == '\"')
+      {
+       /*
+       * Scan quoted name...
+       */
+
+       quote = *ptr++;
+
+       for (end = ptr; *end; end ++)
+         if (*end == quote)
+           break;
+      }
+      else
+      {
+       /*
+       * Scan space or comma-delimited name...
+       */
+
+        for (end = ptr; *end; end ++)
+         if (isspace(*end & 255) || *end == ',')
+           break;
+      }
+
+     /*
+      * Advance to the next name...
+      */
+
+      ptr = end;
+    }
+
+   /*
+    * Build a CUPS-Add-Printer/Class request, which requires the following
+    * attributes:
+    *
+    *    attributes-charset
+    *    attributes-natural-language
+    *    printer-uri
+    *    requesting-user-name-{allowed,denied}
+    */
+
+    request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
+
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
+                    printer);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+                NULL, uri);
+
+    if (num_users == 0)
+      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                   "requesting-user-name-allowed", NULL, "all");
+    else
+    {
+      attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                           type, num_users, NULL, NULL);
+
+      for (i = 0, ptr = (char *)users; *ptr; i ++)
+      {
+       /*
+        * Skip whitespace and commas...
+       */
+
+        while (*ptr == ',' || isspace(*ptr & 255))
+         ptr ++;
+
+        if (*ptr == '\'' || *ptr == '\"')
+       {
+        /*
+         * Scan quoted name...
+         */
+
+         quote = *ptr++;
+
+         for (end = ptr; *end; end ++)
+           if (*end == quote)
+             break;
+       }
+       else
+       {
+        /*
+         * Scan space or comma-delimited name...
+         */
+
+          for (end = ptr; *end; end ++)
+           if (isspace(*end & 255) || *end == ',')
+             break;
+        }
+
+       /*
+        * Terminate the name...
+       */
+
+        if (*end)
+          *end++ = '\0';
+
+       /*
+        * Add the name...
+       */
+
+        attr->values[i].string.text = strdup(ptr);
+
+       /*
+        * Advance to the next name...
+       */
+
+        ptr = end;
+      }
+    }
+
+   /*
+    * Do the request and get back a response...
+    */
+
+    ippDelete(cupsDoRequest(http, request, "/admin/"));
+
+    if (cupsLastError() == IPP_NOT_AUTHORIZED)
+    {
+      puts("Status: 401\n");
+      exit(0);
+    }
+    else if (cupsLastError() > IPP_OK_CONFLICT)
+    {
+      cgiStartHTML(cgiText(_("Set Allowed Users")));
+      cgiShowIPPError(_("Unable to change printer:"));
+    }
+    else
+    {
+     /*
+      * Redirect successful updates back to the printer page...
+      */
+
+      char     url[1024],              /* Printer/class URL */
+               refresh[1024];          /* Refresh URL */
+
+
+      cgiRewriteURL(uri, url, sizeof(url), NULL);
+      cgiFormEncode(uri, url, sizeof(uri));
+      snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s",
+               uri);
+      cgiSetVariable("refresh_page", refresh);
+
+      cgiStartHTML(cgiText(_("Set Allowed Users")));
+
+      cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
+                                     "printer-modified.tmpl");
+    }
+
+    cgiEndHTML();
+  }
+}
+
+
+/*
+ * 'do_set_options()' - Configure the default options for a queue.
+ */
+
+static void
+do_set_options(http_t *http,           /* I - HTTP connection */
+               int    is_class)                /* I - Set options for class? */
+{
+  int          i, j, k, m;             /* Looping vars */
+  int          have_options;           /* Have options? */
+  ipp_t                *request,               /* IPP request */
+               *response;              /* IPP response */
+  ipp_attribute_t *attr;               /* IPP attribute */
+  char         uri[HTTP_MAX_URI];      /* Job URI */
+  const char   *var;                   /* Variable value */
+  const char   *printer;               /* Printer printer name */
+  const char   *filename;              /* PPD filename */
+  char         tempfile[1024];         /* Temporary filename */
+  cups_file_t  *in,                    /* Input file */
+               *out;                   /* Output file */
+  char         line[1024];             /* Line from PPD file */
+  char         keyword[1024],          /* Keyword from Default line */
+               *keyptr;                /* Pointer into keyword... */
+  ppd_file_t   *ppd;                   /* PPD file */
+  ppd_group_t  *group;                 /* Option group */
+  ppd_option_t *option;                /* Option */
+  ppd_attr_t   *protocol;              /* cupsProtocol attribute */
+  const char   *title;                 /* Page title */
+
+
+  title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options"));
+
+  fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http,
+          is_class);
+
+ /*
+  * Get the printer name...
+  */
+
+  if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
+                    printer);
+  else
+  {
+    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
+    cgiStartHTML(title);
+    cgiCopyTemplateLang("error.tmpl");
+    cgiEndHTML();
+    return;
+  }
+
+  fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
+
+ /*
+  * Get the PPD file...
+  */
+
+  if (is_class)
+    filename = NULL;
+  else
+    filename = cupsGetPPD2(http, printer);
+
+  if (filename)
+  {
+    fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
+
+    if ((ppd = ppdOpenFile(filename)) == NULL)
+    {
+      cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i)));
+      cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file:")));
+      cgiStartHTML(title);
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
+      return;
+    }
+  }
+  else
+  {
+    fputs("DEBUG: No PPD file\n", stderr);
+    ppd = NULL;
+  }
+
+  if (cgiGetVariable("job_sheets_start") != NULL ||
+      cgiGetVariable("job_sheets_end") != NULL)
+    have_options = 1;
+  else
+    have_options = 0;
+
+  if (ppd)
+  {
+    ppdMarkDefaults(ppd);
+
+    DEBUG_printf(("<P>ppd->num_groups = %d\n"
+                 "<UL>\n", ppd->num_groups));
+
+    for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+    {
+      DEBUG_printf(("<LI>%s<UL>\n", group->text));
+
+      for (j = group->num_options, option = group->options;
+          j > 0;
+          j --, option ++)
+       if ((var = cgiGetVariable(option->keyword)) != NULL)
+       {
+         DEBUG_printf(("<LI>%s = \"%s\"</LI>\n", option->keyword, var));
+         have_options = 1;
+         ppdMarkOption(ppd, option->keyword, var);
+       }
+#ifdef DEBUG
+       else
+         printf("<LI>%s not defined!</LI>\n", option->keyword);
+#endif /* DEBUG */
+
+      DEBUG_puts("</UL></LI>");
+    }
+
+    DEBUG_printf(("</UL>\n"
+                 "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd)));
+  }
+
+  if (!have_options || ppdConflicts(ppd))
+  {
+   /*
+    * Show the options to the user...
+    */
+
+    fputs("DEBUG: Showing options...\n", stderr);
+
+    cgiStartHTML(cgiText(_("Set Printer Options")));
+    cgiCopyTemplateLang("set-printer-options-header.tmpl");
+
+    if (ppd)
+    {
+      ppdLocalize(ppd);
+
+      if (ppdConflicts(ppd))
+      {
+       for (i = ppd->num_groups, k = 0, group = ppd->groups;
+            i > 0;
+            i --, group ++)
+         for (j = group->num_options, option = group->options;
+              j > 0;
+              j --, option ++)
+           if (option->conflicted)
+           {
+             cgiSetArray("ckeyword", k, option->keyword);
+             cgiSetArray("ckeytext", k, option->text);
+             k ++;
+           }
+
+       cgiCopyTemplateLang("option-conflict.tmpl");
+      }
+
+      for (i = ppd->num_groups, group = ppd->groups;
+          i > 0;
+          i --, group ++)
+      {
+       if (!strcmp(group->name, "InstallableOptions"))
+         cgiSetVariable("GROUP", cgiText(_("Options Installed")));
+       else
+         cgiSetVariable("GROUP", group->text);
+
+       cgiCopyTemplateLang("option-header.tmpl");
+       
+       for (j = group->num_options, option = group->options;
+            j > 0;
+            j --, option ++)
+       {
+         if (!strcmp(option->keyword, "PageRegion"))
+           continue;
+
+         cgiSetVariable("KEYWORD", option->keyword);
+         cgiSetVariable("KEYTEXT", option->text);
+             
+         if (option->conflicted)
+           cgiSetVariable("CONFLICTED", "1");
+         else
+           cgiSetVariable("CONFLICTED", "0");
+
+         cgiSetSize("CHOICES", 0);
+         cgiSetSize("TEXT", 0);
+         for (k = 0, m = 0; k < option->num_choices; k ++)
+         {
+          /*
+           * Hide custom option values...
+           */
+
+           if (!strcmp(option->choices[k].choice, "Custom"))
+             continue;
+
+           cgiSetArray("CHOICES", m, option->choices[k].choice);
+           cgiSetArray("TEXT", m, option->choices[k].text);
+
+           m ++;
+
+           if (option->choices[k].marked)
+             cgiSetVariable("DEFCHOICE", option->choices[k].choice);
+         }
+
+         switch (option->ui)
+         {
+           case PPD_UI_BOOLEAN :
+               cgiCopyTemplateLang("option-boolean.tmpl");
+               break;
+           case PPD_UI_PICKONE :
+               cgiCopyTemplateLang("option-pickone.tmpl");
+               break;
+           case PPD_UI_PICKMANY :
+               cgiCopyTemplateLang("option-pickmany.tmpl");
+               break;
+         }
+       }
+
+       cgiCopyTemplateLang("option-trailer.tmpl");
+      }
+    }
+
+   /*
+    * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+    * following attributes:
+    *
+    *    attributes-charset
+    *    attributes-natural-language
+    *    printer-uri
+    */
+
+    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                     "localhost", 0, "/printers/%s", printer);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+                 NULL, uri);
+
+   /*
+    * Do the request and get back a response...
+    */
+
+    if ((response = cupsDoRequest(http, request, "/")) != NULL)
+    {
+      if ((attr = ippFindAttribute(response, "job-sheets-supported",
+                                   IPP_TAG_ZERO)) != NULL)
+      {
+       /*
+       * Add the job sheets options...
+       */
+
+       cgiSetVariable("GROUP", cgiText(_("Banners")));
+       cgiCopyTemplateLang("option-header.tmpl");
+
+       cgiSetSize("CHOICES", attr->num_values);
+       cgiSetSize("TEXT", attr->num_values);
+       for (k = 0; k < attr->num_values; k ++)
+       {
+         cgiSetArray("CHOICES", k, attr->values[k].string.text);
+         cgiSetArray("TEXT", k, attr->values[k].string.text);
+       }
+
+        attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
+
+        cgiSetVariable("KEYWORD", "job_sheets_start");
+       cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner")));
+        cgiSetVariable("DEFCHOICE", attr == NULL ?
+                                   "" : attr->values[0].string.text);
+
+       cgiCopyTemplateLang("option-pickone.tmpl");
+
+        cgiSetVariable("KEYWORD", "job_sheets_end");
+       cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner")));
+        cgiSetVariable("DEFCHOICE", attr == NULL && attr->num_values > 1 ?
+                                   "" : attr->values[1].string.text);
+
+       cgiCopyTemplateLang("option-pickone.tmpl");
+
+       cgiCopyTemplateLang("option-trailer.tmpl");
+      }
+
+      if (ippFindAttribute(response, "printer-error-policy-supported",
+                           IPP_TAG_ZERO) ||
+          ippFindAttribute(response, "printer-op-policy-supported",
+                          IPP_TAG_ZERO))
+      {
+       /*
+       * Add the error and operation policy options...
+       */
+
+       cgiSetVariable("GROUP", cgiText(_("Policies")));
+       cgiCopyTemplateLang("option-header.tmpl");
+
+       /*
+        * Error policy...
+       */
+
+        attr = ippFindAttribute(response, "printer-error-policy-supported",
+                               IPP_TAG_ZERO);
+
+        if (attr)
+       {
+         cgiSetSize("CHOICES", attr->num_values);
+         cgiSetSize("TEXT", attr->num_values);
+         for (k = 0; k < attr->num_values; k ++)
+         {
+           cgiSetArray("CHOICES", k, attr->values[k].string.text);
+           cgiSetArray("TEXT", k, attr->values[k].string.text);
+         }
+
+          attr = ippFindAttribute(response, "printer-error-policy",
+                                 IPP_TAG_ZERO);
+
+          cgiSetVariable("KEYWORD", "printer_error_policy");
+         cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
+          cgiSetVariable("DEFCHOICE", attr == NULL ?
+                                     "" : attr->values[0].string.text);
+        }
 
-  if (!printer)
-  {
-    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
-    cgiStartHTML(cgiText(_("Set Allowed Users")));
-    cgiCopyTemplateLang("error.tmpl");
-    cgiEndHTML();
-    return;
-  }
+       cgiCopyTemplateLang("option-pickone.tmpl");
 
-  users = cgiGetVariable("users");
-  type  = cgiGetVariable("type");
+       /*
+        * Operation policy...
+       */
 
-  if (!users || !type ||
-      (strcmp(type, "requesting-user-name-allowed") &&
-       strcmp(type, "requesting-user-name-denied")))
-  {
-   /*
-    * Build a Get-Printer-Attributes request, which requires the following
-    * attributes:
-    *
-    *    attributes-charset
-    *    attributes-natural-language
-    *    printer-uri
-    *    requested-attributes
-    */
+        attr = ippFindAttribute(response, "printer-op-policy-supported",
+                               IPP_TAG_ZERO);
 
-    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+        if (attr)
+       {
+         cgiSetSize("CHOICES", attr->num_values);
+         cgiSetSize("TEXT", attr->num_values);
+         for (k = 0; k < attr->num_values; k ++)
+         {
+           cgiSetArray("CHOICES", k, attr->values[k].string.text);
+           cgiSetArray("TEXT", k, attr->values[k].string.text);
+         }
 
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
-                    printer);
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-                NULL, uri);
+          attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
 
-    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                  "requested-attributes",
-                 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
+          cgiSetVariable("KEYWORD", "printer_op_policy");
+         cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
+          cgiSetVariable("DEFCHOICE", attr == NULL ?
+                                     "" : attr->values[0].string.text);
 
-   /*
-    * Do the request and get back a response...
-    */
+         cgiCopyTemplateLang("option-pickone.tmpl");
+        }
 
-    if ((response = cupsDoRequest(http, request, "/")) != NULL)
-    {
-      cgiSetIPPVars(response, NULL, NULL, NULL, 0);
+       cgiCopyTemplateLang("option-trailer.tmpl");
+      }
 
       ippDelete(response);
     }
 
-    cgiStartHTML(cgiText(_("Set Allowed Users")));
-
-    if (cupsLastError() > IPP_OK_CONFLICT)
-      cgiShowIPPError(_("Unable to get printer attributes:"));
-    else
-      cgiCopyTemplateLang("users.tmpl");
-
-    cgiEndHTML();
-  }
-  else
-  {
    /*
-    * Save the changes...
+    * Binary protocol support...
     */
 
-    for (num_users = 0, ptr = (char *)users; *ptr; num_users ++)
+    if (ppd->protocols && strstr(ppd->protocols, "BCP"))
     {
-     /*
-      * Skip whitespace and commas...
-      */
-
-      while (*ptr == ',' || isspace(*ptr & 255))
-       ptr ++;
+      protocol = ppdFindAttr(ppd, "cupsProtocol", NULL);
 
-      if (*ptr == '\'' || *ptr == '\"')
-      {
-       /*
-       * Scan quoted name...
-       */
+      cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol")));
+      cgiCopyTemplateLang("option-header.tmpl");
 
-       quote = *ptr++;
+      cgiSetSize("CHOICES", 2);
+      cgiSetSize("TEXT", 2);
+      cgiSetArray("CHOICES", 0, "None");
+      cgiSetArray("TEXT", 0, cgiText(_("None")));
 
-       for (end = ptr; *end; end ++)
-         if (*end == quote)
-           break;
+      if (strstr(ppd->protocols, "TBCP"))
+      {
+       cgiSetArray("CHOICES", 1, "TBCP");
+       cgiSetArray("TEXT", 1, "TBCP");
       }
       else
       {
-       /*
-       * Scan space or comma-delimited name...
-       */
-
-        for (end = ptr; *end; end ++)
-         if (isspace(*end & 255) || *end == ',')
-           break;
+       cgiSetArray("CHOICES", 1, "BCP");
+       cgiSetArray("TEXT", 1, "BCP");
       }
 
-     /*
-      * Advance to the next name...
-      */
+      cgiSetVariable("KEYWORD", "protocol");
+      cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol")));
+      cgiSetVariable("DEFCHOICE", protocol ? protocol->value : "None");
 
-      ptr = end;
+      cgiCopyTemplateLang("option-pickone.tmpl");
+
+      cgiCopyTemplateLang("option-trailer.tmpl");
     }
 
+    cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
+    cgiEndHTML();
+  }
+  else
+  {
    /*
-    * Build a CUPS-Add-Printer/Class request, which requires the following
-    * attributes:
-    *
-    *    attributes-charset
-    *    attributes-natural-language
-    *    printer-uri
-    *    requesting-user-name-{allowed,denied}
+    * Set default options...
     */
 
-    request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
-
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
-                    printer);
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-                NULL, uri);
+    fputs("DEBUG: Setting options...\n", stderr);
 
-    if (num_users == 0)
-      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                   "requesting-user-name-allowed", NULL, "all");
-    else
+    if (filename)
     {
-      attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                           type, num_users, NULL, NULL);
+      out = cupsTempFile2(tempfile, sizeof(tempfile));
+      in  = cupsFileOpen(filename, "r");
 
-      for (i = 0, ptr = (char *)users; *ptr; i ++)
+      if (!in || !out)
       {
-       /*
-        * Skip whitespace and commas...
-       */
+       cgiSetVariable("ERROR", strerror(errno));
+       cgiStartHTML(cgiText(_("Set Printer Options")));
+       cgiCopyTemplateLang("error.tmpl");
+       cgiEndHTML();
 
-        while (*ptr == ',' || isspace(*ptr & 255))
-         ptr ++;
+       if (in)
+         cupsFileClose(in);
 
-        if (*ptr == '\'' || *ptr == '\"')
+       if (out)
        {
-        /*
-         * Scan quoted name...
-         */
+         cupsFileClose(out);
+         unlink(tempfile);
+       }
 
-         quote = *ptr++;
+       unlink(filename);
+       return;
+      }
 
-         for (end = ptr; *end; end ++)
-           if (*end == quote)
-             break;
-       }
+      while (cupsFileGets(in, line, sizeof(line)))
+      {
+       if (!strncmp(line, "*cupsProtocol:", 14) && cgiGetVariable("protocol"))
+         continue;
+       else if (strncmp(line, "*Default", 8))
+         cupsFilePrintf(out, "%s\n", line);
        else
        {
         /*
-         * Scan space or comma-delimited name...
+         * Get default option name...
          */
 
-          for (end = ptr; *end; end ++)
-           if (isspace(*end & 255) || *end == ',')
+         strlcpy(keyword, line + 8, sizeof(keyword));
+
+         for (keyptr = keyword; *keyptr; keyptr ++)
+           if (*keyptr == ':' || isspace(*keyptr & 255))
              break;
-        }
 
-       /*
-        * Terminate the name...
-       */
+         *keyptr = '\0';
 
-        if (*end)
-          *end++ = '\0';
+         if (!strcmp(keyword, "PageRegion") ||
+             !strcmp(keyword, "PaperDimension") ||
+             !strcmp(keyword, "ImageableArea"))
+           var = cgiGetVariable("PageSize");
+         else
+           var = cgiGetVariable(keyword);
 
-       /*
-        * Add the name...
-       */
+         if (var != NULL)
+           cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
+         else
+           cupsFilePrintf(out, "%s\n", line);
+       }
+      }
 
-        attr->values[i].string.text = strdup(ptr);
+      if ((var = cgiGetVariable("protocol")) != NULL)
+       cupsFilePrintf(out, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
 
-       /*
-        * Advance to the next name...
-       */
+      cupsFileClose(in);
+      cupsFileClose(out);
+    }
+    else
+    {
+     /*
+      * Make sure temporary filename is cleared when there is no PPD...
+      */
 
-        ptr = end;
-      }
+      tempfile[0] = '\0';
     }
 
+   /*
+    * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the
+    * following attributes:
+    *
+    *    attributes-charset
+    *    attributes-natural-language
+    *    printer-uri
+    *    job-sheets-default
+    *    printer-error-policy
+    *    printer-op-policy
+    *    [ppd file]
+    */
+
+    request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS :
+                                       CUPS_ADD_MODIFY_PRINTER);
+
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+                 NULL, uri);
+
+    attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                         "job-sheets-default", 2, NULL, NULL);
+    attr->values[0].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_start"));
+    attr->values[1].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_end"));
+
+    if ((var = cgiGetVariable("printer_error_policy")) != NULL)
+      attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                          "printer-error-policy", NULL, var);
+
+    if ((var = cgiGetVariable("printer_op_policy")) != NULL)
+      attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                          "printer-op-policy", NULL, var);
+
    /*
     * Do the request and get back a response...
     */
 
-    ippDelete(cupsDoRequest(http, request, "/admin/"));
+    if (filename)
+      ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile));
+    else
+      ippDelete(cupsDoRequest(http, request, "/admin/"));
 
-    if (cupsLastError() > IPP_OK_CONFLICT)
+    if (cupsLastError() == IPP_NOT_AUTHORIZED)
     {
-      cgiStartHTML(cgiText(_("Set Allowed Users")));
-      cgiShowIPPError(_("Unable to change printer:"));
+      puts("Status: 401\n");
+      exit(0);
+    }
+    else if (cupsLastError() > IPP_OK_CONFLICT)
+    {
+      cgiStartHTML(title);
+      cgiShowIPPError(_("Unable to set options:"));
     }
     else
     {
@@ -2730,23 +3249,27 @@ do_set_allowed_users(http_t *http)      /* I - HTTP connection */
       * Redirect successful updates back to the printer page...
       */
 
-      char     url[1024],              /* Printer/class URL */
-               refresh[1024];          /* Refresh URL */
+      char     refresh[1024];          /* Refresh URL */
 
 
-      cgiRewriteURL(uri, url, sizeof(url), NULL);
-      cgiFormEncode(uri, url, sizeof(uri));
-      snprintf(refresh, sizeof(refresh), "5;/admin/?OP=redirect&URL=%s", uri);
+      cgiFormEncode(uri, printer, sizeof(uri));
+      snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s",
+              is_class ? "classes" : "printers", uri);
       cgiSetVariable("refresh_page", refresh);
 
-      cgiStartHTML(cgiText(_("Set Allowed Users")));
+      cgiStartHTML(title);
 
-      cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
-                                     "printer-modified.tmpl");
+      cgiCopyTemplateLang("printer-configured.tmpl");
     }
 
     cgiEndHTML();
+
+    if (filename)
+      unlink(tempfile);
   }
+
+  if (filename)
+    unlink(filename);
 }
 
 
@@ -2809,7 +3332,12 @@ do_set_sharing(http_t *http)             /* I - HTTP connection */
     ippDelete(response);
   }
 
-  if (cupsLastError() > IPP_OK_CONFLICT)
+  if (cupsLastError() == IPP_NOT_AUTHORIZED)
+  {
+    puts("Status: 401\n");
+    exit(0);
+  }
+  else if (cupsLastError() > IPP_OK_CONFLICT)
   {
     cgiStartHTML(cgiText(_("Set Publishing")));
     cgiShowIPPError(_("Unable to change printer-is-shared attribute:"));
@@ -2826,7 +3354,7 @@ do_set_sharing(http_t *http)              /* I - HTTP connection */
 
     cgiRewriteURL(uri, url, sizeof(url), NULL);
     cgiFormEncode(uri, url, sizeof(uri));
-    snprintf(refresh, sizeof(refresh), "5;/admin/?OP=redirect&URL=%s", uri);
+    snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
     cgiSetVariable("refresh_page", refresh);
 
     cgiStartHTML(cgiText(_("Set Publishing")));
@@ -2888,5 +3416,5 @@ match_string(const char *a,               /* I - First string */
 
     
 /*
- * End of "$Id: admin.c 5425 2006-04-18 19:59:05Z mike $".
+ * End of "$Id: admin.c 7012 2007-10-10 21:22:45Z mike $".
  */