/***************************************************************************
 * LPRng - An Extended Print Spooler System
 *
 * Copyright 1988-2003, Patrick Powell, San Diego, CA
 *     papowell@lprng.com
 * See LICENSE for conditions of use.
 *
 ***************************************************************************/

 static char *const _id =
"$Id: lpstat.c,v 1.74 2004/09/24 20:19:58 papowell Exp $";


/***************************************************************************
 * LPRng - An Extended Print Spooler System
 *
 * Copyright 1988-2003, Patrick Powell, San Diego, CA
 *     papowell@lprng.com
 * See LICENSE for conditions of use.
 *
 ***************************************************************************
 * MODULE: lpstat.c
 * PURPOSE:
 **************************************************************************/

/***************************************************************************
 * SYNOPSIS
 *   lpstat  [-d]  [-r]  [-R]  [-s]  [-t]  [-a  [list
 *  ]  ]  [-c  [list] ]  [-f  [list]  [-l] ]  [-o  [
 *   list] ]  [-p   [list]  [-D]  [-l] ]  [-S  [list
 *  ]   [-l] ]  [-u  [login- ID -list ] ]  [-v  [list]
 *  ]
 *
 *    lpstat 
 * DESCRIPTION
 *   lpstat sends a status request to lpd(8)
 * See lpstat for details on the way that status is gathered.
 *   The lpstat version simulates the lpstat operation,
 * using the information returned by LPRng in response to a verbose query.
 * 
 ****************************************************************************
 *
 */

#include "lp.h"

#include "child.h"
#include "getopt.h"
#include "getprinter.h"
#include "initialize.h"
#include "linksupport.h"
#include "patchlevel.h"
#include "sendreq.h"

/**** ENDINCLUDE ****/


#undef EXTERN
#undef DEFINE
#define EXTERN
#define DEFINE(X) X
#include "lpstat.h"
/**** ENDINCLUDE ****/

 int A_flag, P_flag, R_flag, S_flag, a_flag, c_flag, d_flag, f_flag, l_flag, n_flag,
	o_flag, p_flag, r_flag, s_flag, t_flag, u_flag, v_flag, flag_count,
	D_flag, Found_flag;
 char *S_val, *a_val, *c_val, *f_val, *o_val, *p_val, *u_val, *v_val;
 struct line_list S_list, f_list;

 struct line_list Lpq_options;
 int Rawformat;

#define MAX_SHORT_STATUS 6

/***************************************************************************
 * main()
 * - top level of LPQ
 *
 ****************************************************************************/

int main(int argc, char *argv[], char *envp[])
{
	int i;
	struct line_list l, options, request_list;
	char msg[SMALLBUFFER], *s;

	Init_line_list(&l);
	Init_line_list(&options);
	Init_line_list(&request_list);

	/* set signal handlers */
	(void) plp_signal (SIGHUP, cleanup_HUP);
	(void) plp_signal (SIGINT, cleanup_INT);
	(void) plp_signal (SIGQUIT, cleanup_QUIT);
	(void) plp_signal (SIGTERM, cleanup_TERM);
	(void) signal(SIGCHLD, SIG_DFL);
	(void) signal(SIGPIPE, SIG_IGN);

	/*
	 * set up the user state
	 */

	Status_line_count = 1;

#ifndef NODEBUG
	Debug = 0;
#endif

	Displayformat = REQ_DLONG;

	Initialize(argc, argv, envp, 'T' );
	Setup_configuration();
	Get_parms(argc, argv );      /* scan input args */
	if( A_flag && !getenv( "AUTH" ) ){
		FPRINTF(STDERR,"lpstat: requested authenticated transfer (-A) and AUTH environment variable not set");
		usage();
	}

	/* set up configuration */
	Get_printer();
	Fix_Rm_Rp_info(0,0);
	Get_all_printcap_entries();

	/* check on printing scheduler is running */
	if( t_flag ){
		All_printers = 1;
		r_flag = d_flag = p_flag = o_flag = 1;
		s_flag = 0;
	}
	if( s_flag ){
		/* a_flag = 1; */
		r_flag = 1;
		d_flag = 1;
		v_flag = 1;
		All_printers = 1;
	}

	if( All_printers ){
		Merge_line_list( &request_list, &All_line_list,0,0,0);
	}
	Merge_line_list( &request_list, &Printer_list,0,0,0);
	Check_max(&options,2);
	if( options.count ){
		for( i = options.count; i > 0 ; --i ){
			options.list[i] = options.list[i-1];
		}
		options.list[0] = safestrdup(Logname_DYN,__FILE__,__LINE__);
		++options.count;
	}
	options.list[options.count] = 0;

	if( Found_flag == 0 ){
		if( request_list.count == 0 ){
			Split(&request_list,Printer_DYN,", ",1,0,1,1,0,0);
		}
		o_flag = 1;
		flag_count = 1;
	}
	if(DEBUGL1)Dump_line_list("lpstat - printer request list", &request_list);
	if(DEBUGL1)Dump_line_list("lpstat - options", &options);

	if( r_flag ){
		Write_fd_str(1,"scheduler is running\n");
	}
	if( d_flag ){
		if( Printer_DYN == 0 ){
			Write_fd_str(1,"no system default destination\n");
		} else {
			SNPRINTF(msg,sizeof(msg))
				"system default destination: %s\n", Printer_DYN);
			Write_fd_str(1,msg);
		}
	}
	if( v_flag ){
		for( i = 0; i < request_list.count; ++i ){
			Set_DYN(&Printer_DYN,request_list.list[i] );
			Fix_Rm_Rp_info(0,0);
			SNPRINTF(msg,sizeof(msg)) "system for %s: %s\n", Printer_DYN, RemoteHost_DYN);
			Write_fd_str(1,msg);
		}
	}

	/* see if additional status required */

	Free_line_list( &Printer_list );

	for( i = 0; i < request_list.count; ++i ){
		s = request_list.list[i];
		Set_DYN(&Printer_DYN,s );
		Show_status(options.list, 0);
	}

	Free_line_list( &Printer_list );
	if( flag_count ){
		for( i = 0; i < request_list.count; ++i ){
			s = request_list.list[i];
			Set_DYN(&Printer_DYN,s );
			Show_status(options.list, 1);
		}
	}

	DEBUG1("lpstat: done");
	Remove_tempfiles();
	DEBUG1("lpstat: tempfiles removed");

	Errorcode = 0;
	DEBUG1("lpstat: cleaning up");
	return(0);
}

void Show_status(char **argv, int display_format)
{
	int fd;
	char msg[LINEBUFFER];

	DEBUG1("Show_status: start");
	/* set up configuration */
	Fix_Rm_Rp_info(0,0);

	if( Check_for_rg_group( Logname_DYN ) ){
		SNPRINTF( msg, sizeof(msg))
			"  Printer: %s - cannot use printer, not in privileged group\n", Printer_DYN );
		if(  Write_fd_str( 1, msg ) < 0 ) cleanup(0);
		return;
	}
	if( A_flag ){
		Set_DYN(&Auth_DYN, getenv("AUTH"));
	}
	if( Direct_DYN && Lp_device_DYN ){
		SNPRINTF( msg, sizeof(msg))
			_(" Printer: %s - direct connection to device '%s'\n"),
			Printer_DYN, Lp_device_DYN );
		if(  Write_fd_str( 1, msg ) < 0 ) cleanup(0);
		return;
	}
	fd = Send_request( 'Q', Displayformat,
		0, Connect_timeout_DYN, Send_query_rw_timeout_DYN, 1 );
	if( fd >= 0 ){
		/* shutdown( fd, 1 ); */
		Read_status_info( RemoteHost_DYN, fd,
			1, Send_query_rw_timeout_DYN, display_format,
			Status_line_count );
		close(fd); fd = -1;
	}
	DEBUG1("Show_status: end");
}


/***************************************************************************
 *int Read_status_info( int ack, int fd, int timeout );
 * ack = ack character from remote site
 * sock  = fd to read status from
 * char *host = host we are reading from
 * int output = output fd
 *  We read the input in blocks,  split up into lines.
 *  We run the status through the SNPRINTF() routine,  which will
 *   rip out any unprintable characters.  This will prevent magic escape
 *   string attacks by users putting codes in job names, etc.
 ***************************************************************************/

int Read_status_info( char *host, int sock,
	int output, int timeout, int display_format,
	int status_line_count )
{
	int i, j, len, n, status, count, index_list, same;
	char buffer[SMALLBUFFER], header[SMALLBUFFER];
	char *s, *t;
	struct line_list l;
	int look_for_pr = 0;

	Init_line_list(&l);

	header[0] = 0;
	status = count = 0;
	/* long status - trim lines */
	DEBUG1("Read_status_info: output %d, timeout %d, dspfmt %d",
		output, timeout, display_format );
	DEBUG1("Read_status_info: status_line_count %d", status_line_count );
	buffer[0] = 0;
	do {
		DEBUG1("Read_status_info: look_for_pr %d, in buffer already '%s'", look_for_pr, buffer );
		if( DEBUGL2 )Dump_line_list("Read_status_info - starting list", &l );
		count = safestrlen(buffer);
		n = sizeof(buffer)-count-1;
		status = 1;
		if( n > 0 ){
			status = Link_read( host, &sock, timeout,
				buffer+count, &n );
			DEBUG1("Read_status_info: Link_read status %d, read %d", status, n );
		}
		if( status || n <= 0 ){
			status = 1;
			buffer[count] = 0;
		} else {
			buffer[count+n] = 0;
		}
		DEBUG1("Read_status_info: got '%s'", buffer );
		/* now we have to split line up */
		if( (s = safestrrchr(buffer,'\n')) ){
			*s++ = 0;
			/* add the lines */
			Split(&l,buffer,Line_ends,0,0,0,0,0,0);
			memmove(buffer,s,safestrlen(s)+1);
		}
		if( DEBUGL2 )Dump_line_list("Read_status_info - status after splitting", &l );
		if( status ){
			if( buffer[0] ){
				Add_line_list(&l,buffer,0,0,0);
				buffer[0] = 0;
			}
			Check_max(&l,1);
			l.list[l.count++] = 0;
		}
		index_list = 0;
 again:
		DEBUG2("Read_status_info: look_for_pr '%d'", look_for_pr );
		if( DEBUGL2 )Dump_line_list("Read_status_info - starting, Printer_list",
			&Printer_list);
		while( look_for_pr && index_list < l.count ){
			s = l.list[index_list];
			if( s == 0 || isspace(cval(s)) || !(t = strstr(s,"Printer:"))
				|| Find_exists_value(&Printer_list,t,0) ){
				++index_list;
			} else {
				DEBUG2("Read_status_info: printer found [%d] '%s', total %d", index_list, s, l.count );
				look_for_pr = 0;
			}
		}
		while( index_list < l.count && (s = l.list[index_list]) ){
			DEBUG2("Read_status_info: checking [%d] '%s', total %d", index_list, s, l.count );
			if( s && !isspace(cval(s)) && (t = strstr(s,"Printer:")) ){
				DEBUG2("Read_status_info: printer heading '%s'", t );
				if( Find_exists_value(&Printer_list,t,0) ){
					DEBUG2("Read_status_info: already done '%s'", t );
					++index_list;
					look_for_pr = 1;
					goto again;
				}
				Add_line_list(&Printer_list,t,0,1,0);
				/* parse the printer line */
				if( (t = strchr(t, ':')) ){
					++t;
					while( isspace(cval(t)) ) ++t;
					Set_DYN(&Printer_DYN,t );
					for( t = Printer_DYN; !ISNULL(t) && !isspace(cval(t)); ++t );
					if( isspace(cval(t)) ) *t = 0;
				}
				if( display_format == 0 ){
					char msg[SMALLBUFFER];
					int nospool, noprint; 
					nospool = (strstr( s, "(spooling disabled)") != 0);
					noprint = (strstr( s, "(printing disabled)") != 0);
					/* Write_fd_str( output, "ANALYZE " );
					if( Write_fd_str( output, s ) < 0
						|| Write_fd_str( output, "\n" ) < 0 ) return(1);
					*/
					if( a_flag ){
						if( !nospool ){
							SNPRINTF(msg,sizeof(msg))
							"%s accepting requests since %s\n",
							Printer_DYN, Time_str(0,0) );
						} else {
							SNPRINTF(msg,sizeof(msg))
							"%s not accepting requests since %s -\n\tunknown reason\n",
							Printer_DYN, Time_str(0,0) );
						}
						if( Write_fd_str( output, msg ) < 0 ) return(1);
					}
					if( p_flag ){
						SNPRINTF(msg,sizeof(msg))
						"printer %s unknown state. %s since %s. available\n",
						Printer_DYN, noprint?"disabled":"enabled",
						Pretty_time(0));
						if( Write_fd_str( output, msg ) < 0 ) return(1);
					}
					if( p_flag && D_flag ){
						SNPRINTF(msg,sizeof(msg))
							"\tDescription: %s@%s\n",
									RemotePrinter_DYN, RemoteHost_DYN ); 
						if( Write_fd_str( output, msg ) < 0 ) return(1);
					}
				} else {
					if( Write_fd_str( output, s ) < 0
						|| Write_fd_str( output, "\n" ) < 0 ) return(1);
				}
				++index_list;
				continue;
			}
			if( display_format == 0 ){
				++index_list;
				continue;
			}
			if( status_line_count > 0 ){
				/*
				 * starting at line_index, we take this as the header.
				 * then we check to see if there are at least status_line_count there.
				 * if there are,  we will increment status_line_count until we get to
				 * the end of the reading (0 string value) or end of list.
				 */
				header[0] = 0;
				strncpy( header, s, sizeof(header)-1);
				header[sizeof(header)-1] = 0;
				if( (s = strchr(header,':')) ){
					*++s = 0;
				}
				len = safestrlen(header);
				/* find the last status_line_count lines */
				same = 1;
				for( i = index_list+1; i < l.count ; ++i ){
					same = !safestrncmp(l.list[i],header,len);
					DEBUG2("Read_status_info: header '%s', len %d, to '%s', same %d",
						header, len, l.list[i], same );
					if( !same ){
						break;
					}
				}
				/* if they are all the same,  then we save for next pass */
				/* we print the i - status_line count to i - 1 lines */
				j = i - status_line_count;
				if( index_list < j ) index_list = j;
				DEBUG2("Read_status_info: header '%s', index_list %d, last %d, same %d",
					header, index_list, i, same );
				if( same ) break;
				while( index_list < i ){
					DEBUG2("Read_status_info: writing [%d] '%s'",
						index_list, l.list[index_list]);
					if( Write_fd_str( output, l.list[index_list] ) < 0
						|| Write_fd_str( output, "\n" ) < 0 ) return(1);
					++index_list;
				}
			} else {
				if( Write_fd_str( output, s ) < 0
					|| Write_fd_str( output, "\n" ) < 0 ) return(1);
				++index_list;
			}
		}
		DEBUG2("Read_status_info: at end index_list %d, count %d", index_list, l.count );
		for( i = 0; i < l.count && i < index_list; ++i ){
			if( l.list[i] ) free( l.list[i] ); l.list[i] = 0;
		}
		for( i = 0; index_list < l.count ; ++i, ++index_list ){
			l.list[i] = l.list[index_list];
		}
		l.count = i;
	} while( status == 0 );
	Free_line_list(&l);
	DEBUG1("Read_status_info: done" );
	return(0);
}
int Add_val( char **var, char *val )
{
	int c = 0;
	if( val && cval(val) != '-' ){
		c = 1;
		if( *var ){
			*var = safeextend3(*var,",",val,__FILE__,__LINE__);
		} else {
			*var = safestrdup(val,__FILE__,__LINE__);
		}
	}
	return(c);
}

/***************************************************************************
 * void Get_parms(int argc, char *argv[])
 * 1. Scan the argument list and get the flags
 * 2. Check for duplicate information
 ***************************************************************************/


void Get_parms(int argc, char *argv[] )
{
	int i;
	char *name, *s;

	if( argv[0] && (name = strrchr( argv[0], '/' )) ) {
		++name;
	} else {
		name = argv[0];
	}
	Name = name;

/*
 SYNOPSIS
     lpstat [-A] [ -d ] [ -r ] [ -R ] [ -s ] [ -t ] [ -a [list] ]
          [ -c [list] ] [ -f [list] [ -l ] ] [ -o [list] ]
          [ -p [list] [ -D] [ -l ] ] [ -P ] [ -S [list] [ -l ] ]
          [ -u [login-ID-list] ] [ -v [list] ] [-n linecount] [-Tdebug]
*/
	flag_count = 0;
	for( i = 1; i < argc; ++i ){
		s = argv[i];
		if( cval(s) == '-' ){
			Found_flag = 1;
			switch( cval(s+1) ){

			case 'd':               d_flag = 1; if(cval(s+2)) usage(); break;
			case 'D':               D_flag = 1; if(cval(s+2)) usage(); break;
			case 'r':               r_flag = 1; if(cval(s+2)) usage(); break;
			case 'A':               A_flag = 1; if(cval(s+2)) usage(); break;
			case 'R':               R_flag = 1; if(cval(s+2)) usage(); break;
			case 's':               s_flag = 1; if(cval(s+2)) usage(); break;
			case 't': ++flag_count; t_flag = 1; if(cval(s+2)) usage(); break;
			case 'l':               l_flag = 1; if(cval(s+2)) usage(); break;
			case 'n':               n_flag += 1; if(cval(s+2)) usage(); break;
			case 'N':               n_flag = -1; if(cval(s+2)) usage(); break;
			case 'P':               P_flag = 1; if(cval(s+2)) usage(); break;
			case 'a':               a_flag = 1; if( cval(s+2) ) Add_val(&a_val,s+2); else { i += Add_val(&a_val,argv[i+1]); } break;
			case 'c': ++flag_count; c_flag = 1; if( cval(s+2) ) Add_val(&c_val,s+2); else { i += Add_val(&c_val,argv[i+1]); } break;
			case 'f': ++flag_count; f_flag = 1; if( cval(s+2) ) Add_val(&f_val,s+2); else { i += Add_val(&f_val,argv[i+1]); } break;
			case 'o': ++flag_count; o_flag = 1; if( cval(s+2) ) Add_val(&o_val,s+2); else { i += Add_val(&o_val,argv[i+1]); } break;
			case 'p': ++flag_count; p_flag = 1; if( cval(s+2) ) Add_val(&p_val,s+2); else { i += Add_val(&p_val,argv[i+1]); } break;
			case 'S': ++flag_count; S_flag = 1; if( cval(s+2) ) Add_val(&S_val,s+2); else { i += Add_val(&S_val,argv[i+1]); } break;
			case 'u': ++flag_count; u_flag = 1; if( cval(s+2) ) Add_val(&u_val,s+2); else { i += Add_val(&u_val,argv[i+1]); } break;
			case 'v':               v_flag = 1; if( cval(s+2) ) Add_val(&v_val,s+2); else { i += Add_val(&v_val,argv[i+1]); } break;
			case 'V': Verbose = 1; break;
			case 'T': if(!cval(s+2)) usage(); Parse_debug( s+2, 1 ); break;
			default: usage(); break;

			}
		} else {
			break;
		}
		if(DEBUGL1){
			LOGDEBUG("A_flag %d, D_flag %d, d_flag %d, r_flag %d, R_flag %d, s_flag %d, t_flag %d, l_flag %d, P_flag %d",
				A_flag, D_flag, d_flag, r_flag, R_flag, s_flag, t_flag, l_flag, P_flag );
			LOGDEBUG("a_flag %d, a_val '%s'", a_flag, a_val );
			LOGDEBUG("c_flag %d, c_val '%s'", c_flag, c_val );
			LOGDEBUG("f_flag %d, f_val '%s'", f_flag, f_val );
			LOGDEBUG("o_flag %d, o_val '%s'", o_flag, o_val );
			LOGDEBUG("p_flag %d, p_val '%s'", p_flag, p_val );
			LOGDEBUG("S_flag %d, S_val '%s'", S_flag, S_val );
			LOGDEBUG("u_flag %d, u_val '%s'", u_flag, u_val );
			LOGDEBUG("v_flag %d, v_val '%s'", v_flag, v_val );
			LOGDEBUG("n_flag %d", n_flag );
		}
	}
	for( ; i < argc; ++i ){
		Add_line_list( &Printer_list, argv[i], 0, 1, 1 );
	}

#undef SX
#define SX(X,Y,Z) \
	if((X)&&!(Y))Y="all"; Split(&Z,Y,", ",1,0,1,1,0,0);
	SX(a_flag,a_val,Printer_list);
	SX(c_flag,c_val,Printer_list);
	SX(f_flag,f_val,f_list);
	SX(o_flag,o_val,Printer_list);
	SX(p_flag,p_val,Printer_list);
	SX(S_flag,S_val,S_list);
	SX(u_flag,u_val,Printer_list);
	SX(v_flag,v_val,Printer_list);
#undef SX

	if( n_flag ){
		if( n_flag < 0 ){
			Status_line_count = -1;
		} else {
			Status_line_count = (1 << n_flag);
		}
	}
	if( Verbose ) {
		FPRINTF( STDOUT, "%s\n", Version );
		if( Verbose > 1 ) Printlist( Copyright, 1 );
	}
	for( i = 0; !All_printers && i < Printer_list.count; ++i ){
		s = Printer_list.list[i];
		All_printers = !safestrcasecmp(s,"all");
	}
	if(DEBUGL1){
		LOGDEBUG("All_printers %d, D_flag %d, d_flag %d, r_flag %d, R_flag %d, s_flag %d, t_flag %d, l_flag %d, P_flag %d",
			All_printers, D_flag,  d_flag, r_flag, R_flag, s_flag, t_flag, l_flag, P_flag );
		LOGDEBUG("a_flag %d, a_val '%s'", a_flag, a_val );
		LOGDEBUG("c_flag %d, c_val '%s'", c_flag, c_val );
		LOGDEBUG("f_flag %d, f_val '%s'", f_flag, f_val );
		LOGDEBUG("o_flag %d, o_val '%s'", o_flag, o_val );
		LOGDEBUG("p_flag %d, p_val '%s'", p_flag, p_val );
		LOGDEBUG("S_flag %d, S_val '%s'", S_flag, S_val );
		LOGDEBUG("u_flag %d, u_val '%s'", u_flag, u_val );
		LOGDEBUG("v_flag %d, v_val '%s'", v_flag, v_val );
		Dump_line_list("lpstat - Printer_list", &Printer_list);
	}
}

 static char *lpstat_msg[] = {
	"usage: %s [-A] [-d] [-l] [-r] [-R] [-s] [-t] [-a [list]]\n",
	"  [-c [list]] [-f [list]] [-o [list]]\n",
	"  [-p [list]] [-P] [-S [list]] [list]\n",
	"  [-u [login-ID-list]] [-v [list]] [-V] [-n] [-Tdbgflags]\n",
	" list is a list of print queues\n",
	" -A        use authentication specified by AUTH environment variable\n",
	" -a [list] destination status *\n",
	" -c [list] class status *\n",
	" -d        print default destination\n",
	" -f [list] forms status *\n",
	" -o [list] job or printer status *\n",
	" -n        each -n increases number of status lines (default 1) *\n",
	" -N        maximum number of status lines *\n",
	" -p [list] printer status *\n",
	" -P        paper types - ignored\n",
	" -r        scheduler status\n",
	" -s        summary status information - short format\n",
	" -S [list] character set - ignored\n",
	" -t        all status information - long format\n",
	" -u [joblist] job status information\n",
	" -v [list] printer mapping *\n",
	" -V        verbose mode \n",
	" -Tdbgflags debug flags\n",
	"    * - long status format produced\n",
	0 };


void usage(void)
{
	char **sptr, *s;
	for( sptr= lpstat_msg; (s = *sptr); ++sptr ){
			FPRINTF( STDERR, s, Name );
	}
	Parse_debug("=",-1);
	FPRINTF( STDOUT, "%s\n", Version );
	exit(1);
}

#if 0

#include "permission.h"
#include "lpd.h"
#include "lpd_status.h"
/* int Send_request( */
	int class,					/* 'Q'= LPQ, 'C'= LPC, M = lprm */
	int format,					/* X for option */
	char **options,				/* options to send */
	int connect_timeout,		/* timeout on connection */
	int transfer_timeout,		/* timeout on transfer */
	int output					/* output on this FD */
	)
{
	int i, n;
	int socket = 1;
	char cmd[SMALLBUFFER];

	cmd[0] = format;
	cmd[1] = 0;
	SNPRINTF(cmd+1, sizeof(cmd)-1, RemotePrinter_DYN);
	for( i = 0; options[i]; ++i ){
		n = safestrlen(cmd);
		SNPRINTF(cmd+n,sizeof(cmd)-n," %s",options[i] );
	}
	Perm_check.remoteuser = "papowell";
	Perm_check.user = "papowell";
	Is_server = 1;
	Job_status(&socket,cmd);
	return(-1);
}

#endif
