]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Load cups into easysw/current.
authorjlovell <jlovell@a1ca3aef-8c08-0410-bb20-df032aa958be>
Thu, 5 Apr 2007 18:24:21 +0000 (18:24 +0000)
committerjlovell <jlovell@a1ca3aef-8c08-0410-bb20-df032aa958be>
Thu, 5 Apr 2007 18:24:21 +0000 (18:24 +0000)
git-svn-id: svn+ssh://src.apple.com/svn/cups/easysw/current@308 a1ca3aef-8c08-0410-bb20-df032aa958be

24 files changed:
CHANGES-1.2.txt
CHANGES.txt
backend/ipp.c
backend/scsi.c
backend/socket.c
backend/usb-darwin.c
backend/usb-unix.c
cgi-bin/admin.c
cups/adminutil.c
cups/ppd.c
doc/help/ref-cupsd-conf.html
scheduler/conf.c
scheduler/conf.h
scheduler/ipp.c
scheduler/job.c
scheduler/printers.c
scheduler/printers.h
systemv/cupstestppd.c
templates/Makefile
templates/admin.tmpl
templates/classes.tmpl
templates/list-available-printers.tmpl [new file with mode: 0644]
templates/printer-configured.tmpl
tools/buttons.txt

index f7e03a8f3219a8830934542017ae5edab5919fd0..a6b1ba0d0214a10bb9a1b2368cf79195ad1f3a34 100644 (file)
@@ -3,6 +3,7 @@ CHANGES-1.2.txt
 
 CHANGES IN CUPS V1.2.11
 
+       - The USB backend did not work on NetBSD (STR #2324)
        - The printer-state-reasons attribute was incorrectly
          cleared after a job completed (STR #2323)
        - The scheduler did not set the printer operation policy
index 98e0328a0e62dad7e8ed05b5fc7d83b36f54237e..45602a84df6244dca41e5325d552231da28bed7c 100644 (file)
@@ -1,9 +1,27 @@
-CHANGES.txt - 2007-03-26
+CHANGES.txt - 2007-04-04
 ------------------------
 
 CHANGES IN CUPS V1.3
 
        - Documentation updates (STR #1775, STR #2130, STR #2131)
+       - The cupstestppd program now tests for existing filters,
+         icons, profiles, and dialog extensions (STR #2326)
+       - The web interface no longer lists new printers on the
+         main administration page.  Instead, a new "List Available
+         Printers" button is provided that shows a separate page
+         with the list of printers.
+       - The web interface now supports setting the banner and
+         policy options on raw printers and classes (STR #2238)
+       - The socket backend now reads any pending back-channel
+         data before shutting down the socket (STR #2325)
+       - Added a new ErrorPolicy directive in the cupsd.conf
+         file (STR #1871)
+       - Printers that use JCL options are now exported to Samba
+         correctly (STR #1985)
+       - The IPP backend now relays printer-state-message values
+         from the server to the client (STR #2109)
+       - Added support for the PWG printer-alert and
+         printer-alert-description attributes (STR #2088)
        - Added support for LPD "stream" mode (STR #2036)
        - The scheduler now reports the PostScript product string
          from PPD files in CUPS-Get-PPDs responses (STR #1900)
index 616c18c62672f2bbca9e06b07f33509a568bd2fd..fba56dbfe00e90c96d3e2c8ca5c303238a07194a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: ipp.c 6422 2007-03-30 20:49:37Z mike $"
+ * "$Id: ipp.c 6434 2007-04-02 22:07:10Z mike $"
  *
  *   IPP backend for the Common UNIX Printing System (CUPS).
  *
@@ -150,6 +150,7 @@ main(int  argc,                             /* I - Number of command-line args */
                  "document-format-supported",
                  "printer-is-accepting-jobs",
                  "printer-state",
+                 "printer-state-message",
                  "printer-state-reasons",
                };
   static const char * const jattrs[] =
@@ -1286,6 +1287,11 @@ check_printer_state(
 {
   ipp_t        *request,                       /* IPP request */
        *response;                      /* IPP response */
+  static const char * const attrs[] =  /* Attributes we want */
+  {
+    "printer-state-message",
+    "printer-state-reasons"
+  };
 
 
  /*
@@ -1302,8 +1308,9 @@ check_printer_state(
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
                  "requesting-user-name", NULL, user);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-               "requested-attributes", NULL, "printer-state-reasons");
+  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                "requested-attributes",
+               (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
 
  /*
   * Do the request...
@@ -1437,7 +1444,8 @@ report_printer_state(ipp_t *ipp)  /* I - IPP response */
 {
   int                  i;              /* Looping var */
   int                  count;          /* Count of reasons shown... */
-  ipp_attribute_t      *reasons;       /* printer-state-reasons */
+  ipp_attribute_t      *psm,           /* pritner-state-message */
+                       *reasons;       /* printer-state-reasons */
   const char           *reason;        /* Current reason */
   const char           *message;       /* Message to show */
   char                 unknown[1024];  /* Unknown message string */
@@ -1445,6 +1453,10 @@ report_printer_state(ipp_t *ipp) /* I - IPP response */
   char                 state[1024];    /* State string */
 
 
+  if ((psm = ippFindAttribute(ipp, "printer-state-message",
+                              IPP_TAG_TEXT)) != NULL)
+    fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
+
   if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
                                   IPP_TAG_KEYWORD)) == NULL)
     return (0);
@@ -1749,5 +1761,5 @@ sigterm_handler(int sig)          /* I - Signal */
 
 
 /*
- * End of "$Id: ipp.c 6422 2007-03-30 20:49:37Z mike $".
+ * End of "$Id: ipp.c 6434 2007-04-02 22:07:10Z mike $".
  */
index 2bb03f84de0b7f1b480595fed727d866fac07f62..91544b8b956e474b496b2c2fa6581027a55a06e4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: scsi.c 6411 2007-03-28 23:02:51Z mike $"
+ * "$Id: scsi.c 6432 2007-04-02 21:50:28Z mike $"
  *
  *   SCSI printer backend for the Common UNIX Printing System (CUPS).
  *
@@ -55,6 +55,7 @@
 
 #include <cups/backend.h>
 #include <cups/cups.h>
+#include <cups/i18n.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -222,5 +223,5 @@ main(int  argc,             /* I - Number of command-line arguments (6 or 7) */
 
 
 /*
- * End of "$Id: scsi.c 6411 2007-03-28 23:02:51Z mike $".
+ * End of "$Id: scsi.c 6432 2007-04-02 21:50:28Z mike $".
  */
index 4c779ec7d7ac1329e96b5e6f547b92d848ceee68..19994f0421777a72b25be800b31c7cdc7ce796b8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: socket.c 6403 2007-03-27 16:00:56Z mike $"
+ * "$Id: socket.c 6438 2007-04-03 17:52:41Z mike $"
  *
  *   AppSocket backend for the Common UNIX Printing System (CUPS).
  *
@@ -27,6 +27,7 @@
  *
  *   main()    - Send a file to the printer or server.
  *   side_cb() - Handle side-channel requests...
+ *   wait_bc() - Wait for back-channel data...
  */
 
 /*
@@ -56,6 +57,7 @@
  */
 
 static void    side_cb(int print_fd, int device_fd, int use_bc);
+static int     wait_bc(int device_fd, int secs);
 
 
 /*
@@ -93,9 +95,6 @@ main(int  argc,                               /* I - Number of command-line arguments (6 or 7) */
                  *addr;                /* Connected address */
   char         addrname[256];          /* Address name */
   ssize_t      tbytes;                 /* Total number of bytes written */
-  struct timeval timeout;              /* Timeout for select() */
-  fd_set       input;                  /* Input set for select() */
-  ssize_t      bc_bytes;               /* Number of back-channel bytes read */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   struct sigaction action;             /* Actions for POSIX signals */
 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
@@ -387,6 +386,12 @@ main(int  argc,                            /* I - Number of command-line arguments (6 or 7) */
               CUPS_LLCAST tbytes);
   }
 
+ /*
+  * Get any pending back-channel data...
+  */
+
+  while (wait_bc(device_fd, 5) > 0);
+
   if (waiteof)
   {
    /*
@@ -398,37 +403,7 @@ main(int  argc,                            /* I - Number of command-line arguments (6 or 7) */
 
     shutdown(device_fd, 1);
 
-    for (;;)
-    {
-     /*
-      * Wait a maximum of 90 seconds for backchannel data or a closed
-      * connection...
-      */
-
-      timeout.tv_sec  = 90;
-      timeout.tv_usec = 0;
-
-      FD_ZERO(&input);
-      FD_SET(device_fd, &input);
-
-      if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0)
-      {
-       /*
-       * Grab the data coming back and spit it out to stderr...
-       */
-
-       if ((bc_bytes = read(device_fd, resource, sizeof(resource))) > 0)
-       {
-         fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
-                 (int)bc_bytes);
-         cupsBackChannelWrite(resource, bc_bytes, 1.0);
-       }
-       else
-         break;
-      }
-      else
-       break;
-    }
+    while (wait_bc(device_fd, 90) > 0);
   }
 
  /*
@@ -503,5 +478,49 @@ side_cb(int print_fd,                      /* I - Print file */
 
 
 /*
- * End of "$Id: socket.c 6403 2007-03-27 16:00:56Z mike $".
+ * 'wait_bc()' - Wait for back-channel data...
+ */
+
+static int                             /* O - # bytes read or -1 on error */
+wait_bc(int device_fd,                 /* I - Socket */
+        int secs)                      /* I - Seconds to wait */
+{
+  struct timeval timeout;              /* Timeout for select() */
+  fd_set       input;                  /* Input set for select() */
+  ssize_t      bytes;                  /* Number of back-channel bytes read */
+  char         buffer[1024];           /* Back-channel buffer */
+
+
+ /*
+  * Wait up to "secs" seconds for backchannel data...
+  */
+
+  timeout.tv_sec  = secs;
+  timeout.tv_usec = 0;
+
+  FD_ZERO(&input);
+  FD_SET(device_fd, &input);
+
+  if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0)
+  {
+   /*
+    * Grab the data coming back and spit it out to stderr...
+    */
+
+    if ((bytes = read(device_fd, buffer, sizeof(buffer))) > 0)
+    {
+      fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
+             (int)bytes);
+      cupsBackChannelWrite(buffer, bytes, 1.0);
+    }
+
+    return (bytes);
+  }
+  else
+    return (-1);
+}
+
+
+/*
+ * End of "$Id: socket.c 6438 2007-04-03 17:52:41Z mike $".
  */
index 4b3cc47b19f0e04b4a835edadc1d1ffeaaa243b5..d1407696171c59dd76850a20fe7b9d749ec94db4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: usb-darwin.c 6422 2007-03-30 20:49:37Z mike $"
+ * "$Id: usb-darwin.c 6432 2007-04-02 21:50:28Z mike $"
  *
  * Copyright Â© 2005-2007 Apple Inc. All rights reserved.
  *
@@ -59,6 +59,7 @@
 #include <mach/mach_time.h>
 #include <cups/debug.h>
 #include <cups/sidechannel.h>
+#include <cups/i18n.h>
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/usb/IOUSBLib.h>
@@ -1728,5 +1729,5 @@ static void usbGetDevState(printer_data_t *userData, cups_sc_status_t *status, c
 }
 
 /*
- * End of "$Id: usb-darwin.c 6422 2007-03-30 20:49:37Z mike $".
+ * End of "$Id: usb-darwin.c 6432 2007-04-02 21:50:28Z mike $".
  */
index 0f58098bbc423aca81b63b6d4e8a63b7195ea6bc..feddc046a9e991f57c66fe2816a3908ef084781a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: usb-unix.c 6422 2007-03-30 20:49:37Z mike $"
+ * "$Id: usb-unix.c 6437 2007-04-03 17:38:10Z mike $"
  *
  *   USB port backend for the Common UNIX Printing System (CUPS).
  *
@@ -80,6 +80,15 @@ print_device(const char *uri,                /* I - Device URI */
 
   do
   {
+#ifdef __NetBSD__
+   /*
+    * NetBSD's ulpt driver currently does not support the
+    * back-channel...
+    */
+
+    use_bc = 0;
+
+#else
    /*
     * Disable backchannel data when printing to Brother, Canon, or
     * Minolta USB printers - apparently these printers will return
@@ -91,6 +100,7 @@ print_device(const char *uri,                /* I - Device URI */
              strcasecmp(hostname, "Canon") &&
              strcasecmp(hostname, "Konica Minolta") &&
              strcasecmp(hostname, "Minolta");
+#endif /* __NetBSD__ */
 
     if ((device_fd = open_device(uri, &use_bc)) == -1)
     {
@@ -519,7 +529,12 @@ open_device(const char *uri,               /* I - Device URI */
   }
 #else
   {
-    if ((fd = open(uri + 4, O_RDWR | O_EXCL)) < 0)
+    if (use_bc)
+      fd = open(uri + 4, O_RDWR | O_EXCL);
+    else
+      fd = -1;
+
+    if (fd < 0)
     {
       fd      = open(uri + 4, O_WRONLY | O_EXCL);
       *use_bc = 0;
@@ -602,5 +617,5 @@ side_cb(int print_fd,                       /* I - Print file */
 
 
 /*
- * End of "$Id: usb-unix.c 6422 2007-03-30 20:49:37Z mike $".
+ * End of "$Id: usb-unix.c 6437 2007-04-03 17:38:10Z mike $".
  */
index f8c0d55e4789fde51c8c6ea4fac9f5719d6116f0..ca0987660d183678970a8cb64b59f6b570c095b4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: admin.c 6378 2007-03-21 07:18:18Z mike $"
+ * "$Id: admin.c 6440 2007-04-03 23:17:17Z mike $"
  *
  *   Administration CGI for the Common UNIX Printing System (CUPS).
  *
  *   do_am_class()             - Add or modify a class.
  *   do_am_printer()           - Add or modify a printer.
  *   do_cancel_subscription()  - Cancel a subscription.
- *   do_config_printer()       - Configure the default options for a printer.
  *   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.
  */
@@ -61,11 +62,12 @@ 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_cancel_subscription(http_t *http);
-static void    do_config_printer(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);
@@ -172,6 +174,8 @@ 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, "list-available-printers"))
+      do_list_printers(http);
     else if (!strcmp(op, "add-class"))
       do_am_class(http, 0);
     else if (!strcmp(op, "add-printer"))
@@ -184,8 +188,10 @@ 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"))
@@ -1295,972 +1301,1018 @@ do_cancel_subscription(http_t *http)/* I - HTTP connection */
 
 
 /*
- * 'do_config_printer()' - Configure the default options for a printer.
+ * 'do_config_server()' - Configure server settings.
  */
 
 static void
-do_config_printer(http_t *http)                /* I - HTTP connection */
+do_config_server(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"));
-
-  fprintf(stderr, "DEBUG: do_config_printer(http=%p)\n", http);
-
- /*
-  * Get the printer name...
-  */
-
-  if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, "/printers/%s", printer);
-  else
+  if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF"))
   {
-    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
-    cgiStartHTML(title);
-    cgiCopyTemplateLang("error.tmpl");
-    cgiEndHTML();
-    return;
-  }
+   /*
+    * Save basic setting changes...
+    */
 
-  fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
+    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 */
 
- /*
-  * Get the PPD file...
-  */
 
-  if ((filename = cupsGetPPD2(http, printer)) == NULL)
-  {
-    fputs("DEBUG: No PPD file!?!\n", stderr);
+   /*
+    * Get the checkbox values from the form...
+    */
 
-    cgiStartHTML(title);
-    cgiShowIPPError(_("Unable to get PPD file!"));
-    cgiEndHTML();
-    return;
-  }
+    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";
 
-  fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
+    fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type);
+#endif /* HAVE_GSSAPI */
 
-  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;
-  }
+   /*
+    * Get the current server settings...
+    */
 
-  if (cgiGetVariable("job_sheets_start") != NULL ||
-      cgiGetVariable("job_sheets_end") != NULL)
-    have_options = 1;
-  else
-    have_options = 0;
+    if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
+    {
+      cgiStartHTML(cgiText(_("Change Settings")));
+      cgiSetVariable("MESSAGE",
+                     cgiText(_("Unable to change server settings:")));
+      cgiSetVariable("ERROR", cupsLastErrorString());
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
+      return;
+    }
 
-  ppdMarkDefaults(ppd);
+   /*
+    * 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)))
+    {
+     /*
+      * Settings *have* changed, so save the changes...
+      */
 
-  DEBUG_printf(("<P>ppd->num_groups = %d\n"
-                "<UL>\n", ppd->num_groups));
+      cupsFreeOptions(num_settings, settings);
 
-  for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
-  {
-    DEBUG_printf(("<LI>%s<UL>\n", group->text));
+      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 */
 
-    for (j = group->num_options, option = group->options;
-         j > 0;
-        j --, option ++)
-      if ((var = cgiGetVariable(option->keyword)) != NULL)
+      if (!cupsAdminSetServerSettings(http, num_settings, settings))
       {
-        DEBUG_printf(("<LI>%s = \"%s\"</LI>\n", option->keyword, var));
-        have_options = 1;
-       ppdMarkOption(ppd, option->keyword, var);
+       cgiStartHTML(cgiText(_("Change Settings")));
+       cgiSetVariable("MESSAGE",
+                       cgiText(_("Unable to change server settings:")));
+       cgiSetVariable("ERROR", cupsLastErrorString());
+       cgiCopyTemplateLang("error.tmpl");
       }
-#ifdef DEBUG
       else
-        printf("<LI>%s not defined!</LI>\n", option->keyword);
-#endif /* DEBUG */
+      {
+       cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
+       cgiStartHTML(cgiText(_("Change Settings")));
+       cgiCopyTemplateLang("restart.tmpl");
+      }
+    }
+    else
+    {
+     /*
+      * No changes...
+      */
 
-    DEBUG_puts("</UL></LI>");
-  }
+      cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
+      cgiStartHTML(cgiText(_("Change Settings")));
+      cgiCopyTemplateLang("norestart.tmpl");
+    }
 
-  DEBUG_printf(("</UL>\n"
-                "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd)));
+    cupsFreeOptions(num_settings, settings);
 
-  if (!have_options || ppdConflicts(ppd))
+    cgiEndHTML();
+  }
+  else if (cgiIsPOST())
   {
    /*
-    * Show the options to the user...
+    * Save hand-edited config file...
     */
 
-    fputs("DEBUG: Showing options...\n", stderr);
+    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 */
 
-    ppdLocalize(ppd);
 
-    cgiStartHTML(cgiText(_("Set Printer Options")));
-    cgiCopyTemplateLang("set-printer-options-header.tmpl");
+   /*
+    * Create a temporary file for the new cupsd.conf file...
+    */
 
-    if (ppdConflicts(ppd))
+    if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
     {
-      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(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
+      cgiSetVariable("ERROR", strerror(errno));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
+      
+      perror(tempfile);
+      return;
     }
 
-    for (i = ppd->num_groups, group = ppd->groups;
-        i > 0;
-        i --, group ++)
+    if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL)
     {
-      if (!strcmp(group->name, "InstallableOptions"))
-       cgiSetVariable("GROUP", cgiText(_("Options Installed")));
-      else
-       cgiSetVariable("GROUP", group->text);
-
-      cgiCopyTemplateLang("option-header.tmpl");
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
+      cgiSetVariable("ERROR", strerror(errno));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
       
-      for (j = group->num_options, option = group->options;
-           j > 0;
-          j --, option ++)
-      {
-        if (!strcmp(option->keyword, "PageRegion"))
-         continue;
+      perror(tempfile);
+      close(tempfd);
+      unlink(tempfile);
+      return;
+    }
 
-        cgiSetVariable("KEYWORD", option->keyword);
-        cgiSetVariable("KEYTEXT", option->text);
-           
-       if (option->conflicted)
-         cgiSetVariable("CONFLICTED", "1");
-       else
-         cgiSetVariable("CONFLICTED", "0");
+   /*
+    * Copy the cupsd.conf text from the form variable...
+    */
 
-       cgiSetSize("CHOICES", 0);
-       cgiSetSize("TEXT", 0);
-       for (k = 0, m = 0; k < option->num_choices; k ++)
-       {
-        /*
-         * Hide custom option values...
-         */
+    start = cgiGetVariable("CUPSDCONF");
+    while (start)
+    {
+      if ((end = strstr(start, "\r\n")) == NULL)
+        if ((end = strstr(start, "\n")) == NULL)
+         end = start + strlen(start);
 
-         if (!strcmp(option->choices[k].choice, "Custom"))
-           continue;
+      cupsFileWrite(temp, start, end - start);
+      cupsFilePutChar(temp, '\n');
 
-         cgiSetArray("CHOICES", m, option->choices[k].choice);
-         cgiSetArray("TEXT", m, option->choices[k].text);
+      if (*end == '\r')
+        start = end + 2;
+      else if (*end == '\n')
+        start = end + 1;
+      else
+        start = NULL;
+    }
 
-          m ++;
+    cupsFileClose(temp);
 
-          if (option->choices[k].marked)
-           cgiSetVariable("DEFCHOICE", option->choices[k].choice);
-       }
+   /*
+    * Upload the configuration file to the server...
+    */
 
-        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;
-       }
-      }
+    status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
 
-      cgiCopyTemplateLang("option-trailer.tmpl");
+    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;URL=/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 */
+
+
    /*
-    * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
-    * following attributes:
-    *
-    *    attributes-charset
-    *    attributes-natural-language
-    *    printer-uri
+    * Locate the cupsd.conf file...
     */
 
-    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+    if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
+      server_root = CUPS_SERVERROOT;
 
-    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);
+    snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root);
 
    /*
-    * Do the request and get back a response...
+    * Figure out the size...
     */
 
-    if ((response = cupsDoRequest(http, request, "/")) != NULL)
+    if (stat(filename, &info))
     {
-      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 access cupsd.conf file:")));
+      cgiSetVariable("ERROR", strerror(errno));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
 
-       cgiSetVariable("GROUP", cgiText(_("Banners")));
-       cgiCopyTemplateLang("option-header.tmpl");
+      perror(filename);
+      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);
-       }
+    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();
 
-        attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
+      fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename,
+              (long)info.st_size);
+      return;
+    }
 
-        cgiSetVariable("KEYWORD", "job_sheets_start");
-       cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner")));
-        cgiSetVariable("DEFCHOICE", attr == NULL ?
-                                   "" : attr->values[0].string.text);
+   /*
+    * Open the cupsd.conf file...
+    */
 
-       cgiCopyTemplateLang("option-pickone.tmpl");
+    if ((cupsd = cupsFileOpen(filename, "r")) == NULL)
+    {
+     /*
+      * Unable to open - log an error...
+      */
 
-        cgiSetVariable("KEYWORD", "job_sheets_end");
-       cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner")));
-        cgiSetVariable("DEFCHOICE", attr == NULL && attr->num_values > 1 ?
-                                   "" : attr->values[1].string.text);
+      cgiStartHTML(cgiText(_("Edit Configuration File")));
+      cgiSetVariable("MESSAGE",
+                     cgiText(_("Unable to access cupsd.conf file:")));
+      cgiSetVariable("ERROR", strerror(errno));
+      cgiCopyTemplateLang("error.tmpl");
+      cgiEndHTML();
 
-       cgiCopyTemplateLang("option-pickone.tmpl");
+      perror(filename);
+      return;
+    }
 
-       cgiCopyTemplateLang("option-trailer.tmpl");
-      }
+   /*
+    * Allocate memory and load the file into a string buffer...
+    */
 
-      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...
-       */
+    buffer = calloc(1, info.st_size + 1);
 
-       cgiSetVariable("GROUP", cgiText(_("Policies")));
-       cgiCopyTemplateLang("option-header.tmpl");
+    cupsFileRead(cupsd, buffer, info.st_size);
+    cupsFileClose(cupsd);
 
-       /*
-        * Error policy...
-       */
+    cgiSetVariable("CUPSDCONF", buffer);
+    free(buffer);
 
-        attr = ippFindAttribute(response, "printer-error-policy-supported",
-                               IPP_TAG_ZERO);
+   /*
+    * Show the current config file...
+    */
 
-        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);
-         }
+    cgiStartHTML(cgiText(_("Edit Configuration File")));
 
-          attr = ippFindAttribute(response, "printer-error-policy",
-                                 IPP_TAG_ZERO);
+    printf("<!-- \"%s\" -->\n", filename);
 
-          cgiSetVariable("KEYWORD", "printer_error_policy");
-         cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
-          cgiSetVariable("DEFCHOICE", attr == NULL ?
-                                     "" : attr->values[0].string.text);
-        }
+    cgiCopyTemplateLang("edit-config.tmpl");
 
-       cgiCopyTemplateLang("option-pickone.tmpl");
+    cgiEndHTML();
+  }
+}
 
-       /*
-        * Operation policy...
-       */
 
-        attr = ippFindAttribute(response, "printer-op-policy-supported",
-                               IPP_TAG_ZERO);
+/*
+ * 'do_delete_class()' - Delete a class...
+ */
 
-        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);
-         }
+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 */
 
-          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);
+ /*
+  * Get form variables...
+  */
 
-         cgiCopyTemplateLang("option-pickone.tmpl");
-        }
+  if (cgiGetVariable("CONFIRM") == NULL)
+  {
+    cgiStartHTML(cgiText(_("Delete Class")));
+    cgiCopyTemplateLang("class-confirm.tmpl");
+    cgiEndHTML();
+    return;
+  }
 
-       cgiCopyTemplateLang("option-trailer.tmpl");
-      }
+  if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL)
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                     "localhost", 0, "/classes/%s", pclass);
+  else
+  {
+    cgiStartHTML(cgiText(_("Delete Class")));
+    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
+    cgiCopyTemplateLang("error.tmpl");
+    cgiEndHTML();
+    return;
+  }
 
-      ippDelete(response);
-    }
+ /*
+  * Build a CUPS_DELETE_CLASS request, which requires the following
+  * attributes:
+  *
+  *    attributes-charset
+  *    attributes-natural-language
+  *    printer-uri
+  */
+
+  request = ippNewRequest(CUPS_DELETE_CLASS);
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, uri);
+
+ /*
+  * Do the request and get back a response...
+  */
+
+  ippDelete(cupsDoRequest(http, request, "/admin/"));
 
+ /*
+  * Show the results...
+  */
+
+  if (cupsLastError() <= IPP_OK_CONFLICT)
+  {
    /*
-    * Binary protocol support...
+    * Redirect successful updates back to the classes page...
     */
 
-    if (ppd->protocols && strstr(ppd->protocols, "BCP"))
-    {
-      protocol = ppdFindAttr(ppd, "cupsProtocol", NULL);
+    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes");
+  }
 
-      cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol")));
-      cgiCopyTemplateLang("option-header.tmpl");
+  cgiStartHTML(cgiText(_("Delete Class")));
 
-      cgiSetSize("CHOICES", 2);
-      cgiSetSize("TEXT", 2);
-      cgiSetArray("CHOICES", 0, "None");
-      cgiSetArray("TEXT", 0, cgiText(_("None")));
+  if (cupsLastError() > IPP_OK_CONFLICT)
+    cgiShowIPPError(_("Unable to delete class:"));
+  else
+    cgiCopyTemplateLang("class-deleted.tmpl");
 
-      if (strstr(ppd->protocols, "TBCP"))
-      {
-       cgiSetArray("CHOICES", 1, "TBCP");
-       cgiSetArray("TEXT", 1, "TBCP");
-      }
-      else
-      {
-       cgiSetArray("CHOICES", 1, "BCP");
-       cgiSetArray("TEXT", 1, "BCP");
-      }
+  cgiEndHTML();
+}
 
-      cgiSetVariable("KEYWORD", "protocol");
-      cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol")));
-      cgiSetVariable("DEFCHOICE", protocol ? protocol->value : "None");
 
-      cgiCopyTemplateLang("option-pickone.tmpl");
+/*
+ * 'do_delete_printer()' - Delete a printer...
+ */
 
-      cgiCopyTemplateLang("option-trailer.tmpl");
-    }
+static void
+do_delete_printer(http_t *http)                /* I - HTTP connection */
+{
+  ipp_t                *request;               /* IPP request */
+  char         uri[HTTP_MAX_URI];      /* Job URI */
+  const char   *printer;               /* Printer printer name */
 
-    cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
+
+ /*
+  * Get form variables...
+  */
+
+  if (cgiGetVariable("CONFIRM") == NULL)
+  {
+    cgiStartHTML(cgiText(_("Delete Printer")));
+    cgiCopyTemplateLang("printer-confirm.tmpl");
     cgiEndHTML();
+    return;
   }
+
+  if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                     "localhost", 0, "/printers/%s", printer);
   else
+  {
+    cgiStartHTML(cgiText(_("Delete Printer")));
+    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
+    cgiCopyTemplateLang("error.tmpl");
+    cgiEndHTML();
+    return;
+  }
+
+ /*
+  * Build a CUPS_DELETE_PRINTER request, which requires the following
+  * attributes:
+  *
+  *    attributes-charset
+  *    attributes-natural-language
+  *    printer-uri
+  */
+
+  request = ippNewRequest(CUPS_DELETE_PRINTER);
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, uri);
+
+ /*
+  * Do the request and get back a response...
+  */
+
+  ippDelete(cupsDoRequest(http, request, "/admin/"));
+
+ /*
+  * Show the results...
+  */
+
+  if (cupsLastError() <= IPP_OK_CONFLICT)
   {
    /*
-    * Set default options...
+    * Redirect successful updates back to the printers page...
     */
 
-    fputs("DEBUG: Setting options...\n", stderr);
+    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers");
+  }
 
-    out = cupsTempFile2(tempfile, sizeof(tempfile));
-    in  = cupsFileOpen(filename, "r");
+  cgiStartHTML(cgiText(_("Delete Printer")));
 
-    if (!in || !out)
-    {
-      cgiSetVariable("ERROR", strerror(errno));
-      cgiStartHTML(cgiText(_("Set Printer Options")));
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
+  if (cupsLastError() > IPP_OK_CONFLICT)
+    cgiShowIPPError(_("Unable to delete printer:"));
+  else
+    cgiCopyTemplateLang("printer-deleted.tmpl");
 
-      if (in)
-        cupsFileClose(in);
+  cgiEndHTML();
+}
 
-      if (out)
-      {
-        cupsFileClose(out);
-       unlink(tempfile);
-      }
 
-      unlink(filename);
-      return;
-    }
+/*
+ * 'do_export()' - Export printers to Samba...
+ */
 
-    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
-      {
-       /*
-        * Get default option name...
-       */
+static void
+do_export(http_t *http)                        /* I - HTTP connection */
+{
+  int          i, j;                   /* Looping vars */
+  ipp_t                *request,               /* IPP request */
+               *response;              /* IPP response */
+  const char   *username,              /* Samba username */
+               *password,              /* Samba password */
+               *export_all;            /* Export all printers? */
+  int          export_count,           /* Number of printers to export */
+               printer_count;          /* Number of available printers */
+  const char   *name,                  /* What name to pull */
+               *dest;                  /* Current destination */
+  char         ppd[1024];              /* PPD file */
 
-        strlcpy(keyword, line + 8, sizeof(keyword));
 
-       for (keyptr = keyword; *keyptr; keyptr ++)
-         if (*keyptr == ':' || isspace(*keyptr & 255))
-           break;
+ /*
+  * Get form data...
+  */
 
-        *keyptr = '\0';
+  username     = cgiGetVariable("USERNAME");
+  password     = cgiGetVariable("PASSWORD");
+  export_all   = cgiGetVariable("EXPORT_ALL");
+  export_count = cgiGetSize("EXPORT_NAME");
 
-        if (!strcmp(keyword, "PageRegion") ||
-           !strcmp(keyword, "PaperDimension") ||
-           !strcmp(keyword, "ImageableArea"))
-         var = cgiGetVariable("PageSize");
-       else
-         var = cgiGetVariable(keyword);
+ /*
+  * Get list of available printers...
+  */
 
-        if (var != NULL)
-         cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
-       else
-         cupsFilePrintf(out, "%s\n", line);
-      }
-    }
+  cgiSetSize("PRINTER_NAME", 0);
+  cgiSetSize("PRINTER_EXPORT", 0);
 
-    if ((var = cgiGetVariable("protocol")) != NULL)
-      cupsFilePrintf(out, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
+  request = ippNewRequest(CUPS_GET_PRINTERS);
 
-    cupsFileClose(in);
-    cupsFileClose(out);
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
+                "printer-type", 0);
 
-   /*
-    * Build a CUPS_ADD_PRINTER request, which requires the following
-    * attributes:
-    *
-    *    attributes-charset
-    *    attributes-natural-language
-    *    printer-uri
-    *    job-sheets-default
-    *    [ppd file]
-    */
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
+                "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE |
+                                    CUPS_PRINTER_IMPLICIT);
 
-    request = ippNewRequest(CUPS_ADD_PRINTER);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+               "requested-attributes", NULL, "printer-name");
 
-    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);
+  if ((response = cupsDoRequest(http, request, "/")) != NULL)
+  {
+    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
+    ippDelete(response);
 
-    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"));
+    if (!export_all)
+    {
+      printer_count = cgiGetSize("PRINTER_NAME");
 
-    if ((var = cgiGetVariable("printer_error_policy")) != NULL)
-      attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                          "printer-error-policy", NULL, var);
+      for (i = 0; i < printer_count; i ++)
+      {
+        dest = cgiGetArray("PRINTER_NAME", i);
 
-    if ((var = cgiGetVariable("printer_op_policy")) != NULL)
-      attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                          "printer-op-policy", NULL, var);
+        for (j = 0; j < export_count; j ++)
+         if (!strcasecmp(dest, cgiGetArray("EXPORT_NAME", j)))
+            break;
+
+        cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : "");
+      }
+    }
+  }
+
+ /*
+  * Export or get the printers to export...
+  */
 
+  if (username && *username && password && *password &&
+      (export_all || export_count > 0))
+  {
    /*
-    * Do the request and get back a response...
+    * Do export...
     */
 
-    ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile));
+    fputs("DEBUG: Export printers...\n", stderr);
 
-    if (cupsLastError() > IPP_OK_CONFLICT)
+    if (export_all)
     {
-      cgiStartHTML(title);
-      cgiShowIPPError(_("Unable to set options:"));
+      name         = "PRINTER_NAME";
+      export_count = cgiGetSize("PRINTER_NAME");
     }
     else
-    {
-     /*
-      * Redirect successful updates back to the printer page...
-      */
+      name = "EXPORT_NAME";
 
-      char     refresh[1024];          /* Refresh URL */
+    for (i = 0; i < export_count; i ++)
+    {
+      dest = cgiGetArray(name, i);
 
+      if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd)))
+        break;
 
-      cgiFormEncode(uri, printer, sizeof(uri));
-      snprintf(refresh, sizeof(refresh),
-               "5;URL=/admin/?OP=redirect&URL=/printers/%s", uri);
-      cgiSetVariable("refresh_page", refresh);
+      j = cupsAdminExportSamba(dest, ppd, "localhost", username, password,
+                               stderr);
 
-      cgiStartHTML(title);
+      unlink(ppd);
 
-      cgiCopyTemplateLang("printer-configured.tmpl");
+      if (!j)
+        break;
     }
 
-    cgiEndHTML();
-
-    unlink(tempfile);
+    if (i < export_count)
+      cgiSetVariable("ERROR", cupsLastErrorString());
+    else
+    {
+      cgiStartHTML(cgiText(_("Export Printers to Samba")));
+      cgiCopyTemplateLang("samba-exported.tmpl");
+      cgiEndHTML();
+      return;
+    }
   }
+  else if (username && !*username)
+    cgiSetVariable("ERROR",
+                   cgiText(_("A Samba username is required to export "
+                            "printer drivers!")));
+  else if (username && (!password || !*password))
+    cgiSetVariable("ERROR",
+                   cgiText(_("A Samba password is required to export "
+                            "printer drivers!")));
+
+ /*
+  * Show form...
+  */
 
-  unlink(filename);
+  cgiStartHTML(cgiText(_("Export Printers to Samba")));
+  cgiCopyTemplateLang("samba-export.tmpl");
+  cgiEndHTML();
 }
 
 
 /*
- * 'do_config_server()' - Configure server settings.
+ * 'do_list_printers()' - List available printers...
  */
 
 static void
-do_config_server(http_t *http)         /* I - HTTP connection */
+do_list_printers(http_t *http)         /* I - HTTP connection */
 {
-  if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF"))
-  {
-   /*
-    * Save basic setting changes...
-    */
+  ipp_t                *request,               /* IPP request */
+               *response;              /* IPP response */
+  ipp_attribute_t *attr;               /* IPP attribute */
 
-    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 */
 
+  cgiStartHTML(cgiText(_("List Available Printers")));
+  fflush(stdout);
+
+ /*
+  * 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);
 
+  if ((response = cupsDoRequest(http, request, "/")) != NULL)
+  {
    /*
-    * Get the checkbox values from the form...
+    * Got the printer list, now load the devices...
     */
 
-    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";
+    int                i;                      /* Looping var */
+    cups_array_t *printer_devices;     /* Printer devices for local printers */
+    char       *printer_device;        /* Current printer device */
 
-    fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type);
-#endif /* HAVE_GSSAPI */
 
    /*
-    * Get the current server settings...
+    * Allocate an array and copy the device strings...
     */
 
-    if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
+    printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+    for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI);
+         attr;
+        attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI))
     {
-      cgiStartHTML(cgiText(_("Change Settings")));
-      cgiSetVariable("MESSAGE",
-                     cgiText(_("Unable to change server settings:")));
-      cgiSetVariable("ERROR", cupsLastErrorString());
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
-      return;
+      cupsArrayAdd(printer_devices, strdup(attr->values[0].string.text));
     }
 
    /*
-    * See if the settings have changed...
+    * Free the printer list and get the device list...
     */
 
-    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)))
-    {
-     /*
-      * Settings *have* changed, so save the changes...
-      */
-
-      cupsFreeOptions(num_settings, settings);
+    ippDelete(response);
 
-      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 */
+    request = ippNewRequest(CUPS_GET_DEVICES);
 
-      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;URL=/admin/?OP=redirect");
-       cgiStartHTML(cgiText(_("Change Settings")));
-       cgiCopyTemplateLang("restart.tmpl");
-      }
-    }
-    else
+    if ((response = cupsDoRequest(http, request, "/")) != NULL)
     {
      /*
-      * No changes...
+      * Got the device list, let's parse it...
       */
 
-      cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
-      cgiStartHTML(cgiText(_("Change Settings")));
-      cgiCopyTemplateLang("norestart.tmpl");
-    }
+      const char *device_uri,          /* device-uri attribute value */
+               *device_make_and_model, /* device-make-and-model value */
+               *device_info;           /* device-info value */
 
-    cupsFreeOptions(num_settings, settings);
 
-    cgiEndHTML();
-  }
-  else if (cgiIsPOST())
-  {
-   /*
-    * Save hand-edited config file...
-    */
+      for (i = 0, attr = response->attrs; attr; attr = attr->next)
+      {
+       /*
+        * Skip leading attributes until we hit a device...
+       */
 
-    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 */
+       while (attr && attr->group_tag != IPP_TAG_PRINTER)
+          attr = attr->next;
 
+       if (!attr)
+          break;
 
-   /*
-    * Create a temporary file for the new cupsd.conf file...
-    */
+       /*
+       * Pull the needed attributes from this device...
+       */
 
-    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;
-    }
+       device_info           = NULL;
+       device_make_and_model = NULL;
+       device_uri            = NULL;
 
-    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;
-    }
+       while (attr && attr->group_tag == IPP_TAG_PRINTER)
+       {
+          if (!strcmp(attr->name, "device-info") &&
+             attr->value_tag == IPP_TAG_TEXT)
+           device_info = attr->values[0].string.text;
 
-   /*
-    * Copy the cupsd.conf text from the form variable...
-    */
+          if (!strcmp(attr->name, "device-make-and-model") &&
+             attr->value_tag == IPP_TAG_TEXT)
+           device_make_and_model = attr->values[0].string.text;
 
-    start = cgiGetVariable("CUPSDCONF");
-    while (start)
-    {
-      if ((end = strstr(start, "\r\n")) == NULL)
-        if ((end = strstr(start, "\n")) == NULL)
-         end = start + strlen(start);
+          if (!strcmp(attr->name, "device-uri") &&
+             attr->value_tag == IPP_TAG_URI)
+           device_uri = attr->values[0].string.text;
 
-      cupsFileWrite(temp, start, end - start);
-      cupsFilePutChar(temp, '\n');
+          attr = attr->next;
+       }
 
-      if (*end == '\r')
-        start = end + 2;
-      else if (*end == '\n')
-        start = end + 1;
-      else
-        start = NULL;
-    }
+       /*
+       * See if we have everything needed...
+       */
 
-    cupsFileClose(temp);
+       if (device_info && device_make_and_model && device_uri &&
+           strcasecmp(device_make_and_model, "unknown") &&
+           strchr(device_uri, ':'))
+       {
+        /*
+         * Yes, now see if there is already a printer for this
+         * device...
+         */
 
-   /*
-    * Upload the configuration file to the server...
-    */
+          if (!cupsArrayFind(printer_devices, (void *)device_uri))
+          {
+          /*
+           * Not found, so it must be a new printer...
+           */
 
-    status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
+            char       options[1024],  /* Form variables for this device */
+                       *options_ptr;   /* Pointer into string */
+           const char  *ptr;           /* Pointer into device string */
 
-    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;URL=/admin/?OP=redirect");
+           /*
+           * Format the printer name variable for this device...
+           *
+           * We use the device-info string first, then device-uri,
+           * and finally device-make-and-model to come up with a
+           * suitable name.
+           */
 
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiCopyTemplateLang("restart.tmpl");
-    }
+           strcpy(options, "TEMPLATE_NAME=");
+           options_ptr = options + strlen(options);
 
-    cgiEndHTML();
+            if (strncasecmp(device_info, "unknown", 7))
+             ptr = device_info;
+            else if ((ptr = strstr(device_uri, "://")) != NULL)
+             ptr += 3;
+           else
+             ptr = device_make_and_model;
 
-    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 */
+           for (;
+                options_ptr < (options + sizeof(options) - 1) && *ptr;
+                ptr ++)
+             if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' ||
+                 *ptr == '.')
+               *options_ptr++ = *ptr;
+             else if ((*ptr == ' ' || *ptr == '/') && options_ptr[-1] != '_')
+               *options_ptr++ = '_';
+             else if (*ptr == '?' || *ptr == '(')
+               break;
 
+           /*
+           * Then add the make and model in the printer info, so
+           * that MacOS clients see something reasonable...
+           */
 
-   /*
-    * Locate the cupsd.conf file...
-    */
+            strlcpy(options_ptr, "&PRINTER_LOCATION=Local+Printer"
+                                "&PRINTER_INFO=",
+                   sizeof(options) - (options_ptr - options));
+           options_ptr += strlen(options_ptr);
 
-    if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
-      server_root = CUPS_SERVERROOT;
+            cgiFormEncode(options_ptr, device_make_and_model,
+                         sizeof(options) - (options_ptr - options));
+           options_ptr += strlen(options_ptr);
 
-    snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root);
+           /*
+           * Then copy the device URI...
+           */
 
-   /*
-    * Figure out the size...
-    */
+           strlcpy(options_ptr, "&DEVICE_URI=",
+                   sizeof(options) - (options_ptr - options));
+           options_ptr += strlen(options_ptr);
 
-    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();
+            cgiFormEncode(options_ptr, device_uri,
+                         sizeof(options) - (options_ptr - options));
+           options_ptr += strlen(options_ptr);
 
-      perror(filename);
-      return;
-    }
+            if (options_ptr < (options + sizeof(options) - 1))
+           {
+             *options_ptr++ = '|';
+             cgiFormEncode(options_ptr, device_make_and_model,
+                         sizeof(options) - (options_ptr - options));
+           }
 
-    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();
+           /*
+           * Finally, set the form variables for this printer...
+           */
 
-      fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename,
-              (long)info.st_size);
-      return;
-    }
+           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 ++;
+         }
+       }
 
-   /*
-    * Open the cupsd.conf file...
-    */
+        if (!attr)
+         break;
+      }
+
+      ippDelete(response);
 
-    if ((cupsd = cupsFileOpen(filename, "r")) == NULL)
-    {
      /*
-      * Unable to open - log an error...
+      * Free the device list...
       */
 
-      cgiStartHTML(cgiText(_("Edit Configuration File")));
-      cgiSetVariable("MESSAGE",
-                     cgiText(_("Unable to access cupsd.conf file:")));
-      cgiSetVariable("ERROR", strerror(errno));
-      cgiCopyTemplateLang("error.tmpl");
-      cgiEndHTML();
+      for (printer_device = (char *)cupsArrayFirst(printer_devices);
+           printer_device;
+          printer_device = (char *)cupsArrayNext(printer_devices))
+        free(printer_device);
 
-      perror(filename);
-      return;
+      cupsArrayDelete(printer_devices);
     }
+  }
 
-   /*
-    * 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);
+ /*
+  * Finally, show the printer list...
+  */
 
-    cgiCopyTemplateLang("edit-config.tmpl");
+  cgiCopyTemplateLang("list-available-printers.tmpl");
 
-    cgiEndHTML();
-  }
+  cgiEndHTML();
 }
 
 
 /*
- * 'do_delete_class()' - Delete a class...
+ * 'do_menu()' - Show the main menu...
  */
 
 static void
-do_delete_class(http_t *http)          /* I - HTTP connection */
+do_menu(http_t *http)                  /* I - HTTP connection */
 {
-  ipp_t                *request;               /* IPP request */
-  char         uri[HTTP_MAX_URI];      /* Job URI */
-  const char   *pclass;                /* Printer class name */
+  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 form variables...
+  * Get the current server settings...
   */
 
-  if (cgiGetVariable("CONFIRM") == NULL)
+  if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
   {
-    cgiStartHTML(cgiText(_("Delete Class")));
-    cgiCopyTemplateLang("class-confirm.tmpl");
-    cgiEndHTML();
-    return;
+    cgiSetVariable("SETTINGS_MESSAGE",
+                   cgiText(_("Unable to open cupsd.conf file:")));
+    cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
   }
 
-  if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL)
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, "/classes/%s", pclass);
-  else
-  {
-    cgiStartHTML(cgiText(_("Delete Class")));
-    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
-    cgiCopyTemplateLang("error.tmpl");
-    cgiEndHTML();
-    return;
-  }
+  if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("DEBUG_LOGGING", "CHECKED");
 
- /*
-  * Build a CUPS_DELETE_CLASS request, which requires the following
-  * attributes:
-  *
-  *    attributes-charset
-  *    attributes-natural-language
-  *    printer-uri
-  */
+  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("REMOTE_ADMIN", "CHECKED");
 
-  request = ippNewRequest(CUPS_DELETE_CLASS);
+  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("REMOTE_ANY", "CHECKED");
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, uri);
+  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
 
- /*
-  * Do the request and get back a response...
-  */
+  if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
+                           settings)) != NULL && atoi(val))
+    cgiSetVariable("SHARE_PRINTERS", "CHECKED");
 
-  ippDelete(cupsDoRequest(http, request, "/admin/"));
+  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);
 
  /*
-  * Show the results...
+  * See if Samba and the Windows drivers are installed...
   */
 
-  if (cupsLastError() <= IPP_OK_CONFLICT)
+  if ((datadir = getenv("CUPS_DATADIR")) == NULL)
+    datadir = CUPS_DATADIR;
+
+  snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir);
+  if (!access(filename, R_OK))
   {
    /*
-    * Redirect successful updates back to the classes page...
+    * Found Windows 2000 driver file, see if we have smbclient and
+    * rpcclient...
     */
 
-    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes");
+    if (cupsFileFind("smbclient", getenv("PATH"), 1, filename,
+                     sizeof(filename)) &&
+        cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
+                    sizeof(filename)))
+      cgiSetVariable("HAVE_SAMBA", "Y");
+    else
+    {
+      if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename,
+                        sizeof(filename)))
+        fputs("ERROR: smbclient not found!\n", stderr);
+
+      if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
+                        sizeof(filename)))
+        fputs("ERROR: rpcclient not found!\n", stderr);
+    }
   }
+  else
+    perror(filename);
 
-  cgiStartHTML(cgiText(_("Delete Class")));
+ /*
+  * Subscriptions...
+  */
 
-  if (cupsLastError() > IPP_OK_CONFLICT)
-    cgiShowIPPError(_("Unable to delete class:"));
-  else
-    cgiCopyTemplateLang("class-deleted.tmpl");
+  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...
+  */
+
+  cgiStartHTML(cgiText(_("Administration")));
+
+  cgiCopyTemplateLang("admin.tmpl");
 
   cgiEndHTML();
 }
 
 
 /*
- * 'do_delete_printer()' - Delete a printer...
+ * 'do_printer_op()' - Do a printer operation.
  */
 
 static void
-do_delete_printer(http_t *http)                /* I - HTTP connection */
+do_printer_op(http_t      *http,       /* I - HTTP connection */
+             ipp_op_t    op,           /* I - Operation to perform */
+             const char  *title)       /* I - Title of page */
 {
   ipp_t                *request;               /* IPP request */
-  char         uri[HTTP_MAX_URI];      /* Job URI */
-  const char   *printer;               /* Printer printer name */
-
+  char         uri[HTTP_MAX_URI];      /* Printer URI */
+  const char   *printer,               /* Printer name (purge-jobs) */
+               *is_class;              /* Is a class? */
 
- /*
-  * Get form variables...
-  */
 
-  if (cgiGetVariable("CONFIRM") == NULL)
-  {
-    cgiStartHTML(cgiText(_("Delete Printer")));
-    cgiCopyTemplateLang("printer-confirm.tmpl");
-    cgiEndHTML();
-    return;
-  }
+  is_class = cgiGetVariable("IS_CLASS");
+  printer  = cgiGetVariable("PRINTER_NAME");
 
-  if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
-    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                     "localhost", 0, "/printers/%s", printer);
-  else
+  if (!printer)
   {
-    cgiStartHTML(cgiText(_("Delete Printer")));
     cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
+    cgiStartHTML(title);
     cgiCopyTemplateLang("error.tmpl");
     cgiEndHTML();
     return;
   }
 
  /*
-  * Build a CUPS_DELETE_PRINTER request, which requires the following
+  * Build a printer request, which requires the following
   * attributes:
   *
   *    attributes-charset
@@ -2268,8 +2320,11 @@ do_delete_printer(http_t *http)          /* I - HTTP connection */
   *    printer-uri
   */
 
-  request = ippNewRequest(CUPS_DELETE_PRINTER);
+  request = ippNewRequest(op);
 
+  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);
 
@@ -2279,812 +2334,812 @@ do_delete_printer(http_t *http)               /* I - HTTP connection */
 
   ippDelete(cupsDoRequest(http, request, "/admin/"));
 
- /*
-  * Show the results...
-  */
-
-  if (cupsLastError() <= IPP_OK_CONFLICT)
+  if (cupsLastError() > IPP_OK_CONFLICT)
+  {
+    cgiStartHTML(title);
+    cgiShowIPPError(_("Unable to change printer:"));
+  }
+  else
   {
    /*
-    * Redirect successful updates back to the printers page...
+    * Redirect successful updates back to the printer page...
     */
 
-    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers");
-  }
+    char       url[1024],              /* Printer/class URL */
+               refresh[1024];          /* Refresh URL */
 
-  cgiStartHTML(cgiText(_("Delete Printer")));
 
-  if (cupsLastError() > IPP_OK_CONFLICT)
-    cgiShowIPPError(_("Unable to delete printer:"));
-  else
-    cgiCopyTemplateLang("printer-deleted.tmpl");
+    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(title);
+
+    if (op == IPP_PAUSE_PRINTER)
+      cgiCopyTemplateLang("printer-stop.tmpl");
+    else if (op == IPP_RESUME_PRINTER)
+      cgiCopyTemplateLang("printer-start.tmpl");
+    else if (op == CUPS_ACCEPT_JOBS)
+      cgiCopyTemplateLang("printer-accept.tmpl");
+    else if (op == CUPS_REJECT_JOBS)
+      cgiCopyTemplateLang("printer-reject.tmpl");
+    else if (op == IPP_PURGE_JOBS)
+      cgiCopyTemplateLang("printer-purge.tmpl");
+    else if (op == CUPS_SET_DEFAULT)
+      cgiCopyTemplateLang("printer-default.tmpl");
+  }
 
   cgiEndHTML();
 }
 
 
 /*
- * 'do_export()' - Export printers to Samba...
+ * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
  */
 
 static void
-do_export(http_t *http)                        /* I - HTTP connection */
+do_set_allowed_users(http_t *http)     /* I - HTTP connection */
 {
-  int          i, j;                   /* Looping vars */
+  int          i;                      /* Looping var */
   ipp_t                *request,               /* IPP request */
                *response;              /* IPP response */
-  const char   *username,              /* Samba username */
-               *password,              /* Samba password */
-               *export_all;            /* Export all printers? */
-  int          export_count,           /* Number of printers to export */
-               printer_count;          /* Number of available printers */
-  const char   *name,                  /* What name to pull */
-               *dest;                  /* Current destination */
-  char         ppd[1024];              /* PPD file */
-
+  char         uri[HTTP_MAX_URI];      /* Printer URI */
+  const char   *printer,               /* Printer name (purge-jobs) */
+               *is_class,              /* Is a class? */
+               *users,                 /* List of users or groups */
+               *type;                  /* Allow/deny type */
+  int          num_users;              /* Number of users */
+  char         *ptr,                   /* Pointer into users string */
+               *end,                   /* Pointer to end of users string */
+               quote;                  /* Quote character */
+  ipp_attribute_t *attr;               /* Attribute */
+  static const char * const attrs[] =  /* Requested attributes */
+               {
+                 "requesting-user-name-allowed",
+                 "requesting-user-name-denied"
+               };
 
- /*
-  * Get form data...
-  */
 
-  username     = cgiGetVariable("USERNAME");
-  password     = cgiGetVariable("PASSWORD");
-  export_all   = cgiGetVariable("EXPORT_ALL");
-  export_count = cgiGetSize("EXPORT_NAME");
+  is_class = cgiGetVariable("IS_CLASS");
+  printer  = cgiGetVariable("PRINTER_NAME");
 
- /*
-  * Get list of available printers...
-  */
+  if (!printer)
+  {
+    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
+    cgiStartHTML(cgiText(_("Set Allowed Users")));
+    cgiCopyTemplateLang("error.tmpl");
+    cgiEndHTML();
+    return;
+  }
 
-  cgiSetSize("PRINTER_NAME", 0);
-  cgiSetSize("PRINTER_EXPORT", 0);
+  users = cgiGetVariable("users");
+  type  = cgiGetVariable("type");
 
-  request = ippNewRequest(CUPS_GET_PRINTERS);
+  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
+    */
 
-  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
-                "printer-type", 0);
+    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
 
-  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
-                "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE |
-                                    CUPS_PRINTER_IMPLICIT);
+    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);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-               "requested-attributes", NULL, "printer-name");
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                  "requested-attributes",
+                 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
 
-  if ((response = cupsDoRequest(http, request, "/")) != NULL)
-  {
-    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
-    ippDelete(response);
+   /*
+    * Do the request and get back a response...
+    */
 
-    if (!export_all)
+    if ((response = cupsDoRequest(http, request, "/")) != NULL)
     {
-      printer_count = cgiGetSize("PRINTER_NAME");
-
-      for (i = 0; i < printer_count; i ++)
-      {
-        dest = cgiGetArray("PRINTER_NAME", i);
-
-        for (j = 0; j < export_count; j ++)
-         if (!strcasecmp(dest, cgiGetArray("EXPORT_NAME", j)))
-            break;
+      cgiSetIPPVars(response, NULL, NULL, NULL, 0);
 
-        cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : "");
-      }
+      ippDelete(response);
     }
-  }
 
- /*
-  * Export or get the printers to export...
-  */
+    cgiStartHTML(cgiText(_("Set Allowed Users")));
 
-  if (username && *username && password && *password &&
-      (export_all || export_count > 0))
+    if (cupsLastError() > IPP_OK_CONFLICT)
+      cgiShowIPPError(_("Unable to get printer attributes:"));
+    else
+      cgiCopyTemplateLang("users.tmpl");
+
+    cgiEndHTML();
+  }
+  else
   {
    /*
-    * Do export...
+    * Save the changes...
     */
 
-    fputs("DEBUG: Export printers...\n", stderr);
-
-    if (export_all)
+    for (num_users = 0, ptr = (char *)users; *ptr; num_users ++)
     {
-      name         = "PRINTER_NAME";
-      export_count = cgiGetSize("PRINTER_NAME");
-    }
-    else
-      name = "EXPORT_NAME";
-
-    for (i = 0; i < export_count; i ++)
-    {
-      dest = cgiGetArray(name, i);
-
-      if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd)))
-        break;
-
-      j = cupsAdminExportSamba(dest, ppd, "localhost", username, password,
-                               stderr);
-
-      unlink(ppd);
-
-      if (!j)
-        break;
-    }
-
-    if (i < export_count)
-      cgiSetVariable("ERROR", cupsLastErrorString());
-    else
-    {
-      cgiStartHTML(cgiText(_("Export Printers to Samba")));
-      cgiCopyTemplateLang("samba-exported.tmpl");
-      cgiEndHTML();
-      return;
-    }
-  }
-  else if (username && !*username)
-    cgiSetVariable("ERROR",
-                   cgiText(_("A Samba username is required to export "
-                            "printer drivers!")));
-  else if (username && (!password || !*password))
-    cgiSetVariable("ERROR",
-                   cgiText(_("A Samba password is required to export "
-                            "printer drivers!")));
-
- /*
-  * Show form...
-  */
+     /*
+      * Skip whitespace and commas...
+      */
 
-  cgiStartHTML(cgiText(_("Export Printers to Samba")));
-  cgiCopyTemplateLang("samba-export.tmpl");
-  cgiEndHTML();
-}
+      while (*ptr == ',' || isspace(*ptr & 255))
+       ptr ++;
 
+      if (*ptr == '\'' || *ptr == '\"')
+      {
+       /*
+       * Scan quoted name...
+       */
 
-/*
- * 'do_menu()' - Show the main menu...
- */
+       quote = *ptr++;
 
-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 */
-  ipp_attribute_t *attr;               /* IPP attribute */
+       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;
+      }
 
- /*
-  * Get the current server settings...
-  */
    /*
+      * Advance to the next name...
+      */
 
-  if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
-  {
-    cgiSetVariable("SETTINGS_MESSAGE",
-                   cgiText(_("Unable to open cupsd.conf file:")));
-    cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
-  }
+      ptr = end;
+    }
 
-  if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("DEBUG_LOGGING", "CHECKED");
+   /*
+    * Build a CUPS-Add-Printer/Class request, which requires the following
+    * attributes:
+    *
+    *    attributes-charset
+    *    attributes-natural-language
+    *    printer-uri
+    *    requesting-user-name-{allowed,denied}
+    */
 
-  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("REMOTE_ADMIN", "CHECKED");
+    request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
 
-  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("REMOTE_ANY", "CHECKED");
+    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 ((val = cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
+    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);
 
-  if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("SHARE_PRINTERS", "CHECKED");
+      for (i = 0, ptr = (char *)users; *ptr; i ++)
+      {
+       /*
+        * Skip whitespace and commas...
+       */
 
-  if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
-                           settings)) != NULL && atoi(val))
-    cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
+        while (*ptr == ',' || isspace(*ptr & 255))
+         ptr ++;
 
-#ifdef HAVE_GSSAPI
-  cgiSetVariable("HAVE_GSSAPI", "1");
+        if (*ptr == '\'' || *ptr == '\"')
+       {
+        /*
+         * Scan quoted name...
+         */
 
-  if ((val = cupsGetOption("DefaultAuthType", num_settings,
-                           settings)) != NULL && !strcasecmp(val, "Negotiate"))
-    cgiSetVariable("KERBEROS", "CHECKED");
-#endif /* HAVE_GSSAPI */
+         quote = *ptr++;
 
-  cupsFreeOptions(num_settings, settings);
+         for (end = ptr; *end; end ++)
+           if (*end == quote)
+             break;
+       }
+       else
+       {
+        /*
+         * Scan space or comma-delimited name...
+         */
 
- /*
-  * Get the list of printers and their devices...
-  */
+          for (end = ptr; *end; end ++)
+           if (isspace(*end & 255) || *end == ',')
+             break;
+        }
 
-  request = ippNewRequest(CUPS_GET_PRINTERS);
+       /*
+        * Terminate the name...
+       */
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-               "requested-attributes", NULL, "device-uri");
+        if (*end)
+          *end++ = '\0';
 
-  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);
+       /*
+        * Add the name...
+       */
 
-  if ((response = cupsDoRequest(http, request, "/")) != NULL)
-  {
-   /*
-    * Got the printer list, now load the devices...
-    */
+        attr->values[i].string.text = strdup(ptr);
 
-    int                i;                      /* Looping var */
-    cups_array_t *printer_devices;     /* Printer devices for local printers */
-    char       *printer_device;        /* Current printer device */
+       /*
+        * Advance to the next name...
+       */
 
+        ptr = end;
+      }
+    }
 
    /*
-    * Allocate an array and copy the device strings...
+    * Do the request and get back a response...
     */
 
-    printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+    ippDelete(cupsDoRequest(http, request, "/admin/"));
 
-    for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI);
-         attr;
-        attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI))
+    if (cupsLastError() > IPP_OK_CONFLICT)
     {
-      cupsArrayAdd(printer_devices, strdup(attr->values[0].string.text));
+      cgiStartHTML(cgiText(_("Set Allowed Users")));
+      cgiShowIPPError(_("Unable to change printer:"));
     }
-
-   /*
-    * Free the printer list and get the device list...
-    */
-
-    ippDelete(response);
-
-    request = ippNewRequest(CUPS_GET_DEVICES);
-
-    if ((response = cupsDoRequest(http, request, "/")) != NULL)
+    else
     {
      /*
-      * Got the device list, let's parse it...
+      * Redirect successful updates back to the printer page...
       */
 
-      const char *device_uri,          /* device-uri attribute value */
-               *device_make_and_model, /* device-make-and-model value */
-               *device_info;           /* device-info value */
+      char     url[1024],              /* Printer/class URL */
+               refresh[1024];          /* Refresh URL */
 
 
-      for (i = 0, attr = response->attrs; attr; attr = attr->next)
-      {
-       /*
-        * Skip leading attributes until we hit a device...
-       */
+      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);
 
-       while (attr && attr->group_tag != IPP_TAG_PRINTER)
-          attr = attr->next;
+      cgiStartHTML(cgiText(_("Set Allowed Users")));
 
-       if (!attr)
-          break;
+      cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
+                                     "printer-modified.tmpl");
+    }
 
-       /*
-       * Pull the needed attributes from this device...
-       */
+    cgiEndHTML();
+  }
+}
 
-       device_info           = NULL;
-       device_make_and_model = NULL;
-       device_uri            = NULL;
-
-       while (attr && attr->group_tag == IPP_TAG_PRINTER)
-       {
-          if (!strcmp(attr->name, "device-info") &&
-             attr->value_tag == IPP_TAG_TEXT)
-           device_info = attr->values[0].string.text;
 
-          if (!strcmp(attr->name, "device-make-and-model") &&
-             attr->value_tag == IPP_TAG_TEXT)
-           device_make_and_model = attr->values[0].string.text;
-
-          if (!strcmp(attr->name, "device-uri") &&
-             attr->value_tag == IPP_TAG_URI)
-           device_uri = attr->values[0].string.text;
-
-          attr = attr->next;
-       }
-
-       /*
-       * See if we have everything needed...
-       */
-
-       if (device_info && device_make_and_model && device_uri &&
-           strcasecmp(device_make_and_model, "unknown") &&
-           strchr(device_uri, ':'))
-       {
-        /*
-         * Yes, now see if there is already a printer for this
-         * device...
-         */
+/*
+ * 'do_set_options()' - Configure the default options for a queue.
+ */
 
-          if (!cupsArrayFind(printer_devices, (void *)device_uri))
-          {
-          /*
-           * Not found, so it must be a new printer...
-           */
+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 */
 
-            char       options[1024],  /* Form variables for this device */
-                       *options_ptr;   /* Pointer into string */
-           const char  *ptr;           /* Pointer into device string */
 
+  title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options"));
 
-           /*
-           * Format the printer name variable for this device...
-           *
-           * We use the device-info string first, then device-uri,
-           * and finally device-make-and-model to come up with a
-           * suitable name.
-           */
+  fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http,
+          is_class);
 
-           strcpy(options, "TEMPLATE_NAME=");
-           options_ptr = options + strlen(options);
+ /*
+  * Get the printer name...
+  */
 
-            if (strncasecmp(device_info, "unknown", 7))
-             ptr = device_info;
-            else if ((ptr = strstr(device_uri, "://")) != NULL)
-             ptr += 3;
-           else
-             ptr = device_make_and_model;
+  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;
+  }
 
-           for (;
-                options_ptr < (options + sizeof(options) - 1) && *ptr;
-                ptr ++)
-             if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' ||
-                 *ptr == '.')
-               *options_ptr++ = *ptr;
-             else if ((*ptr == ' ' || *ptr == '/') && options_ptr[-1] != '_')
-               *options_ptr++ = '_';
-             else if (*ptr == '?' || *ptr == '(')
-               break;
+  fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
 
-           /*
-           * Then add the make and model in the printer info, so
-           * that MacOS clients see something reasonable...
-           */
+ /*
+  * Get the PPD file...
+  */
 
-            strlcpy(options_ptr, "&PRINTER_LOCATION=Local+Printer"
-                                "&PRINTER_INFO=",
-                   sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
+  if (is_class)
+    filename = NULL;
+  else
+    filename = cupsGetPPD2(http, printer);
 
-            cgiFormEncode(options_ptr, device_make_and_model,
-                         sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
+  if (filename)
+  {
+    fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
 
-           /*
-           * Then copy the device URI...
-           */
+    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;
+  }
 
-           strlcpy(options_ptr, "&DEVICE_URI=",
-                   sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
+  if (cgiGetVariable("job_sheets_start") != NULL ||
+      cgiGetVariable("job_sheets_end") != NULL)
+    have_options = 1;
+  else
+    have_options = 0;
 
-            cgiFormEncode(options_ptr, device_uri,
-                         sizeof(options) - (options_ptr - options));
-           options_ptr += strlen(options_ptr);
+  if (ppd)
+  {
+    ppdMarkDefaults(ppd);
 
-            if (options_ptr < (options + sizeof(options) - 1))
-           {
-             *options_ptr++ = '|';
-             cgiFormEncode(options_ptr, device_make_and_model,
-                         sizeof(options) - (options_ptr - options));
-           }
+    DEBUG_printf(("<P>ppd->num_groups = %d\n"
+                 "<UL>\n", ppd->num_groups));
 
-           /*
-           * Finally, set the form variables for this printer...
-           */
+    for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+    {
+      DEBUG_printf(("<LI>%s<UL>\n", group->text));
 
-           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 ++;
-         }
+      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 */
 
-        if (!attr)
-         break;
-      }
-
-      ippDelete(response);
-
-     /*
-      * Free the device list...
-      */
-
-      for (printer_device = (char *)cupsArrayFirst(printer_devices);
-           printer_device;
-          printer_device = (char *)cupsArrayNext(printer_devices))
-        free(printer_device);
-
-      cupsArrayDelete(printer_devices);
+      DEBUG_puts("</UL></LI>");
     }
-  }
-
- /*
-  * See if Samba and the Windows drivers are installed...
-  */
 
-  if ((datadir = getenv("CUPS_DATADIR")) == NULL)
-    datadir = CUPS_DATADIR;
+    DEBUG_printf(("</UL>\n"
+                 "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd)));
+  }
 
-  snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir);
-  if (!access(filename, R_OK))
+  if (!have_options || ppdConflicts(ppd))
   {
    /*
-    * Found Windows 2000 driver file, see if we have smbclient and
-    * rpcclient...
+    * Show the options to the user...
     */
 
-    if (cupsFileFind("smbclient", getenv("PATH"), 1, filename,
-                     sizeof(filename)) &&
-        cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
-                    sizeof(filename)))
-      cgiSetVariable("HAVE_SAMBA", "Y");
-    else
-    {
-      if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename,
-                        sizeof(filename)))
-        fputs("ERROR: smbclient not found!\n", stderr);
+    fputs("DEBUG: Showing options...\n", stderr);
 
-      if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
-                        sizeof(filename)))
-        fputs("ERROR: rpcclient not found!\n", stderr);
-    }
-  }
-  else
-    perror(filename);
+    cgiStartHTML(cgiText(_("Set Printer Options")));
+    cgiCopyTemplateLang("set-printer-options-header.tmpl");
 
- /*
-  * Subscriptions...
-  */
+    if (ppd)
+    {
+      ppdLocalize(ppd);
 
-  request = ippNewRequest(IPP_GET_SUBSCRIPTIONS);
+      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 ++;
+           }
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, "ipp://localhost/");
+       cgiCopyTemplateLang("option-conflict.tmpl");
+      }
 
-  if ((response = cupsDoRequest(http, request, "/")) != NULL)
-  {
-    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
-    ippDelete(response);
-  }
+      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);
 
- /*
-  * Finally, show the main menu template...
-  */
+       cgiCopyTemplateLang("option-header.tmpl");
+       
+       for (j = group->num_options, option = group->options;
+            j > 0;
+            j --, option ++)
+       {
+         if (!strcmp(option->keyword, "PageRegion"))
+           continue;
 
-  cgiStartHTML(cgiText(_("Administration")));
+         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...
+           */
 
-  cgiCopyTemplateLang("admin.tmpl");
+           if (!strcmp(option->choices[k].choice, "Custom"))
+             continue;
 
-  cgiEndHTML();
-}
+           cgiSetArray("CHOICES", m, option->choices[k].choice);
+           cgiSetArray("TEXT", m, option->choices[k].text);
 
+           m ++;
 
-/*
- * 'do_printer_op()' - Do a printer operation.
- */
+           if (option->choices[k].marked)
+             cgiSetVariable("DEFCHOICE", option->choices[k].choice);
+         }
 
-static void
-do_printer_op(http_t      *http,       /* I - HTTP connection */
-             ipp_op_t    op,           /* I - Operation to perform */
-             const char  *title)       /* I - Title of page */
-{
-  ipp_t                *request;               /* IPP request */
-  char         uri[HTTP_MAX_URI];      /* Printer URI */
-  const char   *printer,               /* Printer name (purge-jobs) */
-               *is_class;              /* Is a class? */
+         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");
+      }
+    }
 
-  is_class = cgiGetVariable("IS_CLASS");
-  printer  = cgiGetVariable("PRINTER_NAME");
+   /*
+    * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+    * following attributes:
+    *
+    *    attributes-charset
+    *    attributes-natural-language
+    *    printer-uri
+    */
 
-  if (!printer)
-  {
-    cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
-    cgiStartHTML(title);
-    cgiCopyTemplateLang("error.tmpl");
-    cgiEndHTML();
-    return;
-  }
+    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
 
- /*
-  * Build a printer request, which requires the following
-  * attributes:
-  *
-  *    attributes-charset
-  *    attributes-natural-language
-  *    printer-uri
-  */
+    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);
 
-  request = ippNewRequest(op);
+   /*
+    * Do the request and get back a response...
+    */
 
-  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 ((response = cupsDoRequest(http, request, "/")) != NULL)
+    {
+      if ((attr = ippFindAttribute(response, "job-sheets-supported",
+                                   IPP_TAG_ZERO)) != NULL)
+      {
+       /*
+       * Add the job sheets options...
+       */
 
- /*
-  * Do the request and get back a response...
-  */
+       cgiSetVariable("GROUP", cgiText(_("Banners")));
+       cgiCopyTemplateLang("option-header.tmpl");
 
-  ippDelete(cupsDoRequest(http, request, "/admin/"));
+       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);
+       }
 
-  if (cupsLastError() > IPP_OK_CONFLICT)
-  {
-    cgiStartHTML(title);
-    cgiShowIPPError(_("Unable to change printer:"));
-  }
-  else
-  {
-   /*
-    * Redirect successful updates back to the printer page...
-    */
+        attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
 
-    char       url[1024],              /* Printer/class URL */
-               refresh[1024];          /* Refresh URL */
+        cgiSetVariable("KEYWORD", "job_sheets_start");
+       cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner")));
+        cgiSetVariable("DEFCHOICE", attr == NULL ?
+                                   "" : attr->values[0].string.text);
 
+       cgiCopyTemplateLang("option-pickone.tmpl");
 
-    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);
+        cgiSetVariable("KEYWORD", "job_sheets_end");
+       cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner")));
+        cgiSetVariable("DEFCHOICE", attr == NULL && attr->num_values > 1 ?
+                                   "" : attr->values[1].string.text);
 
-    cgiStartHTML(title);
+       cgiCopyTemplateLang("option-pickone.tmpl");
 
-    if (op == IPP_PAUSE_PRINTER)
-      cgiCopyTemplateLang("printer-stop.tmpl");
-    else if (op == IPP_RESUME_PRINTER)
-      cgiCopyTemplateLang("printer-start.tmpl");
-    else if (op == CUPS_ACCEPT_JOBS)
-      cgiCopyTemplateLang("printer-accept.tmpl");
-    else if (op == CUPS_REJECT_JOBS)
-      cgiCopyTemplateLang("printer-reject.tmpl");
-    else if (op == IPP_PURGE_JOBS)
-      cgiCopyTemplateLang("printer-purge.tmpl");
-    else if (op == CUPS_SET_DEFAULT)
-      cgiCopyTemplateLang("printer-default.tmpl");
-  }
+       cgiCopyTemplateLang("option-trailer.tmpl");
+      }
 
-  cgiEndHTML();
-}
+      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");
 
-/*
* 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
- */
+       /*
       * Error policy...
      */
 
-static void
-do_set_allowed_users(http_t *http)     /* I - HTTP connection */
-{
-  int          i;                      /* Looping var */
-  ipp_t                *request,               /* IPP request */
-               *response;              /* IPP response */
-  char         uri[HTTP_MAX_URI];      /* Printer URI */
-  const char   *printer,               /* Printer name (purge-jobs) */
-               *is_class,              /* Is a class? */
-               *users,                 /* List of users or groups */
-               *type;                  /* Allow/deny type */
-  int          num_users;              /* Number of users */
-  char         *ptr,                   /* Pointer into users string */
-               *end,                   /* Pointer to end of users string */
-               quote;                  /* Quote character */
-  ipp_attribute_t *attr;               /* Attribute */
-  static const char * const attrs[] =  /* Requested attributes */
-               {
-                 "requesting-user-name-allowed",
-                 "requesting-user-name-denied"
-               };
+        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);
 
-  is_class = cgiGetVariable("IS_CLASS");
-  printer  = cgiGetVariable("PRINTER_NAME");
+          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)
     {
-      cgiStartHTML(cgiText(_("Set Allowed Users")));
-      cgiShowIPPError(_("Unable to change printer:"));
+      cgiStartHTML(title);
+      cgiShowIPPError(_("Unable to set options:"));
     }
     else
     {
@@ -3092,24 +3147,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;URL=/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);
 }
 
 
@@ -3251,5 +3309,5 @@ match_string(const char *a,               /* I - First string */
 
     
 /*
- * End of "$Id: admin.c 6378 2007-03-21 07:18:18Z mike $".
+ * End of "$Id: admin.c 6440 2007-04-03 23:17:17Z mike $".
  */
index 2bd6caf5fc389e2babe9a6a72d6dd1a4150d2759..a58f57906768afffc3e7a138e2eda01be724ec17 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: adminutil.c 6378 2007-03-21 07:18:18Z mike $"
+ * "$Id: adminutil.c 6435 2007-04-02 22:43:39Z mike $"
  *
  *   Administration utility API definitions for the Common UNIX Printing
  *   System (CUPS).
@@ -108,6 +108,7 @@ cupsAdminCreateWindowsPPD(
                        option[41],     /* Option */
                        choice[41];     /* Choice */
   int                  jcloption,      /* In a JCL option? */
+                       jclorder,       /* Next JCL order dependency */
                        linenum;        /* Current line number */
   time_t               curtime;        /* Current time */
   struct tm            *curdate;       /* Current date */
@@ -205,6 +206,7 @@ cupsAdminCreateWindowsPPD(
   */
 
   jcloption = 0;
+  jclorder  = 0;
   linenum   = 0;
   language  = cupsLangDefault();
 
@@ -243,10 +245,23 @@ cupsAdminCreateWindowsPPD(
       jcloption = 0;
       cupsFilePrintf(dstfp, "%s\n", line);
     }
+    else if (jcloption && !strncmp(line, "*OrderDependency:", 17))
+    {
+      for (ptr = line + 17; *ptr && isspace(*ptr & 255); ptr ++);
+
+      ptr = strchr(ptr, ' ');
+
+      if (ptr)
+      {
+       cupsFilePrintf(dstfp, "*OrderDependency: %d%s\n", jclorder, ptr);
+       jclorder ++;
+      }
+      else
+        cupsFilePrintf(dstfp, "%s\n", line);
+    }
     else if (jcloption &&
              strncmp(line, "*End", 4) &&
-             strncmp(line, "*Default", 8) &&
-             strncmp(line, "*OrderDependency", 16))
+             strncmp(line, "*Default", 8))
     {
       if ((ptr = strchr(line, ':')) == NULL)
       {
@@ -363,24 +378,24 @@ cupsAdminCreateWindowsPPD(
                                   IPP_TAG_ZERO)) != NULL &&
       (suppattr = ippFindAttribute(response, "job-hold-until-supported",
                                    IPP_TAG_ZERO)) != NULL)
-    write_option(dstfp, 10, "cupsJobHoldUntil", "Hold Until", "job-hold-until",
-                 suppattr, defattr, 0, 1);
+    write_option(dstfp, jclorder ++, "cupsJobHoldUntil", "Hold Until",
+                 "job-hold-until", suppattr, defattr, 0, 1);
 
   if ((defattr = ippFindAttribute(response, "job-priority-default",
                                   IPP_TAG_INTEGER)) != NULL &&
       (suppattr = ippFindAttribute(response, "job-priority-supported",
                                    IPP_TAG_RANGE)) != NULL)
-    write_option(dstfp, 11, "cupsJobPriority", "Priority", "job-priority",
-                 suppattr, defattr, 0, 1);
+    write_option(dstfp, jclorder ++, "cupsJobPriority", "Priority",
+                 "job-priority", suppattr, defattr, 0, 1);
 
   if ((defattr = ippFindAttribute(response, "job-sheets-default",
                                   IPP_TAG_ZERO)) != NULL &&
       (suppattr = ippFindAttribute(response, "job-sheets-supported",
                                    IPP_TAG_ZERO)) != NULL)
   {
-    write_option(dstfp, 20, "cupsJobSheetsStart", "Start Banner",
+    write_option(dstfp, jclorder ++, "cupsJobSheetsStart", "Start Banner",
                  "job-sheets", suppattr, defattr, 0, 2);
-    write_option(dstfp, 21, "cupsJobSheetsEnd", "End Banner",
+    write_option(dstfp, jclorder ++, "cupsJobSheetsEnd", "End Banner",
                  "job-sheets", suppattr, defattr, 1, 2);
   }
 
@@ -2194,5 +2209,5 @@ write_option(cups_file_t     *dstfp,      /* I - PPD file */
 
 
 /*
- * End of "$Id: adminutil.c 6378 2007-03-21 07:18:18Z mike $".
+ * End of "$Id: adminutil.c 6435 2007-04-02 22:43:39Z mike $".
  */
index 6a6c665ab67712f4d76471e5a038ef5e9cb48eea..c25e57ce24729c9a8a8e3cb15e20b8e0bf3e71c2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: ppd.c 6187 2007-01-10 16:20:42Z mike $"
+ * "$Id: ppd.c 6445 2007-04-04 23:43:26Z mike $"
  *
  *   PPD file routines for the Common UNIX Printing System (CUPS).
  *
@@ -1003,7 +1003,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       for (i = 0, sptr = string; i < 4; i ++)
         ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
     }
-    else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True"))
+    else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
     {
       DEBUG_puts("Processing Custom option...");
 
@@ -1076,6 +1076,34 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
        */
 
        ppd_add_size(ppd, "Custom");
+
+       if ((option = ppdFindOption(ppd, "PageRegion")) == NULL)
+       {
+         ppd_group_t   *gtemp;         /* Temporary group */
+
+         if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg,
+                                    encoding)) == NULL)
+         {
+           DEBUG_puts("Unable to get general group!");
+
+           goto error;
+         }
+
+         option = ppd_get_option(gtemp, "PageRegion");
+        }
+
+       if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+       {
+         DEBUG_puts("Unable to add Custom choice!");
+
+         cg->ppd_status = PPD_ALLOC_ERROR;
+
+         goto error;
+       }
+
+       strlcpy(choice->text, text[0] ? text : _("Custom"),
+               sizeof(choice->text));
+        option = NULL;
       }
     }
     else if (!strcmp(keyword, "LandscapeOrientation"))
@@ -1523,10 +1551,10 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
              !strcmp(keyword, "NonUIConstraints"))
     {
       if (ppd->num_consts == 0)
-       constraint = calloc(1, sizeof(ppd_const_t));
+       constraint = calloc(2, sizeof(ppd_const_t));
       else
        constraint = realloc(ppd->consts,
-                            (ppd->num_consts + 1) * sizeof(ppd_const_t));
+                            (ppd->num_consts + 2) * sizeof(ppd_const_t));
 
       if (constraint == NULL)
       {
@@ -1687,6 +1715,34 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
            break;
       }
 
+     /*
+      * For CustomPageSize and InputSlot/ManualFeed, create a duplicate
+      * constraint for PageRegion...
+      */
+
+      if (!strcasecmp(constraint->option1, "CustomPageSize") &&
+          (!strcasecmp(constraint->option2, "InputSlot") ||
+          !strcasecmp(constraint->option2, "ManualFeed")))
+      {
+        ppd->num_consts ++;
+
+        strcpy(constraint[1].option1, "PageRegion");
+       strcpy(constraint[1].choice1, "Custom");
+       strcpy(constraint[1].option2, constraint->option2);
+       strcpy(constraint[1].choice2, constraint->choice2);
+      }
+      else if (!strcasecmp(constraint->option2, "CustomPageSize") &&
+               (!strcasecmp(constraint->option1, "InputSlot") ||
+               !strcasecmp(constraint->option1, "ManualFeed")))
+      {
+        ppd->num_consts ++;
+
+       strcpy(constraint[1].option1, constraint->option1);
+       strcpy(constraint[1].choice1, constraint->choice1);
+        strcpy(constraint[1].option2, "PageRegion");
+       strcpy(constraint[1].choice2, "Custom");
+      }
+
      /*
       * Handle CustomFoo option constraints...
       */
@@ -3046,5 +3102,5 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
 
 
 /*
- * End of "$Id: ppd.c 6187 2007-01-10 16:20:42Z mike $".
+ * End of "$Id: ppd.c 6445 2007-04-04 23:43:26Z mike $".
  */
index 8e07d06200eb3b810414111742de3434eb89e7da..997b479a3350553eebeb8d497c1fbf749caf1709 100644 (file)
@@ -1078,6 +1078,41 @@ default error log file is <VAR>/var/log/cups/error_log</VAR>.</P>
 information to the system log instead of a plain file.</P>
 
 
+<H2 CLASS="title"><SPAN CLASS="info">CUPS 1.3</SPAN><A NAME="ErrorPolicy">ErrorPolicy</A></H2>
+
+<H3>Examples</H3>
+
+<PRE CLASS="command">
+ErrorPolicy abort-job
+ErrorPolicy retry-job
+ErrorPolicy stop-printer
+</PRE>
+
+<H3>Description</H3>
+
+<P>The <CODE>ErrorPolicy</CODE> directive defines the default policy that
+is used when a backend is unable to send a print job to the
+printer.</P>
+
+<P>The following values are supported:</P>
+
+<UL>
+
+       <LI><CODE>abort-job</CODE> - Abort the job and proceed
+       with the next job in the queue</LI>
+
+       <LI><CODE>retry-job</CODE> - Retry the job after waiting
+       for N seconds; the <VAR>cupsd.conf</VAR> <A
+       HREF="#JobRetryInterval"><CODE>JobRetryInterval</CODE></A>
+       directive controls the value of N</LI>
+
+       <LI><CODE>stop-printer</CODE> - Stop the printer and keep
+       the job for future printing; this is the default
+       value</LI>
+
+</UL>
+
+
 <H2 CLASS="title"><SPAN CLASS="info">CUPS 1.1.18</SPAN><A NAME="FileDevice">FileDevice</A></H2>
 
 <H3>Examples</H3>
index 8c12d632278188046fa1cd59105686afec28a100..ad463acb690b19e8db277dfb7bce2f8ed91772a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: conf.c 6409 2007-03-28 18:02:59Z mike $"
+ * "$Id: conf.c 6436 2007-04-02 23:24:02Z mike $"
  *
  *   Configuration routines for the Common UNIX Printing System (CUPS).
  *
@@ -116,6 +116,7 @@ static cupsd_var_t  variables[] =
   { "DefaultShared",           &DefaultShared,         CUPSD_VARTYPE_BOOLEAN },
   { "DocumentRoot",            &DocumentRoot,          CUPSD_VARTYPE_STRING },
   { "ErrorLog",                        &ErrorLog,              CUPSD_VARTYPE_STRING },
+  { "ErrorPolicy",             &ErrorPolicy,           CUPSD_VARTYPE_STRING },
   { "FileDevice",              &FileDevice,            CUPSD_VARTYPE_BOOLEAN },
   { "FilterLimit",             &FilterLimit,           CUPSD_VARTYPE_INTEGER },
   { "FilterNice",              &FilterNice,            CUPSD_VARTYPE_INTEGER },
@@ -431,6 +432,8 @@ cupsdReadConfiguration(void)
   cupsdClearString(&BrowseLocalOptions);
   cupsdClearString(&BrowseRemoteOptions);
 
+  cupsdSetString(&ErrorPolicy, "stop-printer");
+
 #ifdef HAVE_LDAP
   cupsdClearString(&BrowseLDAPBindDN);
   cupsdClearString(&BrowseLDAPDN);
@@ -3349,5 +3352,5 @@ read_policy(cups_file_t *fp,              /* I - Configuration file */
 
 
 /*
- * End of "$Id: conf.c 6409 2007-03-28 18:02:59Z mike $".
+ * End of "$Id: conf.c 6436 2007-04-02 23:24:02Z mike $".
  */
index 0a5e073d69ffbb7f114f979f86dbb9dd9691a237..6bb223ecba11fa362c15f581ba009075422bbc8f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: conf.h 6365 2007-03-19 20:56:57Z mike $"
+ * "$Id: conf.h 6436 2007-04-02 23:24:02Z mike $"
  *
  *   Configuration file definitions for the Common UNIX Printing System (CUPS)
  *   scheduler.
@@ -97,6 +97,8 @@ VAR char              *AccessLog              VALUE(NULL),
                                        /* Default charset */
                        *DefaultLocale          VALUE(NULL),
                                        /* Default locale */
+                       *ErrorPolicy            VALUE(NULL),
+                                       /* Default printer-error-policy */
                        *RIPCache               VALUE(NULL),
                                        /* Amount of memory for RIPs */
                        *TempDir                VALUE(NULL),
@@ -233,5 +235,5 @@ extern int  cupsdLogRequest(cupsd_client_t *con, http_status_t code);
 
 
 /*
- * End of "$Id: conf.h 6365 2007-03-19 20:56:57Z mike $".
+ * End of "$Id: conf.h 6436 2007-04-02 23:24:02Z mike $".
  */
index bb0645e45eb377dc1235f6464491900ede7e10aa..5a8555ac4e47ffac05c9c1ea77461c82f1c5e987 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: ipp.c 6397 2007-03-25 23:33:32Z mike $"
+ * "$Id: ipp.c 6433 2007-04-02 21:50:50Z mike $"
  *
  *   IPP routines for the Common UNIX Printing System (CUPS) scheduler.
  *
@@ -4433,6 +4433,16 @@ copy_printer_attrs(
                 printer->recoverable);
 #endif /* __APPLE__ */
 
+  if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
+    ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
+                 "printer-alert", NULL, printer->alert);
+
+  if (printer->alert_description &&
+      (!ra || cupsArrayFind(ra, "printer-alert-description")))
+    ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+                 "printer-alert-description", NULL,
+                printer->alert_description);
+
   if (!ra || cupsArrayFind(ra, "printer-current-time"))
     ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
                ippTimeToDate(curtime));
@@ -4825,6 +4835,8 @@ create_requested_array(ipp_t *request)    /* I - IPP request */
       cupsArrayAdd(ra, "pages-per-minute");
       cupsArrayAdd(ra, "pages-per-minute-color");
       cupsArrayAdd(ra, "pdl-override-supported");
+      cupsArrayAdd(ra, "printer-alert");
+      cupsArrayAdd(ra, "printer-alert-description");
       cupsArrayAdd(ra, "printer-current-time");
       cupsArrayAdd(ra, "printer-driver-installer");
       cupsArrayAdd(ra, "printer-info");
@@ -9473,5 +9485,5 @@ validate_user(cupsd_job_t    *job,        /* I - Job */
 
 
 /*
- * End of "$Id: ipp.c 6397 2007-03-25 23:33:32Z mike $".
+ * End of "$Id: ipp.c 6433 2007-04-02 21:50:50Z mike $".
  */
index caa31807929e9b4413d7cfd191a7a405f11f0571..96513a2f55ea3b9ba318e58e699e16866fa11bb0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: job.c 6424 2007-04-02 13:09:06Z mike $"
+ * "$Id: job.c 6433 2007-04-02 21:50:50Z mike $"
  *
  *   Job management routines for the Common UNIX Printing System (CUPS).
  *
@@ -3458,6 +3458,19 @@ update_job(cupsd_job_t *job)     /* I - Job to check */
                                 attrs)) != NULL)
         cupsdSetAuthInfoRequired(job->printer, attr, NULL);
 
+      if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL)
+      {
+        cupsdSetString(&job->printer->alert, attr);
+       event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+      }
+
+      if ((attr = cupsGetOption("printer-alert-description", num_attrs,
+                                attrs)) != NULL)
+      {
+        cupsdSetString(&job->printer->alert_description, attr);
+       event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+      }
+
       cupsFreeOptions(num_attrs, attrs);
     }
 #ifdef __APPLE__
@@ -3536,5 +3549,5 @@ update_job(cupsd_job_t *job)      /* I - Job to check */
 
 
 /*
- * End of "$Id: job.c 6424 2007-04-02 13:09:06Z mike $".
+ * End of "$Id: job.c 6433 2007-04-02 21:50:50Z mike $".
  */
index 831d6008db033f55cdda29d298bc544c6bbf4813..1934383ec2ac16cbc9402d77db0224f8ec4099df 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: printers.c 6429 2007-04-02 13:44:52Z mike $"
+ * "$Id: printers.c 6436 2007-04-02 23:24:02Z mike $"
  *
  *   Printer routines for the Common UNIX Printing System (CUPS).
  *
@@ -124,7 +124,7 @@ cupsdAddPrinter(const char *name)   /* I - Name of printer */
   cupsdSetString(&p->job_sheets[0], "none");
   cupsdSetString(&p->job_sheets[1], "none");
 
-  cupsdSetString(&p->error_policy, "stop-printer");
+  cupsdSetString(&p->error_policy, ErrorPolicy);
   cupsdSetString(&p->op_policy, DefaultPolicy);
 
   p->op_policy_ptr = DefaultPolicyPtr;
@@ -747,6 +747,9 @@ cupsdDeletePrinter(
   cupsdClearString(&p->op_policy);
   cupsdClearString(&p->error_policy);
 
+  cupsdClearString(&p->alert);
+  cupsdClearString(&p->alert_description);
+
 #ifdef HAVE_DNSSD
   cupsdClearString(&p->product);
   cupsdClearString(&p->pdl);
@@ -3705,5 +3708,5 @@ write_irix_state(cupsd_printer_t *p)      /* I - Printer to update */
 
 
 /*
- * End of "$Id: printers.c 6429 2007-04-02 13:44:52Z mike $".
+ * End of "$Id: printers.c 6436 2007-04-02 23:24:02Z mike $".
  */
index 77782359d23127eea6cb0c5e04339c55d0fcf485..c822ad157b91d2c35dd21ba2d18ee8132d8d85e9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: printers.h 6318 2007-03-06 04:36:55Z mike $"
+ * "$Id: printers.h 6433 2007-04-02 21:50:50Z mike $"
  *
  *   Printer definitions for the Common UNIX Printing System (CUPS) scheduler.
  *
@@ -93,6 +93,9 @@ typedef struct cupsd_printer_s
   cups_option_t        *options;               /* Default options */
   int          num_auth_info_required; /* Number of required auth fields */
   const char   *auth_info_required[4]; /* Required authentication fields */
+  char         *alert,                 /* PSX printer-alert value */
+               *alert_description;     /* PSX printer-alert-description value */
+
 #ifdef __APPLE__
   char         *recoverable;           /* com.apple.print.recoverable-message */
 #endif /* __APPLE__ */
@@ -172,5 +175,5 @@ extern void         cupsdWritePrintcap(void);
 
 
 /*
- * End of "$Id: printers.h 6318 2007-03-06 04:36:55Z mike $".
+ * End of "$Id: printers.h 6433 2007-04-02 21:50:50Z mike $".
  */
index 474ad02b237d17151a6910f73087ed7f552f20b2..ed1becfb5ae2afd553ffa6e2d804f33cb3456085 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: cupstestppd.c 6430 2007-04-02 14:01:55Z mike $"
+ * "$Id: cupstestppd.c 6444 2007-04-04 22:13:58Z mike $"
  *
  *   PPD test program for the Common UNIX Printing System (CUPS).
  *
@@ -42,6 +42,7 @@
 #include <cups/i18n.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 
 
 /*
@@ -84,31 +85,39 @@ int valid_utf8(const char *s);
  * 'main()' - Main entry for test program.
  */
 
-int                            /* O - Exit status */
-main(int  argc,                        /* I - Number of command-line arguments */
-     char *argv[])             /* I - Command-line arguments */
+int                                    /* O - Exit status */
+main(int  argc,                                /* I - Number of command-line args */
+     char *argv[])                     /* I - Command-line arguments */
 {
-  int          i, j, k, m, n;  /* Looping vars */
-  int          len;            /* Length of option name */
-  char         *opt;           /* Option character */
-  const char   *ptr;           /* Pointer into string */
-  int          files;          /* Number of files */
-  int          verbose;        /* Want verbose output? */
-  int          status;         /* Exit status */
-  int          errors;         /* Number of conformance errors */
-  int          ppdversion;     /* PPD spec version in PPD file */
-  ppd_status_t error;          /* Status of ppdOpen*() */
-  int          line;           /* Line number for error */
-  int          xdpi,           /* X resolution */
-               ydpi;           /* Y resolution */
-  ppd_file_t   *ppd;           /* PPD file record */
-  ppd_attr_t   *attr;          /* PPD attribute */
-  ppd_size_t   *size;          /* Size record */
-  ppd_group_t  *group;         /* UI group */
-  ppd_option_t *option;        /* Standard UI option */
-  ppd_group_t  *group2;        /* UI group */
-  ppd_option_t *option2;       /* Standard UI option */
-  ppd_choice_t *choice;        /* Standard UI option choice */
+  int          i, j, k, m, n;          /* Looping vars */
+  int          len;                    /* Length of option name */
+  char         *opt;                   /* Option character */
+  const char   *ptr;                   /* Pointer into string */
+  int          files;                  /* Number of files */
+  int          verbose;                /* Want verbose output? */
+  int          status;                 /* Exit status */
+  int          errors;                 /* Number of conformance errors */
+  int          ppdversion;             /* PPD spec version in PPD file */
+  ppd_status_t error;                  /* Status of ppdOpen*() */
+  int          line;                   /* Line number for error */
+  struct stat  statbuf;                /* File information */
+  char         super[16],              /* Super-type for filter */
+               type[256],              /* Type for filter */
+               program[256],           /* Program/filter name */
+               pathprog[1024],         /* Complete path to program/filter */
+               *root;                  /* Root directory */
+  int          cost;                   /* Cost of filter */
+  int          xdpi,                   /* X resolution */
+               ydpi;                   /* Y resolution */
+  ppd_file_t   *ppd;                   /* PPD file record */
+  ppd_attr_t   *attr;                  /* PPD attribute */
+  ppd_size_t   *size;                  /* Size record */
+  ppd_group_t  *group;                 /* UI group */
+  ppd_option_t *option;                /* Standard UI option */
+  ppd_group_t  *group2;                /* UI group */
+  ppd_option_t *option2;               /* Standard UI option */
+  ppd_choice_t *choice;                /* Standard UI option choice */
+  ppd_const_t  *c;                     /* Current constraint */
   static char  *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
   static char  *sections[] = { "ANY", "DOCUMENT", "EXIT",
                                 "JCL", "PAGE", "PROLOG" };
@@ -126,6 +135,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
   ppd     = NULL;
   files   = 0;
   status  = ERROR_NONE;
+  root    = "";
 
   for (i = 1; i < argc; i ++)
     if (argv[i][0] == '-' && argv[i][1])
@@ -133,6 +143,15 @@ main(int  argc,                    /* I - Number of command-line arguments */
       for (opt = argv[i] + 1; *opt; opt ++)
         switch (*opt)
        {
+         case 'R' :                    /* Alternate root directory */
+             i ++;
+
+             if (i >= argc)
+               usage();
+
+              root = argv[i];
+             break;
+
          case 'q' :                    /* Quiet mode */
              if (verbose > 0)
              {
@@ -318,6 +337,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
            !strcmp(attr->name, "DefaultImageableArea") ||
            !strcmp(attr->name, "DefaultOutputOrder") ||
            !strcmp(attr->name, "DefaultPaperDimension") ||
+           !strcmp(attr->name, "DefaultResolution") ||
            !strcmp(attr->name, "DefaultTransfer"))
          continue;
 
@@ -1313,12 +1333,59 @@ main(int  argc,                 /* I - Number of command-line arguments */
            attr;
           attr = ppdFindNextAttr(ppd, "cupsFilter", NULL))
       {
-        char   super[16],              /* Filter super type */
-               type[256],              /* Filter type */
-               program[256];           /* Filter program */
-       int     cost;                   /* Filter cost */
+        if (!attr->value ||
+           sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
+                  program) != 4)
+        {
+         if (verbose >= 0)
+         {
+           if (!errors && !verbose)
+             _cupsLangPuts(stdout, _(" FAIL\n"));
+
+           _cupsLangPrintf(stdout,
+                           _("      **FAIL**  Bad cupsFilter value \"%s\"!\n"),
+                           attr->value ? attr->value : "");
+          }
 
+         errors ++;
+       }
+       else
+       {
+         if (program[0] == '/')
+           snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
+         else
+         {
+           if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
+             ptr = CUPS_SERVERBIN;
+
+            if (*ptr == '/' || !*root)
+             snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
+                      program);
+            else
+             snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
+                      program);
+          }
 
+         if (stat(pathprog, &statbuf))
+         {
+           if (verbose >= 0)
+           {
+             if (!errors && !verbose)
+               _cupsLangPuts(stdout, _(" FAIL\n"));
+
+             _cupsLangPrintf(stdout, _("      **FAIL**  Missing cupsFilter "
+                                       "file \"%s\"\n"), program);
+           }
+
+           errors ++;
+         }
+       }
+      }
+
+      for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
+           attr;
+          attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
+      {
         if (!attr->value ||
            sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
                   program) != 4)
@@ -1329,12 +1396,43 @@ main(int  argc,                 /* I - Number of command-line arguments */
              _cupsLangPuts(stdout, _(" FAIL\n"));
 
            _cupsLangPrintf(stdout,
-                           _("      **FAIL**  Bad cupsFilter value \"%s\"!\n"),
+                           _("      **FAIL**  Bad cupsPreFilter value \"%s\"!\n"),
                            attr->value ? attr->value : "");
           }
 
          errors ++;
        }
+       else
+       {
+         if (program[0] == '/')
+           snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
+         else
+         {
+           if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
+             ptr = CUPS_SERVERBIN;
+
+            if (*ptr == '/' || !*root)
+             snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
+                      program);
+            else
+             snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
+                      program);
+          }
+
+         if (stat(pathprog, &statbuf))
+         {
+           if (verbose >= 0)
+           {
+             if (!errors && !verbose)
+               _cupsLangPuts(stdout, _(" FAIL\n"));
+
+             _cupsLangPrintf(stdout, _("      **FAIL**  Missing cupsPreFilter "
+                                       "file \"%s\"\n"), program);
+           }
+
+           errors ++;
+         }
+       }
       }
 
       if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
@@ -1354,6 +1452,66 @@ main(int  argc,                  /* I - Number of command-line arguments */
        errors ++;
       }
 
+     /*
+      * Check for bad UIConstraints...
+      */
+
+      for (j = ppd->num_consts, c = ppd->consts; j > 0; j --, c ++)
+      {
+       option  = ppdFindOption(ppd, c->option1);
+       option2 = ppdFindOption(ppd, c->option2);
+
+       if (!option || !option2)
+       {
+         if (!errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+          if (!option)
+           _cupsLangPrintf(stdout,
+                           _("      **FAIL**  Missing option %s in "
+                             "UIConstraint \"*%s %s *%s %s\"!\n"),
+                           c->option1,
+                           c->option1, c->choice1, c->option2, c->choice2);
+         
+          if (!option2)
+           _cupsLangPrintf(stdout,
+                           _("      **FAIL**  Missing option %s in "
+                             "UIConstraint \"*%s %s *%s %s\"!\n"),
+                           c->option2,
+                           c->option1, c->choice1, c->option2, c->choice2);
+
+          continue;
+       }
+
+       if (c->choice1[0] && !ppdFindChoice(option, c->choice1))
+       {
+         if (!errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         _cupsLangPrintf(stdout,
+                         _("      **FAIL**  Missing choice *%s %s in "
+                           "UIConstraint \"*%s %s *%s %s\"!\n"),
+                         c->option1, c->choice1,
+                         c->option1, c->choice1, c->option2, c->choice2);
+       }
+
+       if (c->choice2[0] && !ppdFindChoice(option2, c->choice2))
+       {
+         if (!errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         _cupsLangPrintf(stdout,
+                         _("      **FAIL**  Missing choice *%s %s in "
+                           "UIConstraint \"*%s %s *%s %s\"!\n"),
+                         c->option2, c->choice2,
+                         c->option1, c->choice1, c->option2, c->choice2);
+       }
+      }
+
+     /*
+      * Final pass/fail notification...
+      */
+
       if (errors)
        status = ERROR_CONFORMANCE;
       else if (!verbose)
@@ -1517,6 +1675,73 @@ main(int  argc,                  /* I - Number of command-line arguments */
          }
       }
 
+     /*
+      * cupsICCProfile
+      */
+
+      for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); 
+          attr != NULL; 
+          attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+      {
+       if (attr->value)
+       {
+         if (attr->value[0] == '/')
+           snprintf(pathprog, sizeof(pathprog), "%s%s", root, attr->value);
+         else
+         {
+           if ((ptr = getenv("CUPS_DATADIR")) == NULL)
+             ptr = CUPS_DATADIR;
+
+            if (*ptr == '/' || !*root)
+             snprintf(pathprog, sizeof(pathprog), "%s%s/profiles/%s", root,
+                      ptr, attr->value);
+            else
+             snprintf(pathprog, sizeof(pathprog), "%s/%s/profiles/%s", root,
+                      ptr, attr->value);
+          }
+       }
+
+       if (!attr->value || !attr->value[0] || stat(pathprog, &statbuf))
+       {
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("        WARN    Missing cupsICCProfile "
+                             "file \"%s\"\n"),
+                           !attr->value || !attr->value[0] ? "<NULL>" :
+                                                             attr->value);
+       }
+      }
+
+#ifdef __APPLE__
+     /*
+      * APDialogExtension
+      */
+
+      for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL); 
+          attr != NULL; 
+          attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
+      {
+       if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
+         _cupsLangPrintf(stdout, _("        WARN    Missing "
+                                   "APDialogExtension file \"%s\"\n"),
+                         attr->value ? attr->value : "<NULL>");
+      }
+
+     /*
+      * APPrinterIconPath
+      */
+
+      for (attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL); 
+          attr != NULL; 
+          attr = ppdFindNextAttr(ppd, "APPrinterIconPath", NULL))
+      {
+       if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
+         _cupsLangPrintf(stdout, _("        WARN    Missing "
+                                   "APPrinterIconPath file \"%s\"\n"),
+                         attr->value ? attr->value : "<NULL>");
+      }
+#endif /* __APPLE__ */
+
       if (verbose > 0)
       {
         if (errors)
@@ -1882,9 +2107,10 @@ void
 usage(void)
 {
   _cupsLangPuts(stdout,
-                _("Usage: cupstestppd [-q] [-r] [-v[v]] filename1.ppd[.gz] "
-                 "[... filenameN.ppd[.gz]]\n"
-                 "       program | cupstestppd [-q] [-r] [-v[v]] -\n"));
+                _("Usage: cupstestppd [-R root-directory] [-q] [-r] [-v[v]] "
+                 "filename1.ppd[.gz] [... filenameN.ppd[.gz]]\n"
+                 "       program | cupstestppd [-R root-directory] [-q] [-r] "
+                 "[-v[v]] -\n"));
 
   exit(ERROR_USAGE);
 }
@@ -1967,5 +2193,5 @@ valid_utf8(const char *s)         /* I - String to check */
 
 
 /*
- * End of "$Id: cupstestppd.c 6430 2007-04-02 14:01:55Z mike $".
+ * End of "$Id: cupstestppd.c 6444 2007-04-04 22:13:58Z mike $".
  */
index 908e622b3ca9fed50bb424258c9b448268ca5584..01722ab6610f84286ba0273836505dd58711d06a 100644 (file)
@@ -1,9 +1,9 @@
 #
-# "$Id: Makefile 5877 2006-08-24 15:54:12Z mike $"
+# "$Id: Makefile 6440 2007-04-03 23:17:17Z mike $"
 #
 #   Template makefile for the Common UNIX Printing System (CUPS).
 #
-#   Copyright 1993-2006 by Easy Software Products.
+#   Copyright 1993-2007 by Easy Software Products.
 #
 #   These coded instructions, statements, and computer programs are the
 #   property of Easy Software Products and are protected by Federal
@@ -58,6 +58,7 @@ FILES =       \
                job-restart.tmpl \
                jobs.tmpl \
                jobs-header.tmpl \
+               list-available-printers.tmpl \
                maintenance.tmpl \
                modify-class.tmpl \
                modify-printer.tmpl \
@@ -156,5 +157,5 @@ uninstall-languages:
 
 
 #
-# End of "$Id: Makefile 5877 2006-08-24 15:54:12Z mike $".
+# End of "$Id: Makefile 6440 2007-04-03 23:17:17Z mike $".
 #
index 137e26475b80fa5af93f5ecd66654596e06fae7d..91aafc4691fa72dd348c4988fa4981c97f9d31e4 100644 (file)
@@ -6,6 +6,9 @@
 <P>
 <A HREF="/admin?op=add-printer"><IMG
 SRC="/images/button-add-printer.gif" ALT="Add Printer" CLASS="button"></A>
+<A HREF="/admin?op=list-available-printers"><IMG
+SRC="/images/button-list-available-printers.gif"
+ALT="List Available Printers" CLASS="button"></A>
 <A HREF="/printers/"><IMG SRC="/images/button-manage-printers.gif"
 ALT="Manage Printers" CLASS="button"></A>
 {have_samba?<A HREF="/admin/?op=export-samba"><IMG
@@ -13,13 +16,6 @@ SRC="/images/button-export-samba.gif" ALT="Export Printers to Samba"
 CLASS="button"></A>:}
 </P>
 
-{#device_uri=0?:<P><B>New Printers Found:</B></P><UL>{[device_uri]
-<LI><A HREF="/admin?op=add-printer&amp;{device_options}"><IMG
-SRC="/images/button-add-this-printer.gif" ALT="Add This Printer" CLASS="button"
-ALIGN="MIDDLE"></A>
-{device_make_and_model} ({device_info})</LI>
-}</UL>}
-
 <H2 CLASS="title">Classes</H2>
 
 <P>
index bc87f9f58a05a70e83f4e43f4fff654c0633ebea..8e17c3996edd122e5869af9a841ed9d7758bcccc 100644 (file)
@@ -42,6 +42,8 @@
 }
 <A HREF="{admin_uri}?op=modify-class&amp;printer_name={%printer_name}">
 <IMG SRC="/images/button-modify-class.gif" ALT="Modify Class" CLASS="button"></A>
+<A HREF="{admin_uri}?op=set-class-options&amp;printer_name={%printer_name}">
+<IMG SRC="/images/button-set-printer-options.gif" ALT="Set Printer Options" CLASS="button"></A>
 <A HREF="{admin_uri}?op=delete-class&amp;printer_name={%printer_name}">
 <IMG SRC="/images/button-delete-class.gif" ALT="Delete Class" CLASS="button"></A>
 <A HREF="{admin_uri}?op=set-as-default&amp;printer_name={%printer_name}&amp;is_class=Y">
diff --git a/templates/list-available-printers.tmpl b/templates/list-available-printers.tmpl
new file mode 100644 (file)
index 0000000..bd7ff88
--- /dev/null
@@ -0,0 +1,10 @@
+<H2 CLASS="title">Available Printers</H2>
+
+{#device_uri=0?<P>No printers found.</P>
+:<UL>{[device_uri]
+<LI><A HREF="/admin?op=add-printer&amp;{device_options}"><IMG
+SRC="/images/button-add-this-printer.gif" ALT="Add This Printer" CLASS="button"
+ALIGN="MIDDLE"></A>
+{device_make_and_model} ({device_info})</LI>
+}</UL>}
+
index 7ec86879ad90cd304512eb2ff3adf1eff08c7aea..3f6b08b7076620e5fbd9af49474d9574ad58206c 100644 (file)
@@ -1,2 +1,2 @@
-<P>Printer <A HREF="/printers/{printer_name}">{printer_name}</A> has
+<P>{OP=set-class-options?Class <A HREF="/classes/{printer_name}">:Printer <A HREF="/printers/{printer_name}">}{printer_name}</A> has
 been configured successfully.
index 51cc4e40974b297a5312589662a3a2c35fb44626..de5defd2938271b2430cbfffcf0d5bc45557d65c 100644 (file)
@@ -22,6 +22,7 @@ button-manage-printers.gif        standard  Manage Printers
 button-manage-server.gif           standard  Manage Server
 button-modify-class.gif            standard  Modify Class
 button-modify-printer.gif          standard  Modify Printer
+button-list-available-printers.gif  standard  List Available Printers
 button-move-job.gif                standard  Move Job
 button-move-jobs.gif               standard  Move All Jobs
 button-print-self-test-page.gif     standard  Print Self-Test Page