Index: /pykota/branches/1.26_fixes/bin/cupspykota
===================================================================
--- /pykota/branches/1.26_fixes/bin/cupspykota (revision 3189)
+++ /pykota/branches/1.26_fixes/bin/cupspykota (revision 3459)
@@ -1,4 +1,4 @@
 #! /usr/bin/env python
-# -*- coding: ISO-8859-15 -*-
+# -*- coding: iso-8859-15 -*-
 
 # CUPSPyKota accounting backend
@@ -16,5 +16,5 @@
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
@@ -55,20 +55,20 @@
 try :
     from pkipplib import pkipplib
-except ImportError :        
+except ImportError :
     haspkipplib = False
-else :    
+else :
     haspkipplib = True
-    
-class FakeObject :        
+
+class FakeObject :
     """Fake object."""
     def __init__(self, name) :
         """Fake init."""
         self.Name = name
-        
-class FakePrinter(FakeObject) :        
+
+class FakePrinter(FakeObject) :
     """Fake printer instance."""
     pass
-    
-class FakeUser(FakeObject) :    
+
+class FakeUser(FakeObject) :
     """Fake user instance."""
     def __init__(self, name) :
@@ -76,5 +76,5 @@
         self.Email = name
         FakeObject.__init__(self, name)
-    
+
 class CUPSBackend(PyKotaTool) :
     """Base class for tools with no database access."""
@@ -90,6 +90,6 @@
         self.lockfilename = None
         self.lockfile = None
-        
-    def deferredInit(self) :    
+
+    def deferredInit(self) :
         """Deferred initialization."""
         PyKotaTool.deferredInit(self)
@@ -101,5 +101,5 @@
         self.disableSigInt()
         self.installSigTermHandler()
-        
+
     def sigtermHandler(self, signum, frame) :
         """Sets an attribute whenever SIGTERM is received."""
@@ -107,30 +107,30 @@
         self.printInfo(_("SIGTERM received, job %s cancelled.") % self.JobId)
         os.environ["PYKOTASTATUS"] = "CANCELLED"
-        
-    def deinstallSigTermHandler(self) :           
+
+    def deinstallSigTermHandler(self) :
         """Deinstalls the SIGTERM handler."""
         self.logdebug("Deinstalling SIGTERM handler...")
         signal.signal(signal.SIGTERM, signal.SIG_IGN)
         self.logdebug("SIGTERM handler deinstalled.")
-        
-    def installSigTermHandler(self) :           
+
+    def installSigTermHandler(self) :
         """Installs the SIGTERM handler."""
         self.logdebug("Installing SIGTERM handler...")
         signal.signal(signal.SIGTERM, self.sigtermHandler)
         self.logdebug("SIGTERM handler installed.")
-        
-    def disableSigInt(self) :    
+
+    def disableSigInt(self) :
         """Disables the SIGINT signal (which raises KeyboardInterrupt)."""
         self.logdebug("Disabling SIGINT...")
         self.oldSigIntHandler = signal.signal(signal.SIGINT, signal.SIG_IGN)
         self.logdebug("SIGINT disabled.")
-        
-    def enableSigInt(self) :    
+
+    def enableSigInt(self) :
         """Enables the SIGINT signal (which raises KeyboardInterrupt)."""
         self.logdebug("Enabling SIGINT...")
         signal.signal(signal.SIGINT, self.oldSigIntHandler)
         self.logdebug("SIGINT enabled.")
-        
-    def waitForLock(self) :    
+
+    def waitForLock(self) :
         """Waits until we can acquire the lock file."""
         self.logdebug("Waiting for lock %s to become available..." % self.lockfilename)
@@ -140,12 +140,12 @@
                 # open the lock file, optionally creating it if needed.
                 self.lockfile = open(self.lockfilename, "a+")
-                
+
                 # we wait indefinitely for the lock to become available.
                 # works over NFS too.
                 fcntl.lockf(self.lockfile, fcntl.LOCK_EX)
                 haslock = True
-                
+
                 self.logdebug("Lock %s acquired." % self.lockfilename)
-                
+
                 # Here we save the PID in the lock file, but we don't use
                 # it, because the lock file may be in a directory shared
@@ -156,11 +156,11 @@
                 self.lockfile.write(str(self.pid))
                 self.lockfile.flush()
-            except IOError, msg :            
+            except IOError, msg :
                 self.logdebug("I/O Error while waiting for lock %s : %s" % (self.lockfilename, msg))
                 time.sleep(0.25)
-                    
-    def discoverOtherBackends(self) :    
+
+    def discoverOtherBackends(self) :
         """Discovers the other CUPS backends.
-        
+
            Executes each existing backend in turn in device enumeration mode.
            Returns the list of available backends.
@@ -186,5 +186,5 @@
                     # process doesn't exist anymore
                     os.remove(lockfilename)
-            
+
         if not os.path.exists(lockfilename) :
             lockfile = open(lockfilename, "w")
@@ -194,15 +194,15 @@
                                 for b in os.listdir(directory) \
                                     if os.access(os.path.join(directory, b), os.X_OK) \
-                                        and (b != myname)] 
-            for backend in allbackends :                            
+                                        and (b != myname)]
+            for backend in allbackends :
                 answer = os.popen(backend, "r")
                 try :
                     devices = [line.strip() for line in answer.readlines()]
-                except :    
+                except :
                     devices = []
                 status = answer.close()
                 if status is None :
                     for d in devices :
-                        # each line is of the form : 
+                        # each line is of the form :
                         # 'xxxx xxxx "xxxx xxx" "xxxx xxx"'
                         # so we have to decompose it carefully
@@ -221,7 +221,7 @@
                         try :
                             (devicetype, device, name, fullname) = arguments
-                        except ValueError :    
+                        except ValueError :
                             pass    # ignore this 'bizarre' device
-                        else :    
+                        else :
                             if name.startswith('"') and name.endswith('"') :
                                 name = name[1:-1]
@@ -237,13 +237,13 @@
                              % (self.myname, self.MyName, self.MyName))
         return available
-                        
-    def initBackendParameters(self) :    
+
+    def initBackendParameters(self) :
         """Initializes the backend's attributes."""
-        # check that the DEVICE_URI environment variable's value is 
+        # check that the DEVICE_URI environment variable's value is
         # prefixed with self.myname otherwise don't touch it.
-        # If this is the case, we have to remove the prefix from 
-        # the environment before launching the real backend 
+        # If this is the case, we have to remove the prefix from
+        # the environment before launching the real backend
         self.logdebug("Initializing backend...")
-        
+        self.softwareJobSize = None
         self.PrinterName = os.environ.get("PRINTER", "")
         directories = [ self.config.getPrinterDirectory(self.PrinterName),
@@ -257,5 +257,5 @@
             else :
                 self.printInfo("Insufficient permissions to access to temporary directory %s" % direc, "warn")
-                
+
         self.Action = "ALLOW"   # job allowed by default
         self.Reason = None
@@ -269,9 +269,9 @@
         if len(sys.argv) == 7 :
             self.InputFile = sys.argv[6] # read job's datas from file
-        else :    
+        else :
             self.InputFile = None        # read job's datas from stdin
         self.DataFile = os.path.join(self.Directory, "%s-%s-%s-%s" % \
                    (self.myname, self.PrinterName, self.UserName, self.JobId))
-        
+
         muststartwith = "%s:" % self.myname
         device_uri = os.environ.get("DEVICE_URI", "")
@@ -280,34 +280,34 @@
             device_uri = fulldevice_uri[len(muststartwith):]
             for i in range(2) :
-                if device_uri.startswith("/") :  
+                if device_uri.startswith("/") :
                     device_uri = device_uri[1:]
         try :
-            (backend, destination) = device_uri.split(":", 1) 
-        except ValueError :    
+            (backend, destination) = device_uri.split(":", 1)
+        except ValueError :
             if not device_uri :
                 self.logdebug("Not attached to an existing print queue.")
                 backend = ""
                 printerhostname = ""
-            else :    
+            else :
                 raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
-        else :        
+        else :
             if backend == "hp" :
                 try :
                     printerhostname = destination.split("=")[1] # hp:/net/HP_LaserJet_8000_Series?ip=192.168.100.100
-                except IndexError :    
+                except IndexError :
                     self.logdebug("Unsupported hplip URI %s" % device_uri)
-                    printerhostname = "" 
-            else :    
+                    printerhostname = ""
+            else :
                 while destination.startswith("/") :
                     destination = destination[1:]
-                checkauth = destination.split("@", 1)    
+                checkauth = destination.split("@", 1)
                 if len(checkauth) == 2 :
                     destination = checkauth[1]
             printerhostname = destination.split("/")[0].split(":")[0]
-            
-        self.PrinterHostName = printerhostname    
+
+        self.PrinterHostName = printerhostname
         self.RealBackend = backend
         self.DeviceURI = device_uri
-        
+
         connerror = False
         if haspkipplib :
@@ -319,20 +319,20 @@
                 self.printInfo(_("Network error while querying the CUPS server : %s") \
                                           % cupsserver.lastErrorMessage, "error")
-                connerror = True                        
-            else :    
+                connerror = True
+            else :
                 self.logdebug("CUPS server answered without error.")
                 try :
                     john = answer.job["job-originating-host-name"]
-                except KeyError :    
+                except KeyError :
                     try :
                         john = answer.operation["job-originating-host-name"]
-                    except KeyError :    
+                    except KeyError :
                         john = (None, None)
-                try :        
+                try :
                     jbing = answer.job["job-billing"]
-                except KeyError :    
+                except KeyError :
                     jbing = (None, None)
-                    
-        if connerror or not haspkipplib :        
+
+        if connerror or not haspkipplib :
             (ippfilename, ippmessage) = self.parseIPPRequestFile()
             self.ControlFile = ippfilename
@@ -341,17 +341,17 @@
                    (None, None)))
             jbing = ippmessage.job_attributes.get("job-billing", (None, None))
-                
-        if type(john) == type([]) : 
+
+        if type(john) == type([]) :
             john = john[-1]
-        (chtype, self.ClientHost) = john 
-        if type(jbing) == type([]) : 
+        (chtype, self.ClientHost) = john
+        if type(jbing) == type([]) :
             jbing = jbing[-1]
         (jbtype, self.JobBillingCode) = jbing
         if self.JobBillingCode is None :
             self.OriginalJobBillingCode = None
-        else :    
+        else :
             self.JobBillingCode = self.UTF8ToUserCharset(self.JobBillingCode)
             self.OriginalJobBillingCode = self.JobBillingCode[:]
-            
+
         baselockfilename = self.DeviceURI.replace("/", ".")
         baselockfilename = baselockfilename.replace(":", ".")
@@ -360,5 +360,5 @@
         baselockfilename = baselockfilename.replace("@", ".")
         self.lockfilename = os.path.join(self.Directory, "%s-%s..LCK" % (self.myname, baselockfilename))
-        
+
         self.logdebug("Backend : %s" % self.RealBackend)
         self.logdebug("DeviceURI : %s" % self.DeviceURI)
@@ -370,18 +370,18 @@
         self.logdebug("Copies : %s" % self.Copies)
         self.logdebug("Options : %s" % self.Options)
-        self.logdebug("Directory : %s" % self.Directory) 
+        self.logdebug("Directory : %s" % self.Directory)
         self.logdebug("DataFile : %s" % self.DataFile)
         self.logdebug("ControlFile : %s" % self.ControlFile)
         self.logdebug("JobBillingCode : %s" % self.JobBillingCode)
         self.logdebug("JobOriginatingHostName : %s" % self.ClientHost)
-        
+
         # fakes some entries to allow for external mailto
         # before real entries are extracted from the database.
         self.User = FakeUser(self.UserName)
         self.Printer = FakePrinter(self.PrinterName)
-        
+
         self.enableSigInt()
         self.logdebug("Backend initialized.")
-        
+
     def overwriteJobAttributes(self) :
         """Overwrites some of the job's attributes if needed."""
@@ -389,11 +389,11 @@
         # First overwrite the job ticket
         self.overwriteJobTicket()
-        
+
         # do we want to strip out the Samba/Winbind domain name ?
         separator = self.config.getWinbindSeparator()
         if separator is not None :
             self.UserName = self.UserName.split(separator)[-1]
-            
-        # this option is deprecated, and we want to tell people    
+
+        # this option is deprecated, and we want to tell people
         # this is the case.
         tolower = self.config.getUserNameToLower()
@@ -404,11 +404,11 @@
             if self.config.isTrue(tolower) :
                 self.UserName = self.UserName.lower()
-                
-        # Now use the newer and more complete 'usernamecase' directive.    
-        casechange = self.config.getUserNameCase()    
+
+        # Now use the newer and more complete 'usernamecase' directive.
+        casechange = self.config.getUserNameCase()
         if casechange != "native" :
             self.UserName = getattr(self.UserName, casechange)()
-            
-        # do we want to strip some prefix off of titles ?    
+
+        # do we want to strip some prefix off of titles ?
         stripprefix = self.config.getStripTitle(self.PrinterName)
         if stripprefix :
@@ -417,17 +417,17 @@
                                       % (stripprefix, self.Title))
                 self.Title = self.Title[len(stripprefix):]
-                
+
         self.logdebug("Username : %s" % self.UserName)
         self.logdebug("BillingCode : %s" % self.JobBillingCode)
         self.logdebug("Title : %s" % self.Title)
         self.logdebug("Job's attributes sanitizing done.")
-                
+
     def didUserConfirm(self) :
         """Asks for user confirmation through an external script.
-        
+
            returns False if the end user wants to cancel the job, else True.
         """
         self.logdebug("Checking if we have to ask for user's confirmation...")
-        answer = None                         
+        answer = None
         confirmationcommand = self.config.getAskConfirmation(self.PrinterName)
         if confirmationcommand :
@@ -440,13 +440,13 @@
                     if answer == "CANCEL" :
                         break
-            except IOError, msg :            
+            except IOError, msg :
                 self.logdebug("IOError while reading subprocess' output : %s" % msg)
-            inputfile.close()    
+            inputfile.close()
             self.logdebug("User's confirmation received : %s" % (((answer == "CANCEL") and "CANCEL") or "CONTINUE"))
-        else :    
+        else :
             self.logdebug("No need to ask for user's confirmation, job processing will continue.")
-        return (answer != "CANCEL")    
-        
-    def overwriteJobTicket(self) :    
+        return (answer != "CANCEL")
+
+    def overwriteJobTicket(self) :
         """Should we overwrite the job's ticket (username and billingcode) ?"""
         self.logdebug("Checking if we need to overwrite the job ticket...")
@@ -456,5 +456,5 @@
             self.logdebug("Launching subprocess [%s] to overwrite the job ticket." \
                                      % jobticketcommand)
-            self.regainPriv()                         
+            self.regainPriv()
             inputfile = os.popen(jobticketcommand, "r")
             try :
@@ -467,8 +467,8 @@
                         self.logdebug("Seen CANCEL command.")
                         action = "CANCEL"
-                    elif line.startswith("USERNAME=") :    
+                    elif line.startswith("USERNAME=") :
                         username = self.userCharsetToUTF8(line.split("=", 1)[1].strip())
                         self.logdebug("Seen new username [%s]" % username)
-                    elif line.startswith("BILLINGCODE=") :    
+                    elif line.startswith("BILLINGCODE=") :
                         billingcode = self.userCharsetToUTF8(line.split("=", 1)[1].strip())
                         self.logdebug("Seen new billing code [%s]" % billingcode)
@@ -476,9 +476,9 @@
                         reason = self.userCharsetToUTF8(line.split("=", 1)[1].strip())
                         self.logdebug("Seen new reason [%s]" % reason)
-            except IOError, msg :            
+            except IOError, msg :
                 self.logdebug("IOError while reading subprocess' output : %s" % msg)
-            inputfile.close()    
+            inputfile.close()
             self.dropPriv()
-            
+
             # now overwrite the job's ticket if new data was supplied
             if action == "DENY" :
@@ -495,12 +495,12 @@
                     self.UserName = username
                 if billingcode is not None :
-                    self.JobBillingCode = billingcode 
+                    self.JobBillingCode = billingcode
         self.logdebug("Job ticket overwriting done.")
-            
+
     def saveDatasAndCheckSum(self) :
         """Saves the input datas into a static file."""
         self.logdebug("Duplicating data stream into %s" % self.DataFile)
         mustclose = 0
-        outfile = open(self.DataFile, "wb")    
+        outfile = open(self.DataFile, "wb")
         if self.InputFile is not None :
             self.regainPriv()
@@ -508,5 +508,5 @@
             self.logdebug("Reading input datas from %s" % self.InputFile)
             mustclose = 1
-        else :    
+        else :
             infile = sys.stdin
             self.logdebug("Reading input datas from stdin")
@@ -516,25 +516,25 @@
         checksum = md5.new()
         while 1 :
-            data = infile.read(CHUNK) 
+            data = infile.read(CHUNK)
             if not data :
                 break
-            sizeread += len(data)    
+            sizeread += len(data)
             outfile.write(data)
-            checksum.update(data)    
+            checksum.update(data)
             if not (dummy % 32) : # Only display every 2 Mb
                 self.logdebug("%s bytes saved..." % sizeread)
-            dummy += 1    
-        if mustclose :    
+            dummy += 1
+        if mustclose :
             infile.close()
             self.dropPriv()
-            
+
         outfile.close()
-        self.JobSizeBytes = sizeread    
+        self.JobSizeBytes = sizeread
         self.JobMD5Sum = checksum.hexdigest()
-        
+
         self.logdebug("JobSizeBytes : %s" % self.JobSizeBytes)
         self.logdebug("JobMD5Sum : %s" % self.JobMD5Sum)
         self.logdebug("Data stream duplicated into %s" % self.DataFile)
-            
+
     def clean(self) :
         """Cleans up the place."""
@@ -545,5 +545,5 @@
             try :
                 keep = self.config.getPrinterKeepFiles(self.PrinterName)
-            except AttributeError :    
+            except AttributeError :
                 keep = False
             if not keep :
@@ -555,7 +555,7 @@
                 else :
                     self.logdebug("Work file %s has been deleted." % self.DataFile)
-            else :    
+            else :
                 self.logdebug("Work file %s will be kept." % self.DataFile)
-        PyKotaTool.clean(self)    
+        PyKotaTool.clean(self)
         if self.lockfile is not None :
             self.logdebug("Unlocking %s..." %  self.lockfilename)
@@ -563,11 +563,11 @@
                 fcntl.lockf(self.lockfile, fcntl.LOCK_UN)
                 self.lockfile.close()
-            except :    
+            except :
                 self.printInfo("Problem while unlocking %s" % self.lockfilename, "error")
-            else :    
+            else :
                 self.logdebug("%s unlocked." % self.lockfilename)
         self.logdebug("Clean.")
-            
-    def precomputeJobSize(self) :    
+
+    def precomputeJobSize(self) :
         """Computes the job size with a software method."""
         self.logdebug("Precomputing job's size...")
@@ -576,6 +576,6 @@
         self.softwareJobSize = self.preaccounter.getJobSize(None)
         self.logdebug("Precomputed job's size is %s pages." % self.softwareJobSize)
-        
-    def precomputeJobPrice(self) :    
+
+    def precomputeJobPrice(self) :
         """Precomputes the job price with a software method."""
         self.logdebug("Precomputing job's price...")
@@ -583,20 +583,20 @@
         self.logdebug("Precomputed job's price is %.3f credits." \
                                    % self.softwareJobPrice)
-        
+
     def getCupsConfigDirectives(self, directives=[]) :
         """Retrieves some CUPS directives from its configuration file.
-        
-           Returns a mapping with lowercased directives as keys and 
+
+           Returns a mapping with lowercased directives as keys and
            their setting as values.
         """
         self.logdebug("Parsing CUPS' configuration file...")
-        dirvalues = {} 
+        dirvalues = {}
         cupsroot = os.environ.get("CUPS_SERVERROOT", "/etc/cups")
         cupsdconf = os.path.join(cupsroot, "cupsd.conf")
         try :
             conffile = open(cupsdconf, "r")
-        except IOError :    
+        except IOError :
             raise PyKotaToolError, "Unable to open %s" % cupsdconf
-        else :    
+        else :
             for line in conffile.readlines() :
                 linecopy = line.strip().lower()
@@ -605,23 +605,23 @@
                         try :
                             val = line.split()[1]
-                        except :    
+                        except :
                             pass # ignore errors, we take the last value in any case.
-                        else :    
+                        else :
                             dirvalues[di] = val
-            conffile.close()            
+            conffile.close()
         self.logdebug("CUPS' configuration file parsed successfully.")
-        return dirvalues       
-            
-    def parseIPPRequestFile(self) :        
+        return dirvalues
+
+    def parseIPPRequestFile(self) :
         """Parses the IPP message file and returns a tuple (filename, parsedvalue)."""
         self.logdebug("Parsing IPP request file...")
-        
+
         class DummyClass :
             """Class used to avoid errors."""
             operation_attributes = {}
             job_attributes = {}
-            
+
         ippmessage = DummyClass() # in case the code below fails
-        
+
         self.regainPriv()
         cupsdconf = self.getCupsConfigDirectives(["RequestRoot"])
@@ -629,20 +629,20 @@
         if (len(self.JobId) < 5) and self.JobId.isdigit() :
             ippmessagefile = "c%05i" % int(self.JobId)
-        else :    
+        else :
             ippmessagefile = "c%s" % self.JobId
         ippmessagefile = os.path.join(requestroot, ippmessagefile)
         try :
             ippdatafile = open(ippmessagefile)
-        except :    
+        except :
             self.logdebug("Unable to open IPP request file %s" % ippmessagefile)
-        else :    
+        else :
             self.logdebug("Parsing of IPP request file %s begins." % ippmessagefile)
             try :
                 ippmessage = oldIPPRequest(ippdatafile.read())
                 ippmessage.parse()
-            except oldIPPError, msg :    
+            except oldIPPError, msg :
                 self.printInfo("Error while parsing %s : %s" \
                                       % (ippmessagefile, msg), "warn")
-            else :    
+            else :
                 self.logdebug("Parsing of IPP request file %s ends." \
                                        % ippmessagefile)
@@ -651,6 +651,6 @@
         self.logdebug("IPP request file parsed successfully.")
         return (ippmessagefile, ippmessage)
-                
-    def exportJobInfo(self) :    
+
+    def exportJobInfo(self) :
         """Exports the actual job's attributes to the environment."""
         self.logdebug("Exporting job information to the environment...")
@@ -675,5 +675,5 @@
         os.environ["PYKOTAPRECOMPUTEDJOBSIZE"] = str(self.softwareJobSize)
         self.logdebug("Environment updated.")
-        
+
     def exportUserInfo(self) :
         """Exports user information to the environment."""
@@ -684,5 +684,5 @@
         os.environ["PYKOTALIFETIMEPAID"] = str(self.User.LifeTimePaid or 0.0)
         os.environ["PYKOTAUSERDESCRIPTION"] = str(self.User.Description or "")
-        
+
         os.environ["PYKOTAPAGECOUNTER"] = str(self.UserPQuota.PageCounter or 0)
         os.environ["PYKOTALIFEPAGECOUNTER"] = str(self.UserPQuota.LifePageCounter or 0)
@@ -691,10 +691,10 @@
         os.environ["PYKOTADATELIMIT"] = str(self.UserPQuota.DateLimit)
         os.environ["PYKOTAWARNCOUNT"] = str(self.UserPQuota.WarnCount)
-        
+
         # TODO : move this elsewhere once software accounting is done only once.
         os.environ["PYKOTAPRECOMPUTEDJOBPRICE"] = str(self.softwareJobPrice)
-        
+
         self.logdebug("Environment updated.")
-        
+
     def exportPrinterInfo(self) :
         """Exports printer information to the environment."""
@@ -709,5 +709,5 @@
         os.environ["PYKOTAPRICEPERJOB"] = str(self.Printer.PricePerJob or 0)
         self.logdebug("Environment updated.")
-        
+
     def exportPhaseInfo(self, phase) :
         """Exports phase information to the environment."""
@@ -715,5 +715,5 @@
         os.environ["PYKOTAPHASE"] = phase
         self.logdebug("Environment updated.")
-        
+
     def exportJobSizeAndPrice(self) :
         """Exports job's size and price information to the environment."""
@@ -722,5 +722,5 @@
         os.environ["PYKOTAJOBPRICE"] = str(self.JobPrice)
         self.logdebug("Environment updated.")
-        
+
     def exportReason(self) :
         """Exports the job's action status and optional reason."""
@@ -730,16 +730,16 @@
             os.environ["PYKOTAREASON"] = str(self.Reason)
         self.logdebug("Environment updated.")
-        
-    def acceptJob(self) :        
+
+    def acceptJob(self) :
         """Returns the appropriate exit code to tell CUPS all is OK."""
         return 0
-            
-    def removeJob(self) :            
+
+    def removeJob(self) :
         """Returns the appropriate exit code to let CUPS think all is OK.
-        
+
            Returning 0 (success) prevents CUPS from stopping the print queue.
-        """   
+        """
         return 0
-        
+
     def launchPreHook(self) :
         """Allows plugging of an external hook before the job gets printed."""
@@ -749,5 +749,5 @@
             retcode = os.system(prehook)
             self.logdebug("pre-hook exited with status %s." % retcode)
-        
+
     def launchPostHook(self) :
         """Allows plugging of an external hook after the job gets printed and/or denied."""
@@ -757,6 +757,6 @@
             retcode = os.system(posthook)
             self.logdebug("post-hook exited with status %s." % retcode)
-            
-    def improveMessage(self, message) :        
+
+    def improveMessage(self, message) :
         """Improves a message by adding more informations in it if possible."""
         try :
@@ -765,15 +765,15 @@
                                         self.JobId, \
                                         message)
-        except :                                               
+        except :
             return message
-        
-    def logdebug(self, message) :        
+
+    def logdebug(self, message) :
         """Improves the debug message before outputting it."""
         PyKotaTool.logdebug(self, self.improveMessage(message))
-        
-    def printInfo(self, message, level="info") :        
+
+    def printInfo(self, message, level="info") :
         """Improves the informational message before outputting it."""
         self.logger.log_message(self.improveMessage(message), level)
-    
+
     def startingBanner(self, withaccounting) :
         """Retrieves a starting banner for current printer and returns its content."""
@@ -781,5 +781,5 @@
         self.printBanner(self.config.getStartingBanner(self.PrinterName), withaccounting)
         self.logdebug("Starting banner retrieved.")
-    
+
     def endingBanner(self, withaccounting) :
         """Retrieves an ending banner for current printer and returns its content."""
@@ -787,8 +787,8 @@
         self.printBanner(self.config.getEndingBanner(self.PrinterName), withaccounting)
         self.logdebug("Ending banner retrieved.")
-        
+
     def printBanner(self, bannerfileorcommand, withaccounting) :
         """Reads a banner or generates one through an external command.
-        
+
            Returns the banner's content in a format which MUST be accepted
            by the printer.
@@ -816,8 +816,8 @@
                 try :
                     fh = open(bannerfileorcommand, 'rb')
-                except IOError, msg :    
+                except IOError, msg :
                     self.printInfo("Impossible to open %s : %s" \
                                        % (bannerfileorcommand, msg), "error")
-                else :    
+                else :
                     self.runOriginalBackend(fh, isBanner=1)
                     fh.close()
@@ -826,10 +826,10 @@
                             self.BannerSize += 1 # TODO : fix this by passing the banner's content through software accounting
         self.logdebug("Banner printed...")
-                
+
     def handleBanner(self, bannertype, withaccounting) :
         """Handles the banner with or without accounting."""
         if withaccounting :
             acc = "with"
-        else :    
+        else :
             acc = "without"
         self.logdebug("Handling %s banner %s accounting..." % (bannertype, acc))
@@ -860,6 +860,6 @@
                         if (avoidduplicatebanners == "YES") :
                             printbanner = False
-                        else :    
-                            # avoidduplicatebanners is an integer, since NO, 
+                        else :
+                            # avoidduplicatebanners is an integer, since NO,
                             # YES and 0 are already handled
                             now = DateTime.now()
@@ -871,5 +871,5 @@
                             self.logdebug("Difference with previous job : %.2f seconds. Try to avoid banners for : %.2f seconds." % (difference, avoidduplicatebanners))
                             if difference < avoidduplicatebanners :
-                                self.logdebug("Duplicate banner avoided because previous banner is less than %.2f seconds old." % avoidduplicatebanners) 
+                                self.logdebug("Duplicate banner avoided because previous banner is less than %.2f seconds old." % avoidduplicatebanners)
                                 printbanner = False
                             else :
@@ -878,6 +878,6 @@
                     getattr(self, "%sBanner" % bannertype)(withaccounting)
         self.logdebug("%s banner done." % bannertype.title())
-        
-    def sanitizeJobSize(self) :    
+
+    def sanitizeJobSize(self) :
         """Sanitizes the job's size if needed."""
         # TODO : there's a difficult to see bug here when banner accounting is activated and hardware accounting is used.
@@ -897,15 +897,15 @@
                     if replacement == "PRECOMPUTED" :
                         self.JobSize = self.softwareJobSize
-                    else :    
+                    else :
                         self.JobSize = replacement
         self.logdebug("Job's size sanitized.")
-                        
-    def getPrinterUserAndUserPQuota(self) :        
+
+    def getPrinterUserAndUserPQuota(self) :
         """Returns a tuple (policy, printer, user, and user print quota) on this printer.
-        
+
            "OK" is returned in the policy if both printer, user and user print quota
            exist in the Quota Storage.
            Otherwise, the policy as defined for this printer in pykota.conf is returned.
-           
+
            If policy was set to "EXTERNAL" and one of printer, user, or user print quota
            doesn't exist in the Quota Storage, then an external command is launched, as
@@ -914,5 +914,5 @@
            or users, for example, and finally extracting printer, user and user print
            quota from the Quota Storage is tried a second time.
-           
+
            "EXTERNALERROR" is returned in case policy was "EXTERNAL" and an error status
            was returned by the external command.
@@ -927,5 +927,5 @@
                 break
             (policy, args) = self.config.getPrinterPolicy(self.PrinterName)
-            if policy == "EXTERNAL" :    
+            if policy == "EXTERNAL" :
                 commandline = self.formatCommandLine(args, user, printer)
                 if not printer.Exists :
@@ -939,5 +939,5 @@
                     policy = "EXTERNALERROR"
                     break
-            else :        
+            else :
                 if not printer.Exists :
                     self.printInfo(_("Printer %s not registered in the PyKota system, applying default policy (%s)") % (self.PrinterName, policy))
@@ -947,6 +947,6 @@
                     self.printInfo(_("User %s doesn't have quota on printer %s in the PyKota system, applying default policy (%s)") % (self.UserName, self.PrinterName, policy))
                 break
-                
-        if policy == "EXTERNAL" :    
+
+        if policy == "EXTERNAL" :
             if not printer.Exists :
                 self.printInfo(_("Printer %s still not registered in the PyKota system, job will be rejected") % self.PrinterName)
@@ -955,13 +955,13 @@
             if not userpquota.Exists :
                 self.printInfo(_("User %s still doesn't have quota on printer %s in the PyKota system, job will be rejected") % (self.UserName, self.PrinterName))
-        self.Policy = policy         
+        self.Policy = policy
         self.Printer = printer
         self.User = user
         self.UserPQuota = userpquota
         self.logdebug("Retrieval of printer, user and user print quota entry done.")
-        
-    def getBillingCode(self) :    
+
+    def getBillingCode(self) :
         """Extracts the billing code from the database.
-         
+
            An optional script is launched to notify the user when
            the billing code is unknown and PyKota was configured to
@@ -983,15 +983,15 @@
                     if self.BillingCode.Exists :
                         self.logdebug(msg + "has been created.")
-                    else :    
+                    else :
                         self.printInfo(msg + "couldn't be created.", "error")
-                else :    
+                else :
                     self.logdebug(msg + "job will be denied.")
                     self.Action = newaction
-                    if script is not None : 
+                    if script is not None :
                         self.logdebug(msg + "launching subprocess [%s] to notify user." % script)
                         os.system(script)
         self.logdebug("Retrieval of billing code information done.")
-        
-    def checkIfDupe(self) :    
+
+    def checkIfDupe(self) :
         """Checks if the job is a duplicate, and handles the situation."""
         self.logdebug("Checking if the job is a duplicate...")
@@ -1014,8 +1014,8 @@
                     self.logdebug("Duplicate job allowed because previous one is more than %.2f seconds old." % duplicatesdelay)
                 else :
-                    # TODO : use the current user's last job instead of  
+                    # TODO : use the current user's last job instead of
                     # TODO : the current printer's last job. This would be
                     # TODO : better but requires an additional database query
-                    # TODO : with SQL, and is much more complex with the 
+                    # TODO : with SQL, and is much more complex with the
                     # TODO : actual LDAP schema. Maybe this is not very
                     # TODO : important, because usually duplicate jobs are sucessive.
@@ -1025,24 +1025,24 @@
                         self.Action = "DENY"
                         self.Reason = _("Duplicate print jobs are not allowed on printer %s.") % self.PrinterName
-                    else :    
+                    else :
                         self.logdebug("Launching subprocess [%s] to see if duplicate jobs should be allowed or not." % denyduplicates)
                         fanswer = os.popen(denyduplicates, "r")
                         self.Action = fanswer.read().strip().upper()
                         fanswer.close()
-                        if self.Action == "DENY" :     
+                        if self.Action == "DENY" :
                             self.printInfo("%s : %s." % (msg, _("Subprocess denied printing of a dupe")), "warn")
                             self.Reason = _("Duplicate print jobs are not allowed on printer %s at this time.") % self.PrinterName
-                        else :    
+                        else :
                             self.printInfo("%s : %s." % (msg, _("Subprocess allowed printing of a dupe")), "warn")
-            else :            
+            else :
                 self.logdebug("Job doesn't seem to be a duplicate.")
         self.logdebug("Checking if the job is a duplicate done.")
-        
+
     def tellUser(self) :
         """Sends a message to an user."""
-        self.logdebug("Sending some feedback to user %s..." % self.UserName)  
+        self.logdebug("Sending some feedback to user %s..." % self.UserName)
         if not self.Reason :
             self.logdebug("No feedback to send to user %s." % self.UserName)
-        else :    
+        else :
             (mailto, arguments) = self.config.getMailTo(self.PrinterName)
             if mailto == "EXTERNAL" :
@@ -1051,5 +1051,5 @@
                 self.externalMailTo(arguments, self.Action, self.User, self.Printer, self.Reason)
                 self.dropPriv()
-            else :    
+            else :
                 # TODO : clean this again
                 admin = self.config.getAdmin(self.PrinterName)
@@ -1061,11 +1061,11 @@
                 if mailto in ("BOTH", "ADMIN") :
                     destination.append(adminmail)
-                if mailto in ("BOTH", "USER") :    
+                if mailto in ("BOTH", "USER") :
                     destination.append(usermail)
-                    
+
                 fullmessage = self.Reason + (_("\n\nYour system administrator :\n\n\t%s - <%s>\n") % (admin, adminmail))
-                try :    
+                try :
                     server = smtplib.SMTP(self.smtpserver)
-                except socket.error, msg :    
+                except socket.error, msg :
                     self.printInfo(_("Impossible to connect to SMTP server : %s") % msg, "error")
                 else :
@@ -1078,9 +1078,9 @@
                             if mailto == "BOTH" :
                                 msg["Cc"] = adminmail
-                        else :    
+                        else :
                             msg["To"] = adminmail
                         msg["Date"] = email.Utils.formatdate(localtime=True)
                         server.sendmail(adminmail, destination, msg.as_string())
-                    except smtplib.SMTPException, answer :    
+                    except smtplib.SMTPException, answer :
                         try :
                             for (k, v) in answer.recipients.items() :
@@ -1090,6 +1090,6 @@
                     server.quit()
             self.logdebug("Feedback sent to user %s." % self.UserName)
-                
-    def mainWork(self) :    
+
+    def mainWork(self) :
         """Main work is done here."""
         if not self.JobSizeBytes :
@@ -1101,5 +1101,5 @@
             self.tellUser()
             return self.removeJob()
-            
+
         self.getPrinterUserAndUserPQuota()
         if self.Policy == "EXTERNALERROR" :
@@ -1116,5 +1116,5 @@
             self.tellUser()
             return self.removeJob()
-        elif self.Policy == "DENY" :    
+        elif self.Policy == "DENY" :
             # Either printer, user or user print quota doesn't exist,
             # and the job should be rejected.
@@ -1134,11 +1134,11 @@
             #            be allowed if current user is allowed to print on this printer
             return self.doWork()
-        else :    
+        else :
             self.Reason = _("Invalid policy %s for printer %s") % (self.Policy, self.PrinterName)
             self.printInfo(self.Reason, "error")
             self.tellUser()
             return self.removeJob()
-    
-    def doWork(self) :    
+
+    def doWork(self) :
         """The accounting work is done here."""
         self.precomputeJobPrice()
@@ -1146,6 +1146,6 @@
         self.exportPrinterInfo()
         self.exportPhaseInfo("BEFORE")
-        
-        if self.Action not in ("DENY", "CANCEL") : 
+
+        if self.Action not in ("DENY", "CANCEL") :
             if self.Printer.MaxJobSize and (self.softwareJobSize > self.Printer.MaxJobSize) :
                 # This printer was set to refuse jobs this large.
@@ -1155,5 +1155,5 @@
                 # because in case of error the user could complain :-)
                 self.Reason = _("You are not allowed to print so many pages on printer %s at this time.") % self.PrinterName
-            
+
         if self.Action not in ("DENY", "CANCEL") :
             if self.User.LimitBy == "noprint" :
@@ -1161,5 +1161,5 @@
                 self.Action = "DENY"
                 self.Reason = _("Your account settings forbid you to print at this time.")
-                
+
         if self.Action not in ("DENY", "CANCEL") :
             # If printing is still allowed at this time, we
@@ -1168,5 +1168,5 @@
             # save some database queries.
             self.getBillingCode()
-            
+
         if self.Action not in ("DENY", "CANCEL") :
             # If printing is still allowed at this time, we
@@ -1175,5 +1175,5 @@
             # save some database queries.
             self.checkIfDupe()
-                    
+
         if self.Action not in ("DENY", "CANCEL") :
             # If printing is still allowed at this time, we
@@ -1183,5 +1183,5 @@
             if self.User.LimitBy in ('noquota', 'nochange') :
                 self.logdebug("User %s is allowed to print with no limit, no need to check quota." % self.UserName)
-            elif self.Printer.PassThrough :    
+            elif self.Printer.PassThrough :
                 self.logdebug("Printer %s is in PassThrough mode, no need to check quota." % self.PrinterName)
             else :
@@ -1194,27 +1194,27 @@
                     self.printInfo(_("Print Quota exceeded for user %s on printer %s") % (self.UserName, self.PrinterName))
                     self.Reason = self.config.getHardWarn(self.PrinterName)
-                elif self.Action == "WARN" :    
+                elif self.Action == "WARN" :
                     self.printInfo(_("Print Quota low for user %s on printer %s") % (self.UserName, self.PrinterName))
-                    if self.User.LimitBy and (self.User.LimitBy.lower() == "balance") : 
+                    if self.User.LimitBy and (self.User.LimitBy.lower() == "balance") :
                         self.Reason = self.config.getPoorWarn()
-                    else :     
+                    else :
                         self.Reason = self.config.getSoftWarn(self.PrinterName)
-            
-        # If job still allowed to print, should we ask for confirmation ?    
-        if self.Action not in ("DENY", "CANCEL") : 
+
+        # If job still allowed to print, should we ask for confirmation ?
+        if self.Action not in ("DENY", "CANCEL") :
             if not self.didUserConfirm() :
                 self.Action = "CANCEL"
                 self.Reason = _("Print job cancelled.")
                 os.environ["PYKOTASTATUS"] = "CANCELLED"
-                
+
         # exports some new environment variables
         self.exportReason()
-        
+
         # now tell the user if he needs to know something
         self.tellUser()
-        
+
         # launches the pre hook
         self.launchPreHook()
-        
+
         # handle starting banner pages without accounting
         self.BannerSize = 0
@@ -1222,8 +1222,8 @@
         if (self.Action != "CANCEL") and accountbanner in ["ENDING", "NONE"] :
             self.handleBanner("starting", 0)
-        
+
         if self.Action == "DENY" :
             self.printInfo(_("Job denied, no accounting will be done."))
-        elif self.Action == "CANCEL" :    
+        elif self.Action == "CANCEL" :
             self.printInfo(_("Job cancelled, no accounting will be done."))
         else :
@@ -1232,28 +1232,28 @@
             self.accounter.beginJob(self.Printer)
             self.installSigTermHandler()
-        
+
         # handle starting banner pages with accounting
         if (self.Action != "CANCEL") and accountbanner in ["STARTING", "BOTH"] :
             if not self.gotSigTerm :
                 self.handleBanner("starting", 1)
-        
-        # pass the job's data to the real backend    
+
+        # pass the job's data to the real backend
         if (not self.gotSigTerm) and (self.Action in ["ALLOW", "WARN"]) :
             retcode = self.printJobDatas()
-        else :        
+        else :
             retcode = self.removeJob()
-        
+
         # indicate phase change
         self.exportPhaseInfo("AFTER")
-        
+
         # handle ending banner pages with accounting
         if (self.Action != "CANCEL") and accountbanner in ["ENDING", "BOTH"] :
             if not self.gotSigTerm :
                 self.handleBanner("ending", 1)
-        
+
         # stops accounting
         if self.Action == "DENY" :
             self.printInfo(_("Job denied, no accounting has been done."))
-        elif self.Action == "CANCEL" :    
+        elif self.Action == "CANCEL" :
             self.printInfo(_("Job cancelled, no accounting has been done."))
         else :
@@ -1262,6 +1262,6 @@
             self.installSigTermHandler()
             self.printInfo(_("Job accounting ends."))
-        
-        # Do all these database changes within a single transaction    
+
+        # Do all these database changes within a single transaction
         # NB : we don't enclose ALL the changes within a single transaction
         # because while waiting for the printer to answer its internal page
@@ -1280,22 +1280,22 @@
                     self.JobSize = 0
                     self.printInfo(_("Job size forced to 0 because the real CUPS backend failed. No accounting will be done."), "warn")
-                else :    
+                else :
                     self.printInfo(_("The real CUPS backend failed, but the job will be accounted for anyway."), "warn")
-                    
-            # retrieve the job size    
+
+            # retrieve the job size
             if self.Action == "DENY" :
                 self.JobSize = 0
                 self.printInfo(_("Job size forced to 0 because printing is denied."))
-            elif self.Action == "CANCEL" :     
+            elif self.Action == "CANCEL" :
                 self.JobSize = 0
                 self.printInfo(_("Job size forced to 0 because printing was cancelled."))
-            else :    
+            else :
                 self.UserPQuota.resetDenyBannerCounter()
-                if (self.Action != "PROBLEM") or ("CHARGE" in onbackenderror) : 
+                if (self.Action != "PROBLEM") or ("CHARGE" in onbackenderror) :
                     self.JobSize = self.accounter.getJobSize(self.Printer)
                     self.sanitizeJobSize()
                     self.JobSize += self.BannerSize
             self.printInfo(_("Job size : %i") % self.JobSize)
-            
+
             if ((self.Action == "PROBLEM") and ("NOCHARGE" in onbackenderror)) or \
                 (self.Action in ("DENY", "CANCEL")) :
@@ -1306,9 +1306,9 @@
                 self.JobPrice = 0.0
             else :
-                # update the quota for the current user on this printer 
+                # update the quota for the current user on this printer
                 self.printInfo(_("Updating user %s's quota on printer %s") % (self.UserName, self.PrinterName))
                 self.JobPrice = self.UserPQuota.increasePagesUsage(self.JobSize, self.accounter.inkUsage)
-            
-            # adds the current job to history    
+
+            # adds the current job to history
             self.Printer.addJobToHistory(self.JobId, self.User, self.accounter.getLastPageCounter(), \
                                     self.Action, self.JobSize, self.JobPrice, self.InputFile, \
@@ -1317,5 +1317,5 @@
                                     self.softwareJobSize, self.softwareJobPrice)
             self.printInfo(_("Job added to history."))
-            
+
             if hasattr(self, "BillingCode") and self.BillingCode and self.BillingCode.Exists :
                 if (self.Action in ("ALLOW", "WARN")) or \
@@ -1323,28 +1323,28 @@
                     self.BillingCode.consume(self.JobSize, self.JobPrice)
                     self.printInfo(_("Billing code %s was updated.") % self.BillingCode.BillingCode)
-        except :    
+        except :
             self.storage.rollbackTransaction()
             raise
-        else :    
+        else :
             self.storage.commitTransaction()
-            
+
         # exports some new environment variables
         self.exportJobSizeAndPrice()
-        
+
         # then re-export user information with new values
         self.exportUserInfo()
-        
+
         # handle ending banner pages without accounting
         if (self.Action != "CANCEL") and accountbanner in ["STARTING", "NONE"] :
             self.handleBanner("ending", 0)
-                    
+
         self.launchPostHook()
-            
-        return retcode    
-               
-    def printJobDatas(self) :           
+
+        return retcode
+
+    def printJobDatas(self) :
         """Sends the job's datas to the real backend."""
         self.logdebug("Sending job's datas to real backend...")
-        
+
         delay = 0
         number = 1
@@ -1355,15 +1355,15 @@
                     if (number < 0) or (delay < 0) :
                         raise ValueError
-                except ValueError :    
+                except ValueError :
                     self.printInfo(_("Incorrect value for the 'onbackenderror' directive in section [%s]") % self.PrinterName, "error")
                     delay = 0
                     number = 1
-                else :    
+                else :
                     break
-        loopcnt = 1 
-        while True :            
+        loopcnt = 1
+        while True :
             if self.InputFile is None :
                 infile = open(self.DataFile, "rb")
-            else :    
+            else :
                 infile = None
             retcode = self.runOriginalBackend(infile)
@@ -1377,10 +1377,10 @@
                     time.sleep(delay)
                     loopcnt += 1
-                else :    
+                else :
                     break
-            
+
         self.logdebug("Job's datas sent to real backend.")
         return retcode
-        
+
     def runOriginalBackend(self, filehandle=None, isBanner=0) :
         """Launches the original backend."""
@@ -1388,5 +1388,5 @@
         if not isBanner :
             arguments = [os.environ["DEVICE_URI"]] + sys.argv[1:]
-        else :    
+        else :
             # For banners, we absolutely WANT
             # to remove any filename from the command line !
@@ -1397,7 +1397,7 @@
         # TODO : do something about the job title : if we are printing a banner and the backend
         # TODO : uses the job's title to name an output file (cups-pdf:// for example), we're stuck !
-        
+
         self.logdebug("Starting original backend %s with args %s" % (originalbackend, " ".join(['"%s"' % a for a in arguments])))
-        self.regainPriv()    
+        self.regainPriv()
         pid = os.fork()
         self.logdebug("Forked !")
@@ -1411,9 +1411,9 @@
             except OSError, msg :
                 self.logdebug("execve() failed: %s" % msg)
-            self.logdebug("We shouldn't be there !!!")    
+            self.logdebug("We shouldn't be there !!!")
             os._exit(-1)
-        self.dropPriv()    
-        
-        self.logdebug("Waiting for original backend to exit...")    
+        self.dropPriv()
+
+        self.logdebug("Waiting for original backend to exit...")
         killed = 0
         status = -1
@@ -1425,5 +1425,5 @@
                     os.kill(pid, signal.SIGTERM)
                     killed = 1
-                    
+
         if os.WIFEXITED(status) :
             status = os.WEXITSTATUS(status)
@@ -1433,5 +1433,5 @@
                 level = "error"
                 self.Reason = message
-            else :    
+            else :
                 level = "info"
             self.printInfo(message, level)
@@ -1445,16 +1445,16 @@
             self.printInfo(self.Reason, "warn")
             return 1
-        
-if __name__ == "__main__" :    
+
+if __name__ == "__main__" :
     # This is a CUPS backend, we should act and die like a CUPS backend
     wrapper = CUPSBackend()
     if len(sys.argv) == 1 :
         print "\n".join(wrapper.discoverOtherBackends())
-        sys.exit(0)                
-    elif len(sys.argv) not in (6, 7) :    
+        sys.exit(0)
+    elif len(sys.argv) not in (6, 7) :
         sys.stderr.write("ERROR: %s job-id user title copies options [file]\n"\
                               % sys.argv[0])
         sys.exit(1)
-    else :    
+    else :
         os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "")
         try :
@@ -1466,23 +1466,24 @@
                     raise KeyboardInterrupt
                 wrapper.saveDatasAndCheckSum()
+                wrapper.exportJobInfo() # exports a first time to give hints to external scripts
                 wrapper.preaccounter = openAccounter(wrapper, ispreaccounter=1)
                 wrapper.accounter = openAccounter(wrapper)
                 wrapper.precomputeJobSize()
-                wrapper.exportJobInfo() # exports a first time to give hints to external scripts 
+                wrapper.exportJobInfo() # exports a first time to give hints to external scripts
                 wrapper.overwriteJobAttributes()
                 wrapper.exportJobInfo() # re-exports in case it was overwritten
                 retcode = wrapper.mainWork()
-            except KeyboardInterrupt :    
+            except KeyboardInterrupt :
                 wrapper.printInfo(_("Job %s interrupted by the administrator !") % wrapper.JobId, "warn")
                 retcode = 0
-            except SystemExit, err :    
+            except SystemExit, err :
                 retcode = err.code
-            except :    
+            except :
                 try :
                     wrapper.crashed("cupspykota backend failed")
-                except :    
+                except :
                     crashed("cupspykota backend failed")
                 retcode = 1
-        finally :        
+        finally :
             wrapper.clean()
         sys.exit(retcode)
