--- system-config-printer-0.7.71+-svn1371.orig/debian/control +++ system-config-printer-0.7.71+-svn1371/debian/control @@ -0,0 +1,16 @@ +Source: system-config-printer +Section: python +Priority: optional +Maintainer: Jani Monoses +XSBC-Original-Maintainer: Otavio Salvador +Build-Depends: cdbs (>= 0.4.43), debhelper (>= 5.0.37.2), python-support, python-all-dev (>= 2.3.5-11), libxml-parser-perl, desktop-file-utils, xmlto +Standards-Version: 3.7.2 +XS-Python-Version: all + +Package: system-config-printer +Architecture: all +Depends: ${shlibs:Depends}, ${python:Depends}, python-cups, python-gtk2, python-glade2, python-xml +Description: printer configuration GUI + This is a CUPS configuration Gtk+2.0 GUI written in Python + as part of the Fedora Core 6 system configuration tools. + It uses IPP to talk to CUPS --- system-config-printer-0.7.71+-svn1371.orig/debian/rules +++ system-config-printer-0.7.71+-svn1371/debian/rules @@ -0,0 +1,14 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses cdbs. Originaly written by Robert Millan. +# This file is public domain. + +DEB_PYTHON_SYSTEM = pysupport + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/autotools.mk +include /usr/share/cdbs/1/rules/simple-patchsys.mk + +DEB_CONFIGURE_EXTRA_FLAGS := --sbindir=/usr/bin + +binary-install/system-config-printer:: + dh_pysupport --- system-config-printer-0.7.71+-svn1371.orig/debian/watch +++ system-config-printer-0.7.71+-svn1371/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://cyberelk.net/tim/data/system-config-printer/system-config-printer-(.*).tar.bz2 debian git-import-orig --- system-config-printer-0.7.71+-svn1371.orig/debian/changelog +++ system-config-printer-0.7.71+-svn1371/debian/changelog @@ -0,0 +1,248 @@ +system-config-printer (0.7.71+-svn1371-0ubuntu1) gutsy; urgency=low + + * New upstream release + o Subversion snapshot r1371 + * debian/patches/50_my_default_printer_fix_system_default.patch, + debian/patches/60_device_handling_improvements.patch, + debian/patches/65_device_entry_sorting.patch: Removed (merhed upstream) + * debian/patches/66_os_environ.patch: Replaced a wrong "sys.environ()" by + the correct "os.environ" call. + + -- Till Kamppeter Mon, 06 Aug 2007 18:58:14 +0100 + +system-config-printer (0.7.71-0ubuntu2) gutsy; urgency=low + + * debian/patches/60_device_handling_improvements.patch, + debian/patches/65_device_entry_sorting.patch: + Vastly improved handling of detected devices + o If an auto-detected device (USB, network) is supported by HPLIP + only the HPLIP URIs are shown, to assure that the user gets it + correctly set up to get access to the complete functionality + o For auto-detected network MF devices from HP also an entry + for setting up the fax queue is shown + o Make and model info of auto-detected network printers is used to + direct the user to the correct PPD/driver + o For manually entered printers make and model are determined via + SNMP. It is also checked whether the printer is supported by HPLIP + o No "HP Fax" entry any more when no Fax-capable HP printer is + connected. + o When selecting an auto-detected non-HPLIP network printer the + fields of the form on the right are correctly prefilled now. + o Improved sorting of the device list entries. + + -- Till Kamppeter Fri, 03 Aug 2007 19:31:52 +0100 + +system-config-printer (0.7.71-0ubuntu1) gutsy; urgency=low + + * New upstream release + o Merged patches 10_ppd_init_make_model_list.patch, + 30_return_default_ppd.patch and + 40_improve_ppd_driver_selection.patch upstream. + o Handle "socket://..." URIs in a better way (LP: #127074) + o Moved default printer label below make-default button because some + translation strings are very long (LP: #128263) + o my-default-printer: Catch exceptions and error out (LP: #129901) + * Removed patches which were merged upstream. + * 50_my_default_printer_fix_system_default.patch: Fixed "System Default" + button in my-default-printer. + + -- Till Kamppeter Fri, 03 Aug 2007 19:31:52 +0100 + +system-config-printer (0.7.70-1ubuntu4) gutsy; urgency=low + + * debian/patches/40_improve_ppd_driver_selection.patch: Added missing + variable initialization. + + -- Till Kamppeter Wed, 1 Aug 2007 19:05:20 +0100 + +system-config-printer (0.7.70-1ubuntu3) gutsy; urgency=low + + * debian/patches/40_improve_ppd_driver_selection.patch: Improved the + automatic selection of the preferred printer driver/PPD file. This is + especially important for the non-interactive automatic printer setup + via hal-cups-utils. + + One important change (besides driver type priority changes) is that + now both PPDs assigned via IEEE-1284 device ID and PPDs assigned by + simple make/model matching are treated in the same level. With this + HP's PostScript PPDs (do not contain device IDs) can be preferred + against HP's HPIJS (PCL 5c/e) PPDs. + + Improvements: + o HP PostScript printers are set up with HP's PostScript PPDs and + not with the HPIJS PPDs (PCL 5c/e). + o The HP LaserJet 1022 is now set up with "foo2zjs" as recommended + by OpenPrinting, not with HPIJS. Als other HP printers where a + non-HPIJS driver is recommended are set up correctly now. + o Samsung SPL2 printers are set up with SpliX and not with the "gdi" + (SPL1) driver now. + o The recognition of drivers which came with CUPS is adapted to + Ubuntu Linux now. + o There was no distinction between the full-feature and simplified + Gutenprint drivers of the CUPS raster type. + + -- Till Kamppeter Tue, 30 Jul 2007 19:05:20 +0100 + +system-config-printer (0.7.70-1ubuntu2) gutsy; urgency=low + + * debian/patches/30_return_default_ppd.patch: Fix output of a default + PPD file if the /usr/lib/hal-cups-utils/hal_lpadmin script (triggered + by HAL, from hal-cups-utils package) is supplying auto-detection data + of an unknown printer. + + -- Till Kamppeter Tue, 24 Jul 2007 21:49:20 +0100 + +system-config-printer (0.7.70-1ubuntu1) gutsy; urgency=low + + * Merged with Debian unstable. + * debian/control: Added "xmlto" to the "Build-Depends:" line. + * debian/patches/10_ppd_init_make_model_list.patch: Fixed missing + initialization of make/model list (bug in upstream code) + * debian/patches/20_support_cups_nonroot.patch: Added support for CUPS + running as non-root user + + -- Till Kamppeter Tue, 24 Jul 2007 12:59:20 +0100 + +system-config-printer (0.7.70-1) unstable; urgency=low + + * New Upstream Version + + -- Otavio Salvador Sun, 15 Jul 2007 20:47:12 -0300 + +system-config-printer (0.7.69-1) unstable; urgency=low + + * New Upstream Version + * debian/watch: Use git-import-orig instead of uupdate so it integrates + better with our git repository. + * debian/rules: Drop the code to fix the executable permissions on + /usr/share since it's being done properly on upstream tarball. + + -- Otavio Salvador Wed, 04 Jul 2007 12:42:36 -0300 + +system-config-printer (0.7.66-1) unstable; urgency=low + + [ This package has been done using 0.7.62-0ubuntu1 sources as + base. Thanks to Jani Monoses by his work on it ] + + * debian/copyright: Update download URI. + * debian/watch: Add to allow easier upgrade of package version. + * debian/control: Remove versioned dependency of python-cups. + * debian/rules, debian/control: Move from python-central to python-support. + * debian/rules: Fix, by hard, wrong executable permissions on /usr/share. + + -- Otavio Salvador Wed, 04 Jul 2007 12:33:01 -0300 + +system-config-printer (0.7.62-0ubuntu1) feisty; urgency=low + + * New upstream release + fixes bugs, in particular crash on browsing SMB (LP #65834) + * debian/patches/02_interpreter_path.patch + debian/patches/05_desktop_file.patch: drop them, fixed upstream + + -- Jani Monoses Tue, 3 Apr 2007 20:07:37 +0300 + +system-config-printer (0.7.60-0ubuntu2) feisty; urgency=low + + * debian/rules: + Use langpack.mk to integrate with Rosetta + + -- Jani Monoses Tue, 27 Mar 2007 20:54:07 +0300 + +system-config-printer (0.7.60-0ubuntu1) feisty; urgency=low + + * New upstream release + add notification area icon for jobs + fix various bugs inlcuding LP #95629 + * debian/control: + depend on python-cups 1.9.19 + * debian/patches/06_tmpfile_remove_crasher.patch: + drop, part of the release + * merge existing patches + + -- Jani Monoses Tue, 27 Mar 2007 20:15:03 +0300 + +system-config-printer (0.7.56-0ubuntu1) feisty; urgency=low + + * New upstream release + bugfixes, including LP #87115 + support new job options + * debian/control: + require python-cups 1.9.18 + * debian/patches/06_tmpfile_remove_crasher.patch: + patch from upstream CVS to fix crash (LP #92914) + + -- Jani Monoses Wed, 21 Mar 2007 16:46:38 +0200 + +system-config-printer (0.7.55-0ubuntu1) feisty; urgency=low + + * New upstream release in sync with FC7 + bugfixes including crasher (LP #86554 and duplicates) + add GUI for more printer options + * debian/patches/01_no_rhpl.patch: + drop a few hunks that were taken upstream + * debian/patches/05_desktop_file.patch: + change to icon to printer.png from cupsprinter.png in desktop file (LP #85652) + + -- Jani Monoses Mon, 12 Mar 2007 16:13:31 +0200 + +system-config-printer (0.7.51-0ubuntu1) feisty; urgency=low + + * New upstream release: + fix crash when setting network printer as default (LP #84842) + * debian/control: + add dependency on python-glade2 + + -- Jani Monoses Tue, 13 Feb 2007 07:33:28 +0200 + +system-config-printer (0.7.49-0ubuntu1) feisty; urgency=low + + * New upstream release in sync with Fedora Core 6 updates + * debian/control: + depend on python-cups (>= 1.9.17) + + -- Jani Monoses Wed, 24 Jan 2007 10:08:24 +0200 + +system-config-printer (0.7.39-0ubuntu1) feisty; urgency=low + + * New upstream release + * debian/patches/04_cvs_fix_password_entry.patch: dropped. + * debian/control: + depend on python-cups (>= 1.9.15) + fix typo caught by lintian: depend on debhelper (>= 5.0.37.2) + not (>= 5.0.32.7) for Python policy support + + -- Jani Monoses Thu, 23 Nov 2006 16:41:52 +0200 + +system-config-printer (0.7.32-0ubuntu2) edgy; urgency=low + + * debian/patches/04_cvs_fix_password_entry.patch: + Fix invisible char in password dialog, patch from upstream CVS + * Install to /usr/bin not /usr/sbin. + * Add categories to desktop file + + -- Jani Monoses Wed, 11 Oct 2006 18:57:21 +0300 + +system-config-printer (0.7.32-0ubuntu1) edgy; urgency=low + + * New upstream tracking Fedora Core 6 package, fixes several upstream bugs + * debian/control: bump python-cups dependency version to 1.9.13 + + -- Jani Monoses Tue, 10 Oct 2006 14:29:39 +0300 + +system-config-printer (0.7.27-0ubuntu1) edgy; urgency=low + + * Initial package of Fedora Core 6 printer config tool + * debian/01_no_rhpl.patch: + - drop in copy of Conf.py from the RHPL library used by all + Fedora config tools as we do not have it packaged yet. + - use standard gettext module instead of rhpl.translate throughout + * debian/02_interpreter_path.patch: + - replace occurences of '#!/bin/env python' with '#!/usr/bin/python' + * debian/03_makefile.patch: + - change Makefile.in to include Conf.py in the install targets, and + to drop some categories passed to desktop-file-install which make + it fail on Ubuntu. (Did not change Makefile.am and autoreconf as it's + much cleaner this way, the diff being very small) + + -- Jani Monoses Fri, 8 Sep 2006 12:08:23 +0300 + --- system-config-printer-0.7.71+-svn1371.orig/debian/compat +++ system-config-printer-0.7.71+-svn1371/debian/compat @@ -0,0 +1 @@ +5 --- system-config-printer-0.7.71+-svn1371.orig/debian/README.Debian +++ system-config-printer-0.7.71+-svn1371/debian/README.Debian @@ -0,0 +1,8 @@ +system-config-printer for Debian +-------------------------------- + +This package contains the CUPS configuration GUI from Fedora Core 6 +It was changed not to depend on the RHPL libraries which are not (yet) +packaged in Ubuntu. + +-- Jani Monoses , Fri, 8 Sep 2006 12:08:23 +0300 --- system-config-printer-0.7.71+-svn1371.orig/debian/patches/03_makefile.patch +++ system-config-printer-0.7.71+-svn1371/debian/patches/03_makefile.patch @@ -0,0 +1,12 @@ +diff --git a/Makefile.in b/Makefile.in +index 0fd3917..b0d0a12 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -222,6 +222,7 @@ nobase_pkgdata_SCRIPTS = \ + applet.py + + nobase_pkgdata_DATA = \ ++ Conf.py \ + config.py \ + cupsd.py \ + cupshelpers.py \ --- system-config-printer-0.7.71+-svn1371.orig/debian/patches/20_support_cups_nonroot.patch +++ system-config-printer-0.7.71+-svn1371/debian/patches/20_support_cups_nonroot.patch @@ -0,0 +1,33 @@ +diff -Nur system-config-printer-0.7.70/system-config-printer.py system-config-printer-0.7.70.new/system-config-printer.py +--- system-config-printer-0.7.70/system-config-printer.py 2007-07-09 14:22:20.000000000 +0100 ++++ system-config-printer-0.7.70.new/system-config-printer.py 2007-07-24 16:17:05.000000000 +0100 +@@ -87,15 +87,16 @@ + + self.printer = None + self.conflicts = set() # of options +- self.connect_server = 'localhost' +- self.connect_user = 'root' ++ self.connect_server = (self.printer and self.printer.getServer()) \ ++ or cups.getServer() ++ self.connect_user = cups.getUser() + self.password = '' + self.passwd_retry = False + cups.setPasswordCB(self.cupsPasswdCallback) + + self.changed = set() # of options + +- self.servers = set(("localhost",)) ++ self.servers = set((self.connect_server,)) + + # Synchronisation objects. + self.ppds_lock = None +@@ -3210,8 +3211,7 @@ + self.reconnect () + + def main(start_printer = None, change_ppd = False): +- # The default configuration requires root for administration. +- cups.setUser ("root") ++ cups.setUser (cups.getUser()) + gtk.gdk.threads_init() + gtk.gdk.threads_enter() + --- system-config-printer-0.7.71+-svn1371.orig/debian/patches/66_os_environ.patch +++ system-config-printer-0.7.71+-svn1371/debian/patches/66_os_environ.patch @@ -0,0 +1,12 @@ +diff -Nur system-config-printer-0.7.71+-svn1371/system-config-printer.py system-config-printer-0.7.71+-svn1371.new/system-config-printer.py +--- system-config-printer-0.7.71+-svn1371/system-config-printer.py 2007-08-06 19:26:09.000000000 +0100 ++++ system-config-printer-0.7.71+-svn1371.new/system-config-printer.py 2007-08-06 19:26:41.000000000 +0100 +@@ -2255,7 +2255,7 @@ + host = device.uri[s:s+e] + # Try to get make and model via SNMP + if host: +- sys.environ["HOST"] = host ++ os.environ["HOST"] = host + cmd = '/usr/lib/cups/backend/snmp "${HOST}" 2>/dev/null' + p = os.popen(cmd, 'r') + output = p.read() --- system-config-printer-0.7.71+-svn1371.orig/debian/patches/01_no_rhpl.patch +++ system-config-printer-0.7.71+-svn1371/debian/patches/01_no_rhpl.patch @@ -0,0 +1,1867 @@ +diff -Nur system-config-printer-0.7.27/Conf.py system-config-printer-0.7.27.new/Conf.py +--- system-config-printer-0.7.27/Conf.py 1970-01-01 02:00:00.000000000 +0200 ++++ system-config-printer-0.7.27.new/Conf.py 2006-09-08 14:37:02.000000000 +0300 +@@ -0,0 +1,1840 @@ ++# Copyright (C) 1996-2002 Red Hat, Inc. ++# Use of this software is subject to the terms of the GNU General ++# Public License ++ ++# This module manages standard configuration file handling ++# These classes are available: ++# Conf: ++# This is the base class. This is good for working with just about ++# any line-oriented configuration file. ++# Currently does not deal with newline escaping; may never... ++# ConfShellVar(Conf): ++# This is a derived class which implements a dictionary for standard ++# VARIABLE=value ++# shell variable setting. ++# Limitations: ++# o one variable per line ++# o assumes everything on the line after the '=' is the value ++# ConfShellVarClone(ConfShellVar): ++# Takes a ConfShellVar instance and records in another ConfShellVar ++# "difference file" only those settings which conflict with the ++# original instance. The delete operator does delete the variable ++# text in the cloned instance file, but that will not really delete ++# the shell variable that occurs, because it does not put an "unset" ++# command in the file. ++# ConfESNetwork(ConfShellVar): ++# This is a derived class specifically intended for /etc/sysconfig/network ++# It is another dictionary, but magically fixes /etc/HOSTNAME when the ++# hostname is changed. ++# ConfEHosts(Conf): ++# Yet another dictionary, this one for /etc/hosts ++# Dictionary keys are numeric IP addresses in string form, values are ++# 2-item lists, the first item of which is the canonical hostname, ++# and the second of which is a list of nicknames. ++# ConfEResolv(Conf): ++# Yet another dictionary, this one for /etc/resolv.conf ++# This ugly file has two different kinds of entries. All but one ++# take the form "key list of arguments", but one entry (nameserver) ++# instead takes multiple lines of "key argument" pairs. ++# In this dictionary, all keys have the same name as the keys in ++# the file, EXCEPT that the multiple nameserver entries are all ++# stored under 'nameservers'. Each value (even singleton values) ++# is a list. ++# ConfESStaticRoutes(Conf): ++# Yet another dictionary, this one for /etc/sysconfig/static-routes ++# This file has a syntax similar to that of /etc/gateways; ++# the interface name is added and active/passive is deleted: ++# net netmask gw ++# The key is the interface, the value is a list of ++# [, , ] lists ++# ConfChat(Conf): ++# Not a dictionary! ++# This reads chat files, and writes a subset of chat files that ++# has all items enclosed in '' and has one expect/send pair on ++# each line. ++# Uses a list of two-element tuples. ++# ConfChatFile(ConfChat): ++# This class is a ConfChat which it interprets as a netcfg-written ++# chat file with a certain amount of structure. It interprets it ++# relative to information in an "ifcfg-" file (devconf) and has a ++# set of abortstrings that can be turned on and off. ++# It exports the following data items: ++# abortstrings list of standard strings on which to abort ++# abortlist list of alternative strings on which to abort ++# defabort boolean: use the default abort strings or not ++# dialcmd string containing dial command (ATDT, for instance) ++# phonenum string containing phone number ++# chatlist list containing chat script after CONNECT ++# chatfile ConfChat instance ++# ConfChatFileClone(ConfChatFile): ++# Creates a chatfile, then removes it if it is identical to the chat ++# file it clones. ++# ConfDIP: ++# This reads chat files, and writes a dip file based on that chat script. ++# Takes three arguments: ++# o The chatfile ++# o The name of the dipfile ++# o The ConfSHellVar instance from which to take variables in the dipfile ++# ConfModules(Conf) ++# This reads /etc/modprobe.conf into a dictionary keyed on device type, ++# holding dictionaries: cm['eth0']['alias'] --> 'smc-ultra' ++# cm['eth0']['options'] --> {'io':'0x300', 'irq':'10'} ++# cm['eth0']['post-install'] --> ['/bin/foo','arg1','arg2'] ++# path[*] entries are ignored (but not removed) ++# New entries are added at the end to make sure that they ++# come after any path[*] entries. ++# Comments are delimited by initial '#' ++# ConfModInfo(Conf) ++# This READ-ONLY class reads /boot/module-info. ++# The first line of /boot/module-info is "Version = "; ++# this class reads versions 0 and 1 module-info files. ++# ConfPw(Conf) ++# This class implements a dictionary based on a :-separated file. ++# It takes as arguments the filename and the field number to key on; ++# The data provided is a list including all fields including the key. ++# Has its own write method to keep files sane. ++# ConfPasswd(ConfPw) ++# This class presents a data-oriented class for making changes ++# to the /etc/passwd file. ++# ConfShadow(ConfPw) ++# This class presents a data-oriented class for making changes ++# to the /etc/shadow file. ++# ConfGroup(ConfPw) ++# This class presents a data-oriented class for making changes ++# to the /etc/group file. ++# May be replaced by a pwdb-based module, we hope. ++# ConfUnix() ++# This class presents a data-oriented class which uses the ConfPasswd ++# and ConfShadow classes (if /etc/shadow exists) to hold data. ++# Designed to be replaced by a pwdb module eventually, we hope. ++# ConfPAP(Conf): ++# Yet another dictionary, this one for /etc/ppp/pap-secrets ++# The key is the remotename, the value is a list of ++# [, ] lists ++# ConfCHAP(ConfPAP): ++# Yet another dictionary, this one for /etc/ppp/chap-secrets ++# The key is the remotename, the value is a list of ++# [, ] lists ++# ConfSecrets: ++# Has-a ConfPAP and ConfCHAP ++# Yet another dictionary, which reads from pap-secrets and ++# chap-secrets, and writes to both when an entry is set. ++# When conflicts occur while reading, the pap version is ++# used in preference to the chap version (this is arbitrary). ++# ConfSysctl: ++# Guess what? A dictionary, this time with key/value pairs for sysctl vars. ++# Duplicate keys get appended to existing values, and broken out again when ++# the file is written (does that even work?) ++ ++# This library exports several Errors, including ++# FileMissing ++# Conf raises this error if create_if_missing == 0 and the file does not ++# exist ++# IndexError ++# ConfShVar raises this error if unbalanced quotes are found ++# BadFile ++# Raised to indicate improperly formatted files ++# WrongMethod ++# Raised to indicate that the wrong method is being called. May indicate ++# that a dictionary class should be written to through methods rather ++# than assignment. ++# VersionMismatch ++# An unsupported file version was found. ++# SystemFull ++# No more UIDs or GIDs are available ++ ++FileMissing = 'Conf.FileMissing' ++IndexError = 'Conf.IndexError' ++BadFile = 'Conf.BadFile' ++WrongMethod = 'Conf.WrongMethod' ++VersionMismatch = 'Conf.VersionMismatch' ++SystemFull = 'conf.SystemFull' ++ ++from string import * ++from UserDict import UserDict ++import re ++import os ++import types ++# Implementation: ++# A configuration file is a list of lines. ++# a line is a string. ++ ++class Conf: ++ def __init__(self, filename, commenttype='#', ++ separators='\t ', separator='\t', ++ merge=1, create_if_missing=1): ++ self.commenttype = commenttype ++ self.separators = separators ++ self.separator = separator ++ self.codedict = {} ++ self.splitdict = {} ++ self.merge = merge ++ self.create_if_missing = create_if_missing ++ self.line = 0 ++ self.rcs = 0 ++ self.mode = -1 ++ # self.line is a "point" -- 0 is before the first line; ++ # 1 is between the first and second lines, etc. ++ # The "current" line is the line after the point. ++ self.filename = filename ++ self.read() ++ def rewind(self): ++ self.line = 0 ++ def fsf(self): ++ self.line = len(self.lines) ++ def tell(self): ++ return self.line ++ def seek(self, line): ++ self.line = line ++ def nextline(self): ++ self.line = min([self.line + 1, len(self.lines)]) ++ def findnextline(self, regexp=None): ++ # returns false if no more lines matching pattern ++ while self.line < len(self.lines): ++ if regexp: ++ if hasattr(regexp, "search"): ++ if regexp.search(self.lines[self.line]): ++ return 1 ++ elif re.search(regexp, self.lines[self.line]): ++ return 1 ++ elif not regexp: ++ return 1 ++ self.line = self.line + 1 ++ # if while loop terminated, pattern not found. ++ return 0 ++ def findnextcodeline(self): ++ # optional whitespace followed by non-comment character ++ # defines a codeline. blank lines, lines with only whitespace, ++ # and comment lines do not count. ++ if not self.codedict.has_key((self.separators, self.commenttype)): ++ self.codedict[(self.separators, self.commenttype)] = \ ++ re.compile('^[' + self.separators \ ++ + ']*' + '[^' + \ ++ self.commenttype + \ ++ self.separators + ']+') ++ codereg = self.codedict[(self.separators, self.commenttype)] ++ return self.findnextline(codereg) ++ def findlinewithfield(self, fieldnum, value): ++ if self.merge: ++ seps = '['+self.separators+']+' ++ else: ++ seps = '['+self.separators+']' ++ rx = '^' ++ for i in range(fieldnum - 1): ++ rx = rx + '[^'+self.separators+']*' + seps ++ rx = rx + value + '\(['+self.separators+']\|$\)' ++ return self.findnextline(rx) ++ def getline(self): ++ if self.line >= len(self.lines): ++ return '' ++ return self.lines[self.line] ++ def getfields(self): ++ # returns list of fields split by self.separators ++ if self.line >= len(self.lines): ++ return [] ++ if self.merge: ++ seps = '['+self.separators+']+' ++ else: ++ seps = '['+self.separators+']' ++ #print "re.split(%s, %s) = " % (self.lines[self.line], seps) + str(re.split(seps, self.lines[self.line])) ++ ++ if not self.splitdict.has_key(seps): ++ self.splitdict[seps] = re.compile(seps) ++ regexp = self.splitdict[seps] ++ return regexp.split(self.lines[self.line]) ++ def setfields(self, list): ++ # replaces current line with line built from list ++ # appends if off the end of the array ++ if self.line < len(self.lines): ++ self.deleteline() ++ self.insertlinelist(list) ++ def insertline(self, line=''): ++ self.lines.insert(self.line, line) ++ def insertlinelist(self, linelist): ++ self.insertline(joinfields(linelist, self.separator)) ++ def sedline(self, pat, repl): ++ if self.line < len(self.lines): ++ self.lines[self.line] = re.sub(pat, repl, \ ++ self.lines[self.line]) ++ def changefield(self, fieldno, fieldtext): ++ fields = self.getfields() ++ fields[fieldno:fieldno+1] = [fieldtext] ++ self.setfields(fields) ++ def setline(self, line=[]): ++ self.deleteline() ++ self.insertline(line) ++ def deleteline(self): ++ self.lines[self.line:self.line+1] = [] ++ def chmod(self, mode=-1): ++ self.mode = mode ++ def read(self): ++ file_exists = 0 ++ if os.path.isfile(self.filename): ++ file_exists = 1 ++ if not self.create_if_missing and not file_exists: ++ raise FileMissing, self.filename + ' does not exist.' ++ if file_exists and os.access(self.filename, os.R_OK): ++ self.file = open(self.filename, 'r', -1) ++ self.lines = self.file.readlines() ++ # strip newlines ++ for index in range(len(self.lines)): ++ if len(self.lines[index]) and self.lines[index][-1] == '\n': ++ self.lines[index] = self.lines[index][:-1] ++ if len(self.lines[index]) and self.lines[index][-1] == '\r': ++ self.lines[index] = self.lines[index][:-1] ++ self.file.close() ++ else: ++ self.lines = [] ++ def write(self): ++ # rcs checkout/checkin errors are thrown away, because they ++ # aren't this tool's fault, and there's nothing much it could ++ # do about them. For example, if the file is already locked ++ # by someone else, too bad! This code is for keeping a trail, ++ # not for managing contention. Too many deadlocks that way... ++ if self.rcs or os.path.exists(os.path.split(self.filename)[0]+'/RCS'): ++ self.rcs = 1 ++ os.system('/usr/bin/co -l '+self.filename+' /dev/null 2>&1') ++ self.file = open(self.filename, 'w', -1) ++ if self.mode >= 0: ++ os.chmod(self.filename, self.mode) ++ # add newlines ++ for index in range(len(self.lines)): ++ self.file.write(self.lines[index] + '\n') ++ self.file.close() ++ if self.rcs: ++ mode = os.stat(self.filename)[0] ++ os.system('/usr/bin/ci -u -m"control panel update" ' + ++ self.filename+' /dev/null 2>&1') ++ os.chmod(self.filename, mode) ++ ++class ConfShellVar(Conf): ++ def __init__(self, filename): ++ self.nextreg = re.compile('^[\t ]*[A-Za-z_][A-Za-z0-9_]*=') ++ self.quotereg = re.compile('[ \t${}*@!~<>?;%^()#&=]') ++ Conf.__init__(self, filename, commenttype='#', ++ separators='=', separator='=') ++ ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ def initvars(self): ++ self.vars = {} ++ self.rewind() ++ while self.findnextline(self.nextreg): ++ # initialize dictionary of variable/name pairs ++ var = self.getfields() ++ # fields 1..n are false separations on "=" character in string, ++ # so we need to join them back together. ++ var[1] = joinfields(var[1:len(var)], '=') ++ if len(var[1]) and var[1][0] in '\'"': ++ # found quote; strip from beginning and end ++ quote = var[1][0] ++ var[1] = var[1][1:] ++ p = -1 ++ try: ++ while cmp(var[1][p], quote): ++ # ignore whitespace, etc. ++ p = p - 1 ++ except: ++ raise IndexError, 'end quote not found in '+self.filename+':'+var[0] ++ var[1] = var[1][:p] ++ else: ++ var[1] = re.sub('#.*', '', var[1]) ++ ++ self.vars[var[0]] = var[1] ++ self.nextline() ++ self.rewind() ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return '' ++ def __setitem__(self, varname, value): ++ # prevent tracebacks ++ if not value: ++ value="" ++ # set *every* instance of varname to value to avoid surprises ++ place=self.tell() ++ self.rewind() ++ missing=1 ++ while self.findnextline('^[\t ]*' + varname + '='): ++ if self.quotereg.search(value): ++ self.sedline('=.*', "='" + value + "'") ++ else: ++ self.sedline('=.*', '=' + value) ++ missing=0 ++ self.nextline() ++ if missing: ++ self.seek(place) ++ if self.quotereg.search(value): ++ self.insertline(varname + "='" + value + "'") ++ else: ++ self.insertline(varname + '=' + value) ++ ++ self.vars[varname] = value ++ ++ def __delitem__(self, varname): ++ # delete *every* instance... ++ ++ self.rewind() ++ while self.findnextline('^[\t ]*' + varname + '='): ++ self.deleteline() ++ if self.vars.has_key(varname): ++ del self.vars[varname] ++ ++ def has_key(self, key): ++ if self.vars.has_key(key): return 1 ++ return 0 ++ def keys(self): ++ return self.vars.keys() ++ ++ ++# ConfShellVarClone(ConfShellVar): ++# Takes a ConfShellVar instance and records in another ConfShellVar ++# "difference file" only those settings which conflict with the ++# original instance. The delete operator does delete the variable ++# text in the cloned instance file, but that will not really delete ++# the shell variable that occurs, because it does not put an "unset" ++# command in the file. ++class ConfShellVarClone(ConfShellVar): ++ def __init__(self, cloneInstance, filename): ++ Conf.__init__(self, filename, commenttype='#', ++ separators='=', separator='=') ++ self.ci = cloneInstance ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ elif self.ci.has_key(varname): ++ return self.ci[varname] ++ else: ++ return '' ++ def __setitem__(self, varname, value): ++ if value != self.ci[varname]: ++ # differs from self.ci; save a local copy. ++ ConfShellVar.__setitem__(self, varname, value) ++ else: ++ # self.ci already has the variable with the same value, ++ # don't duplicate it ++ if self.vars.has_key(varname): ++ del self[varname] ++ # inherit delitem because we don't want to pass it through to self.ci ++ def has_key(self, key): ++ if self.vars.has_key(key): return 1 ++ if self.ci.has_key(key): return 1 ++ return 0 ++ # FIXME: should we implement keys()? ++ ++ ++class ConfESNetwork(ConfShellVar): ++ # explicitly for /etc/sysconfig/network: HOSTNAME is magical value ++ # that writes /etc/HOSTNAME as well ++ def __init__ (self): ++ ConfShellVar.__init__(self, '/etc/sysconfig/network') ++ self.writehostname = 0 ++ def __setitem__(self, varname, value): ++ ConfShellVar.__setitem__(self, varname, value) ++ if varname == 'HOSTNAME': ++ self.writehostname = 1 ++ def write(self): ++ ConfShellVar.write(self) ++ if self.writehostname: ++ file = open('/etc/HOSTNAME', 'w', -1) ++ file.write(self.vars['HOSTNAME'] + '\n') ++ file.close() ++ os.chmod('/etc/HOSTNAME', 0644) ++ def keys(self): ++ # There doesn't appear to be a need to return keys in order ++ # here because we normally always have the same entries in this ++ # file, and order isn't particularly important. ++ return self.vars.keys() ++ def has_key(self, key): ++ return self.vars.has_key(key) ++ ++ ++class ConfEHosts(Conf): ++ # for /etc/hosts ++ # implements a dictionary keyed by IP address, with values ++ # consisting of a list: [ hostname, [list, of, nicknames] ] ++ def __init__(self, filename = '/etc/hosts'): ++ Conf.__init__(self, filename) ++ ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ ++ def initvars(self): ++ self.vars = {} ++ self.rewind() ++ while self.findnextcodeline(): ++ # initialize dictionary of variable/name pairs ++ var = self.getfields() ++ if len(var) > 2: ++ # has nicknames ++ self.vars[var[0]] = [ var[1], var[2:] ] ++ elif len(var) > 1: ++ self.vars[var[0]] = [ var[1], [] ] ++ else: ++ pass ++ # exception is a little bit hard.. just skip the line ++ # raise BadFile, 'Malformed /etc/hosts file' ++ self.nextline() ++ self.rewind() ++ ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return '' ++ ++ def __setitem__(self, varname, value): ++ # set first (should be only) instance to values in list value ++ place=self.tell() ++ self.rewind() ++ while self.findnextline('^\S*' + varname + '[' + \ ++ self.separators + ']+'): ++ self.deleteline() ++ self.seek(place) ++ ++ self.insertlinelist([ varname, value[0], ++ joinfields(value[1], self.separator) ]) ++ self.vars[varname] = value ++ def __delitem__(self, varname): ++ # delete *every* instance... ++ self.rewind() ++ while self.findnextline('^\S*' + varname + '[' + \ ++ self.separators + ']+'): ++ self.deleteline() ++ del self.vars[varname] ++ ++ def keys(self): ++ # It is rather important to return the keys in order here, ++ # in order to maintain a consistent presentation in apps. ++ place=self.tell() ++ self.rewind() ++ keys = [] ++ while self.findnextcodeline(): ++ # initialize dictionary of variable/name pairs ++ var = self.getfields() ++ if len(var) > 1: ++ keys.append(var[0]) ++ self.nextline() ++ self.seek(place) ++ return keys ++ ++class ConfFHosts(Conf): ++ # for /etc/hosts ++ # implements a dictionary keyed by Hostname, with values ++ # consisting of a list: [ IP-Adress, [list, of, nicknames] ] ++ def __init__(self, filename = '/etc/hosts'): ++ Conf.__init__(self, filename) ++ ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ ++ def initvars(self): ++ self.vars = {} ++ self.rewind() ++ while self.findnextcodeline(): ++ # initialize dictionary of variable/name pairs ++ var = self.getfields() ++ if len(var) > 2: ++ # has nicknames ++ self.vars[var[1]] = [ var[0], var[2:] ] ++ elif len(var) > 1: ++ self.vars[var[1]] = [ var[0], [] ] ++ else: ++ # exception is a little bit hard.. just skip the line ++ # raise BadFile, 'Malformed /etc/hosts file' ++ pass ++ self.nextline() ++ self.rewind() ++ ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return '' ++ ++ def __setitem__(self, varname, value): ++ # set first (should be only) instance to values in list value ++ place=self.tell() ++ self.rewind() ++ lastdel = place ++ while self.findnextline('^\S*' + '[' + \ ++ self.separators + ']+' + varname): ++ self.deleteline() ++ lastdel = self.tell() ++ ++ self.seek(lastdel) ++ if type(value) != types.ListType and type(value) != types.TupleType: ++ value = [ value, [] ] ++ ++ self.insertlinelist([ value[0], varname, ++ joinfields(value[1], self.separator) ]) ++ self.vars[varname] = value ++ self.seek(place) ++ ++ def __delitem__(self, varname): ++ # delete *every* instance... ++ self.rewind() ++ while self.findnextline('^\S*[' + self.separators + ']+' + varname): ++ self.deleteline() ++ del self.vars[varname] ++ ++ def keys(self): ++ # It is rather important to return the keys in order here, ++ # in order to maintain a consistent presentation in apps. ++ place=self.tell() ++ self.rewind() ++ keys = [] ++ while self.findnextcodeline(): ++ # initialize dictionary of variable/name pairs ++ var = self.getfields() ++ if len(var) > 1: ++ keys.append(var[1]) ++ self.nextline() ++ self.seek(place) ++ return keys ++ ++class ConfEResolv(Conf): ++ # /etc/resolv.conf ++ def __init__(self): ++ Conf.__init__(self, '/etc/resolv.conf', '#', '\t ', ' ') ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ def initvars(self): ++ self.vars = {} ++ self.rewind() ++ while self.findnextcodeline(): ++ var = self.getfields() ++ if var[0] == 'nameserver': ++ if self.vars.has_key('nameservers'): ++ self.vars['nameservers'].append(var[1]) ++ else: ++ self.vars['nameservers'] = [ var[1] ] ++ else: ++ self.vars[var[0]] = var[1:] ++ self.nextline() ++ self.rewind() ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return [] ++ def __setitem__(self, varname, value): ++ # set first (should be only) instance to values in list value ++ place=self.tell() ++ self.rewind() ++ if varname == 'nameservers': ++ if self.findnextline('^nameserver[' + self.separators + ']+'): ++ # if there is a nameserver line, save the place, ++ # remove all nameserver lines, then put in new ones in order ++ placename=self.tell() ++ while self.findnextline('^nameserver['+self.separators+']+'): ++ self.deleteline() ++ self.seek(placename) ++ for nameserver in value: ++ self.insertline('nameserver' + self.separator + nameserver) ++ self.nextline() ++ self.seek(place) ++ else: ++ # no nameservers entries so far ++ self.seek(place) ++ for nameserver in value: ++ self.insertline('nameserver' + self.separator + nameserver) ++ else: ++ # not a nameserver, so all items on one line... ++ if self.findnextline('^' + varname + '[' + self.separators + ']+'): ++ self.deleteline() ++ self.insertlinelist([ varname, ++ joinfields(value, self.separator) ]) ++ self.seek(place) ++ else: ++ self.seek(place) ++ self.insertlinelist([ varname, ++ joinfields(value, self.separator) ]) ++ # no matter what, update our idea of the variable... ++ self.vars[varname] = value ++ ++ def __delitem__(self, varname): ++ # delete *every* instance... ++ self.rewind() ++ while self.findnextline('^[' + self.separators + ']*' + varname): ++ self.deleteline() ++ del self.vars[varname] ++ ++ def write(self): ++ # Need to make sure __setitem__ is called for each item to ++ # maintain consistancy, in case some did something like ++ # resolv['nameservers'].append('123.123.123.123') ++ # or ++ # resolv['search'].append('another.domain') ++ for key in self.vars.keys(): ++ self[key] = self.vars[key] ++ ++ if self.filename != '/etc/resolv.conf': ++ Conf.write(self) ++ else: ++ # bug 125712: /etc/resolv.conf modifiers MUST use ++ # change_resolv_conf function to change resolv.conf ++ import tempfile ++ self.filename = tempfile.mktemp('','/tmp/') ++ Conf.write(self) ++ import commands ++ commands.getstatusoutput("/bin/bash -c '. /etc/sysconfig/network-scripts/network-functions; change_resolv_conf "+self.filename+"'") ++ self.filename="/etc/resolv.conf" ++ def keys(self): ++ # no need to return list in order here, I think. ++ return self.vars.keys() ++ def has_key(self, key): ++ return self.vars.has_key(key) ++ ++ ++# ConfESStaticRoutes(Conf): ++# Yet another dictionary, this one for /etc/sysconfig/static-routes ++# This file has a syntax similar to that of /etc/gateways; ++# the interface name is added and active/passive is deleted: ++# net netmask gw ++# The key is the interface, the value is a list of ++# [, , ] lists ++class ConfESStaticRoutes(Conf): ++ def __init__(self): ++ Conf.__init__(self, '/etc/sysconfig/static-routes', '#', '\t ', ' ') ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ def initvars(self): ++ self.vars = {} ++ self.rewind() ++ while self.findnextcodeline(): ++ var = self.getfields() ++ if len(var) == 7: ++ if not self.vars.has_key(var[0]): ++ self.vars[var[0]] = [[var[2], var[4], var[6]]] ++ else: ++ self.vars[var[0]].append([var[2], var[4], var[6]]) ++ elif len(var) == 5: ++ if not self.vars.has_key(var[0]): ++ self.vars[var[0]] = [[var[2], var[4], '']] ++ else: ++ self.vars[var[0]].append([var[2], var[4], '']) ++ self.nextline() ++ self.rewind() ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return [[]] ++ def __setitem__(self, varname, value): ++ raise WrongMethod, 'Delete or call addroute instead' ++ def __delitem__(self, varname): ++ # since we re-write the file completely on close, we don't ++ # need to alter it piecemeal here. ++ del self.vars[varname] ++ def delroute(self, device, route): ++ # deletes a route from a device if the route exists, ++ # and if it is the only route for the device, removes ++ # the device from the dictionary ++ # Note: This could normally be optimized considerably, ++ # except that our input may have come from the file, ++ # which others may have hand-edited, and this makes it ++ # possible for us to deal with hand-inserted multiple ++ # identical routes in a reasonably correct way. ++ if self.vars.has_key(device): ++ for i in range(len(self.vars[device])): ++ if i < len(self.vars[device]) and \ ++ not cmp(self.vars[device][i], route): ++ # need first comparison because list shrinks ++ self.vars[device][i:i+1] = [] ++ if len(self.vars[device]) == 0: ++ del self.vars[device] ++ def addroute(self, device, route): ++ # adds a route to a device, deleteing it first to avoid dups ++ self.delroute(device, route) ++ if self.vars.has_key(device): ++ self.vars[device].append(route) ++ else: ++ self.vars[device] = [route] ++ def write(self): ++ # forget current version of file ++ self.rewind() ++ self.lines = [] ++ for device in self.vars.keys(): ++ for route in self.vars[device]: ++ if len(route) == 3: ++ if len(route[2]): ++ self.insertlinelist((device, 'net', route[0], ++ 'netmask', route[1], ++ 'gw', route[2])) ++ else: ++ self.insertlinelist((device, 'net', route[0], ++ 'netmask', route[1])) ++ Conf.write(self) ++ def keys(self): ++ # no need to return list in order here, I think. ++ return self.vars.keys() ++ def has_key(self, key): ++ return self.vars.has_key(key) ++ ++ ++ ++# ConfChat(Conf): ++# Not a dictionary! ++# This reads chat files, and writes a subset of chat files that ++# has all items enclosed in '' and has one expect/send pair on ++# each line. ++# Uses a list of two-element tuples. ++class ConfChat(Conf): ++ def __init__(self, filename): ++ Conf.__init__(self, filename, '', '\t ', ' ') ++ def read(self): ++ Conf.read(self) ++ self.initlist() ++ def initlist(self): ++ self.list = [] ++ i = 0 ++ hastick = 0 ++ s = '' ++ chatlist = [] ++ for line in self.lines: ++ s = s + line + ' ' ++ while i < len(s) and s[i] in " \t": ++ i = i + 1 ++ while i < len(s): ++ str = '' ++ # here i points to a new entry ++ if s[i] in "'": ++ hastick = 1 ++ i = i + 1 ++ while i < len(s) and s[i] not in "'": ++ if s[i] in '\\': ++ if not s[i+1] in " \t'": ++ str = str + '\\' ++ i = i + 1 ++ str = str + s[i] ++ i = i + 1 ++ # eat up the ending ' ++ i = i + 1 ++ else: ++ while i < len(s) and s[i] not in " \t": ++ str = str + s[i] ++ i = i + 1 ++ chatlist.append(str) ++ # eat whitespace between strings ++ while i < len(s) and s[i] in ' \t': ++ i = i + 1 ++ # now form self.list from chatlist ++ if len(chatlist) % 2: ++ chatlist.append('') ++ while chatlist: ++ self.list.append((chatlist[0], chatlist[1])) ++ chatlist[0:2] = [] ++ def getlist(self): ++ return self.list ++ def putlist(self, list): ++ self.list = list ++ def write(self): ++ # create self.lines for Conf.write... ++ self.lines = [] ++ for (p,q) in self.list: ++ p = re.sub("'", "\\'", p) ++ q = re.sub("'", "\\'", q) ++ self.lines.append("'"+p+"' '"+q+"'") ++ Conf.write(self) ++ ++ ++ ++# ConfChatFile(ConfChat): ++# This class is a ConfChat which it interprets as a netcfg-written ++# chat file with a certain amount of structure. It interprets it ++# relative to information in an "ifcfg-" file (devconf) and has a ++# set of abortstrings that can be turned on and off. ++# It exports the following data items: ++# abortstrings list of standard strings on which to abort ++# abortlist list of alternative strings on which to abort ++# defabort boolean: use the default abort strings or not ++# dialcmd string containing dial command (ATDT, for instance) ++# phonenum string containing phone number ++# chatlist list containing chat script after CONNECT ++# chatfile ConfChat instance ++class ConfChatFile(ConfChat): ++ def __init__(self, filename, devconf, abortstrings=None): ++ ConfChat.__init__(self, filename) ++ self.abortstrings = abortstrings ++ self.devconf = devconf ++ self._initlist() ++ def _initlist(self): ++ self.abortlist = [] ++ self.defabort = 1 ++ self.dialcmd = '' ++ self.phonenum = '' ++ self.chatlist = [] ++ dialexp = re.compile('^ATD[TP]?[-0-9,. #*()+]+') ++ if self.list: ++ for (p,q) in self.list: ++ if not cmp(p, 'ABORT'): ++ if not q in self.abortstrings: ++ self.abortlist.append([p,q]) ++ elif not cmp(q, self.devconf['INITSTRING']): ++ # ignore INITSTRING ++ pass ++ elif not self.dialcmd and dialexp.search(q): ++ #elif not self.dialcmd and tempmatch: ++ # First instance of something that looks like a dial ++ # command and a phone number we take as such. ++ tmp = re.search('[-0-9,. #*()+]+', q) ++ index=tmp.group(1) ++ self.dialcmd = q[:index] ++ self.phonenum = q[index:] ++ elif not cmp(p, 'CONNECT'): ++ # ignore dial command ++ pass ++ else: ++ self.chatlist.append([p,q]) ++ def _makelist(self): ++ self.list = [] ++ if self.defabort: ++ for string in self.abortstrings: ++ self.list.append(('ABORT', string)) ++ for string in self.abortlist: ++ self.list.append(('ABORT', string)) ++ self.list.append(('', self.devconf['INITSTRING'])) ++ self.list.append(('OK', self.dialcmd+self.phonenum)) ++ self.list.append(('CONNECT', '')) ++ for pair in self.chatlist: ++ self.list.append(pair) ++ def write(self): ++ self._makelist() ++ ConfChat.write(self) ++ ++ ++ ++# ConfChatFileClone(ConfChatFile): ++# Creates a chatfile, then removes it if it is identical to the chat ++# file it clones. ++class ConfChatFileClone(ConfChatFile): ++ def __init__(self, cloneInstance, filename, devconf, abortstrings=None): ++ self.ci = cloneInstance ++ ConfChatFile.__init__(self, filename, devconf, abortstrings) ++ if not self.list: ++ self.list = [] ++ for item in self.ci.list: ++ self.list.append(item) ++ self._initlist() ++ def write(self): ++ self._makelist() ++ if len(self.list) == len(self.ci.list): ++ for i in range(len(self.list)): ++ if cmp(self.list[i], self.ci.list[i]): ++ # some element differs, so they are different ++ ConfChatFile.write(self) ++ return ++ # the loop completed, so they are the same ++ if os.path.isfile(self.filename): os.unlink(self.filename) ++ else: ++ # lists are different lengths, so they are different ++ ConfChatFile.write(self) ++ ++ ++ ++ ++# ConfDIP: ++# This reads chat files, and writes a dip file based on that chat script. ++# Takes three arguments: ++# o The chatfile ++# o The name of the dipfile ++# o The ConfSHellVar instance from which to take variables in the dipfile ++class ConfDIP: ++ def __init__(self, chatfile, dipfilename, configfile): ++ self.dipfilename = dipfilename ++ self.chatfile = chatfile ++ self.cf = configfile ++ def write(self): ++ self.file = open(self.dipfilename, 'w', -1) ++ os.chmod(self.dipfilename, 0600) ++ self.file.write('# dip script for interface '+self.cf['DEVICE']+'\n' + ++ '# DO NOT HAND-EDIT; ALL CHANGES *WILL* BE LOST BY THE netcfg PROGRAM\n' + ++ '# This file is created automatically from several other files by netcfg\n' + ++ '# Re-run netcfg to modify this file\n\n' + ++ 'main:\n' + ++ ' get $local '+self.cf['IPADDR']+'\n' + ++ ' get $remote '+self.cf['REMIP']+'\n' + ++ ' port '+self.cf['MODEMPORT']+'\n' + ++ ' speed '+self.cf['LINESPEED']+'\n') ++ if self.cf['MTU']: ++ self.file.write(' get $mtu '+self.cf['MTU']+'\n') ++ for pair in self.chatfile.list: ++ if cmp(pair[0], 'ABORT') and cmp(pair[0], 'TIMEOUT'): ++ if pair[0]: ++ self.file.write(' wait '+pair[0]+' 30\n' + ++ ' if $errlvl != 0 goto error\n') ++ self.file.write(' send '+pair[1]+'\\r\\n\n' + ++ ' if $errlvl != 0 goto error\n') ++ if not cmp(self.cf['DEFROUTE'], 'yes'): ++ self.file.write(' default\n') ++ self.file.write(' mode '+self.cf['MODE']+'\n' + ++ ' exit\n' + ++ 'error:\n' + ++ ' print connection to $remote failed.\n') ++ self.file.close() ++ ++ ++class odict(UserDict): ++ def __init__(self, dict = None): ++ self._keys = [] ++ UserDict.__init__(self, dict) ++ ++ def __delitem__(self, key): ++ UserDict.__delitem__(self, key) ++ self._keys.remove(key) ++ ++ def __setitem__(self, key, item): ++ #print "[%s] = %s" % (str(key), str(item)) ++ UserDict.__setitem__(self, key, item) ++ if key not in self._keys: self._keys.append(key) ++ ++ def clear(self): ++ UserDict.clear(self) ++ self._keys = [] ++ ++ def copy(self): ++ dict = UserDict.copy(self) ++ dict._keys = self._keys[:] ++ return dict ++ ++ def items(self): ++ return zip(self._keys, self.values()) ++ ++ def keys(self): ++ return self._keys ++ ++ def popitem(self): ++ try: ++ key = self._keys[-1] ++ except IndexError: ++ raise KeyError('dictionary is empty') ++ ++ val = self[key] ++ del self[key] ++ ++ return (key, val) ++ ++ def setdefault(self, key, failobj = None): ++ UserDict.setdefault(self, key, failobj) ++ if key not in self._keys: self._keys.append(key) ++ ++ def update(self, dict): ++ UserDict.update(self, dict) ++ for key in dict.keys(): ++ if key not in self._keys: self._keys.append(key) ++ ++ def values(self): ++ return map(self.get, self._keys) ++ ++# ConfModules(Conf) ++# This reads /etc/modprobe.conf into a dictionary keyed on device type, ++# holding dictionaries: cm['eth0']['alias'] --> 'smc-ultra' ++# cm['eth0']['options'] --> {'io':'0x300', 'irq':'10'} ++# cm['eth0']['post-install'] --> ['/bin/foo','arg1','arg2'] ++# path[*] entries are ignored (but not removed) ++# New entries are added at the end to make sure that they ++# come after any path[*] entries. ++# Comments are delimited by initial '#' ++class ConfModules(Conf): ++ def __init__(self, filename = '/etc/modprobe.conf'): ++ Conf.__init__(self, filename, '#', '\t ', ' ') ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ def initvars(self): ++ self.vars = odict() ++ keys = ('alias', 'options', 'install', 'remove') ++ self.rewind() ++ while self.findnextcodeline(): ++ var = self.getfields() ++ # assume no -k field ++ if len(var) > 2 and var[0] in keys: ++ if not self.vars.has_key(var[1]): ++ self.vars[var[1]] = odict({'alias':'', 'options':odict(), 'install':[], 'remove':[]}) ++ if not cmp(var[0], 'alias'): ++ self.vars[var[1]]['alias'] = var[2] ++ elif not cmp(var[0], 'options'): ++ self.vars[var[1]]['options'] = self.splitoptlist(var[2:]) ++ elif not cmp(var[0], 'install'): ++ self.vars[var[1]]['install'] = var[2:] ++ elif not cmp(var[0], 'remove'): ++ self.vars[var[1]]['remove'] = var[2:] ++ self.nextline() ++ self.rewind() ++ def splitoptlist(self, optlist): ++ dict = odict() ++ for opt in optlist: ++ optup = self.splitopt(opt) ++ if optup: ++ dict[optup[0]] = optup[1] ++ return dict ++ def splitopt(self, opt): ++ eq = find(opt, '=') ++ if eq > 0: ++ return (opt[:eq], opt[eq+1:]) ++ else: ++ return () ++ def joinoptlist(self, dict): ++ optstring = '' ++ for key in dict.keys(): ++ optstring = optstring + key + '=' + dict[key] + ' ' ++ return optstring ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return odict() ++ ++ def __setitem__(self, varname, value): ++ # set *every* instance (should only be one, but...) to avoid surprises ++ place=self.tell() ++ self.vars[varname] = value ++ for key in value.keys(): ++ self.rewind() ++ missing=1 ++ findexp = '^[\t ]*' + key + '[\t ]+' + varname + '[\t ]+' ++ if not cmp(key, 'alias'): ++ endofline = value[key] ++ replace = key + ' ' + varname + ' ' + endofline ++ elif not cmp(key, 'options'): ++ endofline = self.joinoptlist(value[key]) ++ replace = key + ' ' + varname + ' ' + endofline ++ elif not cmp(key, 'install'): ++ endofline = joinfields(value[key], ' ') ++ replace = key + ' ' + varname + ' ' + endofline ++ elif not cmp(key, 'remove'): ++ endofline = joinfields(value[key], ' ') ++ replace = key + ' ' + varname + ' ' + endofline ++ else: ++ # some idiot apparantly put an unrecognized key in ++ # the dictionary; ignore it... ++ continue ++ if endofline: ++ # there's something to write... ++ while self.findnextline(findexp): ++ cl = split(self.getline(), '#') ++ if len(cl) >= 2: ++ comment = join(cl[1:], '#') ++ replace += ' #' + comment ++ self.setline(replace) ++ missing=0 ++ self.nextline() ++ if missing: ++ self.fsf() ++ self.insertline(replace) ++ else: ++ # delete any instances of this if they exist. ++ while self.findnextline(findexp): ++ self.deleteline() ++ self.seek(place) ++ def __delitem__(self, varname): ++ # delete *every* instance... ++ place=self.tell() ++ for key in self.vars[varname].keys(): ++ self.rewind() ++ while self.findnextline('^[\t ]*' + key + '([\t ]-k)?[\t ]+' + varname): ++ self.deleteline() ++ del self.vars[varname] ++ self.seek(place) ++ def write(self): ++ # need to make sure everything is set, because program above may ++ # well have done cm['eth0']['post-install'] = ['/bin/foo', '-f', '/tmp/bar'] ++ # which is completely reasonable, but won't invoke __setitem__ ++ for key in self.vars.keys(): ++ self[key] = self.vars[key] ++ Conf.write(self) ++ def keys(self): ++ return self.vars.keys() ++ def has_key(self, key): ++ return self.vars.has_key(key) ++ ++ ++# ConfModInfo(Conf) ++# This READ-ONLY class reads /boot/module-info. ++# The first line of /boot/module-info is "Version "; ++# this class reads versions 0 and 1 module-info files. ++class ConfModInfo(Conf): ++ def __init__(self, filename = '/boot/module-info'): ++ Conf.__init__(self, filename, '#', '\t ', ' ', create_if_missing=0) ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ def initvars(self): ++ self.vars = {} ++ self.rewind() ++ device = 0 ++ modtype = 1 ++ description = 2 ++ arguments = 3 ++ lookingfor = device ++ version = self.getfields() ++ self.nextline() ++ if not cmp(version[1], '0'): ++ # Version 0 file format ++ while self.findnextcodeline(): ++ line = self.getline() ++ if not line[0] in self.separators: ++ curdev = line ++ self.vars[curdev] = {} ++ lookingfor = modtype ++ elif lookingfor == modtype: ++ fields = self.getfields() ++ # first "field" is null (before separators) ++ self.vars[curdev]['type'] = fields[1] ++ if len(fields) > 2: ++ self.vars[curdev]['typealias'] = fields[2] ++ lookingfor = description ++ elif lookingfor == description: ++ self.vars[curdev]['description'] = re.sub( ++ '^"', '', re.sub( ++ '^['+self.separators+']', '', re.sub( ++ '"['+self.separators+']*$', '', line))) ++ lookingfor = arguments ++ elif lookingfor == arguments: ++ if not self.vars[curdev].has_key('arguments'): ++ self.vars[curdev]['arguments'] = {} ++ # get argument name (first "field" is null again) ++ thislist = [] ++ # point at first character of argument description ++ p = find(line, '"') ++ while p != -1 and p < len(line): ++ q = find(line[p+1:], '"') ++ # deal with escaped quotes (\") ++ while q != -1 and not cmp(line[p+q-1], '\\'): ++ q = find(line[p+q+1:], '"') ++ if q == -1: ++ break ++ thislist.append(line[p+1:p+q+1]) ++ # advance to beginning of next string, if any ++ r = find(line[p+q+2:], '"') ++ if r >= 0: ++ p = p+q+2+r ++ else: ++ # end of the line ++ p = r ++ self.vars[curdev]['arguments'][self.getfields()[1]] = thislist ++ self.nextline() ++ elif not cmp(version[1], '1'): ++ # Version 1 file format ++ # Version 1 format uses ' ' and ':' characters as field separators ++ # but only uses ' ' in one place, where we explicitly look for it. ++ self.separators = ':' ++ while self.findnextcodeline(): ++ line = self.getline() ++ fields = self.getfields() ++ # pull out module and linetype from the first field... ++ (module, linetype) = re.split('[ \t]+', fields[0]) ++ if not cmp(linetype, 'type'): ++ pass ++ elif not cmp(linetype, 'alias'): ++ pass ++ elif not cmp(linetype, 'desc'): ++ pass ++ elif not cmp(linetype, 'argument'): ++ pass ++ elif not cmp(linetype, 'supports'): ++ pass ++ elif not cmp(linetype, 'arch'): ++ pass ++ elif not cmp(linetype, 'pcimagic'): ++ pass ++ else: ++ # error: unknown flag... ++ raise BadFile, 'unknown flag' + linetype ++ else: ++ print 'Only versions 0 and 1 module-info files are supported' ++ raise VersionMismatch, 'Only versions 0 and 1 module-info files are supported' ++ self.rewind() ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return {} ++ def keys(self): ++ return self.vars.keys() ++ def has_key(self, key): ++ return self.vars.has_key(key) ++ def write(self): ++ pass ++ ++ ++# ConfPw(Conf) ++# This class implements a dictionary based on a :-separated file. ++# It takes as arguments the filename and the field number to key on; ++# The data provided is a list including all fields including the key. ++# Has its own write method to keep files sane. ++class ConfPw(Conf): ++ def __init__(self, filename, keyfield, numfields): ++ self.keyfield = keyfield ++ self.numfields = numfields ++ Conf.__init__(self, filename, '', ':', ':', 0) ++ def read(self): ++ Conf.read(self) ++ self.initvars() ++ def initvars(self): ++ self.vars = {} ++ # need to be able to return the keys in order to keep ++ # things consistent... ++ self.ordered_keys = [] ++ self.rewind() ++ while self.findnextcodeline(): ++ fields = self.getfields() ++ self.vars[fields[self.keyfield]] = fields ++ self.ordered_keys.append(fields[self.keyfield]) ++ self.nextline() ++ self.rewind() ++ def __setitem__(self, key, value): ++ if not self.findlinewithfield(self.keyfield, key): ++ self.fsf() ++ self.ordered_keys.append(key) ++ self.setfields(value) ++ self.vars[key] = value ++ def __getitem__(self, key): ++ if self.vars.has_key(key): ++ return self.vars[key] ++ return [] ++ def __delitem__(self, key): ++ place = self.tell() ++ self.rewind() ++ if self.findlinewithfield(self.keyfield, key): ++ self.deleteline() ++ if self.vars.has_key(key): ++ del self.vars[key] ++ for i in range(len(self.ordered_keys)): ++ if key in self.ordered_keys[i:i+1]: ++ self.ordered_keys[i:i+1] = [] ++ break ++ self.seek(place) ++ def keys(self): ++ return self.ordered_keys ++ def has_key(self, key): ++ return self.vars.has_key(key) ++ def write(self): ++ self.file = open(self.filename + '.new', 'w', -1) ++ # change the mode of the new file to that of the old one ++ if os.path.isfile(self.filename) and self.mode == -1: ++ os.chmod(self.filename + '.new', os.stat(self.filename)[0]) ++ if self.mode >= 0: ++ os.chmod(self.filename + '.new', self.mode) ++ # add newlines while writing ++ for index in range(len(self.lines)): ++ self.file.write(self.lines[index] + '\n') ++ self.file.close() ++ os.rename(self.filename + '.new', self.filename) ++ def changefield(self, key, fieldno, fieldtext): ++ self.rewind() ++ self.findlinewithfield(self.keyfield, key) ++ Conf.changefield(self, fieldno, fieldtext) ++ self.vars[key][fieldno:fieldno+1] = [fieldtext] ++ ++# ConfPwO ++# This class presents a data-oriented meta-class for making ++# changes to ConfPw-managed files. Applications should not ++# instantiate this class directly. ++class ConfPwO(ConfPw): ++ def __init__(self, filename, keyfield, numfields, reflector): ++ ConfPw.__init__(self, filename, keyfield, numfields) ++ self.reflector = reflector ++ def __getitem__(self, key): ++ if self.vars.has_key(key): ++ return self.reflector(self, key) ++ else: ++ return None ++ def __setitem__(self, key): ++ # items are objects which the higher-level code can't touch ++ raise AttributeError, 'Object ' + self + ' is immutable' ++ # __delitem__ is inherited from ConfPw ++ # Do *not* use setitem for this; adding an entry should be ++ # a much different action than accessing an entry or changing ++ # fields in an entry. ++ def addentry(self, key, list): ++ if self.vars.has_key(key): ++ raise AttributeError, key + ' exists' ++ ConfPw.__setitem__(self, key, list) ++ def getfreeid(self, fieldnum): ++ freeid = 500 ++ # first, we try not to re-use id's that have already been assigned. ++ for item in self.vars.keys(): ++ id = atoi(self.vars[item][fieldnum]) ++ if id >= freeid and id < 65533: # ignore nobody on some systems ++ freeid = id + 1 ++ if freeid > 65533: ++ # if that didn't work, we go back and find any free id over 500 ++ ids = {} ++ for item in self.vars.keys(): ++ ids[atoi(self.vars[item][fieldnum])] = 1 ++ i = 500 ++ while i < 65535 and ids.has_key(i): ++ i = i + 1 ++ if freeid > 65533: ++ raise SystemFull, 'No IDs available' ++ return freeid ++ ++# ConfPasswd(ConfPwO) ++# This class presents a data-oriented class for making changes ++# to the /etc/passwd file. ++class _passwd_reflector: ++ # first, we need a helper class... ++ def __init__(self, pw, user): ++ self.pw = pw ++ self.user = user ++ def setgecos(self, oldgecos, fieldnum, value): ++ gecosfields = split(oldgecos, ',') ++ # make sure that we have enough gecos fields ++ for i in range(5-len(gecosfields)): ++ gecosfields.append('') ++ gecosfields[fieldnum] = value ++ return join(gecosfields[0:5], ',') ++ def getgecos(self, oldgecos, fieldnum): ++ gecosfields = split(oldgecos, ',') ++ # make sure that we have enough gecos fields ++ for i in range(5-len(gecosfields)): ++ gecosfields.append('') ++ return gecosfields[fieldnum] ++ def __getitem__(self, name): ++ return self.__getattr__(name) ++ def __setitem__(self, name, value): ++ return self.__setattr__(name, value) ++ def __getattr__(self, name): ++ if not self.pw.has_key(self.user): ++ raise AttributeError, self.user + ' has been deleted' ++ if not cmp(name,'username'): ++ return self.pw.vars[self.user][0] ++ elif not cmp(name,'password'): ++ return self.pw.vars[self.user][1] ++ elif not cmp(name,'uid'): ++ return self.pw.vars[self.user][2] ++ elif not cmp(name,'gid'): ++ return self.pw.vars[self.user][3] ++ elif not cmp(name,'gecos'): ++ return self.pw.vars[self.user][4] ++ elif not cmp(name,'fullname'): ++ return self.getgecos(self.pw.vars[self.user][4], 0) ++ elif not cmp(name,'office'): ++ return self.getgecos(self.pw.vars[self.user][4], 1) ++ elif not cmp(name,'officephone'): ++ return self.getgecos(self.pw.vars[self.user][4], 2) ++ elif not cmp(name,'homephone'): ++ return self.getgecos(self.pw.vars[self.user][4], 3) ++ elif not cmp(name,'homedir'): ++ return self.pw.vars[self.user][5] ++ elif not cmp(name,'shell'): ++ return self.pw.vars[self.user][6] ++ else: ++ raise AttributeError, name ++ def __setattr__(self, name, value): ++ if not cmp(name, 'pw') or not cmp(name, 'user') \ ++ or not cmp(name, 'setgecos') \ ++ or not cmp(name, 'getgecos'): ++ self.__dict__[name] = value ++ return None ++ if not self.pw.has_key(self.user): ++ raise AttributeError, self.user + ' has been deleted' ++ if not cmp(name,'username'): ++ # username is not an lvalue... ++ raise AttributeError, name + ': key is immutable' ++ elif not cmp(name,'password'): ++ self.pw.changefield(self.user, 1, value) ++ elif not cmp(name,'uid'): ++ self.pw.changefield(self.user, 2, str(value)) ++ elif not cmp(name,'gid'): ++ self.pw.changefield(self.user, 3, str(value)) ++ elif not cmp(name,'gecos'): ++ self.pw.changefield(self.user, 4, value) ++ elif not cmp(name,'fullname'): ++ self.pw.changefield(self.user, 4, ++ self.setgecos(self.pw.vars[self.user][4], 0, value)) ++ elif not cmp(name,'office'): ++ self.pw.changefield(self.user, 4, ++ self.setgecos(self.pw.vars[self.user][4], 1, value)) ++ elif not cmp(name,'officephone'): ++ self.pw.changefield(self.user, 4, ++ self.setgecos(self.pw.vars[self.user][4], 2, value)) ++ elif not cmp(name,'homephone'): ++ self.pw.changefield(self.user, 4, ++ self.setgecos(self.pw.vars[self.user][4], 3, value)) ++ elif not cmp(name,'homedir'): ++ self.pw.changefield(self.user, 5, value) ++ elif not cmp(name,'shell'): ++ self.pw.changefield(self.user, 6, value) ++ else: ++ raise AttributeError, name ++class ConfPasswd(ConfPwO): ++ def __init__(self): ++ ConfPwO.__init__(self, '/etc/passwd', 0, 7, _passwd_reflector) ++ def addentry(self, username, password, uid, gid, gecos, homedir, shell): ++ ConfPwO.addentry(self, username, [username, password, uid, gid, gecos, homedir, shell]) ++ def addfullentry(self, username, password, uid, gid, fullname, office, ++ officephone, homephone, homedir, shell): ++ self.addentry(username, password, uid, gid, join([fullname, ++ office, officephone, homephone, ''], ','), homedir, shell) ++ def getfreeuid(self): ++ try: ++ return self.getfreeid(2) ++ except: ++ raise SystemFull, 'No UIDs available' ++ ++ ++# ConfShadow(ConfPwO) ++# This class presents a data-oriented class for making changes ++# to the /etc/shadow file. ++class _shadow_reflector: ++ # first, we need a helper class... ++ def __init__(self, pw, user): ++ self.pw = pw ++ self.user = user ++ def _readstr(self, fieldno): ++ return self.pw.vars[self.user][fieldno] ++ def _readint(self, fieldno): ++ retval = self.pw.vars[self.user][fieldno] ++ if len(retval): return atoi(retval) ++ return -1 ++ def __getitem__(self, name): ++ return self.__getattr__(name) ++ def __setitem__(self, name, value): ++ return self.__setattr__(name, value) ++ def __getattr__(self, name): ++ if not self.pw.has_key(self.user): ++ raise AttributeError, self.user + ' has been deleted' ++ if not cmp(name,'username'): ++ return self._readstr(0) ++ elif not cmp(name,'password'): ++ return self._readstr(1) ++ elif not cmp(name,'lastchanged'): ++ return self._readint(2) ++ elif not cmp(name,'mindays'): ++ return self._readint(3) ++ elif not cmp(name,'maxdays'): ++ return self._readint(4) ++ elif not cmp(name,'warndays'): ++ return self._readint(5) ++ elif not cmp(name,'gracedays'): ++ return self._readint(6) ++ elif not cmp(name,'expires'): ++ return self._readint(7) ++ else: ++ raise AttributeError, name ++ def __setattr__(self, name, value): ++ if not cmp(name, 'pw') or not cmp(name, 'user'): ++ self.__dict__[name] = value ++ return None ++ if not self.pw.has_key(self.user): ++ raise AttributeError, self.user + ' has been deleted' ++ if not cmp(name,'username'): ++ # username is not an lvalue... ++ raise AttributeError, name + ': key is immutable' ++ elif not cmp(name,'password'): ++ self.pw.changefield(self.user, 1, value) ++ elif not cmp(name,'lastchanged'): ++ if not len(str(value)) or value == -1: ++ raise AttributeError, 'illegal value for lastchanged' ++ self.pw.changefield(self.user, 2, str(value)) ++ elif not cmp(name,'mindays'): ++ if not len(str(value)) or value == -1: ++ value = '' ++ self.pw.changefield(self.user, 3, str(value)) ++ elif not cmp(name,'maxdays'): ++ if not len(str(value)) or value == -1: ++ value = '' ++ self.pw.changefield(self.user, 4, str(value)) ++ elif not cmp(name,'warndays'): ++ if not len(str(value)) or value == -1: ++ value = '' ++ self.pw.changefield(self.user, 5, str(value)) ++ elif not cmp(name,'gracedays'): ++ if not len(str(value)) or value == -1: ++ value = '' ++ self.pw.changefield(self.user, 6, str(value)) ++ elif not cmp(name,'expires'): ++ if not len(str(value)) or value == -1: ++ value = '' ++ self.pw.changefield(self.user, 7, str(value)) ++ else: ++ raise AttributeError, name ++class ConfShadow(ConfPwO): ++ def __init__(self): ++ ConfPwO.__init__(self, '/etc/shadow', 0, 9, _shadow_reflector) ++ def addentry(self, username, password, lastchanged, mindays, maxdays, warndays, gracedays, expires): ++ # we need that final '' so that the final : (delimited the ++ # "reserved field" is preserved by ConfPwO.addentry()) ++ ConfPwO.addentry(self, username, ++ [username, password, self._intfield(lastchanged), ++ self._intfield(mindays), self._intfield(maxdays), ++ self._intfield(warndays), self._intfield(gracedays), ++ self._intfield(expires), '']) ++ def _intfield(self, value): ++ try: ++ atoi(value) ++ return value ++ except: ++ if value == -1: ++ return '' ++ else: ++ return str(value) ++ ++# ConfGroup(ConfPwO) ++# This class presents a data-oriented class for making changes ++# to the /etc/group file. ++# May be replaced by a pwdb-based module, we hope. ++class _group_reflector: ++ # first, we need a helper class... ++ def __init__(self, pw, group): ++ self.pw = pw ++ self.group = group ++ def __getitem__(self, name): ++ return self.__getattr__(name) ++ def __setitem__(self, name, value): ++ return self.__setattr__(name, value) ++ def __getattr__(self, name): ++ if not self.pw.has_key(self.group): ++ raise AttributeError, self.group + ' has been deleted' ++ if not cmp(name,'name'): ++ return self.pw.vars[self.group][0] ++ elif not cmp(name,'password'): ++ return self.pw.vars[self.group][1] ++ elif not cmp(name,'gid'): ++ return self.pw.vars[self.group][2] ++ elif not cmp(name,'userlist'): ++ return self.pw.vars[self.group][3] ++ else: ++ raise AttributeError. name ++ def __setattr__(self, name, value): ++ if not cmp(name, 'pw') or not cmp(name, 'group'): ++ self.__dict__[name] = value ++ return None ++ if not self.pw.has_key(self.group): ++ raise AttributeError, self.group + ' has been deleted' ++ if not cmp(name,'name'): ++ # username is not an lvalue... ++ raise AttributeError, name + ': key is immutable' ++ elif not cmp(name,'password'): ++ self.pw.changefield(self.group, 1, value) ++ elif not cmp(name,'gid'): ++ self.pw.changefield(self.group, 2, str(value)) ++ elif not cmp(name,'userlist'): ++ self.pw.changefield(self.group, 3, value) ++ else: ++ raise AttributeError, name ++class ConfGroup(ConfPwO): ++ def __init__(self): ++ ConfPwO.__init__(self, '/etc/group', 0, 4, _group_reflector) ++ def addentry(self, group, password, gid, userlist): ++ ConfPwO.addentry(self, group, [group, password, gid, userlist]) ++ def getfreegid(self): ++ try: ++ return self.getfreeid(2) ++ except: ++ raise SystemFull, 'No GIDs available' ++ ++ def nameofgid(self, gid): ++ try: gid = atoi(gid) ++ except: return '' ++ for group in self.vars.keys(): ++ id = atoi(self.vars[group][2]) ++ if id == gid: ++ return self.vars[group][0] ++ return '' ++ ++ ++# ConfUnix() ++# This class presents a data-oriented class which uses the ConfPasswd ++# and ConfShadow classes (if /etc/shadow exists) to hold data. ++# Designed to be replaced by a pwdb module eventually, we hope. ++class _unix_reflector: ++ # first, we need a helper class... ++ def __init__(self, pw, user): ++ self.pw = pw ++ self.user = user ++ def __getitem__(self, name): ++ return self.__getattr__(name) ++ def __setitem__(self, name, value): ++ return self.__setattr__(name, value) ++ def __getattr__(self, name): ++ if not self.pw.passwd.has_key(self.user): ++ raise AttributeError, self.user + ' has been deleted' ++ if not cmp(name,'username'): ++ if self.pw.shadow: ++ return self.pw.shadow[self.user].username ++ else: ++ return self.pw.passwd[self.user].username ++ elif not cmp(name,'password'): ++ if self.pw.shadow: ++ return self.pw.shadow[self.user].password ++ else: ++ return self.pw.passwd[self.user].password ++ elif not cmp(name,'uid'): ++ return self.pw.passwd[self.user].uid ++ elif not cmp(name,'gid'): ++ return self.pw.passwd[self.user].gid ++ elif not cmp(name,'gecos'): ++ return self.pw.passwd[self.user].gecos ++ elif not cmp(name,'fullname'): ++ return self.pw.passwd[self.user].fullname ++ elif not cmp(name,'office'): ++ return self.pw.passwd[self.user].office ++ elif not cmp(name,'officephone'): ++ return self.pw.passwd[self.user].officephone ++ elif not cmp(name,'homephone'): ++ return self.pw.passwd[self.user].homephone ++ elif not cmp(name,'homedir'): ++ return self.pw.passwd[self.user].homedir ++ elif not cmp(name,'shell'): ++ return self.pw.passwd[self.user].shell ++ elif not cmp(name,'lastchanged'): ++ if self.pw.shadowexists(): ++ return self.pw.shadow[self.user].lastchanged ++ else: ++ return -1 ++ elif not cmp(name,'mindays'): ++ if self.pw.shadowexists(): ++ return self.pw.shadow[self.user].mindays ++ else: ++ return -1 ++ elif not cmp(name,'maxdays'): ++ if self.pw.shadowexists(): ++ return self.pw.shadow[self.user].maxdays ++ else: ++ return -1 ++ elif not cmp(name,'warndays'): ++ if self.pw.shadowexists(): ++ return self.pw.shadow[self.user].warndays ++ else: ++ return -1 ++ elif not cmp(name,'gracedays'): ++ if self.pw.shadowexists(): ++ return self.pw.shadow[self.user].gracedays ++ else: ++ return -1 ++ elif not cmp(name,'expires'): ++ if self.pw.shadowexists(): ++ return self.pw.shadow[self.user].expires ++ else: ++ return -1 ++ else: ++ raise AttributeError, name ++ def __setattr__(self, name, value): ++ if not cmp(name, 'pw') or not cmp(name, 'user'): ++ self.__dict__[name] = value ++ return None ++ if not self.pw.passwd.has_key(self.user): ++ raise AttributeError, self.user + ' has been deleted' ++ if not cmp(name,'username'): ++ # username is not an lvalue... ++ raise AttributeError, name + ': key is immutable' ++ elif not cmp(name,'password'): ++ if self.pw.shadow: ++ self.pw.shadow[self.user].password = value ++ else: ++ self.pw.passwd[self.user].password = value ++ elif not cmp(name,'uid'): ++ self.pw.passwd[self.user].uid = value ++ elif not cmp(name,'gid'): ++ self.pw.passwd[self.user].gid = value ++ elif not cmp(name,'gecos'): ++ self.pw.passwd[self.user].gecos = value ++ elif not cmp(name,'fullname'): ++ self.pw.passwd[self.user].fullname = value ++ elif not cmp(name,'office'): ++ self.pw.passwd[self.user].office = value ++ elif not cmp(name,'officephone'): ++ self.pw.passwd[self.user].officephone = value ++ elif not cmp(name,'homephone'): ++ self.pw.passwd[self.user].homephone = value ++ elif not cmp(name,'homedir'): ++ self.pw.passwd[self.user].homedir = value ++ elif not cmp(name,'shell'): ++ self.pw.passwd[self.user].shell = value ++ elif not cmp(name,'lastchanged'): ++ if self.pw.shadowexists(): ++ self.pw.shadow[self.user].lastchanged = value ++ elif not cmp(name,'mindays'): ++ if self.pw.shadowexists(): ++ self.pw.shadow[self.user].mindays = value ++ elif not cmp(name,'maxdays'): ++ if self.pw.shadowexists(): ++ self.pw.shadow[self.user].maxdays = value ++ elif not cmp(name,'warndays'): ++ if self.pw.shadowexists(): ++ self.pw.shadow[self.user].warndays = value ++ elif not cmp(name,'gracedays'): ++ if self.pw.shadowexists(): ++ self.pw.shadow[self.user].gracedays = value ++ elif not cmp(name,'expires'): ++ if self.pw.shadowexists(): ++ self.pw.shadow[self.user].expires = value ++ else: ++ raise AttributeError, name ++ ++class ConfSysctl(Conf): ++ def __init__(self, filename): ++ Conf.__init__(self, filename, commenttype='#', ++ separators='=', separator='=') ++ def read(self): ++ Conf.read(self) ++ Conf.sedline(self, '\n', '') ++ Conf.sedline(self, ' ', ' ') ++ self.initvars() ++ def initvars(self): ++ self.vars = {} ++ self.rewind() ++ while self.findnextcodeline(): ++ var = self.getfields() ++ # fields 1..n are false matches on "=" character in string, ++ # which is messed up, but try to deal with it ++ var[1] = joinfields(var[1:len(var)], '=') ++ # snip off leading and trailing spaces, which are legal (it's ++ # how sysctl(1) prints them) but can be confusing, and tend to ++ # screw up Python's dictionaries ++ var[0] = strip(var[0]) ++ var[1] = strip(var[1]) ++ if self.vars.has_key(var[0]): ++ self.deleteline() ++ self.vars[var[0]] = var[1] ++ else: ++ self.vars[var[0]] = var[1] ++ self.line = self.line + 1 ++ self.rewind() ++ def __setitem__(self, varname, value): ++ # set it in the line list ++ self.rewind() ++ foundit = 0 ++ while self.findnextcodeline(): ++ var = self.getfields() ++ # snip off leading and trailing spaces, which are legal (it's ++ # how sysctl(1) prints them) but can be confusing, and tend to ++ # screw up Python's dictionaries ++ if(strip(var[0]) == varname): ++ while(strip(var[0]) == varname): ++ self.deleteline() ++ var = self.getfields() ++ for part in split(value, '\n'): ++ self.insertline(varname + ' = ' + part) ++ self.line = self.line + 1 ++ foundit = 1 ++ self.line = self.line + 1 ++ if(foundit == 0): ++ for part in split(value, '\n'): ++ self.lines.append(varname + ' = ' + part) ++ self.rewind() ++ # re-read the file, sort of ++ self.initvars() ++ def __getitem__(self, varname): ++ if self.vars.has_key(varname): ++ return self.vars[varname] ++ else: ++ return '' ++ def write(self): ++ self.file = open(self.filename, 'w', -1) ++ if self.mode >= 0: ++ os.chmod(self.filename, self.mode) ++ # add newlines ++ for index in range(len(self.lines)): ++ self.file.write(self.lines[index] + '\n'); ++ self.file.close() +diff -Nur system-config-printer-0.7.27/cupsd.py system-config-printer-0.7.27.new/cupsd.py +--- system-config-printer-0.7.27/cupsd.py 2006-07-07 17:47:04.000000000 +0300 ++++ system-config-printer-0.7.27.new/cupsd.py 2006-09-08 14:36:34.000000000 +0300 +@@ -1,6 +1,6 @@ + #!/bin/env python + import re +-from rhpl.Conf import Conf ++from Conf import Conf + + class Directive: + separator = ' ' +diff -Nur system-config-printer-0.7.27/cupshelpers.py system-config-printer-0.7.27.new/cupshelpers.py +--- system-config-printer-0.7.27/cupshelpers.py 2006-08-23 15:51:44.000000000 +0300 ++++ system-config-printer-0.7.27.new/cupshelpers.py 2006-09-08 14:34:56.000000000 +0300 +@@ -21,7 +21,7 @@ + ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + import cups, pprint, os, tempfile, re +-from rhpl.translate import _, N_ ++from gettext import gettext as _ + + class Printer: + --- system-config-printer-0.7.71+-svn1371.orig/debian/copyright +++ system-config-printer-0.7.71+-svn1371/debian/copyright @@ -0,0 +1,38 @@ +This package was debianized by Jani Monoses on +Fri, 8 Sep 2006 12:08:23 +0300 + +It was downloaded from http://cyberelk.net/tim/data/system-config-printer/ + +Upstream Author: Tim Waugh + Florian Festi + +Copyright: 1996-2006 Red Hat, Inc + 2002-2006 Tim Waugh + 2006 Florian Festi + + +License: + + This package 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 package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. + +The Debian packaging is (C) 2006, Jani Monoses and +is licensed under the GPL, see above. + + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here.