#!/usr/bin/python
## printconf-backend
## Copyright (C) 2001, 2002 Red Hat, Inc.
## Copyright (C) 2001 Crutcher Dunnavant <crutcher@redhat.com>,
## Copyright (C) 2002 Tim Waugh <twaugh@redhat.com>

## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


# =====================================================================================================================
# Libs
# ----

# printconf
# ---------
# import the general parts of printconf
from printconf_conf import *
from rhpl.translate import _, N_


# =====================================================================================================================
# Is the user root?
if os.geteuid() != 0:
	print _("You must run printconf-backend as root.")
	sys.exit(-1)

# =====================================================================================================================
# Program Data Paths
# ------------------
#
# We need to know where some variable things are.
lpd_spool_dir = "/var/spool/lpd"


# =====================================================================================================================
# File Headers
# ------------
#
# The volatile file is a marker left in a spool directory saying, in effect, 'I was created by printconf'. If
# printconf finds this file in a directory it cannot account for, it will destroy the directory.
volatile_header = """THIS DIRECTORY IS VOLATILE!!!

This directory was created by printconf for a printconf spool.
If printconf-backend does a spool rebuild, and finds volatile
directories that do not currently have valid configurations,
it will DELETE THEM!

printconf-backend detects volatility by the presence of this file.
If you have custom spool directories made in some other manor, they
will be safe from printconf-backend's deletion as long as they do
NOT have a file in them named 'VOLATILE'.

"""

printcap_header = """# /etc/printcap
#
# DO NOT EDIT! MANUAL CHANGES WILL BE LOST!
# This file is autogenerated by printconf-backend during lpd init.
#
# Hand edited changes can be put in /etc/printcap.local, and will be included.

"""

printcap_local_preamble = """###############################################################################
## Everything below here is included verbatim from /etc/printcap.local       ##
###############################################################################
"""

printcap_local_header = """# /etc/printcap.local
#
# This file is included by printconf's generated printcap,
# and can be used to specify custom printcap entries.

"""

# =====================================================================================================================
# printcap.local
# --------------
#
# /etc/printcap is generated; /etc/printcap.local is not, though it is imported by /etc/printcap. We make sure that
# /etc/printcap.local exists, but otherwise ignore it.
if not os.path.exists('/etc/printcap.local'):
	pl = open("/etc/printcap.local", "w")
	pl.write(printcap_local_header)
	pl.close()


# To Rebuild, or Not
# ------------------
#
# Rebuilding the printing system can be expensive, and we want to avoid frivolus rebuilds. On the other hand, taking
# the time to confirm that the existing configuration is valid is also expensive, so we do not rescan the directories
# if the configuration looks current. To check, we look for some force flags, and then examine the switchboard.
# Parse options
rebuild = 0
import getopt
def help_message():
	print '''usage: printconf-backend [OPTIONS]

Options:
 --force-rebuild  Force the queues to be rebuilt
 --help           Display this usage message'''

for i in [None]:
	try:
		options, args = getopt.getopt (sys.argv[1:], '', \
					       ['force-rebuild', 'help'])
	except getopt.error:
		help_message ()
		sys.exit (1)

	for each in options[:]:
		if each[0] == '--force-rebuild':
			debug_print("Rebuilding: Found '--force-rebuild' command line option")
			rebuild = 1
			break

		if each[0] == '--help':
			help_message ()
			sys.exit (0)

	if rebuild:
		break

	if os.environ.get('PRINTCONF_FORCE_REBUILD',None):
		debug_print("Rebuilding: Found 'PRINTCONF_FORCE_REBUILD' environment variable")
		rebuild = 1
		break

	if '--force-rebuild' in sys.argv:
		rebuild = 1
		break

	# Obviously rebuild if the printcap file is not there.
	if not os.path.exists('/etc/printcap'):
		debug_print("Rebuilding: '/etc/printcap' does not exist")
		rebuild = 1
		break

	# Rebuild if the printcap.local file is newer than the printcap file
	if os.stat('/etc/printcap')[9] < os.stat('/etc/printcap.local')[9]:
		debug_print("Rebuilding: '/etc/printcap.local' is newer than '/etc/printcap'")
		rebuild = 1
		break

	# rebuild if the namespace is dirty
	if not checkNspMark(namespace = "printconf", mark = "_PRINTCONF_BACKEND_"):
		debug_print("Rebuilding: printconf namespace is dirty")
		rebuild = 1
		break

# Scan printcap
# -------------
#
# In the scenario that we are not forced to rebuild, we need to know if the existing printcap files have a printer
# defined. This is a tricky task, and we can't take the time to fully validate them. We just itterate through the
# databases, and take note if we ever find something that looks like a printer.
def scan_printcap_files(printcap_files):
	called(scan_printcap_files)

	while len(printcap_files):
		# get the next printcap file to scan
		file = printcap_files.pop()
	
		# address the issue of relative paths.
		if file[0] != '/':
			file = '/etc/' + file
	
		debug_print('scanning %s' % file)

		if not os.path.exists(file):
			# the printcap file in question does not exist, and attempting to start the server would fail.
			# However, if we fail here, we effectively swallow the error message that the print server will
			# throw (and will be more informative). So we just skip this file.
			continue
		
		for line in open(file).readlines():
			match = re.match(r'^\s*include\s*(?P<include>.*)\n?$', line)
			if match:
				# We've found an include file directive
				include = match.group('include')
				debug_print('found include directive: %s' % include)
				printcap_files.append(include)
				continue
			
			if re.match(r'^\s*\w', line):
				# We've found a valid printcap entry, return true.
				debug_print('found a printer line:\n%s' % line)
				return 1
	return None

# If we are not rebuilding, then the appropriate return value is dependent upon whatever scan_printcap_files finds.
if not rebuild:
	debug_print("Not rebuilding, scanning printcap files for entries")
	if scan_printcap_files(['/etc/printcap']):
		debug_print('Exit: Success')
		sys.exit(0)
	else:
		debug_print('Exit: Failure')
		sys.exit(1)


# Rebuilding ...
# --------------
#
# After the early exit case above, from this point on we know that we need to rebuild the print queues, and will no
# longer check the status of the rebuild variable.


# The alchemist context
# ---------------------
#
# To get the current configuration, we do a pull from the alchemist, and mark ourselves current. If it fails,
# we scan the printcap files, and exit on the return value returned.
context = readNsp(namespace = "printconf")
markNsp(namespace = "printconf", mark = "_PRINTCONF_BACKEND_")
if not context:
	if scan_printcap_files(['/etc/printcap']):
		debug_print('Exit: Success')
		sys.exit(0)
	else:
		debug_print('Exit: Failure')
		sys.exit(1)

# Now that we've got the context, extract the print queue list from it, and reorder for the default printer, if one is
# defined and available.
print_queues = context.data['/printconf/print_queues']
try:
	print_queues[context.data['/printconf/default_queue'].value].pos = 0
except:
	pass

# First, get a list of all the queue names
name_list = []
for queue in print_queues[:]:
	if not valid_queue(queue):
		queue.unlink()
	else:
		name_list.append(queue.name)


# Next, scan each queue's alias list, add new aliases to the name list and zap pre-defined aliases
# and strip out all conflicting aliases, to minimize the impact of alias merges from multiple sources.
for queue in print_queues:
	rectify_aliases(queue)

	for alias in queue['alias_list'][:]:
		if alias.value not in name_list:
			name_list.append(alias.value)
		else:
			alias.unlink()


# The 'lp' user and group
# -----------------------
#
# Printer spools and files are owned by the 'lp' user and the 'lp' group. Since we will be creating these files, it is
# appropriate if we know the respective uid and gid of those groups. The pwd module is perfect for this lookup.
import pwd
(lp_uid, lp_gid) = pwd.getpwnam("lp")[2:4]


# Paranoid File Writes
# --------------------
#
# At a number of places in the program, it becomes necessary to write out a file that must be secure from the moment of
# creation. Since this is a bit annoying to do in python, I've got this nice little helper function for exactly this
# task.
def paranoid_file_write(file_name, str, mode, uid, gid):
	called(paranoid_file_write)

	if os.path.exists(file_name):
		os.unlink(file_name)
	fd = os.open(file_name, os.O_WRONLY | os.O_CREAT | os.O_EXCL, mode)
	file = os.fdopen(fd, "w")
	file.write(str)
	os.chown(file_name, uid, gid)
	file.close()


# Magicfilter Configuration
# -------------------------
make_mfomatic_perl_str = r"""

$postscript_wrapper = '/usr/share/printconf/util/mf_postscript_wrapper';
# This complex string, when appended to the postscript_wrapper,
# passes the contents of the LPRNGOPTS variable to lpdomatic.
$lprng_opts = "'``LPRNGOPTS''`";

undef $/;
umask 0077;

$mpage_page_size = $control->{'mpage_page_size'};
$enscript_cmd = "/usr/bin/mpage -b $mpage_page_size -1 -o -P- -";

$rerender_ps = ($control->{'flags'}{'rerender_Postscript'} == 1) ? '--rerender-ps' : '';

$termination = '';
if ($control->{'flags'}{'send_FF'} == 1) {
	$termination .= '\f';
}
if ($control->{'flags'}{'send_EOT'} == 1) {
	$termination .= '\004';
}

$spool_dir = $control->{'spool_dir'};
$mf_type = $control->{'mf_type'};

if ($mf_type eq 'POSTSCRIPT') {
	# This is a pure postscript printer
	
	#
	# Build the mf.cfg string
	#
	$printer_id = 'Postscript Printer';
	$gs_driver = 'None';

	$text_filter = "pipe/postscript/ $enscript_cmd";
	$ps_filter = "filter $postscript_wrapper $lprng_opts $rerender_ps";
	# Yes, this is stupid. But we don't have reliable information anywhere on #dnl
	# if a given printer supports pcl and pjl, so what ya gonna do? #dnl
	$pcl_filter = 'cat';
	$pjl_filter = 'cat';

} elsif ($mf_type eq 'TEXT') {
	# This is a pure text printer
	
	#
	# Build the mf.cfg string
	#
	$printer_id = 'TEXT Printer';
	$gs_driver = 'Dont be silly';

	$text_filter = 'text';
	$ps_filter = 'text';
	$pcl_filter = 'reject';
	$pjl_filter = 'reject';

} elsif ($mf_type eq 'MFOMATIC') {
	# This is a mfomatic printer configuration

	$gs_driver = $control->{'gs_driver'};
	$printer_id = $control->{'printer_id'};

	use Foomatic::Defaults;
	use Foomatic::DB;
	my $db = new Foomatic::DB;

	$foodata = $db->getdat($gs_driver, $printer_id, 0);
	if (!$foodata) {
		exit 1;
	}

	#
	# Override the foomatic file
	#
	for $override (@{$control->{'foomatic_defaults'}}) {
		$name = $override->{'name'};
		next if not ($option = $foodata->{'args_byname'}->{$name});
		next if not ($override->{'type'} == $option->{'type'});

		$option->{'default'} = $override->{'default'};
	}

	$local_foo_file = "$spool_dir/$gs_driver-$printer_id.foo";
	unlink $local_foo_file;
	open (LFOO, '>', $local_foo_file) or exit 1;
	use Data::Dumper;
	$Data::Dumper::Purity=1;
	$Data::Dumper::Indent=1;
	print LFOO Dumper($foodata);
	close LFOO;
	chown $control->{'lp_uid'}, $control->{'lp_gid'}, $local_foo_file;

	#
	# Build the mf.cfg string
	#
	$make = $foodata->{'make'};
	$model = $foodata->{'model'};
	$color = ($foodata->{'color'} ? 'true' : 'false');

	if ($control->{'flags'}{'convert_text_to_Postscript'} || !($foodata->{'ascii'})) {
		$text_filter = "pipe/postscript/ $enscript_cmd"; 
	} else {
		$text_filter = 'text "" \r\n\14 ';
	}

	$ps_filter = "filter $postscript_wrapper $lprng_opts $rerender_ps --mfomatic $gs_driver-$printer_id.foo";
	# Yes, this is stupid. But we don't have reliable information anywhere on #dnl
	# if a given printer supports pcl and pjl, so what ya gonna do? #dnl
	$pcl_filter = 'cat';
	$pjl_filter = 'cat';
} else {
	# I don't understand this format.
	exit 1;
}

if ($control->{'flags'}{'assume_data_is_text'}) {
	$default_filter = $text_filter;
} else {
	$default_filter = qw?`cat'?;
} 

$locale = $control->{'filter_locale'};
$page_size = $control->{'page_size'};
$gs_papersize = $control->{'gs_papersize'};

$mf_cfg_str = "# foomatic/magicfilter configuration
# Make:		$make
# Model:	$model
# Printer Id:	$printer_id
# Driver:	$gs_driver
# TERMINATION=$termination
# FILTER_LOCALE=$locale
# GS_PAPERSIZE=$gs_papersize

define(MAKE, `$make')dnl
define(MODEL, `$model')dnl
define(COLOR, `$color')dnl
define(PAGEsize, `$page_size')dnl
define(TEXTfilter, $text_filter)dnl
define(PSfilter, `$ps_filter')dnl
define(PDFfilter, `$ps_filter')dnl (Same as PS filter for now)
define(PCLfilter, `$pcl_filter')dnl
define(PJLfilter, `$pjl_filter')dnl
define(DEFAULTfilter, $default_filter)dnl
";

#
# Write out the mf.cfg file
#
$mf_cfg = $spool_dir . '/mf.cfg';
unlink $mf_cfg;
open (MF_CFG, '>', $mf_cfg) || exit 1;
print MF_CFG $mf_cfg_str;
close MF_CFG;
chown $control->{'lp_uid'}, $control->{'lp_gid'}, $mf_cfg;

"""

#
# Because most of our magicfilter cases are handled by mfomatic at this point, all magicfilter configuration gets
# passed on to a seccond stage perl script, which has the capability to read the foomatic files that mfomatic uses.
# This function builds the control structure that the second stage evals as it's input.
def magicfilter_cfg(spool_dir, filter_data):
	called(magicfilter_cfg)

	# This function builds a control structure to pass to a perl script
	# which can easily override the foomatic data file's options.
	#
	# Maybe if you don't think about it, it wont disturb you ;)
	p_cont = "$control = {'lp_uid' => %d, 'lp_gid' => %d," % (lp_uid, lp_gid)


	# Universal values
	mf_type = filter_data["mf_type"].value

	# Read the flags list
	p_cont = p_cont + "'flags'=>{"
	for flag in filter_data['flags']:
		p_cont = "%s '%s'=>'%s'," % (p_cont, flag.name, str(flag.value) )
	p_cont = p_cont + "},"

	# We start with the assumption of 'Letter' as the page size. If we find something to override this, we change.
	page_size = 'Letter'

	# mfomatic values
	if mf_type == 'POSTSCRIPT':
		page_size = filter_data['page_size'].value

	elif mf_type == "MFOMATIC":
		printer_id = filter_data['printer_id'].value
		gs_driver = filter_data['gs_driver'].value
		p_cont = p_cont + "'printer_id'=>'%s','gs_driver'=>'%s'," % (printer_id, gs_driver) 

		p_cont = p_cont + "'foomatic_defaults' => ["
		for option in filter_data["foomatic_defaults"]:
			if option.name != "option_default":
				continue

			o_name = option["name"].value
			o_type = option["type"].value
			o_default = option["default"].value

			p_cont = p_cont + "{'name'=>'%s','type'=>'%s','default'=>'%s'}," % \
					(o_name, o_type, o_default)

			# Might as well look for pagesize
			if string.lower(o_name) == "pagesize":
				page_size = o_default


		p_cont = p_cont + '],'

	try:
		filter_locale = filter_data['filter_locale'].value
	except:
		filter_locale = 'C'

	(mpage_page_size, gs_papersize) = select_page_sizes(page_size)
	p_cont = p_cont + "'filter_locale' => '%s', 'mf_type' => '%s', 'spool_dir' => '%s', 'page_size' => '%s', 'mpage_page_size' => '%s', 'gs_papersize' => '%s'};" % \
			(filter_locale, mf_type, spool_dir, page_size, mpage_page_size, gs_papersize)

	perl_pipe = os.popen("perl", "w");
	debug_print(p_cont)
	perl_pipe.write(p_cont)
	perl_pipe.write(make_mfomatic_perl_str)
	return not perl_pipe.close()


# Select Page Sizes
# -----------------
#
# Yes! Standards Are Fun! Here we take the size we are given, and try to match it to mpage and ghostscript page sizes.
_gs_page_sizes = [
	'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'a10', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5',
	'archA', 'archB', 'archC', 'archD', 'archE', 'flsa', 'flse', 'halfletter', 'note', 'letter', 'legal',
	'11x17', 'ledger'
];
_mpage_page_sizes = None
def select_page_sizes(page_size):
	called(select_page_sizes)
	# Could be smarter (and not annoy europe and asia)

	def match_sizes(array, size, default):
		if (size in array):
			return size
		for check_size in array:
			if (string.lower(size) == string.lower(check_size)):
				return check_size
		return default
			
	if not _mpage_page_sizes:
		populate_mpage_page_sizes()

	return (match_sizes(_mpage_page_sizes, page_size, 'Letter'), match_sizes(_gs_page_sizes, page_size, 'letter'))

def populate_mpage_page_sizes():
	called(populate_mpage_page_sizes)
	global _mpage_page_sizes

	_mpage_page_sizes = []
	mpage_pipe = os.popen('/usr/bin/mpage -b?', 'r')
	lines = mpage_pipe.readlines()
	mpage_pipe.close()
	# Dump header
	lines.pop(0)
	lines.pop(0)
	lines.pop(0)
	regex = re.compile(r'^(\w*)')
	for line in lines:
		match = regex.match(line)
		if match:
			_mpage_page_sizes.append(match.group())


# Build Print Queue
# -----------------
#
# This function takes a queue, and rebuilds it on the system. It should be rewritten as a series of co-functions for
# the different queue and filter types available in the future.
def build_print_queue(queue):
	called(build_print_queue)

	queue_name = queue.name
	debug_print('Rebuilding: %s' % queue_name)

	# build the appropriate queue directory, if it does not exist
	queue_dir = lpd_spool_dir + '/' + queue_name 
	if not os.access(queue_dir, os.X_OK):
		os.mkdir(queue_dir, 0700)
		os.chown(queue_dir, lp_uid, lp_gid)

	paranoid_file_write("%s/VOLATILE" % queue_dir, volatile_header, 0600, lp_uid, lp_gid)
	
	# build the name list for the printcap entry
	name_list = [queue_name]
	name_list.extend(map(lambda x: x.value, queue['alias_list']))

	# start building the options list
	options_list = []
	options_list.extend( [	string.join(name_list, '|'),
				'ml#0',
				'mx#0',
				'sd=%s' % queue_dir,
				'af=%s/%s.acct' % (queue_dir,queue_name) ] )

	# Extract the real info
	queue_type = queue["queue_type"].value
	queue_data = queue["queue_data"]
	filter_type = queue["filter_type"].value
	filter_data = queue["filter_data"]

	# header page handling (YES, it is a filtration option)
	if not filter_data.has_key('print_header_page') or not filter_data['print_header_page'].value:
		options_list.append('sh')

	# Handle the queue cases
	if queue_type == "LOCAL":
		# This is a local printer
		options_list.append("lp=%s" % queue_data["local_printer_device"].value)

	elif queue_type == "CUSTOM":
		custom_type = filter_data["custom_type"].value
		if custom_type == "PATH":
			options_list.append("lp=|%s" % queue_data["custom_filter_path"].value)

		elif custom_type == "SCRIPT":
			paranoid_file_write(queue_dir + "/filter-delivery.script",
					filter_data["custom_filter_script"].value, 0755, lp_uid, lp_gid)
			options_list.append("lp=|%s/filter-delivery.script" % queue_dir)

		elif custom_type == "BINARY":
			paranoid_file_write(queue_dir + "/filter-delivery.binary",
						filter_data["custom_filter_binary"].binValue, 0755, lp_uid, lp_gid)
			options_list.append("lp=|%s/filter-delivery.binary" % queue_dir)
		else:
			return None


	elif queue_type == "LPD":
		# This is a lpd network queue
		options_list.append("rm=%s" % queue_data["lpd_server"].value)

		lpd_queue = queue_data["lpd_queue"].value
		if (lpd_queue != ""):
			options_list.append("rp=%s" % lpd_queue)

		if (queue_data["lpd_strict_rfc1179"].value):
			options_list.append("bk")
			options_list.append("control_filter=%s/strip_control_file.sh" % conf.printconf_util_dir)


	elif queue_type == "SMB":
		# This is a SMB network share
		if not os.path.exists("/usr/bin/smbclient"):
			return None

		config_str = """
share='%s'
hostip='%s'
user='%s'
password="%s"
workgroup='%s'
translate='%s'
""" % (		queue_data["smb_share"].value,
		queue_data["smb_ip"].value,
		queue_data["smb_user"].value, 
		re.sub(r'([\$`"\\])',r'\\\1',queue_data["smb_password"].value), 
		queue_data["smb_workgroup"].value, 
		queue_data["smb_translate"].value and "yes" or "no" )

		paranoid_file_write(queue_dir + "/script.cfg", config_str, 0600, lp_uid, lp_gid)

		options_list.append("lp=|%s/smbprint" % conf.printconf_util_dir)

	elif queue_type == "NCP":
		# This is a NCP network printer
		if not os.path.exists("/usr/bin/nprint"):
			return None

		config_str = """
server='%s'
queue='%s'
user='%s'
password='%s'
""" % (		queue_data["ncp_server"].value,
		queue_data["ncp_queue"].value,
		queue_data["ncp_user"].value,
		queue_data["ncp_password"].value )
		
		paranoid_file_write(queue_dir + "/script.cfg", config_str, 0600, lp_uid, lp_gid)

		options_list.append("lp=|%s/ncpprint" % conf.printconf_util_dir)

	elif queue_type == "JETDIRECT":
		# This is a Jet Direct printer on the network
		config_str = """
printer_ip=%s
port=%s
""" % (		queue_data["jetdirect_ip"].value,
		queue_data["jetdirect_port"].value )

		paranoid_file_write(queue_dir + "/script.cfg", config_str, 0600, lp_uid, lp_gid)

		options_list.append("lp=|%s/jetdirectprint" % conf.printconf_util_dir)

	else:	# What queue type is this? Nevermind, I'll just skip it.
		return None

	# Handle the filtration cases
	if filter_type == "NONE":
		# We are not filtering at all
		None

	elif filter_type == "MAGICFILTER":
		# We are using the magicfilter filter set
		if not magicfilter_cfg(queue_dir, filter_data):
			return None

		options_list.extend( [	"lpd_bounce=true",
					"if=%s/mf_wrapper" % conf.printconf_util_dir] )

	elif filter_type == "CUSTOM":
		custom_type = filter_data["custom_type"].value
		if custom_type == "PATH":
			options_list.extend( [	"lpd_bounce=true",
						"if=%s" % filter_data["custom_filter_path"].value ] )

		elif custom_type == "SCRIPT":
			paranoid_file_write(queue_dir + "/filter-data.script",
						filter_data["custom_filter_script"].value, 0755, lp_uid, lp_gid)
			options_list.extend( [	"lpd_bounce=true",
						"if=%s/filter-data.script" % queue_dir ] )

		elif custom_type == "BINARY":
			paranoid_file_write(queue_dir + "/filter-data.binary",
						filter_data["custom_filter_binary"].binValue, 0755, lp_uid, lp_gid)
			options_list.extend( [	"lpd_bounce=true",
						"if=%s/filter-data.binary" % queue_dir ] )

		else:
			return None

	else:	# What kind of filter is this? Hmm, I'll skip it.
		return None

	return string.join(options_list, ":\\\n\t:") + ":\n\n"


# Rebuild Queues
# --------------
#
# We are now ready to iterate over the list of remaining queues, and rebuild each one. This function does this, and
# returns the number of active queues that if found. It also destroys obsolete spool directories which it made in the
# past.
def rebuild_queues():
	called(rebuild_queues)

	# Rebuild each print queue, append their returned printcap entries to the printcap_entry_list
	printcap_entry_list = []
	active_queue_dirs = []
	for queue in print_queues:
		debug_print("rebuilding %s" % (queue.name))
		try:
			printcap_entry_str = build_print_queue(queue)
		except Exception, e:
			# Quietly swallow a failed queue build, and continue.
			debug_print(e.args)
			continue

		if printcap_entry_str:
			active_queue_dirs.append(queue.name)
			printcap_entry_list.append(printcap_entry_str)

	# backup the old printcap, you know, cause we are about to zap it, and people like backups.
	if os.path.exists('/etc/printcap'):
		os.system("cp -a /etc/printcap /etc/printcap.old")

	# Write out the printcap string
	printcap = open("/etc/printcap",'w')
	printcap_local = open("/etc/printcap.local", 'r')
	printcap.write(printcap_header)
	printcap.write(string.join(printcap_entry_list, ''))
	printcap.write(printcap_local_preamble)
	printcap.write(printcap_local.read())
	printcap.close()
	printcap_local.close()

	# This scans the spool directory, and distroys any directory marked volatile that does not have a current
	# valid configuration.
	for dir in map(lambda x: "%s/%s" % (lpd_spool_dir, x), os.listdir(lpd_spool_dir)):
		if os.path.exists("%s/VOLATILE" % dir):
			try:
				active_queue_dirs.index(os.path.basename(dir))
			except:
				# Yeah, I could do this in python, but it would suck
				os.system("rm -rf %s" % dir)


	return len(printcap_entry_list)

# We now try to rebuild. If that fails, we scan the printcap files for our return value.
if rebuild_queues() or scan_printcap_files(['/etc/printcap']):
	debug_print('Exit: Success')
	sys.exit(0)
else:
	debug_print('Exit: Failure')
	sys.exit(1)


