Ferdinand Keil's

electronic notes

Jun 22, 2017

Fixing cups-lpd on Raspbian Jessie

As I have mentioned before I use a Raspberry Pi running Rasbian Jessie as a print-server. After I made the switch to LPR I started noticing new problems: some documents just wouldn't print. Printing web-pages from Firefox stopped working alltogether. So what now?

What were the symptoms? Whenever I tried to print one of the offending documents, these lines showed up in /var/log/cups/error_log:

W [15/Jun/2017:15:44:32 +0000] Unexpected 'document-name' operation attribute in a Create-Job request.
E [15/Jun/2017:15:44:32 +0000] [Client 17] Returning IPP client-error-attributes-or-values-not-supported for Create-Job (ipp://localhost/printers/Samsung_ML-2010) from localhost

With the CUPS release used in Raspbian Jessie being around since 2014 there was a good chance someone else has had these problems before. And indeed, I was lucky.

Unexpected 'document-name' operation attribute in a Create-Job request.

So what is happening here? Well, the cups-lpd mini daemon is sending a document-name attribute via IPP to CUPS when creating a new job. But, CUPS does not expect this attribute and starts complaining. You can read more about it in issue #4790. These are the necessary changes to cups-lpd.c :


@@ -344,10 +345,6 @@ create_job(http_t        *http,                /* I - HTTP connection */
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
                  NULL, title);

-  if (docname[0])
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
-                 NULL, docname);
-
   cupsEncodeOptions(request, num_options, options);

  /*

This basically just removes the lines responsible for sending the unwanted document-name attribute.

Returning IPP client-error-attributes-or-values-not-supported

This one is a little tougher. Turns out, it's a problem with Unicode support. More details can be found in issue #4899<https://github.com/apple/cups/issues/4899>. To get rid of it I applied some of the changes from commit 5babee8<https://github.com/apple/cups/commit/5babee86cb84055bce3597ab537abe4a6c6d8dbc> to the code:

@@ -71,6 +71,7 @@ static int        remove_jobs(const char *name, const char *agent,
 static int send_state(const char *name, const char *list,
                       int longstatus);
 static char        *smart_gets(char *s, int len, FILE *fp);
+static void        smart_strlcpy(char *dst, const char *src, size_t dstsize);


 /*
@@ -1043,15 +1040,15 @@ recv_print_job(
    switch (line[0])
    {
      case 'J' : /* Job name */
-         strlcpy(title, line + 1, sizeof(title));
+         smart_strlcpy(title, line + 1, sizeof(title));
          break;

           case 'N' : /* Document name */
-              strlcpy(docname, line + 1, sizeof(docname));
+              smart_strlcpy(docname, line + 1, sizeof(docname));
               break;

      case 'P' : /* User identification */
-         strlcpy(user, line + 1, sizeof(user));
+         smart_strlcpy(user, line + 1, sizeof(user));
          break;

      case 'L' : /* Print banner page */
@@ -1136,7 +1133,7 @@ recv_print_job(
      switch (line[0])
      {
        case 'N' : /* Document name */
-           strlcpy(docname, line + 1, sizeof(docname));
+           smart_strlcpy(docname, line + 1, sizeof(docname));
            break;

        case 'c' : /* Plot CIF file */
@@ -1612,5 +1609,96 @@ smart_gets(char *s,                  /* I - Pointer to line buffer */


 /*
+ * 'smart_strlcpy()' - Copy a string and convert from ISO-8859-1 to UTF-8 as needed.
+ */
+
+static void
+smart_strlcpy(char       *dst,             /* I - Output buffer */
+              const char *src,             /* I - Input string */
+              size_t     dstsize)  /* I - Size of output buffer */
+{
+  const unsigned char      *srcptr;        /* Pointer into input string */
+  unsigned char            *dstptr,        /* Pointer into output buffer */
+                   *dstend;        /* End of output buffer */
+  int                      saw_8859 = 0;   /* Saw an extended character that was not UTF-8? */
+
+
+  for (srcptr = (unsigned char *)src, dstptr = (unsigned char *)dst, dstend = dstptr + dstsize - 1; *srcptr;)
+  {
+    if (*srcptr < 0x80)
+      *dstptr++ = *srcptr++;               /* ASCII */
+    else if (saw_8859)
+    {
+     /*
+      * Map ISO-8859-1 (most likely character set for legacy LPD clients) to
+      * UTF-8...
+      */
+
+      if (dstptr > (dstend - 2))
+        break;
+
+      *dstptr++ = 0xc0 | (*srcptr >> 6);
+      *dstptr++ = 0x80 | (*srcptr++ & 0x3f);
+    }
+    else if ((*srcptr & 0xe0) == 0xc0 && (srcptr[1] & 0xc0) == 0x80)
+    {
+     /*
+      * 2-byte UTF-8 sequence...
+      */
+
+      if (dstptr > (dstend - 2))
+        break;
+
+      *dstptr++ = *srcptr++;
+      *dstptr++ = *srcptr++;
+    }
+    else if ((*srcptr & 0xf0) == 0xe0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80)
+    {
+     /*
+      * 3-byte UTF-8 sequence...
+      */
+
+      if (dstptr > (dstend - 3))
+        break;
+
+      *dstptr++ = *srcptr++;
+      *dstptr++ = *srcptr++;
+      *dstptr++ = *srcptr++;
+    }
+    else if ((*srcptr & 0xf8) == 0xf0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80 && (srcptr[3] & 0xc0) == 0x80)
+    {
+     /*
+      * 4-byte UTF-8 sequence...
+      */
+
+      if (dstptr > (dstend - 4))
+        break;
+
+      *dstptr++ = *srcptr++;
+      *dstptr++ = *srcptr++;
+      *dstptr++ = *srcptr++;
+      *dstptr++ = *srcptr++;
+    }
+    else
+    {
+     /*
+      * Bad UTF-8 sequence, this must be an ISO-8859-1 string...
+      */
+
+      saw_8859 = 1;
+
+      if (dstptr > (dstend - 2))
+        break;
+
+      *dstptr++ = 0xc0 | (*srcptr >> 6);
+      *dstptr++ = 0x80 | (*srcptr++ & 0x3f);
+    }
+  }
+
+  *dstptr = '\0';
+}
+
+
+/*
  * End of "$Id: cups-lpd.c 11623 2014-02-19 20:18:10Z msweet $".
  */

Well, that did it.

Now, how does one compile their own .deb package?

Applying the changes and compiling the module

I followed the tutorial on debian.org for building packages. Some of the steps didn't work for me and couldn't be bothered to find out why. Here is what I ended up doing:

sudo nano /etc/apt/sources.list
# uncomment the line starting with deb-src
sudo apt-get update
sudo apt-get install -y build-essential fakeroot devscripts
sudo apt-get build-dep -y cups
mkdir -p src/debian
cd src/debian
apt-get source cups
cd cups-1.7.5
nano schedules/cups-lpd.c
# apply the changes mentioned above
debuild -b -uc -us -tc
# ^- that builds the package, so it might take a while

This results in several packages being build, however you only need cups_1.7.5-11+deb8u1_armhf.deb. To install it type:

sudo dpkg -i cups_1.7.5-11+deb8u1_armhf.deb

TL;DR

The cups-lpd coming with Raspbian Jessie is broken and will not print certain documents. Install the package below to get rid of these problems.


cups_1.7.5-11+deb8u1_armhf.deb