root / tea4cups / trunk / cupsoftee @ 574

Revision 574, 11.1 kB (checked in by jerome, 19 years ago)

Moved some methods

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Rev
RevLine 
[565]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
[568]27import errno
[565]28import cStringIO
29import shlex
[568]30import tempfile
[570]31import ConfigParser
[565]32
[570]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       
[568]52class CupsBackend :
53    """Base class for tools with no database access."""
54    def __init__(self) :
55        """Initializes the CUPS backend wrapper."""
[570]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])
[573]61            self.debug = self.isTrue(self.getGlobalOption("debug", ignore=1))
[570]62        else :   
63            self.config = FakeConfig()
64            self.debug = 1      # no config, so force debug mode !
[574]65       
66    def logDebug(self, message) :   
67        """Logs something to debug output if debug is enabled."""
68        if self.debug :
69            sys.stderr.write("DEBUG: %s\n" % message)
70            sys.stderr.flush()
[570]71           
[574]72    def logInfo(self, message, level="info") :       
73        """Logs a message to CUPS' error_log file."""
74        sys.stderr.write("%s: %s\n" % (level.upper(), message))
75        sys.stderr.flush()
76       
[570]77    def isTrue(self, option) :       
78        """Returns 1 if option is set to true, else 0."""
79        if (option is not None) and (option.upper().strip() in ['Y', 'YES', '1', 'ON', 'T', 'TRUE']) :
80            return 1
81        else :   
82            return 0
83                       
84    def getGlobalOption(self, option, ignore=0) :   
85        """Returns an option from the global section, or raises a ConfigError if ignore is not set, else returns None."""
86        try :
87            return self.config.get("global", option, raw=1)
88        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
89            if ignore :
90                return
91            else :
92                raise ConfigError, "Option %s not found in section global of %s" % (option, self.conffile)
93               
[574]94    def getSectionOption(self, printqueuename, option) :   
[570]95        """Returns an option from the printer section, or the global section, or raises a ConfigError."""
96        globaloption = self.getGlobalOption(option, ignore=1)
97        try :
[574]98            return self.config.get(printqueuename, option, raw=1)
[570]99        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
100            if globaloption is not None :
101                return globaloption
102            else :
[574]103                raise ConfigError, "Option %s not found in section [%s] of %s" % (option, printqueuename, self.conffile)
[571]104               
[574]105    def enumTeeBranches(self, printqueuename) :
[573]106        """Returns the list of branches for a particular section's Tee."""
107        try :
108            globalbranches = [ (k, v) for (k, v) in self.config.items("global") if k.startswith("tee_") ]
109        except ConfigParser.NoSectionError, msg :   
110            self.printInfo("Invalid configuration file : %s" % msg, "error")
111            globalbranches = []
112        try :
[574]113            sectionbranches = [ (k, v) for (k, v) in self.config.items(printqueuename) if k.startswith("tee_") ]
[573]114        except ConfigParser.NoSectionError, msg :   
115            self.printInfo("Invalid configuration file : %s" % msg, "error")
116            sectionbranches = []
117        branches = {}
118        for (k, v) in globalbranches :
119            value = v.strip()
120            if value :
121                branches[k] = value
122        for (k, v) in sectionbranches :   
123            value = v.strip()
124            if value :
125                branches[k] = value # overwrite any global option or set a new value
126            else :   
127                del branches[k] # empty value disables a global option
128        return branches
[568]129       
130    def discoverOtherBackends(self) :   
131        """Discovers the other CUPS backends.
132       
133           Executes each existing backend in turn in device enumeration mode.
134           Returns the list of available backends.
135        """
[569]136        # Unfortunately this method can't output any debug information
137        # to stdout or stderr, else CUPS considers that the device is
138        # not available.
[568]139        available = []
[569]140        (directory, myname) = os.path.split(sys.argv[0])
[568]141        tmpdir = tempfile.gettempdir()
[569]142        lockfilename = os.path.join(tmpdir, "%s..LCK" % myname)
[568]143        if os.path.exists(lockfilename) :
144            lockfile = open(lockfilename, "r")
145            pid = int(lockfile.read())
146            lockfile.close()
147            try :
148                # see if the pid contained in the lock file is still running
149                os.kill(pid, 0)
150            except OSError, e :   
151                if e.errno != errno.EPERM :
152                    # process doesn't exist anymore
153                    os.remove(lockfilename)
154           
155        if not os.path.exists(lockfilename) :
156            lockfile = open(lockfilename, "w")
157            lockfile.write("%i" % os.getpid())
158            lockfile.close()
[569]159            allbackends = [ os.path.join(directory, b) \
160                                for b in os.listdir(directory) 
161                                    if os.access(os.path.join(directory, b), os.X_OK) \
162                                        and (b != myname)] 
163            for backend in allbackends :                           
[568]164                answer = os.popen(backend, "r")
165                try :
166                    devices = [line.strip() for line in answer.readlines()]
167                except :   
168                    devices = []
169                status = answer.close()
170                if status is None :
171                    for d in devices :
172                        # each line is of the form :
173                        # 'xxxx xxxx "xxxx xxx" "xxxx xxx"'
174                        # so we have to decompose it carefully
175                        fdevice = cStringIO.StringIO(d)
176                        tokenizer = shlex.shlex(fdevice)
177                        tokenizer.wordchars = tokenizer.wordchars + \
178                                                        r".:,?!~/\_$*-+={}[]()#"
179                        arguments = []
180                        while 1 :
181                            token = tokenizer.get_token()
182                            if token :
183                                arguments.append(token)
184                            else :
185                                break
186                        fdevice.close()
187                        try :
188                            (devicetype, device, name, fullname) = arguments
189                        except ValueError :   
190                            pass    # ignore this 'bizarre' device
191                        else :   
192                            if name.startswith('"') and name.endswith('"') :
193                                name = name[1:-1]
194                            if fullname.startswith('"') and fullname.endswith('"') :
195                                fullname = fullname[1:-1]
196                            available.append('%s cupsoftee:%s "CupsOfTee+%s" "CupsOfTee managed %s"' \
197                                                 % (devicetype, device, name, fullname))
198            os.remove(lockfilename)
199        return available
200                       
[569]201    def fakePrint(self) :   
202        """Fakes to print the job."""
203        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
204            # Either the job's datas are on our stdin, or in a file
205            if len(sys.argv) == 7 :
206                self.InputFile = sys.argv[6]
207            else :   
208                self.InputFile = None
209               
210            # check that the DEVICE_URI environment variable's value is
211            # prefixed with "cupsoftee:" otherwise don't touch it.
212            # If this is the case, we have to remove the prefix from
213            # the environment before launching the real backend in cupspykota
214            device_uri = os.environ.get("DEVICE_URI", "")
215            if device_uri.startswith("cupspykota:") :
216                fulldevice_uri = device_uri[:]
217                device_uri = fulldevice_uri[len("cupspykota:"):]
218                if device_uri.startswith("//") :    # lpd (at least)
219                    device_uri = device_uri[2:]
220                os.environ["DEVICE_URI"] = device_uri   # TODO : side effect !
221            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
222            try :
223                (backend, destination) = device_uri.split(":", 1) 
224            except ValueError :   
225                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
226            while destination.startswith("/") :
227                destination = destination[1:]
228            checkauth = destination.split("@", 1)   
229            if len(checkauth) == 2 :
230                destination = checkauth[1]
231            printerhostname = destination.split("/")[0].split(":")[0]
232            return ("CUPS", \
233                    printerhostname, \
234                    os.environ.get("PRINTER"), \
235                    sys.argv[2].strip(), \
236                    sys.argv[1].strip(), \
237                    inputfile, \
238                    int(sys.argv[4].strip()), \
239                    sys.argv[3], \
240                    sys.argv[5], \
241                    backend)
[568]242
[565]243if __name__ == "__main__" :   
244    # This is a CUPS backend, we should act and die like a CUPS backend
[568]245    wrapper = CupsBackend()
[565]246    if len(sys.argv) == 1 :
[568]247        print "\n".join(wrapper.discoverOtherBackends())
248        sys.exit(0)               
[565]249    elif len(sys.argv) not in (6, 7) :   
[568]250        sys.stderr.write("ERROR: %s job-id user title copies options [file]\n"\
251                              % sys.argv[0])
252        sys.exit(1)
[565]253    else :   
254        sys.stderr.write("ERROR: Not Yet !\n")
[568]255        sys.exit(1)
Note: See TracBrowser for help on using the browser.