xref: /freebsd/usr.sbin/lpr/lpr/lpr.c (revision e28a4053)
1 /*
2  * Copyright (c) 1983, 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #ifndef lint
41 static const char copyright[] =
42 "@(#) Copyright (c) 1983, 1989, 1993\n\
43 	The Regents of the University of California.  All rights reserved.\n";
44 #endif /* not lint */
45 
46 #if 0
47 #ifndef lint
48 static char sccsid[] = "@(#)lpr.c	8.4 (Berkeley) 4/28/95";
49 #endif /* not lint */
50 #endif
51 
52 #include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
53 __FBSDID("$FreeBSD$");
54 
55 /*
56  *      lpr -- off line print
57  *
58  * Allows multiple printers and printers on remote machines by
59  * using information from a printer data base.
60  */
61 
62 #include <sys/param.h>
63 #include <sys/stat.h>
64 
65 #include <netinet/in.h>		/* N_BADMAG uses ntohl() */
66 
67 #include <dirent.h>
68 #include <fcntl.h>
69 #include <a.out.h>
70 #include <err.h>
71 #include <locale.h>
72 #include <signal.h>
73 #include <syslog.h>
74 #include <pwd.h>
75 #include <grp.h>
76 #include <unistd.h>
77 #include <stdlib.h>
78 #include <stdio.h>
79 #include <ctype.h>
80 #include <string.h>
81 #include "lp.h"
82 #include "lp.local.h"
83 #include "pathnames.h"
84 
85 static char	*cfname;	/* daemon control files, linked from tf's */
86 static char	*class = local_host;	/* class title on header page */
87 static char	*dfname;	/* data files */
88 static char	*fonts[4];	/* troff font names */
89 static char	 format = 'f';	/* format char for printing files */
90 static int	 hdr = 1;	/* print header or not (default is yes) */
91 static int	 iflag;		/* indentation wanted */
92 static int	 inchar;	/* location to increment char in file names */
93 static int	 indent;	/* amount to indent */
94 static const char *jobname;	/* job name on header page */
95 static int	 mailflg;	/* send mail */
96 static int	 nact;		/* number of jobs to act on */
97 static int	 ncopies = 1;	/* # of copies to make */
98 static char	*lpr_username;  /* person sending the print job(s) */
99 static int	 qflag;		/* q job, but don't exec daemon */
100 static int	 rflag;		/* remove files upon completion */
101 static int	 sflag;		/* symbolic link flag */
102 static int	 tfd;		/* control file descriptor */
103 static char	*tfname;	/* tmp copy of cf before linking */
104 static char	*title;		/* pr'ing title */
105 static char     *locale;        /* pr'ing locale */
106 static int	 userid;	/* user id */
107 static char	*Uflag;		/* user name specified with -U flag */
108 static char	*width;		/* width for versatec printing */
109 static char	*Zflag;		/* extra filter options for LPRng servers */
110 
111 static struct stat statb;
112 
113 static void	 card(int _c, const char *_p2);
114 static int	 checkwriteperm(const char *_file, const char *_directory);
115 static void	 chkprinter(const char *_ptrname, struct printer *_pp);
116 static void	 cleanup(int _signo);
117 static void	 copy(const struct printer *_pp, int _f, const char _n[]);
118 static char	*itoa(int _i);
119 static const char  *linked(const char *_file);
120 int		 main(int _argc, char *_argv[]);
121 static char	*lmktemp(const struct printer *_pp, const char *_id,
122 		    int _num, int len);
123 static void	 mktemps(const struct printer *_pp);
124 static int	 nfile(char *_n);
125 static int	 test(const char *_file);
126 static void	 usage(void);
127 
128 uid_t	uid, euid;
129 
130 int
131 main(int argc, char *argv[])
132 {
133 	struct passwd *pw;
134 	struct group *gptr;
135 	const char *arg, *cp, *printer;
136 	char *p;
137 	char buf[BUFSIZ];
138 	int c, i, f, errs;
139 	int	 ret, didlink;
140 	struct stat stb;
141 	struct stat statb1, statb2;
142 	struct printer myprinter, *pp = &myprinter;
143 
144 	printer = NULL;
145 	euid = geteuid();
146 	uid = getuid();
147 	seteuid(uid);
148 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
149 		signal(SIGHUP, cleanup);
150 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
151 		signal(SIGINT, cleanup);
152 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
153 		signal(SIGQUIT, cleanup);
154 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
155 		signal(SIGTERM, cleanup);
156 
157 	progname = argv[0];
158 	gethostname(local_host, sizeof(local_host));
159 	openlog("lpd", 0, LOG_LPR);
160 
161 	errs = 0;
162 	while ((c = getopt(argc, argv,
163 			   ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:"))
164 	       != -1)
165 		switch (c) {
166 		case '#':		/* n copies */
167 			i = strtol(optarg, &p, 10);
168 			if (*p)
169 				errx(1, "Bad argument to -#, number expected");
170 			if (i > 0)
171 				ncopies = i;
172 			break;
173 
174 		case '1':		/* troff fonts */
175 		case '2':
176 		case '3':
177 		case '4':
178 			fonts[optopt - '1'] = optarg;
179 			break;
180 
181 		case 'C':		/* classification spec */
182 			hdr++;
183 			class = optarg;
184 			break;
185 
186 		case 'J':		/* job name */
187 			hdr++;
188 			jobname = optarg;
189 			break;
190 
191 		case 'P':		/* specifiy printer name */
192 			printer = optarg;
193 			break;
194 
195 		case 'L':               /* pr's locale */
196 			locale = optarg;
197 			break;
198 
199 		case 'T':		/* pr's title line */
200 			title = optarg;
201 			break;
202 
203 		case 'U':		/* user name */
204 			hdr++;
205 			Uflag = optarg;
206 			break;
207 
208 		case 'Z':
209 			Zflag = optarg;
210 			break;
211 
212 		case 'c':		/* print cifplot output */
213 		case 'd':		/* print tex output (dvi files) */
214 		case 'g':		/* print graph(1G) output */
215 		case 'l':		/* literal output */
216 		case 'n':		/* print ditroff output */
217 		case 't':		/* print troff output (cat files) */
218 		case 'p':		/* print using ``pr'' */
219 		case 'v':		/* print vplot output */
220 			format = optopt;
221 			break;
222 
223 		case 'f':		/* print fortran output */
224 			format = 'r';
225 			break;
226 
227 		case 'h':		/* nulifiy header page */
228 			hdr = 0;
229 			break;
230 
231 		case 'i':		/* indent output */
232 			iflag++;
233 			indent = strtol(optarg, &p, 10);
234 			if (*p)
235 				errx(1, "Bad argument to -i, number expected");
236 			break;
237 
238 		case 'm':		/* send mail when done */
239 			mailflg++;
240 			break;
241 
242 		case 'q':		/* just queue job */
243 			qflag++;
244 			break;
245 
246 		case 'r':		/* remove file when done */
247 			rflag++;
248 			break;
249 
250 		case 's':		/* try to link files */
251 			sflag++;
252 			break;
253 
254 		case 'w':		/* versatec page width */
255 			width = optarg;
256 			break;
257 
258 		case ':':		/* catch "missing argument" error */
259 			if (optopt == 'i') {
260 				iflag++; /* -i without args is valid */
261 				indent = 8;
262 			} else
263 				errs++;
264 			break;
265 
266 		default:
267 			errs++;
268 		}
269 	argc -= optind;
270 	argv += optind;
271 	if (errs)
272 		usage();
273 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
274 		printer = DEFLP;
275 	chkprinter(printer, pp);
276 	if (pp->no_copies && ncopies > 1)
277 		errx(1, "multiple copies are not allowed");
278 	if (pp->max_copies > 0 && ncopies > pp->max_copies)
279 		errx(1, "only %ld copies are allowed", pp->max_copies);
280 	/*
281 	 * Get the identity of the person doing the lpr using the same
282 	 * algorithm as lprm.  Actually, not quite -- lprm will override
283 	 * the login name with "root" if the user is running as root;
284 	 * the daemon actually checks for the string "root" in its
285 	 * permission checking.  Sigh.
286 	 */
287 	userid = getuid();
288 	if (Uflag) {
289 		if (userid != 0 && userid != pp->daemon_user)
290 			errx(1, "only privileged users may use the `-U' flag");
291 		lpr_username = Uflag;		/* -U person doing 'lpr' */
292 	} else {
293 		lpr_username = getlogin();	/* person doing 'lpr' */
294 		if (userid != pp->daemon_user || lpr_username == 0) {
295 			if ((pw = getpwuid(userid)) == NULL)
296 				errx(1, "Who are you?");
297 			lpr_username = pw->pw_name;
298 		}
299 	}
300 
301 	/*
302 	 * Check for restricted group access.
303 	 */
304 	if (pp->restrict_grp != NULL && userid != pp->daemon_user) {
305 		if ((gptr = getgrnam(pp->restrict_grp)) == NULL)
306 			errx(1, "Restricted group specified incorrectly");
307 		if (gptr->gr_gid != getgid()) {
308 			while (*gptr->gr_mem != NULL) {
309 				if ((strcmp(lpr_username, *gptr->gr_mem)) == 0)
310 					break;
311 				gptr->gr_mem++;
312 			}
313 			if (*gptr->gr_mem == NULL)
314 				errx(1, "Not a member of the restricted group");
315 		}
316 	}
317 	/*
318 	 * Check to make sure queuing is enabled if userid is not root.
319 	 */
320 	lock_file_name(pp, buf, sizeof buf);
321 	if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS))
322 		errx(1, "Printer queue is disabled");
323 	/*
324 	 * Initialize the control file.
325 	 */
326 	mktemps(pp);
327 	tfd = nfile(tfname);
328 	seteuid(euid);
329 	(void) fchown(tfd, pp->daemon_user, -1);
330 	/* owned by daemon for protection */
331 	seteuid(uid);
332 	card('H', local_host);
333 	card('P', lpr_username);
334 	card('C', class);
335 	if (hdr && !pp->no_header) {
336 		if (jobname == NULL) {
337 			if (argc == 0)
338 				jobname = "stdin";
339 			else
340 				jobname = ((arg = strrchr(argv[0], '/'))
341 					   ? arg + 1 : argv[0]);
342 		}
343 		card('J', jobname);
344 		card('L', lpr_username);
345 	}
346 	if (format != 'p' && Zflag != 0)
347 		card('Z', Zflag);
348 	if (iflag)
349 		card('I', itoa(indent));
350 	if (mailflg)
351 		card('M', lpr_username);
352 	if (format == 't' || format == 'n' || format == 'd')
353 		for (i = 0; i < 4; i++)
354 			if (fonts[i] != NULL)
355 				card('1'+i, fonts[i]);
356 	if (width != NULL)
357 		card('W', width);
358 	/*
359 	 * XXX
360 	 * Our use of `Z' here is incompatible with LPRng's
361 	 * use.  We assume that the only use of our existing
362 	 * `Z' card is as shown for `p' format (pr) files.
363 	 */
364 	if (format == 'p') {
365 		char *s;
366 
367 		if (locale)
368 			card('Z', locale);
369 		else if ((s = setlocale(LC_TIME, "")) != NULL)
370 			card('Z', s);
371 	}
372 
373 	/*
374 	 * Read the files and spool them.
375 	 */
376 	if (argc == 0)
377 		copy(pp, 0, " ");
378 	else while (argc--) {
379 		if (argv[0][0] == '-' && argv[0][1] == '\0') {
380 			/* use stdin */
381 			copy(pp, 0, " ");
382 			argv++;
383 			continue;
384 		}
385 		if ((f = test(arg = *argv++)) < 0)
386 			continue;	/* file unreasonable */
387 
388 		if (sflag && (cp = linked(arg)) != NULL) {
389 			(void) snprintf(buf, sizeof(buf), "%u %u", statb.st_dev,
390 				statb.st_ino);
391 			card('S', buf);
392 			if (format == 'p')
393 				card('T', title ? title : arg);
394 			for (i = 0; i < ncopies; i++)
395 				card(format, &dfname[inchar-2]);
396 			card('U', &dfname[inchar-2]);
397 			if (f)
398 				card('U', cp);
399 			card('N', arg);
400 			dfname[inchar]++;
401 			nact++;
402 			continue;
403 		}
404 		if (sflag)
405 			printf("%s: %s: not linked, copying instead\n",
406 			    progname, arg);
407 
408 		if (f) {
409 			/*
410 			 * The user wants the file removed after it is copied
411 			 * to the spool area, so see if the file can be moved
412 			 * instead of copy/unlink'ed.  This is much faster and
413 			 * uses less spool space than copying the file.  This
414 			 * can be very significant when running services like
415 			 * samba, pcnfs, CAP, et al.
416 			 */
417 			seteuid(euid);
418 			didlink = 0;
419 			/*
420 			 * There are several things to check to avoid any
421 			 * security issues.  Some of these are redundant
422 			 * under BSD's, but are necessary when lpr is built
423 			 * under some other OS's (which I do do...)
424 			 */
425 			if (lstat(arg, &statb1) < 0)
426 				goto nohardlink;
427 			if (S_ISLNK(statb1.st_mode))
428 				goto nohardlink;
429 			if (link(arg, dfname) != 0)
430 				goto nohardlink;
431 			didlink = 1;
432 			/*
433 			 * Make sure the user hasn't tried to trick us via
434 			 * any race conditions
435 			 */
436 			if (lstat(dfname, &statb2) < 0)
437 				goto nohardlink;
438 			if (statb1.st_dev != statb2.st_dev)
439 				goto nohardlink;
440 			if (statb1.st_ino != statb2.st_ino)
441 				goto nohardlink;
442 			/*
443 			 * Skip if the file already had multiple hard links,
444 			 * because changing the owner and access-bits would
445 			 * change ALL versions of the file
446 			 */
447 			if (statb2.st_nlink > 2)
448 				goto nohardlink;
449 			/*
450 			 * If we can access and remove the original file
451 			 * without special setuid-ness then this method is
452 			 * safe.  Otherwise, abandon the move and fall back
453 			 * to the (usual) copy method.
454 			 */
455 			seteuid(uid);
456 			ret = access(dfname, R_OK);
457 			if (ret == 0)
458 				ret = unlink(arg);
459 			seteuid(euid);
460 			if (ret != 0)
461 				goto nohardlink;
462 			/*
463 			 * Unlink of user file was successful.  Change the
464 			 * owner and permissions, add entries to the control
465 			 * file, and skip the file copying step.
466 			 */
467 			chown(dfname, pp->daemon_user, getegid());
468 			chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
469 			seteuid(uid);
470 			if (format == 'p')
471 				card('T', title ? title : arg);
472 			for (i = 0; i < ncopies; i++)
473 				card(format, &dfname[inchar-2]);
474 			card('U', &dfname[inchar-2]);
475 			card('N', arg);
476 			nact++;
477 			continue;
478 		nohardlink:
479 			if (didlink)
480 				unlink(dfname);
481 			seteuid(uid);           /* restore old uid */
482 		} /* end: if (f) */
483 
484 		if ((i = open(arg, O_RDONLY)) < 0) {
485 			printf("%s: cannot open %s\n", progname, arg);
486 		} else {
487 			copy(pp, i, arg);
488 			(void) close(i);
489 			if (f && unlink(arg) < 0)
490 				printf("%s: %s: not removed\n", progname, arg);
491 		}
492 	}
493 
494 	if (nact) {
495 		(void) close(tfd);
496 		tfname[inchar]--;
497 		/*
498 		 * Touch the control file to fix position in the queue.
499 		 */
500 		seteuid(euid);
501 		if ((tfd = open(tfname, O_RDWR)) >= 0) {
502 			char touch_c;
503 
504 			if (read(tfd, &touch_c, 1) == 1 &&
505 			    lseek(tfd, (off_t)0, 0) == 0 &&
506 			    write(tfd, &touch_c, 1) != 1) {
507 				printf("%s: cannot touch %s\n", progname,
508 				    tfname);
509 				tfname[inchar]++;
510 				cleanup(0);
511 			}
512 			(void) close(tfd);
513 		}
514 		if (link(tfname, cfname) < 0) {
515 			printf("%s: cannot rename %s\n", progname, cfname);
516 			tfname[inchar]++;
517 			cleanup(0);
518 		}
519 		unlink(tfname);
520 		seteuid(uid);
521 		if (qflag)		/* just q things up */
522 			exit(0);
523 		if (!startdaemon(pp))
524 			printf("jobs queued, but cannot start daemon.\n");
525 		exit(0);
526 	}
527 	cleanup(0);
528 	return (1);
529 	/* NOTREACHED */
530 }
531 
532 /*
533  * Create the file n and copy from file descriptor f.
534  */
535 static void
536 copy(const struct printer *pp, int f, const char n[])
537 {
538 	register int fd, i, nr, nc;
539 	char buf[BUFSIZ];
540 
541 	if (format == 'p')
542 		card('T', title ? title : n);
543 	for (i = 0; i < ncopies; i++)
544 		card(format, &dfname[inchar-2]);
545 	card('U', &dfname[inchar-2]);
546 	card('N', n);
547 	fd = nfile(dfname);
548 	nr = nc = 0;
549 	while ((i = read(f, buf, BUFSIZ)) > 0) {
550 		if (write(fd, buf, i) != i) {
551 			printf("%s: %s: temp file write error\n", progname, n);
552 			break;
553 		}
554 		nc += i;
555 		if (nc >= BUFSIZ) {
556 			nc -= BUFSIZ;
557 			nr++;
558 			if (pp->max_blocks > 0 && nr > pp->max_blocks) {
559 				printf("%s: %s: copy file is too large\n",
560 				    progname, n);
561 				break;
562 			}
563 		}
564 	}
565 	(void) close(fd);
566 	if (nc==0 && nr==0)
567 		printf("%s: %s: empty input file\n", progname,
568 		    f ? n : "stdin");
569 	else
570 		nact++;
571 }
572 
573 /*
574  * Try and link the file to dfname. Return a pointer to the full
575  * path name if successful.
576  */
577 static const char *
578 linked(const char *file)
579 {
580 	register char *cp;
581 	static char buf[MAXPATHLEN];
582 	register int ret;
583 
584 	if (*file != '/') {
585 		if (getcwd(buf, sizeof(buf)) == NULL)
586 			return(NULL);
587 		while (file[0] == '.') {
588 			switch (file[1]) {
589 			case '/':
590 				file += 2;
591 				continue;
592 			case '.':
593 				if (file[2] == '/') {
594 					if ((cp = strrchr(buf, '/')) != NULL)
595 						*cp = '\0';
596 					file += 3;
597 					continue;
598 				}
599 			}
600 			break;
601 		}
602 		strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
603 		strncat(buf, file, sizeof(buf) - strlen(buf) - 1);
604 		file = buf;
605 	}
606 	seteuid(euid);
607 	ret = symlink(file, dfname);
608 	seteuid(uid);
609 	return(ret ? NULL : file);
610 }
611 
612 /*
613  * Put a line into the control file.
614  */
615 static void
616 card(int c, const char *p2)
617 {
618 	char buf[BUFSIZ];
619 	register char *p1 = buf;
620 	size_t len = 2;
621 
622 	*p1++ = c;
623 	while ((c = *p2++) != '\0' && len < sizeof(buf)) {
624 		*p1++ = (c == '\n') ? ' ' : c;
625 		len++;
626 	}
627 	*p1++ = '\n';
628 	write(tfd, buf, len);
629 }
630 
631 /*
632  * Create a new file in the spool directory.
633  */
634 static int
635 nfile(char *n)
636 {
637 	register int f;
638 	int oldumask = umask(0);		/* should block signals */
639 
640 	seteuid(euid);
641 	f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
642 	(void) umask(oldumask);
643 	if (f < 0) {
644 		printf("%s: cannot create %s\n", progname, n);
645 		cleanup(0);
646 	}
647 	if (fchown(f, userid, -1) < 0) {
648 		printf("%s: cannot chown %s\n", progname, n);
649 		cleanup(0);	/* cleanup does exit */
650 	}
651 	seteuid(uid);
652 	if (++n[inchar] > 'z') {
653 		if (++n[inchar-2] == 't') {
654 			printf("too many files - break up the job\n");
655 			cleanup(0);
656 		}
657 		n[inchar] = 'A';
658 	} else if (n[inchar] == '[')
659 		n[inchar] = 'a';
660 	return(f);
661 }
662 
663 /*
664  * Cleanup after interrupts and errors.
665  */
666 static void
667 cleanup(int signo __unused)
668 {
669 	register int i;
670 
671 	signal(SIGHUP, SIG_IGN);
672 	signal(SIGINT, SIG_IGN);
673 	signal(SIGQUIT, SIG_IGN);
674 	signal(SIGTERM, SIG_IGN);
675 	i = inchar;
676 	seteuid(euid);
677 	if (tfname)
678 		do
679 			unlink(tfname);
680 		while (tfname[i]-- != 'A');
681 	if (cfname)
682 		do
683 			unlink(cfname);
684 		while (cfname[i]-- != 'A');
685 	if (dfname)
686 		do {
687 			do
688 				unlink(dfname);
689 			while (dfname[i]-- != 'A');
690 			dfname[i] = 'z';
691 		} while (dfname[i-2]-- != 'd');
692 	exit(1);
693 }
694 
695 /*
696  * Test to see if this is a printable file.
697  * Return -1 if it is not, 0 if its printable, and 1 if
698  * we should remove it after printing.
699  */
700 static int
701 test(const char *file)
702 {
703 	struct exec execb;
704 	size_t dlen;
705 	int fd;
706 	char *cp, *dirpath;
707 
708 	if (access(file, 4) < 0) {
709 		printf("%s: cannot access %s\n", progname, file);
710 		return(-1);
711 	}
712 	if (stat(file, &statb) < 0) {
713 		printf("%s: cannot stat %s\n", progname, file);
714 		return(-1);
715 	}
716 	if ((statb.st_mode & S_IFMT) == S_IFDIR) {
717 		printf("%s: %s is a directory\n", progname, file);
718 		return(-1);
719 	}
720 	if (statb.st_size == 0) {
721 		printf("%s: %s is an empty file\n", progname, file);
722 		return(-1);
723  	}
724 	if ((fd = open(file, O_RDONLY)) < 0) {
725 		printf("%s: cannot open %s\n", progname, file);
726 		return(-1);
727 	}
728 	/*
729 	 * XXX Shall we add a similar test for ELF?
730 	 */
731 	if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
732 	    !N_BADMAG(execb)) {
733 		printf("%s: %s is an executable program", progname, file);
734 		goto error1;
735 	}
736 	(void) close(fd);
737 	if (rflag) {
738 		/*
739 		 * aside: note that 'cp' is technically a 'const char *'
740 		 * (because it points into 'file'), even though strrchr
741 		 * returns a value of type 'char *'.
742 		 */
743 		if ((cp = strrchr(file, '/')) == NULL) {
744 			if (checkwriteperm(file,".") == 0)
745 				return(1);
746 		} else {
747 			if (cp == file) {
748 				fd = checkwriteperm(file,"/");
749 			} else {
750 				/* strlcpy will change the '/' to '\0' */
751 				dlen = cp - file + 1;
752 				dirpath = malloc(dlen);
753 				strlcpy(dirpath, file, dlen);
754 				fd = checkwriteperm(file, dirpath);
755 				free(dirpath);
756 			}
757 			if (fd == 0)
758 				return(1);
759 		}
760 		printf("%s: %s: is not removable by you\n", progname, file);
761 	}
762 	return(0);
763 
764 error1:
765 	printf(" and is unprintable\n");
766 	(void) close(fd);
767 	return(-1);
768 }
769 
770 static int
771 checkwriteperm(const char *file, const char *directory)
772 {
773 	struct	stat	stats;
774 	if (access(directory, W_OK) == 0) {
775 		stat(directory, &stats);
776 		if (stats.st_mode & S_ISVTX) {
777 			stat(file, &stats);
778 			if(stats.st_uid == userid) {
779 				return(0);
780 			}
781 		} else return(0);
782 	}
783 	return(-1);
784 }
785 
786 /*
787  * itoa - integer to string conversion
788  */
789 static char *
790 itoa(int i)
791 {
792 	static char b[10] = "########";
793 	register char *p;
794 
795 	p = &b[8];
796 	do
797 		*p-- = i%10 + '0';
798 	while (i /= 10);
799 	return(++p);
800 }
801 
802 /*
803  * Perform lookup for printer name or abbreviation --
804  */
805 static void
806 chkprinter(const char *ptrname, struct printer *pp)
807 {
808 	int status;
809 
810 	init_printer(pp);
811 	status = getprintcap(ptrname, pp);
812 	switch(status) {
813 	case PCAPERR_OSERR:
814 	case PCAPERR_TCLOOP:
815 		errx(1, "%s: %s", ptrname, pcaperr(status));
816 	case PCAPERR_NOTFOUND:
817 		errx(1, "%s: unknown printer", ptrname);
818 	case PCAPERR_TCOPEN:
819 		warnx("%s: unresolved tc= reference(s)", ptrname);
820 	}
821 }
822 
823 /*
824  * Tell the user what we wanna get.
825  */
826 static void
827 usage(void)
828 {
829 	fprintf(stderr, "%s\n",
830 "usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n"
831 	"\t[-Z daemon-options] [-i[numcols]] [-i[numcols]] [-1234 font]\n"
832 	"\t[-L locale] [-wnum] [-cdfghlnmprstv] [name ...]");
833 	exit(1);
834 }
835 
836 
837 /*
838  * Make the temp files.
839  */
840 static void
841 mktemps(const struct printer *pp)
842 {
843 	register int len, fd, n;
844 	register char *cp;
845 	char buf[BUFSIZ];
846 
847 	(void) snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir);
848 	seteuid(euid);
849 	if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
850 		printf("%s: cannot create %s\n", progname, buf);
851 		exit(1);
852 	}
853 	if (flock(fd, LOCK_EX)) {
854 		printf("%s: cannot lock %s\n", progname, buf);
855 		exit(1);
856 	}
857 	seteuid(uid);
858 	n = 0;
859 	if ((len = read(fd, buf, sizeof(buf))) > 0) {
860 		for (cp = buf; len--; ) {
861 			if (*cp < '0' || *cp > '9')
862 				break;
863 			n = n * 10 + (*cp++ - '0');
864 		}
865 	}
866 	len = strlen(pp->spool_dir) + strlen(local_host) + 8;
867 	tfname = lmktemp(pp, "tf", n, len);
868 	cfname = lmktemp(pp, "cf", n, len);
869 	dfname = lmktemp(pp, "df", n, len);
870 	inchar = strlen(pp->spool_dir) + 3;
871 	n = (n + 1) % 1000;
872 	(void) lseek(fd, (off_t)0, 0);
873 	snprintf(buf, sizeof(buf), "%03d\n", n);
874 	(void) write(fd, buf, strlen(buf));
875 	(void) close(fd);	/* unlocks as well */
876 }
877 
878 /*
879  * Make a temp file name.
880  */
881 static char *
882 lmktemp(const struct printer *pp, const char *id, int num, int len)
883 {
884 	register char *s;
885 
886 	if ((s = malloc(len)) == NULL)
887 		errx(1, "out of memory");
888 	(void) snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num,
889 	    local_host);
890 	return(s);
891 }
892