ctlinfo.c revision 86935
179746Sgad/*
279746Sgad * ------+---------+---------+---------+---------+---------+---------+---------*
379746Sgad * Copyright (c) 2001  - Garance Alistair Drosehn <gad@FreeBSD.org>.
479746Sgad * All rights reserved.
579746Sgad *
679746Sgad * Redistribution and use in source and binary forms, with or without
779746Sgad * modification, are permitted provided that the following conditions
879746Sgad * are met:
979746Sgad *   1. Redistributions of source code must retain the above copyright
1079746Sgad *      notice, this list of conditions and the following disclaimer.
1179746Sgad *   2. Redistributions in binary form must reproduce the above copyright
1279746Sgad *      notice, this list of conditions and the following disclaimer in the
1379746Sgad *      documentation and/or other materials provided with the distribution.
1479746Sgad *
1579746Sgad * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1679746Sgad * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1779746Sgad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1879746Sgad * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1979746Sgad * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2079746Sgad * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2179746Sgad * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2279746Sgad * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2379746Sgad * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2479746Sgad * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2579746Sgad * SUCH DAMAGE.
2679746Sgad *
2779746Sgad * The views and conclusions contained in the software and documentation
2879746Sgad * are those of the authors and should not be interpreted as representing
2979746Sgad * official policies, either expressed or implied, of the FreeBSD Project.
3079746Sgad *
3179746Sgad * ------+---------+---------+---------+---------+---------+---------+---------*
3279746Sgad */
3379746Sgad
3479746Sgad#ifndef lint
3579746Sgadstatic const char rcsid[] =
3679746Sgad  "$FreeBSD: head/usr.sbin/lpr/common_source/ctlinfo.c 86935 2001-11-27 01:32:25Z gad $";
3779746Sgad#endif /* not lint */
3879746Sgad
3979746Sgad/*
4079746Sgad * ctlinfo - This collection of routines will know everything there is to
4179746Sgad * know about the information inside a control file ('cf*') which is used
4279746Sgad * to describe a print job in lpr & friends.  The eventual goal is that it
4379746Sgad * will be the ONLY source file to know what's inside these control-files.
4479746Sgad */
4579746Sgad
4679746Sgad/*
4779746Sgad * Some define's useful for debuging.
4879746Sgad * TRIGGERTEST_FNAME and DEBUGREADCF_FNAME, allow us to do testing on
4979746Sgad * a per-spool-directory basis.
5079746Sgad */
5179746Sgad/* #define TRIGGERTEST_FNAME "LpdTestRenameTF" */
5279746Sgad/* #define DEBUGREADCF_FNAME "LpdDebugReadCF" */
5379746Sgad/* #define LEAVE_TMPCF_FILES 1 */
5479746Sgad
5579746Sgad#include <sys/types.h>
5679746Sgad#include <sys/stat.h>
5779746Sgad#include <ctype.h>
5879746Sgad#include <errno.h>
5979746Sgad#include <fcntl.h>
6079746Sgad#include <limits.h>
6179746Sgad#include <netdb.h>
6279746Sgad#include <stdio.h>
6379746Sgad#include <stdlib.h>
6479746Sgad#include <string.h>
6579746Sgad#include <syslog.h>
6679746Sgad#include <unistd.h>
6779746Sgad#include "ctlinfo.h"
6879746Sgad
6979746Sgadstruct cjprivate {
7079746Sgad	struct cjobinfo pub;
7179746Sgad	char	*cji_buff;		/* buffer for getline */
7279746Sgad	char	*cji_eobuff;		/* last byte IN the buffer */
7379746Sgad	FILE	*cji_fstream;
7479746Sgad	int	 cji_buffsize;		/* # bytes in the buffer */
7579746Sgad	int	 cji_dumpit;
7679746Sgad};
7779746Sgad
7879746Sgad#define roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
7979746Sgad
8079746Sgad/*
8179746Sgad * This has to be large enough to fit the maximum length of a single line
8279746Sgad * in a control-file, including the leading 'command id', a trailing '\n'
8379746Sgad * and ending '\0'.  The max size of an 'U'nlink line, for instance, is
8479746Sgad * 1 ('U') + PATH_MAX (filename) + 2 ('\n\0').  The maximum 'H'ost line is
8579746Sgad * 1 ('H') + NI_MAXHOST (remote hostname) + 2 ('\n\0').  Other lines can be
8679746Sgad * even longer than those.  So, pick some nice, large, arbitrary value.
8779746Sgad */
8879746Sgad#define CTI_LINEMAX  PATH_MAX+NI_MAXHOST+5
8979746Sgad
9079746Sgadextern const char	*from_host;	/* client's machine name */
9179746Sgadextern const char	*from_ip;	/* client machine's IP address */
9279746Sgad
9379746Sgad__BEGIN_DECLS
9479746Sgadvoid		 ctl_dumpcji(FILE *_dbg_stream, const char *_heading,
9579746Sgad		    struct cjobinfo *_cjinf);
9679746Sgadvoid		 ctl_freeinf(struct cjobinfo *_cjinf);
9779746Sgadstatic char	*ctl_getline(struct cjobinfo *_cjinf);
9879746Sgadstruct cjobinfo	*ctl_readcf(const char *_ptrname, const char *_cfname);
9979746Sgadstatic void	 ctl_rewindcf(struct cjobinfo *_cjinf);
10079746Sgadchar		*ctl_rmjob(const char *_ptrname, const char *_cfname);
10179746Sgad__END_DECLS
10279746Sgad
10379746Sgad/*
10479746Sgad * Here are some things which might be needed when compiling this under
10579746Sgad * platforms other than FreeBSD.
10679746Sgad */
10779746Sgad#ifndef __FreeBSD__
10879746Sgad#   ifndef NAME_MAX
10979746Sgad#	define NAME_MAX	255
11079746Sgad#   endif
11179746Sgad#   ifndef NI_MAXHOST
11279746Sgad#	define NI_MAXHOST	1025
11379746Sgad#   endif
11479746Sgad#   ifndef PATH_MAX
11579746Sgad#	define PATH_MAX	1024
11679746Sgad#   endif
11779746Sgad__BEGIN_DECLS
11879746Sgadchar		*strdup(const char *_src);
11979746Sgadsize_t		 strlcpy(char *_dst, const char *_src, size_t _siz);
12079746Sgad__END_DECLS
12179746Sgad#endif
12279746Sgad
12379746Sgad/*
12479746Sgad *	Control-files (cf*) have the following format.
12579746Sgad *
12679746Sgad *	Each control-file describes a single job.  It will list one or more
12779746Sgad *	"datafiles" (df*) which should be copied to some printer.  Usually
12879746Sgad *	there is only one datafile per job.  For the curious, RFC 1179 is an
12979746Sgad *	informal and out-of-date description of lpr/lpd circa 1990.
13079746Sgad *
13179746Sgad *	Each line in the file gives an attribute of the job as a whole, or one
13279746Sgad *	of the datafiles in the job, or a "command" indicating something to do
13379746Sgad *	with one of the datafiles.  Each line starts with an 'id' that indicates
13479746Sgad *	what that line is there for.  The 'id' is historically a single byte,
13579746Sgad *	but may be multiple bytes (obviously it would be best if multi-byte ids
13679746Sgad *	started with some letter not already used as a single-byte id!).
13779746Sgad *	After the 'id', the remainder of the line will be the value of the
13879746Sgad *	indicated attribute, or a name of the datafile to be operated on.
13979746Sgad *
14079746Sgad *	In the following lists of ids, the ids with a '!' in front of them are
14179746Sgad *	NOT explicitly supported by this version of lpd, or at least "not yet
14279746Sgad *	supported".  They are only listed for reference purposes, so people
14379746Sgad *	won't be tempted to reuse the same id for a different purpose.
14479746Sgad *
14579746Sgad *	The following are attributes of the job which should not appear more
14679746Sgad *	than once in a control file.  Only the 'H' and 'P' lines are required
14779746Sgad *	by the RFC, but some implementations of lpr won't even get that right.
14879746Sgad *
14979746Sgad *	! A   - [used by lprNG]
15079746Sgad *	  B   - As far as I know, this is never used as a single-byte id.
15179746Sgad *		Therefore, I intend to use it for multi-byte id codes.
15279746Sgad *	  C   - "class name" to display on banner page (this is sometimes
15379746Sgad *		used to hold options for print filters)
15479746Sgad *	! D   - [in lprNG, "timestamp" of when the job was submitted]
15579746Sgad *	! E   - "environment variables" to set [some versions of linux]
15679746Sgad *	  H   - "host name" of machine where the original 'lpr' was done
15779746Sgad *	  I   - "indent", the amount to indent output
15879746Sgad *	  J   - "job name" to display on banner page
15979746Sgad *	  L   - "literal" user's name as it should be displayed on the
16079746Sgad *		banner page (it is the existence of an 'L' line which
16179746Sgad *		indicates that a job should have a banner page).
16279746Sgad *	  M   - "mail", userid to mail to when done printing (with email
16379746Sgad *		going to 'M'@'H', so to speak).
16479746Sgad *	  P   - "person", the user's login name (e.g. for accounting)
16579746Sgad *	! Q   - [used by lprNG for queue-name]
16679746Sgad *	  R   - "resolution" in dpi, for some laser printer queues
16779746Sgad *	  T   - "title" for files sent thru 'pr'
16879746Sgad *	  W   - "width" to use for printing plain-text files
16979746Sgad *	  Z   - In BSD, "locale" to use for datafiles sent thru 'pr'.
17079746Sgad *		(this BSD usage should move to a different id...)
17179746Sgad *		[in lprNG - this line holds the "Z options"]
17279746Sgad *	  1   - "R font file" for files sent thru troff
17379746Sgad *	  2   - "I font file" for files sent thru troff
17479746Sgad *	  3   - "B font file" for files sent thru troff
17579746Sgad *	  4   - "S font file" for files sent thru troff
17679746Sgad *
17779746Sgad *	The following are attributes attached to a datafile, and thus may
17879746Sgad *	appear multiple times in a control file (once per datafile):
17979746Sgad *
18079746Sgad *	  N   - "name" of file (for display purposes, used by 'lpq')
18179746Sgad *	  S   - "stat() info" used for symbolic link ('lpr -s')
18279746Sgad *		security checks.
18379746Sgad *
18479746Sgad *	The following indicate actions to take on a given datafile.  The same
18579746Sgad *	datafile may appear on more than one "print this file" command in the
18683684Sgad *	control file.  Note that ALL ids with lowercase letters are expected
18783684Sgad *	to be actions to "print this file":
18879746Sgad *
18983684Sgad *	  c   - "file name", cifplot file to print.  This action appears
19083684Sgad *		when the user has requested 'lpr -c'.
19183684Sgad *	  d   - "file name", dvi file to print, user requested 'lpr -d'
19283684Sgad *	  f   - "file name", a plain-text file to print = "standard"
19383684Sgad *	  g   - "file name", plot(1G) file to print, ie 'lpr -g'
19483684Sgad *	  l   - "file name", text file with control chars which should
19583684Sgad *		be printed literally, ie 'lpr -l'  (note: some printers
19686935Sgad *		take this id as a request to print a postscript file,
19786935Sgad *		and because of *that* some OS's use 'l' to indicate
19886935Sgad *		that a datafile is a postscript file)
19983684Sgad *	  n   - "file name", ditroff(1) file to print, ie 'lpr -n'
20083684Sgad *	  o   - "file name", a postscript file to print.  This id is
20183684Sgad *		described in the original RFC, but not much has been
20283684Sgad *		done with it.  This 'lpr' does not generate control
20383684Sgad *		lines with 'o'-actions, but lpd's printjob processing
20486935Sgad *		will treat it the same as 'l'.
20583684Sgad *	  p   - "file name", text file to print with pr(1), ie 'lpr -p'
20683684Sgad *	  t   - "file name", troff(1) file to print, ie 'lpr -t'
20779746Sgad *	  v   - "file name", plain raster file to print
20879746Sgad *
20979746Sgad *	  U   - "file name" of datafile to unlink (ie, remove file
21079746Sgad *		from spool directory.  To be done in a 'Pass 2',
21179746Sgad *		AFTER having processed all datafiles in the job).
21279746Sgad *
21379746Sgad */
21479746Sgad
21579746Sgadvoid
21679746Sgadctl_freeinf(struct cjobinfo *cjinf)
21779746Sgad{
21879746Sgad#define FREESTR(xStr) \
21979746Sgad	if (xStr != NULL) { \
22079746Sgad		free(xStr); \
22179746Sgad		xStr = NULL;\
22279746Sgad	}
22379746Sgad
22479746Sgad	struct cjprivate *cpriv;
22579746Sgad
22679746Sgad	if (cjinf == NULL)
22779746Sgad		return;
22879746Sgad	cpriv = cjinf->cji_priv;
22979746Sgad	if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
23079746Sgad		syslog(LOG_ERR, "in ctl_freeinf(%p): invalid cjinf (cpriv %p)",
23179746Sgad		    cjinf, cpriv);
23279746Sgad		return;
23379746Sgad	}
23479746Sgad
23579746Sgad	FREESTR(cpriv->pub.cji_accthost);
23679746Sgad	FREESTR(cpriv->pub.cji_acctuser);
23779746Sgad	FREESTR(cpriv->pub.cji_class);
23879746Sgad	FREESTR(cpriv->pub.cji_curqueue);
23979746Sgad	/* [cpriv->pub.cji_fname is part of cpriv-malloced area] */
24079746Sgad	FREESTR(cpriv->pub.cji_jobname);
24179746Sgad	FREESTR(cpriv->pub.cji_mailto);
24279746Sgad	FREESTR(cpriv->pub.cji_username);
24379746Sgad
24479746Sgad	if (cpriv->cji_fstream != NULL) {
24579746Sgad		fclose(cpriv->cji_fstream);
24679746Sgad		cpriv->cji_fstream = NULL;
24779746Sgad	}
24879746Sgad
24979746Sgad	cjinf->cji_priv = NULL;
25079746Sgad	free(cpriv);
25179746Sgad#undef FREESTR
25279746Sgad}
25379746Sgad
25479746Sgad#ifdef DEBUGREADCF_FNAME
25579746Sgadstatic FILE *ctl_dbgfile = NULL;
25679746Sgadstatic struct stat ctl_dbgstat;
25779746Sgad#endif
25879746Sgadstatic int ctl_dbgline = 0;
25979746Sgad
26079746Sgadstruct cjobinfo *
26179746Sgadctl_readcf(const char *ptrname, const char *cfname)
26279746Sgad{
26379746Sgad	int id;
26479746Sgad	char *lbuff;
26579746Sgad	void *cstart;
26679746Sgad	FILE *cfile;
26779746Sgad	struct cjprivate *cpriv;
26879746Sgad	struct cjobinfo *cjinf;
26979746Sgad	size_t msize, sroom, sroom2;
27079746Sgad
27179746Sgad	cfile = fopen(cfname, "r");
27279746Sgad	if (cfile == NULL) {
27379746Sgad		syslog(LOG_ERR, "%s: ctl_readcf error fopen(%s): %s",
27479746Sgad		    ptrname, cfname, strerror(errno));
27579746Sgad		return NULL;
27679746Sgad	}
27779746Sgad
27879746Sgad	sroom = roundup(sizeof(struct cjprivate), 8);
27979746Sgad	sroom2 = sroom + strlen(cfname) + 1;
28079746Sgad	sroom2 = roundup(sroom2, 8);
28179746Sgad	msize = sroom2 + CTI_LINEMAX;
28279746Sgad	msize = roundup(msize, 8);
28379746Sgad	cstart = malloc(msize);
28479746Sgad	if (cstart == NULL)
28579746Sgad		return NULL;
28679746Sgad	memset(cstart, 0, msize);
28779746Sgad	cpriv = (struct cjprivate *)cstart;
28879746Sgad	cpriv->pub.cji_priv = cpriv;
28979746Sgad
29079746Sgad	cpriv->pub.cji_fname = (char *)cstart + sroom;
29179746Sgad	strcpy(cpriv->pub.cji_fname, cfname);
29279746Sgad	cpriv->cji_buff = (char *)cstart + sroom2;
29379746Sgad	cpriv->cji_buffsize = (int)(msize - sroom2);
29479746Sgad	cpriv->cji_eobuff = (char *)cstart + msize - 1;
29579746Sgad
29679746Sgad	cpriv->cji_fstream = cfile;
29779746Sgad	cpriv->pub.cji_curqueue = strdup(ptrname);
29879746Sgad
29979746Sgad	ctl_dbgline = 0;
30079746Sgad#ifdef DEBUGREADCF_FNAME
30179746Sgad	ctl_dbgfile = NULL;
30279746Sgad	id = stat(DEBUGREADCF_FNAME, &ctl_dbgstat);
30379746Sgad	if (id != -1) {
30479746Sgad		/* the file exists in this spool directory, write some simple
30579746Sgad		 * debugging info to it */
30679746Sgad		ctl_dbgfile = fopen(DEBUGREADCF_FNAME, "a");
30779746Sgad		if (ctl_dbgfile != NULL) {
30879746Sgad			fprintf(ctl_dbgfile, "%s: s=%p r=%ld e=%p %p->%s\n",
30979746Sgad			    ptrname, cpriv, (long)sroom, cpriv->cji_eobuff,
31079746Sgad			    cpriv->pub.cji_fname, cpriv->pub.cji_fname);
31179746Sgad		}
31279746Sgad	}
31379746Sgad#endif
31479746Sgad	/*
31579746Sgad	 * Copy job-attribute values from control file to the struct of
31679746Sgad	 * "public" information.  In some cases, it is invalid for the
31779746Sgad	 * value to be a null-string, so that is ignored.
31879746Sgad	 */
31979746Sgad	cjinf = &(cpriv->pub);
32079746Sgad	lbuff = ctl_getline(cjinf);
32179746Sgad	while (lbuff != NULL) {
32279746Sgad		id = *lbuff++;
32379746Sgad		switch (id) {
32479746Sgad		case 'C':
32579746Sgad			cpriv->pub.cji_class = strdup(lbuff);
32679746Sgad			break;
32779746Sgad		case 'H':
32879746Sgad			if (*lbuff == '\0')
32979746Sgad				break;
33079746Sgad			cpriv->pub.cji_accthost = strdup(lbuff);
33179746Sgad			break;
33279746Sgad		case 'J':
33379746Sgad			cpriv->pub.cji_jobname = strdup(lbuff);
33479746Sgad			break;
33579746Sgad		case 'L':
33679746Sgad			cpriv->pub.cji_username = strdup(lbuff);
33779746Sgad			break;
33879746Sgad		case 'M':
33979746Sgad			/*
34079746Sgad			 * No valid mail-to address would start with a minus.
34179746Sgad			 * If this one does, it is probably some trickster who
34279746Sgad			 * is trying to trigger options on sendmail.  Ignore.
34379746Sgad			 */
34479746Sgad			if (*lbuff == '-')
34579746Sgad				break;
34679746Sgad			if (*lbuff == '\0')
34779746Sgad				break;
34879746Sgad			cpriv->pub.cji_mailto = strdup(lbuff);
34979746Sgad			break;
35079746Sgad		case 'P':
35179746Sgad			/* don't allow userid's with a leading minus, either */
35279746Sgad			if (*lbuff == '-')
35379746Sgad				break;
35479746Sgad			if (*lbuff == '\0')
35579746Sgad				break;
35679746Sgad			cpriv->pub.cji_acctuser = strdup(lbuff);
35779746Sgad			break;
35879746Sgad		default:
35979746Sgad			if (islower(id)) {
36079746Sgad				cpriv->pub.cji_dfcount++;
36179746Sgad			}
36279746Sgad			break;
36379746Sgad		}
36479746Sgad		lbuff = ctl_getline(cjinf);
36579746Sgad	}
36679746Sgad
36779746Sgad	/* the 'H'ost and 'P'erson fields are *always* supposed to be there */
36879746Sgad	if (cpriv->pub.cji_accthost == NULL)
36979746Sgad		cpriv->pub.cji_accthost = strdup(".na.");
37079746Sgad	if (cpriv->pub.cji_acctuser == NULL)
37179746Sgad		cpriv->pub.cji_acctuser = strdup(".na.");
37279746Sgad
37379746Sgad#ifdef DEBUGREADCF_FNAME
37479746Sgad	if (ctl_dbgfile != NULL) {
37579746Sgad		if (cpriv->cji_dumpit)
37679746Sgad			ctl_dumpcji(ctl_dbgfile, "end readcf", &(cpriv->pub));
37779746Sgad		fclose(ctl_dbgfile);
37879746Sgad		ctl_dbgfile = NULL;
37979746Sgad	}
38079746Sgad#endif
38179746Sgad	return &(cpriv->pub);
38279746Sgad}
38379746Sgad
38479746Sgad/*
38579746Sgad * This routine renames the temporary control file as received from some
38679746Sgad * other (remote) host.  That file will always start with 'tfA*', because
38779746Sgad * that's the name it is created with in recvjob.c.  This will rewrite
38879746Sgad * the file to 'tfB*' (correcting any lines which need correcting), rename
38979746Sgad * 'tfB*' to 'cfA*', and then remove the original 'tfA*' file.
39079746Sgad *
39179746Sgad * The purpose of this routine is to be a little paranoid about the contents
39279746Sgad * of that control file.  It is partially meant to protect against people
39379746Sgad * TRYING to cause trouble (perhaps after breaking into root of some host
39479746Sgad * that this host will accept print jobs from).  The fact that we're willing
39579746Sgad * to print jobs from some remote host does not mean that we should blindly
39679746Sgad * do anything that host tells us to do.
39779746Sgad *
39879746Sgad * This is also meant to protect us from errors in other implementations of
39979746Sgad * lpr, particularly since we may want to use some values from the control
40079746Sgad * file as environment variables when it comes time to print, or as parameters
40179746Sgad * to commands which will be exec'ed, or values in statistics records.
40279746Sgad *
40379746Sgad * This may also do some "conversions" between how different versions of
40479746Sgad * lpr or lprNG define the contents of various lines in a control file.
40579746Sgad *
40679746Sgad * If there is an error, it returns a pointer to a descriptive error message.
40779746Sgad * Error messages which are RETURNED (as opposed to syslog-ed) do not include
40879746Sgad * the printer-queue name.  Let the caller add that if it is wanted.
40979746Sgad */
41079746Sgadchar *
41179746Sgadctl_renametf(const char *ptrname, const char *tfname)
41279746Sgad{
41379746Sgad	int res, newfd, nogood;
41479746Sgad	FILE *newcf;
41579746Sgad	struct cjobinfo *cjinf;
41679746Sgad	char *lbuff, *slash, *cp;
41779746Sgad	char tfname2[NAME_MAX+1], cfname2[NAME_MAX+1];
41879746Sgad	char errm[CTI_LINEMAX];
41979746Sgad
42079746Sgad#ifdef TRIGGERTEST_FNAME
42179746Sgad	struct stat tstat;
42279746Sgad	res = stat(TRIGGERTEST_FNAME, &tstat);
42379746Sgad	if (res == -1) {
42479746Sgad		/*
42579746Sgad		 * if the trigger file does NOT exist in this spool directory,
42679746Sgad		 * then do the exact same steps that the pre-ctlinfo code had
42779746Sgad		 * been doing.  Ie, very little.
42879746Sgad		 */
42979746Sgad		strlcpy(cfname2, tfname, sizeof(cfname2));
43079746Sgad		cfname2[0] = 'c';
43179746Sgad		res = link(tfname, cfname2);
43279746Sgad		if (res < 0) {
43379746Sgad			snprintf(errm, sizeof(errm),
43479746Sgad			    "ctl_renametf error link(%s,%s): %s", tfname,
43579746Sgad			    cfname2, strerror(errno));
43679746Sgad			return strdup(errm);
43779746Sgad		}
43879746Sgad		unlink(tfname);
43979746Sgad		return NULL;
44079746Sgad	}
44179746Sgad#endif
44279746Sgad	cjinf = NULL;		/* in case of early jump to error_ret */
44379746Sgad	newcf = NULL;		/* in case of early jump to error_ret */
44479746Sgad	*errm = '\0';		/* in case of early jump to error_ret */
44579746Sgad
44679746Sgad	if (strncmp(tfname, "tfA", (size_t)3) != 0) {
44779746Sgad		snprintf(errm, sizeof(errm),
44879746Sgad		    "ctl_renametf invalid filename: %s", tfname);
44979746Sgad		goto error_ret;
45079746Sgad	}
45179746Sgad
45279746Sgad	cjinf = ctl_readcf(ptrname, tfname);
45379746Sgad	if (cjinf == NULL) {
45479746Sgad		snprintf(errm, sizeof(errm),
45579746Sgad		    "ctl_renametf error cti_readcf(%s)", tfname);
45679746Sgad		goto error_ret;
45779746Sgad	}
45879746Sgad
45979746Sgad	/*
46079746Sgad	 * This uses open+fdopen instead of fopen because that combination
46179746Sgad	 * gives us greater control over file-creation issues.
46279746Sgad	 */
46379746Sgad	strlcpy(tfname2, tfname, sizeof(tfname2));
46479746Sgad	tfname2[2] = 'B';		/* tfB<job><hostname> */
46579746Sgad	newfd = open(tfname2, O_WRONLY|O_CREAT|O_TRUNC, 0660);
46679746Sgad	if (newfd == -1) {
46779746Sgad		snprintf(errm, sizeof(errm),
46879746Sgad		    "ctl_renametf error open(%s): %s", tfname2,
46979746Sgad		    strerror(errno));
47079746Sgad		goto error_ret;
47179746Sgad	}
47279746Sgad	newcf = fdopen(newfd, "w");
47379746Sgad	if (newcf == NULL) {
47479746Sgad		close(newfd);
47579746Sgad		snprintf(errm, sizeof(errm),
47679746Sgad		    "ctl_renametf error fopen(%s): %s", tfname2,
47779746Sgad		    strerror(errno));
47879746Sgad		goto error_ret;
47979746Sgad	}
48079746Sgad
48179746Sgad	/*
48279746Sgad	 * Do extra sanity checks on some key job-attribute fields, and
48379746Sgad	 * write them out first (thus making sure they are written in the
48479746Sgad	 * order we generally expect them to be in).
48579746Sgad	 */
48679746Sgad	/*
48779746Sgad	 * Some lpr implementations on PC's set a null-string for their
48879746Sgad	 * hostname.  A MacOS 10 system which has not correctly setup
48979746Sgad	 * /etc/hostconfig will claim a hostname of 'localhost'.  Anything
49079746Sgad	 * with blanks in it would be an invalid value for hostname.  For
49179746Sgad	 * any of these invalid hostname values, replace the given value
49279746Sgad	 * with the name of the host that this job is coming from.
49379746Sgad	 */
49479746Sgad	nogood = 0;
49579746Sgad	if (cjinf->cji_accthost == NULL)
49679746Sgad		nogood = 1;
49779746Sgad	else if (strcmp(cjinf->cji_accthost, ".na.") == 0)
49879746Sgad		nogood = 1;
49979746Sgad	else if (strcmp(cjinf->cji_accthost, "localhost") == 0)
50079746Sgad		nogood = 1;
50179746Sgad	else {
50279746Sgad		for (cp = cjinf->cji_accthost; *cp != '\0'; cp++) {
50379746Sgad			if (*cp <= ' ') {
50479746Sgad				nogood = 1;
50579746Sgad				break;
50679746Sgad			}
50779746Sgad		}
50879746Sgad	}
50979746Sgad	if (nogood)
51079746Sgad		fprintf(newcf, "H%s\n", from_host);
51179746Sgad	else
51279746Sgad		fprintf(newcf, "H%s\n", cjinf->cji_accthost);
51379746Sgad
51479746Sgad	/*
51579746Sgad	 * Now do some sanity checks on the 'P' (original userid) value.  Note
51679746Sgad	 * that the 'P'erson line is the second line which is ALWAYS supposed
51779746Sgad	 * to be present in a control file.
51879746Sgad	 *
51979746Sgad	 * There is no particularly good value to use for replacements, but
52079746Sgad	 * at least make sure the value is something reasonable to use in
52179746Sgad	 * environment variables and statistics records.  Again, some PC
52279746Sgad	 * implementations send a null-string for a value.  Various Mac
52379746Sgad	 * implementations will set whatever string the user has set for
52479746Sgad	 * their 'Owner Name', which usually includes blanks, etc.
52579746Sgad	 */
52679746Sgad	nogood = 0;
52779746Sgad	if (cjinf->cji_acctuser == NULL)
52879746Sgad		nogood = 1;
52979746Sgad	else {
53079746Sgad		for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) {
53179746Sgad			if (*cp <= ' ')
53279746Sgad				*cp = '_';
53379746Sgad		}
53479746Sgad	}
53579746Sgad	if (nogood)
53679746Sgad		fprintf(newcf, "P%s\n", ".na.");
53779746Sgad	else
53879746Sgad		fprintf(newcf, "P%s\n", cjinf->cji_acctuser);
53979746Sgad
54079746Sgad	/* No need for sanity checks on class, jobname, "literal" user. */
54179746Sgad	if (cjinf->cji_class != NULL)
54279746Sgad		fprintf(newcf, "C%s\n", cjinf->cji_class);
54379746Sgad	if (cjinf->cji_jobname != NULL)
54479746Sgad		fprintf(newcf, "J%s\n", cjinf->cji_jobname);
54579746Sgad	if (cjinf->cji_username != NULL)
54679746Sgad		fprintf(newcf, "L%s\n", cjinf->cji_username);
54779746Sgad
54879746Sgad	/*
54979746Sgad	 * This should probably add more sanity checks on mailto value.
55079746Sgad	 * Note that if the mailto value is "wrong", then there's no good
55179746Sgad	 * way to know what the "correct" value would be, and we should not
55279746Sgad	 * semd email to some random address.  At least for now, just ignore
55379746Sgad	 * any invalid values.
55479746Sgad	 */
55579746Sgad	nogood = 0;
55679746Sgad	if (cjinf->cji_mailto == NULL)
55779746Sgad		nogood = 1;
55879746Sgad	else {
55979746Sgad		for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) {
56079746Sgad			if (*cp <= ' ') {
56179746Sgad				nogood = 1;
56279746Sgad				break;
56379746Sgad			}
56479746Sgad		}
56579746Sgad	}
56679746Sgad	if (!nogood)
56779746Sgad		fprintf(newcf, "M%s\n", cjinf->cji_mailto);
56879746Sgad
56979746Sgad	/*
57079746Sgad	 * Now go thru the old control file, copying all information which
57179746Sgad	 * hasn't already been written into the new file.
57279746Sgad	 */
57379746Sgad	ctl_rewindcf(cjinf);
57479746Sgad	lbuff = ctl_getline(cjinf);
57579746Sgad	while (lbuff != NULL) {
57679746Sgad		switch (lbuff[0]) {
57779746Sgad		case 'H':
57879746Sgad		case 'P':
57979746Sgad		case 'C':
58079746Sgad		case 'J':
58179746Sgad		case 'L':
58279746Sgad		case 'M':
58379746Sgad			/* already wrote values for these to the newcf */
58479746Sgad			break;
58579746Sgad		case 'N':
58679746Sgad			/* see comments under 'U'... */
58779746Sgad			if (cjinf->cji_dfcount == 0) {
58879746Sgad				/* in this case, 'N's will be done in 'U' */
58979746Sgad				break;
59079746Sgad			}
59179746Sgad			fprintf(newcf, "%s\n", lbuff);
59279746Sgad			break;
59379746Sgad		case 'U':
59479746Sgad			/*
59579746Sgad			 * check for the very common case where the remote
59679746Sgad			 * host had to process 'lpr -s -r', but it did not
59779746Sgad			 * remove the Unlink line from the control file.
59879746Sgad			 * Such Unlink lines will legitimately have a '/' in
59979746Sgad			 * them, but it is the original lpr host which would
60079746Sgad			 * have done the unlink of such files, and not any
60179746Sgad			 * host receiving that job.
60279746Sgad			 */
60379746Sgad			slash = strchr(lbuff, '/');
60479746Sgad			if (slash != NULL) {
60579746Sgad				break;		/* skip this line */
60679746Sgad			}
60779746Sgad			/*
60879746Sgad			 * Okay, another kind of broken lpr implementation
60979746Sgad			 * is one which send datafiles, and Unlink's those
61079746Sgad			 * datafiles, but never includes any PRINT request
61179746Sgad			 * for those files.  Experimentation shows that one
61279746Sgad			 * copy of those datafiles should be printed with a
61379746Sgad			 * format of 'f'.  If this is an example of such a
61479746Sgad			 * screwed-up control file, fix it here.
61579746Sgad			 */
61679746Sgad			if (cjinf->cji_dfcount == 0) {
61779746Sgad				lbuff++;
61879746Sgad				if (strncmp(lbuff, "df", (size_t)2) == 0) {
61979746Sgad					fprintf(newcf, "f%s\n", lbuff);
62079746Sgad					fprintf(newcf, "U%s\n", lbuff);
62179746Sgad					fprintf(newcf, "N%s\n", lbuff);
62279746Sgad				}
62379746Sgad				break;
62479746Sgad			}
62579746Sgad			fprintf(newcf, "%s\n", lbuff);
62679746Sgad			break;
62779746Sgad		default:
62879746Sgad			fprintf(newcf, "%s\n", lbuff);
62979746Sgad			break;
63079746Sgad		}
63179746Sgad		lbuff = ctl_getline(cjinf);
63279746Sgad	}
63379746Sgad
63479746Sgad	ctl_freeinf(cjinf);
63579746Sgad	cjinf = NULL;
63679746Sgad
63779746Sgad	res = fclose(newcf);
63879746Sgad	newcf = NULL;
63979746Sgad	if (res != 0) {
64079746Sgad		snprintf(errm, sizeof(errm),
64179746Sgad		    "ctl_renametf error fclose(%s): %s", tfname2,
64279746Sgad		    strerror(errno));
64379746Sgad		goto error_ret;
64479746Sgad	}
64579746Sgad
64679746Sgad	strlcpy(cfname2, tfname, sizeof(cfname2));
64779746Sgad	cfname2[0] = 'c';		/* rename new file to 'cfA*' */
64879746Sgad	res = link(tfname2, cfname2);
64979746Sgad	if (res != 0) {
65079746Sgad		snprintf(errm, sizeof(errm),
65179746Sgad		    "ctl_renametf error link(%s,%s): %s", tfname2, cfname2,
65279746Sgad		    strerror(errno));
65379746Sgad		goto error_ret;
65479746Sgad	}
65579746Sgad
65679746Sgad	/* All the important work is done.  Now just remove temp files */
65779746Sgad#ifdef LEAVE_TMPCF_FILES
65879746Sgad	{
65979746Sgad		struct stat tfstat;
66079746Sgad		size_t size1;
66179746Sgad		tfstat.st_size = 1;	/* certainly invalid value */
66279746Sgad		res = stat(tfname, &tfstat);
66379746Sgad		size1 = tfstat.st_size;
66479746Sgad		tfstat.st_size = 2;	/* certainly invalid value */
66579746Sgad		res = stat(tfname2, &tfstat);
66679746Sgad		/* if the sizes do not match, or either stat call failed,
66779746Sgad		 * then do not remove the temp files, but return "all OK".
66879746Sgad		 * This is just so I can see what this routine had changed.
66979746Sgad		 */
67079746Sgad		if (size1 != tfstat.st_size)
67179746Sgad			return NULL;
67279746Sgad	}
67379746Sgad#endif
67479746Sgad	unlink(tfname);
67579746Sgad	unlink(tfname2);
67679746Sgad
67779746Sgad	return NULL;
67879746Sgad
67979746Sgaderror_ret:
68079746Sgad	if (cjinf != NULL)
68179746Sgad		ctl_freeinf(cjinf);
68279746Sgad	if (newcf != NULL)
68379746Sgad		fclose(newcf);
68479746Sgad
68579746Sgad	if (*errm != '\0')
68679746Sgad		return strdup(errm);
68779746Sgad	return strdup("ctl_renametf internal (missed) error");
68879746Sgad}
68979746Sgad
69079746Sgadvoid
69179746Sgadctl_rewindcf(struct cjobinfo *cjinf)
69279746Sgad{
69379746Sgad	struct cjprivate *cpriv;
69479746Sgad
69579746Sgad	if (cjinf == NULL)
69679746Sgad		return;
69779746Sgad	cpriv = cjinf->cji_priv;
69879746Sgad	if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
69979746Sgad		syslog(LOG_ERR, "in ctl_rewindcf(%p): invalid cjinf (cpriv %p)",
70079746Sgad		    cjinf, cpriv);
70179746Sgad		return;
70279746Sgad	}
70379746Sgad
70479746Sgad	rewind(cpriv->cji_fstream);		/* assume no errors... :-) */
70579746Sgad}
70679746Sgad
70779746Sgadchar *
70879746Sgadctl_rmjob(const char *ptrname, const char *cfname)
70979746Sgad{
71079746Sgad	struct cjobinfo	*cjinf;
71179746Sgad	char *lbuff;
71279746Sgad	char errm[CTI_LINEMAX];
71379746Sgad
71479746Sgad	cjinf = ctl_readcf(ptrname, cfname);
71579746Sgad	if (cjinf == NULL) {
71679746Sgad		snprintf(errm, sizeof(errm),
71779746Sgad		    "ctl_renametf error cti_readcf(%s)", cfname);
71879746Sgad		return strdup(errm);
71979746Sgad	}
72079746Sgad
72179746Sgad	ctl_rewindcf(cjinf);
72279746Sgad	lbuff = ctl_getline(cjinf);
72379746Sgad	while (lbuff != NULL) {
72479746Sgad		/* obviously we need to fill in the following... */
72579746Sgad		switch (lbuff[0]) {
72679746Sgad		case 'S':
72779746Sgad			break;
72879746Sgad		case 'U':
72979746Sgad			break;
73079746Sgad		default:
73179746Sgad			break;
73279746Sgad		}
73379746Sgad		lbuff = ctl_getline(cjinf);
73479746Sgad	}
73579746Sgad
73679746Sgad	ctl_freeinf(cjinf);
73779746Sgad	cjinf = NULL;
73879746Sgad
73979746Sgad	return NULL;
74079746Sgad}
74179746Sgad
74279746Sgad/*
74379746Sgad * The following routine was originally written to pin down a bug.  It is
74479746Sgad * no longer needed for that problem, but may be useful to keep around for
74579746Sgad * other debugging.
74679746Sgad */
74779746Sgadvoid
74879746Sgadctl_dumpcji(FILE *dbg_stream, const char *heading, struct cjobinfo *cjinf)
74979746Sgad{
75079746Sgad#define PRINTSTR(xHdr,xStr) \
75179746Sgad	astr = xStr; \
75279746Sgad	ctl_dbgline++; \
75379746Sgad	fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, xHdr); \
75479746Sgad	if (astr == NULL) \
75579746Sgad		fprintf(dbg_stream, "NULL\n"); \
75679746Sgad	else \
75779746Sgad		fprintf(dbg_stream, "%p -> %s\n", astr, astr)
75879746Sgad
75979746Sgad	struct cjprivate *cpriv;
76079746Sgad	char *astr;
76179746Sgad
76279746Sgad	if (cjinf == NULL) {
76379746Sgad		fprintf(dbg_stream,
76479746Sgad		    "ctl_dumpcji: ptr to cjobinfo for '%s' is NULL\n",
76579746Sgad		    heading);
76679746Sgad		return;
76779746Sgad	}
76879746Sgad	cpriv = cjinf->cji_priv;
76979746Sgad
77079746Sgad	fprintf(dbg_stream, "ctl_dumpcji: Dump '%s' of cjobinfo at %p->%p\n",
77179746Sgad	    heading, cjinf, cpriv->cji_buff);
77279746Sgad
77379746Sgad	PRINTSTR("accthost.H", cpriv->pub.cji_accthost);
77479746Sgad	PRINTSTR("acctuser.P", cpriv->pub.cji_acctuser);
77579746Sgad	PRINTSTR("class.C", cpriv->pub.cji_class);
77679746Sgad	PRINTSTR("cf-qname", cpriv->pub.cji_curqueue);
77779746Sgad	PRINTSTR("cf-fname", cpriv->pub.cji_fname);
77879746Sgad	PRINTSTR("jobname.J", cpriv->pub.cji_jobname);
77979746Sgad	PRINTSTR("mailto.M", cpriv->pub.cji_mailto);
78079746Sgad	PRINTSTR("hdruser.L", cpriv->pub.cji_username);
78179746Sgad
78279746Sgad	ctl_dbgline++;
78379746Sgad	fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, "*cjprivate");
78479746Sgad	if (cpriv->pub.cji_priv == NULL)
78579746Sgad		fprintf(dbg_stream, "NULL !!\n");
78679746Sgad	else
78779746Sgad		fprintf(dbg_stream, "%p\n", cpriv->pub.cji_priv);
78879746Sgad
78979746Sgad	fprintf(dbg_stream, "|- - - - --> Dump '%s' complete\n", heading);
79079746Sgad
79179746Sgad	/* flush output for the benefit of anyone doing a 'tail -f' */
79279746Sgad	fflush(dbg_stream);
79379746Sgad
79479746Sgad#undef PRINTSTR
79579746Sgad}
79679746Sgad
79779746Sgad/*
79879746Sgad * This routine reads in the next line from the control-file, and removes
79979746Sgad * the trailing newline character.
80079746Sgad *
80179746Sgad * Historical note: Earlier versions of this routine did tab-expansion for
80279746Sgad * ALL lines read in, which did not make any sense for most of the lines
80379746Sgad * in a control file.  For the lines where tab-expansion is useful, it will
80479746Sgad * now have to be done by the calling routine.
80579746Sgad */
80679746Sgadstatic char *
80779746Sgadctl_getline(struct cjobinfo *cjinf)
80879746Sgad{
80979746Sgad	char *strp, *nl;
81079746Sgad	struct cjprivate *cpriv;
81179746Sgad
81279746Sgad	if (cjinf == NULL)
81379746Sgad		return NULL;
81479746Sgad	cpriv = cjinf->cji_priv;
81579746Sgad	if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
81679746Sgad		syslog(LOG_ERR, "in ctl_getline(%p): invalid cjinf (cpriv %p)",
81779746Sgad		    cjinf, cpriv);
81879746Sgad		return NULL;
81979746Sgad	}
82079746Sgad
82179746Sgad	errno = 0;
82279746Sgad	strp = fgets(cpriv->cji_buff, cpriv->cji_buffsize, cpriv->cji_fstream);
82379746Sgad	if (strp == NULL) {
82479746Sgad		if (errno != 0)
82579746Sgad			syslog(LOG_ERR, "%s: ctl_getline error fgets(%s): %s",
82679746Sgad			    cpriv->pub.cji_curqueue, cpriv->pub.cji_fname,
82779746Sgad			    strerror(errno));
82879746Sgad		return NULL;
82979746Sgad	}
83079746Sgad	nl = strchr(strp, '\n');
83179746Sgad	if (nl != NULL)
83279746Sgad		*nl = '\0';
83379746Sgad
83479746Sgad#ifdef DEBUGREADCF_FNAME
83579746Sgad	/* I'd like to find out if the previous work to expand tabs was ever
83679746Sgad	 * really used, and if so, on what lines and for what reason.
83779746Sgad	 * Yes, all this work probably means I'm obsessed about this 'tab'
83879746Sgad	 * issue, but isn't programming a matter of obsession?
83979746Sgad	 */
84079746Sgad	{
84179746Sgad		int tabcnt;
84279746Sgad		char *ch;
84379746Sgad
84479746Sgad		tabcnt = 0;
84579746Sgad		ch = strp;
84679746Sgad		for (ch = strp; *ch != '\0'; ch++) {
84779746Sgad			if (*ch == '\t')
84879746Sgad				tabcnt++;
84979746Sgad		}
85079746Sgad
85179746Sgad		if (tabcnt && (ctl_dbgfile != NULL)) {
85279746Sgad			cpriv->cji_dumpit++;
85379746Sgad			fprintf(ctl_dbgfile, "%s: tabs=%d '%s'\n",
85479746Sgad			    cpriv->pub.cji_fname, tabcnt, cpriv->cji_buff);
85579746Sgad		}
85679746Sgad	}
85779746Sgad#endif
85879746Sgad	return strp;
85979746Sgad}
860