#!/usr/bin/python -O
## import_printtool_queues.py 
## Copyright (C) 2000 Red Hat, Inc.
## Copyright (C) 2000 Crutcher Dunnavant <crutcher@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.

import sys
if not "/usr/share/printconf/util" in sys.path:
	sys.path.append("/usr/share/printconf/util")
from printconf_conf import *

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()


old2new = {
'CanonBJ10e':('58656', ['bj10e']),
'SECPCL4':('32672', ['lj4dith', 'ljet4']),
'U_EpsonStylusColor':('61184', ['st800', 'stc.upp', 'stc_h.upp', 'stc_l.upp', 'stcolor']),
'SECPCL6':('210153', ['ljet4']),
'PaintJetXL':('22848', ['pjxl']),
'XeroxXES':('25408', ['xes']),
'Lexmark2050':('59392', ['c2050']),
'Ricoh4081':('23424', ['r4081']),
'Lexmark5700':('62016', ['lex5700', 'lxm5700m']),
'EpsonStylus800':('80896', ['stc2.upp', 'stc2_h.upp', 'stc740ih.upp', 'stc740p.upp', 'stc740pl.upp']),
'DeskJetUNK':('267881', ['deskjet']),
'DeskJet890':('226985', ['cdj890']),
'{HP DeskJet 720}':('61504', ['pnm2ppa']),
'LaserJetPlus':('23040', ['ljetplus']),
'LaserJet3d':('23168', ['ljet3d']),
'SECINK2PEN':('173888', ['hpdj']),
'EpsonLQ9':('23904', ['epsonc']),
'Epson9MedRes':('24448', ['eps9high', 'eps9mid', 'epson', 'epsonc']),
'StarJet48':('25280', ['sj48']),
'Tek4693d4':('23552', ['t4693d4']),
'CanonLBP8':('189961', ['lbp8']),
'NECP6':('24768', ['necp6']),
'DeskJet670':('60928', ['cdj670']),
'EpsonAP3250':('23296', ['ap3250']),
'DeskJet500':('79840', ['cdj500', 'hpdj']),
'DECLN03':('130112', ['ln03']),
'Tek4693d8':('23584', ['t4693d8']),
'U_HPDeskjet840':('133664', ['cdj550', 'cdj670', 'cdj880', 'hpdj']),
'BJC600':('77568', ['bjc600', 'bjc610a0.upp', 'bjc610a1.upp', 'bjc610a2.upp', 'bjc610a3.upp', 'bjc610a4.upp', 'bjc610a5.upp', 'bjc610a6.upp', 'bjc610a7.upp', 'bjc610a8.upp']),
'U_HPDeskjet550c':('58464', ['cdj550', 'cdj550.upp', 'djet500', 'hpdj']),
'EpsonLQ24':('24320', ['epsonc']),
'DeskJet850':('61024', ['cdj850', 'hpdj']),
'Tek4693d2':('23488', ['t4693d2']),
'DECLJ250':('22944', ['declj250']),
'DeskJet500Mono':('58363', ['djet500', 'hpdj']),
'IBMJetPrinter3853':('23360', ['jetp3852']),
'ALPSMD5K':('240041', ['md5k']),
'Lexmark5700a':('62016', ['lex5700', 'lxm5700m']),
'Epson24':('46400', ['epson']),
'CItohM8510':('44256', ['m8510']),
'AppleDMP':('23648', ['appledmp']),
'ImageWriterLQ':('23776', ['iwhi', 'iwlo']),
'{HP DeskJet 1000}':('61600', ['pnm2ppa']),
'SECPCL5':('32672', ['lj4dith', 'ljet4']),
'DeskJet550':('58464', ['cdj550', 'cdj550.upp', 'djet500', 'hpdj']),
'CanonBJ200':('58720', ['bj200']),
'LaserJet2p':('74176', ['ljet2p']),
'U_CanonBJC610':('60192', ['bjc600', 'bjc610a0.upp', 'bjc610a1.upp', 'bjc610a2.upp', 'bjc610a3.upp', 'bjc610a4.upp', 'bjc610a5.upp', 'bjc610a6.upp', 'bjc610a7.upp', 'bjc610a8.upp', 'bjc610b1.upp', 'bjc610b2.upp', 'bjc610b3.upp', 'bjc610b4.upp', 'bjc610b6.upp', 'bjc610b7.upp', 'bjc610b8.upp']),
'Imagen':('44192', ['imagen']),
'DeskJet880':('68384', ['cdj850', 'cdj880', 'hpdj']),
'PaintJet':('22784', ['pj']),
'SECINK1PEN':('173888', ['hpdj']),
'DeskJet':('267881', ['deskjet']),
'CanonLIPS':('24512', ['lips3']),
'LaserJet4dither':('491506', ['ljet4']),
'Lexmark3200':('70912', ['lxm3200', 'lxm3200c']),
'{HP DeskJet 820}':('61568', ['pbm2ppa','pnm2ppa']),
'DeskJet1600':('61120', ['cdj1600']),
'Epson9':('24448', ['eps9high', 'eps9mid', 'epson', 'epsonc']),
'DECLA75':('24640', ['la75']),
'OKI4W':('64032', ['ljet4']),
'OKI182':('25152', ['oki182']),
'MitCP50':('44320', ['cp50']),
'Lexmark7000':('59520', ['lex7000']),
'HPDesignJet650C':('64992', ['dnj650c']),
'DECLA75P':('24704', ['la75plus']),
'ALPSMD2K':('240105', ['md2k']),
'SECPRT':('517394', []),
'ImageWriterHI':('23776', ['iwhi', 'iwlo']),
'LaserJet3':('78144', ['ljet3']),
'Epson9HiRes':('24448', ['eps9high', 'eps9mid', 'epson', 'epsonc']),
'LJ250':('22944', ['declj250']),
'ImageWriterLO':('23776', ['iwhi', 'iwlo']),
'PaintJetXL300':('63680', ['pjxl300']),
'DECLA70':('375433', ['la70']),
'LaserJet':('22976', ['laserjet']),
'U_NECPrinwriter2X':('60224', ['necp2x.upp', 'necp2x6.upp']),
'Tek4696':('25344', ['tek4696']),
'LaserJet4':('491506', ['ljet4']),
'DECLA50':('24576', ['la50']),
'EPSONMJ6000C':('62912', ['mj6000c']),
'EPSONMJ8000C':('208969', ['mj8000c']),
'U_EpsonStylusColor':('61248', ['mj500c']),
}

# Die if there is no printcap on the system
if not os.path.exists('/etc/printcap'):
	raise SystemExit

# Backup the printcap to printcap.save
os.system('cp -a /etc/printcap /etc/printcap.save')

# Open up the printcap file, and read all it's lines
printcap = open('/etc/printcap')
printcap_lines = printcap.readlines()
printcap.close()

# Zap the old printcap to force a rebuild
os.unlink('/etc/printcap')

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'.

"""


# Now, we juggle!
# take the raw printcap lines, and:
#   1 look for ##PRINTTOOL3## lines,
#   2 look for printer entries
# if a printer entry comes after a ##PRINTTOOL3## line, it gets treated as a
# printtool # printer. If it does not have a ##PRINTTOOL3## line, it gets
# relocated to printcap.local

# The line is of the general form:
##PRINTTOOL3## TYPE DRIVER RESOLUTION PAGE_SIZE {?} DB_INDEX BPP {?}
# and DB_INDEX /might/ be wrapped in {} and contain spaces
printtool_pattern = re.compile(
r'\s*##PRINTTOOL3##' + \
r'\s+(?P<type>\S+)' + \
r'(' + \
r'\s+(?P<driver>\S+)' + \
r'\s+(?P<resolution>\S+)' + \
r'\s+(?P<page_size>\S+)' + \
r'\s+(?P<trash1>{[^}]*})' + \
r'\s+(?P<db_index>{[^}]*}|\S+)' + \
r'\s+(?P<bpp>\S+)' + \
r'\s+(?P<trash2>{[^}]*})' + \
r')?'
)


# Known entries were made by printtool, Unknown entries were not.
known_entries = []
unknown_entries = []

printtool_data = None
i = 0
try: # This keeps us from ever tracebacking
	while i < len(printcap_lines):
		if re.match(r'\s*##PRINTTOOL3##', printcap_lines[i]):
			# This is a printtool line, digest it.
			match = printtool_pattern.match(printcap_lines[i])
			printtool_data = match.groupdict()
	
		elif re.match(r'\s*[^#:|\s]', printcap_lines[i]):
			# This is the beginning of a printcap entry, consume it.
			printer_entry = {}
	
			# Scan forward to find the the index of the line that starts
			# the next entry (or end of the file).
			j = i + 1;
			while j < len(printcap_lines) and not re.match(r'\s*[^#:|\s]', printcap_lines[j]):
				j = j + 1
	
			# Take out our slice, ignoring comment lines
			entry_lines = filter(lambda x: re.match(r'\s*[^#]', x), printcap_lines[i:j])
			entry_str = string.join(entry_lines)
	
			# now split by keys, and throw away trivial keys
			entry_options = filter(lambda x: re.search(r'[^\\\s]', x), string.split(entry_str, ':'))
	
			# Pull off the name list, and split it up.
			names = []
			for name in string.split(entry_options.pop(0), '|'):
				name_match = re.search(r'(?P<name>[-\w_]+)', name)
				if name_match:
					names.append(name_match.group('name'))
			printer_entry['name'] = names.pop(0)
			printer_entry['aliases'] = names
	
			# split up the rest of the options
			printer_entry['options'] = []
			for op in entry_options:
				op = re.sub(r'^\s*|\n.*', '', op)
				op = re.sub(r'\s*$', '', op)
	
				# There are some complex escape possibilities, so
				# We do not break the entry up past this point.
				# It slows down scanning latter, but is safer.
				printer_entry['options'].append(op)
	
			# Stick the printer in the right list.
			if printtool_data:
				printer_entry.update(printtool_data)
				printtool_data = None
	
				known_entries.append(printer_entry)
			else:
				unknown_entries.append(printer_entry)
	
		i = i + 1
	
	# Go ahead and write unknown entries into printcap.local
	pcl = open('/etc/printcap.local','w')
	pcl.write("""# printcap.local
#
# This file is included by printconf's generated printcap,
# and can be used to specify custom printcap entries.

""")
	
	for entry in unknown_entries:
		# a discusion about unknow entries.
		#
		# These entries were made without using printtool, we make NO assumptions
		# about them, and do not check anything about their spools, as there is
		# nothing we could intelligently do about whatever we find.
		name_list = [entry['name']]
		name_list.extend(entry['aliases'])
		option_list = [string.join(name_list, '|')]
		option_list.extend(entry['options'])
	
		pcl.write("# This printcap entry auto-imported at upgrade time. YMMV\n%s:\n\n" % \
			string.join(option_list, ":\\\n\t:"))

	pcl.close()
	
	
	# Initialize my edit session
	init_queue_edit("local")
	ctx = queue_edit.dynamic_queue_ctx

	# Extract the print_queues list from the local context.
	print_queues = ctx.data['/printconf/print_queues']
	
	changed = 0
	for entry in known_entries:
		# A discusion about imports
		#
		# Here we try to add a new printer to the print_queues, but we do not
		# Attempt to recover if it fails. This is true throughout the known loop,
		# And the reason is straight forward. The printers in the known_entries
		# list were made by printtool, and thus use rhs-printfilters. So, we /know/
		# that they won't work with the printconf, because there filter is not there.
		try:
			queue = print_queues.addData(AdmListType, entry['name'])
			queue.atomic = 1
		except:
			continue
	
		alias_list = queue.addData(AdmListType, 'alias_list')
		alias_list.anonymous = 1
	
		queue_type = queue.addData(AdmStringType, 'queue_type')
		queue_data = queue.addData(AdmListType, 'queue_data')
	
		filter_type = queue.addData(AdmStringType, 'filter_type')
		filter_data = queue.addData(AdmListType, 'filter_data')
	
		# Set up the alias list
		for alias in entry['aliases']:
			alias_list.addData(AdmStringType, 'alias').value = alias
	
		# Find the spool directory
		try:
			spool_dir = map(lambda y: y[3:], filter(lambda x: x[0:3] == 'sd=', entry['options']))[0]
		except:
			queue.unlink()
			continue
	
		# Read in the config file, if appropriate
		if entry['type'] == "SMB" or entry['type'] == "NCP" or entry['type'] == "DIRECT":
	 		if not os.path.exists("%s/.config" % spool_dir):
				queue.unlink()
				continue
	
			account_dict = {}
			config_lines = open("%s/.config" % spool_dir).readlines()
			for x in config_lines:
				match = re.search(r'(\w+)\s*=\s*(.*)',x)
				if match:
					(key, value) = match.groups()
					account_dict[key] = re.sub(r'(^\s*("|\')|("|\')\s*$|\n)','', value)
			
		if entry['type'] == "LOCAL":
			# This is a local attached printer
			queue_type.value = 'LOCAL'
	
			# Find the local printer device
			try:
				local_printer_device = map(lambda y: y[3:], filter(lambda x: x[0:3] == 'lp=', entry['options']))[0]
				queue_data.addData(AdmStringType, 'local_printer_device').value = local_printer_device
			except:
				queue.unlink()
				continue
	
		elif entry['type'] == "REMOTE":
			# This is a remote lpd print server of some sort
			queue_type.value = 'LPD'
	
			# Find the remote printer
			try:
				rm = map(lambda y: y[3:], filter(lambda x: x[0:3] == 'rm=', entry['options']))[0]
				queue_data.addData(AdmStringType, 'lpd_server').value = rm
	
				rp = map(lambda y: y[3:], filter(lambda x: x[0:3] == 'rp=', entry['options']))[0]
				queue_data.addData(AdmStringType, 'lpd_queue').value = rp
			except:
				queue.unlink()
				continue
	
			# We assume yes, because, hey, why not. (Might help solaris)
			queue_data.addData(AdmBoolType, 'lpd_strict_rfc1179').value = 1
	
		elif entry['type'] == "SMB":
			# this is a windows-like SMB print share
			queue_type.value = 'SMB'
			queue_data.addData(AdmStringType, 'smb_share').value = account_dict.get('share', '')
			queue_data.addData(AdmStringType, 'smb_ip').value = account_dict.get('hostip', '')
			queue_data.addData(AdmStringType, 'smb_user').value = account_dict.get('user', '')
			queue_data.addData(AdmStringType, 'smb_password').value = account_dict.get('password', '')
			queue_data.addData(AdmStringType, 'smb_workgroup').value = account_dict.get('workgroup', '')
			queue_data.addData(AdmBoolType, 'smb_translate').value = account_dict.has_key('translate')
	
		elif entry['type'] == "NCP":
			# This is a netware-like NCP server
			queue_type.value = 'NCP'
			queue_data.addData(AdmStringType, 'ncp_server').value = account_dict.get('server', '')
			queue_data.addData(AdmStringType, 'ncp_queue').value = account_dict.get('queue', '')
			queue_data.addData(AdmStringType, 'ncp_user').value = account_dict.get('user', '')
			queue_data.addData(AdmStringType, 'ncp_password').value = account_dict.get('password', '')
	
		elif entry['type'] == "DIRECT":
			# This is a jetdirect printer
			queue_type.value = 'JETDIRECT'
			queue_data.addData(AdmStringType, 'jetdirect_ip').value = account_dict.get('printer_ip', '')
			queue_data.addData(AdmStringType, 'jetdirect_port').value = account_dict.get('port', '')
	
		else:
			# This is what?
			queue.unlink()
			continue
	
	
		if not entry['driver']:
			filter_type.value = 'NONE'
		else:
			filter_type.value = 'MAGICFILTER'
			filter_data.addData(AdmListType, 'flags')
	
			if entry['driver'] == 'POSTSCRIPT':
				filter_data.addData(AdmStringType, 'mf_type').value = 'POSTSCRIPT'
				filter_data.addData(AdmStringType, 'page_size').value = 'Letter'

			elif entry['driver'] == 'TEXT':
				filter_data.addData(AdmStringType, 'mf_type').value = 'TEXT'
	
			else:
				filter_data.addData(AdmStringType, 'mf_type').value = 'MFOMATIC'
	
				(printer_id, driver_vec) = old2new.get(entry['db_index'], (None, []))
				if not printer_id or len(driver_vec) == 0:
					queue.unlink()
					continue
	
				driver = entry['driver']
				if driver == 'uniprint' or driver == 'ppa':
					driver == entry['bpp']
	
				try:
					driver_vec.index(driver)
				except:
					driver = driver_vec[0]
	
				filter_data.addData(AdmStringType, 'gs_driver').value = driver
				filter_data.addData(AdmStringType, 'printer_id').value = printer_id
				filter_data.addData(AdmListType, 'foomatic_defaults')
	
		# If I got here, I must have succeded. (I hope)
		changed = 1

		# claim the spool dir as MINE!!!
		if os.path.exists("%s" % spool_dir):
			paranoid_file_write("%s/VOLATILE" % spool_dir, volatile_header, 0600, lp_uid, lp_gid)

		# And ... we zap the old 'filter' link, to shut up checkcfg
		if os.path.exists("%s/filter" % spool_dir):
			os.unlink("%s/filter" % spool_dir)
	
	if changed:
		save_queues()
	
except:
	raise SystemExit
