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.