Show
Ignore:
Timestamp:
11/25/08 00:51:30 (16 years ago)
Author:
jerome
Message:

Backported the fix to #32. References #32.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • pykota/branches/1.26_fixes/bin/cupspykota

    r3189 r3459  
    11#! /usr/bin/env python 
    2 # -*- coding: ISO-8859-15 -*- 
     2# -*- coding: iso-8859-15 -*- 
    33 
    44# CUPSPyKota accounting backend 
     
    1616# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1717# GNU General Public License for more details. 
    18 #  
     18# 
    1919# You should have received a copy of the GNU General Public License 
    2020# along with this program; if not, write to the Free Software 
     
    5555try : 
    5656    from pkipplib import pkipplib 
    57 except ImportError :         
     57except ImportError : 
    5858    haspkipplib = False 
    59 else :     
     59else : 
    6060    haspkipplib = True 
    61      
    62 class FakeObject :         
     61 
     62class FakeObject : 
    6363    """Fake object.""" 
    6464    def __init__(self, name) : 
    6565        """Fake init.""" 
    6666        self.Name = name 
    67          
    68 class FakePrinter(FakeObject) :         
     67 
     68class FakePrinter(FakeObject) : 
    6969    """Fake printer instance.""" 
    7070    pass 
    71      
    72 class FakeUser(FakeObject) :     
     71 
     72class FakeUser(FakeObject) : 
    7373    """Fake user instance.""" 
    7474    def __init__(self, name) : 
     
    7676        self.Email = name 
    7777        FakeObject.__init__(self, name) 
    78      
     78 
    7979class CUPSBackend(PyKotaTool) : 
    8080    """Base class for tools with no database access.""" 
     
    9090        self.lockfilename = None 
    9191        self.lockfile = None 
    92          
    93     def deferredInit(self) :     
     92 
     93    def deferredInit(self) : 
    9494        """Deferred initialization.""" 
    9595        PyKotaTool.deferredInit(self) 
     
    101101        self.disableSigInt() 
    102102        self.installSigTermHandler() 
    103          
     103 
    104104    def sigtermHandler(self, signum, frame) : 
    105105        """Sets an attribute whenever SIGTERM is received.""" 
     
    107107        self.printInfo(_("SIGTERM received, job %s cancelled.") % self.JobId) 
    108108        os.environ["PYKOTASTATUS"] = "CANCELLED" 
    109          
    110     def deinstallSigTermHandler(self) :            
     109 
     110    def deinstallSigTermHandler(self) : 
    111111        """Deinstalls the SIGTERM handler.""" 
    112112        self.logdebug("Deinstalling SIGTERM handler...") 
    113113        signal.signal(signal.SIGTERM, signal.SIG_IGN) 
    114114        self.logdebug("SIGTERM handler deinstalled.") 
    115          
    116     def installSigTermHandler(self) :            
     115 
     116    def installSigTermHandler(self) : 
    117117        """Installs the SIGTERM handler.""" 
    118118        self.logdebug("Installing SIGTERM handler...") 
    119119        signal.signal(signal.SIGTERM, self.sigtermHandler) 
    120120        self.logdebug("SIGTERM handler installed.") 
    121          
    122     def disableSigInt(self) :     
     121 
     122    def disableSigInt(self) : 
    123123        """Disables the SIGINT signal (which raises KeyboardInterrupt).""" 
    124124        self.logdebug("Disabling SIGINT...") 
    125125        self.oldSigIntHandler = signal.signal(signal.SIGINT, signal.SIG_IGN) 
    126126        self.logdebug("SIGINT disabled.") 
    127          
    128     def enableSigInt(self) :     
     127 
     128    def enableSigInt(self) : 
    129129        """Enables the SIGINT signal (which raises KeyboardInterrupt).""" 
    130130        self.logdebug("Enabling SIGINT...") 
    131131        signal.signal(signal.SIGINT, self.oldSigIntHandler) 
    132132        self.logdebug("SIGINT enabled.") 
    133          
    134     def waitForLock(self) :     
     133 
     134    def waitForLock(self) : 
    135135        """Waits until we can acquire the lock file.""" 
    136136        self.logdebug("Waiting for lock %s to become available..." % self.lockfilename) 
     
    140140                # open the lock file, optionally creating it if needed. 
    141141                self.lockfile = open(self.lockfilename, "a+") 
    142                  
     142 
    143143                # we wait indefinitely for the lock to become available. 
    144144                # works over NFS too. 
    145145                fcntl.lockf(self.lockfile, fcntl.LOCK_EX) 
    146146                haslock = True 
    147                  
     147 
    148148                self.logdebug("Lock %s acquired." % self.lockfilename) 
    149                  
     149 
    150150                # Here we save the PID in the lock file, but we don't use 
    151151                # it, because the lock file may be in a directory shared 
     
    156156                self.lockfile.write(str(self.pid)) 
    157157                self.lockfile.flush() 
    158             except IOError, msg :             
     158            except IOError, msg : 
    159159                self.logdebug("I/O Error while waiting for lock %s : %s" % (self.lockfilename, msg)) 
    160160                time.sleep(0.25) 
    161                      
    162     def discoverOtherBackends(self) :     
     161 
     162    def discoverOtherBackends(self) : 
    163163        """Discovers the other CUPS backends. 
    164          
     164 
    165165           Executes each existing backend in turn in device enumeration mode. 
    166166           Returns the list of available backends. 
     
    186186                    # process doesn't exist anymore 
    187187                    os.remove(lockfilename) 
    188              
     188 
    189189        if not os.path.exists(lockfilename) : 
    190190            lockfile = open(lockfilename, "w") 
     
    194194                                for b in os.listdir(directory) \ 
    195195                                    if os.access(os.path.join(directory, b), os.X_OK) \ 
    196                                         and (b != myname)]  
    197             for backend in allbackends :                             
     196                                        and (b != myname)] 
     197            for backend in allbackends : 
    198198                answer = os.popen(backend, "r") 
    199199                try : 
    200200                    devices = [line.strip() for line in answer.readlines()] 
    201                 except :     
     201                except : 
    202202                    devices = [] 
    203203                status = answer.close() 
    204204                if status is None : 
    205205                    for d in devices : 
    206                         # each line is of the form :  
     206                        # each line is of the form : 
    207207                        # 'xxxx xxxx "xxxx xxx" "xxxx xxx"' 
    208208                        # so we have to decompose it carefully 
     
    221221                        try : 
    222222                            (devicetype, device, name, fullname) = arguments 
    223                         except ValueError :     
     223                        except ValueError : 
    224224                            pass    # ignore this 'bizarre' device 
    225                         else :     
     225                        else : 
    226226                            if name.startswith('"') and name.endswith('"') : 
    227227                                name = name[1:-1] 
     
    237237                             % (self.myname, self.MyName, self.MyName)) 
    238238        return available 
    239                          
    240     def initBackendParameters(self) :     
     239 
     240    def initBackendParameters(self) : 
    241241        """Initializes the backend's attributes.""" 
    242         # check that the DEVICE_URI environment variable's value is  
     242        # check that the DEVICE_URI environment variable's value is 
    243243        # prefixed with self.myname otherwise don't touch it. 
    244         # If this is the case, we have to remove the prefix from  
    245         # the environment before launching the real backend  
     244        # If this is the case, we have to remove the prefix from 
     245        # the environment before launching the real backend 
    246246        self.logdebug("Initializing backend...") 
    247          
     247        self.softwareJobSize = None 
    248248        self.PrinterName = os.environ.get("PRINTER", "") 
    249249        directories = [ self.config.getPrinterDirectory(self.PrinterName), 
     
    257257            else : 
    258258                self.printInfo("Insufficient permissions to access to temporary directory %s" % direc, "warn") 
    259                  
     259 
    260260        self.Action = "ALLOW"   # job allowed by default 
    261261        self.Reason = None 
     
    269269        if len(sys.argv) == 7 : 
    270270            self.InputFile = sys.argv[6] # read job's datas from file 
    271         else :     
     271        else : 
    272272            self.InputFile = None        # read job's datas from stdin 
    273273        self.DataFile = os.path.join(self.Directory, "%s-%s-%s-%s" % \ 
    274274                   (self.myname, self.PrinterName, self.UserName, self.JobId)) 
    275          
     275 
    276276        muststartwith = "%s:" % self.myname 
    277277        device_uri = os.environ.get("DEVICE_URI", "") 
     
    280280            device_uri = fulldevice_uri[len(muststartwith):] 
    281281            for i in range(2) : 
    282                 if device_uri.startswith("/") :   
     282                if device_uri.startswith("/") : 
    283283                    device_uri = device_uri[1:] 
    284284        try : 
    285             (backend, destination) = device_uri.split(":", 1)  
    286         except ValueError :     
     285            (backend, destination) = device_uri.split(":", 1) 
     286        except ValueError : 
    287287            if not device_uri : 
    288288                self.logdebug("Not attached to an existing print queue.") 
    289289                backend = "" 
    290290                printerhostname = "" 
    291             else :     
     291            else : 
    292292                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri 
    293         else :         
     293        else : 
    294294            if backend == "hp" : 
    295295                try : 
    296296                    printerhostname = destination.split("=")[1] # hp:/net/HP_LaserJet_8000_Series?ip=192.168.100.100 
    297                 except IndexError :     
     297                except IndexError : 
    298298                    self.logdebug("Unsupported hplip URI %s" % device_uri) 
    299                     printerhostname = ""  
    300             else :     
     299                    printerhostname = "" 
     300            else : 
    301301                while destination.startswith("/") : 
    302302                    destination = destination[1:] 
    303                 checkauth = destination.split("@", 1)     
     303                checkauth = destination.split("@", 1) 
    304304                if len(checkauth) == 2 : 
    305305                    destination = checkauth[1] 
    306306            printerhostname = destination.split("/")[0].split(":")[0] 
    307              
    308         self.PrinterHostName = printerhostname     
     307 
     308        self.PrinterHostName = printerhostname 
    309309        self.RealBackend = backend 
    310310        self.DeviceURI = device_uri 
    311          
     311 
    312312        connerror = False 
    313313        if haspkipplib : 
     
    319319                self.printInfo(_("Network error while querying the CUPS server : %s") \ 
    320320                                          % cupsserver.lastErrorMessage, "error") 
    321                 connerror = True                         
    322             else :     
     321                connerror = True 
     322            else : 
    323323                self.logdebug("CUPS server answered without error.") 
    324324                try : 
    325325                    john = answer.job["job-originating-host-name"] 
    326                 except KeyError :     
     326                except KeyError : 
    327327                    try : 
    328328                        john = answer.operation["job-originating-host-name"] 
    329                     except KeyError :     
     329                    except KeyError : 
    330330                        john = (None, None) 
    331                 try :         
     331                try : 
    332332                    jbing = answer.job["job-billing"] 
    333                 except KeyError :     
     333                except KeyError : 
    334334                    jbing = (None, None) 
    335                      
    336         if connerror or not haspkipplib :         
     335 
     336        if connerror or not haspkipplib : 
    337337            (ippfilename, ippmessage) = self.parseIPPRequestFile() 
    338338            self.ControlFile = ippfilename 
     
    341341                   (None, None))) 
    342342            jbing = ippmessage.job_attributes.get("job-billing", (None, None)) 
    343                  
    344         if type(john) == type([]) :  
     343 
     344        if type(john) == type([]) : 
    345345            john = john[-1] 
    346         (chtype, self.ClientHost) = john  
    347         if type(jbing) == type([]) :  
     346        (chtype, self.ClientHost) = john 
     347        if type(jbing) == type([]) : 
    348348            jbing = jbing[-1] 
    349349        (jbtype, self.JobBillingCode) = jbing 
    350350        if self.JobBillingCode is None : 
    351351            self.OriginalJobBillingCode = None 
    352         else :     
     352        else : 
    353353            self.JobBillingCode = self.UTF8ToUserCharset(self.JobBillingCode) 
    354354            self.OriginalJobBillingCode = self.JobBillingCode[:] 
    355              
     355 
    356356        baselockfilename = self.DeviceURI.replace("/", ".") 
    357357        baselockfilename = baselockfilename.replace(":", ".") 
     
    360360        baselockfilename = baselockfilename.replace("@", ".") 
    361361        self.lockfilename = os.path.join(self.Directory, "%s-%s..LCK" % (self.myname, baselockfilename)) 
    362          
     362 
    363363        self.logdebug("Backend : %s" % self.RealBackend) 
    364364        self.logdebug("DeviceURI : %s" % self.DeviceURI) 
     
    370370        self.logdebug("Copies : %s" % self.Copies) 
    371371        self.logdebug("Options : %s" % self.Options) 
    372         self.logdebug("Directory : %s" % self.Directory)  
     372        self.logdebug("Directory : %s" % self.Directory) 
    373373        self.logdebug("DataFile : %s" % self.DataFile) 
    374374        self.logdebug("ControlFile : %s" % self.ControlFile) 
    375375        self.logdebug("JobBillingCode : %s" % self.JobBillingCode) 
    376376        self.logdebug("JobOriginatingHostName : %s" % self.ClientHost) 
    377          
     377 
    378378        # fakes some entries to allow for external mailto 
    379379        # before real entries are extracted from the database. 
    380380        self.User = FakeUser(self.UserName) 
    381381        self.Printer = FakePrinter(self.PrinterName) 
    382          
     382 
    383383        self.enableSigInt() 
    384384        self.logdebug("Backend initialized.") 
    385          
     385 
    386386    def overwriteJobAttributes(self) : 
    387387        """Overwrites some of the job's attributes if needed.""" 
     
    389389        # First overwrite the job ticket 
    390390        self.overwriteJobTicket() 
    391          
     391 
    392392        # do we want to strip out the Samba/Winbind domain name ? 
    393393        separator = self.config.getWinbindSeparator() 
    394394        if separator is not None : 
    395395            self.UserName = self.UserName.split(separator)[-1] 
    396              
    397         # this option is deprecated, and we want to tell people     
     396 
     397        # this option is deprecated, and we want to tell people 
    398398        # this is the case. 
    399399        tolower = self.config.getUserNameToLower() 
     
    404404            if self.config.isTrue(tolower) : 
    405405                self.UserName = self.UserName.lower() 
    406                  
    407         # Now use the newer and more complete 'usernamecase' directive.     
    408         casechange = self.config.getUserNameCase()     
     406 
     407        # Now use the newer and more complete 'usernamecase' directive. 
     408        casechange = self.config.getUserNameCase() 
    409409        if casechange != "native" : 
    410410            self.UserName = getattr(self.UserName, casechange)() 
    411              
    412         # do we want to strip some prefix off of titles ?     
     411 
     412        # do we want to strip some prefix off of titles ? 
    413413        stripprefix = self.config.getStripTitle(self.PrinterName) 
    414414        if stripprefix : 
     
    417417                                      % (stripprefix, self.Title)) 
    418418                self.Title = self.Title[len(stripprefix):] 
    419                  
     419 
    420420        self.logdebug("Username : %s" % self.UserName) 
    421421        self.logdebug("BillingCode : %s" % self.JobBillingCode) 
    422422        self.logdebug("Title : %s" % self.Title) 
    423423        self.logdebug("Job's attributes sanitizing done.") 
    424                  
     424 
    425425    def didUserConfirm(self) : 
    426426        """Asks for user confirmation through an external script. 
    427          
     427 
    428428           returns False if the end user wants to cancel the job, else True. 
    429429        """ 
    430430        self.logdebug("Checking if we have to ask for user's confirmation...") 
    431         answer = None                          
     431        answer = None 
    432432        confirmationcommand = self.config.getAskConfirmation(self.PrinterName) 
    433433        if confirmationcommand : 
     
    440440                    if answer == "CANCEL" : 
    441441                        break 
    442             except IOError, msg :             
     442            except IOError, msg : 
    443443                self.logdebug("IOError while reading subprocess' output : %s" % msg) 
    444             inputfile.close()     
     444            inputfile.close() 
    445445            self.logdebug("User's confirmation received : %s" % (((answer == "CANCEL") and "CANCEL") or "CONTINUE")) 
    446         else :     
     446        else : 
    447447            self.logdebug("No need to ask for user's confirmation, job processing will continue.") 
    448         return (answer != "CANCEL")     
    449          
    450     def overwriteJobTicket(self) :     
     448        return (answer != "CANCEL") 
     449 
     450    def overwriteJobTicket(self) : 
    451451        """Should we overwrite the job's ticket (username and billingcode) ?""" 
    452452        self.logdebug("Checking if we need to overwrite the job ticket...") 
     
    456456            self.logdebug("Launching subprocess [%s] to overwrite the job ticket." \ 
    457457                                     % jobticketcommand) 
    458             self.regainPriv()                          
     458            self.regainPriv() 
    459459            inputfile = os.popen(jobticketcommand, "r") 
    460460            try : 
     
    467467                        self.logdebug("Seen CANCEL command.") 
    468468                        action = "CANCEL" 
    469                     elif line.startswith("USERNAME=") :     
     469                    elif line.startswith("USERNAME=") : 
    470470                        username = self.userCharsetToUTF8(line.split("=", 1)[1].strip()) 
    471471                        self.logdebug("Seen new username [%s]" % username) 
    472                     elif line.startswith("BILLINGCODE=") :     
     472                    elif line.startswith("BILLINGCODE=") : 
    473473                        billingcode = self.userCharsetToUTF8(line.split("=", 1)[1].strip()) 
    474474                        self.logdebug("Seen new billing code [%s]" % billingcode) 
     
    476476                        reason = self.userCharsetToUTF8(line.split("=", 1)[1].strip()) 
    477477                        self.logdebug("Seen new reason [%s]" % reason) 
    478             except IOError, msg :             
     478            except IOError, msg : 
    479479                self.logdebug("IOError while reading subprocess' output : %s" % msg) 
    480             inputfile.close()     
     480            inputfile.close() 
    481481            self.dropPriv() 
    482              
     482 
    483483            # now overwrite the job's ticket if new data was supplied 
    484484            if action == "DENY" : 
     
    495495                    self.UserName = username 
    496496                if billingcode is not None : 
    497                     self.JobBillingCode = billingcode  
     497                    self.JobBillingCode = billingcode 
    498498        self.logdebug("Job ticket overwriting done.") 
    499              
     499 
    500500    def saveDatasAndCheckSum(self) : 
    501501        """Saves the input datas into a static file.""" 
    502502        self.logdebug("Duplicating data stream into %s" % self.DataFile) 
    503503        mustclose = 0 
    504         outfile = open(self.DataFile, "wb")     
     504        outfile = open(self.DataFile, "wb") 
    505505        if self.InputFile is not None : 
    506506            self.regainPriv() 
     
    508508            self.logdebug("Reading input datas from %s" % self.InputFile) 
    509509            mustclose = 1 
    510         else :     
     510        else : 
    511511            infile = sys.stdin 
    512512            self.logdebug("Reading input datas from stdin") 
     
    516516        checksum = md5.new() 
    517517        while 1 : 
    518             data = infile.read(CHUNK)  
     518            data = infile.read(CHUNK) 
    519519            if not data : 
    520520                break 
    521             sizeread += len(data)     
     521            sizeread += len(data) 
    522522            outfile.write(data) 
    523             checksum.update(data)     
     523            checksum.update(data) 
    524524            if not (dummy % 32) : # Only display every 2 Mb 
    525525                self.logdebug("%s bytes saved..." % sizeread) 
    526             dummy += 1     
    527         if mustclose :     
     526            dummy += 1 
     527        if mustclose : 
    528528            infile.close() 
    529529            self.dropPriv() 
    530              
     530 
    531531        outfile.close() 
    532         self.JobSizeBytes = sizeread     
     532        self.JobSizeBytes = sizeread 
    533533        self.JobMD5Sum = checksum.hexdigest() 
    534          
     534 
    535535        self.logdebug("JobSizeBytes : %s" % self.JobSizeBytes) 
    536536        self.logdebug("JobMD5Sum : %s" % self.JobMD5Sum) 
    537537        self.logdebug("Data stream duplicated into %s" % self.DataFile) 
    538              
     538 
    539539    def clean(self) : 
    540540        """Cleans up the place.""" 
     
    545545            try : 
    546546                keep = self.config.getPrinterKeepFiles(self.PrinterName) 
    547             except AttributeError :     
     547            except AttributeError : 
    548548                keep = False 
    549549            if not keep : 
     
    555555                else : 
    556556                    self.logdebug("Work file %s has been deleted." % self.DataFile) 
    557             else :     
     557            else : 
    558558                self.logdebug("Work file %s will be kept." % self.DataFile) 
    559         PyKotaTool.clean(self)     
     559        PyKotaTool.clean(self) 
    560560        if self.lockfile is not None : 
    561561            self.logdebug("Unlocking %s..." %  self.lockfilename) 
     
    563563                fcntl.lockf(self.lockfile, fcntl.LOCK_UN) 
    564564                self.lockfile.close() 
    565             except :     
     565            except : 
    566566                self.printInfo("Problem while unlocking %s" % self.lockfilename, "error") 
    567             else :     
     567            else : 
    568568                self.logdebug("%s unlocked." % self.lockfilename) 
    569569        self.logdebug("Clean.") 
    570              
    571     def precomputeJobSize(self) :     
     570 
     571    def precomputeJobSize(self) : 
    572572        """Computes the job size with a software method.""" 
    573573        self.logdebug("Precomputing job's size...") 
     
    576576        self.softwareJobSize = self.preaccounter.getJobSize(None) 
    577577        self.logdebug("Precomputed job's size is %s pages." % self.softwareJobSize) 
    578          
    579     def precomputeJobPrice(self) :     
     578 
     579    def precomputeJobPrice(self) : 
    580580        """Precomputes the job price with a software method.""" 
    581581        self.logdebug("Precomputing job's price...") 
     
    583583        self.logdebug("Precomputed job's price is %.3f credits." \ 
    584584                                   % self.softwareJobPrice) 
    585          
     585 
    586586    def getCupsConfigDirectives(self, directives=[]) : 
    587587        """Retrieves some CUPS directives from its configuration file. 
    588          
    589            Returns a mapping with lowercased directives as keys and  
     588 
     589           Returns a mapping with lowercased directives as keys and 
    590590           their setting as values. 
    591591        """ 
    592592        self.logdebug("Parsing CUPS' configuration file...") 
    593         dirvalues = {}  
     593        dirvalues = {} 
    594594        cupsroot = os.environ.get("CUPS_SERVERROOT", "/etc/cups") 
    595595        cupsdconf = os.path.join(cupsroot, "cupsd.conf") 
    596596        try : 
    597597            conffile = open(cupsdconf, "r") 
    598         except IOError :     
     598        except IOError : 
    599599            raise PyKotaToolError, "Unable to open %s" % cupsdconf 
    600         else :     
     600        else : 
    601601            for line in conffile.readlines() : 
    602602                linecopy = line.strip().lower() 
     
    605605                        try : 
    606606                            val = line.split()[1] 
    607                         except :     
     607                        except : 
    608608                            pass # ignore errors, we take the last value in any case. 
    609                         else :     
     609                        else : 
    610610                            dirvalues[di] = val 
    611             conffile.close()             
     611            conffile.close() 
    612612        self.logdebug("CUPS' configuration file parsed successfully.") 
    613         return dirvalues        
    614              
    615     def parseIPPRequestFile(self) :         
     613        return dirvalues 
     614 
     615    def parseIPPRequestFile(self) : 
    616616        """Parses the IPP message file and returns a tuple (filename, parsedvalue).""" 
    617617        self.logdebug("Parsing IPP request file...") 
    618          
     618 
    619619        class DummyClass : 
    620620            """Class used to avoid errors.""" 
    621621            operation_attributes = {} 
    622622            job_attributes = {} 
    623              
     623 
    624624        ippmessage = DummyClass() # in case the code below fails 
    625          
     625 
    626626        self.regainPriv() 
    627627        cupsdconf = self.getCupsConfigDirectives(["RequestRoot"]) 
     
    629629        if (len(self.JobId) < 5) and self.JobId.isdigit() : 
    630630            ippmessagefile = "c%05i" % int(self.JobId) 
    631         else :     
     631        else : 
    632632            ippmessagefile = "c%s" % self.JobId 
    633633        ippmessagefile = os.path.join(requestroot, ippmessagefile) 
    634634        try : 
    635635            ippdatafile = open(ippmessagefile) 
    636         except :     
     636        except : 
    637637            self.logdebug("Unable to open IPP request file %s" % ippmessagefile) 
    638         else :     
     638        else : 
    639639            self.logdebug("Parsing of IPP request file %s begins." % ippmessagefile) 
    640640            try : 
    641641                ippmessage = oldIPPRequest(ippdatafile.read()) 
    642642                ippmessage.parse() 
    643             except oldIPPError, msg :     
     643            except oldIPPError, msg : 
    644644                self.printInfo("Error while parsing %s : %s" \ 
    645645                                      % (ippmessagefile, msg), "warn") 
    646             else :     
     646            else : 
    647647                self.logdebug("Parsing of IPP request file %s ends." \ 
    648648                                       % ippmessagefile) 
     
    651651        self.logdebug("IPP request file parsed successfully.") 
    652652        return (ippmessagefile, ippmessage) 
    653                  
    654     def exportJobInfo(self) :     
     653 
     654    def exportJobInfo(self) : 
    655655        """Exports the actual job's attributes to the environment.""" 
    656656        self.logdebug("Exporting job information to the environment...") 
     
    675675        os.environ["PYKOTAPRECOMPUTEDJOBSIZE"] = str(self.softwareJobSize) 
    676676        self.logdebug("Environment updated.") 
    677          
     677 
    678678    def exportUserInfo(self) : 
    679679        """Exports user information to the environment.""" 
     
    684684        os.environ["PYKOTALIFETIMEPAID"] = str(self.User.LifeTimePaid or 0.0) 
    685685        os.environ["PYKOTAUSERDESCRIPTION"] = str(self.User.Description or "") 
    686          
     686 
    687687        os.environ["PYKOTAPAGECOUNTER"] = str(self.UserPQuota.PageCounter or 0) 
    688688        os.environ["PYKOTALIFEPAGECOUNTER"] = str(self.UserPQuota.LifePageCounter or 0) 
     
    691691        os.environ["PYKOTADATELIMIT"] = str(self.UserPQuota.DateLimit) 
    692692        os.environ["PYKOTAWARNCOUNT"] = str(self.UserPQuota.WarnCount) 
    693          
     693 
    694694        # TODO : move this elsewhere once software accounting is done only once. 
    695695        os.environ["PYKOTAPRECOMPUTEDJOBPRICE"] = str(self.softwareJobPrice) 
    696          
     696 
    697697        self.logdebug("Environment updated.") 
    698          
     698 
    699699    def exportPrinterInfo(self) : 
    700700        """Exports printer information to the environment.""" 
     
    709709        os.environ["PYKOTAPRICEPERJOB"] = str(self.Printer.PricePerJob or 0) 
    710710        self.logdebug("Environment updated.") 
    711          
     711 
    712712    def exportPhaseInfo(self, phase) : 
    713713        """Exports phase information to the environment.""" 
     
    715715        os.environ["PYKOTAPHASE"] = phase 
    716716        self.logdebug("Environment updated.") 
    717          
     717 
    718718    def exportJobSizeAndPrice(self) : 
    719719        """Exports job's size and price information to the environment.""" 
     
    722722        os.environ["PYKOTAJOBPRICE"] = str(self.JobPrice) 
    723723        self.logdebug("Environment updated.") 
    724          
     724 
    725725    def exportReason(self) : 
    726726        """Exports the job's action status and optional reason.""" 
     
    730730            os.environ["PYKOTAREASON"] = str(self.Reason) 
    731731        self.logdebug("Environment updated.") 
    732          
    733     def acceptJob(self) :         
     732 
     733    def acceptJob(self) : 
    734734        """Returns the appropriate exit code to tell CUPS all is OK.""" 
    735735        return 0 
    736              
    737     def removeJob(self) :             
     736 
     737    def removeJob(self) : 
    738738        """Returns the appropriate exit code to let CUPS think all is OK. 
    739          
     739 
    740740           Returning 0 (success) prevents CUPS from stopping the print queue. 
    741         """    
     741        """ 
    742742        return 0 
    743          
     743 
    744744    def launchPreHook(self) : 
    745745        """Allows plugging of an external hook before the job gets printed.""" 
     
    749749            retcode = os.system(prehook) 
    750750            self.logdebug("pre-hook exited with status %s." % retcode) 
    751          
     751 
    752752    def launchPostHook(self) : 
    753753        """Allows plugging of an external hook after the job gets printed and/or denied.""" 
     
    757757            retcode = os.system(posthook) 
    758758            self.logdebug("post-hook exited with status %s." % retcode) 
    759              
    760     def improveMessage(self, message) :         
     759 
     760    def improveMessage(self, message) : 
    761761        """Improves a message by adding more informations in it if possible.""" 
    762762        try : 
     
    765765                                        self.JobId, \ 
    766766                                        message) 
    767         except :                                                
     767        except : 
    768768            return message 
    769          
    770     def logdebug(self, message) :         
     769 
     770    def logdebug(self, message) : 
    771771        """Improves the debug message before outputting it.""" 
    772772        PyKotaTool.logdebug(self, self.improveMessage(message)) 
    773          
    774     def printInfo(self, message, level="info") :         
     773 
     774    def printInfo(self, message, level="info") : 
    775775        """Improves the informational message before outputting it.""" 
    776776        self.logger.log_message(self.improveMessage(message), level) 
    777      
     777 
    778778    def startingBanner(self, withaccounting) : 
    779779        """Retrieves a starting banner for current printer and returns its content.""" 
     
    781781        self.printBanner(self.config.getStartingBanner(self.PrinterName), withaccounting) 
    782782        self.logdebug("Starting banner retrieved.") 
    783      
     783 
    784784    def endingBanner(self, withaccounting) : 
    785785        """Retrieves an ending banner for current printer and returns its content.""" 
     
    787787        self.printBanner(self.config.getEndingBanner(self.PrinterName), withaccounting) 
    788788        self.logdebug("Ending banner retrieved.") 
    789          
     789 
    790790    def printBanner(self, bannerfileorcommand, withaccounting) : 
    791791        """Reads a banner or generates one through an external command. 
    792          
     792 
    793793           Returns the banner's content in a format which MUST be accepted 
    794794           by the printer. 
     
    816816                try : 
    817817                    fh = open(bannerfileorcommand, 'rb') 
    818                 except IOError, msg :     
     818                except IOError, msg : 
    819819                    self.printInfo("Impossible to open %s : %s" \ 
    820820                                       % (bannerfileorcommand, msg), "error") 
    821                 else :     
     821                else : 
    822822                    self.runOriginalBackend(fh, isBanner=1) 
    823823                    fh.close() 
     
    826826                            self.BannerSize += 1 # TODO : fix this by passing the banner's content through software accounting 
    827827        self.logdebug("Banner printed...") 
    828                  
     828 
    829829    def handleBanner(self, bannertype, withaccounting) : 
    830830        """Handles the banner with or without accounting.""" 
    831831        if withaccounting : 
    832832            acc = "with" 
    833         else :     
     833        else : 
    834834            acc = "without" 
    835835        self.logdebug("Handling %s banner %s accounting..." % (bannertype, acc)) 
     
    860860                        if (avoidduplicatebanners == "YES") : 
    861861                            printbanner = False 
    862                         else :     
    863                             # avoidduplicatebanners is an integer, since NO,  
     862                        else : 
     863                            # avoidduplicatebanners is an integer, since NO, 
    864864                            # YES and 0 are already handled 
    865865                            now = DateTime.now() 
     
    871871                            self.logdebug("Difference with previous job : %.2f seconds. Try to avoid banners for : %.2f seconds." % (difference, avoidduplicatebanners)) 
    872872                            if difference < avoidduplicatebanners : 
    873                                 self.logdebug("Duplicate banner avoided because previous banner is less than %.2f seconds old." % avoidduplicatebanners)  
     873                                self.logdebug("Duplicate banner avoided because previous banner is less than %.2f seconds old." % avoidduplicatebanners) 
    874874                                printbanner = False 
    875875                            else : 
     
    878878                    getattr(self, "%sBanner" % bannertype)(withaccounting) 
    879879        self.logdebug("%s banner done." % bannertype.title()) 
    880          
    881     def sanitizeJobSize(self) :     
     880 
     881    def sanitizeJobSize(self) : 
    882882        """Sanitizes the job's size if needed.""" 
    883883        # TODO : there's a difficult to see bug here when banner accounting is activated and hardware accounting is used. 
     
    897897                    if replacement == "PRECOMPUTED" : 
    898898                        self.JobSize = self.softwareJobSize 
    899                     else :     
     899                    else : 
    900900                        self.JobSize = replacement 
    901901        self.logdebug("Job's size sanitized.") 
    902                          
    903     def getPrinterUserAndUserPQuota(self) :         
     902 
     903    def getPrinterUserAndUserPQuota(self) : 
    904904        """Returns a tuple (policy, printer, user, and user print quota) on this printer. 
    905          
     905 
    906906           "OK" is returned in the policy if both printer, user and user print quota 
    907907           exist in the Quota Storage. 
    908908           Otherwise, the policy as defined for this printer in pykota.conf is returned. 
    909             
     909 
    910910           If policy was set to "EXTERNAL" and one of printer, user, or user print quota 
    911911           doesn't exist in the Quota Storage, then an external command is launched, as 
     
    914914           or users, for example, and finally extracting printer, user and user print 
    915915           quota from the Quota Storage is tried a second time. 
    916             
     916 
    917917           "EXTERNALERROR" is returned in case policy was "EXTERNAL" and an error status 
    918918           was returned by the external command. 
     
    927927                break 
    928928            (policy, args) = self.config.getPrinterPolicy(self.PrinterName) 
    929             if policy == "EXTERNAL" :     
     929            if policy == "EXTERNAL" : 
    930930                commandline = self.formatCommandLine(args, user, printer) 
    931931                if not printer.Exists : 
     
    939939                    policy = "EXTERNALERROR" 
    940940                    break 
    941             else :         
     941            else : 
    942942                if not printer.Exists : 
    943943                    self.printInfo(_("Printer %s not registered in the PyKota system, applying default policy (%s)") % (self.PrinterName, policy)) 
     
    947947                    self.printInfo(_("User %s doesn't have quota on printer %s in the PyKota system, applying default policy (%s)") % (self.UserName, self.PrinterName, policy)) 
    948948                break 
    949                  
    950         if policy == "EXTERNAL" :     
     949 
     950        if policy == "EXTERNAL" : 
    951951            if not printer.Exists : 
    952952                self.printInfo(_("Printer %s still not registered in the PyKota system, job will be rejected") % self.PrinterName) 
     
    955955            if not userpquota.Exists : 
    956956                self.printInfo(_("User %s still doesn't have quota on printer %s in the PyKota system, job will be rejected") % (self.UserName, self.PrinterName)) 
    957         self.Policy = policy          
     957        self.Policy = policy 
    958958        self.Printer = printer 
    959959        self.User = user 
    960960        self.UserPQuota = userpquota 
    961961        self.logdebug("Retrieval of printer, user and user print quota entry done.") 
    962          
    963     def getBillingCode(self) :     
     962 
     963    def getBillingCode(self) : 
    964964        """Extracts the billing code from the database. 
    965           
     965 
    966966           An optional script is launched to notify the user when 
    967967           the billing code is unknown and PyKota was configured to 
     
    983983                    if self.BillingCode.Exists : 
    984984                        self.logdebug(msg + "has been created.") 
    985                     else :     
     985                    else : 
    986986                        self.printInfo(msg + "couldn't be created.", "error") 
    987                 else :     
     987                else : 
    988988                    self.logdebug(msg + "job will be denied.") 
    989989                    self.Action = newaction 
    990                     if script is not None :  
     990                    if script is not None : 
    991991                        self.logdebug(msg + "launching subprocess [%s] to notify user." % script) 
    992992                        os.system(script) 
    993993        self.logdebug("Retrieval of billing code information done.") 
    994          
    995     def checkIfDupe(self) :     
     994 
     995    def checkIfDupe(self) : 
    996996        """Checks if the job is a duplicate, and handles the situation.""" 
    997997        self.logdebug("Checking if the job is a duplicate...") 
     
    10141014                    self.logdebug("Duplicate job allowed because previous one is more than %.2f seconds old." % duplicatesdelay) 
    10151015                else : 
    1016                     # TODO : use the current user's last job instead of   
     1016                    # TODO : use the current user's last job instead of 
    10171017                    # TODO : the current printer's last job. This would be 
    10181018                    # TODO : better but requires an additional database query 
    1019                     # TODO : with SQL, and is much more complex with the  
     1019                    # TODO : with SQL, and is much more complex with the 
    10201020                    # TODO : actual LDAP schema. Maybe this is not very 
    10211021                    # TODO : important, because usually duplicate jobs are sucessive. 
     
    10251025                        self.Action = "DENY" 
    10261026                        self.Reason = _("Duplicate print jobs are not allowed on printer %s.") % self.PrinterName 
    1027                     else :     
     1027                    else : 
    10281028                        self.logdebug("Launching subprocess [%s] to see if duplicate jobs should be allowed or not." % denyduplicates) 
    10291029                        fanswer = os.popen(denyduplicates, "r") 
    10301030                        self.Action = fanswer.read().strip().upper() 
    10311031                        fanswer.close() 
    1032                         if self.Action == "DENY" :      
     1032                        if self.Action == "DENY" : 
    10331033                            self.printInfo("%s : %s." % (msg, _("Subprocess denied printing of a dupe")), "warn") 
    10341034                            self.Reason = _("Duplicate print jobs are not allowed on printer %s at this time.") % self.PrinterName 
    1035                         else :     
     1035                        else : 
    10361036                            self.printInfo("%s : %s." % (msg, _("Subprocess allowed printing of a dupe")), "warn") 
    1037             else :             
     1037            else : 
    10381038                self.logdebug("Job doesn't seem to be a duplicate.") 
    10391039        self.logdebug("Checking if the job is a duplicate done.") 
    1040          
     1040 
    10411041    def tellUser(self) : 
    10421042        """Sends a message to an user.""" 
    1043         self.logdebug("Sending some feedback to user %s..." % self.UserName)   
     1043        self.logdebug("Sending some feedback to user %s..." % self.UserName) 
    10441044        if not self.Reason : 
    10451045            self.logdebug("No feedback to send to user %s." % self.UserName) 
    1046         else :     
     1046        else : 
    10471047            (mailto, arguments) = self.config.getMailTo(self.PrinterName) 
    10481048            if mailto == "EXTERNAL" : 
     
    10511051                self.externalMailTo(arguments, self.Action, self.User, self.Printer, self.Reason) 
    10521052                self.dropPriv() 
    1053             else :     
     1053            else : 
    10541054                # TODO : clean this again 
    10551055                admin = self.config.getAdmin(self.PrinterName) 
     
    10611061                if mailto in ("BOTH", "ADMIN") : 
    10621062                    destination.append(adminmail) 
    1063                 if mailto in ("BOTH", "USER") :     
     1063                if mailto in ("BOTH", "USER") : 
    10641064                    destination.append(usermail) 
    1065                      
     1065 
    10661066                fullmessage = self.Reason + (_("\n\nYour system administrator :\n\n\t%s - <%s>\n") % (admin, adminmail)) 
    1067                 try :     
     1067                try : 
    10681068                    server = smtplib.SMTP(self.smtpserver) 
    1069                 except socket.error, msg :     
     1069                except socket.error, msg : 
    10701070                    self.printInfo(_("Impossible to connect to SMTP server : %s") % msg, "error") 
    10711071                else : 
     
    10781078                            if mailto == "BOTH" : 
    10791079                                msg["Cc"] = adminmail 
    1080                         else :     
     1080                        else : 
    10811081                            msg["To"] = adminmail 
    10821082                        msg["Date"] = email.Utils.formatdate(localtime=True) 
    10831083                        server.sendmail(adminmail, destination, msg.as_string()) 
    1084                     except smtplib.SMTPException, answer :     
     1084                    except smtplib.SMTPException, answer : 
    10851085                        try : 
    10861086                            for (k, v) in answer.recipients.items() : 
     
    10901090                    server.quit() 
    10911091            self.logdebug("Feedback sent to user %s." % self.UserName) 
    1092                  
    1093     def mainWork(self) :     
     1092 
     1093    def mainWork(self) : 
    10941094        """Main work is done here.""" 
    10951095        if not self.JobSizeBytes : 
     
    11011101            self.tellUser() 
    11021102            return self.removeJob() 
    1103              
     1103 
    11041104        self.getPrinterUserAndUserPQuota() 
    11051105        if self.Policy == "EXTERNALERROR" : 
     
    11161116            self.tellUser() 
    11171117            return self.removeJob() 
    1118         elif self.Policy == "DENY" :     
     1118        elif self.Policy == "DENY" : 
    11191119            # Either printer, user or user print quota doesn't exist, 
    11201120            # and the job should be rejected. 
     
    11341134            #            be allowed if current user is allowed to print on this printer 
    11351135            return self.doWork() 
    1136         else :     
     1136        else : 
    11371137            self.Reason = _("Invalid policy %s for printer %s") % (self.Policy, self.PrinterName) 
    11381138            self.printInfo(self.Reason, "error") 
    11391139            self.tellUser() 
    11401140            return self.removeJob() 
    1141      
    1142     def doWork(self) :     
     1141 
     1142    def doWork(self) : 
    11431143        """The accounting work is done here.""" 
    11441144        self.precomputeJobPrice() 
     
    11461146        self.exportPrinterInfo() 
    11471147        self.exportPhaseInfo("BEFORE") 
    1148          
    1149         if self.Action not in ("DENY", "CANCEL") :  
     1148 
     1149        if self.Action not in ("DENY", "CANCEL") : 
    11501150            if self.Printer.MaxJobSize and (self.softwareJobSize > self.Printer.MaxJobSize) : 
    11511151                # This printer was set to refuse jobs this large. 
     
    11551155                # because in case of error the user could complain :-) 
    11561156                self.Reason = _("You are not allowed to print so many pages on printer %s at this time.") % self.PrinterName 
    1157              
     1157 
    11581158        if self.Action not in ("DENY", "CANCEL") : 
    11591159            if self.User.LimitBy == "noprint" : 
     
    11611161                self.Action = "DENY" 
    11621162                self.Reason = _("Your account settings forbid you to print at this time.") 
    1163                  
     1163 
    11641164        if self.Action not in ("DENY", "CANCEL") : 
    11651165            # If printing is still allowed at this time, we 
     
    11681168            # save some database queries. 
    11691169            self.getBillingCode() 
    1170              
     1170 
    11711171        if self.Action not in ("DENY", "CANCEL") : 
    11721172            # If printing is still allowed at this time, we 
     
    11751175            # save some database queries. 
    11761176            self.checkIfDupe() 
    1177                      
     1177 
    11781178        if self.Action not in ("DENY", "CANCEL") : 
    11791179            # If printing is still allowed at this time, we 
     
    11831183            if self.User.LimitBy in ('noquota', 'nochange') : 
    11841184                self.logdebug("User %s is allowed to print with no limit, no need to check quota." % self.UserName) 
    1185             elif self.Printer.PassThrough :     
     1185            elif self.Printer.PassThrough : 
    11861186                self.logdebug("Printer %s is in PassThrough mode, no need to check quota." % self.PrinterName) 
    11871187            else : 
     
    11941194                    self.printInfo(_("Print Quota exceeded for user %s on printer %s") % (self.UserName, self.PrinterName)) 
    11951195                    self.Reason = self.config.getHardWarn(self.PrinterName) 
    1196                 elif self.Action == "WARN" :     
     1196                elif self.Action == "WARN" : 
    11971197                    self.printInfo(_("Print Quota low for user %s on printer %s") % (self.UserName, self.PrinterName)) 
    1198                     if self.User.LimitBy and (self.User.LimitBy.lower() == "balance") :  
     1198                    if self.User.LimitBy and (self.User.LimitBy.lower() == "balance") : 
    11991199                        self.Reason = self.config.getPoorWarn() 
    1200                     else :      
     1200                    else : 
    12011201                        self.Reason = self.config.getSoftWarn(self.PrinterName) 
    1202              
    1203         # If job still allowed to print, should we ask for confirmation ?     
    1204         if self.Action not in ("DENY", "CANCEL") :  
     1202 
     1203        # If job still allowed to print, should we ask for confirmation ? 
     1204        if self.Action not in ("DENY", "CANCEL") : 
    12051205            if not self.didUserConfirm() : 
    12061206                self.Action = "CANCEL" 
    12071207                self.Reason = _("Print job cancelled.") 
    12081208                os.environ["PYKOTASTATUS"] = "CANCELLED" 
    1209                  
     1209 
    12101210        # exports some new environment variables 
    12111211        self.exportReason() 
    1212          
     1212 
    12131213        # now tell the user if he needs to know something 
    12141214        self.tellUser() 
    1215          
     1215 
    12161216        # launches the pre hook 
    12171217        self.launchPreHook() 
    1218          
     1218 
    12191219        # handle starting banner pages without accounting 
    12201220        self.BannerSize = 0 
     
    12221222        if (self.Action != "CANCEL") and accountbanner in ["ENDING", "NONE"] : 
    12231223            self.handleBanner("starting", 0) 
    1224          
     1224 
    12251225        if self.Action == "DENY" : 
    12261226            self.printInfo(_("Job denied, no accounting will be done.")) 
    1227         elif self.Action == "CANCEL" :     
     1227        elif self.Action == "CANCEL" : 
    12281228            self.printInfo(_("Job cancelled, no accounting will be done.")) 
    12291229        else : 
     
    12321232            self.accounter.beginJob(self.Printer) 
    12331233            self.installSigTermHandler() 
    1234          
     1234 
    12351235        # handle starting banner pages with accounting 
    12361236        if (self.Action != "CANCEL") and accountbanner in ["STARTING", "BOTH"] : 
    12371237            if not self.gotSigTerm : 
    12381238                self.handleBanner("starting", 1) 
    1239          
    1240         # pass the job's data to the real backend     
     1239 
     1240        # pass the job's data to the real backend 
    12411241        if (not self.gotSigTerm) and (self.Action in ["ALLOW", "WARN"]) : 
    12421242            retcode = self.printJobDatas() 
    1243         else :         
     1243        else : 
    12441244            retcode = self.removeJob() 
    1245          
     1245 
    12461246        # indicate phase change 
    12471247        self.exportPhaseInfo("AFTER") 
    1248          
     1248 
    12491249        # handle ending banner pages with accounting 
    12501250        if (self.Action != "CANCEL") and accountbanner in ["ENDING", "BOTH"] : 
    12511251            if not self.gotSigTerm : 
    12521252                self.handleBanner("ending", 1) 
    1253          
     1253 
    12541254        # stops accounting 
    12551255        if self.Action == "DENY" : 
    12561256            self.printInfo(_("Job denied, no accounting has been done.")) 
    1257         elif self.Action == "CANCEL" :     
     1257        elif self.Action == "CANCEL" : 
    12581258            self.printInfo(_("Job cancelled, no accounting has been done.")) 
    12591259        else : 
     
    12621262            self.installSigTermHandler() 
    12631263            self.printInfo(_("Job accounting ends.")) 
    1264          
    1265         # Do all these database changes within a single transaction     
     1264 
     1265        # Do all these database changes within a single transaction 
    12661266        # NB : we don't enclose ALL the changes within a single transaction 
    12671267        # because while waiting for the printer to answer its internal page 
     
    12801280                    self.JobSize = 0 
    12811281                    self.printInfo(_("Job size forced to 0 because the real CUPS backend failed. No accounting will be done."), "warn") 
    1282                 else :     
     1282                else : 
    12831283                    self.printInfo(_("The real CUPS backend failed, but the job will be accounted for anyway."), "warn") 
    1284                      
    1285             # retrieve the job size     
     1284 
     1285            # retrieve the job size 
    12861286            if self.Action == "DENY" : 
    12871287                self.JobSize = 0 
    12881288                self.printInfo(_("Job size forced to 0 because printing is denied.")) 
    1289             elif self.Action == "CANCEL" :      
     1289            elif self.Action == "CANCEL" : 
    12901290                self.JobSize = 0 
    12911291                self.printInfo(_("Job size forced to 0 because printing was cancelled.")) 
    1292             else :     
     1292            else : 
    12931293                self.UserPQuota.resetDenyBannerCounter() 
    1294                 if (self.Action != "PROBLEM") or ("CHARGE" in onbackenderror) :  
     1294                if (self.Action != "PROBLEM") or ("CHARGE" in onbackenderror) : 
    12951295                    self.JobSize = self.accounter.getJobSize(self.Printer) 
    12961296                    self.sanitizeJobSize() 
    12971297                    self.JobSize += self.BannerSize 
    12981298            self.printInfo(_("Job size : %i") % self.JobSize) 
    1299              
     1299 
    13001300            if ((self.Action == "PROBLEM") and ("NOCHARGE" in onbackenderror)) or \ 
    13011301                (self.Action in ("DENY", "CANCEL")) : 
     
    13061306                self.JobPrice = 0.0 
    13071307            else : 
    1308                 # update the quota for the current user on this printer  
     1308                # update the quota for the current user on this printer 
    13091309                self.printInfo(_("Updating user %s's quota on printer %s") % (self.UserName, self.PrinterName)) 
    13101310                self.JobPrice = self.UserPQuota.increasePagesUsage(self.JobSize, self.accounter.inkUsage) 
    1311              
    1312             # adds the current job to history     
     1311 
     1312            # adds the current job to history 
    13131313            self.Printer.addJobToHistory(self.JobId, self.User, self.accounter.getLastPageCounter(), \ 
    13141314                                    self.Action, self.JobSize, self.JobPrice, self.InputFile, \ 
     
    13171317                                    self.softwareJobSize, self.softwareJobPrice) 
    13181318            self.printInfo(_("Job added to history.")) 
    1319              
     1319 
    13201320            if hasattr(self, "BillingCode") and self.BillingCode and self.BillingCode.Exists : 
    13211321                if (self.Action in ("ALLOW", "WARN")) or \ 
     
    13231323                    self.BillingCode.consume(self.JobSize, self.JobPrice) 
    13241324                    self.printInfo(_("Billing code %s was updated.") % self.BillingCode.BillingCode) 
    1325         except :     
     1325        except : 
    13261326            self.storage.rollbackTransaction() 
    13271327            raise 
    1328         else :     
     1328        else : 
    13291329            self.storage.commitTransaction() 
    1330              
     1330 
    13311331        # exports some new environment variables 
    13321332        self.exportJobSizeAndPrice() 
    1333          
     1333 
    13341334        # then re-export user information with new values 
    13351335        self.exportUserInfo() 
    1336          
     1336 
    13371337        # handle ending banner pages without accounting 
    13381338        if (self.Action != "CANCEL") and accountbanner in ["STARTING", "NONE"] : 
    13391339            self.handleBanner("ending", 0) 
    1340                      
     1340 
    13411341        self.launchPostHook() 
    1342              
    1343         return retcode     
    1344                 
    1345     def printJobDatas(self) :            
     1342 
     1343        return retcode 
     1344 
     1345    def printJobDatas(self) : 
    13461346        """Sends the job's datas to the real backend.""" 
    13471347        self.logdebug("Sending job's datas to real backend...") 
    1348          
     1348 
    13491349        delay = 0 
    13501350        number = 1 
     
    13551355                    if (number < 0) or (delay < 0) : 
    13561356                        raise ValueError 
    1357                 except ValueError :     
     1357                except ValueError : 
    13581358                    self.printInfo(_("Incorrect value for the 'onbackenderror' directive in section [%s]") % self.PrinterName, "error") 
    13591359                    delay = 0 
    13601360                    number = 1 
    1361                 else :     
     1361                else : 
    13621362                    break 
    1363         loopcnt = 1  
    1364         while True :             
     1363        loopcnt = 1 
     1364        while True : 
    13651365            if self.InputFile is None : 
    13661366                infile = open(self.DataFile, "rb") 
    1367             else :     
     1367            else : 
    13681368                infile = None 
    13691369            retcode = self.runOriginalBackend(infile) 
     
    13771377                    time.sleep(delay) 
    13781378                    loopcnt += 1 
    1379                 else :     
     1379                else : 
    13801380                    break 
    1381              
     1381 
    13821382        self.logdebug("Job's datas sent to real backend.") 
    13831383        return retcode 
    1384          
     1384 
    13851385    def runOriginalBackend(self, filehandle=None, isBanner=0) : 
    13861386        """Launches the original backend.""" 
     
    13881388        if not isBanner : 
    13891389            arguments = [os.environ["DEVICE_URI"]] + sys.argv[1:] 
    1390         else :     
     1390        else : 
    13911391            # For banners, we absolutely WANT 
    13921392            # to remove any filename from the command line ! 
     
    13971397        # TODO : do something about the job title : if we are printing a banner and the backend 
    13981398        # TODO : uses the job's title to name an output file (cups-pdf:// for example), we're stuck ! 
    1399          
     1399 
    14001400        self.logdebug("Starting original backend %s with args %s" % (originalbackend, " ".join(['"%s"' % a for a in arguments]))) 
    1401         self.regainPriv()     
     1401        self.regainPriv() 
    14021402        pid = os.fork() 
    14031403        self.logdebug("Forked !") 
     
    14111411            except OSError, msg : 
    14121412                self.logdebug("execve() failed: %s" % msg) 
    1413             self.logdebug("We shouldn't be there !!!")     
     1413            self.logdebug("We shouldn't be there !!!") 
    14141414            os._exit(-1) 
    1415         self.dropPriv()     
    1416          
    1417         self.logdebug("Waiting for original backend to exit...")     
     1415        self.dropPriv() 
     1416 
     1417        self.logdebug("Waiting for original backend to exit...") 
    14181418        killed = 0 
    14191419        status = -1 
     
    14251425                    os.kill(pid, signal.SIGTERM) 
    14261426                    killed = 1 
    1427                      
     1427 
    14281428        if os.WIFEXITED(status) : 
    14291429            status = os.WEXITSTATUS(status) 
     
    14331433                level = "error" 
    14341434                self.Reason = message 
    1435             else :     
     1435            else : 
    14361436                level = "info" 
    14371437            self.printInfo(message, level) 
     
    14451445            self.printInfo(self.Reason, "warn") 
    14461446            return 1 
    1447          
    1448 if __name__ == "__main__" :     
     1447 
     1448if __name__ == "__main__" : 
    14491449    # This is a CUPS backend, we should act and die like a CUPS backend 
    14501450    wrapper = CUPSBackend() 
    14511451    if len(sys.argv) == 1 : 
    14521452        print "\n".join(wrapper.discoverOtherBackends()) 
    1453         sys.exit(0)                 
    1454     elif len(sys.argv) not in (6, 7) :     
     1453        sys.exit(0) 
     1454    elif len(sys.argv) not in (6, 7) : 
    14551455        sys.stderr.write("ERROR: %s job-id user title copies options [file]\n"\ 
    14561456                              % sys.argv[0]) 
    14571457        sys.exit(1) 
    1458     else :     
     1458    else : 
    14591459        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "") 
    14601460        try : 
     
    14661466                    raise KeyboardInterrupt 
    14671467                wrapper.saveDatasAndCheckSum() 
     1468                wrapper.exportJobInfo() # exports a first time to give hints to external scripts 
    14681469                wrapper.preaccounter = openAccounter(wrapper, ispreaccounter=1) 
    14691470                wrapper.accounter = openAccounter(wrapper) 
    14701471                wrapper.precomputeJobSize() 
    1471                 wrapper.exportJobInfo() # exports a first time to give hints to external scripts  
     1472                wrapper.exportJobInfo() # exports a first time to give hints to external scripts 
    14721473                wrapper.overwriteJobAttributes() 
    14731474                wrapper.exportJobInfo() # re-exports in case it was overwritten 
    14741475                retcode = wrapper.mainWork() 
    1475             except KeyboardInterrupt :     
     1476            except KeyboardInterrupt : 
    14761477                wrapper.printInfo(_("Job %s interrupted by the administrator !") % wrapper.JobId, "warn") 
    14771478                retcode = 0 
    1478             except SystemExit, err :     
     1479            except SystemExit, err : 
    14791480                retcode = err.code 
    1480             except :     
     1481            except : 
    14811482                try : 
    14821483                    wrapper.crashed("cupspykota backend failed") 
    1483                 except :     
     1484                except : 
    14841485                    crashed("cupspykota backend failed") 
    14851486                retcode = 1 
    1486         finally :         
     1487        finally : 
    14871488            wrapper.clean() 
    14881489        sys.exit(retcode)