]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - tools/ippeveprinter.c
Also authenticate web IF.
[thirdparty/cups.git] / tools / ippeveprinter.c
index 19b0d5711960363212b1a1ebbdbe927526f5b08a..a1c08790e125e91155a383ba641792ba859031bc 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <cups/cups-private.h>
+#include <cups/debug-private.h>
 #if !CUPS_LITE
 #  include <cups/ppd-private.h>
 #endif /* !CUPS_LITE */
@@ -50,6 +51,7 @@ extern char **environ;
 #  include <avahi-common/error.h>
 #  include <avahi-common/thread-watch.h>
 #endif /* HAVE_DNSSD */
+
 #ifdef HAVE_SYS_MOUNT_H
 #  include <sys/mount.h>
 #endif /* HAVE_SYS_MOUNT_H */
@@ -63,6 +65,14 @@ extern char **environ;
 #  include <sys/vfs.h>
 #endif /* HAVE_SYS_VFS_H */
 
+#if HAVE_LIBPAM
+#  ifdef HAVE_PAM_PAM_APPL_H
+#    include <pam/pam_appl.h>
+#  else
+#    include <security/pam_appl.h>
+#  endif /* HAVE_PAM_PAM_APPL_H */
+#endif /* HAVE_LIBPAM */
+
 #include "printer-png.h"
 
 
@@ -147,6 +157,14 @@ typedef void *ippeve_srv_t;                /* Service reference */
 typedef void *ippeve_txt_t;            /* TXT record */
 #endif /* HAVE_DNSSD */
 
+#if HAVE_LIBPAM
+typedef struct ippeve_authdata_s       /* Authentication data */
+{
+  char username[HTTP_MAX_VALUE],       /* Username string */
+       *password;                      /* Password string */
+} ippeve_authdata_t;
+#endif /* HAVE_LIBPAM */
+
 typedef struct ippeve_filter_s         /**** Attribute filter ****/
 {
   cups_array_t         *ra;            /* Requested attributes */
@@ -171,6 +189,7 @@ typedef struct ippeve_printer_s             /**** Printer data ****/
                        *hostname,      /* Hostname */
                        *uri,           /* printer-uri-supported */
                        *device_uri,    /* Device URI (if any) */
+                       *output_format, /* Output format */
 #if !CUPS_LITE
                        *ppdfile,       /* PPD file (if any) */
 #endif /* !CUPS_LITE */
@@ -222,7 +241,9 @@ typedef struct ippeve_client_s              /**** Client data ****/
   char                 uri[1024],      /* Request URI */
                        *options;       /* URI options */
   http_addr_t          addr;           /* Client address */
-  char                 hostname[256];  /* Client hostname */
+  char                 hostname[256],  /* Client hostname */
+                       username[HTTP_MAX_VALUE];
+                                       /* Authenticated username, if any */
   ippeve_printer_t     *printer;       /* Printer */
   ippeve_job_t         *job;           /* Current job, if any */
 } ippeve_client_t;
@@ -232,6 +253,7 @@ typedef struct ippeve_client_s              /**** Client data ****/
  * Local functions...
  */
 
+static http_status_t   authenticate_request(ippeve_client_t *client);
 static void            clean_jobs(ippeve_printer_t *printer);
 static int             compare_jobs(ippeve_job_t *a, ippeve_job_t *b);
 static void            copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy);
@@ -242,7 +264,7 @@ static int          create_job_file(ippeve_job_t *job, char *fname, size_t fnamesize, co
 static int             create_listener(const char *name, int port, int family);
 static ipp_t           *create_media_col(const char *media, const char *source, const char *type, int width, int length, int bottom, int left, int right, int top);
 static ipp_t           *create_media_size(int width, int length);
-static ippeve_printer_t        *create_printer(const char *servername, int serverport, const char *name, const char *location, const char *icon, cups_array_t *docformats, const char *subtypes, const char *directory, const char *command, const char *device_uri, ipp_t *attrs);
+static ippeve_printer_t        *create_printer(const char *servername, int serverport, const char *name, const char *location, const char *icon, cups_array_t *docformats, const char *subtypes, const char *directory, const char *command, const char *device_uri, const char *output_format, ipp_t *attrs);
 static void            debug_attributes(const char *title, ipp_t *ipp, int response);
 static void            delete_client(ippeve_client_t *client);
 static void            delete_job(ippeve_job_t *job);
@@ -279,6 +301,9 @@ static ipp_t                *load_legacy_attributes(const char *make, const char *model, int p
 #if !CUPS_LITE
 static ipp_t           *load_ppd_attributes(const char *ppdfile, cups_array_t *docformats);
 #endif /* !CUPS_LITE */
+#if HAVE_LIBPAM
+static int             pam_func(int, const struct pam_message **, struct pam_response **, void *);
+#endif /* HAVE_LIBPAM */
 static int             parse_options(ippeve_client_t *client, cups_option_t **options);
 static void            process_attr_message(ippeve_job_t *job, char *message);
 static void            *process_client(ippeve_client_t *client);
@@ -314,6 +339,8 @@ static AvahiClient  *DNSSDClient = NULL;
 static int             KeepFiles = 0,  /* Keep spooled job files? */
                        MaxVersion = 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
                        Verbosity = 0;  /* Verbosity level */
+static const char      *PAMService = NULL;
+                                       /* PAM service */
 
 
 /*
@@ -329,6 +356,7 @@ main(int  argc,                             /* I - Number of command-line args */
                *attrfile = NULL,       /* ippserver attributes file */
                *command = NULL,        /* Command to run with job files */
                *device_uri = NULL,     /* Device URI */
+               *output_format = NULL,  /* Output format */
                *icon = NULL,           /* Icon file */
 #ifdef HAVE_SSL
                *keypath = NULL,        /* Keychain path */
@@ -368,6 +396,14 @@ main(int  argc,                            /* I - Number of command-line args */
     {
       web_forms = 0;
     }
+    else if (!strcmp(argv[i], "--pam-service"))
+    {
+      i ++;
+      if (i >= argc)
+        usage(1);
+
+      PAMService = argv[i];
+    }
     else if (!strcmp(argv[i], "--version"))
     {
       puts(CUPS_SVERSION);
@@ -389,6 +425,11 @@ main(int  argc,                            /* I - Number of command-line args */
              legacy = 1;
              break;
 
+          case 'A' : /* -A (enable authentication) */
+              if (!PAMService)
+                PAMService = "other";
+             break;
+
           case 'D' : /* -D device-uri */
              i ++;
              if (i >= argc)
@@ -397,6 +438,14 @@ main(int  argc,                            /* I - Number of command-line args */
              device_uri = argv[i];
              break;
 
+          case 'F' : /* -F output/format */
+             i ++;
+             if (i >= argc)
+               usage(1);
+
+             output_format = argv[i];
+             break;
+
 #ifdef HAVE_SSL
          case 'K' : /* -K keypath */
              i ++;
@@ -620,10 +669,6 @@ main(int  argc,                            /* I - Number of command-line args */
       _cupsLangPrintf(stderr, _("Using spool directory \"%s\"."), directory);
   }
 
-#ifdef HAVE_SSL
-  cupsSetServerCredentials(keypath, servername, 1);
-#endif /* HAVE_SSL */
-
  /*
   * Initialize DNS-SD...
   */
@@ -646,12 +691,15 @@ main(int  argc,                           /* I - Number of command-line args */
 
     if (!command)
       command = "ippeveps";
+
+    if (!output_format)
+      output_format = "application/postscript";
   }
 #endif /* !CUPS_LITE */
   else
     attrs = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats);
 
-  if ((printer = create_printer(servername, serverport, name, location, icon, docformats, subtypes, directory, command, device_uri, attrs)) == NULL)
+  if ((printer = create_printer(servername, serverport, name, location, icon, docformats, subtypes, directory, command, device_uri, output_format, attrs)) == NULL)
     return (1);
 
   printer->web_forms = web_forms;
@@ -661,6 +709,10 @@ main(int  argc,                            /* I - Number of command-line args */
     printer->ppdfile = strdup(ppdfile);
 #endif /* !CUPS_LITE */
 
+#ifdef HAVE_SSL
+  cupsSetServerCredentials(keypath, printer->hostname, 1);
+#endif /* HAVE_SSL */
+
  /*
   * Run the print service...
   */
@@ -677,6 +729,102 @@ main(int  argc,                           /* I - Number of command-line args */
 }
 
 
+/*
+ * 'authenticate_request()' - Try to authenticate the request.
+ */
+
+static http_status_t                   /* O - HTTP_STATUS_CONTINUE to keep going, otherwise status to return */
+authenticate_request(
+    ippeve_client_t *client)           /* I - Client */
+{
+#if HAVE_LIBPAM
+ /*
+  * If PAM isn't enabled, return 'continue' now...
+  */
+
+  const char           *authorization; /* Pointer into Authorization string */
+  int                  userlen;        /* Username:password length */
+  pam_handle_t         *pamh;          /* PAM authentication handle */
+  int                  pamerr;         /* PAM error code */
+  struct pam_conv      pamdata;        /* PAM conversation data */
+  ippeve_authdata_t    data;           /* Authentication data */
+
+
+  if (!PAMService)
+    return (HTTP_STATUS_CONTINUE);
+
+ /*
+  * Try authenticating using PAM...
+  */
+
+  authorization = httpGetField(client->http, HTTP_FIELD_AUTHORIZATION);
+
+  if (strncmp(authorization, "Basic ", 6))
+  {
+    fputs("Unsupported scheme in Authorization header.\n", stderr);
+    return (HTTP_STATUS_BAD_REQUEST);
+  }
+
+  authorization += 5;
+  while (isspace(*authorization & 255))
+    authorization ++;
+
+  userlen = sizeof(data.username);
+  httpDecode64_2(data.username, &userlen, authorization);
+
+  if ((data.password = strchr(data.username, ':')) == NULL)
+  {
+    fputs("No password in Authorization header.\n", stderr);
+    return (HTTP_STATUS_BAD_REQUEST);
+  }
+
+  *(data.password)++ = '\0';
+
+  if (!data.username[0])
+  {
+    fputs("No username in Authorization header.\n", stderr);
+    return (HTTP_STATUS_BAD_REQUEST);
+  }
+
+  pamdata.conv        = pam_func;
+  pamdata.appdata_ptr = &data;
+
+  if ((pamerr = pam_start(PAMService, data.username, &pamdata, &pamh)) != PAM_SUCCESS)
+  {
+    fprintf(stderr, "pam_start() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
+    return (HTTP_STATUS_SERVER_ERROR);
+  }
+
+  if ((pamerr = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS)
+  {
+    fprintf(stderr, "pam_authenticate() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
+    pam_end(pamh, 0);
+    return (HTTP_STATUS_UNAUTHORIZED);
+  }
+
+  if ((pamerr = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS)
+  {
+    fprintf(stderr, "pam_acct_mgmt() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
+    pam_end(pamh, 0);
+    return (HTTP_STATUS_SERVER_ERROR);
+  }
+
+  strlcpy(client->username, data.username, sizeof(client->username));
+
+  pam_end(pamh, PAM_SUCCESS);
+
+  return (HTTP_STATUS_CONTINUE);
+
+#else
+ /*
+  * No authentication support built-in, return 'continue'...
+  */
+
+  return (HTTP_STATUS_CONTINUE);
+#endif /* HAVE_LIBPAM */
+}
+
+
 /*
  * 'clean_jobs()' - Clean out old (completed) jobs.
  */
@@ -1240,6 +1388,7 @@ create_printer(
     const char   *directory,           /* I - Spool directory */
     const char   *command,             /* I - Command to run on job files, if any */
     const char   *device_uri,          /* I - Output device, if any */
+    const char   *output_format,       /* I - Output format, if any */
     ipp_t        *attrs)               /* I - Capability attributes */
 {
   ippeve_printer_t     *printer;       /* Printer */
@@ -1478,6 +1627,7 @@ create_printer(
   printer->dnssd_name    = strdup(name);
   printer->command       = command ? strdup(command) : NULL;
   printer->device_uri    = device_uri ? strdup(device_uri) : NULL;
+  printer->output_format = output_format ? strdup(output_format) : NULL;
   printer->directory     = strdup(directory);
   printer->icon          = icon ? strdup(icon) : NULL;
   printer->port          = serverport;
@@ -1540,9 +1690,9 @@ create_printer(
     fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
     fprintf(stderr, "printer-supply-info-uri=\"%s\"\n", supplyurl);
 #ifdef HAVE_SSL
-    fprintf(stderr, "printer-uri=\"%s\"\n", uri);
-#else
     fprintf(stderr, "printer-uri=\"%s\",\"%s\"\n", uri, securi);
+#else
+    fprintf(stderr, "printer-uri=\"%s\"\n", uri);
 #endif /* HAVE_SSL */
   }
 
@@ -4845,13 +4995,13 @@ load_ppd_attributes(
   attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL);
   ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
   for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
-    ippSetInteger(attrs, &attr, i, finishings->value);
+    ippSetInteger(attrs, &attr, i, (int)finishings->value);
 
   /* finishings-supported */
   attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL);
   ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
   for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
-    ippSetInteger(attrs, &attr, i, finishings->value);
+    ippSetInteger(attrs, &attr, i, (int)finishings->value);
 
   /* media-bottom-margin-supported */
   for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
@@ -5210,6 +5360,78 @@ load_ppd_attributes(
 #endif /* !CUPS_LITE */
 
 
+#if HAVE_LIBPAM
+/*
+ * 'pam_func()' - PAM conversation function.
+ */
+
+static int                             /* O - Success or failure */
+pam_func(
+    int                      num_msg,  /* I - Number of messages */
+    const struct pam_message **msg,    /* I - Messages */
+    struct pam_response      **resp,   /* O - Responses */
+    void                     *appdata_ptr)
+                                       /* I - Pointer to connection */
+{
+  int                  i;              /* Looping var */
+  struct pam_response  *replies;       /* Replies */
+  ippeve_authdata_t    *data;          /* Pointer to auth data */
+
+
+ /*
+  * Allocate memory for the responses...
+  */
+
+  if ((replies = malloc(sizeof(struct pam_response) * (size_t)num_msg)) == NULL)
+    return (PAM_CONV_ERR);
+
+ /*
+  * Answer all of the messages...
+  */
+
+  data = (ippeve_authdata_t *)appdata_ptr;
+
+  for (i = 0; i < num_msg; i ++)
+  {
+    switch (msg[i]->msg_style)
+    {
+      case PAM_PROMPT_ECHO_ON:
+          replies[i].resp_retcode = PAM_SUCCESS;
+          replies[i].resp         = strdup(data->username);
+          break;
+
+      case PAM_PROMPT_ECHO_OFF:
+          replies[i].resp_retcode = PAM_SUCCESS;
+          replies[i].resp         = strdup(data->password);
+          break;
+
+      case PAM_TEXT_INFO:
+          replies[i].resp_retcode = PAM_SUCCESS;
+          replies[i].resp         = NULL;
+          break;
+
+      case PAM_ERROR_MSG:
+          replies[i].resp_retcode = PAM_SUCCESS;
+          replies[i].resp         = NULL;
+          break;
+
+      default:
+          free(replies);
+          return (PAM_CONV_ERR);
+    }
+  }
+
+ /*
+  * Return the responses back to PAM...
+  */
+
+  *resp = replies;
+
+  return (PAM_SUCCESS);
+}
+#endif /* HAVE_LIBPAM */
+
+
 /*
  * 'parse_options()' - Parse URL options into CUPS options.
  *
@@ -5415,6 +5637,8 @@ process_http(ippeve_client_t *client)     /* I - Client connection */
   * Clear state variables...
   */
 
+  client->username[0] = '\0';
+
   ippDelete(client->request);
   ippDelete(client->response);
 
@@ -5510,8 +5734,7 @@ process_http(ippeve_client_t *client)     /* I - Client connection */
   * Handle HTTP Upgrade...
   */
 
-  if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
-                        "Upgrade"))
+  if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), "Upgrade"))
   {
 #ifdef HAVE_SSL
     if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
@@ -5630,32 +5853,44 @@ process_http(ippeve_client_t *client)   /* I - Client connection */
            httpFlushWrite(client->http);
          }
        }
-       else if (!strcmp(client->uri, "/"))
+       else
        {
         /*
-         * Show web status page...
+         * Authenticate if needed...
          */
 
-          return (show_status(client));
-       }
-       else if (!strcmp(client->uri, "/media"))
-       {
-        /*
-         * Show web media page...
-         */
+         if ((http_status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
+         {
+           return (respond_http(client, http_status, NULL, NULL, 0));
+         }
 
-          return (show_media(client));
-       }
-       else if (!strcmp(client->uri, "/supplies"))
-       {
-        /*
-         * Show web supplies page...
-         */
+         if (!strcmp(client->uri, "/"))
+         {
+          /*
+           * Show web status page...
+           */
+
+           return (show_status(client));
+         }
+         else if (!strcmp(client->uri, "/media"))
+         {
+          /*
+           * Show web media page...
+           */
 
-          return (show_supplies(client));
+           return (show_media(client));
+         }
+         else if (!strcmp(client->uri, "/supplies"))
+         {
+          /*
+           * Show web supplies page...
+           */
+
+           return (show_supplies(client));
+         }
+         else
+           return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
        }
-       else
-         return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
        break;
 
     case HTTP_STATE_POST :
@@ -5714,6 +5949,7 @@ process_ipp(ippeve_client_t *client)      /* I - Client */
   ipp_attribute_t      *uri;           /* Printer URI attribute */
   int                  major, minor;   /* Version number */
   const char           *name;          /* Name of attribute */
+  http_status_t                status;         /* Authentication status */
 
 
   debug_attributes("Request", client->request, 1);
@@ -5864,13 +6100,18 @@ process_ipp(ippeve_client_t *client)    /* I - Client */
                   strcmp(resource, "/ipp/print")))
          respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
                      name, ippGetString(uri, 0, NULL));
-       else
+       else if (client->operation_id != IPP_OP_GET_PRINTER_ATTRIBUTES && (status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
+        {
+          return (respond_http(client, status, NULL, NULL, 0));
+        }
+        else
        {
         /*
          * Try processing the operation...
          */
 
-         switch (ippGetOperation(client->request))
+
+         switch (client->operation_id)
          {
            case IPP_OP_PRINT_JOB :
                ipp_print_job(client);
@@ -6013,22 +6254,34 @@ process_job(ippeve_job_t *job)          /* I - Job */
       goto error;
     }
 
-    if (asprintf(myenvp + myenvc, "CONTENT_TYPE=%s", job->format) > 0)
-      myenvc ++;
+    snprintf(val, sizeof(val), "CONTENT_TYPE=%s", job->format);
+    myenvp[myenvc ++] = strdup(val);
 
-    if (job->printer->device_uri && asprintf(myenvp + myenvc, "DEVICE_URI=%s", job->printer->device_uri) > 0)
-      myenvc ++;
+    if (job->printer->device_uri)
+    {
+      snprintf(val, sizeof(val), "DEVICE_URI=%s", job->printer->device_uri);
+      myenvp[myenvc ++] = strdup(val);
+    }
+
+    if (job->printer->output_format)
+    {
+      snprintf(val, sizeof(val), "OUTPUT_TYPE=%s", job->printer->output_format);
+      myenvp[myenvc ++] = strdup(val);
+    }
 
 #if !CUPS_LITE
-    if (job->printer->ppdfile && asprintf(myenvp + myenvc, "PPD=%s", job->printer->ppdfile) > 0)
-      myenvc ++;
+    if (job->printer->ppdfile)
+    {
+      snprintf(val, sizeof(val), "PPD=%s", job->printer->ppdfile);
+      myenvp[myenvc++] = strdup(val);
+    }
 #endif /* !CUPS_LITE */
 
     for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
     {
      /*
       * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
-      * then add the value(s) from the attribute.
+      * "pwg-xxx" to "IPP_PWG_XXX", then add the value(s) from the attribute.
       */
 
       const char       *name = ippGetName(attr),
@@ -6036,7 +6289,7 @@ process_job(ippeve_job_t *job)            /* I - Job */
                        *suffix = strstr(name, "-default");
                                        /* Suffix on attribute name */
 
-      if (!suffix || suffix[8])
+      if (strncmp(name, "pwg-", 4) && (!suffix || suffix[8]))
         continue;
 
       valptr = val;
@@ -6368,7 +6621,7 @@ process_job(ippeve_job_t *job)            /* I - Job */
     * Sleep for a random amount of time to simulate job processing.
     */
 
-    sleep((unsigned)(5 + (rand() % 11)));
+    sleep((unsigned)(5 + (CUPS_RAND() % 11)));
   }
 
   if (job->cancel)
@@ -6501,6 +6754,10 @@ register_printer(
                        urf[252],       /* List of supported URF values */
                        *ptr;           /* Pointer into string */
 
+
+  if (!strcmp(subtypes, "off"))
+    return (1);
+
   color_supported           = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
   document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
   printer_location          = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
@@ -6546,6 +6803,7 @@ register_printer(
 #ifdef HAVE_DNSSD
   DNSServiceErrorType  error;          /* Error from Bonjour */
   char                 regtype[256];   /* Bonjour service type */
+  uint32_t             interface;      /* Interface index */
 
 
  /*
@@ -6578,9 +6836,11 @@ register_printer(
   * defend our service name but not actually support LPD...
   */
 
+  interface = !strcmp(printer->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
+
   printer->printer_ref = DNSSDMaster;
 
-  if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
+  if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
   {
     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
     return (0);
@@ -6598,7 +6858,7 @@ register_printer(
   else
     strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
 
-  if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
+  if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
   {
     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
     return (0);
@@ -6617,7 +6877,7 @@ register_printer(
   else
     strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
 
-  if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
+  if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
   {
     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
     return (0);
@@ -6631,7 +6891,7 @@ register_printer(
 
   printer->http_ref = DNSSDMaster;
 
-  if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
+  if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
   {
     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
     return (0);
@@ -6796,6 +7056,14 @@ respond_http(
       client->operation == HTTP_STATE_OPTIONS)
     httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
 
+  if (code == HTTP_STATUS_UNAUTHORIZED)
+  {
+    char value[256];                   /* WWW-Authenticate value */
+
+    snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
+    httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
+  }
+
   if (type)
   {
     if (!strcmp(type, "text/html"))
@@ -7032,8 +7300,8 @@ show_media(ippeve_client_t  *client)      /* I - Client connection */
                        *tray_ptr;      /* Pointer into value */
   int                  tray_len;       /* Length of printer-input-tray value */
   int                  ready_sheets;   /* printer-input-tray sheets value */
-  int                  num_options;    /* Number of form options */
-  cups_option_t                *options;       /* Form options */
+  int                  num_options = 0;/* Number of form options */
+  cups_option_t                *options = NULL;/* Form options */
   static const int     sheets[] =      /* Number of sheets */
   {
     250,
@@ -7106,8 +7374,6 @@ show_media(ippeve_client_t  *client)      /* I - Client connection */
 
   if (printer->web_forms)
     num_options = parse_options(client, &options);
-  else
-    num_options = 0;
 
   if (num_options > 0)
   {
@@ -7256,7 +7522,7 @@ show_media(ippeve_client_t  *client)      /* I - Client connection */
       }
       html_printf(client, "</select>");
     }
-    else
+    else if (ready_type)
       html_printf(client, ", %s", ready_type);
 
    /*
@@ -7293,6 +7559,8 @@ show_media(ippeve_client_t  *client)      /* I - Client connection */
       }
       html_printf(client, "</select></td></tr>\n");
     }
+    else if (ready_sheets == 1)
+      html_printf(client, ", 1 sheet</td></tr>\n");
     else if (ready_sheets > 0)
       html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
     else
@@ -7432,8 +7700,8 @@ show_supplies(
                num_supply;             /* Number of supplies */
   ipp_attribute_t *supply,             /* printer-supply attribute */
                *supply_desc;           /* printer-supply-description attribute */
-  int          num_options;            /* Number of form options */
-  cups_option_t        *options;               /* Form options */
+  int          num_options = 0;        /* Number of form options */
+  cups_option_t        *options = NULL;        /* Form options */
   int          supply_len,             /* Length of supply value */
                level;                  /* Supply level */
   const char   *supply_value;          /* Supply value */
@@ -7500,8 +7768,6 @@ show_supplies(
 
   if (printer->web_forms)
     num_options = parse_options(client, &options);
-  else
-    num_options = 0;
 
   if (num_options > 0)
   {
@@ -7619,10 +7885,12 @@ time_string(time_t tv,                  /* I - Time value */
             char   *buffer,            /* I - Buffer */
            size_t bufsize)             /* I - Size of buffer */
 {
-  struct tm    *curtime = localtime(&tv);
-                                       /* Local time */
+  struct tm    date;                   /* Local time and date */
+
+  localtime_r(&tv, &date);
+
+  strftime(buffer, bufsize, "%X", &date);
 
-  strftime(buffer, bufsize, "%X", curtime);
   return (buffer);
 }
 
@@ -7636,11 +7904,14 @@ usage(int status)                       /* O - Exit status */
 {
   _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
   _cupsLangPuts(stdout, _("Options:"));
-  _cupsLangPuts(stderr, _("--help                  Show program help"));
-  _cupsLangPuts(stderr, _("--no-web-forms          Disable web forms for media and supplies"));
-  _cupsLangPuts(stderr, _("--version               Show program version"));
+  _cupsLangPuts(stdout, _("--help                  Show program help"));
+  _cupsLangPuts(stdout, _("--no-web-forms          Disable web forms for media and supplies"));
+  _cupsLangPuts(stdout, _("--pam-service service   Use the named PAM service"));
+  _cupsLangPuts(stdout, _("--version               Show program version"));
   _cupsLangPuts(stdout, _("-2                      Set 2-sided printing support (default=1-sided)"));
+  _cupsLangPuts(stdout, _("-A                      Enable authentication"));
   _cupsLangPuts(stdout, _("-D device-uri           Set the device URI for the printer"));
+  _cupsLangPuts(stdout, _("-F output-type/subtype  Set the output format for the printer"));
 #ifdef HAVE_SSL
   _cupsLangPuts(stdout, _("-K keypath              Set location of server X.509 certificates and keys."));
 #endif /* HAVE_SSL */
@@ -7659,7 +7930,7 @@ usage(int status)                 /* O - Exit status */
   _cupsLangPuts(stdout, _("-p port                 Set port number for printer"));
   _cupsLangPuts(stdout, _("-r subtype,[subtype]    Set DNS-SD service subtype"));
   _cupsLangPuts(stdout, _("-s speed[,color-speed]  Set speed in pages per minute"));
-  _cupsLangPuts(stderr, _("-v                      Be verbose"));
+  _cupsLangPuts(stdout, _("-v                      Be verbose"));
 
   exit(status);
 }