#!/usr/bin/python

#     printconf - silly script to set up every printer attached to the
#                 system with zero fuss.
#     Copyright (C) 2004-05 Chris Lawrence <lawrencc@debian.org>

#     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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# $Id: printconf,v 1.27 2008/10/03 11:18:23 lordsutch Exp $

import foomatic
import foomatic.foomatic
import foomatic.detect
import pprint
import re
import os
import textwrap
import commands
import sys
import tempfile

from optparse import OptionParser

try:
    r, c = commands.getoutput('stty size').split()
    rows, columns = int(r) or 24, (int(c) or 80)-1
except:
    rows, columns = 24, 79

parser = OptionParser(version='%prog '+foomatic.__version__)
parser.add_option('-n', '--no-action', '--dry-run', dest='dryrun',
                  action='store_true', help="don't configure any printers")
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
                  help="include more detail about what's happening")
options, args = parser.parse_args()

dryrun = options.dryrun
verbose = options.verbose

if os.geteuid() and not dryrun:
    print >> sys.stderr, "Please run printconf as root or specify the --dry-run option."
    sys.exit(1)

# regex for invalid characters in queue names to be filtered
_invalidre = re.compile(r'([^A-Za-z0-9_])')

def print_fill(text, *args):
    if args:
        text = text % args
    print textwrap.fill(text, columns)

def cleanup_name(model):
    model = model.lower().replace(' ', '_')
    newmod = _invalidre.sub('', model)
    return newmod

from xml.sax.saxutils import escape

def format_detectdata(data, device):
    dtype = 'general'
    if device.startswith('parallel'):
        dtype = 'parallel'
    elif device.startswith('usb'):
        dtype = 'usb'

    print '<autodetect>'
    print '  <%s>' % dtype
    for param in ('commandset', 'description', 'manufacturer', 'model'):
        d = data.get(param)
        if d:
            print '    <%s>%s</%s>' % (param, escape(d), param)
    print '  </%s>' % dtype
    print '</autodetect>'
    return

# CUPS needs a non-empty printers.conf before lpadmin will work properly
if not os.path.exists('/etc/cups/printers.conf'):
    f = file('/etc/cups/printers.conf', 'a')
    f.close()

if verbose:
    print 'Getting printer information...'

printdb = foomatic.foomatic.get_printer_db()
if not printdb:
    print_fill('Unable to read printer database.  Please ensure the "foomatic-db" package is installed properly.')
    sys.exit(1)

conns = foomatic.detect.printer_connections()

if verbose:
    print 'Autoconfiguring printers...'
# Ensure we don't overwrite any existing queues
queues = {}
existing = foomatic.foomatic.get_printer_queues()
if existing:
    for q in existing.queues:
        queues[q['name']] = True

for (device, detectdata, devdesc, detectdesc) in conns:
    # Skip everything we don't have autodetection data for
    if not detectdata:
        if verbose:
            print 'Skipping %s; no autodetection data available.' % device
        continue

    # Get the IEEE 1284 printer description
    desc = detectdata.get('description')
    pdbinfo = None
    if desc:
        pdbinfo = printdb.autodetect_ids.get(desc)

    if not pdbinfo:
        mfg = detectdata.get('manufacturer', '')
        model = detectdata.get('model', '')
        pdbinfo = printdb.autodetect_ids.get((mfg, model))

    if not pdbinfo:
        # Try using the manufacturer and model information to query the
        # database (this is an ad hoc approach, but may often work)
        mfg = detectdata.get('manufacturer', '').upper()
        model = detectdata.get('model', '').upper()
        pdbinfo = printdb.cmakes.get(mfg, {}).get(model)
        if pdbinfo:
            print_fill('Printer on %s was detected by Debian using the ad-hoc method.  Please submit the following information to foomatic-db@packages.debian.org:', device)
            format_detectdata(detectdata, device)
            print
    
    # Unknown printer; probably should print a message here asking
    # people to contribute this information to foomatic-db
    if not pdbinfo:
        if detectdata:
            print_fill('Printer on %s was not automatically configurable by Debian.  Please submit the following information to foomatic-db@packages.debian.org:', device)
            format_detectdata(detectdata, device)
            print
        continue

    make = pdbinfo.get('make')
    model = pdbinfo.get('model')

    prefdriver = pdbinfo.get('driver')
    printer = pdbinfo.get('id')
    # Unsupported printer; barf something here
    if not prefdriver or not printer:
        print_fill('%s %s on %s is not currently supported by Debian.',
                   make, model, devdesc)
        print
        continue

    drivers = pdbinfo.get('drivers', [])

    if prefdriver == 'gimp-print' and 'gimp-print-ijs' in drivers:
        prefdriver = 'gimp-print-ijs'

    if verbose:
        print 'Printer database data:'
        pprint.pprint(pdbinfo)

    # In case more than one printer of the same type is on the computer
    qname = cleanname = cleanup_name(model)
    i=1
    while queues.get(qname):
        qname = '%s_%d' % (cleanname, i)
        i += 1

    data = {'connect' : device, 'description' : desc, 'name' : qname,
            'location' : devdesc }

    foundppd = False
    if prefdriver == 'postscript' and pdbinfo.get('ppd'):
        ppd_content = foomatic.get_ppd_content(pdbinfo.get('ppd'))
        if ppd_content:
            foundppd = True

            (tfh, tfname) = tempfile.mkstemp(text=True, suffix=".ppd",
                                             prefix="printconf-")
            tf = os.fdopen(tfh, 'w+')
            tf.write(ppd_content)
            tf.close()
            data['ppdfile'] = tfname
            print_fill(
                'Configuring %s %s on %s with PPD %s as queue "%s".',
                make, model, device, fp.name, qname)

    if not foundppd:
        data['driver'] = prefdriver
        data['printer'] = printer
        print_fill(
            'Configuring %s %s on %s with %s driver as queue "%s".',
            make, model, device, prefdriver, qname)

    if dryrun:
        print '(dry run; skipping call to foomatic-configure)'
    else:
        foomatic.foomatic.setup_queue(data)

    if foundppd:
        os.unlink(tfname)
    
    # Queue X is now set up with name based on the IEEE model
    # some sort of message here would be nice
    queues[qname] = True
    print

# After loop, reinit CUPS
if not dryrun:
    os.system('/usr/sbin/invoke-rc.d cups force-reload')    

    print_fill('''If printconf was unable to install all of your printers,
please visit http://www.linuxprinting.org/ for printer information and support
from fellow users.''')
