root / tea4cups / trunk / cupsoftee @ 571

Revision 571, 10.1 kB (checked in by jerome, 19 years ago)

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