root / tea4cups / trunk / cupsoftee @ 573

Revision 573, 11.1 kB (checked in by jerome, 20 years ago)

Added configuration file parser

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Rev
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# CupsOfTee : Tee for CUPS
5#
6# (c) 2005 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20#
21# $Id$
22#
23#
24
25import sys
26import os
27import errno
28import cStringIO
29import shlex
30import tempfile
31import ConfigParser
32
33class TeeError(Exception):
34    """Base exception for CupsOfTee related stuff."""
35    def __init__(self, message = ""):
36        self.message = message
37        Exception.__init__(self, message)
38    def __repr__(self):
39        return self.message
40    __str__ = __repr__
41   
42class ConfigError(TeeError) :   
43    """Configuration related exceptions."""
44    pass 
45   
46class FakeConfig :   
47    """Fakes a configuration file parser."""
48    def get(self, section, option, raw=0) :
49        """Fakes the retrieval of a global option."""
50        raise ConfigError, "Invalid configuration file : no option %s in section [%s]" % (option, section)
51       
52class CupsBackend :
53    """Base class for tools with no database access."""
54    def __init__(self) :
55        """Initializes the CUPS backend wrapper."""
56        confdir = os.environ.get("CUPS_SERVERROOT", ".") 
57        self.conffile = os.path.join(confdir, "cupsoftee.conf")
58        if os.path.isfile(self.conffile) :
59            self.config = ConfigParser.ConfigParser()
60            self.config.read([self.conffile])
61            self.debug = self.isTrue(self.getGlobalOption("debug", ignore=1))
62        else :   
63            self.config = FakeConfig()
64            self.debug = 1      # no config, so force debug mode !
65           
66    def isTrue(self, option) :       
67        """Returns 1 if option is set to true, else 0."""
68        if (option is not None) and (option.upper().strip() in ['Y', 'YES', '1', 'ON', 'T', 'TRUE']) :
69            return 1
70        else :   
71            return 0
72                       
73    def getGlobalOption(self, option, ignore=0) :   
74        """Returns an option from the global section, or raises a ConfigError if ignore is not set, else returns None."""
75        try :
76            return self.config.get("global", option, raw=1)
77        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
78            if ignore :
79                return
80            else :
81                raise ConfigError, "Option %s not found in section global of %s" % (option, self.conffile)
82               
83    def getSectionOption(self, sectionname, option) :   
84        """Returns an option from the printer section, or the global section, or raises a ConfigError."""
85        globaloption = self.getGlobalOption(option, ignore=1)
86        try :
87            return self.config.get(sectionname, option, raw=1)
88        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
89            if globaloption is not None :
90                return globaloption
91            else :
92                raise ConfigError, "Option %s not found in section [%s] of %s" % (option, sectionname, self.conffile)
93               
94    def enumTeeBranches(self, sectionname) :
95        """Returns the list of branches for a particular section's Tee."""
96        try :
97            globalbranches = [ (k, v) for (k, v) in self.config.items("global") if k.startswith("tee_") ]
98        except ConfigParser.NoSectionError, msg :   
99            self.printInfo("Invalid configuration file : %s" % msg, "error")
100            globalbranches = []
101        try :
102            sectionbranches = [ (k, v) for (k, v) in self.config.items(sectionname) if k.startswith("tee_") ]
103        except ConfigParser.NoSectionError, msg :   
104            self.printInfo("Invalid configuration file : %s" % msg, "error")
105            sectionbranches = []
106        branches = {}
107        for (k, v) in globalbranches :
108            value = v.strip()
109            if value :
110                branches[k] = value
111        for (k, v) in sectionbranches :   
112            value = v.strip()
113            if value :
114                branches[k] = value # overwrite any global option or set a new value
115            else :   
116                del branches[k] # empty value disables a global option
117        return branches
118       
119    def discoverOtherBackends(self) :   
120        """Discovers the other CUPS backends.
121       
122           Executes each existing backend in turn in device enumeration mode.
123           Returns the list of available backends.
124        """
125        # Unfortunately this method can't output any debug information
126        # to stdout or stderr, else CUPS considers that the device is
127        # not available.
128        available = []
129        (directory, myname) = os.path.split(sys.argv[0])
130        tmpdir = tempfile.gettempdir()
131        lockfilename = os.path.join(tmpdir, "%s..LCK" % myname)
132        if os.path.exists(lockfilename) :
133            lockfile = open(lockfilename, "r")
134            pid = int(lockfile.read())
135            lockfile.close()
136            try :
137                # see if the pid contained in the lock file is still running
138                os.kill(pid, 0)
139            except OSError, e :   
140                if e.errno != errno.EPERM :
141                    # process doesn't exist anymore
142                    os.remove(lockfilename)
143           
144        if not os.path.exists(lockfilename) :
145            lockfile = open(lockfilename, "w")
146            lockfile.write("%i" % os.getpid())
147            lockfile.close()
148            allbackends = [ os.path.join(directory, b) \
149                                for b in os.listdir(directory) 
150                                    if os.access(os.path.join(directory, b), os.X_OK) \
151                                        and (b != myname)] 
152            for backend in allbackends :                           
153                answer = os.popen(backend, "r")
154                try :
155                    devices = [line.strip() for line in answer.readlines()]
156                except :   
157                    devices = []
158                status = answer.close()
159                if status is None :
160                    for d in devices :
161                        # each line is of the form :
162                        # 'xxxx xxxx "xxxx xxx" "xxxx xxx"'
163                        # so we have to decompose it carefully
164                        fdevice = cStringIO.StringIO(d)
165                        tokenizer = shlex.shlex(fdevice)
166                        tokenizer.wordchars = tokenizer.wordchars + \
167                                                        r".:,?!~/\_$*-+={}[]()#"
168                        arguments = []
169                        while 1 :
170                            token = tokenizer.get_token()
171                            if token :
172                                arguments.append(token)
173                            else :
174                                break
175                        fdevice.close()
176                        try :
177                            (devicetype, device, name, fullname) = arguments
178                        except ValueError :   
179                            pass    # ignore this 'bizarre' device
180                        else :   
181                            if name.startswith('"') and name.endswith('"') :
182                                name = name[1:-1]
183                            if fullname.startswith('"') and fullname.endswith('"') :
184                                fullname = fullname[1:-1]
185                            available.append('%s cupsoftee:%s "CupsOfTee+%s" "CupsOfTee managed %s"' \
186                                                 % (devicetype, device, name, fullname))
187            os.remove(lockfilename)
188        return available
189                       
190    def fakePrint(self) :   
191        """Fakes to print the job."""
192        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
193            # Either the job's datas are on our stdin, or in a file
194            if len(sys.argv) == 7 :
195                self.InputFile = sys.argv[6]
196            else :   
197                self.InputFile = None
198               
199            # check that the DEVICE_URI environment variable's value is
200            # prefixed with "cupsoftee:" otherwise don't touch it.
201            # If this is the case, we have to remove the prefix from
202            # the environment before launching the real backend in cupspykota
203            device_uri = os.environ.get("DEVICE_URI", "")
204            if device_uri.startswith("cupspykota:") :
205                fulldevice_uri = device_uri[:]
206                device_uri = fulldevice_uri[len("cupspykota:"):]
207                if device_uri.startswith("//") :    # lpd (at least)
208                    device_uri = device_uri[2:]
209                os.environ["DEVICE_URI"] = device_uri   # TODO : side effect !
210            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
211            try :
212                (backend, destination) = device_uri.split(":", 1) 
213            except ValueError :   
214                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
215            while destination.startswith("/") :
216                destination = destination[1:]
217            checkauth = destination.split("@", 1)   
218            if len(checkauth) == 2 :
219                destination = checkauth[1]
220            printerhostname = destination.split("/")[0].split(":")[0]
221            return ("CUPS", \
222                    printerhostname, \
223                    os.environ.get("PRINTER"), \
224                    sys.argv[2].strip(), \
225                    sys.argv[1].strip(), \
226                    inputfile, \
227                    int(sys.argv[4].strip()), \
228                    sys.argv[3], \
229                    sys.argv[5], \
230                    backend)
231       
232    def logDebug(self, message) :   
233        """Logs something to debug output if debug is enabled."""
234        if self.debug :
235            sys.stderr.write("DEBUG: %s\n" % message)
236            sys.stderr.flush()
237           
238    def logInfo(self, message, level="info") :       
239        """Logs a message to CUPS' error_log file."""
240        sys.stderr.write("%s: %s\n" % (level.upper(), message))
241        sys.stderr.flush()
242
243if __name__ == "__main__" :   
244    # This is a CUPS backend, we should act and die like a CUPS backend
245    wrapper = CupsBackend()
246    if len(sys.argv) == 1 :
247        print "\n".join(wrapper.discoverOtherBackends())
248        sys.exit(0)               
249    elif len(sys.argv) not in (6, 7) :   
250        sys.stderr.write("ERROR: %s job-id user title copies options [file]\n"\
251                              % sys.argv[0])
252        sys.exit(1)
253    else :   
254        sys.stderr.write("ERROR: Not Yet !\n")
255        sys.exit(1)
Note: See TracBrowser for help on using the browser.