diff -Naur busybox.orig/printutils/lpd.c busybox/printutils/lpd.c
--- busybox.orig/printutils/lpd.c	2008-02-25 23:37:30 +0000
+++ busybox/printutils/lpd.c	2008-02-28 00:05:54 +0000
@@ -10,6 +10,21 @@
 
 // TODO: xmalloc_reads is vulnerable to remote OOM attack!
 
+// strip argument of bad chars
+static char *sane(char *str)
+{
+	char *s = str;
+	char *p = s;
+	while (*s) {
+		if (isalnum(*s) || '-' == *s) {
+			*p++ = *s;
+		}
+		s++;
+	}
+	*p = '\0';
+	return str;
+}
+
 int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
 int lpd_main(int argc, char *argv[])
 {
@@ -31,7 +46,9 @@
 	// these links or files are called "queues"
 	// OR
 	// if a directory named as given queue exists within spool directory
-	// then LPD enters spooling mode and just dumps both control and data files to it
+	// then LPD enters spooling mode, dumps data file to the spool directory
+	// sets environment variables with the values from corresponding control file
+	// and then spawns argv[2] script (if any).
 
 	// goto spool directory
 	if (argv[1])
@@ -42,7 +59,7 @@
 	*strchrnul(s, '\n') = '\0';
 
 	// protect against "/../" attacks
-	if (queue[0] == '.' || strstr(queue, "/."))
+	if (!*sane(queue))
 		return EXIT_FAILURE;
 
 	// queue is a directory -> chdir to it and enter spooling mode
@@ -80,10 +97,8 @@
 		*fname++ = '\0';
 		if (spooling) {
 			// spooling mode: dump both files
-			// make "/../" attacks in file names ineffective
-			xchroot(".");
 			// job in flight has mode 0200 "only writable"
-			fd = xopen3(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200);
+			fd = xopen3(sane(fname), O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200);
 		} else {
 			// non-spooling mode:
 			// 2: control file (ignoring), 3: data file
@@ -99,6 +114,60 @@
 				expected_len, real_len);
 			return EXIT_FAILURE;
 		}
+		// chmod completely downloaded job as "readable+writable"
+		if (spooling)
+			fchmod(fd, 0600);
+		close(fd); // NB: can do close(-1). Who cares?
+		// data file dumped? ->
+		if (spooling && 3 == s[0]
+			// if spool helper is specified...
+			&& argc > 2
+			// ... spawn it
+			// N.B. errors are silently ignored
+			&& !vfork()
+		) {
+			char *ctl, *p, *q;
+			// read control file, then zap it
+			fname[0] = 'c';
+			// N.B. RFC 1179 states LPD server MUST be capable
+			// to receive control file before data file.
+			// so if we are here the control file must have already been dumped!
+			ctl = xmalloc_open_read_close(fname, NULL);
+			unlink(fname);
+			// use fname as storage for environment var name
+			fname[1] = '\0';
+			// parse control file by \n
+			q = ctl;
+			while ((p = strchr(q, '\n')) != NULL) {
+				*p++ = '\0';
+				// here q is a line of <SYM><VALUE>
+				// let us set environment string <SYM>=<VALUE>
+				fname[0] = *q;
+				setenv(fname, q+1, 1);
+				// next line, plz!
+				q = p;
+			}
+			// we are the helper. we wanna be silent
+			// TODO: maybe mark 0,1,2 close_on_exec?
+			close(STDIN_FILENO);
+			close(STDOUT_FILENO);
+			close(STDERR_FILENO);
+			// NOTE: a good LPR sets the following variables:
+/*
+			H HOST - host which issues the job
+			P USER - user who prints
+			C CLASS - this is printed on banner page (if L cmd is also given)
+			J JOBNAME - ?
+			L USER - print banner page, with given user's name
+			M WHOM_TO_MAIL = USER
+			l DATA_FILE_NAME ("dfAxxx") - file to be printed
+*/
+			// thus these variables are accessible by spool helper.
+			// NOTE: spool helper is responsible for
+			// cleaning up processed data files!
+			BB_EXECVP(argv[2], argv+2);
+			_exit(127);
+		}
 		// get ACK and see whether it is NUL (ok)
 		if (read(STDIN_FILENO, s, 1) != 1 || s[0] != 0) {
 			// don't send error msg to peer - it obviously
@@ -106,10 +175,6 @@
 			// it can't understand us either
 			return EXIT_FAILURE;
 		}
-		// chmod completely downloaded job as "readable+writable"
-		if (spooling)
-			fchmod(fd, 0600);
-		close(fd); // NB: can do close(-1). Who cares?
 		free(s);
 	} /* while (1) */
 }
