]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/cups-lpd.c
Import CUPS v2.0.3
[thirdparty/cups.git] / scheduler / cups-lpd.c
index 25c9e8b8ddc3ef278e21c265a9e62b16a282d6cc..7ce14ef62839589cd5ab9ba752c9e82c1c0ff4dd 100644 (file)
@@ -1,56 +1,35 @@
 /*
- * "$Id: cups-lpd.c 4807 2005-10-21 19:17:52Z mike $"
+ * "$Id: cups-lpd.c 12612 2015-05-06 15:30:50Z msweet $"
  *
- *   Line Printer Daemon interface for the Common UNIX Printing System (CUPS).
+ * Line Printer Daemon interface for CUPS.
  *
- *   Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
  *
- *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
- *
- * Contents:
- *
- *   main()             - Process an incoming LPD request...
- *   print_file()       - Print a file to a printer or class.
- *   recv_print_job()   - Receive a print job from the client.
- *   remove_jobs()      - Cancel one or more jobs.
- *   send_state()       - Send the queue state.
- *   smart_gets()       - Get a line of text, removing the trailing CR
- *                        and/or LF.
+ * 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"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
  */
 
 /*
  * Include necessary headers...
  */
 
-#include <cups/cups.h>
-#include <cups/string.h>
-#include <cups/language.h>
-#include <stdlib.h>
-#include <errno.h>
+#include <cups/cups-private.h>
 #include <syslog.h>
-#include <ctype.h>
 #include <unistd.h>
 #include <fcntl.h>
-
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
 
+#ifdef HAVE_INTTYPES_H
+#  include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+
 
 /*
  * LPD "mini-daemon" for CUPS.  This program must be used in conjunction
  * Prototypes...
  */
 
-int    print_file(const char *name, const char *file,
-                  const char *title, const char *docname,
-                  const char *user, int num_options,
-                  cups_option_t *options);
-int    recv_print_job(const char *dest, int num_defaults, cups_option_t *defaults);
-int    remove_jobs(const char *dest, const char *agent, const char *list);
-int    send_state(const char *dest, const char *list, int longstatus);
-char   *smart_gets(char *s, int len, FILE *fp);
+static int     create_job(http_t *http, const char *dest, const char *title,
+                           const char *docname, const char *user,
+                          int num_options, cups_option_t *options);
+static int     get_printer(http_t *http, const char *name, char *dest,
+                           size_t destsize, cups_option_t **options,
+                           int *accepting, int *shared, ipp_pstate_t *state);
+static int     print_file(http_t *http, int id, const char *filename,
+                          const char *docname, const char *user,
+                          const char *format, int last);
+static int     recv_print_job(const char *name, int num_defaults,
+                              cups_option_t *defaults);
+static int     remove_jobs(const char *name, const char *agent,
+                           const char *list);
+static int     send_state(const char *name, const char *list,
+                          int longstatus);
+static char    *smart_gets(char *s, int len, FILE *fp);
 
 
 /*
@@ -108,6 +95,7 @@ main(int  argc,                              /* I - Number of command-line arguments */
   char         hostname[256],          /* Name of client */
                hostip[256],            /* IP address */
                *hostfamily;            /* Address family */
+  int          hostlookups;            /* Do hostname lookups? */
 
 
  /*
@@ -122,47 +110,32 @@ main(int  argc,                           /* I - Number of command-line arguments */
 
   openlog("cups-lpd", LOG_PID, LOG_LPR);
 
- /*
-  * Get the address of the client...
-  */
-
-  hostlen = sizeof(hostaddr);
-
-  if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
-  {
-    syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
-    strcpy(hostname, "unknown");
-  }
-  else
-  {
-    httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
-    httpAddrString(&hostaddr, hostip, sizeof(hostip));
-
-#ifdef AF_INET6
-    if (hostaddr.addr.sa_family == AF_INET6)
-      hostfamily = "IPv6";
-    else
-#endif /* AF_INET6 */
-    hostfamily = "IPv4";
-
-    syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily, hostip);
-  }
-
  /*
   * Scan the command-line for options...
   */
 
   num_defaults = 0;
   defaults     = NULL;
-
-  num_defaults = cupsAddOption("job-originating-host-name", hostname,
-                               num_defaults, &defaults);
+  hostlookups  = 1;
 
   for (i = 1; i < argc; i ++)
     if (argv[i][0] == '-')
     {
       switch (argv[i][1])
       {
+        case 'h' : /* -h hostname[:port] */
+            if (argv[i][2])
+             cupsSetServer(argv[i] + 2);
+           else
+           {
+             i ++;
+             if (i < argc)
+               cupsSetServer(argv[i]);
+             else
+               syslog(LOG_WARNING, "Expected hostname string after -h option!");
+           }
+           break;
+
        case 'o' : /* Option */
            if (argv[i][2])
              num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
@@ -171,18 +144,59 @@ main(int  argc,                           /* I - Number of command-line arguments */
            {
              i ++;
              if (i < argc)
-               num_defaults = cupsParseOptions(argv[i], num_defaults, &defaults);
+               num_defaults = cupsParseOptions(argv[i], num_defaults,
+                                               &defaults);
               else
                syslog(LOG_WARNING, "Expected option string after -o option!");
             }
            break;
+
+        case 'n' : /* Don't do hostname lookups */
+           hostlookups = 0;
+           break;
+
        default :
            syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
            break;
       }
     }
     else
-      syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!", argv[i]);
+      syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!",
+             argv[i]);
+
+ /*
+  * Get the address of the client...
+  */
+
+  hostlen = sizeof(hostaddr);
+
+  if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
+  {
+    syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
+    strlcpy(hostname, "unknown", sizeof(hostname));
+  }
+  else
+  {
+    httpAddrString(&hostaddr, hostip, sizeof(hostip));
+
+    if (hostlookups)
+      httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
+    else
+      strlcpy(hostname, hostip, sizeof(hostname));
+
+#ifdef AF_INET6
+    if (hostaddr.addr.sa_family == AF_INET6)
+      hostfamily = "IPv6";
+    else
+#endif /* AF_INET6 */
+    hostfamily = "IPv4";
+
+    syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily,
+           hostip);
+  }
+
+  num_defaults = cupsAddOption("job-originating-host-name", hostname,
+                               num_defaults, &defaults);
 
  /*
   * RFC1179 specifies that only 1 daemon command can be received for
@@ -205,13 +219,20 @@ main(int  argc,                           /* I - Number of command-line arguments */
   * resource list, and/or user name.
   */
 
-  command = line[0];
-  dest    = line + 1;
+  if ((command = line[0]) == '\0')
+    dest = line;
+  else
+    dest = line + 1;
 
-  for (list = dest + 1; *list && !isspace(*list & 255); list ++);
+  if (command == 0x02)
+    list = NULL;
+  else
+  {
+    for (list = dest; *list && !isspace(*list & 255); list ++);
 
-  while (isspace(*list & 255))
-    *list++ = '\0';
+    while (isspace(*list & 255))
+      *list++ = '\0';
+  }
 
  /*
   * Do the command...
@@ -238,37 +259,42 @@ main(int  argc,                           /* I - Number of command-line arguments */
         syslog(LOG_INFO, "Receive print job for %s", dest);
         /* recv_print_job() sends initial status byte */
 
-        status = recv_print_job(dest, num_defaults, defaults);
+        status = (char)recv_print_job(dest, num_defaults, defaults);
        break;
 
     case 0x03 : /* Send queue state (short) */
         syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
        /* no status byte for this command */
 
-        status = send_state(dest, list, 0);
+        status = (char)send_state(dest, list, 0);
        break;
 
     case 0x04 : /* Send queue state (long) */
         syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
        /* no status byte for this command */
 
-        status = send_state(dest, list, 1);
+        status = (char)send_state(dest, list, 1);
        break;
 
     case 0x05 : /* Remove jobs */
-       /*
-        * Grab the agent and skip to the list of users and/or jobs.
-       */
+        if (list)
+       {
+        /*
+         * Grab the agent and skip to the list of users and/or jobs.
+         */
 
-        agent = list;
+         agent = list;
 
-       for (; *list && !isspace(*list & 255); list ++);
-       while (isspace(*list & 255))
-         *list++ = '\0';
+         for (; *list && !isspace(*list & 255); list ++);
+         while (isspace(*list & 255))
+           *list++ = '\0';
 
-        syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
+         syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
 
-        status = remove_jobs(dest, agent, list);
+         status = (char)remove_jobs(dest, agent, list);
+        }
+       else
+         status = 1;
 
        putchar(status);
        break;
@@ -282,220 +308,451 @@ main(int  argc,                         /* I - Number of command-line arguments */
 
 
 /*
- * 'check_printer()' - Check that a printer exists and is accepting jobs.
+ * 'create_job()' - Create a new print job.
  */
 
-int                                    /* O - Job ID */
-check_printer(const char *name)                /* I - Printer or class name */
+static int                             /* O - Job ID or -1 on error */
+create_job(http_t        *http,                /* I - HTTP connection */
+           const char    *dest,                /* I - Destination name */
+          const char    *title,        /* I - job-name */
+          const char    *docname,      /* I - Name of job file */
+           const char    *user,                /* I - requesting-user-name */
+          int           num_options,   /* I - Number of options for job */
+          cups_option_t *options)      /* I - Options for job */
 {
-  http_t       *http;                  /* Connection to server */
   ipp_t                *request;               /* IPP request */
   ipp_t                *response;              /* IPP response */
-  ipp_attribute_t *attr;               /* IPP job-id attribute */
+  ipp_attribute_t *attr;               /* IPP attribute */
   char         uri[HTTP_MAX_URI];      /* Printer URI */
-  cups_lang_t  *language;              /* Language to use */
-  int          accepting;              /* printer-is-accepting-jobs value */
-
-
- /*
-  * Setup a connection and request data...
-  */
+  int          id;                     /* Job ID */
 
-  if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
-                                 cupsEncryption())) == NULL)
-  {
-    syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
-           strerror(errno));
-    return (0);
-  }
 
  /*
-  * Build a standard CUPS URI for the printer and fill the standard IPP
-  * attributes...
+  * Setup the Create-Job request...
   */
 
-  if ((request = ippNew()) == NULL)
-  {
-    syslog(LOG_ERR, "Unable to create request: %s", strerror(errno));
-    httpClose(http);
-    return (0);
-  }
-
-  request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
-  request->request.op.request_id   = 1;
+  request = ippNewRequest(IPP_CREATE_JOB);
 
-  httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
-                   "/printers/%s", name);
+  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                   "localhost", 0, "/printers/%s", dest);
 
-  language = cupsLangDefault();
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, uri);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+               "requesting-user-name", NULL, user);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL,
-               language != NULL ? language->language : "C");
+  if (title[0])
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
+                 NULL, title);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, uri);
+  if (docname[0])
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
+                 NULL, docname);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requested-attributes",
-               NULL, "printer-is-accepting-jobs");
+  cupsEncodeOptions(request, num_options, options);
 
  /*
   * Do the request...
   */
 
-  response = cupsDoRequest(http, request, "/");
+  snprintf(uri, sizeof(uri), "/printers/%s", dest);
 
-  if (response == NULL)
-  {
-    syslog(LOG_ERR, "Unable to check printer status - %s",
-           ippErrorString(cupsLastError()));
-    accepting = 0;
-  }
-  else if (response->request.status.status_code > IPP_OK_CONFLICT)
+  response = cupsDoRequest(http, request, uri);
+
+  if (!response || cupsLastError() > IPP_OK_CONFLICT)
   {
-    syslog(LOG_ERR, "Unable to check printer status - %s",
-           ippErrorString(response->request.status.status_code));
-    accepting = 0;
+    syslog(LOG_ERR, "Unable to create job - %s", cupsLastErrorString());
+
+    ippDelete(response);
+
+    return (-1);
   }
-  else if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
-                                    IPP_TAG_BOOLEAN)) == NULL)
+
+ /*
+  * Get the job-id value from the response and return it...
+  */
+
+  if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
   {
-    syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in response from server!");
-    accepting = 0;
+    id = -1;
+
+    syslog(LOG_ERR, "No job-id attribute found in response from server!");
   }
   else
-    accepting = attr->values[0].boolean;
+  {
+    id = attr->values[0].integer;
 
-  if (response != NULL)
-    ippDelete(response);
+    syslog(LOG_INFO, "Print file - job ID = %d", id);
+  }
 
-  httpClose(http);
-  cupsLangFree(language);
+  ippDelete(response);
 
-  return (accepting);
+  return (id);
 }
 
 
 /*
- * 'print_file()' - Print a file to a printer or class.
+ * 'get_printer()' - Get the named printer and its options.
  */
 
-int                                    /* O - Job ID */
-print_file(const char    *name,                /* I - Printer or class name */
-           const char    *file,                /* I - File to print */
-           const char    *title,       /* I - Title of job */
-           const char    *docname,     /* I - Name of job file */
-           const char    *user,                /* I - Owner of job */
-           int           num_options,  /* I - Number of options */
-          cups_option_t *options)      /* I - Options */
+static int                             /* O - Number of options or -1 on error */
+get_printer(http_t        *http,       /* I - HTTP connection */
+            const char    *name,       /* I - Printer name from request */
+           char          *dest,        /* I - Destination buffer */
+            size_t        destsize,    /* I - Size of destination buffer */
+           cups_option_t **options,    /* O - Printer options */
+           int           *accepting,   /* O - printer-is-accepting-jobs value */
+           int           *shared,      /* O - printer-is-shared value */
+           ipp_pstate_t  *state)       /* O - printer-state value */
 {
-  http_t       *http;                  /* Connection to server */
+  int          num_options;            /* Number of options */
+  cups_file_t  *fp;                    /* lpoptions file */
+  char         line[1024],             /* Line from lpoptions file */
+               *value,                 /* Pointer to value on line */
+               *optptr;                /* Pointer to options on line */
+  int          linenum;                /* Line number in file */
+  const char   *cups_serverroot;       /* CUPS_SERVERROOT env var */
   ipp_t                *request;               /* IPP request */
   ipp_t                *response;              /* IPP response */
-  ipp_attribute_t *attr;               /* IPP job-id attribute */
+  ipp_attribute_t *attr;               /* IPP attribute */
   char         uri[HTTP_MAX_URI];      /* Printer URI */
-  cups_lang_t  *language;              /* Language to use */
-  int          jobid;                  /* New job ID */
+  static const char * const requested[] =
+               {                       /* Requested attributes */
+                 "printer-info",
+                 "printer-is-accepting-jobs",
+                 "printer-is-shared",
+                 "printer-name",
+                 "printer-state"
+               };
 
 
  /*
-  * Setup a connection and request data...
+  * Initialize everything...
   */
 
-  if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
-                                 cupsEncryption())) == NULL)
+  if (accepting)
+    *accepting = 0;
+  if (shared)
+    *shared = 0;
+  if (state)
+    *state = IPP_PRINTER_STOPPED;
+  if (options)
+    *options = NULL;
+
+ /*
+  * See if the name is a queue name optionally with an instance name.
+  */
+
+  strlcpy(dest, name, destsize);
+  if ((value = strchr(dest, '/')) != NULL)
+    *value = '\0';
+
+ /*
+  * Setup the Get-Printer-Attributes request...
+  */
+
+  request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+
+  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                  "localhost", 0, "/printers/%s", dest);
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+              NULL, uri);
+
+  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+               "requested-attributes",
+               (int)(sizeof(requested) / sizeof(requested[0])),
+               NULL, requested);
+
+ /*
+  * Do the request...
+  */
+
+  response = cupsDoRequest(http, request, "/");
+
+  if (!response || cupsLastError() > IPP_OK_CONFLICT)
   {
-    syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
-           strerror(errno));
-    return (0);
+   /*
+    * If we can't find the printer by name, look up the printer-name
+    * using the printer-info values...
+    */
+
+    ipp_attribute_t    *accepting_attr,/* printer-is-accepting-jobs */
+                       *info_attr,     /* printer-info */
+                       *name_attr,     /* printer-name */
+                       *shared_attr,   /* printer-is-shared */
+                       *state_attr;    /* printer-state */
+
+
+    ippDelete(response);
+
+   /*
+    * Setup the CUPS-Get-Printers request...
+    */
+
+    request = ippNewRequest(CUPS_GET_PRINTERS);
+
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                  "requested-attributes",
+                 (int)(sizeof(requested) / sizeof(requested[0])),
+                  NULL, requested);
+
+   /*
+    * Do the request...
+    */
+
+    response = cupsDoRequest(http, request, "/");
+
+    if (!response || cupsLastError() > IPP_OK_CONFLICT)
+    {
+      syslog(LOG_ERR, "Unable to get list of printers - %s",
+             cupsLastErrorString());
+
+      ippDelete(response);
+
+      return (-1);
+    }
+
+   /*
+    * Scan the response for printers...
+    */
+
+    *dest = '\0';
+    attr  = response->attrs;
+
+    while (attr)
+    {
+     /*
+      * Skip to the next printer...
+      */
+
+      while (attr && attr->group_tag != IPP_TAG_PRINTER)
+        attr = attr->next;
+
+      if (!attr)
+        break;
+
+     /*
+      * Get all of the attributes for the current printer...
+      */
+
+      accepting_attr = NULL;
+      info_attr      = NULL;
+      name_attr      = NULL;
+      shared_attr    = NULL;
+      state_attr     = NULL;
+
+      while (attr && attr->group_tag == IPP_TAG_PRINTER)
+      {
+        if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
+           attr->value_tag == IPP_TAG_BOOLEAN)
+         accepting_attr = attr;
+       else if (!strcmp(attr->name, "printer-info") &&
+                attr->value_tag == IPP_TAG_TEXT)
+         info_attr = attr;
+       else if (!strcmp(attr->name, "printer-name") &&
+                attr->value_tag == IPP_TAG_NAME)
+         name_attr = attr;
+       else if (!strcmp(attr->name, "printer-is-shared") &&
+                attr->value_tag == IPP_TAG_BOOLEAN)
+         shared_attr = attr;
+       else if (!strcmp(attr->name, "printer-state") &&
+                attr->value_tag == IPP_TAG_ENUM)
+         state_attr = attr;
+
+        attr = attr->next;
+      }
+
+      if (info_attr && name_attr &&
+          !_cups_strcasecmp(name, info_attr->values[0].string.text))
+      {
+       /*
+        * Found a match, use this one!
+       */
+
+       strlcpy(dest, name_attr->values[0].string.text, destsize);
+
+       if (accepting && accepting_attr)
+         *accepting = accepting_attr->values[0].boolean;
+
+       if (shared && shared_attr)
+         *shared = shared_attr->values[0].boolean;
+
+       if (state && state_attr)
+         *state = (ipp_pstate_t)state_attr->values[0].integer;
+
+        break;
+      }
+    }
+
+    ippDelete(response);
+
+    if (!*dest)
+    {
+      syslog(LOG_ERR, "Unable to find \"%s\" in list of printers!", name);
+
+      return (-1);
+    }
+
+    name = dest;
+  }
+  else
+  {
+   /*
+    * Get values from the response...
+    */
+
+    if (accepting)
+    {
+      if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
+                                  IPP_TAG_BOOLEAN)) == NULL)
+       syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in "
+                       "response from server!");
+      else
+       *accepting = attr->values[0].boolean;
+    }
+
+    if (shared)
+    {
+      if ((attr = ippFindAttribute(response, "printer-is-shared",
+                                  IPP_TAG_BOOLEAN)) == NULL)
+      {
+       syslog(LOG_ERR, "No printer-is-shared attribute found in "
+                       "response from server!");
+       *shared = 1;
+      }
+      else
+       *shared = attr->values[0].boolean;
+    }
+
+    if (state)
+    {
+      if ((attr = ippFindAttribute(response, "printer-state",
+                                  IPP_TAG_ENUM)) == NULL)
+       syslog(LOG_ERR, "No printer-state attribute found in "
+                       "response from server!");
+      else
+       *state = (ipp_pstate_t)attr->values[0].integer;
+    }
+
+    ippDelete(response);
   }
 
  /*
-  * Build a standard CUPS URI for the printer and fill the standard IPP
-  * attributes...
+  * Next look for the printer in the lpoptions file...
   */
 
-  if ((request = ippNew()) == NULL)
+  num_options = 0;
+
+  if (options && shared && accepting)
   {
-    syslog(LOG_ERR, "Unable to create request: %s", strerror(errno));
-    httpClose(http);
-    return (0);
+    if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
+      cups_serverroot = CUPS_SERVERROOT;
+
+    snprintf(line, sizeof(line), "%s/lpoptions", cups_serverroot);
+    if ((fp = cupsFileOpen(line, "r")) != NULL)
+    {
+      linenum = 0;
+      while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+      {
+       /*
+       * Make sure we have "Dest name options" or "Default name options"...
+       */
+
+       if ((_cups_strcasecmp(line, "Dest") && _cups_strcasecmp(line, "Default")) || !value)
+          continue;
+
+       /*
+       * Separate destination name from options...
+       */
+
+       for (optptr = value; *optptr && !isspace(*optptr & 255); optptr ++);
+
+       while (*optptr == ' ')
+         *optptr++ = '\0';
+
+       /*
+       * If this is our destination, parse the options and break out of
+       * the loop - we're done!
+       */
+
+       if (!_cups_strcasecmp(value, name))
+       {
+          num_options = cupsParseOptions(optptr, num_options, options);
+         break;
+       }
+      }
+
+      cupsFileClose(fp);
+    }
   }
+  else if (options)
+    *options = NULL;
+
+ /*
+  * Return the number of options for this destination...
+  */
+
+  return (num_options);
+}
 
-  request->request.op.operation_id = IPP_PRINT_JOB;
-  request->request.op.request_id   = 1;
 
-  httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
-                   "/printers/%s", name);
+/*
+ * 'print_file()' - Add a file to the current job.
+ */
 
-  language = cupsLangDefault();
+static int                             /* O - 0 on success, -1 on failure */
+print_file(http_t     *http,           /* I - HTTP connection */
+           int        id,              /* I - Job ID */
+          const char *filename,        /* I - File to print */
+           const char *docname,                /* I - document-name */
+          const char *user,            /* I - requesting-user-name */
+          const char *format,          /* I - document-format */
+          int        last)             /* I - 1 = last file in job */
+{
+  ipp_t                *request;               /* IPP request */
+  char         uri[HTTP_MAX_URI];      /* Printer URI */
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL,
-               language != NULL ? language->language : "C");
+ /*
+  * Setup the Send-Document request...
+  */
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, uri);
+  request = ippNewRequest(IPP_SEND_DOCUMENT);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-               NULL, user);
+  snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+               "requesting-user-name", NULL, user);
 
-  if (title)
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title);
   if (docname)
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", NULL, docname);
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                "document-name", NULL, docname);
 
- /*
-  * Then add all options on the command-line...
-  */
+  if (format)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
+                 "document-format", NULL, format);
 
-  cupsEncodeOptions(request, num_options, options);
+  ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last);
 
  /*
   * Do the request...
   */
 
-  snprintf(uri, sizeof(uri), "/printers/%s", name);
+  snprintf(uri, sizeof(uri), "/jobs/%d", id);
 
-  response = cupsDoFileRequest(http, request, uri, file);
+  ippDelete(cupsDoFileRequest(http, request, uri, filename));
 
-  if (response == NULL)
-  {
-    syslog(LOG_ERR, "Unable to print file - %s",
-           ippErrorString(cupsLastError()));
-    jobid = 0;
-  }
-  else if (response->request.status.status_code > IPP_OK_CONFLICT)
-  {
-    syslog(LOG_ERR, "Unable to print file - %s",
-           ippErrorString(response->request.status.status_code));
-    jobid = 0;
-  }
-  else if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
-  {
-    syslog(LOG_ERR, "No job-id attribute found in response from server!");
-    jobid = 0;
-  }
-  else
+  if (cupsLastError() > IPP_OK_CONFLICT)
   {
-    jobid = attr->values[0].integer;
+    syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString());
 
-    syslog(LOG_INFO, "Print file - job ID = %d", jobid);
+    return (-1);
   }
 
-  if (response != NULL)
-    ippDelete(response);
-
-  httpClose(http);
-  cupsLangFree(language);
-
-  return (jobid);
+  return (0);
 }
 
 
@@ -503,87 +760,89 @@ print_file(const char    *name,           /* I - Printer or class name */
  * 'recv_print_job()' - Receive a print job from the client.
  */
 
-int                                    /* O - Command status */
-recv_print_job(const char    *dest,    /* I - Destination */
-               int           num_defaults,
-                                       /* I - Number of default options */
-              cups_option_t *defaults) /* I - Default options */
+static int                             /* O - Command status */
+recv_print_job(
+    const char    *queue,              /* I - Printer name */
+    int           num_defaults,                /* I - Number of default options */
+    cups_option_t *defaults)           /* I - Default options */
 {
+  http_t       *http;                  /* HTTP connection */
   int          i;                      /* Looping var */
   int          status;                 /* Command status */
   int          fd;                     /* Temporary file */
   FILE         *fp;                    /* File pointer */
   char         filename[1024];         /* Temporary filename */
-  int          bytes;                  /* Bytes received */
+  ssize_t      bytes;                  /* Bytes received */
+  size_t       total;                  /* Total bytes */
   char         line[256],              /* Line from file/stdin */
                command,                /* Command from line */
                *count,                 /* Number of bytes */
                *name;                  /* Name of file */
-  const char   *cupsd_job_sheets;              /* Job sheets */
+  const char   *job_sheets;            /* Job sheets */
   int          num_data;               /* Number of data files */
   char         control[1024],          /* Control filename */
-               data[32][256],          /* Data files */
-               temp[32][1024];         /* Temporary files */
+               data[100][256],         /* Data files */
+               temp[100][1024];        /* Temporary files */
   char         user[1024],             /* User name */
                title[1024],            /* Job title */
                docname[1024],          /* Document name */
-               queue[256],             /* Printer/class queue */
-               *instance;              /* Printer/class instance */
-  int          num_dests;              /* Number of destinations */
-  cups_dest_t  *dests,                 /* Destinations */
-               *destptr;               /* Current destination */
-  int          num_options;            /* Number of options */
+               dest[256];              /* Printer/class queue */
+  int          accepting,              /* printer-is-accepting */
+               shared,                 /* printer-is-shared */
+               num_options;            /* Number of options */
   cups_option_t        *options;               /* Options */
-  int          banner;                 /* Print banner? */
-
-
-  status   = 0;
-  num_data = 0;
-  fd       = -1;
-
-  control[0] = '\0';
+  int          id;                     /* Job ID */
+  int          docnumber,              /* Current document number */
+               doccount;               /* Count of documents */
 
-  strlcpy(queue, dest, sizeof(queue));
 
-  if ((instance = strrchr(queue, '/')) != NULL)
-    *instance++ = '\0';
+ /*
+  * Connect to the server...
+  */
 
-  num_dests = cupsGetDests(&dests);
-  if ((destptr = cupsGetDest(queue, instance, num_dests, dests)) == NULL)
+  http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
+  if (!http)
   {
-   /*
-    * If the queue name is blank or "lp" then use the default queue.
-    */
-
-    if (!queue[0] || !strcmp(queue, "lp"))
-      if ((destptr = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
-       strlcpy(queue, destptr->name, sizeof(queue));
+    syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
 
-    if (destptr == NULL)
-    {
-      if (instance)
-       syslog(LOG_ERR, "Unknown destination %s/%s!", queue, instance);
-      else
-       syslog(LOG_ERR, "Unknown destination %s!", queue);
+    putchar(1);
 
-      cupsFreeDests(num_dests, dests);
+    return (1);
+  }
 
-      putchar(1);
+ /*
+  * See if the printer is available...
+  */
 
-      return (1);
-    }
-  }
+  num_options = get_printer(http, queue, dest, sizeof(dest), &options,
+                            &accepting, &shared, NULL);
 
-  if (!check_printer(queue))
+  if (num_options < 0 || !accepting || !shared)
   {
-    cupsFreeDests(num_dests, dests);
+    if (dest[0])
+      syslog(LOG_INFO, "Rejecting job because \"%s\" is not %s", dest,
+             !accepting ? "accepting jobs" : "shared");
+    else
+      syslog(LOG_ERR, "Unable to get printer information for \"%s\"", queue);
+
+    httpClose(http);
 
     putchar(1);
 
     return (1);
   }
 
-  putchar(0);
+  putchar(0);                          /* OK so far... */
+
+ /*
+  * Read the request...
+  */
+
+  status   = 0;
+  num_data = 0;
+  fd       = -1;
+
+  control[0] = '\0';
 
   while (smart_gets(line, sizeof(line), stdin) != NULL)
   {
@@ -647,7 +906,7 @@ recv_print_job(const char    *dest, /* I - Destination */
              break;
            }
 
-           strcpy(filename, control);
+           strlcpy(filename, control, sizeof(filename));
          }
          break;
 
@@ -660,7 +919,7 @@ recv_print_job(const char    *dest, /* I - Destination */
            break;
          }
 
-          if (num_data >= (sizeof(data) / sizeof(data[0])))
+          if (num_data >= (int)(sizeof(data) / sizeof(data[0])))
          {
           /*
            * Too many data files...
@@ -683,7 +942,7 @@ recv_print_job(const char    *dest, /* I - Destination */
            break;
          }
 
-         strcpy(filename, temp[num_data]);
+         strlcpy(filename, temp[num_data], sizeof(filename));
 
           num_data ++;
          break;
@@ -698,15 +957,15 @@ recv_print_job(const char    *dest,       /* I - Destination */
     * Copy the data or control file from the client...
     */
 
-    for (i = atoi(count); i > 0; i -= bytes)
+    for (total = (size_t)strtoll(count, NULL, 10); total > 0; total -= (size_t)bytes)
     {
-      if (i > sizeof(line))
-        bytes = sizeof(line);
+      if (total > sizeof(line))
+        bytes = (ssize_t)sizeof(line);
       else
-        bytes = i;
+        bytes = (ssize_t)total;
 
-      if ((bytes = fread(line, 1, bytes, stdin)) > 0)
-        bytes = write(fd, line, bytes);
+      if ((bytes = (ssize_t)fread(line, 1, (size_t)bytes, stdin)) > 0)
+        bytes = write(fd, line, (size_t)bytes);
 
       if (bytes < 1)
       {
@@ -760,13 +1019,22 @@ recv_print_job(const char    *dest,      /* I - Destination */
     else
     {
      /*
-      * Grab the job information first...
+      * Copy the default options...
+      */
+
+      for (i = 0; i < num_defaults; i ++)
+       num_options = cupsAddOption(defaults[i].name,
+                                   defaults[i].value,
+                                   num_options, &options);
+
+     /*
+      * Grab the job information...
       */
 
       title[0]   = '\0';
       user[0]    = '\0';
       docname[0] = '\0';
-      banner     = 0;
+      doccount   = 0;
 
       while (smart_gets(line, sizeof(line), fp) != NULL)
       {
@@ -780,37 +1048,31 @@ recv_print_job(const char    *dest,      /* I - Destination */
              strlcpy(title, line + 1, sizeof(title));
              break;
 
-         case 'N' : /* Document name */
-             strlcpy(docname, line + 1, sizeof(docname));
-             break;
+          case 'N' : /* Document name */
+              strlcpy(docname, line + 1, sizeof(docname));
+              break;
 
          case 'P' : /* User identification */
              strlcpy(user, line + 1, sizeof(user));
              break;
 
          case 'L' : /* Print banner page */
-             banner = 1;
-             break;
-       }
-
-       if (status)
-         break;
-      }
-
-     /*
-      * Then print the jobs...
-      */
-
-      rewind(fp);
+            /*
+             * If a banner was requested and it's not overridden by a
+             * command line option and the destination's default is none
+             * then add the standard banner...
+             */
 
-      while (smart_gets(line, sizeof(line), fp) != NULL)
-      {
-       /*
-        * Process control lines...
-       */
+             if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL &&
+                 ((job_sheets = cupsGetOption("job-sheets", num_options,
+                                              options)) == NULL ||
+                  !strcmp(job_sheets, "none,none")))
+             {
+               num_options = cupsAddOption("job-sheets", "standard",
+                                           num_options, &options);
+             }
+             break;
 
-       switch (line[0])
-       {
          case 'c' : /* Plot CIF file */
          case 'd' : /* Print DVI file */
          case 'f' : /* Print formatted file */
@@ -822,97 +1084,118 @@ recv_print_job(const char    *dest,     /* I - Destination */
          case 'r' : /* File to print with FORTRAN carriage control */
          case 't' : /* Print troff output file */
          case 'v' : /* Print raster file */
-            /*
-             * Check that we have a username...
-             */
+             doccount ++;
 
-             if (!user[0])
-             {
-               syslog(LOG_WARNING, "No username specified by client! "
-                                   "Using \"anonymous\"...");
-               strcpy(user, "anonymous");
-             }
+             if (line[0] == 'l' &&
+                 !cupsGetOption("document-format", num_options, options))
+               num_options = cupsAddOption("raw", "", num_options, &options);
 
-             /*
-             * Copy the default options...
-             */
+              if (line[0] == 'p')
+               num_options = cupsAddOption("prettyprint", "", num_options,
+                                           &options);
+              break;
+       }
+
+       if (status)
+         break;
+      }
 
-              num_options = 0;
-             options     = NULL;
+     /*
+      * Check that we have a username...
+      */
 
-             for (i = 0; i < destptr->num_options; i ++)
-               num_options = cupsAddOption(destptr->options[i].name,
-                                           destptr->options[i].value,
-                                           num_options, &options);
-             for (i = 0; i < num_defaults; i ++)
-               num_options = cupsAddOption(defaults[i].name,
-                                           defaults[i].value,
-                                           num_options, &options);
+      if (!user[0])
+      {
+       syslog(LOG_WARNING, "No username specified by client! "
+                           "Using \"anonymous\"...");
+       strlcpy(user, "anonymous", sizeof(user));
+      }
 
-             /*
-             * If a banner was requested and it's not overridden by a
-             * command line option and the destination's default is none
-             * then add the standard banner...
-             */
+     /*
+      * Create the job...
+      */
 
-              if (banner &&
-                 cupsGetOption("job-sheets", num_defaults, defaults) == NULL &&
-                  ((cupsd_job_sheets = cupsGetOption("job-sheets",
-                                              destptr->num_options,
-                                              destptr->options)) == NULL ||
-                   !strcmp(cupsd_job_sheets, "none,none")))
-             {
-               num_options = cupsAddOption("job-sheets", "standard",
-                                           num_options, &options);
-             }
+      if ((id = create_job(http, dest, title, docname, user, num_options,
+                           options)) < 0)
+        status = 1;
+      else
+      {
+       /*
+       * Then print the job files...
+       */
 
-             /*
-             * Add additional options as needed...
-             */
+       rewind(fp);
 
-             if (line[0] == 'l')
-               num_options = cupsAddOption("raw", "", num_options, &options);
+       docname[0] = '\0';
+       docnumber  = 0;
 
-              if (line[0] == 'p')
-               num_options = cupsAddOption("prettyprint", "", num_options,
-                                           &options);
+       while (smart_gets(line, sizeof(line), fp) != NULL)
+       {
+        /*
+          * Process control lines...
+         */
 
-             /*
-             * Figure out which file we are printing...
-             */
+         switch (line[0])
+         {
+           case 'N' : /* Document name */
+               strlcpy(docname, line + 1, sizeof(docname));
+               break;
 
-             for (i = 0; i < num_data; i ++)
-               if (strcmp(data[i], line + 1) == 0)
+           case 'c' : /* Plot CIF file */
+           case 'd' : /* Print DVI file */
+           case 'f' : /* Print formatted file */
+           case 'g' : /* Plot file */
+           case 'l' : /* Print file leaving control characters (raw) */
+           case 'n' : /* Print ditroff output file */
+           case 'o' : /* Print PostScript output file */
+           case 'p' : /* Print file with 'pr' format (prettyprint) */
+           case 'r' : /* File to print with FORTRAN carriage control */
+           case 't' : /* Print troff output file */
+           case 'v' : /* Print raster file */
+               /*
+               * Figure out which file we are printing...
+               */
+
+               for (i = 0; i < num_data; i ++)
+                 if (!strcmp(data[i], line + 1))
+                   break;
+
+               if (i >= num_data)
+               {
+                 status = 1;
                  break;
+               }
 
-              if (i >= num_data)
-             {
-               status = 1;
-               break;
-             }
+               /*
+               * Send the print file...
+               */
 
-             /*
-             * Send the print request...
-             */
+               docnumber ++;
 
-              if (print_file(queue, temp[i], title, docname, user, num_options,
-                            options) == 0)
-                status = 1;
-             else
-               status = 0;
+               if (print_file(http, id, temp[i], docname, user,
+                              cupsGetOption("document-format", num_options,
+                                            options),
+                              docnumber == doccount))
+                  status = 1;
+               else
+                 status = 0;
 
-              cupsFreeOptions(num_options, options);
-             break;
-       }
+               break;
+         }
 
-       if (status)
-         break;
+         if (status)
+           break;
+       }
       }
 
       fclose(fp);
     }
   }
 
+  cupsFreeOptions(num_options, options);
+
+  httpClose(http);
+
  /*
   * Clean up all temporary files and return...
   */
@@ -922,8 +1205,6 @@ recv_print_job(const char    *dest,        /* I - Destination */
   for (i = 0; i < num_data; i ++)
     unlink(temp[i]);
 
-  cupsFreeDests(num_dests, dests);
-
   return (status);
 }
 
@@ -932,16 +1213,14 @@ recv_print_job(const char    *dest,      /* I - Destination */
  * 'remove_jobs()' - Cancel one or more jobs.
  */
 
-int                                    /* O - Command status */
+static int                             /* O - Command status */
 remove_jobs(const char *dest,          /* I - Destination */
             const char *agent,         /* I - User agent */
            const char *list)           /* I - List of jobs or users */
 {
   int          id;                     /* Job ID */
   http_t       *http;                  /* HTTP server connection */
-  ipp_t                *request,               /* IPP Request */
-               *response;              /* IPP Response */
-  cups_lang_t  *language;              /* Default language */
+  ipp_t                *request;               /* IPP Request */
   char         uri[HTTP_MAX_URI];      /* Job URI */
 
 
@@ -959,8 +1238,6 @@ remove_jobs(const char *dest,              /* I - Destination */
     return (1);
   }
 
-  language = cupsLangDefault();
-
  /*
   * Loop for each job...
   */
@@ -986,16 +1263,7 @@ remove_jobs(const char *dest,             /* I - Destination */
     *    requesting-user-name
     */
 
-    request = ippNew();
-
-    request->request.op.operation_id = IPP_CANCEL_JOB;
-    request->request.op.request_id   = 1;
-
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-                "attributes-charset", NULL, cupsLangEncoding(language));
-
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-                "attributes-natural-language", NULL, language->language);
+    request = ippNewRequest(IPP_CANCEL_JOB);
 
     sprintf(uri, "ipp://localhost/jobs/%d", id);
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
@@ -1007,33 +1275,19 @@ remove_jobs(const char *dest,           /* I - Destination */
     * Do the request and get back a response...
     */
 
-    if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
-    {
-      if (response->request.status.status_code > IPP_OK_CONFLICT)
-      {
-       syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
-               ippErrorString(response->request.status.status_code));
-       ippDelete(response);
-       cupsLangFree(language);
-       httpClose(http);
-       return (1);
-      }
-      else
-        syslog(LOG_INFO, "Job ID %d cancelled", id);
+    ippDelete(cupsDoRequest(http, request, "/jobs"));
 
-      ippDelete(response);
-    }
-    else
+    if (cupsLastError() > IPP_OK_CONFLICT)
     {
       syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
-             ippErrorString(cupsLastError()));
-      cupsLangFree(language);
+             cupsLastErrorString());
       httpClose(http);
       return (1);
     }
+    else
+      syslog(LOG_INFO, "Job ID %d canceled", id);
   }
 
-  cupsLangFree(language);
   httpClose(http);
 
   return (0);
@@ -1044,8 +1298,8 @@ remove_jobs(const char *dest,             /* I - Destination */
  * 'send_state()' - Send the queue state.
  */
 
-int                                    /* O - Command status */
-send_state(const char *dest,           /* I - Destination */
+static int                             /* O - Command status */
+send_state(const char *queue,          /* I - Destination */
            const char *list,           /* I - Job or user */
           int        longstatus)       /* I - List of jobs or users */
 {
@@ -1054,7 +1308,6 @@ send_state(const char *dest,              /* I - Destination */
   ipp_t                *request,               /* IPP Request */
                *response;              /* IPP Response */
   ipp_attribute_t *attr;               /* Current attribute */
-  cups_lang_t  *language;              /* Default language */
   ipp_pstate_t state;                  /* Printer state */
   const char   *jobdest,               /* Pointer into job-printer-uri */
                *jobuser,               /* Pointer to job-originating-user-name */
@@ -1068,8 +1321,7 @@ send_state(const char *dest,              /* I - Destination */
   char         rankstr[255];           /* Rank string */
   char         namestr[1024];          /* Job name string */
   char         uri[HTTP_MAX_URI];      /* Printer URI */
-  char         queue[256],             /* Printer/class queue */
-               *instance;              /* Printer/class instance */
+  char         dest[256];              /* Printer/class queue */
   static const char * const ranks[10] =        /* Ranking strings */
                {
                  "th",
@@ -1095,15 +1347,6 @@ send_state(const char *dest,             /* I - Destination */
                };
 
 
- /*
-  * Remove instance from destination, if any...
-  */
-
-  strlcpy(queue, dest, sizeof(queue));
-
-  if ((instance = strrchr(queue, '/')) != NULL)
-    *instance++ = '\0';
-
  /*
   * Try connecting to the local server...
   */
@@ -1118,78 +1361,32 @@ send_state(const char *dest,            /* I - Destination */
   }
 
  /*
-  * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
-  * attributes:
-  *
-  *    attributes-charset
-  *    attributes-natural-language
-  *    printer-uri
+  * Get the actual destination name and printer state...
   */
 
-  request = ippNew();
-
-  request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
-  request->request.op.request_id   = 1;
-
-  language = cupsLangDefault();
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL, language->language);
-
-  httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
-                   "/printers/%s", queue);
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
-               "printer-uri", NULL, uri);
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-               "requested-attributes", NULL, "printer-state");
+  if (get_printer(http, queue, dest, sizeof(dest), NULL, NULL, NULL, &state))
+  {
+    syslog(LOG_ERR, "Unable to get printer %s: %s", queue,
+           cupsLastErrorString());
+    printf("Unable to get printer %s: %s", queue, cupsLastErrorString());
+    return (1);
+  }
 
  /*
-  * Do the request and get back a response...
+  * Show the queue state...
   */
 
-  if ((response = cupsDoRequest(http, request, "/")) != NULL)
-  {
-    if (response->request.status.status_code > IPP_OK_CONFLICT)
-    {
-      syslog(LOG_WARNING, "Unable to get printer list: %s\n",
-             ippErrorString(response->request.status.status_code));
-      printf("Unable to get printer list: %s\n",
-             ippErrorString(response->request.status.status_code));
-      ippDelete(response);
-      return (1);
-    }
-
-    if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
-      state = (ipp_pstate_t)attr->values[0].integer;
-    else
-      state = IPP_PRINTER_STOPPED;
-
-    switch (state)
-    {
-      case IPP_PRINTER_IDLE :
-          printf("%s is ready\n", dest);
-         break;
-      case IPP_PRINTER_PROCESSING :
-          printf("%s is ready and printing\n", dest);
-         break;
-      case IPP_PRINTER_STOPPED :
-          printf("%s is not ready\n", dest);
-         break;
-    }
-
-    ippDelete(response);
-  }
-  else
+  switch (state)
   {
-    syslog(LOG_WARNING, "Unable to get printer list: %s\n",
-           ippErrorString(cupsLastError()));
-    printf("Unable to get printer list: %s\n",
-           ippErrorString(cupsLastError()));
-    return (1);
+    case IPP_PRINTER_IDLE :
+        printf("%s is ready\n", dest);
+       break;
+    case IPP_PRINTER_PROCESSING :
+        printf("%s is ready and printing\n", dest);
+       break;
+    case IPP_PRINTER_STOPPED :
+        printf("%s is not ready\n", dest);
+       break;
   }
 
  /*
@@ -1203,19 +1400,10 @@ send_state(const char *dest,            /* I - Destination */
 
   id = atoi(list);
 
-  request = ippNew();
-
-  request->request.op.operation_id = id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
-  request->request.op.request_id   = 1;
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL, language->language);
+  request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
 
-  httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
-                   "/printers/%s", queue);
+  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                   "localhost", 0, "/printers/%s", dest);
 
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                NULL, uri);
@@ -1230,7 +1418,8 @@ send_state(const char *dest,              /* I - Destination */
   }
 
   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                "requested-attributes", sizeof(requested) / sizeof(requested[0]),
+                "requested-attributes",
+               sizeof(requested) / sizeof(requested[0]),
                NULL, requested);
 
  /*
@@ -1238,144 +1427,132 @@ send_state(const char *dest,          /* I - Destination */
   */
 
   jobcount = 0;
+  response = cupsDoRequest(http, request, "/");
 
-  if ((response = cupsDoRequest(http, request, "/")) != NULL)
+  if (cupsLastError() > IPP_OK_CONFLICT)
   {
-    if (response->request.status.status_code > IPP_OK_CONFLICT)
-    {
-      printf("get-jobs failed: %s\n",
-             ippErrorString(response->request.status.status_code));
-      ippDelete(response);
-      return (1);
-    }
+    printf("get-jobs failed: %s\n", cupsLastErrorString());
+    ippDelete(response);
+    return (1);
+  }
 
-    rank = 1;
+ /*
+  * Loop through the job list and display them...
+  */
 
+  for (attr = response->attrs, rank = 1; attr; attr = attr->next)
+  {
    /*
-    * Loop through the job list and display them...
+    * Skip leading attributes until we hit a job...
     */
 
-    for (attr = response->attrs; attr != NULL; attr = attr->next)
-    {
-     /*
-      * Skip leading attributes until we hit a job...
-      */
-
-      while (attr != NULL &&
-             (attr->group_tag != IPP_TAG_JOB || attr->name == NULL))
-        attr = attr->next;
-
-      if (attr == NULL)
-        break;
+    while (attr && (attr->group_tag != IPP_TAG_JOB || !attr->name))
+      attr = attr->next;
 
-     /*
-      * Pull the needed attributes from this job...
-      */
+    if (!attr)
+      break;
 
-      jobid       = 0;
-      jobsize     = 0;
-      jobstate    = IPP_JOB_PENDING;
-      jobname     = "untitled";
-      jobuser     = NULL;
-      jobdest     = NULL;
-      jobcopies   = 1;
+   /*
+    * Pull the needed attributes from this job...
+    */
 
-      while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
-      {
-        if (strcmp(attr->name, "job-id") == 0 &&
-           attr->value_tag == IPP_TAG_INTEGER)
-         jobid = attr->values[0].integer;
+    jobid     = 0;
+    jobsize   = 0;
+    jobstate  = IPP_JOB_PENDING;
+    jobname   = "untitled";
+    jobuser   = NULL;
+    jobdest   = NULL;
+    jobcopies = 1;
 
-        if (strcmp(attr->name, "job-k-octets") == 0 &&
-           attr->value_tag == IPP_TAG_INTEGER)
-         jobsize = attr->values[0].integer;
+    while (attr && attr->group_tag == IPP_TAG_JOB)
+    {
+      if (!strcmp(attr->name, "job-id") &&
+         attr->value_tag == IPP_TAG_INTEGER)
+       jobid = attr->values[0].integer;
 
-        if (strcmp(attr->name, "job-state") == 0 &&
-           attr->value_tag == IPP_TAG_ENUM)
-         jobstate = (ipp_jstate_t)attr->values[0].integer;
+      if (!strcmp(attr->name, "job-k-octets") &&
+         attr->value_tag == IPP_TAG_INTEGER)
+       jobsize = attr->values[0].integer;
 
-        if (strcmp(attr->name, "job-printer-uri") == 0 &&
-           attr->value_tag == IPP_TAG_URI)
-         if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
-           jobdest ++;
+      if (!strcmp(attr->name, "job-state") &&
+         attr->value_tag == IPP_TAG_ENUM)
+       jobstate = (ipp_jstate_t)attr->values[0].integer;
 
-        if (strcmp(attr->name, "job-originating-user-name") == 0 &&
-           attr->value_tag == IPP_TAG_NAME)
-         jobuser = attr->values[0].string.text;
+      if (!strcmp(attr->name, "job-printer-uri") &&
+         attr->value_tag == IPP_TAG_URI)
+       if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
+         jobdest ++;
 
-        if (strcmp(attr->name, "job-name") == 0 &&
-           attr->value_tag == IPP_TAG_NAME)
-         jobname = attr->values[0].string.text;
+      if (!strcmp(attr->name, "job-originating-user-name") &&
+         attr->value_tag == IPP_TAG_NAME)
+       jobuser = attr->values[0].string.text;
 
-        if (strcmp(attr->name, "copies") == 0 &&
-           attr->value_tag == IPP_TAG_INTEGER)
-         jobcopies = attr->values[0].integer;
+      if (!strcmp(attr->name, "job-name") &&
+         attr->value_tag == IPP_TAG_NAME)
+       jobname = attr->values[0].string.text;
 
-        attr = attr->next;
-      }
+      if (!strcmp(attr->name, "copies") &&
+         attr->value_tag == IPP_TAG_INTEGER)
+       jobcopies = attr->values[0].integer;
 
-     /*
-      * See if we have everything needed...
-      */
+      attr = attr->next;
+    }
 
-      if (jobdest == NULL || jobid == 0)
-      {
-        if (attr == NULL)
-         break;
-       else
-          continue;
-      }
+   /*
+    * See if we have everything needed...
+    */
 
-      if (!longstatus && jobcount == 0)
-       puts("Rank    Owner   Job     File(s)                         Total Size");
+    if (!jobdest || !jobid)
+    {
+      if (!attr)
+       break;
+      else
+        continue;
+    }
 
-      jobcount ++;
+    if (!longstatus && jobcount == 0)
+      puts("Rank    Owner   Job     File(s)                         Total Size");
 
-     /*
-      * Display the job...
-      */
+    jobcount ++;
 
-      if (jobstate == IPP_JOB_PROCESSING)
-       strcpy(rankstr, "active");
-      else
-      {
-       snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
-       rank ++;
-      }
+   /*
+    * Display the job...
+    */
 
-      if (longstatus)
-      {
-        puts("");
+    if (jobstate == IPP_JOB_PROCESSING)
+      strlcpy(rankstr, "active", sizeof(rankstr));
+    else
+    {
+      snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
+      rank ++;
+    }
 
-        if (jobcopies > 1)
-         snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
-                  jobname);
-       else
-         strlcpy(namestr, jobname, sizeof(namestr));
+    if (longstatus)
+    {
+      puts("");
 
-        printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
-        printf("        %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
-      }
+      if (jobcopies > 1)
+       snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
+                jobname);
       else
-        printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
-              jobid, jobname, 1024.0 * jobsize);
+       strlcpy(namestr, jobname, sizeof(namestr));
 
-      if (attr == NULL)
-        break;
+      printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
+      printf("        %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
     }
+    else
+      printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
+            jobid, jobname, 1024.0 * jobsize);
 
-    ippDelete(response);
-  }
-  else
-  {
-    printf("get-jobs failed: %s\n", ippErrorString(cupsLastError()));
-    return (1);
+    if (!attr)
+      break;
   }
 
+  ippDelete(response);
+
   if (jobcount == 0)
     puts("no entries");
 
-  cupsLangFree(language);
   httpClose(http);
 
   return (0);
@@ -1386,7 +1563,7 @@ send_state(const char *dest,              /* I - Destination */
  * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
  */
 
-char *                                 /* O - Line read or NULL */
+static char *                          /* O - Line read or NULL */
 smart_gets(char *s,                    /* I - Pointer to line buffer */
            int  len,                   /* I - Size of line buffer */
           FILE *fp)                    /* I - File to read from */
@@ -1424,7 +1601,7 @@ smart_gets(char *s,                       /* I - Pointer to line buffer */
       break;
     }
     else if (ptr < end)
-      *ptr++ = ch;
+      *ptr++ = (char)ch;
   }
 
   *ptr = '\0';
@@ -1437,5 +1614,5 @@ smart_gets(char *s,                       /* I - Pointer to line buffer */
 
 
 /*
- * End of "$Id: cups-lpd.c 4807 2005-10-21 19:17:52Z mike $".
+ * End of "$Id: cups-lpd.c 12612 2015-05-06 15:30:50Z msweet $".
  */