root / tea4cups / trunk / cupsoftee @ 570

Revision 570, 9.9 kB (checked in by jerome, 19 years ago)

Should now be able to handle the configuration file.

  • 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(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 getPrinterOption(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 discoverOtherBackends(self) :   
95        """Discovers the other CUPS backends.
96       
97           Executes each existing backend in turn in device enumeration mode.
98           Returns the list of available backends.
99        """
100        # Unfortunately this method can't output any debug information
101        # to stdout or stderr, else CUPS considers that the device is
102        # not available.
103        available = []
104        (directory, myname) = os.path.split(sys.argv[0])
105        tmpdir = tempfile.gettempdir()
106        lockfilename = os.path.join(tmpdir, "%s..LCK" % myname)
107        if os.path.exists(lockfilename) :
108            lockfile = open(lockfilename, "r")
109            pid = int(lockfile.read())
110            lockfile.close()
111            try :
112                # see if the pid contained in the lock file is still running
113                os.kill(pid, 0)
114            except OSError, e :   
115                if e.errno != errno.EPERM :
116                    # process doesn't exist anymore
117                    os.remove(lockfilename)
118           
119        if not os.path.exists(lockfilename) :
120            lockfile = open(lockfilename, "w")
121            lockfile.write("%i" % os.getpid())
122            lockfile.close()
123            allbackends = [ os.path.join(directory, b) \
124                                for b in os.listdir(directory) 
125                                    if os.access(os.path.join(directory, b), os.X_OK) \
126                                        and (b != myname)] 
127            for backend in allbackends :                           
128                answer = os.popen(backend, "r")
129                try :
130                    devices = [line.strip() for line in answer.readlines()]
131                except :   
132                    devices = []
133                status = answer.close()
134                if status is None :
135                    for d in devices :
136                        # each line is of the form :
137                        # 'xxxx xxxx "xxxx xxx" "xxxx xxx"'
138                        # so we have to decompose it carefully
139                        fdevice = cStringIO.StringIO(d)
140                        tokenizer = shlex.shlex(fdevice)
141                        tokenizer.wordchars = tokenizer.wordchars + \
142                                                        r".:,?!~/\_$*-+={}[]()#"
143                        arguments = []
144                        while 1 :
145                            token = tokenizer.get_token()
146                            if token :
147                                arguments.append(token)
148                            else :
149                                break
150                        fdevice.close()
151                        try :
152                            (devicetype, device, name, fullname) = arguments
153                        except ValueError :   
154                            pass    # ignore this 'bizarre' device
155                        else :   
156                            if name.startswith('"') and name.endswith('"') :
157                                name = name[1:-1]
158                            if fullname.startswith('"') and fullname.endswith('"') :
159                                fullname = fullname[1:-1]
160                            available.append('%s cupsoftee:%s "CupsOfTee+%s" "CupsOfTee managed %s"' \
161                                                 % (devicetype, device, name, fullname))
162            os.remove(lockfilename)
163        return available
164                       
165    def fakePrint(self) :   
166        """Fakes to print the job."""
167        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
168            # Either the job's datas are on our stdin, or in a file
169            if len(sys.argv) == 7 :
170                self.InputFile = sys.argv[6]
171            else :   
172                self.InputFile = None
173               
174            # check that the DEVICE_URI environment variable's value is
175            # prefixed with "cupsoftee:" otherwise don't touch it.
176            # If this is the case, we have to remove the prefix from
177            # the environment before launching the real backend in cupspykota
178            device_uri = os.environ.get("DEVICE_URI", "")
179            if device_uri.startswith("cupspykota:") :
180                fulldevice_uri = device_uri[:]
181                device_uri = fulldevice_uri[len("cupspykota:"):]
182                if device_uri.startswith("//") :    # lpd (at least)
183                    device_uri = device_uri[2:]
184                os.environ["DEVICE_URI"] = device_uri   # TODO : side effect !
185            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
186            try :
187                (backend, destination) = device_uri.split(":", 1) 
188            except ValueError :   
189                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
190            while destination.startswith("/") :
191                destination = destination[1:]
192            checkauth = destination.split("@", 1)   
193            if len(checkauth) == 2 :
194                destination = checkauth[1]
195            printerhostname = destination.split("/")[0].split(":")[0]
196            return ("CUPS", \
197                    printerhostname, \
198                    os.environ.get("PRINTER"), \
199                    sys.argv[2].strip(), \
200                    sys.argv[1].strip(), \
201                    inputfile, \
202                    int(sys.argv[4].strip()), \
203                    sys.argv[3], \
204                    sys.argv[5], \
205                    backend)
206       
207    def logDebug(self, message) :   
208        """Logs something to debug output if debug is enabled."""
209        if self.debug :
210            sys.stderr.write("DEBUG: %s\n" % message)
211            sys.stderr.flush()
212           
213    def logInfo(self, message, level="info") :       
214        """Logs a message to CUPS' error_log file."""
215        sys.stderr.write("%s: %s\n" % (level.upper(), message))
216        sys.stderr.flush()
217
218if __name__ == "__main__" :   
219    # This is a CUPS backend, we should act and die like a CUPS backend
220    wrapper = CupsBackend()
221    if len(sys.argv) == 1 :
222        print "\n".join(wrapper.discoverOtherBackends())
223        sys.exit(0)               
224    elif len(sys.argv) not in (6, 7) :   
225        sys.stderr.write("ERROR: %s job-id user title copies options [file]\n"\
226                              % sys.argv[0])
227        sys.exit(1)
228    else :   
229        sys.stderr.write("ERROR: Not Yet !\n")
230        sys.exit(1)
Note: See TracBrowser for help on using the browser.