]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/ipp.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / backend / ipp.c
index 180586e0548ba79283e58b2a729a2b0f672031aa..f2f676de3a0c09c4c7a8c5f090b08e76455af6be 100644 (file)
@@ -1,36 +1,14 @@
 /*
- * "$Id: ipp.c 9759 2011-05-11 03:24:33Z mike $"
+ * IPP backend for CUPS.
  *
- *   IPP backend for CUPS.
+ * Copyright 2007-2017 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
- *   Copyright 2007-2012 by Apple Inc.
- *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
- *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   "LICENSE" which should have been included with this file.  If this
- *   file is missing or damaged, see the license at "http://www.cups.org/".
- *
- *   This file is subject to the Apple OS-Developed Software exception.
- *
- * Contents:
- *
- *   main()                - Send a file to the printer or server.
- *   cancel_job()          - Cancel a print job.
- *   check_printer_state()  - Check the printer state.
- *   monitor_printer()     - Monitor the printer state.
- *   new_request()         - Create a new print creation or validation
- *                           request.
- *   password_cb()         - Disable the password prompt for
- *                           cupsDoFileRequest().
- *   quote_string()        - Quote a string value.
- *   report_attr()         - Report an IPP attribute value.
- *   report_printer_state() - Report the printer state.
- *   run_as_user()         - Run the IPP backend as the printing user.
- *   sigterm_handler()     - Handle 'terminate' signals that stop the backend.
- *   timeout_cb()          - Handle HTTP timeouts.
- *   update_reasons()      - Update the printer-state-reasons values.
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * "LICENSE" which should have been included with this file.  If this
+ * file is missing or damaged, see the license at "http://www.cups.org/".
  */
 
 /*
@@ -38,6 +16,7 @@
  */
 
 #include "backend-private.h"
+#include <cups/ppd-private.h>
 #include <cups/array-private.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -82,6 +61,7 @@ typedef struct _cups_monitor_s                /**** Monitoring data ****/
                        version,        /* IPP version */
                        job_id,         /* Job ID for submitted job */
                        job_reasons,    /* Job state reasons bits */
+                       create_job,     /* Support Create-Job? */
                        get_job_attrs;  /* Support Get-Job-Attributes? */
   const char           *job_name;      /* Job name for submitted job */
   http_encryption_t    encryption;     /* Use encryption? */
@@ -97,7 +77,7 @@ typedef struct _cups_monitor_s                /**** Monitoring data ****/
 static const char      *auth_info_required;
                                        /* New auth-info-required value */
 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
-static int             child_pid = 0;  /* Child process ID */
+static pid_t           child_pid = 0;  /* Child process ID */
 #endif /* HAVE_GSSAPI && HAVE_XPC */
 static const char * const jattrs[] =   /* Job attributes we want */
 {
@@ -109,14 +89,14 @@ static const char * const jattrs[] =       /* Job attributes we want */
   "job-state",
   "job-state-reasons"
 };
-static int             job_canceled = 0;
+static int             job_canceled = 0,
                                        /* Job cancelled? */
+                       uri_credentials = 0;
+                                       /* Credentials supplied in URI? */
 static char            username[256] = "",
                                        /* Username for device URI */
                        *password = NULL;
                                        /* Password for device URI */
-static int             password_tries = 0;
-                                       /* Password tries */
 static const char * const pattrs[] =   /* Printer attributes we want */
 {
 #ifdef HAVE_LIBZ
@@ -125,6 +105,7 @@ static const char * const pattrs[] =        /* Printer attributes we want */
   "copies-supported",
   "cups-version",
   "document-format-supported",
+  "job-password-encryption-supported",
   "marker-colors",
   "marker-high-levels",
   "marker-levels",
@@ -135,9 +116,11 @@ static const char * const pattrs[] =       /* Printer attributes we want */
   "media-col-supported",
   "multiple-document-handling-supported",
   "operations-supported",
+  "print-color-mode-supported",
   "printer-alert",
   "printer-alert-description",
   "printer-is-accepting-jobs",
+  "printer-mandatory-job-attributes",
   "printer-state",
   "printer-state-message",
   "printer-state-reasons"
@@ -161,6 +144,8 @@ static cups_option_t        *attr_cache = NULL;
 static cups_array_t    *state_reasons; /* Array of printe-state-reasons keywords */
 static char            tmpfilename[1024] = "";
                                        /* Temporary spool file name */
+static char            mandatory_attrs[1024] = "";
+                                       /* cupsMandatory value */
 
 
 /*
@@ -173,6 +158,7 @@ static void         cancel_job(http_t *http, const char *uri, int id,
 static ipp_pstate_t    check_printer_state(http_t *http, const char *uri,
                                            const char *resource,
                                            const char *user, int version);
+static void            debug_attributes(ipp_t *ipp);
 static void            *monitor_printer(_cups_monitor_t *monitor);
 static ipp_t           *new_request(ipp_op_t op, int version, const char *uri,
                                     const char *user, const char *title,
@@ -182,10 +168,10 @@ static ipp_t              *new_request(ipp_op_t op, int version, const char *uri,
                                     ppd_file_t *ppd,
                                     ipp_attribute_t *media_col_sup,
                                     ipp_attribute_t *doc_handling_sup,
-                                    int print_color_mode);
+                                    ipp_attribute_t *print_color_mode_sup);
 static const char      *password_cb(const char *prompt, http_t *http,
                                     const char *method, const char *resource,
-                                    void *user_data);
+                                    int *user_data);
 static const char      *quote_string(const char *s, char *q, size_t qsize);
 static void            report_attr(ipp_attribute_t *attr);
 static void            report_printer_state(ipp_t *ipp);
@@ -223,6 +209,7 @@ main(int  argc,                             /* I - Number of command-line args */
                *name,                  /* Name of option */
                *value,                 /* Value of option */
                sep;                    /* Separator character */
+  int          password_tries = 0;     /* Password tries */
   http_addrlist_t *addrlist;           /* Address of printer */
   int          snmp_enabled = 1;       /* Is SNMP enabled? */
   int          snmp_fd,                /* SNMP socket */
@@ -234,7 +221,6 @@ main(int  argc,                             /* I - Number of command-line args */
                *compatfile = NULL;     /* Compatibility filename */
   off_t                compatsize = 0;         /* Size of compatibility file */
   int          port;                   /* Port number (not used) */
-  char         portname[255];          /* Port name */
   char         uri[HTTP_MAX_URI];      /* Updated URI without user/pass */
   char         print_job_name[1024];   /* Update job-name for Print-Job */
   http_status_t        http_status;            /* Status of HTTP request */
@@ -248,9 +234,10 @@ main(int  argc,                            /* I - Number of command-line args */
   int          delay,                  /* Delay for retries */
                prev_delay;             /* Previous delay */
   const char   *compression;           /* Compression mode */
-  int          waitjob,                /* Wait for job complete? */
+  int          waitjob,                        /* Wait for job complete? */
                waitjob_tries = 0,      /* Number of times we've waited */
                waitprinter;            /* Wait for printer ready? */
+  time_t       waittime;               /* Wait time for held jobs */
   _cups_monitor_t monitor;             /* Monitoring data */
   ipp_attribute_t *job_id_attr;                /* job-id attribute */
   int          job_id;                 /* job-id value */
@@ -261,18 +248,20 @@ main(int  argc,                           /* I - Number of command-line args */
 #endif /* HAVE_LIBZ */
   ipp_attribute_t *copies_sup;         /* copies-supported */
   ipp_attribute_t *cups_version;       /* cups-version */
+  ipp_attribute_t *encryption_sup;     /* job-password-encryption-supported */
   ipp_attribute_t *format_sup;         /* document-format-supported */
+  ipp_attribute_t *job_auth;           /* job-authorization-uri */
   ipp_attribute_t *media_col_sup;      /* media-col-supported */
   ipp_attribute_t *operations_sup;     /* operations-supported */
   ipp_attribute_t *doc_handling_sup;   /* multiple-document-handling-supported */
   ipp_attribute_t *printer_state;      /* printer-state attribute */
   ipp_attribute_t *printer_accepting;  /* printer-is-accepting-jobs */
+  ipp_attribute_t *print_color_mode_sup;/* Does printer support print-color-mode? */
   int          create_job = 0,         /* Does printer support Create-Job? */
                get_job_attrs = 0,      /* Does printer support Get-Job-Attributes? */
                send_document = 0,      /* Does printer support Send-Document? */
                validate_job = 0,       /* Does printer support Validate-Job? */
-               print_color_mode = 0;   /* Does printer support print-color-mode? */
-  int          copies,                 /* Number of copies for job */
+               copies,                 /* Number of copies for job */
                copies_remaining;       /* Number of copies remaining */
   const char   *content_type,          /* CONTENT_TYPE environment variable */
                *final_content_type,    /* FINAL_CONTENT_TYPE environment var */
@@ -366,7 +355,8 @@ main(int  argc,                             /* I - Number of command-line args */
   * that way.
   */
 
-  if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
+  if (!getuid() && (value = getenv("AUTH_UID")) != NULL &&
+      !getenv("AUTH_PASSWORD"))
   {
     uid_t      uid = (uid_t)atoi(value);
                                        /* User ID */
@@ -431,7 +421,7 @@ main(int  argc,                             /* I - Number of command-line args */
   if (!port)
     port = IPP_PORT;                   /* Default to port 631 */
 
-  if (!strcmp(scheme, "https"))
+  if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
     cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
   else
     cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
@@ -542,8 +532,8 @@ main(int  argc,                             /* I - Number of command-line args */
          */
 
          snmp_enabled = !value[0] || !_cups_strcasecmp(value, "on") ||
-                        _cups_strcasecmp(value, "yes") ||
-                        _cups_strcasecmp(value, "true");
+                        !_cups_strcasecmp(value, "yes") ||
+                        !_cups_strcasecmp(value, "true");
       }
       else if (!_cups_strcasecmp(name, "version"))
       {
@@ -634,7 +624,7 @@ main(int  argc,                             /* I - Number of command-line args */
   * Set the authentication info, if any...
   */
 
-  cupsSetPasswordCB2(password_cb, NULL);
+  cupsSetPasswordCB2((cups_password_cb2_t)password_cb, &password_tries);
 
   if (username[0])
   {
@@ -646,6 +636,7 @@ main(int  argc,                             /* I - Number of command-line args */
       *password++ = '\0';
 
     cupsSetUser(username);
+    uri_credentials = 1;
   }
   else
   {
@@ -670,29 +661,10 @@ main(int  argc,                           /* I - Number of command-line args */
 
   start_time = time(NULL);
 
-  sprintf(portname, "%d", port);
-
-  update_reasons(NULL, "+connecting-to-device");
-  fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
+  addrlist = backendLookup(hostname, port, &job_canceled);
 
-  while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
-  {
-    _cupsLangPrintFilter(stderr, "INFO",
-                         _("Unable to locate printer \"%s\"."), hostname);
-    sleep(10);
-
-    if (getenv("CLASS") != NULL)
-    {
-      update_reasons(NULL, "-connecting-to-device");
-      return (CUPS_BACKEND_STOP);
-    }
-
-    if (job_canceled)
-      return (CUPS_BACKEND_OK);
-  }
-
-  http = _httpCreate(hostname, port, addrlist, AF_UNSPEC, cupsEncryption(), 1,
-                     _HTTP_MODE_CLIENT);
+  http = httpConnect2(hostname, port, addrlist, AF_UNSPEC, cupsEncryption(), 1,
+                      0, NULL);
   httpSetTimeout(http, 30.0, timeout_cb, NULL);
 
  /*
@@ -769,8 +741,7 @@ main(int  argc,                             /* I - Number of command-line args */
 
       fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
 
-      if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
-          errno == EHOSTUNREACH)
+      if (errno == ECONNREFUSED || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ETIMEDOUT || errno == ENOTCONN)
       {
         if (contimeout && (time(NULL) - start_time) > contimeout)
        {
@@ -789,19 +760,19 @@ main(int  argc,                           /* I - Number of command-line args */
              break;
 
          case EHOSTUNREACH :
+         default :
              _cupsLangPrintFilter(stderr, "WARNING",
                                   _("The printer is unreachable at this "
                                     "time."));
              break;
 
          case ECONNREFUSED :
-         default :
              _cupsLangPrintFilter(stderr, "WARNING",
                                   _("The printer is in use."));
              break;
         }
 
-       sleep(delay);
+       sleep((unsigned)delay);
 
         delay = _cupsNextDelay(delay, &prev_delay);
       }
@@ -825,6 +796,76 @@ main(int  argc,                            /* I - Number of command-line args */
   else if (!http)
     return (CUPS_BACKEND_FAILED);
 
+  if (httpIsEncrypted(http))
+  {
+   /*
+    * Validate TLS credentials...
+    */
+
+    cups_array_t       *creds;         /* TLS credentials */
+    cups_array_t       *lcreds = NULL; /* Loaded credentials */
+    http_trust_t       trust;          /* Trust level */
+    char               credinfo[1024], /* Information on credentials */
+                       lcredinfo[1024];/* Information on saved credentials */
+    static const char  * const trusts[] = { NULL, "+cups-pki-invalid", "+cups-pki-changed", "+cups-pki-expired", NULL, "+cups-pki-unknown" };
+                                       /* Trust keywords */
+    static const char  * const trust_msgs[] =
+    {
+      "Credentials are OK/trusted",
+      "Credentials are invalid",
+      "Credentials have changed",
+      "Credentials are expired",
+      "Credentials have been renewed",
+      "Credentials are unknown/new"
+    };
+
+    fputs("DEBUG: Connection is encrypted.\n", stderr);
+
+    if (!httpCopyCredentials(http, &creds))
+    {
+      trust = httpCredentialsGetTrust(creds, hostname);
+      httpCredentialsString(creds, credinfo, sizeof(credinfo));
+
+      fprintf(stderr, "DEBUG: %s (%s)\n", trust_msgs[trust], cupsLastErrorString());
+      fprintf(stderr, "DEBUG: Printer credentials: %s\n", credinfo);
+
+      if (!httpLoadCredentials(NULL, &lcreds, hostname))
+      {
+        httpCredentialsString(lcreds, lcredinfo, sizeof(lcredinfo));
+       fprintf(stderr, "DEBUG: Stored credentials: %s\n", lcredinfo);
+      }
+      else
+        fputs("DEBUG: No stored credentials.\n", stderr);
+
+      update_reasons(NULL, "-cups-pki-invalid,cups-pki-changed,cups-pki-expired,cups-pki-unknown");
+      if (trusts[trust])
+      {
+        update_reasons(NULL, trusts[trust]);
+        return (CUPS_BACKEND_STOP);
+      }
+
+      if (!lcreds)
+      {
+       /*
+        * Could not load the credentials, let's save the ones we have so we
+        * can detect changes...
+        */
+
+        httpSaveCredentials(NULL, creds, hostname);
+      }
+
+      httpFreeCredentials(lcreds);
+      httpFreeCredentials(creds);
+    }
+    else
+    {
+      fputs("DEBUG: No printer credentials.\n", stderr);
+
+      update_reasons(NULL, "cups-pki-unknown");
+      return (CUPS_BACKEND_STOP);
+    }
+  }
+
   update_reasons(NULL, "-connecting-to-device");
   _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
 
@@ -847,15 +888,17 @@ main(int  argc,                           /* I - Number of command-line args */
   */
 
 #ifdef HAVE_LIBZ
-  compression_sup  = NULL;
+  compression_sup      = NULL;
 #endif /* HAVE_LIBZ */
-  copies_sup       = NULL;
-  cups_version     = NULL;
-  format_sup       = NULL;
-  media_col_sup    = NULL;
-  supported        = NULL;
-  operations_sup   = NULL;
-  doc_handling_sup = NULL;
+  copies_sup           = NULL;
+  cups_version         = NULL;
+  encryption_sup       = NULL;
+  format_sup           = NULL;
+  media_col_sup        = NULL;
+  supported            = NULL;
+  operations_sup       = NULL;
+  doc_handling_sup     = NULL;
+  print_color_mode_sup = NULL;
 
   do
   {
@@ -870,9 +913,7 @@ main(int  argc,                             /* I - Number of command-line args */
     */
 
     request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
-    request->request.op.version[0] = version / 10;
-    request->request.op.version[1] = version % 10;
-
+    ippSetVersion(request, version / 10, version % 10);
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                 NULL, uri);
 
@@ -921,7 +962,7 @@ main(int  argc,                             /* I - Number of command-line args */
 
         report_printer_state(supported);
 
-       sleep(delay);
+       sleep((unsigned)delay);
 
         delay = _cupsNextDelay(delay, &prev_delay);
       }
@@ -1035,12 +1076,13 @@ main(int  argc,                         /* I - Number of command-line args */
 
        report_printer_state(supported);
 
-       sleep(delay);
+       sleep((unsigned)delay);
 
        delay = _cupsNextDelay(delay, &prev_delay);
 
        ippDelete(supported);
-       supported = NULL;
+       supported  = NULL;
+       ipp_status = IPP_STATUS_ERROR_BUSY;
        continue;
       }
     }
@@ -1064,12 +1106,12 @@ main(int  argc,                         /* I - Number of command-line args */
                         "compression value \"%s\".\n", compression);
         compression = NULL;
       }
-      else if (!compression)
+      else if (!compression && (!strcmp(final_content_type, "image/pwg-raster") || !strcmp(final_content_type, "image/urf")))
       {
-        if (ippContainsString(compression_sup, "deflate"))
-          compression = "deflate";
-        else if (ippContainsString(compression_sup, "gzip"))
+        if (ippContainsString(compression_sup, "gzip"))
           compression = "gzip";
+        else if (ippContainsString(compression_sup, "deflate"))
+          compression = "deflate";
 
         if (compression)
           fprintf(stderr, "DEBUG: Automatically using \"%s\" compression.\n",
@@ -1096,6 +1138,8 @@ main(int  argc,                           /* I - Number of command-line args */
 
     cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
 
+    encryption_sup = ippFindAttribute(supported, "job-password-encryption-supported", IPP_TAG_KEYWORD);
+
     if ((format_sup = ippFindAttribute(supported, "document-format-supported",
                                       IPP_TAG_MIMETYPE)) != NULL)
     {
@@ -1116,9 +1160,7 @@ main(int  argc,                           /* I - Number of command-line args */
                media_col_sup->values[i].string.text);
     }
 
-    print_color_mode = ippFindAttribute(supported,
-                                       "print-color-mode-supported",
-                                       IPP_TAG_KEYWORD) != NULL;
+    print_color_mode_sup = ippFindAttribute(supported, "print-color-mode-supported", IPP_TAG_KEYWORD);
 
     if ((operations_sup = ippFindAttribute(supported, "operations-supported",
                                           IPP_TAG_ENUM)) != NULL)
@@ -1271,11 +1313,51 @@ main(int  argc,                         /* I - Number of command-line args */
       * Load the PPD file and generate PWG attribute mapping information...
       */
 
+      ppd_attr_t *mandatory;           /* cupsMandatory value */
+
       ppd = ppdOpenFile(getenv("PPD"));
       pc  = _ppdCacheCreateWithPPD(ppd);
 
       ppdMarkDefaults(ppd);
       cupsMarkOptions(ppd, num_options, options);
+
+      if ((mandatory = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
+        strlcpy(mandatory_attrs, mandatory->value, sizeof(mandatory_attrs));
+    }
+
+   /*
+    * Validate job-password/-encryption...
+    */
+
+    if (cupsGetOption("job-password", num_options, options))
+    {
+      const char *keyword;             /* job-password-encryption value */
+      static const char * const hashes[] =
+      {                                        /* List of supported hash algorithms, in order of preference */
+        "sha-512",
+        "sha-384",
+        "sha-512_256",
+        "sha-512-224",
+        "sha-256",
+        "sha-224",
+        "sha",
+        "none"
+      };
+
+      if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL || !ippContainsString(encryption_sup, keyword))
+      {
+       /*
+        * Either no job-password-encryption or the value isn't supported by
+        * the printer...
+        */
+
+        for (i = 0; i < (int)(sizeof(hashes) / sizeof(hashes[0])); i ++)
+          if (ippContainsString(encryption_sup, hashes[i]))
+            break;
+
+        if (i < (int)(sizeof(hashes) / sizeof(hashes[0])))
+          num_options = cupsAddOption("job-password-encryption", hashes[i], num_options, &options);
+      }
     }
   }
   else
@@ -1285,24 +1367,10 @@ main(int  argc,                         /* I - Number of command-line args */
 
   if (format_sup != NULL)
   {
-    for (i = 0; i < format_sup->num_values; i ++)
-      if (!_cups_strcasecmp(final_content_type,
-                            format_sup->values[i].string.text))
-      {
-        document_format = final_content_type;
-       break;
-      }
-
-    if (!document_format)
-    {
-      for (i = 0; i < format_sup->num_values; i ++)
-       if (!_cups_strcasecmp("application/octet-stream",
-                             format_sup->values[i].string.text))
-       {
-         document_format = "application/octet-stream";
-         break;
-       }
-    }
+    if (ippContainsString(format_sup, final_content_type))
+      document_format = final_content_type;
+    else if (ippContainsString(format_sup, "application/octet-stream"))
+      document_format = "application/octet-stream";
   }
 
   fprintf(stderr, "DEBUG: final_content_type=\"%s\", document_format=\"%s\"\n",
@@ -1325,7 +1393,7 @@ main(int  argc,                           /* I - Number of command-line args */
 
     _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
 
-    if ((compatsize = write(fd, buffer, bytes)) < 0)
+    if ((compatsize = write(fd, buffer, (size_t)bytes)) < 0)
     {
       perror("DEBUG: Unable to write temporary file");
       return (CUPS_BACKEND_FAILED);
@@ -1354,8 +1422,9 @@ main(int  argc,                           /* I - Number of command-line args */
  /*
   * If the printer only claims to support IPP/1.0, or if the user specifically
   * included version=1.0 in the URI, then do not try to use Create-Job or
-  * Send-Document.  This is another dreaded compatibility hack, but unfortunately
-  * there are enough broken printers out there that we need this for now...
+  * Send-Document.  This is another dreaded compatibility hack, but
+  * unfortunately there are enough broken printers out there that we need
+  * this for now...
   */
 
   if (version == 10)
@@ -1372,6 +1441,7 @@ main(int  argc,                           /* I - Number of command-line args */
   monitor.port          = port;
   monitor.version       = version;
   monitor.job_id        = 0;
+  monitor.create_job    = create_job;
   monitor.get_job_attrs = get_job_attrs;
   monitor.encryption    = cupsEncryption();
   monitor.job_state     = IPP_JOB_PENDING;
@@ -1396,17 +1466,26 @@ main(int  argc,                         /* I - Number of command-line args */
 
   while (!job_canceled && validate_job)
   {
-    request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2],
+    request = new_request(IPP_OP_VALIDATE_JOB, version, uri, argv[2],
                           monitor.job_name, num_options, options, compression,
                          copies_sup ? copies : 1, document_format, pc, ppd,
-                         media_col_sup, doc_handling_sup, print_color_mode);
+                         media_col_sup, doc_handling_sup, print_color_mode_sup);
 
-    ippDelete(cupsDoRequest(http, request, resource));
+    response = cupsDoRequest(http, request, resource);
 
     ipp_status = cupsLastError();
 
     fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
             ippErrorString(ipp_status), cupsLastErrorString());
+    debug_attributes(response);
+
+    if ((job_auth = ippFindAttribute(response, "job-authorization-uri",
+                                    IPP_TAG_URI)) != NULL)
+      num_options = cupsAddOption("job-authorization-uri",
+                                  ippGetString(job_auth, 0, NULL), num_options,
+                                  &options);
+
+    ippDelete(response);
 
     if (job_canceled)
       break;
@@ -1418,12 +1497,14 @@ main(int  argc,                         /* I - Number of command-line args */
       sleep(10);
     }
     else if (ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED ||
+             ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
              ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
              ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
              ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
              ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
       goto cleanup;
     else if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
+             ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED ||
             ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
     {
       const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
@@ -1449,6 +1530,8 @@ main(int  argc,                           /* I - Number of command-line args */
     else if (ipp_status < IPP_REDIRECTION_OTHER_SITE ||
              ipp_status == IPP_BAD_REQUEST)
       break;
+    else if (job_auth == NULL && ipp_status > IPP_BAD_REQUEST)
+      goto cleanup;
   }
 
  /*
@@ -1477,7 +1560,7 @@ main(int  argc,                           /* I - Number of command-line args */
                          version, uri, argv[2], monitor.job_name, num_options,
                          options, compression, copies_sup ? copies : 1,
                          document_format, pc, ppd, media_col_sup,
-                         doc_handling_sup, print_color_mode);
+                         doc_handling_sup, print_color_mode_sup);
 
    /*
     * Do the request...
@@ -1500,6 +1583,9 @@ main(int  argc,                           /* I - Number of command-line args */
       http_status = cupsSendRequest(http, request, resource, length);
       if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
       {
+       if (compression && strcmp(compression, "none"))
+         httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
+
         if (num_files == 1)
         {
          if ((fd = open(files[0], O_RDONLY)) < 0)
@@ -1511,7 +1597,7 @@ main(int  argc,                           /* I - Number of command-line args */
        else
        {
          fd          = 0;
-         http_status = cupsWriteRequestData(http, buffer, bytes);
+         http_status = cupsWriteRequestData(http, buffer, (size_t)bytes);
         }
 
         while (http_status == HTTP_CONTINUE &&
@@ -1524,6 +1610,7 @@ main(int  argc,                           /* I - Number of command-line args */
           FD_ZERO(&input);
          FD_SET(fd, &input);
          FD_SET(snmp_fd, &input);
+         FD_SET(CUPS_SC_FD, &input);
 
           while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
                        NULL) <= 0 && !job_canceled);
@@ -1537,7 +1624,8 @@ main(int  argc,                           /* I - Number of command-line args */
             {
              fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
 
-             if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
+             if ((http_status = cupsWriteRequestData(http, buffer, (size_t)bytes))
+                     != HTTP_CONTINUE)
                break;
            }
            else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
@@ -1545,6 +1633,10 @@ main(int  argc,                          /* I - Number of command-line args */
          }
        }
 
+       if (http_status == HTTP_ERROR)
+         fprintf(stderr, "DEBUG: Error writing document data for "
+                         "Print-Job: %s\n", strerror(httpError(http)));
+
         if (num_files == 1)
          close(fd);
       }
@@ -1558,6 +1650,7 @@ main(int  argc,                           /* I - Number of command-line args */
     fprintf(stderr, "DEBUG: %s: %s (%s)\n",
             (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
             ippErrorString(ipp_status), cupsLastErrorString());
+    debug_attributes(response);
 
     if (ipp_status > IPP_OK_CONFLICT)
     {
@@ -1585,6 +1678,7 @@ main(int  argc,                           /* I - Number of command-line args */
       }
       else if (ipp_status == IPP_STATUS_ERROR_JOB_CANCELED ||
                ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED ||
+               ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
               ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
               ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
               ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
@@ -1667,8 +1761,7 @@ main(int  argc,                           /* I - Number of command-line args */
        */
 
        request = ippNewRequest(IPP_SEND_DOCUMENT);
-       request->request.op.version[0] = version / 10;
-       request->request.op.version[1] = version % 10;
+       ippSetVersion(request, version / 10, version % 10);
 
        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                     NULL, uri);
@@ -1680,8 +1773,8 @@ main(int  argc,                           /* I - Number of command-line args */
          ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
                        "requesting-user-name", NULL, argv[2]);
 
-        if ((i + 1) >= num_files)
-         ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
+       ippAddBoolean(request, IPP_TAG_OPERATION, "last-document",
+                     (i + 1) >= num_files);
 
        if (document_format)
          ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
@@ -1692,6 +1785,9 @@ main(int  argc,                           /* I - Number of command-line args */
                       "compression", NULL, compression);
 
        fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
+       fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
+       debug_attributes(request);
+
        http_status = cupsSendRequest(http, request, resource, 0);
        if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
        {
@@ -1701,7 +1797,7 @@ main(int  argc,                           /* I - Number of command-line args */
          if (num_files == 0)
          {
            fd          = 0;
-           http_status = cupsWriteRequestData(http, buffer, bytes);
+           http_status = cupsWriteRequestData(http, buffer, (size_t)bytes);
          }
          else
          {
@@ -1720,7 +1816,7 @@ main(int  argc,                           /* I - Number of command-line args */
          while (!job_canceled && http_status == HTTP_CONTINUE &&
                 (bytes = read(fd, buffer, sizeof(buffer))) > 0)
          {
-           if ((http_status = cupsWriteRequestData(http, buffer, bytes))
+           if ((http_status = cupsWriteRequestData(http, buffer, (size_t)bytes))
                    != HTTP_CONTINUE)
              break;
            else
@@ -1737,13 +1833,19 @@ main(int  argc,                         /* I - Number of command-line args */
            close(fd);
        }
 
-       ippDelete(cupsGetResponse(http, resource));
+        if (http_status == HTTP_ERROR)
+          fprintf(stderr, "DEBUG: Error writing document data for "
+                          "Send-Document: %s\n", strerror(httpError(http)));
+
+       response = cupsGetResponse(http, resource);
        ippDelete(request);
 
        fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
                ippErrorString(cupsLastError()), cupsLastErrorString());
+        debug_attributes(response);
+        ippDelete(response);
 
-       if (cupsLastError() > IPP_OK_CONFLICT)
+       if (cupsLastError() > IPP_OK_CONFLICT && !job_canceled)
        {
          ipp_status = cupsLastError();
 
@@ -1761,11 +1863,28 @@ main(int  argc,                         /* I - Number of command-line args */
       }
     }
 
+    if (job_canceled)
+      break;
+
     if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
     {
       fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
       copies_remaining --;
     }
+    else if ((ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR || ipp_status == IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE) &&
+             argc == 6 &&
+             document_format && strcmp(document_format, "image/pwg-raster") && strcmp(document_format, "image/urf"))
+    {
+     /*
+      * Need to reprocess the job as raster...
+      */
+
+      fputs("JOBSTATE: cups-retry-as-raster\n", stderr);
+      if (job_id > 0)
+       cancel_job(http, uri, job_id, resource, argv[2], version);
+
+      goto cleanup;
+    }
     else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
              ipp_status == IPP_NOT_POSSIBLE ||
             ipp_status == IPP_PRINTER_BUSY)
@@ -1800,6 +1919,27 @@ main(int  argc,                          /* I - Number of command-line args */
 
       goto cleanup;
     }
+    else if (ipp_status == IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED)
+    {
+     /*
+      * Server is configured incorrectly; the policy for Create-Job and
+      * Send-Document has to be the same (auth or no auth, encryption or
+      * no encryption).  Force the queue to stop since printing will never
+      * work.
+      */
+
+      fputs("DEBUG: The server or printer is configured incorrectly.\n",
+            stderr);
+      fputs("DEBUG: The policy for Create-Job and Send-Document must have the "
+            "same authentication and encryption requirements.\n", stderr);
+
+      ipp_status = IPP_STATUS_ERROR_INTERNAL;
+
+      if (job_id > 0)
+       cancel_job(http, uri, job_id, resource, argv[2], version);
+
+      goto cleanup;
+    }
     else if (ipp_status == IPP_NOT_FOUND)
     {
      /*
@@ -1828,9 +1968,11 @@ main(int  argc,                          /* I - Number of command-line args */
     if (!job_id || !waitjob || !get_job_attrs)
       continue;
 
+    fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
+
     _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
 
-    for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
+    for (delay = _cupsNextDelay(0, &prev_delay), waittime = time(NULL) + 30; !job_canceled;)
     {
      /*
       * Check for side-channel requests...
@@ -1838,13 +1980,21 @@ main(int  argc,                         /* I - Number of command-line args */
 
       backendCheckSideChannel(snmp_fd, http->hostaddr);
 
+     /*
+      * Check printer state...
+      */
+
+      check_printer_state(http, uri, resource, argv[2], version);
+
+      if (cupsLastError() <= IPP_OK_CONFLICT)
+        password_tries = 0;
+
      /*
       * Build an IPP_GET_JOB_ATTRIBUTES request...
       */
 
       request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
-      request->request.op.version[0] = version / 10;
-      request->request.op.version[1] = version % 10;
+      ippSetVersion(request, version / 10, version % 10);
 
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                   NULL, uri);
@@ -1860,6 +2010,9 @@ main(int  argc,                           /* I - Number of command-line args */
                     "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
                    NULL, jattrs);
 
+      fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
+      debug_attributes(request);
+
      /*
       * Do the request...
       */
@@ -1884,6 +2037,7 @@ main(int  argc,                           /* I - Number of command-line args */
 
       fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
              ippErrorString(ipp_status), cupsLastErrorString());
+      debug_attributes(response);
 
       if (ipp_status <= IPP_OK_CONFLICT)
        password_tries = 0;
@@ -1925,22 +2079,19 @@ main(int  argc,                         /* I - Number of command-line args */
                           remote_job_states[job_state->values[0].integer -
                                             IPP_JOB_PENDING]);
 
-         if ((job_sheets = ippFindAttribute(response,
-                                            "job-media-sheets-completed",
-                                            IPP_TAG_INTEGER)) == NULL)
-           job_sheets = ippFindAttribute(response,
-                                         "job-impressions-completed",
-                                         IPP_TAG_INTEGER);
+         if ((job_sheets = ippFindAttribute(response, "job-impressions-completed", IPP_TAG_INTEGER)) == NULL)
+           job_sheets = ippFindAttribute(response, "job-media-sheets-completed", IPP_TAG_INTEGER);
 
          if (job_sheets)
            fprintf(stderr, "PAGE: total %d\n",
                    job_sheets->values[0].integer);
 
         /*
-          * Stop polling if the job is finished or pending-held...
+          * Stop polling if the job is finished or pending-held for 30 seconds...
          */
 
-          if (job_state->values[0].integer > IPP_JOB_STOPPED)
+          if (job_state->values[0].integer > IPP_JSTATE_STOPPED ||
+             (job_state->values[0].integer == IPP_JSTATE_HELD && time(NULL) > waittime))
          {
            ippDelete(response);
            break;
@@ -1969,7 +2120,7 @@ main(int  argc,                           /* I - Number of command-line args */
       * Wait before polling again...
       */
 
-      sleep(delay);
+      sleep((unsigned)delay);
 
       delay = _cupsNextDelay(delay, &prev_delay);
     }
@@ -1980,20 +2131,29 @@ main(int  argc,                         /* I - Number of command-line args */
   */
 
   if (job_canceled > 0 && job_id > 0)
+  {
     cancel_job(http, uri, job_id, resource, argv[2], version);
 
+    if (cupsLastError() > IPP_OK_CONFLICT)
+      _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
+  }
+
  /*
   * Check the printer state and report it if necessary...
   */
 
   check_printer_state(http, uri, resource, argv[2], version);
 
+  if (cupsLastError() <= IPP_OK_CONFLICT)
+    password_tries = 0;
+
  /*
   * Collect the final page count as needed...
   */
 
   if (have_supplies &&
-      !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
+      !backendSNMPSupplies(snmp_fd, &(http->addrlist->addr), &page_count,
+                           NULL) &&
       page_count > start_count)
     fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
 
@@ -2046,17 +2206,19 @@ main(int  argc,                         /* I - Number of command-line args */
     fputs("JOBSTATE: account-authorization-failed\n", stderr);
 
   if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
-      ipp_status == IPP_AUTHENTICATION_CANCELED ||
-      ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
-      ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
-      ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
-      ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
+      ipp_status == IPP_AUTHENTICATION_CANCELED)
     return (CUPS_BACKEND_AUTH_REQUIRED);
+  else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
+          ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
+          ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
+          ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
+    return (CUPS_BACKEND_HOLD);
   else if (ipp_status == IPP_INTERNAL_ERROR)
     return (CUPS_BACKEND_STOP);
-  else if (ipp_status == IPP_CONFLICT)
+  else if (ipp_status == IPP_CONFLICT || ipp_status == IPP_STATUS_ERROR_REQUEST_ENTITY || ipp_status == IPP_STATUS_ERROR_REQUEST_VALUE)
     return (CUPS_BACKEND_FAILED);
   else if (ipp_status == IPP_REQUEST_VALUE ||
+          ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
            ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0)
   {
     if (ipp_status == IPP_REQUEST_VALUE)
@@ -2064,6 +2226,9 @@ main(int  argc,                           /* I - Number of command-line args */
     else if (ipp_status == IPP_DOCUMENT_FORMAT)
       _cupsLangPrintFilter(stderr, "ERROR",
                            _("Printer cannot print supplied content."));
+    else if (ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES)
+      _cupsLangPrintFilter(stderr, "ERROR",
+                           _("Printer cannot print with supplied options."));
     else
       _cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer."));
 
@@ -2094,8 +2259,7 @@ cancel_job(http_t     *http,              /* I - HTTP connection */
   _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
 
   request = ippNewRequest(IPP_CANCEL_JOB);
-  request->request.op.version[0] = version / 10;
-  request->request.op.version[1] = version % 10;
+  ippSetVersion(request, version / 10, version % 10);
 
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                NULL, uri);
@@ -2110,9 +2274,6 @@ cancel_job(http_t     *http,              /* I - HTTP connection */
   */
 
   ippDelete(cupsDoRequest(http, request, resource));
-
-  if (cupsLastError() > IPP_OK_CONFLICT)
-    _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
 }
 
 
@@ -2140,8 +2301,7 @@ check_printer_state(
   */
 
   request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
-  request->request.op.version[0] = version / 10;
-  request->request.op.version[1] = version % 10;
+  ippSetVersion(request, version / 10, version % 10);
 
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
               NULL, uri);
@@ -2154,6 +2314,9 @@ check_printer_state(
                "requested-attributes",
                (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
 
+  fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
+  debug_attributes(request);
+
   if ((response = cupsDoRequest(http, request, resource)) != NULL)
   {
     report_printer_state(response);
@@ -2161,15 +2324,12 @@ check_printer_state(
     if ((attr = ippFindAttribute(response, "printer-state",
                                 IPP_TAG_ENUM)) != NULL)
       printer_state = (ipp_pstate_t)attr->values[0].integer;
-
-    ippDelete(response);
   }
 
   fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
          ippErrorString(cupsLastError()), cupsLastErrorString());
-
-  if (cupsLastError() <= IPP_OK_CONFLICT)
-    password_tries = 0;
+  debug_attributes(response);
+  ippDelete(response);
 
  /*
   * Return the printer-state value...
@@ -2179,6 +2339,51 @@ check_printer_state(
 }
 
 
+/*
+ * 'debug_attributes()' - Print out the request or response attributes as DEBUG
+ * messages...
+ */
+
+static void
+debug_attributes(ipp_t *ipp)           /* I - Request or response message */
+{
+  ipp_tag_t    group;                  /* Current group */
+  ipp_attribute_t *attr;               /* Current attribute */
+  char         buffer[1024];           /* Value buffer */
+
+
+  for (group = IPP_TAG_ZERO, attr = ippFirstAttribute(ipp);
+       attr;
+       attr = ippNextAttribute(ipp))
+  {
+    const char *name = ippGetName(attr);
+
+    if (!name)
+    {
+      group = IPP_TAG_ZERO;
+      continue;
+    }
+
+    if (group != ippGetGroupTag(attr))
+    {
+      group = ippGetGroupTag(attr);
+      fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(group));
+    }
+
+    if (!strcmp(name, "job-password"))
+      strlcpy(buffer, "---", sizeof(buffer));
+    else
+      ippAttributeString(attr, buffer, sizeof(buffer));
+
+    fprintf(stderr, "DEBUG: %s %s%s %s\n", name,
+            ippGetCount(attr) > 1 ? "1setOf " : "",
+            ippTagString(ippGetValueTag(attr)), buffer);
+  }
+
+  fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(IPP_TAG_END));
+}
+
+
 /*
  * 'monitor_printer()' - Monitor the printer state.
  */
@@ -2198,18 +2403,20 @@ monitor_printer(
   const char   *job_name;              /* Job name */
   ipp_jstate_t job_state;              /* Job state */
   const char   *job_user;              /* Job originating user name */
+  int          password_tries = 0;     /* Password tries */
 
 
  /*
   * Make a copy of the printer connection...
   */
 
-  http = _httpCreate(monitor->hostname, monitor->port, NULL, AF_UNSPEC,
-                     monitor->encryption, 1, _HTTP_MODE_CLIENT);
+  http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC,
+                      monitor->encryption, 1, 0, NULL);
   httpSetTimeout(http, 30.0, timeout_cb, NULL);
   if (username[0])
     cupsSetUser(username);
-  cupsSetPasswordCB2(password_cb, NULL);
+
+  cupsSetPasswordCB2((cups_password_cb2_t)password_cb, &password_tries);
 
  /*
   * Loop until the job is canceled, aborted, or completed.
@@ -2222,10 +2429,13 @@ monitor_printer(
   while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
   {
    /*
-    * Reconnect to the printer...
+    * Reconnect to the printer as needed...
     */
 
-    if (!httpReconnect(http))
+    if (httpGetFd(http) < 0)
+      httpReconnect(http);
+
+    if (httpGetFd(http) >= 0)
     {
      /*
       * Connected, so check on the printer state...
@@ -2235,6 +2445,17 @@ monitor_printer(
                                                    monitor->resource,
                                                   monitor->user,
                                                   monitor->version);
+      if (cupsLastError() <= IPP_OK_CONFLICT)
+        password_tries = 0;
+
+      if (monitor->job_id == 0 && monitor->create_job)
+      {
+       /*
+        * No job-id yet, so continue...
+       */
+
+        goto monitor_sleep;
+      }
 
      /*
       * Check the status of the job itself...
@@ -2243,8 +2464,7 @@ monitor_printer(
       job_op  = (monitor->job_id > 0 && monitor->get_job_attrs) ?
                     IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
       request = ippNewRequest(job_op);
-      request->request.op.version[0] = monitor->version / 10;
-      request->request.op.version[1] = monitor->version % 10;
+      ippSetVersion(request, monitor->version / 10, monitor->version % 10);
 
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                   NULL, monitor->uri);
@@ -2306,7 +2526,7 @@ monitor_printer(
               job_name = attr->values[0].string.text;
             else if (!strcmp(attr->name, "job-state") &&
                     attr->value_tag == IPP_TAG_ENUM)
-              job_state = attr->values[0].integer;
+              job_state = (ipp_jstate_t)attr->values[0].integer;
             else if (!strcmp(attr->name, "job-originating-user-name") &&
                     (attr->value_tag == IPP_TAG_NAME ||
                      attr->value_tag == IPP_TAG_NAMELANG))
@@ -2328,6 +2548,17 @@ monitor_printer(
         }
       }
 
+      fprintf(stderr, "DEBUG: (monitor) job-state = %s\n",
+              ippEnumString("job-state", monitor->job_state));
+
+      if (!job_canceled &&
+          (monitor->job_state == IPP_JOB_CANCELED ||
+          monitor->job_state == IPP_JOB_ABORTED))
+      {
+       job_canceled = -1;
+       fprintf(stderr, "DEBUG: (monitor) job_canceled = -1\n");
+      }
+
       if ((attr = ippFindAttribute(response, "job-state-reasons",
                                    IPP_TAG_KEYWORD)) != NULL)
       {
@@ -2349,6 +2580,9 @@ monitor_printer(
             new_reasons |= _CUPS_JSR_JOB_PASSWORD_WAIT;
           else if (!strcmp(attr->values[i].string.text, "job-release-wait"))
             new_reasons |= _CUPS_JSR_JOB_RELEASE_WAIT;
+         if (!job_canceled &&
+             (!strncmp(attr->values[i].string.text, "job-canceled-", 13) || !strcmp(attr->values[i].string.text, "aborted-by-system")))
+            job_canceled = 1;
         }
 
         if (new_reasons != monitor->job_reasons)
@@ -2374,26 +2608,22 @@ monitor_printer(
 
       ippDelete(response);
 
-      fprintf(stderr, "DEBUG: (monitor) job-state=%s\n",
+      fprintf(stderr, "DEBUG: (monitor) job-state = %s\n",
               ippEnumString("job-state", monitor->job_state));
 
       if (!job_canceled &&
           (monitor->job_state == IPP_JOB_CANCELED ||
           monitor->job_state == IPP_JOB_ABORTED))
        job_canceled = -1;
-
-     /*
-      * Disconnect from the printer - we'll reconnect on the next poll...
-      */
-
-      _httpDisconnect(http);
     }
 
    /*
     * Sleep for N seconds...
     */
 
-    sleep(delay);
+    monitor_sleep:
+
+    sleep((unsigned)delay);
 
     delay = _cupsNextDelay(delay, &prev_delay);
   }
@@ -2403,10 +2633,23 @@ monitor_printer(
   */
 
   if (job_canceled > 0 && monitor->job_id > 0)
-    if (!httpReconnect(http))
+  {
+    if (httpGetFd(http) < 0)
+      httpReconnect(http);
+
+    if (httpGetFd(http) >= 0)
+    {
       cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
                  monitor->user, monitor->version);
 
+      if (cupsLastError() > IPP_OK_CONFLICT)
+      {
+       fprintf(stderr, "DEBUG: (monitor) cancel_job() = %s\n", cupsLastErrorString());
+       _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
+      }
+    }
+  }
+
  /*
   * Cleanup and return...
   */
@@ -2437,31 +2680,19 @@ new_request(
     ppd_file_t      *ppd,              /* I - PPD file data */
     ipp_attribute_t *media_col_sup,    /* I - media-col-supported values */
     ipp_attribute_t *doc_handling_sup,  /* I - multiple-document-handling-supported values */
-    int             print_color_mode)  /* I - Printer supports print-color-mode */
+    ipp_attribute_t *print_color_mode_sup)
+                                       /* I - Printer supports print-color-mode */
 {
-  int          i;                      /* Looping var */
   ipp_t                *request;               /* Request data */
   const char   *keyword;               /* PWG keyword */
-  _pwg_size_t  *size;                  /* PWG media size */
-  ipp_t                *media_col,             /* media-col value */
-               *media_size;            /* media-size value */
-  const char   *media_source,          /* media-source value */
-               *media_type,            /* media-type value */
-               *collate_str,           /* multiple-document-handling value */
-               *mandatory;             /* Mandatory attributes */
-  ipp_tag_t    group;                  /* Current group */
-  ipp_attribute_t *attr;               /* Current attribute */
-  const char   *color_attr_name;       /* Supported color attribute */
-  char         buffer[1024];           /* Value buffer */
 
 
  /*
   * Create the IPP request...
   */
 
-  request                        = ippNewRequest(op);
-  request->request.op.version[0] = version / 10;
-  request->request.op.version[1] = version % 10;
+  request = ippNewRequest(op);
+  ippSetVersion(request, version / 10, version % 10);
 
   fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
          ippOpString(request->request.op.operation_id),
@@ -2472,36 +2703,31 @@ new_request(
   * Add standard attributes...
   */
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-              NULL, uri);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
   fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
 
   if (user && *user)
   {
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
-                "requesting-user-name", NULL, user);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, user);
     fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
   }
 
   if (title && *title)
   {
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
-                title);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title);
     fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
   }
 
   if (format && op != IPP_CREATE_JOB)
   {
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
-                "document-format", NULL, format);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
     fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
   }
 
 #ifdef HAVE_LIBZ
   if (compression && op != IPP_OP_CREATE_JOB && op != IPP_OP_VALIDATE_JOB)
   {
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                "compression", NULL, compression);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "compression", NULL, compression);
     fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
   }
 #endif /* HAVE_LIBZ */
@@ -2514,287 +2740,13 @@ new_request(
   {
     if (pc)
     {
-      int      num_finishings = 0,     /* Number of finishing values */
-               finishings[10];         /* Finishing enum values */
-      ppd_choice_t *choice;            /* Marked choice */
-
      /*
       * Send standard IPP attributes...
       */
 
       fputs("DEBUG: Adding standard IPP operation/job attributes.\n", stderr);
 
-      if (pc->password &&
-          (keyword = cupsGetOption("job-password", num_options,
-                                   options)) != NULL)
-      {
-        ippAddOctetString(request, IPP_TAG_OPERATION, "job-password",
-                          keyword, strlen(keyword));
-
-        if ((keyword = cupsGetOption("job-password-encryption", num_options,
-                                    options)) == NULL)
-         keyword = "none";
-
-        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                     "job-password-encryption", NULL, keyword);
-      }
-
-      if (pc->account_id &&
-          (keyword = cupsGetOption("job-account-id", num_options,
-                                   options)) != NULL)
-        ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id",
-                     NULL, keyword);
-
-      if (pc->account_id &&
-          (keyword = cupsGetOption("job-accounting-user-id", num_options,
-                                   options)) != NULL)
-        ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME,
-                     "job-accounting-user-id", NULL, keyword);
-
-      for (mandatory = (char *)cupsArrayFirst(pc->mandatory);
-           mandatory;
-           mandatory = (char *)cupsArrayNext(pc->mandatory))
-      {
-        if (strcmp(mandatory, "copies") &&
-            strcmp(mandatory, "destination-uris") &&
-            strcmp(mandatory, "finishings") &&
-            strcmp(mandatory, "job-account-id") &&
-            strcmp(mandatory, "job-accounting-user-id") &&
-            strcmp(mandatory, "job-password") &&
-            strcmp(mandatory, "job-password-encryption") &&
-            strcmp(mandatory, "media") &&
-            strncmp(mandatory, "media-col", 9) &&
-            strcmp(mandatory, "multiple-document-handling") &&
-            strcmp(mandatory, "output-bin") &&
-            strcmp(mandatory, "print-color-mode") &&
-            strcmp(mandatory, "print-quality") &&
-            strcmp(mandatory, "sides") &&
-            (keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
-       {
-         _ipp_option_t *opt = _ippFindOption(mandatory);
-                                       /* Option type */
-          ipp_tag_t    value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
-                                       /* Value type */
-
-          switch (value_tag)
-          {
-            case IPP_TAG_INTEGER :
-            case IPP_TAG_ENUM :
-                ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory,
-                              atoi(keyword));
-                break;
-            case IPP_TAG_BOOLEAN :
-                ippAddBoolean(request, IPP_TAG_JOB, mandatory,
-                              !_cups_strcasecmp(keyword, "true"));
-                break;
-            case IPP_TAG_RANGE :
-                {
-                  int lower, upper;    /* Range */
-
-                 if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
-                   lower = upper = atoi(keyword);
-
-                 ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
-                }
-                break;
-            case IPP_TAG_STRING :
-                ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword,
-                                  strlen(keyword));
-                break;
-            default :
-                ippAddString(request, IPP_TAG_JOB, value_tag, mandatory,
-                             NULL, keyword);
-                break;
-         }
-       }
-      }
-
-      if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
-       keyword = cupsGetOption("media", num_options, options);
-
-      if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
-      {
-       /*
-        * Add a media-col value...
-       */
-
-       media_size = ippNew();
-       ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
-                     "x-dimension", size->width);
-       ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
-                     "y-dimension", size->length);
-
-       media_col = ippNew();
-       ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
-
-       media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
-                                                           num_options,
-                                                           options));
-       media_type   = _ppdCacheGetType(pc, cupsGetOption("MediaType",
-                                                         num_options,
-                                                         options));
-
-       for (i = 0; i < media_col_sup->num_values; i ++)
-       {
-         if (!strcmp(media_col_sup->values[i].string.text,
-                     "media-left-margin"))
-           ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
-                         "media-left-margin", size->left);
-         else if (!strcmp(media_col_sup->values[i].string.text,
-                          "media-bottom-margin"))
-           ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
-                         "media-bottom-margin", size->bottom);
-         else if (!strcmp(media_col_sup->values[i].string.text,
-                          "media-right-margin"))
-           ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
-                         "media-right-margin", size->right);
-         else if (!strcmp(media_col_sup->values[i].string.text,
-                          "media-top-margin"))
-           ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
-                         "media-top-margin", size->top);
-         else if (!strcmp(media_col_sup->values[i].string.text,
-                          "media-source") && media_source)
-           ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
-                        "media-source", NULL, media_source);
-         else if (!strcmp(media_col_sup->values[i].string.text,
-                          "media-type") && media_type)
-           ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
-                        "media-type", NULL, media_type);
-       }
-
-       ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
-      }
-
-      if ((keyword = cupsGetOption("output-bin", num_options,
-                                  options)) == NULL)
-      {
-        if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL)
-         keyword = _ppdCacheGetBin(pc, choice->choice);
-      }
-
-      if (keyword)
-       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
-                    NULL, keyword);
-
-      color_attr_name = print_color_mode ? "print-color-mode" : "output-mode";
-
-      if ((keyword = cupsGetOption("print-color-mode", num_options,
-                                  options)) != NULL)
-       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name,
-                    NULL, keyword);
-      else if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
-      {
-       if (!_cups_strcasecmp(choice->choice, "Gray"))
-         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
-                      color_attr_name, NULL, "monochrome");
-       else
-         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
-                      color_attr_name, NULL, "color");
-      }
-
-      if ((keyword = cupsGetOption("print-quality", num_options,
-                                  options)) != NULL)
-       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
-                     atoi(keyword));
-      else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL)
-      {
-       if (!_cups_strcasecmp(choice->choice, "draft"))
-         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
-                       IPP_QUALITY_DRAFT);
-       else if (!_cups_strcasecmp(choice->choice, "normal"))
-         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
-                       IPP_QUALITY_NORMAL);
-       else if (!_cups_strcasecmp(choice->choice, "high"))
-         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
-                       IPP_QUALITY_HIGH);
-      }
-
-      if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
-       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
-                    NULL, keyword);
-      else if (pc->sides_option &&
-               (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
-      {
-       if (!_cups_strcasecmp(choice->choice, pc->sides_1sided))
-         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
-                      NULL, "one-sided");
-       else if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
-         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
-                      NULL, "two-sided-long-edge");
-       if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
-         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
-                      NULL, "two-sided-short-edge");
-      }
-
-      if ((keyword = cupsGetOption("multiple-document-handling",
-                                  num_options, options)) != NULL)
-      {
-        if (strstr(keyword, "uncollated"))
-          keyword = "false";
-        else
-          keyword = "true";
-      }
-      else if ((keyword = cupsGetOption("collate", num_options,
-                                        options)) == NULL)
-        keyword = "true";
-
-      if (format)
-      {
-        if (!_cups_strcasecmp(format, "image/gif") ||
-           !_cups_strcasecmp(format, "image/jp2") ||
-           !_cups_strcasecmp(format, "image/jpeg") ||
-           !_cups_strcasecmp(format, "image/png") ||
-           !_cups_strcasecmp(format, "image/tiff") ||
-           !_cups_strncasecmp(format, "image/x-", 8))
-       {
-        /*
-         * Collation makes no sense for single page image formats...
-         */
-
-         keyword = "false";
-       }
-       else if (!_cups_strncasecmp(format, "image/", 6) ||
-                !_cups_strcasecmp(format, "application/vnd.cups-raster"))
-       {
-        /*
-         * Multi-page image formats will have copies applied by the upstream
-         * filters...
-         */
-
-         copies = 1;
-       }
-      }
-
-      if (doc_handling_sup)
-      {
-        if (!_cups_strcasecmp(keyword, "true"))
-         collate_str = "separate-documents-collated-copies";
-       else
-         collate_str = "separate-documents-uncollated-copies";
-
-        for (i = 0; i < doc_handling_sup->num_values; i ++)
-         if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
-         {
-           ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
-                        "multiple-document-handling", NULL, collate_str);
-           break;
-          }
-
-        if (i >= doc_handling_sup->num_values)
-          copies = 1;
-      }
-
-     /*
-      * Map finishing options...
-      */
-
-      num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options,
-                                                   (int)(sizeof(finishings) /
-                                                         sizeof(finishings[0])),
-                                                   finishings);
-      if (num_finishings > 0)
-       ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings",
-                      num_finishings, finishings);
+      copies = _cupsConvertOptions(request, ppd, pc, media_col_sup, doc_handling_sup, print_color_mode_sup, user, format, copies, num_options, options);
 
      /*
       * Map FaxOut options...
@@ -2803,19 +2755,39 @@ new_request(
       if ((keyword = cupsGetOption("phone", num_options, options)) != NULL)
       {
        ipp_t   *destination;           /* destination collection */
-       char    tel_uri[1024];          /* tel: URI */
+       char    phone[1024],            /* Phone number string */
+               *ptr,                   /* Pointer into string */
+               tel_uri[1024];          /* tel: URI */
+        static const char * const allowed = "0123456789#*-+.()";
+                                       /* Allowed characters */
 
         destination = ippNew();
 
-        httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel",
-                        NULL, NULL, 0, keyword);
-        ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri",
-                     NULL, tel_uri);
+       /*
+        * Unescape and filter out spaces and other characters that are not
+        * allowed in a tel: URI.
+        */
+
+        _httpDecodeURI(phone, keyword, sizeof(phone));
+        for (ptr = phone; *ptr;)
+       {
+         if (!strchr(allowed, *ptr))
+           _cups_strcpy(ptr, ptr + 1);
+         else
+           ptr ++;
+        }
+
+        httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel", NULL, NULL, 0, phone);
+        ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri", NULL, tel_uri);
 
        if ((keyword = cupsGetOption("faxPrefix", num_options,
                                     options)) != NULL && *keyword)
-         ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT,
-                      "pre-dial-string", NULL, keyword);
+        {
+         char  predial[1024];          /* Pre-dial string */
+
+         _httpDecodeURI(predial, keyword, sizeof(predial));
+         ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT, "pre-dial-string", NULL, predial);
+       }
 
         ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination);
         ippDelete(destination);
@@ -2836,33 +2808,8 @@ new_request(
       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
   }
 
-  fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10,
-          ippOpString(ippGetOperation(request)), ippGetRequestId(request));
-  for (group = IPP_TAG_ZERO, attr = ippFirstAttribute(request);
-       attr;
-       attr = ippNextAttribute(request))
-  {
-    const char *name = ippGetName(attr);
-
-    if (!name)
-    {
-      group = IPP_TAG_ZERO;
-      continue;
-    }
-
-    if (group != ippGetGroupTag(attr))
-    {
-      group = ippGetGroupTag(attr);
-      fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(group));
-    }
-
-    ippAttributeString(attr, buffer, sizeof(buffer));
-    fprintf(stderr, "DEBUG: %s %s%s %s\n", name,
-            ippGetCount(attr) > 1 ? "1setOf " : "",
-            ippTagString(ippGetValueTag(attr)), buffer);
-  }
-
-  fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(IPP_TAG_END));
+  fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
+  debug_attributes(request);
 
   return (request);
 }
@@ -2877,38 +2824,44 @@ password_cb(const char *prompt,         /* I - Prompt (not used) */
             http_t     *http,          /* I - Connection */
             const char *method,                /* I - Request method (not used) */
             const char *resource,      /* I - Resource path (not used) */
-            void       *user_data)     /* I - User data (not used) */
+            int        *password_tries)        /* I - Password tries */
 {
   char def_username[HTTP_MAX_VALUE];   /* Default username */
 
 
-  fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\"), password=%p, "
-          "password_tries=%d\n", prompt, password, password_tries);
+  fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\", http=%p, method=\"%s\", "
+                  "resource=\"%s\", password_tries=%p(%d)), password=%p\n",
+          prompt, http, method, resource, password_tries, *password_tries,
+          password);
 
   (void)prompt;
   (void)method;
   (void)resource;
-  (void)user_data;
 
- /*
-  * Remember that we need to authenticate...
-  */
+  if (!uri_credentials)
+  {
+   /*
+    * Remember that we need to authenticate...
+    */
 
-  auth_info_required = "username,password";
+    auth_info_required = "username,password";
 
-  if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username",
-                      def_username))
-  {
-    char       quoted[HTTP_MAX_VALUE * 2 + 4];
-                                       /* Quoted string */
+    if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username",
+                       def_username))
+    {
+      char     quoted[HTTP_MAX_VALUE * 2 + 4];
+                                         /* Quoted string */
 
-    fprintf(stderr, "ATTR: auth-info-default=%s,\n",
-            quote_string(def_username, quoted, sizeof(quoted)));
+      fprintf(stderr, "ATTR: auth-info-default=%s,\n",
+             quote_string(def_username, quoted, sizeof(quoted)));
+    }
   }
 
-  if (password && *password && password_tries < 3)
+  if (password && *password && *password_tries < 3)
   {
-    password_tries ++;
+    (*password_tries) ++;
+
+    cupsSetUser(username);
 
     return (password);
   }
@@ -2952,7 +2905,7 @@ quote_string(const char *s,               /* I - String */
   {
     if (*s == '\\' || *s == '\"' || *s == '\'')
     {
-      if (q < (qend - 3))
+      if (qptr < (qend - 4))
       {
        *qptr++ = '\\';
        *qptr++ = '\\';
@@ -3001,16 +2954,14 @@ report_attr(ipp_attribute_t *attr)      /* I - Attribute */
     {
       case IPP_TAG_INTEGER :
       case IPP_TAG_ENUM :
-          snprintf(valptr, sizeof(value) - (valptr - value), "%d",
-                  attr->values[i].integer);
+          snprintf(valptr, sizeof(value) - (size_t)(valptr - value), "%d", attr->values[i].integer);
          valptr += strlen(valptr);
          break;
 
       case IPP_TAG_TEXT :
       case IPP_TAG_NAME :
       case IPP_TAG_KEYWORD :
-          quote_string(attr->values[i].string.text, valptr,
-                       value + sizeof(value) - valptr);
+          quote_string(attr->values[i].string.text, valptr, (size_t)(value + sizeof(value) - valptr));
           valptr += strlen(valptr);
           break;
 
@@ -3052,6 +3003,7 @@ report_printer_state(ipp_t *ipp)  /* I - IPP response */
 {
   ipp_attribute_t      *pa,            /* printer-alert */
                        *pam,           /* printer-alert-message */
+                       *pmja,          /* printer-mandatory-job-attributes */
                        *psm,           /* printer-state-message */
                        *reasons,       /* printer-state-reasons */
                        *marker;        /* marker-* attributes */
@@ -3072,6 +3024,26 @@ report_printer_state(ipp_t *ipp) /* I - IPP response */
                               IPP_TAG_TEXT)) != NULL)
     report_attr(pam);
 
+  if ((pmja = ippFindAttribute(ipp, "printer-mandatory-job-attributes", IPP_TAG_KEYWORD)) != NULL)
+  {
+    int        i,                              /* Looping var */
+       count = ippGetCount(pmja);      /* Number of values */
+
+    for (i = 0, valptr = value; i < count; i ++, valptr += strlen(valptr))
+    {
+      if (i)
+        snprintf(valptr, sizeof(value) - (size_t)(valptr - value), " %s", ippGetString(pmja, i, NULL));
+      else
+        strlcpy(value, ippGetString(pmja, i, NULL), sizeof(value));
+    }
+
+    if (strcmp(value, mandatory_attrs))
+    {
+      strlcpy(mandatory_attrs, value, sizeof(mandatory_attrs));
+      fprintf(stderr, "PPD: cupsMandatory=\"%s\"\n", value);
+    }
+  }
+
   if ((psm = ippFindAttribute(ipp, "printer-state-message",
                               IPP_TAG_TEXT)) != NULL)
   {
@@ -3261,12 +3233,12 @@ run_as_user(char       *argv[],         /* I - Command-line arguments */
 
   if (response)
   {
-    child_pid = xpc_dictionary_get_int64(response, "child-pid");
+    child_pid = (pid_t)xpc_dictionary_get_int64(response, "child-pid");
 
     xpc_release(response);
 
     if (child_pid)
-      fprintf(stderr, "DEBUG: Child PID=%d.\n", child_pid);
+      fprintf(stderr, "DEBUG: Child PID=%d.\n", (int)child_pid);
     else
     {
       _cupsLangPrintFilter(stderr, "ERROR",
@@ -3312,7 +3284,7 @@ run_as_user(char       *argv[],           /* I - Command-line arguments */
 
   if (response)
   {
-    status = xpc_dictionary_get_int64(response, "status");
+    status = (int)xpc_dictionary_get_int64(response, "status");
 
     if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
     {
@@ -3340,7 +3312,6 @@ run_as_user(char       *argv[],           /* I - Command-line arguments */
 
   if (conn)
   {
-    xpc_connection_suspend(conn);
     xpc_connection_cancel(conn);
     xpc_release(conn);
   }
@@ -3375,7 +3346,7 @@ sigterm_handler(int sig)          /* I - Signal */
     * Flag that the job should be canceled...
     */
 
-    write(2, "DEBUG: job_canceled = 1.\n", 25);
+    write(2, "DEBUG: sigterm_handler: job_canceled = 1.\n", 25);
 
     job_canceled = 1;
     return;
@@ -3389,7 +3360,7 @@ sigterm_handler(int sig)          /* I - Signal */
   if (tmpfilename[0])
     unlink(tmpfilename);
 
-  exit(1);
+  _exit(1);
 }
 
 
@@ -3512,8 +3483,7 @@ update_reasons(ipp_attribute_t *attr,     /* I - printer-state-reasons or NULL */
               temp = (char *)cupsArrayNext(state_reasons))
            if (!strncmp(temp, "cups-remote-", 12))
            {
-             snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
-                      temp);
+             snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, temp);
              remptr    += strlen(remptr);
              remprefix = ",";
 
@@ -3526,8 +3496,7 @@ update_reasons(ipp_attribute_t *attr,     /* I - printer-state-reasons or NULL */
 
         cupsArrayAdd(state_reasons, reason);
 
-        snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
-                reason);
+        snprintf(addptr, sizeof(add) - (size_t)(addptr - add), "%s%s", addprefix, reason);
        addptr    += strlen(addptr);
        addprefix = ",";
       }
@@ -3545,8 +3514,7 @@ update_reasons(ipp_attribute_t *attr,     /* I - printer-state-reasons or NULL */
     {
       if (cupsArrayFind(state_reasons, reason))
       {
-       snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
-                reason);
+       snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, reason);
        remptr    += strlen(remptr);
        remprefix = ",";
 
@@ -3566,8 +3534,7 @@ update_reasons(ipp_attribute_t *attr,     /* I - printer-state-reasons or NULL */
     {
       if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
       {
-       snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
-                reason);
+       snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, reason);
        remptr    += strlen(remptr);
        remprefix = ",";
 
@@ -3583,8 +3550,7 @@ update_reasons(ipp_attribute_t *attr,     /* I - printer-state-reasons or NULL */
       {
         cupsArrayAdd(state_reasons, reason);
 
-        snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
-                reason);
+        snprintf(addptr, sizeof(add) - (size_t)(addptr - add), "%s%s", addprefix, reason);
        addptr    += strlen(addptr);
        addprefix = ",";
       }
@@ -3604,7 +3570,3 @@ update_reasons(ipp_attribute_t *attr,     /* I - printer-state-reasons or NULL */
   else if (rem[0])
     fprintf(stderr, "%s\n", rem);
 }
-
-/*
- * End of "$Id: ipp.c 9759 2011-05-11 03:24:33Z mike $".
- */